pax_global_header00006660000000000000000000000064132031417400014505gustar00rootroot0000000000000052 comment=909031d7a562fcb224b5d3c71580268a09fb49a2 multipath-tools-0.7.4/000077500000000000000000000000001320314174000146625ustar00rootroot00000000000000multipath-tools-0.7.4/.gitignore000066400000000000000000000003701320314174000166520ustar00rootroot00000000000000*.o .dotest *~ *.so *.so.0 *.a *.gz kpartx/kpartx multipath/multipath multipathd/multipathd mpathpersist/mpathpersist .nfs* *.swp *.patch *.rej *.orig libdmmp/docs/man/*.3.gz libdmmp/*.so.* libdmmp/test/libdmmp_test libdmmp/test/libdmmp_speed_test multipath-tools-0.7.4/COPYING000066400000000000000000000614471320314174000157310ustar00rootroot00000000000000 GNU LIBRARY GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1991 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the library GPL. It is numbered 2 because it goes with version 2 of the ordinary GPL.] 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 Library General Public License, applies to some specially designated Free Software Foundation software, and to any other libraries whose authors decide to use it. You can use it for your libraries, 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 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 a program 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. Our method of protecting your rights has two steps: (1) copyright the library, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the library. Also, for each distributor's protection, we want to make certain that everyone understands that there is no warranty for this free library. If the library is modified by someone else and passed on, we want its recipients to know that what they have is not the original version, 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 companies distributing free software will individually obtain patent licenses, thus in effect transforming the program into proprietary software. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License, which was designed for utility programs. This license, the GNU Library General Public License, applies to certain designated libraries. This license is quite different from the ordinary one; be sure to read it in full, and don't assume that anything in it is the same as in the ordinary license. The reason we have a separate public license for some libraries is that they blur the distinction we usually make between modifying or adding to a program and simply using it. Linking a program with a library, without changing the library, is in some sense simply using the library, and is analogous to running a utility program or application program. However, in a textual and legal sense, the linked executable is a combined work, a derivative of the original library, and the ordinary General Public License treats it as such. Because of this blurred distinction, using the ordinary General Public License for libraries did not effectively promote software sharing, because most developers did not use the libraries. We concluded that weaker conditions might promote sharing better. However, unrestricted linking of non-free programs would deprive the users of those programs of all benefit from the free status of the libraries themselves. This Library General Public License is intended to permit developers of non-free programs to use free libraries, while preserving your freedom as a user of such programs to change the free libraries that are incorporated in them. (We have not seen how to achieve this as regards changes in header files, but we have achieved it as regards changes in the actual functions of the Library.) The hope is that this will lead to faster development of free libraries. 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, while the latter only works together with the library. Note that it is possible for a library to be covered by the ordinary General Public License rather than by this special one. GNU LIBRARY GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Library 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 compile 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) 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. c) 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. d) 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 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. 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 to 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 Library 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 Library General Public License as published by the Free Software Foundation; either version 2 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, 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! multipath-tools-0.7.4/Makefile000066400000000000000000000015651320314174000163310ustar00rootroot00000000000000# # Copyright (C) 2003 Christophe Varoqui, # BUILDDIRS = \ libmpathcmd \ libmultipath \ libmultipath/prioritizers \ libmultipath/checkers \ libmpathpersist \ multipath \ multipathd \ mpathpersist \ kpartx ifneq ($(ENABLE_LIBDMMP),0) BUILDDIRS += \ libdmmp endif all: recurse recurse: @for dir in $(BUILDDIRS); do $(MAKE) -C $$dir || exit $?; done recurse_clean: @for dir in $(BUILDDIRS); do \ $(MAKE) -C $$dir clean || exit $?; \ done recurse_install: @for dir in $(BUILDDIRS); do \ $(MAKE) -C $$dir install || exit $?; \ done recurse_uninstall: @for dir in $(BUILDDIRS); do \ $(MAKE) -C $$dir uninstall || exit $?; \ done clean: recurse_clean install: recurse_install uninstall: recurse_uninstall .PHONY: TAGS TAGS: etags -a libmultipath/*.c etags -a libmultipath/*.h etags -a multipathd/*.c etags -a multipathd/*.h multipath-tools-0.7.4/Makefile.inc000066400000000000000000000063321320314174000170760ustar00rootroot00000000000000# # Copyright (C) 2004 Christophe Varoqui, # # # Allow to force some libraries to be used statically. (Uncomment one of the # following lines or define the values when calling make.) # # WITH_LOCAL_LIBDM = 1 # WITH_LOCAL_LIBSYSFS = 1 # # Uncomment to disable RADOS support (e.g. if rados headers are missing). # ENABLE_RADOS = 0 # # Uncomment to disable libdmmp support # ENABLE_LIBDMMP = 0 ifeq ($(TOPDIR),) TOPDIR = .. endif ifndef LIB ifeq ($(shell test -d /lib64 && echo 1),1) LIB=lib64 else LIB=lib endif endif ifndef RUN ifeq ($(shell test -L /var/run -o ! -d /var/run && echo 1),1) RUN=run else RUN=var/run endif endif ifndef SYSTEMD ifeq ($(shell systemctl --version > /dev/null 2>&1 && echo 1), 1) SYSTEMD = $(shell systemctl --version 2> /dev/null | sed -n 's/systemd \([0-9]*\)/\1/p') endif endif ifndef SYSTEMDPATH SYSTEMDPATH=usr/lib endif prefix = exec_prefix = $(prefix) usr_prefix = $(prefix) bindir = $(exec_prefix)/sbin libudevdir = $(prefix)/$(SYSTEMDPATH)/udev udevrulesdir = $(libudevdir)/rules.d multipathdir = $(TOPDIR)/libmultipath man8dir = $(prefix)/usr/share/man/man8 man5dir = $(prefix)/usr/share/man/man5 man3dir = $(prefix)/usr/share/man/man3 syslibdir = $(prefix)/$(LIB) usrlibdir = $(usr_prefix)/$(LIB) libdir = $(prefix)/$(LIB)/multipath unitdir = $(prefix)/$(SYSTEMDPATH)/systemd/system mpathpersistdir = $(TOPDIR)/libmpathpersist mpathcmddir = $(TOPDIR)/libmpathcmd thirdpartydir = $(TOPDIR)/third-party libdmmpdir = $(TOPDIR)/libdmmp includedir = $(prefix)/usr/include pkgconfdir = $(usrlibdir)/pkgconfig GZIP = gzip -9 -c RM = rm -f LN = ln -sf INSTALL_PROGRAM = install # $(call TEST_CC_OPTION,option,fallback) # Test if the C compiler supports the option. # Evaluates to "option" if yes, and "fallback" otherwise. TEST_CC_OPTION = $(shell \ if echo 'int main(void){return 0;}' | $(CC) -o /dev/null -c "$(1)" -xc - &>/dev/null; \ then \ echo "$(1)"; \ else \ echo "$(2)"; \ fi) STACKPROT := $(call TEST_CC_OPTION,-fstack-protector-strong,-fstack-protector) OPTFLAGS = -O2 -g -pipe -Wall -Wextra -Wformat=2 -Werror=implicit-int \ -Werror=implicit-function-declaration -Werror=format-security \ -Wno-sign-compare -Wno-unused-parameter -Wno-clobbered \ -Wp,-D_FORTIFY_SOURCE=2 $(STACKPROT) \ --param=ssp-buffer-size=4 CFLAGS = $(OPTFLAGS) -DLIB_STRING=\"${LIB}\" -DRUN_DIR=\"${RUN}\" BIN_CFLAGS = -fPIE -DPIE LIB_CFLAGS = -fPIC SHARED_FLAGS = -shared LDFLAGS = -Wl,-z,relro -Wl,-z,now BIN_LDFLAGS = -pie # Check whether a function with name $1 has been declared in header file $2. check_func = \ $(shell \ if grep -Eq "^[^[:blank:]]+[[:blank:]]+$1[[:blank:]]*(.*)*" "$2"; then \ found=1; \ status="yes"; \ else \ found=0; \ status="no"; \ fi; \ echo 1>&2 "Checking for $1 in $2 ... $$status"; \ echo "$$found" \ ) # Checker whether a file with name $1 exists check_file = $(shell \ if [ -f "$1" ]; then \ found=1; \ status="yes"; \ else \ found=0; \ status="no"; \ fi; \ echo 1>&2 "Checking if $1 exists ... $$status"; \ echo "$$found" \ ) %.o: %.c $(CC) $(CFLAGS) -c -o $@ $< multipath-tools-0.7.4/README000066400000000000000000000030371320314174000155450ustar00rootroot00000000000000 multipath-tools for Linux This package provides the following binaries to drive the Device Mapper multipathing driver: multipath - Device mapper target autoconfig. multipathd - Multipath daemon. mpathpersist - Manages SCSI persistent reservations on dm multipath devices. kpartx - Create device maps from partition tables. Releases ======== Tarballs are not generated anymore, to get a specific release do: git clone https://git.opensvc.com/multipath-tools/.git cd multipath-tools git tag git archive --format=tar.gz --prefix=multipath-tools-X.Y.Z/ X.Y.Z > ../multipath-tools-X.Y.Z.tar.gz Alternatively it may be obtained from gitweb, go to: https://git.opensvc.com/?p=multipath-tools/.git;a=tags select a release-tag and then click on "snapshot". Source code =========== To get latest devel code: git clone https://git.opensvc.com/multipath-tools/.git Gitweb: https://git.opensvc.com/?p=multipath-tools/.git Add storage devices =================== Follow the instructions in the libmultipath/hwtable.c header. Mailing list (subscribers-only) ============ To subscribe and archives: https://www.redhat.com/mailman/listinfo/dm-devel Searchable: https://marc.info/?l=dm-devel Changelog ========= pre-0.4.5: https://web.archive.org/web/20070309224034/http://christophe.varoqui.free.fr/wiki/wakka.php?wiki=ChangeLog post-0.4.5: https://git.opensvc.com/?p=multipath-tools/.git;a=log Maintainer ========== Christophe Varoqui Device-mapper development mailing list multipath-tools-0.7.4/kpartx/000077500000000000000000000000001320314174000161735ustar00rootroot00000000000000multipath-tools-0.7.4/kpartx/Makefile000066400000000000000000000032231320314174000176330ustar00rootroot00000000000000# # Copyright (C) 2003 Christophe Varoqui, # include ../Makefile.inc CFLAGS += $(BIN_CFLAGS) -I. -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 LDFLAGS += $(BIN_LDFLAGS) LIBDEPS += -ldevmapper ifneq ($(call check_func,dm_task_set_cookie,/usr/include/libdevmapper.h),0) CFLAGS += -DLIBDM_API_COOKIE endif OBJS = bsd.o dos.o kpartx.o solaris.o unixware.o dasd.o sun.o \ gpt.o mac.o ps3.o crc32.o lopart.o xstrncpy.o devmapper.o EXEC = kpartx all: $(EXEC) $(EXEC): $(OBJS) $(CC) $(CFLAGS) $(OBJS) -o $(EXEC) $(LDFLAGS) $(LIBDEPS) $(GZIP) $(EXEC).8 > $(EXEC).8.gz install: $(EXEC) $(EXEC).8 $(INSTALL_PROGRAM) -d $(DESTDIR)$(bindir) $(INSTALL_PROGRAM) -m 755 $(EXEC) $(DESTDIR)$(bindir) $(INSTALL_PROGRAM) -d $(DESTDIR)$(libudevdir) $(INSTALL_PROGRAM) -m 755 kpartx_id $(DESTDIR)$(libudevdir) $(INSTALL_PROGRAM) -d $(DESTDIR)$(libudevdir)/rules.d $(INSTALL_PROGRAM) -m 644 dm-parts.rules $(DESTDIR)$(libudevdir)/rules.d/11-dm-parts.rules $(INSTALL_PROGRAM) -m 644 kpartx.rules $(DESTDIR)$(libudevdir)/rules.d/66-kpartx.rules $(INSTALL_PROGRAM) -m 644 del-part-nodes.rules $(DESTDIR)$(libudevdir)/rules.d/68-del-part-nodes.rules $(INSTALL_PROGRAM) -d $(DESTDIR)$(man8dir) $(INSTALL_PROGRAM) -m 644 $(EXEC).8.gz $(DESTDIR)$(man8dir) uninstall: $(RM) $(DESTDIR)$(bindir)/$(EXEC) $(RM) $(DESTDIR)$(man8dir)/$(EXEC).8.gz $(RM) $(DESTDIR)$(libudevdir)/kpartx_id $(RM) $(DESTDIR)$(libudevdir)/rules.d/11-dm-parts.rules $(RM) $(DESTDIR)$(libudevdir)/rules.d/66-kpartx.rules $(RM) $(DESTDIR)$(libudevdir)/rules.d/67-kpartx-compat.rules $(RM) $(DESTDIR)$(libudevdir)/rules.d/68-del-part-nodes.rules clean: $(RM) core *.o $(EXEC) *.gz multipath-tools-0.7.4/kpartx/bsd.c000066400000000000000000000073601320314174000171150ustar00rootroot00000000000000#include "kpartx.h" #include #define BSD_DISKMAGIC (0x82564557UL) /* The disk magic number */ #define XBSD_MAXPARTITIONS 16 #define BSD_FS_UNUSED 0 struct bsd_disklabel { unsigned int d_magic; /* the magic number */ short int d_type; /* drive type */ short int d_subtype; /* controller/d_type specific */ char d_typename[16]; /* type name, e.g. "eagle" */ char d_packname[16]; /* pack identifier */ unsigned int d_secsize; /* # of bytes per sector */ unsigned int d_nsectors; /* # of data sectors per track */ unsigned int d_ntracks; /* # of tracks per cylinder */ unsigned int d_ncylinders; /* # of data cylinders per unit */ unsigned int d_secpercyl; /* # of data sectors per cylinder */ unsigned int d_secperunit; /* # of data sectors per unit */ unsigned short d_sparespertrack;/* # of spare sectors per track */ unsigned short d_sparespercyl; /* # of spare sectors per cylinder */ unsigned int d_acylinders; /* # of alt. cylinders per unit */ unsigned short d_rpm; /* rotational speed */ unsigned short d_interleave; /* hardware sector interleave */ unsigned short d_trackskew; /* sector 0 skew, per track */ unsigned short d_cylskew; /* sector 0 skew, per cylinder */ unsigned int d_headswitch; /* head switch time, usec */ unsigned int d_trkseek; /* track-to-track seek, usec */ unsigned int d_flags; /* generic flags */ unsigned int d_drivedata[5]; /* drive-type specific information */ unsigned int d_spare[5]; /* reserved for future use */ unsigned int d_magic2; /* the magic number (again) */ unsigned short d_checksum; /* xor of data incl. partitions */ /* filesystem and partition information: */ unsigned short d_npartitions; /* number of partitions in following */ unsigned int d_bbsize; /* size of boot area at sn0, bytes */ unsigned int d_sbsize; /* max size of fs superblock, bytes */ struct bsd_partition { /* the partition table */ unsigned int p_size; /* number of sectors in partition */ unsigned int p_offset; /* starting sector */ unsigned int p_fsize; /* filesystem basic fragment size */ unsigned char p_fstype; /* filesystem type, see below */ unsigned char p_frag; /* filesystem fragments per block */ unsigned short p_cpg; /* filesystem cylinders per group */ } d_partitions[XBSD_MAXPARTITIONS];/* actually may be more */ }; int read_bsd_pt(int fd, struct slice all, struct slice *sp, int ns) { struct bsd_disklabel *l; struct bsd_partition *p; unsigned int offset = all.start, end; int max_partitions; char *bp; int n = 0, i, j; bp = getblock(fd, offset+1); /* 1 sector suffices */ if (bp == NULL) return -1; l = (struct bsd_disklabel *) bp; if (l->d_magic != BSD_DISKMAGIC) return -1; max_partitions = 16; if (l->d_npartitions < max_partitions) max_partitions = l->d_npartitions; for (p = l->d_partitions; p - l->d_partitions < max_partitions; p++) { if (p->p_fstype == BSD_FS_UNUSED) /* nothing */; else if (n < ns) { sp[n].start = p->p_offset; sp[n].size = p->p_size; n++; } else { fprintf(stderr, "bsd_partition: too many slices\n"); break; } } /* * Convention has it that the bsd disklabel will always have * the 'c' partition spanning the entire disk. * So we have to check for contained slices. */ for(i = 0; i < n; i++) { if (sp[i].size == 0) continue; end = sp[i].start + sp[i].size; for(j = 0; j < n; j ++) { if ( i == j ) continue; if (sp[j].size == 0) continue; if (sp[i].start < sp[j].start) { if (end > sp[j].start && end < sp[j].start + sp[j].size) { /* Invalid slice */ fprintf(stderr, "bsd_disklabel: slice %d overlaps with %d\n", i , j); sp[i].size = 0; } } else { if (end <= sp[j].start + sp[j].size) { sp[i].container = j + 1; } } } } return n; } multipath-tools-0.7.4/kpartx/byteorder.h000066400000000000000000000012601320314174000203420ustar00rootroot00000000000000#ifndef BYTEORDER_H_INCLUDED #define BYTEORDER_H_INCLUDED #ifdef __linux__ # include # include #else # error unsupported #endif #if BYTE_ORDER == LITTLE_ENDIAN # define le16_to_cpu(x) (x) # define be16_to_cpu(x) bswap_16(x) # define le32_to_cpu(x) (x) # define le64_to_cpu(x) (x) # define be32_to_cpu(x) bswap_32(x) # define be64_to_cpu(x) bswap_64(x) #elif BYTE_ORDER == BIG_ENDIAN # define le16_to_cpu(x) bswap_16(x) # define be16_to_cpu(x) (x) # define le32_to_cpu(x) bswap_32(x) # define le64_to_cpu(x) bswap_64(x) # define be32_to_cpu(x) (x) # define be64_to_cpu(x) (x) #else # error unsupported #endif #endif /* BYTEORDER_H_INCLUDED */ multipath-tools-0.7.4/kpartx/crc32.c000066400000000000000000000315051320314174000172570ustar00rootroot00000000000000/* * crc32.c * This code is in the public domain; copyright abandoned. * Liability for non-performance of this code is limited to the amount * you paid for it. Since it is distributed for free, your refund will * be very very small. If it breaks, you get to keep both pieces. */ #include "crc32.h" #if __GNUC__ >= 3 /* 2.x has "attribute", but only 3.0 has "pure */ #define attribute(x) __attribute__(x) #else #define attribute(x) #endif /* * There are multiple 16-bit CRC polynomials in common use, but this is * *the* standard CRC-32 polynomial, first popularized by Ethernet. * x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x^1+x^0 */ #define CRCPOLY_LE 0xedb88320 #define CRCPOLY_BE 0x04c11db7 /* How many bits at a time to use. Requires a table of 4< 8 || CRC_LE_BITS < 1 || CRC_LE_BITS & CRC_LE_BITS-1 # error CRC_LE_BITS must be a power of 2 between 1 and 8 #endif #if CRC_LE_BITS == 1 /* * In fact, the table-based code will work in this case, but it can be * simplified by inlining the table in ?: form. */ #define crc32init_le() #define crc32cleanup_le() /** * crc32_le() - Calculate bitwise little-endian Ethernet AUTODIN II CRC32 * @crc - seed value for computation. ~0 for Ethernet, sometimes 0 for * other uses, or the previous crc32 value if computing incrementally. * @p - pointer to buffer over which CRC is run * @len - length of buffer @p * */ uint32_t attribute((pure)) crc32_le(uint32_t crc, unsigned char const *p, size_t len) { int i; while (len--) { crc ^= *p++; for (i = 0; i < 8; i++) crc = (crc >> 1) ^ ((crc & 1) ? CRCPOLY_LE : 0); } return crc; } #else /* Table-based approach */ static uint32_t *crc32table_le; /** * crc32init_le() - allocate and initialize LE table data * * crc is the crc of the byte i; other entries are filled in based on the * fact that crctable[i^j] = crctable[i] ^ crctable[j]. * */ static int crc32init_le(void) { unsigned i, j; uint32_t crc = 1; crc32table_le = malloc((1 << CRC_LE_BITS) * sizeof(uint32_t)); if (!crc32table_le) return 1; crc32table_le[0] = 0; for (i = 1 << (CRC_LE_BITS - 1); i; i >>= 1) { crc = (crc >> 1) ^ ((crc & 1) ? CRCPOLY_LE : 0); for (j = 0; j < 1 << CRC_LE_BITS; j += 2 * i) crc32table_le[i + j] = crc ^ crc32table_le[j]; } return 0; } /** * crc32cleanup_le(): free LE table data */ static void crc32cleanup_le(void) { if (crc32table_le) free(crc32table_le); crc32table_le = NULL; } /** * crc32_le() - Calculate bitwise little-endian Ethernet AUTODIN II CRC32 * @crc - seed value for computation. ~0 for Ethernet, sometimes 0 for * other uses, or the previous crc32 value if computing incrementally. * @p - pointer to buffer over which CRC is run * @len - length of buffer @p * */ uint32_t attribute((pure)) crc32_le(uint32_t crc, unsigned char const *p, size_t len) { while (len--) { # if CRC_LE_BITS == 8 crc = (crc >> 8) ^ crc32table_le[(crc ^ *p++) & 255]; # elif CRC_LE_BITS == 4 crc ^= *p++; crc = (crc >> 4) ^ crc32table_le[crc & 15]; crc = (crc >> 4) ^ crc32table_le[crc & 15]; # elif CRC_LE_BITS == 2 crc ^= *p++; crc = (crc >> 2) ^ crc32table_le[crc & 3]; crc = (crc >> 2) ^ crc32table_le[crc & 3]; crc = (crc >> 2) ^ crc32table_le[crc & 3]; crc = (crc >> 2) ^ crc32table_le[crc & 3]; # endif } return crc; } #endif /* * Big-endian CRC computation. Used with serial bit streams sent * msbit-first. Be sure to use cpu_to_be32() to append the computed CRC. */ #if CRC_BE_BITS > 8 || CRC_BE_BITS < 1 || CRC_BE_BITS & CRC_BE_BITS-1 # error CRC_BE_BITS must be a power of 2 between 1 and 8 #endif #if CRC_BE_BITS == 1 /* * In fact, the table-based code will work in this case, but it can be * simplified by inlining the table in ?: form. */ #define crc32init_be() #define crc32cleanup_be() /** * crc32_be() - Calculate bitwise big-endian Ethernet AUTODIN II CRC32 * @crc - seed value for computation. ~0 for Ethernet, sometimes 0 for * other uses, or the previous crc32 value if computing incrementally. * @p - pointer to buffer over which CRC is run * @len - length of buffer @p * */ uint32_t attribute((pure)) crc32_be(uint32_t crc, unsigned char const *p, size_t len) { int i; while (len--) { crc ^= *p++ << 24; for (i = 0; i < 8; i++) crc = (crc << 1) ^ ((crc & 0x80000000) ? CRCPOLY_BE : 0); } return crc; } #else /* Table-based approach */ static uint32_t *crc32table_be; /** * crc32init_be() - allocate and initialize BE table data */ static int crc32init_be(void) { unsigned i, j; uint32_t crc = 0x80000000; crc32table_be = malloc((1 << CRC_BE_BITS) * sizeof(uint32_t)); if (!crc32table_be) return 1; crc32table_be[0] = 0; for (i = 1; i < 1 << CRC_BE_BITS; i <<= 1) { crc = (crc << 1) ^ ((crc & 0x80000000) ? CRCPOLY_BE : 0); for (j = 0; j < i; j++) crc32table_be[i + j] = crc ^ crc32table_be[j]; } return 0; } /** * crc32cleanup_be(): free BE table data */ static void crc32cleanup_be(void) { if (crc32table_be) free(crc32table_be); crc32table_be = NULL; } /** * crc32_be() - Calculate bitwise big-endian Ethernet AUTODIN II CRC32 * @crc - seed value for computation. ~0 for Ethernet, sometimes 0 for * other uses, or the previous crc32 value if computing incrementally. * @p - pointer to buffer over which CRC is run * @len - length of buffer @p * */ uint32_t attribute((pure)) crc32_be(uint32_t crc, unsigned char const *p, size_t len) { while (len--) { # if CRC_BE_BITS == 8 crc = (crc << 8) ^ crc32table_be[(crc >> 24) ^ *p++]; # elif CRC_BE_BITS == 4 crc ^= *p++ << 24; crc = (crc << 4) ^ crc32table_be[crc >> 28]; crc = (crc << 4) ^ crc32table_be[crc >> 28]; # elif CRC_BE_BITS == 2 crc ^= *p++ << 24; crc = (crc << 2) ^ crc32table_be[crc >> 30]; crc = (crc << 2) ^ crc32table_be[crc >> 30]; crc = (crc << 2) ^ crc32table_be[crc >> 30]; crc = (crc << 2) ^ crc32table_be[crc >> 30]; # endif } return crc; } #endif /* * A brief CRC tutorial. * * A CRC is a long-division remainder. You add the CRC to the message, * and the whole thing (message+CRC) is a multiple of the given * CRC polynomial. To check the CRC, you can either check that the * CRC matches the recomputed value, *or* you can check that the * remainder computed on the message+CRC is 0. This latter approach * is used by a lot of hardware implementations, and is why so many * protocols put the end-of-frame flag after the CRC. * * It's actually the same long division you learned in school, except that * - We're working in binary, so the digits are only 0 and 1, and * - When dividing polynomials, there are no carries. Rather than add and * subtract, we just xor. Thus, we tend to get a bit sloppy about * the difference between adding and subtracting. * * A 32-bit CRC polynomial is actually 33 bits long. But since it's * 33 bits long, bit 32 is always going to be set, so usually the CRC * is written in hex with the most significant bit omitted. (If you're * familiar with the IEEE 754 floating-point format, it's the same idea.) * * Note that a CRC is computed over a string of *bits*, so you have * to decide on the endianness of the bits within each byte. To get * the best error-detecting properties, this should correspond to the * order they're actually sent. For example, standard RS-232 serial is * little-endian; the most significant bit (sometimes used for parity) * is sent last. And when appending a CRC word to a message, you should * do it in the right order, matching the endianness. * * Just like with ordinary division, the remainder is always smaller than * the divisor (the CRC polynomial) you're dividing by. Each step of the * division, you take one more digit (bit) of the dividend and append it * to the current remainder. Then you figure out the appropriate multiple * of the divisor to subtract to being the remainder back into range. * In binary, it's easy - it has to be either 0 or 1, and to make the * XOR cancel, it's just a copy of bit 32 of the remainder. * * When computing a CRC, we don't care about the quotient, so we can * throw the quotient bit away, but subtract the appropriate multiple of * the polynomial from the remainder and we're back to where we started, * ready to process the next bit. * * A big-endian CRC written this way would be coded like: * for (i = 0; i < input_bits; i++) { * multiple = remainder & 0x80000000 ? CRCPOLY : 0; * remainder = (remainder << 1 | next_input_bit()) ^ multiple; * } * Notice how, to get at bit 32 of the shifted remainder, we look * at bit 31 of the remainder *before* shifting it. * * But also notice how the next_input_bit() bits we're shifting into * the remainder don't actually affect any decision-making until * 32 bits later. Thus, the first 32 cycles of this are pretty boring. * Also, to add the CRC to a message, we need a 32-bit-long hole for it at * the end, so we have to add 32 extra cycles shifting in zeros at the * end of every message, * * So the standard trick is to rearrage merging in the next_input_bit() * until the moment it's needed. Then the first 32 cycles can be precomputed, * and merging in the final 32 zero bits to make room for the CRC can be * skipped entirely. * This changes the code to: * for (i = 0; i < input_bits; i++) { * remainder ^= next_input_bit() << 31; * multiple = (remainder & 0x80000000) ? CRCPOLY : 0; * remainder = (remainder << 1) ^ multiple; * } * With this optimization, the little-endian code is simpler: * for (i = 0; i < input_bits; i++) { * remainder ^= next_input_bit(); * multiple = (remainder & 1) ? CRCPOLY : 0; * remainder = (remainder >> 1) ^ multiple; * } * * Note that the other details of endianness have been hidden in CRCPOLY * (which must be bit-reversed) and next_input_bit(). * * However, as long as next_input_bit is returning the bits in a sensible * order, we can actually do the merging 8 or more bits at a time rather * than one bit at a time: * for (i = 0; i < input_bytes; i++) { * remainder ^= next_input_byte() << 24; * for (j = 0; j < 8; j++) { * multiple = (remainder & 0x80000000) ? CRCPOLY : 0; * remainder = (remainder << 1) ^ multiple; * } * } * Or in little-endian: * for (i = 0; i < input_bytes; i++) { * remainder ^= next_input_byte(); * for (j = 0; j < 8; j++) { * multiple = (remainder & 1) ? CRCPOLY : 0; * remainder = (remainder << 1) ^ multiple; * } * } * If the input is a multiple of 32 bits, you can even XOR in a 32-bit * word at a time and increase the inner loop count to 32. * * You can also mix and match the two loop styles, for example doing the * bulk of a message byte-at-a-time and adding bit-at-a-time processing * for any fractional bytes at the end. * * The only remaining optimization is to the byte-at-a-time table method. * Here, rather than just shifting one bit of the remainder to decide * in the correct multiple to subtract, we can shift a byte at a time. * This produces a 40-bit (rather than a 33-bit) intermediate remainder, * but again the multiple of the polynomial to subtract depends only on * the high bits, the high 8 bits in this case. * * The multile we need in that case is the low 32 bits of a 40-bit * value whose high 8 bits are given, and which is a multiple of the * generator polynomial. This is simply the CRC-32 of the given * one-byte message. * * Two more details: normally, appending zero bits to a message which * is already a multiple of a polynomial produces a larger multiple of that * polynomial. To enable a CRC to detect this condition, it's common to * invert the CRC before appending it. This makes the remainder of the * message+crc come out not as zero, but some fixed non-zero value. * * The same problem applies to zero bits prepended to the message, and * a similar solution is used. Instead of starting with a remainder of * 0, an initial remainder of all ones is used. As long as you start * the same way on decoding, it doesn't make a difference. */ /** * init_crc32(): generates CRC32 tables * * On successful initialization, use count is increased. * This guarantees that the library functions will stay resident * in memory, and prevents someone from 'rmmod crc32' while * a driver that needs it is still loaded. * This also greatly simplifies drivers, as there's no need * to call an initialization/cleanup function from each driver. * Since crc32.o is a library module, there's no requirement * that the user can unload it. */ int init_crc32(void) { int rc1, rc2, rc; rc1 = crc32init_le(); rc2 = crc32init_be(); rc = rc1 || rc2; return rc; } /** * cleanup_crc32(): frees crc32 data when no longer needed */ void cleanup_crc32(void) { crc32cleanup_le(); crc32cleanup_be(); } multipath-tools-0.7.4/kpartx/crc32.h000066400000000000000000000010431320314174000172560ustar00rootroot00000000000000/* * crc32.h */ #ifndef _CRC32_H #define _CRC32_H #include #include extern int init_crc32(void); extern void cleanup_crc32(void); extern uint32_t crc32_le(uint32_t crc, unsigned char const *p, size_t len); extern uint32_t crc32_be(uint32_t crc, unsigned char const *p, size_t len); #define crc32(seed, data, length) crc32_le(seed, (unsigned char const *)data, length) #define ether_crc_le(length, data) crc32_le(~0, data, length) #define ether_crc(length, data) crc32_be(~0, data, length) #endif /* _CRC32_H */ multipath-tools-0.7.4/kpartx/dasd.c000066400000000000000000000156701320314174000172630ustar00rootroot00000000000000/* * dasd.c * * IBM DASD partition table handling. * * Mostly taken from drivers/s390/block/dasd.c * * Copyright (c) 2005, Hannes Reinecke, SUSE Linux Products GmbH * Copyright IBM Corporation, 2009 * * 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, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "devmapper.h" #include "kpartx.h" #include "byteorder.h" #include "dasd.h" unsigned long long sectors512(unsigned long long sectors, int blocksize) { return sectors * (blocksize >> 9); } /* * Magic records per track calculation, copied from fdasd.c */ static unsigned int ceil_quot(unsigned int d1, unsigned int d2) { return (d1 + (d2 - 1)) / d2; } unsigned int recs_per_track(unsigned int dl) { int dn = ceil_quot(dl + 6, 232) + 1; return 1729 / (10 + 9 + ceil_quot(dl + 6 * dn, 34)); } typedef unsigned int __attribute__((__may_alias__)) label_ints_t; /* */ int read_dasd_pt(int fd, struct slice all, struct slice *sp, int ns) { int retval = -1; int blocksize; uint64_t disksize; uint64_t offset, size, fmt_size; dasd_information_t info; struct hd_geometry geo; char type[5] = {0,}; volume_label_t vlabel; unsigned char *data = NULL; uint64_t blk; int fd_dasd = -1; struct stat sbuf; dev_t dev; char *devname; char pathname[256]; if (fd < 0) { return -1; } if (fstat(fd, &sbuf) == -1) { return -1; } devname = dm_mapname(major(sbuf.st_rdev), minor(sbuf.st_rdev)); if (devname != NULL) { /* We were passed a handle to a dm device. * Get the first target and operate on that instead. */ if (!(dev = dm_get_first_dep(devname))) { free(devname); return -1; } free(devname); if ((unsigned int)major(dev) != 94) { /* Not a DASD */ return -1; } /* * Hard to believe, but there's no simple way to translate * major/minor into an openable device file, so we have * to create one for ourselves. */ sprintf(pathname, "/dev/.kpartx-node-%u-%u", (unsigned int)major(dev), (unsigned int)minor(dev)); if ((fd_dasd = open(pathname, O_RDONLY)) == -1) { /* Devicenode does not exist. Try to create one */ if (mknod(pathname, 0600 | S_IFBLK, dev) == -1) { /* Couldn't create a device node */ return -1; } fd_dasd = open(pathname, O_RDONLY); /* * The file will vanish when the last process (we) * has ceased to access it. */ unlink(pathname); } if (!fd_dasd) { /* Couldn't open the device */ return -1; } } else if ((unsigned int)major(sbuf.st_rdev) != 94) { /* Not a DASD */ return -1; } else { fd_dasd = fd; } if (ioctl(fd_dasd, BIODASDINFO, (unsigned long)&info) != 0) { info.label_block = 2; info.FBA_layout = 0; memcpy(info.type, "ECKD", sizeof(info.type)); } if (ioctl(fd_dasd, BLKSSZGET, &blocksize) != 0) goto out; if (ioctl(fd_dasd, BLKGETSIZE64, &disksize) != 0) goto out; if (ioctl(fd_dasd, HDIO_GETGEO, (unsigned long)&geo) != 0) { unsigned int cyl; geo.heads = 15; geo.sectors = recs_per_track(blocksize); cyl = disksize / (blocksize * geo.heads * geo.sectors); if (cyl < LV_COMPAT_CYL) geo.cylinders = cyl; else geo.cylinders = LV_COMPAT_CYL; geo.start = 0; } disksize >>= 9; if (blocksize < 512 || blocksize > 4096) goto out; /* * Get volume label, extract name and type. */ if (!(data = (unsigned char *)malloc(blocksize))) goto out; if (lseek(fd_dasd, info.label_block * blocksize, SEEK_SET) == -1) goto out; if (read(fd_dasd, data, blocksize) == -1) { perror("read"); goto out; } if ((!info.FBA_layout) && (!strcmp(info.type, "ECKD"))) memcpy (&vlabel, data, sizeof(vlabel)); else { bzero(&vlabel,4); memcpy (&vlabel.vollbl, data, sizeof(vlabel) - 4); } vtoc_ebcdic_dec(vlabel.vollbl, type, 4); /* * Three different types: CMS1, VOL1 and LNX1/unlabeled */ if (strncmp(type, "CMS1", 4) == 0) { /* * VM style CMS1 labeled disk */ label_ints_t *label = (label_ints_t *) &vlabel; blocksize = label[4]; if (label[14] != 0) { /* disk is reserved minidisk */ offset = label[14]; size = sectors512(label[8] - 1, blocksize); } else { offset = info.label_block + 1; size = sectors512(label[8], blocksize); } sp[0].start = sectors512(offset, blocksize); sp[0].size = size - sp[0].start; retval = 1; } else if ((strncmp(type, "VOL1", 4) == 0) && (!info.FBA_layout) && (!strcmp(info.type, "ECKD"))) { /* * New style VOL1 labeled disk */ int counter; /* get block number and read then go through format1 labels */ blk = cchhb2blk(&vlabel.vtoc, &geo) + 1; counter = 0; if (lseek(fd_dasd, blk * blocksize, SEEK_SET) == -1) goto out; while (read(fd_dasd, data, blocksize) != -1) { format1_label_t f1; memcpy(&f1, data, sizeof(format1_label_t)); /* skip FMT4 / FMT5 / FMT7 labels */ if (EBCtoASC[f1.DS1FMTID] == '4' || EBCtoASC[f1.DS1FMTID] == '5' || EBCtoASC[f1.DS1FMTID] == '7' || EBCtoASC[f1.DS1FMTID] == '9') { blk++; continue; } /* only FMT1 and FMT8 valid at this point */ if (EBCtoASC[f1.DS1FMTID] != '1' && EBCtoASC[f1.DS1FMTID] != '8') break; /* OK, we got valid partition data */ offset = cchh2blk(&f1.DS1EXT1.llimit, &geo); size = cchh2blk(&f1.DS1EXT1.ulimit, &geo) - offset + geo.sectors; sp[counter].start = sectors512(offset, blocksize); sp[counter].size = sectors512(size, blocksize); counter++; blk++; } retval = counter; } else { /* * Old style LNX1 or unlabeled disk */ if (strncmp(type, "LNX1", 4) == 0) { if (vlabel.ldl_version == 0xf2) { fmt_size = sectors512(vlabel.formatted_blocks, blocksize); } else if (!strcmp(info.type, "ECKD")) { /* formatted w/o large volume support */ fmt_size = geo.cylinders * geo.heads * geo.sectors * (blocksize >> 9); } else { /* old label and no usable disk geometry * (e.g. DIAG) */ fmt_size = disksize; } size = disksize; if (fmt_size < size) size = fmt_size; } else size = disksize; sp[0].start = sectors512(info.label_block + 1, blocksize); sp[0].size = size - sp[0].start; retval = 1; } out: if (data != NULL) free(data); if (fd_dasd != -1 && fd_dasd != fd) close(fd_dasd); return retval; } multipath-tools-0.7.4/kpartx/dasd.h000066400000000000000000000273621320314174000172710ustar00rootroot00000000000000/* * dasd.h * * IBM DASD partition table handling. * * Mostly taken from drivers/s390/block/dasd.c * * Copyright (c) 2005, Hannes Reinecke, SUSE Linux Products GmbH * Copyright IBM Corporation, 2009 * * 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, see . */ #ifndef _DASD_H #define _DASD_H typedef struct ttr { uint16_t tt; uint8_t r; } __attribute__ ((packed)) ttr_t; typedef struct cchhb { uint16_t cc; uint16_t hh; uint8_t b; } __attribute__ ((packed)) cchhb_t; typedef struct cchh { uint16_t cc; uint16_t hh; } __attribute__ ((packed)) cchh_t; typedef struct labeldate { uint8_t year; uint16_t day; } __attribute__ ((packed)) labeldate_t; typedef struct volume_label { char volkey[4]; /* volume key = volume label */ char vollbl[4]; /* volume label */ char volid[6]; /* volume identifier */ uint8_t security; /* security byte */ cchhb_t vtoc; /* VTOC address */ char res1[5]; /* reserved */ char cisize[4]; /* CI-size for FBA,... */ /* ...blanks for CKD */ char blkperci[4]; /* no of blocks per CI (FBA), blanks for CKD */ char labperci[4]; /* no of labels per CI (FBA), blanks for CKD */ char res2[4]; /* reserved */ char lvtoc[14]; /* owner code for LVTOC */ char res3[28]; /* reserved */ uint8_t ldl_version; /* version number, valid for ldl format */ uint64_t formatted_blocks; /* valid when ldl_version >= f2 */ } __attribute__ ((packed)) volume_label_t; typedef struct extent { uint8_t typeind; /* extent type indicator */ uint8_t seqno; /* extent sequence number */ cchh_t llimit; /* starting point of this extent */ cchh_t ulimit; /* ending point of this extent */ } __attribute__ ((packed)) extent_t; typedef struct dev_const { uint16_t DS4DSCYL; /* number of logical cyls */ uint16_t DS4DSTRK; /* number of tracks in a logical cylinder */ uint16_t DS4DEVTK; /* device track length */ uint8_t DS4DEVI; /* non-last keyed record overhead */ uint8_t DS4DEVL; /* last keyed record overhead */ uint8_t DS4DEVK; /* non-keyed record overhead differential */ uint8_t DS4DEVFG; /* flag byte */ uint16_t DS4DEVTL; /* device tolerance */ uint8_t DS4DEVDT; /* number of DSCB's per track */ uint8_t DS4DEVDB; /* number of directory blocks per track */ } __attribute__ ((packed)) dev_const_t; typedef struct format1_label { char DS1DSNAM[44]; /* data set name */ uint8_t DS1FMTID; /* format identifier */ char DS1DSSN[6]; /* data set serial number */ uint16_t DS1VOLSQ; /* volume sequence number */ labeldate_t DS1CREDT; /* creation date: ydd */ labeldate_t DS1EXPDT; /* expiration date */ uint8_t DS1NOEPV; /* number of extents on volume */ uint8_t DS1NOBDB; /* no. of bytes used in last direction blk */ uint8_t DS1FLAG1; /* flag 1 */ char DS1SYSCD[13]; /* system code */ labeldate_t DS1REFD; /* date last referenced */ uint8_t DS1SMSFG; /* system managed storage indicators */ uint8_t DS1SCXTF; /* sec. space extension flag byte */ uint16_t DS1SCXTV; /* secondary space extension value */ uint8_t DS1DSRG1; /* data set organisation byte 1 */ uint8_t DS1DSRG2; /* data set organisation byte 2 */ uint8_t DS1RECFM; /* record format */ uint8_t DS1OPTCD; /* option code */ uint16_t DS1BLKL; /* block length */ uint16_t DS1LRECL; /* record length */ uint8_t DS1KEYL; /* key length */ uint16_t DS1RKP; /* relative key position */ uint8_t DS1DSIND; /* data set indicators */ uint8_t DS1SCAL1; /* secondary allocation flag byte */ char DS1SCAL3[3]; /* secondary allocation quantity */ ttr_t DS1LSTAR; /* last used track and block on track */ uint16_t DS1TRBAL; /* space remaining on last used track */ uint16_t res1; /* reserved */ extent_t DS1EXT1; /* first extent description */ extent_t DS1EXT2; /* second extent description */ extent_t DS1EXT3; /* third extent description */ cchhb_t DS1PTRDS; /* possible pointer to f2 or f3 DSCB */ } __attribute__ ((packed)) format1_label_t; /* * struct dasd_information_t * represents any data about the data, which is visible to userspace */ typedef struct dasd_information_t { unsigned int devno; /* S/390 devno */ unsigned int real_devno; /* for aliases */ unsigned int schid; /* S/390 subchannel identifier */ unsigned int cu_type : 16; /* from SenseID */ unsigned int cu_model : 8; /* from SenseID */ unsigned int dev_type : 16; /* from SenseID */ unsigned int dev_model : 8; /* from SenseID */ unsigned int open_count; unsigned int req_queue_len; unsigned int chanq_len; /* length of chanq */ char type[4]; /* from discipline.name, 'none' for unknown */ unsigned int status; /* current device level */ unsigned int label_block; /* where to find the VOLSER */ unsigned int FBA_layout; /* fixed block size (like AIXVOL) */ unsigned int characteristics_size; unsigned int confdata_size; char characteristics[64]; /* from read_device_characteristics */ char configuration_data[256]; /* from read_configuration_data */ } dasd_information_t; #define DASD_IOCTL_LETTER 'D' #define BIODASDINFO _IOR(DASD_IOCTL_LETTER,1,dasd_information_t) #define BLKGETSIZE _IO(0x12,96) #define BLKSSZGET _IO(0x12,104) #define BLKGETSIZE64 _IOR(0x12,114,size_t) /* device size in bytes (u64 *arg)*/ #define LV_COMPAT_CYL 0xFFFE /* * Only compile this on S/390. Doesn't make any sense * for other architectures. */ static unsigned char EBCtoASC[256] = { /* 0x00 NUL SOH STX ETX *SEL HT *RNL DEL */ 0x00, 0x01, 0x02, 0x03, 0x07, 0x09, 0x07, 0x7F, /* 0x08 -GE -SPS -RPT VT FF CR SO SI */ 0x07, 0x07, 0x07, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, /* 0x10 DLE DC1 DC2 DC3 -RES -NL BS -POC -ENP ->LF */ 0x10, 0x11, 0x12, 0x13, 0x07, 0x0A, 0x08, 0x07, /* 0x18 CAN EM -UBS -CU1 -IFS -IGS -IRS -ITB -IUS */ 0x18, 0x19, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, /* 0x20 -DS -SOS FS -WUS -BYP LF ETB ESC -INP */ 0x07, 0x07, 0x1C, 0x07, 0x07, 0x0A, 0x17, 0x1B, /* 0x28 -SA -SFE -SM -CSP -MFA ENQ ACK BEL -SW */ 0x07, 0x07, 0x07, 0x07, 0x07, 0x05, 0x06, 0x07, /* 0x30 ---- ---- SYN -IR -PP -TRN -NBS EOT */ 0x07, 0x07, 0x16, 0x07, 0x07, 0x07, 0x07, 0x04, /* 0x38 -SBS -IT -RFF -CU3 DC4 NAK ---- SUB */ 0x07, 0x07, 0x07, 0x07, 0x14, 0x15, 0x07, 0x1A, /* 0x40 SP RSP ä ---- */ 0x20, 0xFF, 0x83, 0x84, 0x85, 0xA0, 0x07, 0x86, /* 0x48 . < ( + | */ 0x87, 0xA4, 0x9B, 0x2E, 0x3C, 0x28, 0x2B, 0x7C, /* 0x50 & ---- */ 0x26, 0x82, 0x88, 0x89, 0x8A, 0xA1, 0x8C, 0x07, /* 0x58 ß ! $ * ) ; */ 0x8D, 0xE1, 0x21, 0x24, 0x2A, 0x29, 0x3B, 0xAA, /* 0x60 - / ---- Ä ---- ---- ---- */ 0x2D, 0x2F, 0x07, 0x8E, 0x07, 0x07, 0x07, 0x8F, /* 0x68 ---- , % _ > ? */ 0x80, 0xA5, 0x07, 0x2C, 0x25, 0x5F, 0x3E, 0x3F, /* 0x70 --- ---- ---- ---- ---- ---- ---- */ 0x07, 0x90, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, /* 0x78 * ` : # @ ' = " */ 0x70, 0x60, 0x3A, 0x23, 0x40, 0x27, 0x3D, 0x22, /* 0x80 * a b c d e f g */ 0x07, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* 0x88 h i ---- ---- ---- */ 0x68, 0x69, 0xAE, 0xAF, 0x07, 0x07, 0x07, 0xF1, /* 0x90 ° j k l m n o p */ 0xF8, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, /* 0x98 q r ---- ---- */ 0x71, 0x72, 0xA6, 0xA7, 0x91, 0x07, 0x92, 0x07, /* 0xA0 ~ s t u v w x */ 0xE6, 0x7E, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, /* 0xA8 y z ---- ---- ---- ---- */ 0x79, 0x7A, 0xAD, 0xAB, 0x07, 0x07, 0x07, 0x07, /* 0xB0 ^ ---- § ---- */ 0x5E, 0x9C, 0x9D, 0xFA, 0x07, 0x07, 0x07, 0xAC, /* 0xB8 ---- [ ] ---- ---- ---- ---- */ 0xAB, 0x07, 0x5B, 0x5D, 0x07, 0x07, 0x07, 0x07, /* 0xC0 { A B C D E F G */ 0x7B, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 0xC8 H I ---- ö ---- */ 0x48, 0x49, 0x07, 0x93, 0x94, 0x95, 0xA2, 0x07, /* 0xD0 } J K L M N O P */ 0x7D, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, /* 0xD8 Q R ---- ü */ 0x51, 0x52, 0x07, 0x96, 0x81, 0x97, 0xA3, 0x98, /* 0xE0 \ S T U V W X */ 0x5C, 0xF6, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, /* 0xE8 Y Z ---- Ö ---- ---- ---- */ 0x59, 0x5A, 0xFD, 0x07, 0x99, 0x07, 0x07, 0x07, /* 0xF0 0 1 2 3 4 5 6 7 */ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 0xF8 8 9 ---- ---- Ü ---- ---- ---- */ 0x38, 0x39, 0x07, 0x07, 0x9A, 0x07, 0x07, 0x07 }; static inline void vtoc_ebcdic_dec (const char *source, char *target, int l) { int i; for (i = 0; i < l; i++) target[i]=(char)EBCtoASC[(unsigned char)(source[i])]; } /* * compute the block number from a * cyl-cyl-head-head structure */ static inline uint64_t cchh2blk (cchh_t *ptr, struct hd_geometry *geo) { uint64_t cyl; uint16_t head; /*decode cylinder and heads for large volumes */ cyl = ptr->hh & 0xFFF0; cyl <<= 12; cyl |= ptr->cc; head = ptr->hh & 0x000F; return cyl * geo->heads * geo->sectors + head * geo->sectors; } /* * compute the block number from a * cyl-cyl-head-head-block structure */ static inline uint64_t cchhb2blk (cchhb_t *ptr, struct hd_geometry *geo) { uint64_t cyl; uint16_t head; /*decode cylinder and heads for large volumes */ cyl = ptr->hh & 0xFFF0; cyl <<= 12; cyl |= ptr->cc; head = ptr->hh & 0x000F; return cyl * geo->heads * geo->sectors + head * geo->sectors + ptr->b; } #endif /* _DASD_H */ multipath-tools-0.7.4/kpartx/del-part-nodes.rules000066400000000000000000000021061320314174000220640ustar00rootroot00000000000000# These rules can delete partitions devnodes for slave devices # for certain aggregate devices such as multipath. # This is desirable to avoid confusion and keep the number # of device nodes and symlinks within limits. # # This is done only once on the first "add" or "change" event for # any given device. # # To suppress this, use the kernel parameter "dont_del_part_nodes", # or create an udev rule file that sets ENV{DONT_DEL_PART_NODES}="1". SUBSYSTEM!="block", GOTO="end_del_part_nodes" KERNEL!="sd*|dasd*|rbd*", GOTO="end_del_part_nodes" ACTION!="add|change", GOTO="end_del_part_nodes" IMPORT{cmdline}="dont_del_part_nodes" ENV{dont_del_part_nodes}=="1", GOTO="end_del_part_nodes" ENV{DONT_DEL_PART_NODES}=="1", GOTO="end_del_part_nodes" # dm-multipath ENV{DM_MULTIPATH_DEVICE_PATH}=="1", GOTO="del_part_nodes" # Other aggregate device types can be added here. GOTO="end_del_part_nodes" LABEL="del_part_nodes" IMPORT{db}="DM_DEL_PART_NODES" ENV{DM_DEL_PART_NODES}!="1", ENV{DM_DEL_PART_NODES}="1", \ RUN+="/usr/sbin/partx -d --nr 1-1024 $env{DEVNAME}" LABEL="end_del_part_nodes" multipath-tools-0.7.4/kpartx/devmapper.c000066400000000000000000000335761320314174000203400ustar00rootroot00000000000000/* * Copyright (c) 2004, 2005 Christophe Varoqui */ #include #include #include #include #include #include #include #include #include "devmapper.h" #define _UUID_PREFIX "part" #define UUID_PREFIX _UUID_PREFIX "%d-" #define _UUID_PREFIX_LEN (sizeof(_UUID_PREFIX) - 1) #define MAX_PREFIX_LEN (_UUID_PREFIX_LEN + 4) #define PARAMS_SIZE 1024 int dm_prereq(char * str, int x, int y, int z) { int r = 1; struct dm_task *dmt; struct dm_versions *target; struct dm_versions *last_target; if (!(dmt = dm_task_create(DM_DEVICE_LIST_VERSIONS))) return 1; dm_task_no_open_count(dmt); if (!dm_task_run(dmt)) goto out; target = dm_task_get_versions(dmt); /* Fetch targets and print 'em */ do { last_target = target; if (!strncmp(str, target->name, strlen(str)) && /* dummy prereq on multipath version */ target->version[0] >= x && target->version[1] >= y && target->version[2] >= z ) r = 0; target = (void *) target + target->next; } while (last_target != target); out: dm_task_destroy(dmt); return r; } int dm_simplecmd(int task, const char *name, int no_flush, uint16_t udev_flags) { int r = 0; int udev_wait_flag = (task == DM_DEVICE_RESUME || task == DM_DEVICE_REMOVE); #ifdef LIBDM_API_COOKIE uint32_t cookie = 0; #endif struct dm_task *dmt; if (!(dmt = dm_task_create(task))) return 0; if (!dm_task_set_name(dmt, name)) goto out; dm_task_no_open_count(dmt); dm_task_skip_lockfs(dmt); if (no_flush) dm_task_no_flush(dmt); #ifdef LIBDM_API_COOKIE if (!udev_sync) udev_flags |= DM_UDEV_DISABLE_LIBRARY_FALLBACK; if (udev_wait_flag && !dm_task_set_cookie(dmt, &cookie, udev_flags)) goto out; #endif r = dm_task_run(dmt); #ifdef LIBDM_API_COOKIE if (udev_wait_flag) dm_udev_wait(cookie); #endif out: dm_task_destroy(dmt); return r; } static void strip_slash (char * device) { char * p = device; while (*(p++) != 0x0) { if (*p == '/') *p = '!'; } } static int format_partname(char *buf, size_t bufsiz, const char *mapname, const char *delim, int part) { if (snprintf(buf, bufsiz, "%s%s%d", mapname, delim, part) >= bufsiz) return 0; strip_slash(buf); return 1; } static char *make_prefixed_uuid(int part, const char *uuid) { char *prefixed_uuid; int len = MAX_PREFIX_LEN + strlen(uuid) + 1; prefixed_uuid = malloc(len); if (!prefixed_uuid) { fprintf(stderr, "cannot create prefixed uuid : %s\n", strerror(errno)); return NULL; } snprintf(prefixed_uuid, len, UUID_PREFIX "%s", part, uuid); return prefixed_uuid; } int dm_addmap(int task, const char *name, const char *target, const char *params, uint64_t size, int ro, const char *uuid, int part, mode_t mode, uid_t uid, gid_t gid) { int r = 0; struct dm_task *dmt; char *prefixed_uuid = NULL; #ifdef LIBDM_API_COOKIE uint32_t cookie = 0; uint16_t udev_flags = 0; #endif if (!(dmt = dm_task_create (task))) return 0; if (!dm_task_set_name (dmt, name)) goto addout; if (!dm_task_add_target (dmt, 0, size, target, params)) goto addout; if (ro && !dm_task_set_ro (dmt)) goto addout; if (task == DM_DEVICE_CREATE && uuid) { prefixed_uuid = make_prefixed_uuid(part, uuid); if (prefixed_uuid == NULL) goto addout; if (!dm_task_set_uuid(dmt, prefixed_uuid)) goto addout; } if (!dm_task_set_mode(dmt, mode)) goto addout; if (!dm_task_set_uid(dmt, uid)) goto addout; if (!dm_task_set_gid(dmt, gid)) goto addout; dm_task_no_open_count(dmt); #ifdef LIBDM_API_COOKIE if (!udev_sync) udev_flags = DM_UDEV_DISABLE_LIBRARY_FALLBACK; if (task == DM_DEVICE_CREATE && !dm_task_set_cookie(dmt, &cookie, udev_flags)) goto addout; #endif r = dm_task_run (dmt); #ifdef LIBDM_API_COOKIE if (task == DM_DEVICE_CREATE) dm_udev_wait(cookie); #endif addout: dm_task_destroy (dmt); free(prefixed_uuid); return r; } static int dm_map_present(char *str, char **uuid) { int r = 0; struct dm_task *dmt; const char *uuidtmp; struct dm_info info; if (uuid) *uuid = NULL; if (!(dmt = dm_task_create(DM_DEVICE_INFO))) return 0; if (!dm_task_set_name(dmt, str)) goto out; dm_task_no_open_count(dmt); if (!dm_task_run(dmt)) goto out; if (!dm_task_get_info(dmt, &info)) goto out; if (!info.exists) goto out; r = 1; if (uuid) { uuidtmp = dm_task_get_uuid(dmt); if (uuidtmp && strlen(uuidtmp)) *uuid = strdup(uuidtmp); } out: dm_task_destroy(dmt); return r; } static int dm_rename (const char *old, const char *new) { int r = 0; struct dm_task *dmt; uint16_t udev_flags = DM_UDEV_DISABLE_LIBRARY_FALLBACK; uint32_t cookie = 0; dmt = dm_task_create(DM_DEVICE_RENAME); if (!dmt) return r; if (!dm_task_set_name(dmt, old) || !dm_task_set_newname(dmt, new) || !dm_task_no_open_count(dmt) || !dm_task_set_cookie(dmt, &cookie, udev_flags)) goto out; r = dm_task_run(dmt); dm_udev_wait(cookie); out: dm_task_destroy(dmt); return r; } static const char *dm_find_uuid(const char *uuid) { struct dm_task *dmt; const char *name = NULL, *tmp; if ((dmt = dm_task_create(DM_DEVICE_INFO)) == NULL) return NULL; if (!dm_task_set_uuid(dmt, uuid) || !dm_task_run(dmt)) goto out; tmp = dm_task_get_name(dmt); if (tmp != NULL && *tmp != '\0') name = strdup(tmp); out: dm_task_destroy(dmt); return name; } char * dm_mapname(int major, int minor) { struct dm_task *dmt; char *mapname = NULL; const char *map; if (!(dmt = dm_task_create(DM_DEVICE_INFO))) return NULL; dm_task_no_open_count(dmt); dm_task_set_major(dmt, major); dm_task_set_minor(dmt, minor); if (!dm_task_run(dmt)) goto out; map = dm_task_get_name(dmt); if (map && strlen(map)) mapname = strdup(map); out: dm_task_destroy(dmt); return mapname; } /* * dm_get_first_dep * * Return the device number of the first dependend device * for a given target. */ dev_t dm_get_first_dep(char *devname) { struct dm_task *dmt; struct dm_deps *dm_deps; dev_t ret = 0; if ((dmt = dm_task_create(DM_DEVICE_DEPS)) == NULL) { return ret; } if (!dm_task_set_name(dmt, devname)) { goto out; } if (!dm_task_run(dmt)) { goto out; } if ((dm_deps = dm_task_get_deps(dmt)) == NULL) { goto out; } if (dm_deps->count > 0) { ret = dm_deps->device[0]; } out: dm_task_destroy(dmt); return ret; } char * dm_mapuuid(const char *mapname) { struct dm_task *dmt; const char *tmp; char *uuid = NULL; if (!(dmt = dm_task_create(DM_DEVICE_INFO))) return NULL; if (!dm_task_set_name(dmt, mapname)) goto out; dm_task_no_open_count(dmt); if (!dm_task_run(dmt)) goto out; tmp = dm_task_get_uuid(dmt); if (tmp[0] != '\0') uuid = strdup(tmp); out: dm_task_destroy(dmt); return uuid; } int dm_devn (const char * mapname, int *major, int *minor) { int r = 1; struct dm_task *dmt; struct dm_info info; if (!(dmt = dm_task_create(DM_DEVICE_INFO))) return 0; if (!dm_task_set_name(dmt, mapname)) goto out; if (!dm_task_run(dmt)) goto out; if (!dm_task_get_info(dmt, &info) || info.exists == 0) goto out; *major = info.major; *minor = info.minor; r = 0; out: dm_task_destroy(dmt); return r; } static int dm_get_map(const char *mapname, char * outparams) { int r = 1; struct dm_task *dmt; uint64_t start, length; char *target_type = NULL; char *params = NULL; if (!(dmt = dm_task_create(DM_DEVICE_TABLE))) return 1; if (!dm_task_set_name(dmt, mapname)) goto out; dm_task_no_open_count(dmt); if (!dm_task_run(dmt)) goto out; /* Fetch 1st target */ dm_get_next_target(dmt, NULL, &start, &length, &target_type, ¶ms); if (snprintf(outparams, PARAMS_SIZE, "%s", params) <= PARAMS_SIZE) r = 0; out: dm_task_destroy(dmt); return r; } static int dm_get_opencount (const char * mapname) { int r = -1; struct dm_task *dmt; struct dm_info info; if (!(dmt = dm_task_create(DM_DEVICE_INFO))) return 0; if (!dm_task_set_name(dmt, mapname)) goto out; if (!dm_task_run(dmt)) goto out; if (!dm_task_get_info(dmt, &info)) goto out; if (!info.exists) goto out; r = info.open_count; out: dm_task_destroy(dmt); return r; } /* * returns: * 1 : match * 0 : no match * -1 : empty map */ static int dm_type(const char * name, char * type) { int r = 0; struct dm_task *dmt; uint64_t start, length; char *target_type = NULL; char *params; if (!(dmt = dm_task_create(DM_DEVICE_TABLE))) return 0; if (!dm_task_set_name(dmt, name)) goto out; dm_task_no_open_count(dmt); if (!dm_task_run(dmt)) goto out; /* Fetch 1st target */ if (dm_get_next_target(dmt, NULL, &start, &length, &target_type, ¶ms) != NULL) /* more than one target */ r = -1; else if (!target_type) r = -1; else if (!strcmp(target_type, type)) r = 1; out: dm_task_destroy(dmt); return r; } /* * returns: * 0 : if both uuids end with same suffix which starts with UUID_PREFIX * 1 : otherwise */ int dm_compare_uuid(const char *mapuuid, const char *partname) { char *partuuid; int r = 1; partuuid = dm_mapuuid(partname); if (!partuuid) return 1; if (!strncmp(partuuid, _UUID_PREFIX, _UUID_PREFIX_LEN)) { char *p = partuuid + _UUID_PREFIX_LEN; /* skip partition number */ while (isdigit(*p)) p++; if (p != partuuid + _UUID_PREFIX_LEN && *p == '-' && !strcmp(mapuuid, p + 1)) r = 0; } free(partuuid); return r; } struct remove_data { int verbose; }; static int do_foreach_partmaps (const char * mapname, const char *uuid, dev_t devt, int (*partmap_func)(const char *, void *), void *data) { struct dm_task *dmt; struct dm_names *names; struct remove_data *rd = data; unsigned next = 0; char params[PARAMS_SIZE]; int major, minor; char dev_t[32]; int r = 1; int is_dmdev = 1; if (!(dmt = dm_task_create(DM_DEVICE_LIST))) return 1; dm_task_no_open_count(dmt); if (!dm_task_run(dmt)) goto out; if (!(names = dm_task_get_names(dmt))) goto out; if (!names->dev) { r = 0; /* this is perfectly valid */ goto out; } if (dm_devn(mapname, &major, &minor) || (major != major(devt) || minor != minor(devt))) /* * The latter could happen if a dm device "/dev/mapper/loop0" * exits while kpartx is called on "/dev/loop0". */ is_dmdev = 0; sprintf(dev_t, "%d:%d", major(devt), minor(devt)); do { /* * skip our devmap */ if (is_dmdev && !strcmp(names->name, mapname)) goto next; /* * skip if we cannot fetch the map table from the kernel */ if (dm_get_map(names->name, ¶ms[0])) goto next; /* * skip if the table does not map over the multipath map */ if (!strstr(params, dev_t)) goto next; /* * skip if devmap target is not "linear" */ if (dm_type(names->name, "linear") != 1) { if (rd->verbose) printf("%s: is not a linear target. Not removing\n", names->name); goto next; } /* * skip if uuids don't match */ if (uuid && dm_compare_uuid(uuid, names->name)) { if (rd->verbose) printf("%s: is not a kpartx partition. Not removing\n", names->name); goto next; } if (partmap_func(names->name, data) != 0) goto out; next: next = names->next; names = (void *) names + next; } while (next); r = 0; out: dm_task_destroy (dmt); return r; } static int remove_partmap(const char *name, void *data) { struct remove_data *rd = (struct remove_data *)data; int r = 0; if (dm_get_opencount(name)) { if (rd->verbose) printf("%s is in use. Not removing", name); return 1; } if (!dm_simplecmd(DM_DEVICE_REMOVE, name, 0, 0)) { if (rd->verbose) printf("%s: failed to remove\n", name); r = 1; } else if (rd->verbose) printf("del devmap : %s\n", name); return r; } int dm_remove_partmaps (char * mapname, char *uuid, dev_t devt, int verbose) { struct remove_data rd = { verbose }; return do_foreach_partmaps(mapname, uuid, devt, remove_partmap, &rd); } int dm_find_part(const char *parent, const char *delim, int part, const char *parent_uuid, char *name, size_t namesiz, char **part_uuid, int verbose) { int r; char params[PARAMS_SIZE]; const char *tmp; char *uuid; int major, minor; char dev_t[32]; if (!format_partname(name, namesiz, parent, delim, part)) { if (verbose) fprintf(stderr, "partname too small\n"); return 0; } r = dm_map_present(name, part_uuid); if (r == 1 || parent_uuid == NULL || *parent_uuid == '\0') return r; uuid = make_prefixed_uuid(part, parent_uuid); if (!uuid) return 0; tmp = dm_find_uuid(uuid); if (tmp == NULL) return r; /* Sanity check on partition, see dm_foreach_partmaps */ if (dm_type(tmp, "linear") != 1) goto out; /* * Try nondm uuid first. That way we avoid confusing * a device name with a device mapper name. */ if (!nondm_parse_uuid(parent_uuid, &major, &minor) && dm_devn(parent, &major, &minor)) goto out; snprintf(dev_t, sizeof(dev_t), "%d:%d", major, minor); if (dm_get_map(tmp, params)) goto out; if (!strstr(params, dev_t)) goto out; if (verbose) fprintf(stderr, "found map %s for uuid %s, renaming to %s\n", tmp, uuid, name); r = dm_rename(tmp, name); if (r == 0) { free(uuid); if (verbose) fprintf(stderr, "renaming %s->%s failed\n", tmp, name); } else *part_uuid = uuid; out: free((void*)tmp); return r; } char *nondm_create_uuid(dev_t devt) { #define NONDM_UUID_BUFLEN (34 + sizeof(NONDM_UUID_PREFIX) + \ sizeof(NONDM_UUID_SUFFIX)) static char uuid_buf[NONDM_UUID_BUFLEN]; snprintf(uuid_buf, sizeof(uuid_buf), "%s_%u:%u_%s", NONDM_UUID_PREFIX, major(devt), minor(devt), NONDM_UUID_SUFFIX); uuid_buf[NONDM_UUID_BUFLEN-1] = '\0'; return uuid_buf; } int nondm_parse_uuid(const char *uuid, int *major, int *minor) { const char *p; char *e; int ma, mi; if (strncmp(uuid, NONDM_UUID_PREFIX "_", sizeof(NONDM_UUID_PREFIX))) return 0; p = uuid + sizeof(NONDM_UUID_PREFIX); ma = strtoul(p, &e, 10); if (e == p || *e != ':') return 0; p = e + 1; mi = strtoul(p, &e, 10); if (e == p || *e != '_') return 0; p = e + 1; if (strcmp(p, NONDM_UUID_SUFFIX)) return 0; *major = ma; *minor = mi; return 1; } multipath-tools-0.7.4/kpartx/devmapper.h000066400000000000000000000026421320314174000203330ustar00rootroot00000000000000#ifndef _KPARTX_DEVMAPPER_H #define _KPARTX_DEVMAPPER_H #ifdef DM_SUBSYSTEM_UDEV_FLAG0 #define MPATH_UDEV_RELOAD_FLAG DM_SUBSYSTEM_UDEV_FLAG0 #else #define MPATH_UDEV_RELOAD_FLAG 0 #endif extern int udev_sync; int dm_prereq (char *, int, int, int); int dm_simplecmd (int, const char *, int, uint16_t); int dm_addmap (int, const char *, const char *, const char *, uint64_t, int, const char *, int, mode_t, uid_t, gid_t); char * dm_mapname(int major, int minor); dev_t dm_get_first_dep(char *devname); char * dm_mapuuid(const char *mapname); int dm_devn (const char * mapname, int *major, int *minor); int dm_remove_partmaps (char * mapname, char *uuid, dev_t devt, int verbose); int dm_find_part(const char *parent, const char *delim, int part, const char *parent_uuid, char *name, size_t namesiz, char **part_uuid, int verbose); /* * UUID format for partitions created on non-DM devices * ${UUID_PREFIX}devnode_${MAJOR}:${MINOR}_${NONDM_UUID_SUFFIX}" * where ${UUID_PREFIX} is "part${PARTNO}-" (see devmapper.c). * * The suffix should be sufficiently unique to avoid incidental conflicts; * the value below is a base64-encoded random number. * The UUID format shouldn't be changed between kpartx releases. */ #define NONDM_UUID_PREFIX "devnode" #define NONDM_UUID_SUFFIX "Wh5pYvM" char *nondm_create_uuid(dev_t devt); int nondm_parse_uuid(const char *uuid, int *major, int *minor); #endif /* _KPARTX_DEVMAPPER_H */ multipath-tools-0.7.4/kpartx/dm-parts.rules000066400000000000000000000025701320314174000210020ustar00rootroot00000000000000# Rules for partitions created by kpartx KERNEL!="dm-*", GOTO="dm_parts_end" ACTION!="add|change", GOTO="dm_parts_end" ENV{DM_UUID}!="part[0-9]*", GOTO="dm_parts_end" # We must take care that symlinks don't get lost, # even if blkid fails in 13-dm-disk.rules later. # # Fixme: we have currently no way to avoid calling blkid on # partitions of broken mpath maps such as DM_NOSCAN. # But when partition devices appear, kpartx has likely read # the partition table shortly before, so odds are not bad # that blkid will also succeed. IMPORT{db}="ID_FS_USAGE" IMPORT{db}="ID_FS_UUID_ENC" IMPORT{db}="ID_FS_LABEL_ENC" IMPORT{db}="ID_PART_ENTRY_NAME" IMPORT{db}="ID_PART_ENTRY_UUID" IMPORT{db}="ID_PART_ENTRY_SCHEME" # Maps should take precedence over their members. ENV{DM_UDEV_LOW_PRIORITY_FLAG}!="1", OPTIONS+="link_priority=50" # Set some additional symlinks that typically exist for mpath # path members, too, and should be overridden. # # kpartx_id is very robust, it works for suspended maps and maps # with 0 dependencies. It sets DM_TYPE, DM_PART, DM_WWN IMPORT{program}=="kpartx_id %M %m $env{DM_UUID}" # DM_TYPE only has a reasonable value for partitions on multipath. ENV{DM_UUID}=="*-mpath-*", ENV{DM_TYPE}=="?*" \ SYMLINK+="disk/by-id/$env{DM_TYPE}-$env{DM_NAME}" ENV{DM_WWN}=="?*", ENV{DM_PART}=="?*", \ SYMLINK+="disk/by-id/wwn-$env{DM_WWN}-part$env{DM_PART}" LABEL="dm_parts_end" multipath-tools-0.7.4/kpartx/dos.c000066400000000000000000000047721320314174000171360ustar00rootroot00000000000000/* * Source: copy of util-linux' partx dos.c * * Copyrights of the original file apply * Copyright (c) 2005 Bastian Blank */ #include "kpartx.h" #include "byteorder.h" #include #include #include "dos.h" static int is_extended(int type) { return (type == 5 || type == 0xf || type == 0x85); } static int read_extended_partition(int fd, struct partition *ep, int en, struct slice *sp, int ns) { struct partition p; unsigned long start, here, next; unsigned char *bp; int loopct = 0; int moretodo = 1; int i, n=0; int sector_size_mul = get_sector_size(fd)/512; next = start = sector_size_mul * le32_to_cpu(ep->start_sect); while (moretodo) { here = next; moretodo = 0; if (++loopct > 100) return n; bp = (unsigned char *)getblock(fd, here); if (bp == NULL) return n; if (bp[510] != 0x55 || bp[511] != 0xaa) return n; for (i=0; i<2; i++) { memcpy(&p, bp + 0x1be + i * sizeof (p), sizeof (p)); if (is_extended(p.sys_type)) { if (p.start_sect && p.nr_sects && !moretodo) { next = start + sector_size_mul * le32_to_cpu(p.start_sect); moretodo = 1; } continue; } if (n < ns) { sp[n].start = here + sector_size_mul * le32_to_cpu(p.start_sect); sp[n].size = sector_size_mul * le32_to_cpu(p.nr_sects); sp[n].container = en + 1; n++; } else { fprintf(stderr, "dos_extd_partition: too many slices\n"); return n; } loopct = 0; } } return n; } static int is_gpt(int type) { return (type == 0xEE); } int read_dos_pt(int fd, struct slice all, struct slice *sp, int ns) { struct partition p; unsigned long offset = all.start; int i, n=4; unsigned char *bp; uint64_t sector_size_mul = get_sector_size(fd)/512; bp = (unsigned char *)getblock(fd, offset); if (bp == NULL) return -1; if (bp[510] != 0x55 || bp[511] != 0xaa) return -1; for (i=0; i<4; i++) { memcpy(&p, bp + 0x1be + i * sizeof (p), sizeof (p)); if (is_gpt(p.sys_type)) return 0; if (i < ns) { sp[i].start = sector_size_mul * le32_to_cpu(p.start_sect); sp[i].size = sector_size_mul * le32_to_cpu(p.nr_sects); } else { fprintf(stderr, "dos_partition: too many slices\n"); break; } if (is_extended(p.sys_type)) { /* extended partitions only get one or two sectors mapped for LILO to install, whichever is needed to have 1kb of space */ if (sector_size_mul == 1) sp[i].size = 2; else sp[i].size = sector_size_mul; n += read_extended_partition(fd, &p, i, sp+n, ns-n); } } return n; } multipath-tools-0.7.4/kpartx/dos.h000066400000000000000000000004531320314174000171330ustar00rootroot00000000000000#ifndef DOS_H_INCLUDED #define DOS_H_INCLUDED struct partition { unsigned char boot_ind; /* 0x80 - active */ unsigned char bh, bs, bc; unsigned char sys_type; unsigned char eh, es, ec; unsigned int start_sect; unsigned int nr_sects; } __attribute__((packed)); #endif /* DOS_H_INCLUDED */ multipath-tools-0.7.4/kpartx/efi.h000066400000000000000000000033301320314174000171060ustar00rootroot00000000000000/* efi.[ch] - Manipulates EFI variables as exported in /proc/efi/vars Copyright (C) 2001 Dell Computer Corporation 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, see . */ #ifndef EFI_H #define EFI_H /* * Extensible Firmware Interface * Based on 'Extensible Firmware Interface Specification' * version 1.02, 12 December, 2000 */ #include #include typedef struct { uint8_t b[16]; } efi_guid_t; #define EFI_GUID(a,b,c,d0,d1,d2,d3,d4,d5,d6,d7) \ ((efi_guid_t) \ {{ (a) & 0xff, ((a) >> 8) & 0xff, ((a) >> 16) & 0xff, ((a) >> 24) & 0xff, \ (b) & 0xff, ((b) >> 8) & 0xff, \ (c) & 0xff, ((c) >> 8) & 0xff, \ (d0), (d1), (d2), (d3), (d4), (d5), (d6), (d7) }}) /****************************************************** * GUIDs ******************************************************/ #define NULL_GUID \ EFI_GUID( 0x00000000, 0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00) static inline int efi_guidcmp(efi_guid_t left, efi_guid_t right) { return memcmp(&left, &right, sizeof (efi_guid_t)); } typedef uint16_t efi_char16_t; /* UNICODE character */ #endif /* EFI_H */ multipath-tools-0.7.4/kpartx/gpt.c000066400000000000000000000427551320314174000171460ustar00rootroot00000000000000/* gpt.[ch] Copyright (C) 2000-2001 Dell Computer Corporation EFI GUID Partition Table handling Per Intel EFI Specification v1.02 http://developer.intel.com/technology/efi/efi.htm 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, see . */ #define _FILE_OFFSET_BITS 64 #include "gpt.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "crc32.h" #include "kpartx.h" #if BYTE_ORDER == LITTLE_ENDIAN # define __le16_to_cpu(x) (x) # define __le32_to_cpu(x) (x) # define __le64_to_cpu(x) (x) # define __cpu_to_le32(x) (x) #elif BYTE_ORDER == BIG_ENDIAN # define __le16_to_cpu(x) bswap_16(x) # define __le32_to_cpu(x) bswap_32(x) # define __le64_to_cpu(x) bswap_64(x) # define __cpu_to_le32(x) bswap_32(x) #endif #ifndef BLKGETLASTSECT #define BLKGETLASTSECT _IO(0x12,108) /* get last sector of block device */ #endif #ifndef BLKGETSIZE #define BLKGETSIZE _IO(0x12,96) /* return device size */ #endif #ifndef BLKSSZGET #define BLKSSZGET _IO(0x12,104) /* get block device sector size */ #endif #ifndef BLKGETSIZE64 #define BLKGETSIZE64 _IOR(0x12,114,sizeof(uint64_t)) /* return device size in bytes (u64 *arg) */ #endif struct blkdev_ioctl_param { unsigned int block; size_t content_length; char * block_contents; }; /** * efi_crc32() - EFI version of crc32 function * @buf: buffer to calculate crc32 of * @len - length of buf * * Description: Returns EFI-style CRC32 value for @buf * * This function uses the little endian Ethernet polynomial * but seeds the function with ~0, and xor's with ~0 at the end. * Note, the EFI Specification, v1.02, has a reference to * Dr. Dobbs Journal, May 1994 (actually it's in May 1992). */ static inline uint32_t efi_crc32(const void *buf, unsigned long len) { return (crc32(~0L, buf, len) ^ ~0L); } /** * is_pmbr_valid(): test Protective MBR for validity * @mbr: pointer to a legacy mbr structure * * Description: Returns 1 if PMBR is valid, 0 otherwise. * Validity depends on two things: * 1) MSDOS signature is in the last two bytes of the MBR * 2) One partition of type 0xEE is found */ static int is_pmbr_valid(legacy_mbr *mbr) { int i, found = 0, signature = 0; if (!mbr) return 0; signature = (__le16_to_cpu(mbr->signature) == MSDOS_MBR_SIGNATURE); for (i = 0; signature && i < 4; i++) { if (mbr->partition[i].sys_type == EFI_PMBR_OSTYPE_EFI_GPT) { found = 1; break; } } return (signature && found); } /************************************************************ * _get_num_sectors * Requires: * - filedes is an open file descriptor, suitable for reading * Modifies: nothing * Returns: * Last LBA value on success * 0 on error * * Try getting BLKGETSIZE64 and BLKSSZGET first, * then BLKGETSIZE if necessary. * Kernels 2.4.15-2.4.18 and 2.5.0-2.5.3 have a broken BLKGETSIZE64 * which returns the number of 512-byte sectors, not the size of * the disk in bytes. Fixed in kernels 2.4.18-pre8 and 2.5.4-pre3. ************************************************************/ static uint64_t _get_num_sectors(int filedes) { int rc; uint64_t bytes=0; rc = ioctl(filedes, BLKGETSIZE64, &bytes); if (!rc) return bytes / get_sector_size(filedes); return 0; } /************************************************************ * last_lba(): return number of last logical block of device * * @fd * * Description: returns Last LBA value on success, 0 on error. * Notes: The value st_blocks gives the size of the file * in 512-byte blocks, which is OK if * EFI_BLOCK_SIZE_SHIFT == 9. ************************************************************/ static uint64_t last_lba(int filedes) { int rc; uint64_t sectors = 0; struct stat s; memset(&s, 0, sizeof (s)); rc = fstat(filedes, &s); if (rc == -1) { fprintf(stderr, "last_lba() could not stat: %s\n", strerror(errno)); return 0; } if (S_ISBLK(s.st_mode)) { sectors = _get_num_sectors(filedes); } else { fprintf(stderr, "last_lba(): I don't know how to handle files with mode %x\n", s.st_mode); sectors = 1; } return sectors ? sectors - 1 : 0; } static ssize_t read_lastoddsector(int fd, uint64_t lba, void *buffer, size_t count) { int rc; struct blkdev_ioctl_param ioctl_param; if (!buffer) return 0; ioctl_param.block = 0; /* read the last sector */ ioctl_param.content_length = count; ioctl_param.block_contents = buffer; rc = ioctl(fd, BLKGETLASTSECT, &ioctl_param); if (rc == -1) perror("read failed"); return !rc; } static ssize_t read_lba(int fd, uint64_t lba, void *buffer, size_t bytes) { int sector_size = get_sector_size(fd); off_t offset = lba * sector_size; uint64_t lastlba; ssize_t bytesread; if (lseek(fd, offset, SEEK_SET) < 0) return 0; bytesread = read(fd, buffer, bytes); lastlba = last_lba(fd); if (!lastlba) return bytesread; /* Kludge. This is necessary to read/write the last block of an odd-sized disk, until Linux 2.5.x kernel fixes. This is only used by gpt.c, and only to read one sector, so we don't have to be fancy. */ if (!bytesread && !(lastlba & 1) && lba == lastlba) { bytesread = read_lastoddsector(fd, lba, buffer, bytes); } return bytesread; } /** * alloc_read_gpt_entries(): reads partition entries from disk * @fd is an open file descriptor to the whole disk * @gpt is a buffer into which the GPT will be put * Description: Returns ptes on success, NULL on error. * Allocates space for PTEs based on information found in @gpt. * Notes: remember to free pte when you're done! */ static gpt_entry * alloc_read_gpt_entries(int fd, gpt_header * gpt) { gpt_entry *pte; size_t count = __le32_to_cpu(gpt->num_partition_entries) * __le32_to_cpu(gpt->sizeof_partition_entry); if (!count) return NULL; pte = (gpt_entry *)malloc(count); if (!pte) return NULL; memset(pte, 0, count); if (!read_lba(fd, __le64_to_cpu(gpt->partition_entry_lba), pte, count)) { free(pte); return NULL; } return pte; } /** * alloc_read_gpt_header(): Allocates GPT header, reads into it from disk * @fd is an open file descriptor to the whole disk * @lba is the Logical Block Address of the partition table * * Description: returns GPT header on success, NULL on error. Allocates * and fills a GPT header starting at @ from @bdev. * Note: remember to free gpt when finished with it. */ static gpt_header * alloc_read_gpt_header(int fd, uint64_t lba) { gpt_header *gpt; gpt = (gpt_header *) malloc(sizeof (gpt_header)); if (!gpt) return NULL; memset(gpt, 0, sizeof (*gpt)); if (!read_lba(fd, lba, gpt, sizeof (gpt_header))) { free(gpt); return NULL; } return gpt; } /** * is_gpt_valid() - tests one GPT header and PTEs for validity * @fd is an open file descriptor to the whole disk * @lba is the logical block address of the GPT header to test * @gpt is a GPT header ptr, filled on return. * @ptes is a PTEs ptr, filled on return. * * Description: returns 1 if valid, 0 on error. * If valid, returns pointers to newly allocated GPT header and PTEs. */ static int is_gpt_valid(int fd, uint64_t lba, gpt_header ** gpt, gpt_entry ** ptes) { int rc = 0; /* default to not valid */ uint32_t crc, origcrc; if (!gpt || !ptes) return 0; if (!(*gpt = alloc_read_gpt_header(fd, lba))) return 0; /* Check the GUID Partition Table signature */ if (__le64_to_cpu((*gpt)->signature) != GPT_HEADER_SIGNATURE) { /* printf("GUID Partition Table Header signature is wrong: %" PRIx64" != %" PRIx64 "\n", __le64_to_cpu((*gpt)->signature), GUID_PT_HEADER_SIGNATURE); */ free(*gpt); *gpt = NULL; return rc; } /* Check the GUID Partition Table Header CRC */ origcrc = __le32_to_cpu((*gpt)->header_crc32); (*gpt)->header_crc32 = 0; crc = efi_crc32(*gpt, __le32_to_cpu((*gpt)->header_size)); if (crc != origcrc) { // printf( "GPTH CRC check failed, %x != %x.\n", origcrc, crc); (*gpt)->header_crc32 = __cpu_to_le32(origcrc); free(*gpt); *gpt = NULL; return 0; } (*gpt)->header_crc32 = __cpu_to_le32(origcrc); /* Check that the my_lba entry points to the LBA * that contains the GPT we read */ if (__le64_to_cpu((*gpt)->my_lba) != lba) { /* printf( "my_lba % PRIx64 "x != lba %"PRIx64 "x.\n", __le64_to_cpu((*gpt)->my_lba), lba); */ free(*gpt); *gpt = NULL; return 0; } /* Check that sizeof_partition_entry has the correct value */ if (__le32_to_cpu((*gpt)->sizeof_partition_entry) != sizeof(gpt_entry)) { // printf("GUID partition entry size check failed.\n"); free(*gpt); *gpt = NULL; return 0; } /* Check that sizeof_partition_entry has the correct value */ if (__le32_to_cpu((*gpt)->sizeof_partition_entry) != sizeof(gpt_entry)) { // printf("GUID partition entry size check failed.\n"); free(*gpt); *gpt = NULL; return 0; } if (!(*ptes = alloc_read_gpt_entries(fd, *gpt))) { free(*gpt); *gpt = NULL; return 0; } /* Check the GUID Partition Entry Array CRC */ crc = efi_crc32(*ptes, __le32_to_cpu((*gpt)->num_partition_entries) * __le32_to_cpu((*gpt)->sizeof_partition_entry)); if (crc != __le32_to_cpu((*gpt)->partition_entry_array_crc32)) { // printf("GUID Partitition Entry Array CRC check failed.\n"); free(*gpt); *gpt = NULL; free(*ptes); *ptes = NULL; return 0; } /* We're done, all's well */ return 1; } /** * compare_gpts() - Search disk for valid GPT headers and PTEs * @pgpt is the primary GPT header * @agpt is the alternate GPT header * @lastlba is the last LBA number * Description: Returns nothing. Sanity checks pgpt and agpt fields * and prints warnings on discrepancies. * */ static void compare_gpts(gpt_header *pgpt, gpt_header *agpt, uint64_t lastlba) { int error_found = 0; if (!pgpt || !agpt) return; if (__le64_to_cpu(pgpt->my_lba) != __le64_to_cpu(agpt->alternate_lba)) { error_found++; fprintf(stderr, "GPT:Primary header LBA != Alt. header alternate_lba\n"); #ifdef DEBUG fprintf(stderr, "GPT:%" PRIx64 " != %" PRIx64 "\n", __le64_to_cpu(pgpt->my_lba), __le64_to_cpu(agpt->alternate_lba)); #endif } if (__le64_to_cpu(pgpt->alternate_lba) != __le64_to_cpu(agpt->my_lba)) { error_found++; fprintf(stderr, "GPT:Primary header alternate_lba != Alt. header my_lba\n"); #ifdef DEBUG fprintf(stderr, "GPT:%" PRIx64 " != %" PRIx64 "\n", __le64_to_cpu(pgpt->alternate_lba), __le64_to_cpu(agpt->my_lba)); #endif } if (__le64_to_cpu(pgpt->first_usable_lba) != __le64_to_cpu(agpt->first_usable_lba)) { error_found++; fprintf(stderr, "GPT:first_usable_lbas don't match.\n"); #ifdef DEBUG fprintf(stderr, "GPT:%" PRIx64 " != %" PRIx64 "\n", __le64_to_cpu(pgpt->first_usable_lba), __le64_to_cpu(agpt->first_usable_lba)); #endif } if (__le64_to_cpu(pgpt->last_usable_lba) != __le64_to_cpu(agpt->last_usable_lba)) { error_found++; fprintf(stderr, "GPT:last_usable_lbas don't match.\n"); #ifdef DEBUG fprintf(stderr, "GPT:%" PRIx64 " != %" PRIx64 "\n", __le64_to_cpu(pgpt->last_usable_lba), __le64_to_cpu(agpt->last_usable_lba)); #endif } if (efi_guidcmp(pgpt->disk_guid, agpt->disk_guid)) { error_found++; fprintf(stderr, "GPT:disk_guids don't match.\n"); } if (__le32_to_cpu(pgpt->num_partition_entries) != __le32_to_cpu(agpt->num_partition_entries)) { error_found++; fprintf(stderr, "GPT:num_partition_entries don't match: " "0x%x != 0x%x\n", __le32_to_cpu(pgpt->num_partition_entries), __le32_to_cpu(agpt->num_partition_entries)); } if (__le32_to_cpu(pgpt->sizeof_partition_entry) != __le32_to_cpu(agpt->sizeof_partition_entry)) { error_found++; fprintf(stderr, "GPT:sizeof_partition_entry values don't match: " "0x%x != 0x%x\n", __le32_to_cpu(pgpt->sizeof_partition_entry), __le32_to_cpu(agpt->sizeof_partition_entry)); } if (__le32_to_cpu(pgpt->partition_entry_array_crc32) != __le32_to_cpu(agpt->partition_entry_array_crc32)) { error_found++; fprintf(stderr, "GPT:partition_entry_array_crc32 values don't match: " "0x%x != 0x%x\n", __le32_to_cpu(pgpt->partition_entry_array_crc32), __le32_to_cpu(agpt->partition_entry_array_crc32)); } if (__le64_to_cpu(pgpt->alternate_lba) != lastlba) { error_found++; fprintf(stderr, "GPT:Primary header thinks Alt. header is not at the end of the disk.\n"); #ifdef DEBUG fprintf(stderr, "GPT:%" PRIx64 " != %" PRIx64 "\n", __le64_to_cpu(pgpt->alternate_lba), lastlba); #endif } if (__le64_to_cpu(agpt->my_lba) != lastlba) { error_found++; fprintf(stderr, "GPT:Alternate GPT header not at the end of the disk.\n"); #ifdef DEBUG fprintf(stderr, "GPT:%" PRIx64 " != %" PRIx64 "\n", __le64_to_cpu(agpt->my_lba), lastlba); #endif } if (error_found) fprintf(stderr, "GPT: Use GNU Parted to correct GPT errors.\n"); return; } /** * find_valid_gpt() - Search disk for valid GPT headers and PTEs * @fd is an open file descriptor to the whole disk * @gpt is a GPT header ptr, filled on return. * @ptes is a PTEs ptr, filled on return. * Description: Returns 1 if valid, 0 on error. * If valid, returns pointers to newly allocated GPT header and PTEs. * Validity depends on finding either the Primary GPT header and PTEs valid, * or the Alternate GPT header and PTEs valid, and the PMBR valid. */ static int find_valid_gpt(int fd, gpt_header ** gpt, gpt_entry ** ptes) { int good_pgpt = 0, good_agpt = 0, good_pmbr = 0; gpt_header *pgpt = NULL, *agpt = NULL; gpt_entry *pptes = NULL, *aptes = NULL; legacy_mbr *legacymbr = NULL; uint64_t lastlba; if (!gpt || !ptes) return 0; if (!(lastlba = last_lba(fd))) return 0; good_pgpt = is_gpt_valid(fd, GPT_PRIMARY_PARTITION_TABLE_LBA, &pgpt, &pptes); if (good_pgpt) { good_agpt = is_gpt_valid(fd, __le64_to_cpu(pgpt->alternate_lba), &agpt, &aptes); if (!good_agpt) { good_agpt = is_gpt_valid(fd, lastlba, &agpt, &aptes); } } else { good_agpt = is_gpt_valid(fd, lastlba, &agpt, &aptes); } /* The obviously unsuccessful case */ if (!good_pgpt && !good_agpt) { goto fail; } /* This will be added to the EFI Spec. per Intel after v1.02. */ legacymbr = malloc(sizeof (*legacymbr)); if (legacymbr) { memset(legacymbr, 0, sizeof (*legacymbr)); read_lba(fd, 0, (uint8_t *) legacymbr, sizeof (*legacymbr)); good_pmbr = is_pmbr_valid(legacymbr); free(legacymbr); legacymbr=NULL; } /* Failure due to bad PMBR */ if ((good_pgpt || good_agpt) && !good_pmbr && !force_gpt) { fprintf(stderr, " Warning: Disk has a valid GPT signature " "but invalid PMBR.\n" " Assuming this disk is *not* a GPT disk anymore.\n" " Use gpt kernel option to override. " "Use GNU Parted to correct disk.\n"); goto fail; } /* Would fail due to bad PMBR, but force GPT anyhow */ if ((good_pgpt || good_agpt) && !good_pmbr && force_gpt) { fprintf(stderr, " Warning: Disk has a valid GPT signature but " "invalid PMBR.\n" " Use GNU Parted to correct disk.\n" " gpt option taken, disk treated as GPT.\n"); } compare_gpts(pgpt, agpt, lastlba); /* The good cases */ if (good_pgpt && (good_pmbr || force_gpt)) { *gpt = pgpt; *ptes = pptes; if (agpt) { free(agpt); agpt = NULL; } if (aptes) { free(aptes); aptes = NULL; } if (!good_agpt) { fprintf(stderr, "Alternate GPT is invalid, " "using primary GPT.\n"); } return 1; } else if (good_agpt && (good_pmbr || force_gpt)) { *gpt = agpt; *ptes = aptes; if (pgpt) { free(pgpt); pgpt = NULL; } if (pptes) { free(pptes); pptes = NULL; } fprintf(stderr, "Primary GPT is invalid, using alternate GPT.\n"); return 1; } fail: if (pgpt) { free(pgpt); pgpt=NULL; } if (agpt) { free(agpt); agpt=NULL; } if (pptes) { free(pptes); pptes=NULL; } if (aptes) { free(aptes); aptes=NULL; } *gpt = NULL; *ptes = NULL; return 0; } /** * read_gpt_pt() * @fd * @all - slice with start/size of whole disk * * 0 if this isn't our partition table * number of partitions if successful * */ int read_gpt_pt (int fd, struct slice all, struct slice *sp, int ns) { gpt_header *gpt = NULL; gpt_entry *ptes = NULL; uint32_t i; int n = 0; int last_used_index=-1; int sector_size_mul = get_sector_size(fd)/512; if (!find_valid_gpt (fd, &gpt, &ptes) || !gpt || !ptes) { if (gpt) free (gpt); if (ptes) free (ptes); return 0; } for (i = 0; i < __le32_to_cpu(gpt->num_partition_entries) && i < ns; i++) { if (!efi_guidcmp (NULL_GUID, ptes[i].partition_type_guid)) { sp[n].start = 0; sp[n].size = 0; n++; } else { sp[n].start = sector_size_mul * __le64_to_cpu(ptes[i].starting_lba); sp[n].size = sector_size_mul * (__le64_to_cpu(ptes[i].ending_lba) - __le64_to_cpu(ptes[i].starting_lba) + 1); last_used_index=n; n++; } } free (ptes); free (gpt); return last_used_index+1; } multipath-tools-0.7.4/kpartx/gpt.h000066400000000000000000000072241320314174000171430ustar00rootroot00000000000000/* gpt.[ch] Copyright (C) 2000-2001 Dell Computer Corporation EFI GUID Partition Table handling Per Intel EFI Specification v1.02 http://developer.intel.com/technology/efi/efi.htm 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, see . */ #ifndef _GPT_H #define _GPT_H #include #include "kpartx.h" #include "dos.h" #include "efi.h" #define EFI_PMBR_OSTYPE_EFI 0xEF #define EFI_PMBR_OSTYPE_EFI_GPT 0xEE #define MSDOS_MBR_SIGNATURE 0xaa55 #define GPT_BLOCK_SIZE 512 #define GPT_HEADER_SIGNATURE 0x5452415020494645ULL #define GPT_HEADER_REVISION_V1_02 0x00010200 #define GPT_HEADER_REVISION_V1_00 0x00010000 #define GPT_HEADER_REVISION_V0_99 0x00009900 #define GPT_PRIMARY_PARTITION_TABLE_LBA 1 typedef struct _gpt_header { uint64_t signature; uint32_t revision; uint32_t header_size; uint32_t header_crc32; uint32_t reserved1; uint64_t my_lba; uint64_t alternate_lba; uint64_t first_usable_lba; uint64_t last_usable_lba; efi_guid_t disk_guid; uint64_t partition_entry_lba; uint32_t num_partition_entries; uint32_t sizeof_partition_entry; uint32_t partition_entry_array_crc32; uint8_t reserved2[GPT_BLOCK_SIZE - 92]; } __attribute__ ((packed)) gpt_header; typedef struct _gpt_entry_attributes { uint64_t required_to_function:1; uint64_t reserved:47; uint64_t type_guid_specific:16; } __attribute__ ((packed)) gpt_entry_attributes; typedef struct _gpt_entry { efi_guid_t partition_type_guid; efi_guid_t unique_partition_guid; uint64_t starting_lba; uint64_t ending_lba; gpt_entry_attributes attributes; efi_char16_t partition_name[72 / sizeof(efi_char16_t)]; } __attribute__ ((packed)) gpt_entry; /* These values are only defaults. The actual on-disk structures may define different sizes, so use those unless creating a new GPT disk! */ #define GPT_DEFAULT_RESERVED_PARTITION_ENTRY_ARRAY_SIZE 16384 /* Number of actual partition entries should be calculated as: */ #define GPT_DEFAULT_RESERVED_PARTITION_ENTRIES \ (GPT_DEFAULT_RESERVED_PARTITION_ENTRY_ARRAY_SIZE / \ sizeof(gpt_entry)) /* Protected Master Boot Record & Legacy MBR share same structure */ /* Needs to be packed because the u16s force misalignment. */ typedef struct _legacy_mbr { uint8_t bootcode[440]; uint32_t unique_mbr_signature; uint16_t unknown; struct partition partition[4]; uint16_t signature; } __attribute__ ((packed)) legacy_mbr; #define EFI_GPT_PRIMARY_PARTITION_TABLE_LBA 1 /* Functions */ int read_gpt_pt (int fd, struct slice all, struct slice *sp, int ns); #endif /* * Overrides for Emacs so that we follow Linus's tabbing style. * Emacs will notice this stuff at the end of the file and automatically * adjust the settings for this buffer only. This must remain at the end * of the file. * --------------------------------------------------------------------------- * Local variables: * c-indent-level: 4 * c-brace-imaginary-offset: 0 * c-brace-offset: -4 * c-argdecl-indent: 4 * c-label-offset: -4 * c-continued-statement-offset: 4 * c-continued-brace-offset: 0 * indent-tabs-mode: nil * tab-width: 8 * End: */ multipath-tools-0.7.4/kpartx/kpartx.8000066400000000000000000000061541320314174000176030ustar00rootroot00000000000000.\" ---------------------------------------------------------------------------- .\" Update the date below if you make any significant change. .\" Make sure there are no errors with: .\" groff -z -wall -b -e -t kpartx/kpartx.8 .\" .\" ---------------------------------------------------------------------------- . .TH KPARTX 8 2016-10-28 "Linux" . . .\" ---------------------------------------------------------------------------- .SH NAME .\" ---------------------------------------------------------------------------- . kpartx \- Create device maps from partition tables. . . .\" ---------------------------------------------------------------------------- .SH SYNOPSIS .\" ---------------------------------------------------------------------------- . .B kpartx .RB [\| \-a | \-d | \-u | \-l \|] .RB [\| \-r \|] .RB [\| \-p \|] .RB [\| \-f \|] .RB [\| \-g \|] .RB [\| \-s \|] .RB [\| \-v \|] .B wholedisk . . .\" ---------------------------------------------------------------------------- .SH DESCRIPTION .\" ---------------------------------------------------------------------------- . This tool, derived from util-linux' partx, reads partition tables on specified device and create device maps over partitions segments detected. It is called from hotplug upon device maps creation and deletion. . . .\" ---------------------------------------------------------------------------- .SH OPTIONS .\" ---------------------------------------------------------------------------- . .TP .B \-a Add partition mappings. . .TP .B \-d Delete partition mappings. . .TP .B \-u Update partition mappings. . .TP .B \-l List partition mappings that would be added \-a. . .TP .B \-r Read-only partition mappings. . .TP .B \-p Set device name-partition number delimiter. . .TP .B \-f Force creation of mappings; overrides 'no_partitions' feature. . .TP .B \-g Force GUID partition table (GPT). . .TP .B \-s Sync mode. Don't return until the partitions are created. . .TP .B \-v Operate verbosely. . . .\" ---------------------------------------------------------------------------- .SH EXAMPLE .\" ---------------------------------------------------------------------------- . To mount all the partitions in a raw disk image: .IP kpartx \-av disk.img .PP This will output lines such as: .IP add map loop1p1 (254:4): 0 409597 linear 7:1 3 .PP The \fIloop1p1\fR is the name of a device file under \fI/dev/mapper\fR which you can use to access the partition, for example to fsck it: .IP fsck /dev/mapper/loop1p1 .PP When you're done, you need to remove the devices: .IP kpartx \-d disk.img . . .\" ---------------------------------------------------------------------------- .SH "SEE ALSO" .\" ---------------------------------------------------------------------------- . .BR multipath (8) .BR multipathd (8) .BR hotplug (8) . . .\" ---------------------------------------------------------------------------- .SH AUTHORS .\" ---------------------------------------------------------------------------- . This man page was assembled By Patrick Caulfield for the Debian project. .PP \fImultipath-tools\fR was developed by Christophe Varoqui and others. .\" EOF multipath-tools-0.7.4/kpartx/kpartx.c000066400000000000000000000355071320314174000176620ustar00rootroot00000000000000/* * Source: copy of util-linux' partx partx.c * * Copyrights of the original file applies * Copyright (c) 2004, 2005 Christophe Varoqui * Copyright (c) 2005 Kiyoshi Ueda * Copyright (c) 2005 Lars Soltau */ /* * Given a block device and a partition table type, * try to parse the partition table, and list the * contents. Optionally add or remove partitions. * * Read wholedisk and add all partitions: * kpartx [-a|-d|-l] [-v] wholedisk * * aeb, 2000-03-21 * cva, 2002-10-26 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "devmapper.h" #include "crc32.h" #include "lopart.h" #include "kpartx.h" #define SIZE(a) (sizeof(a)/sizeof((a)[0])) #define READ_SIZE 1024 #define MAXTYPES 64 #define MAXSLICES 256 #define DM_TARGET "linear" #define LO_NAME_SIZE 64 #define PARTNAME_SIZE 128 #define DELIM_SIZE 8 struct slice slices[MAXSLICES]; enum action { LIST, ADD, DELETE, UPDATE }; struct pt { char *type; ptreader *fn; } pts[MAXTYPES]; int ptct = 0; int udev_sync = 1; static void addpts(char *t, ptreader f) { if (ptct >= MAXTYPES) { fprintf(stderr, "addpts: too many types\n"); exit(1); } pts[ptct].type = t; pts[ptct].fn = f; ptct++; } static void initpts(void) { addpts("gpt", read_gpt_pt); addpts("dos", read_dos_pt); addpts("bsd", read_bsd_pt); addpts("solaris", read_solaris_pt); addpts("unixware", read_unixware_pt); addpts("dasd", read_dasd_pt); addpts("mac", read_mac_pt); addpts("sun", read_sun_pt); addpts("ps3", read_ps3_pt); } static char short_opts[] = "rladfgvp:t:snu"; /* Used in gpt.c */ int force_gpt=0; int force_devmap=0; static int usage(void) { printf("usage : kpartx [-a|-d|-l] [-f] [-v] wholedisk\n"); printf("\t-a add partition devmappings\n"); printf("\t-r devmappings will be readonly\n"); printf("\t-d del partition devmappings\n"); printf("\t-u update partition devmappings\n"); printf("\t-l list partitions devmappings that would be added by -a\n"); printf("\t-p set device name-partition number delimiter\n"); printf("\t-g force GUID partition table (GPT)\n"); printf("\t-f force devmap create\n"); printf("\t-v verbose\n"); printf("\t-n nosync mode. Return before the partitions are created\n"); printf("\t-s sync mode. Don't return until the partitions are created. Default.\n"); return 1; } static void set_delimiter (char * device, char * delimiter) { char * p = device; if (*p == 0x0) return; while (*(++p) != 0x0) continue; if (isdigit(*(p - 1))) *delimiter = 'p'; } static int find_devname_offset (char * device) { char *p, *q; q = p = device; while (*p) { if (*p == '/') q = p + 1; p++; } return (int)(q - device); } static char * get_hotplug_device(void) { unsigned int major, minor, off, len; char *mapname; char *devname = NULL; char *device = NULL; char *var = NULL; struct stat buf; var = getenv("ACTION"); if (!var || strcmp(var, "add")) return NULL; /* Get dm mapname for hotpluged device. */ if (!(devname = getenv("DEVNAME"))) return NULL; if (stat(devname, &buf)) return NULL; major = major(buf.st_rdev); minor = minor(buf.st_rdev); if (!(mapname = dm_mapname(major, minor))) /* Not dm device. */ return NULL; off = find_devname_offset(devname); len = strlen(mapname); /* Dirname + mapname + \0 */ if (!(device = (char *)malloc(sizeof(char) * (off + len + 1)))) { free(mapname); return NULL; } /* Create new device name. */ snprintf(device, off + 1, "%s", devname); snprintf(device + off, len + 1, "%s", mapname); if (strlen(device) != (off + len)) { free(device); free(mapname); return NULL; } free(mapname); return device; } static int check_uuid(char *uuid, char *part_uuid, char **err_msg) { char *map_uuid = strchr(part_uuid, '-'); if (!map_uuid || strncmp(part_uuid, "part", 4) != 0) { *err_msg = "not a kpartx partition"; return -1; } map_uuid++; if (strcmp(uuid, map_uuid) != 0) { *err_msg = "a partition of a different device"; return -1; } return 0; } int main(int argc, char **argv){ int i, j, m, n, op, off, arg, c, d, ro=0; int fd = -1; struct slice all; struct pt *ptp; enum action what = LIST; char *type, *diskdevice, *device, *progname; int verbose = 0; char partname[PARTNAME_SIZE], params[PARTNAME_SIZE + 16]; char * loopdev = NULL; char * delim = NULL; char *uuid = NULL; char *mapname = NULL; int hotplug = 0; int loopcreated = 0; struct stat buf; initpts(); init_crc32(); type = device = diskdevice = NULL; memset(&all, 0, sizeof(all)); memset(&partname, 0, sizeof(partname)); /* Check whether hotplug mode. */ progname = strrchr(argv[0], '/'); if (!progname) progname = argv[0]; else progname++; if (!strcmp(progname, "kpartx.dev")) { /* Hotplug mode */ hotplug = 1; /* Setup for original kpartx variables */ if (!(device = get_hotplug_device())) exit(1); diskdevice = device; what = ADD; } else if (argc < 2) { usage(); exit(1); } while ((arg = getopt(argc, argv, short_opts)) != EOF) switch(arg) { case 'r': ro=1; break; case 'f': force_devmap=1; break; case 'g': force_gpt=1; break; case 't': type = optarg; break; case 'v': verbose = 1; break; case 'p': delim = optarg; break; case 'l': what = LIST; break; case 'a': what = ADD; break; case 'd': what = DELETE; break; case 's': udev_sync = 1; break; case 'n': udev_sync = 0; break; case 'u': what = UPDATE; break; default: usage(); exit(1); } #ifdef LIBDM_API_COOKIE if (!udev_sync) dm_udev_set_sync_support(0); else dm_udev_set_sync_support(1); #endif if (dm_prereq(DM_TARGET, 0, 0, 0) && (what == ADD || what == DELETE || what == UPDATE)) { fprintf(stderr, "device mapper prerequisites not met\n"); exit(1); } if (hotplug) { /* already got [disk]device */ } else if (optind == argc-2) { device = argv[optind]; diskdevice = argv[optind+1]; } else if (optind == argc-1) { diskdevice = device = argv[optind]; } else { usage(); exit(1); } if (stat(device, &buf)) { printf("failed to stat() %s\n", device); exit (1); } if (S_ISREG (buf.st_mode)) { /* already looped file ? */ char rpath[PATH_MAX]; if (realpath(device, rpath) == NULL) { fprintf(stderr, "Error: %s: %s\n", device, strerror(errno)); exit (1); } loopdev = find_loop_by_file(rpath); if (!loopdev && what == DELETE) exit (0); if (!loopdev) { loopdev = find_unused_loop_device(); if (set_loop(loopdev, device, 0, &ro)) { fprintf(stderr, "can't set up loop\n"); exit (1); } loopcreated = 1; } device = loopdev; if (stat(device, &buf)) { printf("failed to stat() %s\n", device); exit (1); } } else if (!S_ISBLK(buf.st_mode)) { fprintf(stderr, "invalid device: %s\n", device); exit(1); } off = find_devname_offset(device); if (!loopdev) { mapname = dm_mapname(major(buf.st_rdev), minor(buf.st_rdev)); if (mapname) uuid = dm_mapuuid(mapname); } /* * We are called for a non-DM device. * Make up a fake UUID for the device, unless "-d -f" is given. * This allows deletion of partitions created with older kpartx * versions which didn't use the fake UUID during creation. */ if (!uuid && !(what == DELETE && force_devmap)) uuid = nondm_create_uuid(buf.st_rdev); if (!mapname) mapname = device + off; if (delim == NULL) { delim = malloc(DELIM_SIZE); memset(delim, 0, DELIM_SIZE); set_delimiter(mapname, delim); } fd = open(device, O_RDONLY); if (fd == -1) { perror(device); exit(1); } /* add/remove partitions to the kernel devmapper tables */ int r = 0; if (what == DELETE) { r = dm_remove_partmaps(mapname, uuid, buf.st_rdev, verbose); if (loopdev) { if (del_loop(loopdev)) { if (verbose) fprintf(stderr, "can't del loop : %s\n", loopdev); r = 1; } else fprintf(stderr, "loop deleted : %s\n", loopdev); } goto end; } for (i = 0; i < ptct; i++) { ptp = &pts[i]; if (type && strcmp(type, ptp->type)) continue; /* here we get partitions */ n = ptp->fn(fd, all, slices, SIZE(slices)); #ifdef DEBUG if (n >= 0) printf("%s: %d slices\n", ptp->type, n); #endif if (n > 0) { close(fd); fd = -1; } else continue; switch(what) { case LIST: for (j = 0, c = 0, m = 0; j < n; j++) { if (slices[j].size == 0) continue; if (slices[j].container > 0) { c++; continue; } slices[j].minor = m++; printf("%s%s%d : 0 %" PRIu64 " %s %" PRIu64"\n", mapname, delim, j+1, slices[j].size, device, slices[j].start); } /* Loop to resolve contained slices */ d = c; while (c) { for (j = 0; j < n; j++) { uint64_t start; int k = slices[j].container - 1; if (slices[j].size == 0) continue; if (slices[j].minor > 0) continue; if (slices[j].container == 0) continue; slices[j].minor = m++; start = slices[j].start - slices[k].start; printf("%s%s%d : 0 %" PRIu64 " /dev/dm-%d %" PRIu64 "\n", mapname, delim, j+1, slices[j].size, slices[k].minor, start); c--; } /* Terminate loop if nothing more to resolve */ if (d == c) break; } break; case ADD: case UPDATE: /* ADD and UPDATE share the same code that adds new partitions. */ for (j = 0, c = 0; j < n; j++) { char *part_uuid, *reason; if (slices[j].size == 0) continue; /* Skip all contained slices */ if (slices[j].container > 0) { c++; continue; } if (safe_sprintf(params, "%d:%d %" PRIu64 , major(buf.st_rdev), minor(buf.st_rdev), slices[j].start)) { fprintf(stderr, "params too small\n"); exit(1); } op = (dm_find_part(mapname, delim, j + 1, uuid, partname, sizeof(partname), &part_uuid, verbose) ? DM_DEVICE_RELOAD : DM_DEVICE_CREATE); if (part_uuid && uuid) { if (check_uuid(uuid, part_uuid, &reason) != 0) { fprintf(stderr, "%s is already in use, and %s\n", partname, reason); r++; free(part_uuid); continue; } free(part_uuid); } if (!dm_addmap(op, partname, DM_TARGET, params, slices[j].size, ro, uuid, j+1, buf.st_mode & 0777, buf.st_uid, buf.st_gid)) { fprintf(stderr, "create/reload failed on %s\n", partname); r++; continue; } if (op == DM_DEVICE_RELOAD && !dm_simplecmd(DM_DEVICE_RESUME, partname, 1, MPATH_UDEV_RELOAD_FLAG)) { fprintf(stderr, "resume failed on %s\n", partname); r++; continue; } dm_devn(partname, &slices[j].major, &slices[j].minor); if (verbose) printf("add map %s (%d:%d): 0 %" PRIu64 " %s %s\n", partname, slices[j].major, slices[j].minor, slices[j].size, DM_TARGET, params); } /* Loop to resolve contained slices */ d = c; while (c) { for (j = 0; j < n; j++) { char *part_uuid, *reason; int k = slices[j].container - 1; if (slices[j].size == 0) continue; /* Skip all existing slices */ if (slices[j].minor > 0) continue; /* Skip all simple slices */ if (slices[j].container == 0) continue; /* Check container slice */ if (slices[k].size == 0) fprintf(stderr, "Invalid slice %d\n", k); if (safe_sprintf(params, "%d:%d %" PRIu64, major(buf.st_rdev), minor(buf.st_rdev), slices[j].start)) { fprintf(stderr, "params too small\n"); exit(1); } op = (dm_find_part(mapname, delim, j + 1, uuid, partname, sizeof(partname), &part_uuid, verbose) ? DM_DEVICE_RELOAD : DM_DEVICE_CREATE); if (part_uuid && uuid) { if (check_uuid(uuid, part_uuid, &reason) != 0) { fprintf(stderr, "%s is already in use, and %s\n", partname, reason); free(part_uuid); continue; } free(part_uuid); } dm_addmap(op, partname, DM_TARGET, params, slices[j].size, ro, uuid, j+1, buf.st_mode & 0777, buf.st_uid, buf.st_gid); if (op == DM_DEVICE_RELOAD) dm_simplecmd(DM_DEVICE_RESUME, partname, 1, MPATH_UDEV_RELOAD_FLAG); dm_devn(partname, &slices[j].major, &slices[j].minor); if (verbose) printf("add map %s (%d:%d): 0 %" PRIu64 " %s %s\n", partname, slices[j].major, slices[j].minor, slices[j].size, DM_TARGET, params); c--; } /* Terminate loop */ if (d == c) break; } if (what == ADD) { /* Skip code that removes devmappings for deleted partitions */ break; } for (j = MAXSLICES-1; j >= 0; j--) { char *part_uuid, *reason; if (slices[j].size || !dm_find_part(mapname, delim, j + 1, uuid, partname, sizeof(partname), &part_uuid, verbose)) continue; if (part_uuid && uuid) { if (check_uuid(uuid, part_uuid, &reason) != 0) { fprintf(stderr, "%s is %s. Not removing\n", partname, reason); free(part_uuid); continue; } free(part_uuid); } if (!dm_simplecmd(DM_DEVICE_REMOVE, partname, 1, 0)) { r++; continue; } if (verbose) printf("del devmap : %s\n", partname); } default: break; } if (n > 0) break; } if (what == LIST && loopcreated && S_ISREG (buf.st_mode)) { if (fd != -1) close(fd); if (del_loop(device)) { if (verbose) printf("can't del loop : %s\n", device); exit(1); } printf("loop deleted : %s\n", device); } end: dm_lib_release(); dm_lib_exit(); return r; } void * xmalloc (size_t size) { void *t; if (size == 0) return NULL; t = malloc (size); if (t == NULL) { fprintf(stderr, "Out of memory\n"); exit(1); } return t; } /* * sseek: seek to specified sector */ static int sseek(int fd, unsigned int secnr) { off64_t in, out; in = ((off64_t) secnr << 9); out = 1; if ((out = lseek64(fd, in, SEEK_SET)) != in) { fprintf(stderr, "llseek error\n"); return -1; } return 0; } static struct block { unsigned int secnr; char *block; struct block *next; } *blockhead; char * getblock (int fd, unsigned int secnr) { struct block *bp; for (bp = blockhead; bp; bp = bp->next) if (bp->secnr == secnr) return bp->block; if (sseek(fd, secnr)) return NULL; bp = xmalloc(sizeof(struct block)); bp->secnr = secnr; bp->next = blockhead; blockhead = bp; bp->block = (char *) xmalloc(READ_SIZE); if (read(fd, bp->block, READ_SIZE) != READ_SIZE) { fprintf(stderr, "read error, sector %d\n", secnr); bp->block = NULL; } return bp->block; } int get_sector_size(int filedes) { int rc, sector_size = 512; rc = ioctl(filedes, BLKSSZGET, §or_size); if (rc) sector_size = 512; return sector_size; } multipath-tools-0.7.4/kpartx/kpartx.h000066400000000000000000000025501320314174000176570ustar00rootroot00000000000000#ifndef _KPARTX_H #define _KPARTX_H #include #include /* * For each partition type there is a routine that takes * a block device and a range, and returns the list of * slices found there in the supplied array SP that can * hold NS entries. The return value is the number of * entries stored, or -1 if the appropriate type is not * present. */ #define likely(x) __builtin_expect(!!(x), 1) #define unlikely(x) __builtin_expect(!!(x), 0) #define safe_sprintf(var, format, args...) \ snprintf(var, sizeof(var), format, ##args) >= sizeof(var) #ifndef BLKSSZGET #define BLKSSZGET _IO(0x12,104) /* get block device sector size */ #endif int get_sector_size(int filedes); /* * units: 512 byte sectors */ struct slice { uint64_t start; uint64_t size; int container; int major; int minor; }; typedef int (ptreader)(int fd, struct slice all, struct slice *sp, int ns); extern int force_gpt; extern ptreader read_dos_pt; extern ptreader read_bsd_pt; extern ptreader read_solaris_pt; extern ptreader read_unixware_pt; extern ptreader read_gpt_pt; extern ptreader read_dasd_pt; extern ptreader read_mac_pt; extern ptreader read_sun_pt; extern ptreader read_ps3_pt; char *getblock(int fd, unsigned int secnr); static inline int four2int(unsigned char *p) { return p[0] + (p[1]<<8) + (p[2]<<16) + (p[3]<<24); } #endif /* _KPARTX_H */ multipath-tools-0.7.4/kpartx/kpartx.rules000066400000000000000000000022061320314174000205600ustar00rootroot00000000000000# # persistent links for device-mapper devices # only hardware-backed device-mapper devices (ie multipath, dmraid, # and kpartx) have meaningful persistent device names # KERNEL!="dm-*", GOTO="kpartx_end" ACTION!="add|change", GOTO="kpartx_end" ENV{DM_UUID}!="?*", GOTO="kpartx_end" # Create dm tables for partitions on multipath devices. ENV{DM_UUID}!="mpath-?*", GOTO="mpath_kpartx_end" # DM_SUBSYSTEM_UDEV_FLAG1 is the "skip_kpartx" flag. # For events not generated by libdevmapper, we need to fetch it from db. ENV{DM_UDEV_PRIMARY_SOURCE_FLAG}!="1", IMPORT{db}="DM_SUBSYSTEM_UDEV_FLAG1" ENV{DM_SUBSYSTEM_UDEV_FLAG1}=="1", GOTO="mpath_kpartx_end" # 11-dm-mpath.rules sets MPATH_UNCHANGED for events that can be ignored. ENV{MPATH_UNCHANGED}=="1", GOTO="mpath_kpartx_end" # Don't run kpartx now if we know it will fail or hang. ENV{DM_SUSPENDED}=="1", GOTO="mpath_kpartx_end" ENV{DM_NOSCAN}=="1", GOTO="mpath_kpartx_end" # Run kpartx GOTO="run_kpartx" LABEL="mpath_kpartx_end" ## Code for other subsystems (non-multipath) could be placed here ## GOTO="kpartx_end" LABEL="run_kpartx" RUN+="/sbin/kpartx -un -p -part /dev/$name" LABEL="kpartx_end" multipath-tools-0.7.4/kpartx/kpartx_id000077500000000000000000000043221320314174000201070ustar00rootroot00000000000000#!/bin/sh # # kpartx_id # # Generates ID information for device-mapper tables. # # Copyright (C) 2006 SUSE Linux Products GmbH # Author: # Hannes Reinecke # # # 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 version 2 of the License. # # This script generates ID information used to generate persistent symlinks. # It relies on the UUID strings generated by the various programs; the name # of the tables are of no consequence. # # Please note that dmraid does not provide the UUIDs (yet); a patch has been # sent upstream but has not been accepted yet. # DMSETUP=/sbin/dmsetup MAJOR=$1 MINOR=$2 UUID=$3 if [ -z "$MAJOR" -o -z "$MINOR" ]; then echo "usage: $0 major minor" exit 1; fi # Device-mapper not installed; not an error if [ ! -x $DMSETUP ] ; then exit 0 fi # Table UUIDs are always '-'. dmuuid=${UUID#*-} dmtbl=${UUID%%-*} dmpart=${dmtbl#part} # kpartx types are 'part' if [ "$dmpart" = "$dmtbl" ] ; then dmpart= else dmtbl=part fi # Set the name of the table. We're only interested in dmraid, # multipath, and kpartx tables; everything else is ignored. if [ "$dmtbl" = "part" ] ; then dmname=$($DMSETUP info -c --noheadings -o name -u $dmuuid) echo "DM_MPATH=$dmname" # We need the dependencies of the parent table to figure out # the type if the parent is a multipath table case "$dmuuid" in mpath-*) dmdeps=$($DMSETUP deps -u $dmuuid) ;; esac elif [ "$dmtbl" = "mpath" ] ; then dmname="$dmuuid" # We need the dependencies of the table to figure out the type dmdeps=$($DMSETUP deps -u $UUID) fi [ -n "$dmpart" ] && echo "DM_PART=$dmpart" # Figure out the type of the map. For non-multipath maps it's # always 'raid'. if [ -n "$dmdeps" ] ; then case "$dmdeps" in *\(94,*) echo "DM_TYPE=ccw" ;; *\(104,* | *\(105,* | *\(106,* | *\(107,* | *\(108,* | *\(109,* | *\(110,* | *\(112,*) echo "DM_TYPE=cciss" ;; *\(9*) echo "DM_TYPE=raid" ;; *) echo "DM_TYPE=scsi" echo "DM_WWN=0x${dmname#?}" ;; esac else echo "DM_TYPE=raid" fi exit 0 multipath-tools-0.7.4/kpartx/lopart.c000066400000000000000000000136731320314174000176520ustar00rootroot00000000000000/* Taken from Ted's losetup.c - Mitch */ /* Added vfs mount options - aeb - 960223 */ /* Removed lomount - aeb - 960224 */ /* 1999-02-22 Arkadiusz MiÅ›kiewicz * - added Native Language Support * Sun Mar 21 1999 - Arnaldo Carvalho de Melo * - fixed strerr(errno) in gettext calls */ #define PROC_DEVICES "/proc/devices" /* * losetup.c - setup and control loop devices */ #include "kpartx.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "lopart.h" #include "xstrncpy.h" #ifndef LOOP_CTL_GET_FREE #define LOOP_CTL_GET_FREE 0x4C82 #endif static char * xstrdup (const char *s) { char *t; if (s == NULL) return NULL; t = strdup (s); if (t == NULL) { fprintf(stderr, "not enough memory"); exit(1); } return t; } #define SIZE(a) (sizeof(a)/sizeof(a[0])) char *find_loop_by_file(const char *filename) { DIR *dir; struct dirent *dent; char dev[64], *found = NULL, *p; int fd; struct stat statbuf; struct loop_info loopinfo; const char VIRT_BLOCK[] = "/sys/devices/virtual/block"; char path[PATH_MAX]; dir = opendir(VIRT_BLOCK); if (!dir) return NULL; while ((dent = readdir(dir)) != NULL) { if (strncmp(dent->d_name,"loop",4)) continue; if (snprintf(path, PATH_MAX, "%s/%s/dev", VIRT_BLOCK, dent->d_name) >= PATH_MAX) continue; fd = open(path, O_RDONLY); if (fd < 0) continue; if (read(fd, dev, sizeof(dev)) <= 0) { close(fd); continue; } close(fd); dev[sizeof(dev)-1] = '\0'; p = strchr(dev, '\n'); if (p != NULL) *p = '\0'; if (snprintf(path, PATH_MAX, "/dev/block/%s", dev) >= PATH_MAX) continue; fd = open (path, O_RDONLY); if (fd < 0) continue; if (fstat (fd, &statbuf) != 0 || !S_ISBLK(statbuf.st_mode)) { close (fd); continue; } if (ioctl (fd, LOOP_GET_STATUS, &loopinfo) != 0) { close (fd); continue; } close (fd); if (0 == strcmp(filename, loopinfo.lo_name)) { found = realpath(path, NULL); break; } } closedir(dir); return found; } char *find_unused_loop_device(void) { char dev[20], *next_loop_dev = NULL; int fd, next_loop = 0, somedev = 0, someloop = 0, loop_known = 0; struct stat statbuf; struct loop_info loopinfo; FILE *procdev; while (next_loop_dev == NULL) { if (stat("/dev/loop-control", &statbuf) == 0 && S_ISCHR(statbuf.st_mode)) { int next_loop_fd; next_loop_fd = open("/dev/loop-control", O_RDWR); if (next_loop_fd < 0) return NULL; next_loop = ioctl(next_loop_fd, LOOP_CTL_GET_FREE); close(next_loop_fd); if (next_loop < 0) return NULL; } sprintf(dev, "/dev/loop%d", next_loop); fd = open (dev, O_RDONLY); if (fd >= 0) { if (fstat (fd, &statbuf) == 0 && S_ISBLK(statbuf.st_mode)) { somedev++; if(ioctl (fd, LOOP_GET_STATUS, &loopinfo) == 0) someloop++; /* in use */ else if (errno == ENXIO) next_loop_dev = xstrdup(dev); } close (fd); /* continue trying as long as devices exist */ continue; } break; } if (next_loop_dev) return next_loop_dev; /* Nothing found. Why not? */ if ((procdev = fopen(PROC_DEVICES, "r")) != NULL) { char line[100]; while (fgets (line, sizeof(line), procdev)) if (strstr (line, " loop\n")) { loop_known = 1; break; } fclose(procdev); if (!loop_known) loop_known = -1; } if (!somedev) fprintf(stderr, "mount: could not find any device /dev/loop#"); else if (!someloop) { if (loop_known == 1) fprintf(stderr, "mount: Could not find any loop device.\n" " Maybe /dev/loop# has a wrong major number?"); else if (loop_known == -1) fprintf(stderr, "mount: Could not find any loop device, and, according to %s,\n" " this kernel does not know about the loop device.\n" " (If so, then recompile or `modprobe loop'.)", PROC_DEVICES); else fprintf(stderr, "mount: Could not find any loop device. Maybe this kernel does not know\n" " about the loop device (then recompile or `modprobe loop'), or\n" " maybe /dev/loop# has the wrong major number?"); } else fprintf(stderr, "mount: could not find any free loop device"); return NULL; } int set_loop(const char *device, const char *file, int offset, int *loopro) { struct loop_info loopinfo; int fd, ffd, mode; mode = (*loopro ? O_RDONLY : O_RDWR); if ((ffd = open (file, mode)) < 0) { if (!*loopro && (errno == EROFS || errno == EACCES)) ffd = open (file, mode = O_RDONLY); if (ffd < 0) { perror (file); return 1; } } if ((fd = open (device, mode)) < 0) { close(ffd); perror (device); return 1; } *loopro = (mode == O_RDONLY); memset (&loopinfo, 0, sizeof (loopinfo)); xstrncpy (loopinfo.lo_name, file, LO_NAME_SIZE); loopinfo.lo_offset = offset; loopinfo.lo_encrypt_type = LO_CRYPT_NONE; loopinfo.lo_encrypt_key_size = 0; if (ioctl(fd, LOOP_SET_FD, (void*)(uintptr_t)(ffd)) < 0) { perror ("ioctl: LOOP_SET_FD"); close (fd); close (ffd); return 1; } if (ioctl (fd, LOOP_SET_STATUS, &loopinfo) < 0) { (void) ioctl (fd, LOOP_CLR_FD, 0); perror ("ioctl: LOOP_SET_STATUS"); close (fd); close (ffd); return 1; } close (fd); close (ffd); return 0; } int del_loop(const char *device) { int retries = 5; int fd; if ((fd = open (device, O_RDONLY)) < 0) { int errsv = errno; fprintf(stderr, "loop: can't delete device %s: %s\n", device, strerror (errsv)); return 1; } while (ioctl (fd, LOOP_CLR_FD, 0) < 0) { if (errno != EBUSY || retries-- <= 0) { perror ("ioctl: LOOP_CLR_FD"); close (fd); return 1; } fprintf(stderr, "loop: device %s still in use, retrying delete\n", device); sleep(1); continue; } close (fd); return 0; } multipath-tools-0.7.4/kpartx/lopart.h000066400000000000000000000003241320314174000176440ustar00rootroot00000000000000extern int verbose; extern int set_loop (const char *, const char *, int, int *); extern int del_loop (const char *); extern char * find_unused_loop_device (void); extern char * find_loop_by_file (const char *); multipath-tools-0.7.4/kpartx/mac.c000066400000000000000000000021611320314174000170770ustar00rootroot00000000000000#include "kpartx.h" #include "byteorder.h" #include #include #include "mac.h" int read_mac_pt(int fd, struct slice all, struct slice *sp, int ns) { struct mac_driver_desc *md; struct mac_partition *part; unsigned secsize; char *data; int blk, blocks_in_map; int n = 0; md = (struct mac_driver_desc *) getblock(fd, 0); if (md == NULL) return -1; if (be16_to_cpu(md->signature) != MAC_DRIVER_MAGIC) return -1; secsize = be16_to_cpu(md->block_size); data = getblock(fd, secsize/512); if (!data) return -1; part = (struct mac_partition *) (data + secsize%512); if (be16_to_cpu(part->signature) != MAC_PARTITION_MAGIC) return -1; blocks_in_map = be32_to_cpu(part->map_count); for (blk = 1; blk <= blocks_in_map && blk <= ns; ++blk, ++n) { int pos = blk * secsize; data = getblock(fd, pos/512); if (!data) return -1; part = (struct mac_partition *) (data + pos%512); if (be16_to_cpu(part->signature) != MAC_PARTITION_MAGIC) break; sp[n].start = be32_to_cpu(part->start_block) * (secsize/512); sp[n].size = be32_to_cpu(part->block_count) * (secsize/512); } return n; } multipath-tools-0.7.4/kpartx/mac.h000066400000000000000000000014541320314174000171100ustar00rootroot00000000000000#ifndef MAC_H #define MAC_H #include #define MAC_PARTITION_MAGIC 0x504d /* type field value for A/UX or other Unix partitions */ #define APPLE_AUX_TYPE "Apple_UNIX_SVR2" struct mac_partition { uint16_t signature; /* expected to be MAC_PARTITION_MAGIC */ uint16_t res1; uint32_t map_count; /* # blocks in partition map */ uint32_t start_block; /* absolute starting block # of partition */ uint32_t block_count; /* number of blocks in partition */ /* there is more stuff after this that we don't need */ }; #define MAC_DRIVER_MAGIC 0x4552 /* Driver descriptor structure, in block 0 */ struct mac_driver_desc { uint16_t signature; /* expected to be MAC_DRIVER_MAGIC */ uint16_t block_size; uint32_t block_count; /* ... more stuff */ }; #endif multipath-tools-0.7.4/kpartx/ps3.c000066400000000000000000000027351320314174000170530ustar00rootroot00000000000000#include "kpartx.h" #include "byteorder.h" #include #include #define SECTOR_SIZE 512 #define MAX_ACL_ENTRIES 8 #define MAX_PARTITIONS 8 #define MAGIC1 0x0FACE0FFULL #define MAGIC2 0xDEADFACEULL struct p_acl_entry { u_int64_t laid; u_int64_t rights; }; struct d_partition { u_int64_t p_start; u_int64_t p_size; struct p_acl_entry p_acl[MAX_ACL_ENTRIES]; }; struct disklabel { u_int8_t d_res1[16]; u_int64_t d_magic1; u_int64_t d_magic2; u_int64_t d_res2; u_int64_t d_res3; struct d_partition d_partitions[MAX_PARTITIONS]; u_int8_t d_pad[0x600 - MAX_PARTITIONS * sizeof(struct d_partition) - 0x30]; }; static int read_disklabel(int fd, struct disklabel *label) { unsigned char *data; int i; for (i = 0; i < sizeof(struct disklabel) / SECTOR_SIZE; i++) { data = (unsigned char *) getblock(fd, i); if (!data) return 0; memcpy((unsigned char *) label + i * SECTOR_SIZE, data, SECTOR_SIZE); } return 1; } int read_ps3_pt(int fd, struct slice all, struct slice *sp, int ns) { struct disklabel label; int n = 0; int i; if (!read_disklabel(fd, &label)) return -1; if ((be64_to_cpu(label.d_magic1) != MAGIC1) || (be64_to_cpu(label.d_magic2) != MAGIC2)) return -1; for (i = 0; i < MAX_PARTITIONS; i++) { if (label.d_partitions[i].p_start && label.d_partitions[i].p_size) { sp[n].start = be64_to_cpu(label.d_partitions[i].p_start); sp[n].size = be64_to_cpu(label.d_partitions[i].p_size); n++; } } return n; } multipath-tools-0.7.4/kpartx/solaris.c000066400000000000000000000034151320314174000200160ustar00rootroot00000000000000#include "kpartx.h" #include #include #include /* time_t */ #define SOLARIS_X86_NUMSLICE 8 #define SOLARIS_X86_VTOC_SANE (0x600DDEEEUL) //typedef int daddr_t; /* or long - check */ struct solaris_x86_slice { unsigned short s_tag; /* ID tag of partition */ unsigned short s_flag; /* permision flags */ long s_start; /* start sector no of partition */ long s_size; /* # of blocks in partition */ }; struct solaris_x86_vtoc { unsigned long v_bootinfo[3]; /* info for mboot */ unsigned long v_sanity; /* to verify vtoc sanity */ unsigned long v_version; /* layout version */ char v_volume[8]; /* volume name */ unsigned short v_sectorsz; /* sector size in bytes */ unsigned short v_nparts; /* number of partitions */ unsigned long v_reserved[10]; /* free space */ struct solaris_x86_slice v_slice[SOLARIS_X86_NUMSLICE]; /* slice headers */ time_t timestamp[SOLARIS_X86_NUMSLICE]; /* timestamp */ char v_asciilabel[128]; /* for compatibility */ }; int read_solaris_pt(int fd, struct slice all, struct slice *sp, int ns) { struct solaris_x86_vtoc *v; struct solaris_x86_slice *s; unsigned int offset = all.start; int i, n; char *bp; bp = getblock(fd, offset+1); /* 1 sector suffices */ if (bp == NULL) return -1; v = (struct solaris_x86_vtoc *) bp; if(v->v_sanity != SOLARIS_X86_VTOC_SANE) return -1; if(v->v_version != 1) { fprintf(stderr, "Cannot handle solaris version %ld vtoc\n", v->v_version); return 0; } for(i=0, n=0; iv_slice[i]; if (s->s_size == 0) continue; if (n < ns) { sp[n].start = offset + s->s_start; sp[n].size = s->s_size; n++; } else { fprintf(stderr, "solaris_x86_partition: too many slices\n"); break; } } return n; } multipath-tools-0.7.4/kpartx/sun.c000066400000000000000000000062711320314174000171520ustar00rootroot00000000000000/* * Lifted from util-linux' partx sun.c * * Copyrights of the original file apply * Copyright (c) 2007 Hannes Reinecke */ #include "kpartx.h" #include "byteorder.h" #include #include #include /* time_t */ #define SUN_DISK_MAGIC 0xDABE /* Disk magic number */ #define SUN_DISK_MAXPARTITIONS 8 struct __attribute__ ((packed)) sun_raw_part { u_int32_t start_cylinder; /* where the part starts... */ u_int32_t num_sectors; /* ...and it's length */ }; struct __attribute__ ((packed)) sun_part_info { u_int8_t spare1; u_int8_t id; /* Partition type */ u_int8_t spare2; u_int8_t flags; /* Partition flags */ }; struct __attribute__ ((packed)) sun_disk_label { char info[128]; /* Informative text string */ u_int8_t spare0[14]; struct sun_part_info infos[SUN_DISK_MAXPARTITIONS]; u_int8_t spare1[246]; /* Boot information etc. */ u_int16_t rspeed; /* Disk rotational speed */ u_int16_t pcylcount; /* Physical cylinder count */ u_int16_t sparecyl; /* extra sects per cylinder */ u_int8_t spare2[4]; /* More magic... */ u_int16_t ilfact; /* Interleave factor */ u_int16_t ncyl; /* Data cylinder count */ u_int16_t nacyl; /* Alt. cylinder count */ u_int16_t ntrks; /* Tracks per cylinder */ u_int16_t nsect; /* Sectors per track */ u_int8_t spare3[4]; /* Even more magic... */ struct sun_raw_part partitions[SUN_DISK_MAXPARTITIONS]; u_int16_t magic; /* Magic number */ u_int16_t csum; /* Label xor'd checksum */ }; /* Checksum Verification */ static int sun_verify_checksum (struct sun_disk_label *label) { u_int16_t *ush = ((u_int16_t *)(label + 1)) - 1; u_int16_t csum = 0; while (ush >= (u_int16_t *)label) csum ^= *ush--; return !csum; } int read_sun_pt(int fd, struct slice all, struct slice *sp, int ns) { struct sun_disk_label *l; struct sun_raw_part *s; unsigned int offset = all.start, end; int i, j, n; char *bp; bp = getblock(fd, offset); if (bp == NULL) return -1; l = (struct sun_disk_label *) bp; if(be16_to_cpu(l->magic) != SUN_DISK_MAGIC) return -1; if (!sun_verify_checksum(l)) { fprintf(stderr, "Corrupted Sun disk label\n"); return -1; } for(i=0, n=0; ipartitions[i]; if (n < ns) { sp[n].start = offset + be32_to_cpu(s->start_cylinder) * be16_to_cpu(l->nsect) * be16_to_cpu(l->ntrks); sp[n].size = be32_to_cpu(s->num_sectors); n++; } else { fprintf(stderr, "sun_disklabel: too many slices\n"); break; } } /* * Convention has it that the SUN disklabel will always have * the 'c' partition spanning the entire disk. * So we have to check for contained slices. */ for(i = 0; i < SUN_DISK_MAXPARTITIONS; i++) { if (sp[i].size == 0) continue; end = sp[i].start + sp[i].size; for(j = 0; j < SUN_DISK_MAXPARTITIONS; j ++) { if ( i == j ) continue; if (sp[j].size == 0) continue; if (sp[i].start < sp[j].start) { if (end > sp[j].start && end < sp[j].start + sp[j].size) { /* Invalid slice */ fprintf(stderr, "sun_disklabel: slice %d overlaps with %d\n", i , j); sp[i].size = 0; } } else { if (end <= sp[j].start + sp[j].size) { sp[i].container = j + 1; } } } } return n; } multipath-tools-0.7.4/kpartx/test-kpartx000077500000000000000000000175411320314174000204170ustar00rootroot00000000000000#! /bin/bash # This is a unit test program for kpartx, in particular for deleting partitions. # # The rationale is the following: # # 1) kpartx should delete all mappings it created beforehand. # 2) kpartx should handle partitions on dm devices and other devices # (e.g. loop devices) equally well. # 3) kpartx should only delete "partitions", which are single-target # linear mappings into a block device. Other maps should not be touched. # 4) kpartx should only delete mappings it created itself beforehand. # In particular, it shouldn't delete LVM LVs, even if they are fully # contained in the block device at hand and thus look like partitions # in the first place. (For historical compatibility reasons, we allow # such mappings to be deleted with the -f/--force flag). # 5) DM map names may be changed, thus kpartx shouldn't rely on them to # check whether a mapping is a partition of a particular device. It is # legal for a partition of /dev/loop0 to be named "loop0". # Note: This program tries hard to clean up, but if tests fail, # stale DM or loop devices may keep lurking around. # Set WORKDIR in environment to existing dir to for persistence # WARNING: exisiting files will be truncated. # If empty, test will be done in temporary dir : ${WORKDIR:=} # Set this environment variable to test an alternative kpartx executable : ${KPARTX:=} # Options to pass to kpartx always : ${KPARTX_OPTS:=-s} # Time to wait for device nodes to appear (microseconds) # Waiting is only needed if "s" is not in $KPARTX_OPTS : ${WAIT_US:=0} # IMPORTANT: The ERR trap is essential for this program to work correctly! trap 'LINE=$LINENO; trap - ERR; echo "== error in $BASH_COMMAND on line $LINE ==" >&2; exit 1' ERR trap 'cleanup' 0 CLEANUP=: cleanup() { trap - ERR trap - 0 if [[ $OK ]]; then echo == all tests completed successfully == >&2 else echo == step $STEP failed == >&2 fi eval "$CLEANUP" &>/dev/null } push_cleanup() { CLEANUP="$@;$CLEANUP" } pop_cleanup() { # CAUTION: simplistic CLEANUP=${CLEANUP#*;} } step() { STEP="$@" echo == Test step: $STEP == >&2 } mk_partitions() { parted -s $1 mklabel msdos parted -s -- $1 mkpart prim ext2 1MiB -1s } wipe_ptable() { dd if=/dev/zero of=$1 bs=1b count=1 } step preparation [[ $UID -eq 0 ]] [[ $KPARTX ]] || { if [[ -x $PWD/kpartx/kpartx ]]; then KPARTX=$PWD/kpartx/kpartx else KPARTX=$(which kpartx) fi } [[ $KPARTX ]] FILE1=kpartx1 FILE2=kpartx2 FILE3=kpartx3 SIZE=$((1024*1024*1024)) # use bytes as units here SECTSIZ=512 OFFS=32 # offset of linear mapping into dev, sectors VG=kpvg # volume group name LV=kplv # logical vol name LVMCONF='devices { filter = [ "a|/dev/loop.*|", r".*" ] }' OK= [[ $WORKDIR ]] || { WORKDIR=$(mktemp -d /tmp/kpartx-XXXXXX) push_cleanup 'rm -rf $WORKDIR' } push_cleanup "cd $PWD" cd "$WORKDIR" step "create loop devices" truncate -s $SIZE $FILE1 truncate -s $SIZE $FILE2 truncate -s $SIZE $FILE3 LO1=$(losetup -f $FILE1 --show) push_cleanup 'losetup -d $LO1' LO2=$(losetup -f $FILE2 --show) push_cleanup 'losetup -d $LO2' LO3=$(losetup -f $FILE3 --show) push_cleanup 'losetup -d $LO3' [[ $LO1 && $LO2 && $LO3 && -b $LO1 && -b $LO2 && -b $LO3 ]] DEV1=$(stat -c "%t:%T" $LO1) DEV2=$(stat -c "%t:%T" $LO2) DEV3=$(stat -c "%t:%T" $LO3) usleep $WAIT_US step "create DM devices (spans)" # Create two linear mappings spanning two loopdevs. # One of them gets a pathological name colliding with # the loop device name. # These mappings must not be removed by kpartx. # They also serve as DM devices to test partition removal on those. TABLE="\ 0 $((SIZE/SECTSIZ-OFFS)) linear $DEV1 $OFFS $((SIZE/SECTSIZ-OFFS)) $((SIZE/SECTSIZ-OFFS)) linear $DEV2 $OFFS" SPAN1=kpt SPAN2=$(basename $LO2) dmsetup create $SPAN1 <<<"$TABLE" push_cleanup 'dmsetup remove -f $SPAN1' dmsetup create $SPAN2 <<<"$TABLE" push_cleanup 'dmsetup remove -f $SPAN2' usleep $WAIT_US [[ -b /dev/mapper/$SPAN1 ]] [[ -b /dev/mapper/$SPAN2 ]] step "create vg on $LO3" # On the 3rd loop device, we create a VG and an LV # The LV should not be removed by kpartx. pvcreate --config "$LVMCONF" -f $LO3 vgcreate --config "$LVMCONF" $VG $LO3 push_cleanup 'vgremove --config "$LVMCONF" -f $VG' lvcreate --config "$LVMCONF" -L $((SIZE/2))B -n $LV $VG push_cleanup 'lvremove --config "$LVMCONF" -f $VG/$LV' usleep $WAIT_US [[ -b /dev/mapper/$VG-$LV ]] # dmsetup table /dev/mapper/$VG-$LV # dmsetup info /dev/mapper/$VG-$LV step "create partitions on loop devices" mk_partitions $LO1 mk_partitions $LO2 # Test invocation of kpartx with regular file here LO2P1=/dev/mapper/$(basename $LO2)-foo1 $KPARTX $KPARTX_OPTS -a -p -foo $FILE2 [[ -b $LO2P1 ]] push_cleanup 'dmsetup remove -f $(basename $LO2P1)' step "remove partitions with deleted ptable" wipe_ptable $LO2 $KPARTX $KPARTX_OPTS -d $LO2 [[ ! -b $LO2P1 ]] mk_partitions $LO2 $KPARTX $KPARTX_OPTS -a -p -foo $FILE2 [[ -b $LO2P1 ]] LO1P1=/dev/mapper/$(basename $LO1)-eggs1 $KPARTX $KPARTX_OPTS -a -p -eggs $LO1 push_cleanup 'dmsetup remove -f $(basename $LO1P1)' usleep $WAIT_US [[ -b $LO1P1 ]] [[ -b $LO2P1 ]] # dmsetup info $LO2P1 # Set pathological name for partition on $LO1 (same as loop device itself) dmsetup rename $(basename $LO1P1) $(basename $LO1) LO1P1=/dev/mapper/$(basename $LO1) pop_cleanup push_cleanup 'dmsetup remove -f $(basename $LO1P1)' # dmsetup info $LO1P1 step "create partitions on DM devices" mk_partitions /dev/mapper/$SPAN2 $KPARTX $KPARTX_OPTS -a -p -bar /dev/mapper/$SPAN2 SPAN2P1=/dev/mapper/${SPAN2}-bar1 # udev rules may have created partition mappings without UUIDs # which aren't removed by default (if system standard kpartx doesn't # set the UUID). Remove them using -f push_cleanup '$KPARTX $KPARTX_OPTS -f -d /dev/mapper/$SPAN2' push_cleanup 'dmsetup remove -f $(basename $SPAN2P1)' $KPARTX $KPARTX_OPTS -a -p -spam /dev/mapper/$SPAN1 SPAN1P1=/dev/mapper/${SPAN1}-spam1 # see above push_cleanup '$KPARTX $KPARTX_OPTS -f -d /dev/mapper/$SPAN1' push_cleanup 'dmsetup remove -f $(basename $SPAN1P1)' usleep $WAIT_US [[ -b $SPAN2P1 ]] [[ -b $SPAN1P1 ]] step "rename partitions on DM device to default" $KPARTX $KPARTX_OPTS -u /dev/mapper/$SPAN1 [[ ! -b ${SPAN1P1} ]] # This assumes that $SPAN1 ends in a non-digit [[ -b ${SPAN1P1//-spam/} ]] step "rename partitions on DM device back from default" $KPARTX $KPARTX_OPTS -u -p -spam /dev/mapper/$SPAN1 [[ -b ${SPAN1P1} ]] [[ ! -b ${SPANP1//-foo/} ]] step "delete partitions on DM devices" $KPARTX $KPARTX_OPTS -d /dev/mapper/$SPAN1 >&2 usleep $WAIT_US [[ -b $SPAN2P1 ]] [[ -b $LO1P1 ]] [[ -b $LO2P1 ]] [[ ! -b $SPAN1P1 ]] $KPARTX $KPARTX_OPTS -d /dev/mapper/$SPAN2 usleep $WAIT_US [[ -b $LO1P1 ]] [[ -b $LO2P1 ]] [[ ! -b $SPAN2P1 ]] step "rename partitions on loop device" $KPARTX $KPARTX_OPTS -u -p -spam $LO2 [[ ! -b ${LO2P1} ]] [[ -b ${LO2P1//-foo/-spam} ]] step "rename partitions on loop device back" $KPARTX $KPARTX_OPTS -u -p -foo $LO2 [[ -b ${LO2P1} ]] [[ ! -b ${LO2P1//-foo/-spam} ]] step "rename partitions on loop device to default" $KPARTX $KPARTX_OPTS -u $LO2 #read a [[ ! -b ${LO2P1} ]] # $LO1 ends in a digit [[ -b ${LO2P1//-foo/p} ]] step "rename partitions on loop device back from default" $KPARTX $KPARTX_OPTS -u -p -foo $LO2 [[ -b ${LO2P1} ]] [[ ! -b ${LO2P1//-foo/p} ]] step "rename partitions on loop devices" $KPARTX $KPARTX_OPTS -u -p spam $LO2 step "delete partitions on loop devices" $KPARTX $KPARTX_OPTS -d $LO3 # This will also delete the loop device $KPARTX $KPARTX_OPTS -d $FILE2 $KPARTX $KPARTX_OPTS -d $LO1 usleep $WAIT_US # ls -l /dev/mapper [[ ! -b $LO1P1 ]] pop_cleanup [[ ! -b $LO2P1 ]] pop_cleanup # spans should not have been removed [[ -b /dev/mapper/$SPAN1 ]] [[ -b /dev/mapper/$SPAN2 ]] # LVs neither [[ -b /dev/mapper/$VG-$LV ]] step "delete partitions on $LO3 with -f" $KPARTX $KPARTX_OPTS -f -d $LO3 # -d -f should delete the LV, too [[ ! -b /dev/mapper/$VG-$LV ]] [[ -b /dev/mapper/$SPAN1 ]] [[ -b /dev/mapper/$SPAN2 ]] OK=yes multipath-tools-0.7.4/kpartx/unixware.c000066400000000000000000000052431320314174000202050ustar00rootroot00000000000000#include "kpartx.h" #include #define UNIXWARE_FS_UNUSED 0 #define UNIXWARE_NUMSLICE 16 #define UNIXWARE_DISKMAGIC (0xCA5E600D) #define UNIXWARE_DISKMAGIC2 (0x600DDEEE) struct unixware_slice { unsigned short s_label; /* label */ unsigned short s_flags; /* permission flags */ unsigned int start_sect; /* starting sector */ unsigned int nr_sects; /* number of sectors in slice */ }; struct unixware_disklabel { unsigned int d_type; /* drive type */ unsigned char d_magic[4]; /* the magic number */ unsigned int d_version; /* version number */ char d_serial[12]; /* serial number of the device */ unsigned int d_ncylinders; /* # of data cylinders per device */ unsigned int d_ntracks; /* # of tracks per cylinder */ unsigned int d_nsectors; /* # of data sectors per track */ unsigned int d_secsize; /* # of bytes per sector */ unsigned int d_part_start; /* # of first sector of this partition */ unsigned int d_unknown1[12]; /* ? */ unsigned int d_alt_tbl; /* byte offset of alternate table */ unsigned int d_alt_len; /* byte length of alternate table */ unsigned int d_phys_cyl; /* # of physical cylinders per device */ unsigned int d_phys_trk; /* # of physical tracks per cylinder */ unsigned int d_phys_sec; /* # of physical sectors per track */ unsigned int d_phys_bytes; /* # of physical bytes per sector */ unsigned int d_unknown2; /* ? */ unsigned int d_unknown3; /* ? */ unsigned int d_pad[8]; /* pad */ struct unixware_vtoc { unsigned char v_magic[4]; /* the magic number */ unsigned int v_version; /* version number */ char v_name[8]; /* volume name */ unsigned short v_nslices; /* # of slices */ unsigned short v_unknown1; /* ? */ unsigned int v_reserved[10]; /* reserved */ struct unixware_slice v_slice[UNIXWARE_NUMSLICE]; /* slice headers */ } vtoc; }; /* 408 */ int read_unixware_pt(int fd, struct slice all, struct slice *sp, int ns) { struct unixware_disklabel *l; struct unixware_slice *p; unsigned int offset = all.start; char *bp; int n = 0; bp = getblock(fd, offset+29); /* 1 sector suffices */ if (bp == NULL) return -1; l = (struct unixware_disklabel *) bp; if (four2int(l->d_magic) != UNIXWARE_DISKMAGIC || four2int(l->vtoc.v_magic) != UNIXWARE_DISKMAGIC2) return -1; p = &l->vtoc.v_slice[1]; /* slice 0 is the whole disk. */ while (p - &l->vtoc.v_slice[0] < UNIXWARE_NUMSLICE) { if (p->s_label == UNIXWARE_FS_UNUSED) /* nothing */; else if (n < ns) { sp[n].start = p->start_sect; sp[n].size = p->nr_sects; n++; } else { fprintf(stderr, "unixware_partition: too many slices\n"); break; } p++; } return n; } multipath-tools-0.7.4/kpartx/xstrncpy.c000066400000000000000000000003261320314174000202320ustar00rootroot00000000000000/* NUL-terminated version of strncpy() */ #include #include "xstrncpy.h" /* caller guarantees n > 0 */ void xstrncpy(char *dest, const char *src, size_t n) { strncpy(dest, src, n-1); dest[n-1] = 0; } multipath-tools-0.7.4/kpartx/xstrncpy.h000066400000000000000000000000751320314174000202400ustar00rootroot00000000000000extern void xstrncpy(char *dest, const char *src, size_t n); multipath-tools-0.7.4/libdmmp/000077500000000000000000000000001320314174000163065ustar00rootroot00000000000000multipath-tools-0.7.4/libdmmp/DEV_NOTES000066400000000000000000000025001320314174000176140ustar00rootroot00000000000000== Planed features == * Expose all properties used by /usr/bin/multipath == Code style == * Keep things as simple as possible. * Linux Kernel code style. * Don't use typedef. * Don't use enum. * We are not smarter than API user, so don't create wrapping function like: ``` dmmp_mpath_search_by_id(struct dmmp_context *ctx, struct dmmp_mpath **dmmp_mp, uint32_t dmmp_mp_count, const char *id) dmmp_path_group_id_search(struct dmmp_mpath *dmmp_mp, const char *blk_name) ``` * The performance is the same for query single mpath and query all mpaths, so no `dmmp_mpath_of_wwid(struct dmmp_context *ctx, const char *wwid)` yet. == Naming scheme == * Public constants should be named as `DMMP_XXX_YYY`. * Public functions should be named as `dmmp__`. * Private constants should be named as `_DMMP_XXX_YYY`. * Private functions should be named as `_dmmp__`. == Code Layout == * libdmmp_private.h Internal functions or macros. * libdmmp.c Handling multipathd IPC and generate dmmp_context and dmmp_mpath_array_get(). * libdmmp_mp.c For `struct dmmp_mpath` * libdmmp_pg.c For `struct dmmp_path_group` * libdmmp_path.c For `struct dmmp_path` * libdmmp_misc.c Misc functions. multipath-tools-0.7.4/libdmmp/Makefile000066400000000000000000000044401320314174000177500ustar00rootroot00000000000000# Makefile # # Copyright (C) 2015 - 2016 Red Hat, Inc. # Gris Ge # include ../Makefile.inc LIBDMMP_VERSION=0.1.0 SONAME=$(LIBDMMP_VERSION) DEVLIB = libdmmp.so LIBS = $(DEVLIB).$(SONAME) PKGFILE = libdmmp.pc EXTRA_MAN_FILES = libdmmp.h.3 HEADERS = libdmmp/libdmmp.h OBJS = libdmmp.o libdmmp_mp.o libdmmp_pg.o libdmmp_path.o libdmmp_misc.o CFLAGS += $(LIB_CFLAGS) -fvisibility=hidden -I$(libdmmpdir) -I$(mpathcmddir) \ $(shell pkg-config --cflags json-c) LIBDEPS += $(shell pkg-config --libs json-c) -L$(mpathcmddir) -lmpathcmd -lpthread all: $(LIBS) doc $(LIBS): $(OBJS) $(CC) $(LDFLAGS) $(SHARED_FLAGS) -Wl,-soname=$@ -o $@ $(OBJS) $(LIBDEPS) $(LN) $@ $(DEVLIB) install: mkdir -p $(DESTDIR)$(usrlibdir) $(INSTALL_PROGRAM) -m 755 $(LIBS) $(DESTDIR)$(usrlibdir)/$(LIBS) $(INSTALL_PROGRAM) -m 644 -D \ $(HEADERS) $(DESTDIR)$(includedir)/$(HEADERS) $(LN) $(LIBS) $(DESTDIR)$(usrlibdir)/$(DEVLIB) $(INSTALL_PROGRAM) -m 644 -D \ $(PKGFILE).in $(DESTDIR)$(pkgconfdir)/$(PKGFILE) perl -i -pe 's|__VERSION__|$(LIBDMMP_VERSION)|g' \ $(DESTDIR)$(pkgconfdir)/$(PKGFILE) perl -i -pe 's|__LIBDIR__|$(usrlibdir)|g' \ $(DESTDIR)$(pkgconfdir)/$(PKGFILE) perl -i -pe 's|__INCLUDEDIR__|$(includedir)|g' \ $(DESTDIR)$(pkgconfdir)/$(PKGFILE) @for file in docs/man/*.3.gz; do \ $(INSTALL_PROGRAM) -m 644 -D \ $$file \ $(DESTDIR)$(man3dir)/ || exit $?; \ done uninstall: $(RM) $(DESTDIR)$(usrlibdir)/$(LIBS) $(RM) $(DESTDIR)$(includedir)/$(HEADERS) $(RM) $(DESTDIR)$(usrlibdir)/$(DEVLIB) @for file in $(DESTDIR)$(man3dir)/dmmp_*; do \ $(RM) $$file; \ done $(RM) $(DESTDIR)$(man3dir)/libdmmp.h* $(RM) $(DESTDIR)$(pkgconfdir)/$(PKGFILE) clean: $(RM) core *.a *.o *.gz *.so *.so.* $(RM) -r docs/man $(MAKE) -C test clean check: all $(MAKE) -C test check speed_test: all $(MAKE) -C test speed_test doc: docs/man/$(EXTRA_MAN_FILES).gz TEMPFILE := $(shell mktemp) docs/man/$(EXTRA_MAN_FILES).gz: $(HEADERS) @for file in $(EXTRA_MAN_FILES); do \ $(INSTALL_PROGRAM) -v -m 644 -D docs/$$file docs/man/$$file; \ done cat $(HEADERS) | \ perl docs/doc-preclean.pl > "$(TEMPFILE)" perl docs/kernel-doc -man "$(TEMPFILE)" | \ perl docs/split-man.pl docs/man -rm -f "$(TEMPFILE)" @for file in docs/man/*.3; do \ gzip -f $$file; \ done find docs/man -type f -name \*[0-9].gz multipath-tools-0.7.4/libdmmp/docs/000077500000000000000000000000001320314174000172365ustar00rootroot00000000000000multipath-tools-0.7.4/libdmmp/docs/doc-preclean.pl000066400000000000000000000015461320314174000221350ustar00rootroot00000000000000#!/usr/bin/perl # Copyright (C) 2016 Red Hat, Inc. # # 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 3 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, see . # # Author: Gris Ge use strict; my @REMOVE_KEY_LIST=("DMMP_DLL_EXPORT"); while (<>) { for my $key (@REMOVE_KEY_LIST) { (s/$key//g); } print; } multipath-tools-0.7.4/libdmmp/docs/kernel-doc000066400000000000000000002603111320314174000212070ustar00rootroot00000000000000#!/usr/bin/perl -w use strict; ## Copyright (c) 1998 Michael Zucchi, All Rights Reserved ## ## Copyright (C) 2000, 1 Tim Waugh ## ## Copyright (C) 2001 Simon Huggins ## ## Copyright (C) 2005-2012 Randy Dunlap ## ## Copyright (C) 2012 Dan Luedtke ## ## ## ## #define enhancements by Armin Kuster ## ## Copyright (c) 2000 MontaVista Software, Inc. ## ## ## ## This software falls under the GNU General Public License. ## ## Please read the COPYING file for more information ## # 18/01/2001 - Cleanups # Functions prototyped as foo(void) same as foo() # Stop eval'ing where we don't need to. # -- huggie@earth.li # 27/06/2001 - Allowed whitespace after initial "/**" and # allowed comments before function declarations. # -- Christian Kreibich # Still to do: # - add perldoc documentation # - Look more closely at some of the scarier bits :) # 26/05/2001 - Support for separate source and object trees. # Return error code. # Keith Owens # 23/09/2001 - Added support for typedefs, structs, enums and unions # Support for Context section; can be terminated using empty line # Small fixes (like spaces vs. \s in regex) # -- Tim Jansen # 25/07/2012 - Added support for HTML5 # -- Dan Luedtke sub usage { my $message = <<"EOF"; Usage: $0 [OPTION ...] FILE ... Read C language source or header FILEs, extract embedded documentation comments, and print formatted documentation to standard output. The documentation comments are identified by "/**" opening comment mark. See Documentation/kernel-doc-nano-HOWTO.txt for the documentation comment syntax. Output format selection (mutually exclusive): -docbook Output DocBook format. -html Output HTML format. -html5 Output HTML5 format. -list Output symbol list format. This is for use by docproc. -man Output troff manual page format. This is the default. -rst Output reStructuredText format. -text Output plain text format. Output selection (mutually exclusive): -export Only output documentation for symbols that have been exported using EXPORT_SYMBOL() or EXPORT_SYMBOL_GPL() in any input FILE or -export-file FILE. -internal Only output documentation for symbols that have NOT been exported using EXPORT_SYMBOL() or EXPORT_SYMBOL_GPL() in any input FILE or -export-file FILE. -function NAME Only output documentation for the given function(s) or DOC: section title(s). All other functions and DOC: sections are ignored. May be specified multiple times. -nofunction NAME Do NOT output documentation for the given function(s); only output documentation for the other functions and DOC: sections. May be specified multiple times. Output selection modifiers: -no-doc-sections Do not output DOC: sections. -enable-lineno Enable output of #define LINENO lines. Only works with reStructuredText format. -export-file FILE Specify an additional FILE in which to look for EXPORT_SYMBOL() and EXPORT_SYMBOL_GPL(). To be used with -export or -internal. May be specified multiple times. Other parameters: -v Verbose output, more warnings and other information. -h Print this help. EOF print $message; exit 1; } # # format of comments. # In the following table, (...)? signifies optional structure. # (...)* signifies 0 or more structure elements # /** # * function_name(:)? (- short description)? # (* @parameterx: (description of parameter x)?)* # (* a blank line)? # * (Description:)? (Description of function)? # * (section header: (section description)? )* # (*)?*/ # # So .. the trivial example would be: # # /** # * my_function # */ # # If the Description: header tag is omitted, then there must be a blank line # after the last parameter specification. # e.g. # /** # * my_function - does my stuff # * @my_arg: its mine damnit # * # * Does my stuff explained. # */ # # or, could also use: # /** # * my_function - does my stuff # * @my_arg: its mine damnit # * Description: Does my stuff explained. # */ # etc. # # Besides functions you can also write documentation for structs, unions, # enums and typedefs. Instead of the function name you must write the name # of the declaration; the struct/union/enum/typedef must always precede # the name. Nesting of declarations is not supported. # Use the argument mechanism to document members or constants. # e.g. # /** # * struct my_struct - short description # * @a: first member # * @b: second member # * # * Longer description # */ # struct my_struct { # int a; # int b; # /* private: */ # int c; # }; # # All descriptions can be multiline, except the short function description. # # For really longs structs, you can also describe arguments inside the # body of the struct. # eg. # /** # * struct my_struct - short description # * @a: first member # * @b: second member # * # * Longer description # */ # struct my_struct { # int a; # int b; # /** # * @c: This is longer description of C # * # * You can use paragraphs to describe arguments # * using this method. # */ # int c; # }; # # This should be use only for struct/enum members. # # You can also add additional sections. When documenting kernel functions you # should document the "Context:" of the function, e.g. whether the functions # can be called form interrupts. Unlike other sections you can end it with an # empty line. # A non-void function should have a "Return:" section describing the return # value(s). # Example-sections should contain the string EXAMPLE so that they are marked # appropriately in DocBook. # # Example: # /** # * user_function - function that can only be called in user context # * @a: some argument # * Context: !in_interrupt() # * # * Some description # * Example: # * user_function(22); # */ # ... # # # All descriptive text is further processed, scanning for the following special # patterns, which are highlighted appropriately. # # 'funcname()' - function # '$ENVVAR' - environmental variable # '&struct_name' - name of a structure (up to two words including 'struct') # '@parameter' - name of a parameter # '%CONST' - name of a constant. ## init lots of data my $errors = 0; my $warnings = 0; my $anon_struct_union = 0; # match expressions used to find embedded type information my $type_constant = '\%([-_\w]+)'; my $type_func = '(\w+)\(\)'; my $type_param = '\@(\w+(\.\.\.)?)'; my $type_fp_param = '\@(\w+)\(\)'; # Special RST handling for func ptr params my $type_struct = '\&((struct\s*)*[_\w]+)'; my $type_struct_xml = '\\&((struct\s*)*[_\w]+)'; my $type_env = '(\$\w+)'; my $type_enum_full = '\&(enum)\s*([_\w]+)'; my $type_struct_full = '\&(struct)\s*([_\w]+)'; my $type_typedef_full = '\&(typedef)\s*([_\w]+)'; my $type_union_full = '\&(union)\s*([_\w]+)'; my $type_member = '\&([_\w]+)((\.|->)[_\w]+)'; my $type_member_func = $type_member . '\(\)'; # Output conversion substitutions. # One for each output format # these work fairly well my @highlights_html = ( [$type_constant, "\$1"], [$type_func, "\$1"], [$type_struct_xml, "\$1"], [$type_env, "\$1"], [$type_param, "\$1"] ); my $local_lt = "\\\\\\\\lt:"; my $local_gt = "\\\\\\\\gt:"; my $blankline_html = $local_lt . "p" . $local_gt; # was "

" # html version 5 my @highlights_html5 = ( [$type_constant, "\$1"], [$type_func, "\$1"], [$type_struct_xml, "\$1"], [$type_env, "\$1"], [$type_param, "\$1]"] ); my $blankline_html5 = $local_lt . "br /" . $local_gt; # XML, docbook format my @highlights_xml = ( ["([^=])\\\"([^\\\"<]+)\\\"", "\$1\$2"], [$type_constant, "\$1"], [$type_struct_xml, "\$1"], [$type_param, "\$1"], [$type_func, "\$1"], [$type_env, "\$1"] ); my $blankline_xml = $local_lt . "/para" . $local_gt . $local_lt . "para" . $local_gt . "\n"; # gnome, docbook format my @highlights_gnome = ( [$type_constant, "\$1"], [$type_func, "\$1"], [$type_struct, "\$1"], [$type_env, "\$1"], [$type_param, "\$1" ] ); my $blankline_gnome = "\n"; # these are pretty rough my @highlights_man = ( [$type_constant, "\$1"], [$type_func, "\\\\fB\$1\\\\fP"], [$type_struct, "\\\\fI\$1\\\\fP"], [$type_param, "\\\\fI\$1\\\\fP"] ); my $blankline_man = ""; # text-mode my @highlights_text = ( [$type_constant, "\$1"], [$type_func, "\$1"], [$type_struct, "\$1"], [$type_param, "\$1"] ); my $blankline_text = ""; # rst-mode my @highlights_rst = ( [$type_constant, "``\$1``"], # Note: need to escape () to avoid func matching later [$type_member_func, "\\:c\\:type\\:`\$1\$2\\\\(\\\\) <\$1>`"], [$type_member, "\\:c\\:type\\:`\$1\$2 <\$1>`"], [$type_fp_param, "**\$1\\\\(\\\\)**"], [$type_func, "\\:c\\:func\\:`\$1()`"], [$type_struct_full, "\\:c\\:type\\:`\$1 \$2 <\$2>`"], [$type_enum_full, "\\:c\\:type\\:`\$1 \$2 <\$2>`"], [$type_typedef_full, "\\:c\\:type\\:`\$1 \$2 <\$2>`"], [$type_union_full, "\\:c\\:type\\:`\$1 \$2 <\$2>`"], # in rst this can refer to any type [$type_struct, "\\:c\\:type\\:`\$1`"], [$type_param, "**\$1**"] ); my $blankline_rst = "\n"; # list mode my @highlights_list = ( [$type_constant, "\$1"], [$type_func, "\$1"], [$type_struct, "\$1"], [$type_param, "\$1"] ); my $blankline_list = ""; # read arguments if ($#ARGV == -1) { usage(); } my $kernelversion; my $dohighlight = ""; my $verbose = 0; my $output_mode = "man"; my $output_preformatted = 0; my $no_doc_sections = 0; my $enable_lineno = 0; my @highlights = @highlights_man; my $blankline = $blankline_man; my $modulename = "Kernel API"; use constant { OUTPUT_ALL => 0, # output all symbols and doc sections OUTPUT_INCLUDE => 1, # output only specified symbols OUTPUT_EXCLUDE => 2, # output everything except specified symbols OUTPUT_EXPORTED => 3, # output exported symbols OUTPUT_INTERNAL => 4, # output non-exported symbols }; my $output_selection = OUTPUT_ALL; my $show_not_found = 0; my @export_file_list; my @build_time; if (defined($ENV{'KBUILD_BUILD_TIMESTAMP'}) && (my $seconds = `date -d"${ENV{'KBUILD_BUILD_TIMESTAMP'}}" +%s`) ne '') { @build_time = gmtime($seconds); } else { @build_time = localtime; } my $man_date = ('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December')[$build_time[4]] . " " . ($build_time[5]+1900); # Essentially these are globals. # They probably want to be tidied up, made more localised or something. # CAVEAT EMPTOR! Some of the others I localised may not want to be, which # could cause "use of undefined value" or other bugs. my ($function, %function_table, %parametertypes, $declaration_purpose); my $declaration_start_line; my ($type, $declaration_name, $return_type); my ($newsection, $newcontents, $prototype, $brcount, %source_map); if (defined($ENV{'KBUILD_VERBOSE'})) { $verbose = "$ENV{'KBUILD_VERBOSE'}"; } # Generated docbook code is inserted in a template at a point where # docbook v3.1 requires a non-zero sequence of RefEntry's; see: # http://www.oasis-open.org/docbook/documentation/reference/html/refentry.html # We keep track of number of generated entries and generate a dummy # if needs be to ensure the expanded template can be postprocessed # into html. my $section_counter = 0; my $lineprefix=""; # Parser states use constant { STATE_NORMAL => 0, # normal code STATE_NAME => 1, # looking for function name STATE_FIELD => 2, # scanning field start STATE_PROTO => 3, # scanning prototype STATE_DOCBLOCK => 4, # documentation block STATE_INLINE => 5, # gathering documentation outside main block }; my $state; my $in_doc_sect; # Inline documentation state use constant { STATE_INLINE_NA => 0, # not applicable ($state != STATE_INLINE) STATE_INLINE_NAME => 1, # looking for member name (@foo:) STATE_INLINE_TEXT => 2, # looking for member documentation STATE_INLINE_END => 3, # done STATE_INLINE_ERROR => 4, # error - Comment without header was found. # Spit a warning as it's not # proper kernel-doc and ignore the rest. }; my $inline_doc_state; #declaration types: can be # 'function', 'struct', 'union', 'enum', 'typedef' my $decl_type; my $doc_start = '^/\*\*\s*$'; # Allow whitespace at end of comment start. my $doc_end = '\*/'; my $doc_com = '\s*\*\s*'; my $doc_com_body = '\s*\* ?'; my $doc_decl = $doc_com . '(\w+)'; # @params and a strictly limited set of supported section names my $doc_sect = $doc_com . '\s*(\@[.\w]+|\@\.\.\.|description|context|returns?|notes?|examples?)\s*:(.*)'; my $doc_content = $doc_com_body . '(.*)'; my $doc_block = $doc_com . 'DOC:\s*(.*)?'; my $doc_inline_start = '^\s*/\*\*\s*$'; my $doc_inline_sect = '\s*\*\s*(@[\w\s]+):(.*)'; my $doc_inline_end = '^\s*\*/\s*$'; my $doc_inline_oneline = '^\s*/\*\*\s*(@[\w\s]+):\s*(.*)\s*\*/\s*$'; my $export_symbol = '^\s*EXPORT_SYMBOL(_GPL)?\s*\(\s*(\w+)\s*\)\s*;'; my %parameterdescs; my %parameterdesc_start_lines; my @parameterlist; my %sections; my @sectionlist; my %section_start_lines; my $sectcheck; my $struct_actual; my $contents = ""; my $new_start_line = 0; # the canonical section names. see also $doc_sect above. my $section_default = "Description"; # default section my $section_intro = "Introduction"; my $section = $section_default; my $section_context = "Context"; my $section_return = "Return"; my $undescribed = "-- undescribed --"; reset_state(); while ($ARGV[0] =~ m/^-(.*)/) { my $cmd = shift @ARGV; if ($cmd eq "-html") { $output_mode = "html"; @highlights = @highlights_html; $blankline = $blankline_html; } elsif ($cmd eq "-html5") { $output_mode = "html5"; @highlights = @highlights_html5; $blankline = $blankline_html5; } elsif ($cmd eq "-man") { $output_mode = "man"; @highlights = @highlights_man; $blankline = $blankline_man; } elsif ($cmd eq "-text") { $output_mode = "text"; @highlights = @highlights_text; $blankline = $blankline_text; } elsif ($cmd eq "-rst") { $output_mode = "rst"; @highlights = @highlights_rst; $blankline = $blankline_rst; } elsif ($cmd eq "-docbook") { $output_mode = "xml"; @highlights = @highlights_xml; $blankline = $blankline_xml; } elsif ($cmd eq "-list") { $output_mode = "list"; @highlights = @highlights_list; $blankline = $blankline_list; } elsif ($cmd eq "-gnome") { $output_mode = "gnome"; @highlights = @highlights_gnome; $blankline = $blankline_gnome; } elsif ($cmd eq "-module") { # not needed for XML, inherits from calling document $modulename = shift @ARGV; } elsif ($cmd eq "-function") { # to only output specific functions $output_selection = OUTPUT_INCLUDE; $function = shift @ARGV; $function_table{$function} = 1; } elsif ($cmd eq "-nofunction") { # output all except specific functions $output_selection = OUTPUT_EXCLUDE; $function = shift @ARGV; $function_table{$function} = 1; } elsif ($cmd eq "-export") { # only exported symbols $output_selection = OUTPUT_EXPORTED; %function_table = (); } elsif ($cmd eq "-internal") { # only non-exported symbols $output_selection = OUTPUT_INTERNAL; %function_table = (); } elsif ($cmd eq "-export-file") { my $file = shift @ARGV; push(@export_file_list, $file); } elsif ($cmd eq "-v") { $verbose = 1; } elsif (($cmd eq "-h") || ($cmd eq "--help")) { usage(); } elsif ($cmd eq '-no-doc-sections') { $no_doc_sections = 1; } elsif ($cmd eq '-enable-lineno') { $enable_lineno = 1; } elsif ($cmd eq '-show-not-found') { $show_not_found = 1; } } # continue execution near EOF; # get kernel version from env sub get_kernel_version() { my $version = 'unknown kernel version'; if (defined($ENV{'KERNELVERSION'})) { $version = $ENV{'KERNELVERSION'}; } return $version; } # sub print_lineno { my $lineno = shift; if ($enable_lineno && defined($lineno)) { print "#define LINENO " . $lineno . "\n"; } } ## # dumps section contents to arrays/hashes intended for that purpose. # sub dump_section { my $file = shift; my $name = shift; my $contents = join "\n", @_; if ($name =~ m/$type_param/) { $name = $1; $parameterdescs{$name} = $contents; $sectcheck = $sectcheck . $name . " "; $parameterdesc_start_lines{$name} = $new_start_line; $new_start_line = 0; } elsif ($name eq "@\.\.\.") { $name = "..."; $parameterdescs{$name} = $contents; $sectcheck = $sectcheck . $name . " "; $parameterdesc_start_lines{$name} = $new_start_line; $new_start_line = 0; } else { if (defined($sections{$name}) && ($sections{$name} ne "")) { # Only warn on user specified duplicate section names. if ($name ne $section_default) { print STDERR "${file}:$.: warning: duplicate section name '$name'\n"; ++$warnings; } $sections{$name} .= $contents; } else { $sections{$name} = $contents; push @sectionlist, $name; $section_start_lines{$name} = $new_start_line; $new_start_line = 0; } } } ## # dump DOC: section after checking that it should go out # sub dump_doc_section { my $file = shift; my $name = shift; my $contents = join "\n", @_; if ($no_doc_sections) { return; } if (($output_selection == OUTPUT_ALL) || ($output_selection == OUTPUT_INCLUDE && defined($function_table{$name})) || ($output_selection == OUTPUT_EXCLUDE && !defined($function_table{$name}))) { dump_section($file, $name, $contents); output_blockhead({'sectionlist' => \@sectionlist, 'sections' => \%sections, 'module' => $modulename, 'content-only' => ($output_selection != OUTPUT_ALL), }); } } ## # output function # # parameterdescs, a hash. # function => "function name" # parameterlist => @list of parameters # parameterdescs => %parameter descriptions # sectionlist => @list of sections # sections => %section descriptions # sub output_highlight { my $contents = join "\n",@_; my $line; # DEBUG # if (!defined $contents) { # use Carp; # confess "output_highlight got called with no args?\n"; # } if ($output_mode eq "html" || $output_mode eq "html5" || $output_mode eq "xml") { $contents = local_unescape($contents); # convert data read & converted thru xml_escape() into &xyz; format: $contents =~ s/\\\\\\/\&/g; } # print STDERR "contents b4:$contents\n"; eval $dohighlight; die $@ if $@; # print STDERR "contents af:$contents\n"; # strip whitespaces when generating html5 if ($output_mode eq "html5") { $contents =~ s/^\s+//; $contents =~ s/\s+$//; } foreach $line (split "\n", $contents) { if (! $output_preformatted) { $line =~ s/^\s*//; } if ($line eq ""){ if (! $output_preformatted) { print $lineprefix, local_unescape($blankline); } } else { $line =~ s/\\\\\\/\&/g; if ($output_mode eq "man" && substr($line, 0, 1) eq ".") { print "\\&$line"; } else { print $lineprefix, $line; } } print "\n"; } } # output sections in html sub output_section_html(%) { my %args = %{$_[0]}; my $section; foreach $section (@{$args{'sectionlist'}}) { print "

$section

\n"; print "
\n"; output_highlight($args{'sections'}{$section}); print "
\n"; } } # output enum in html sub output_enum_html(%) { my %args = %{$_[0]}; my ($parameter); my $count; print "

enum " . $args{'enum'} . "

\n"; print "enum " . $args{'enum'} . " {
\n"; $count = 0; foreach $parameter (@{$args{'parameterlist'}}) { print " " . $parameter . ""; if ($count != $#{$args{'parameterlist'}}) { $count++; print ",\n"; } print "
"; } print "};
\n"; print "

Constants

\n"; print "
\n"; foreach $parameter (@{$args{'parameterlist'}}) { print "
" . $parameter . "\n"; print "
"; output_highlight($args{'parameterdescs'}{$parameter}); } print "
\n"; output_section_html(@_); print "
\n"; } # output typedef in html sub output_typedef_html(%) { my %args = %{$_[0]}; my ($parameter); my $count; print "

typedef " . $args{'typedef'} . "

\n"; print "typedef " . $args{'typedef'} . "\n"; output_section_html(@_); print "
\n"; } # output struct in html sub output_struct_html(%) { my %args = %{$_[0]}; my ($parameter); print "

" . $args{'type'} . " " . $args{'struct'} . " - " . $args{'purpose'} . "

\n"; print "" . $args{'type'} . " " . $args{'struct'} . " {
\n"; foreach $parameter (@{$args{'parameterlist'}}) { if ($parameter =~ /^#/) { print "$parameter
\n"; next; } my $parameter_name = $parameter; $parameter_name =~ s/\[.*//; ($args{'parameterdescs'}{$parameter_name} ne $undescribed) || next; $type = $args{'parametertypes'}{$parameter}; if ($type =~ m/([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)/) { # pointer-to-function print "    $1$parameter) ($2);
\n"; } elsif ($type =~ m/^(.*?)\s*(:.*)/) { # bitfield print "    $1 $parameter$2;
\n"; } else { print "    $type $parameter;
\n"; } } print "};
\n"; print "

Members

\n"; print "
\n"; foreach $parameter (@{$args{'parameterlist'}}) { ($parameter =~ /^#/) && next; my $parameter_name = $parameter; $parameter_name =~ s/\[.*//; ($args{'parameterdescs'}{$parameter_name} ne $undescribed) || next; print "
" . $parameter . "\n"; print "
"; output_highlight($args{'parameterdescs'}{$parameter_name}); } print "
\n"; output_section_html(@_); print "
\n"; } # output function in html sub output_function_html(%) { my %args = %{$_[0]}; my ($parameter, $section); my $count; print "

" . $args{'function'} . " - " . $args{'purpose'} . "

\n"; print "" . $args{'functiontype'} . "\n"; print "" . $args{'function'} . "\n"; print "("; $count = 0; foreach $parameter (@{$args{'parameterlist'}}) { $type = $args{'parametertypes'}{$parameter}; if ($type =~ m/([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)/) { # pointer-to-function print "$1$parameter) ($2)"; } else { print "" . $type . " " . $parameter . ""; } if ($count != $#{$args{'parameterlist'}}) { $count++; print ",\n"; } } print ")\n"; print "

Arguments

\n"; print "
\n"; foreach $parameter (@{$args{'parameterlist'}}) { my $parameter_name = $parameter; $parameter_name =~ s/\[.*//; ($args{'parameterdescs'}{$parameter_name} ne $undescribed) || next; print "
" . $parameter . "\n"; print "
"; output_highlight($args{'parameterdescs'}{$parameter_name}); } print "
\n"; output_section_html(@_); print "
\n"; } # output DOC: block header in html sub output_blockhead_html(%) { my %args = %{$_[0]}; my ($parameter, $section); my $count; foreach $section (@{$args{'sectionlist'}}) { print "

$section

\n"; print "
    \n"; output_highlight($args{'sections'}{$section}); print "
\n"; } print "
\n"; } # output sections in html5 sub output_section_html5(%) { my %args = %{$_[0]}; my $section; foreach $section (@{$args{'sectionlist'}}) { print "
\n"; print "

$section

\n"; print "

\n"; output_highlight($args{'sections'}{$section}); print "

\n"; print "
\n"; } } # output enum in html5 sub output_enum_html5(%) { my %args = %{$_[0]}; my ($parameter); my $count; my $html5id; $html5id = $args{'enum'}; $html5id =~ s/[^a-zA-Z0-9\-]+/_/g; print "
"; print "

enum " . $args{'enum'} . "

\n"; print "
    \n"; print "
  1. "; print "enum "; print "" . $args{'enum'} . " {"; print "
  2. \n"; $count = 0; foreach $parameter (@{$args{'parameterlist'}}) { print "
  3. "; print "" . $parameter . ""; if ($count != $#{$args{'parameterlist'}}) { $count++; print ","; } print "
  4. \n"; } print "
  5. };
  6. \n"; print "
\n"; print "
\n"; print "

Constants

\n"; print "
\n"; foreach $parameter (@{$args{'parameterlist'}}) { print "
" . $parameter . "
\n"; print "
"; output_highlight($args{'parameterdescs'}{$parameter}); print "
\n"; } print "
\n"; print "
\n"; output_section_html5(@_); print "
\n"; } # output typedef in html5 sub output_typedef_html5(%) { my %args = %{$_[0]}; my ($parameter); my $count; my $html5id; $html5id = $args{'typedef'}; $html5id =~ s/[^a-zA-Z0-9\-]+/_/g; print "
\n"; print "

typedef " . $args{'typedef'} . "

\n"; print "
    \n"; print "
  1. "; print "typedef "; print "" . $args{'typedef'} . ""; print "
  2. \n"; print "
\n"; output_section_html5(@_); print "
\n"; } # output struct in html5 sub output_struct_html5(%) { my %args = %{$_[0]}; my ($parameter); my $html5id; $html5id = $args{'struct'}; $html5id =~ s/[^a-zA-Z0-9\-]+/_/g; print "
\n"; print "
\n"; print "

" . $args{'type'} . " " . $args{'struct'} . "

"; print "

". $args{'purpose'} . "

\n"; print "
\n"; print "
    \n"; print "
  1. "; print "" . $args{'type'} . " "; print "" . $args{'struct'} . " {"; print "
  2. \n"; foreach $parameter (@{$args{'parameterlist'}}) { print "
  3. "; if ($parameter =~ /^#/) { print "" . $parameter ."\n"; print "
  4. \n"; next; } my $parameter_name = $parameter; $parameter_name =~ s/\[.*//; ($args{'parameterdescs'}{$parameter_name} ne $undescribed) || next; $type = $args{'parametertypes'}{$parameter}; if ($type =~ m/([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)/) { # pointer-to-function print "$1 "; print "$parameter"; print ") "; print "($2);"; } elsif ($type =~ m/^(.*?)\s*(:.*)/) { # bitfield print "$1 "; print "$parameter"; print "$2;"; } else { print "$type "; print "$parameter;"; } print "\n"; } print "
  5. };
  6. \n"; print "
\n"; print "
\n"; print "

Members

\n"; print "
\n"; foreach $parameter (@{$args{'parameterlist'}}) { ($parameter =~ /^#/) && next; my $parameter_name = $parameter; $parameter_name =~ s/\[.*//; ($args{'parameterdescs'}{$parameter_name} ne $undescribed) || next; print "
" . $parameter . "
\n"; print "
"; output_highlight($args{'parameterdescs'}{$parameter_name}); print "
\n"; } print "
\n"; print "
\n"; output_section_html5(@_); print "
\n"; } # output function in html5 sub output_function_html5(%) { my %args = %{$_[0]}; my ($parameter, $section); my $count; my $html5id; $html5id = $args{'function'}; $html5id =~ s/[^a-zA-Z0-9\-]+/_/g; print "
\n"; print "
\n"; print "

" . $args{'function'} . "

"; print "

" . $args{'purpose'} . "

\n"; print "
\n"; print "
    \n"; print "
  1. "; print "" . $args{'functiontype'} . " "; print "" . $args{'function'} . " ("; print "
  2. "; $count = 0; foreach $parameter (@{$args{'parameterlist'}}) { print "
  3. "; $type = $args{'parametertypes'}{$parameter}; if ($type =~ m/([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)/) { # pointer-to-function print "$1 "; print "$parameter"; print ") "; print "($2)"; } else { print "$type "; print "$parameter"; } if ($count != $#{$args{'parameterlist'}}) { $count++; print ","; } print "
  4. \n"; } print "
  5. )
  6. \n"; print "
\n"; print "
\n"; print "

Arguments

\n"; print "

\n"; print "

\n"; foreach $parameter (@{$args{'parameterlist'}}) { my $parameter_name = $parameter; $parameter_name =~ s/\[.*//; ($args{'parameterdescs'}{$parameter_name} ne $undescribed) || next; print "
" . $parameter . "
\n"; print "
"; output_highlight($args{'parameterdescs'}{$parameter_name}); print "
\n"; } print "
\n"; print "
\n"; output_section_html5(@_); print "
\n"; } # output DOC: block header in html5 sub output_blockhead_html5(%) { my %args = %{$_[0]}; my ($parameter, $section); my $count; my $html5id; foreach $section (@{$args{'sectionlist'}}) { $html5id = $section; $html5id =~ s/[^a-zA-Z0-9\-]+/_/g; print "
\n"; print "

$section

\n"; print "

\n"; output_highlight($args{'sections'}{$section}); print "

\n"; } print "
\n"; } sub output_section_xml(%) { my %args = %{$_[0]}; my $section; # print out each section $lineprefix=" "; foreach $section (@{$args{'sectionlist'}}) { print "\n"; print "$section\n"; if ($section =~ m/EXAMPLE/i) { print "\n"; $output_preformatted = 1; } else { print "\n"; } output_highlight($args{'sections'}{$section}); $output_preformatted = 0; if ($section =~ m/EXAMPLE/i) { print "\n"; } else { print "\n"; } print "\n"; } } # output function in XML DocBook sub output_function_xml(%) { my %args = %{$_[0]}; my ($parameter, $section); my $count; my $id; $id = "API-" . $args{'function'}; $id =~ s/[^A-Za-z0-9]/-/g; print "\n"; print "\n"; print " LINUX\n"; print " Kernel Hackers Manual\n"; print " $man_date\n"; print "\n"; print "\n"; print " " . $args{'function'} . "\n"; print " 9\n"; print " " . $kernelversion . "\n"; print "\n"; print "\n"; print " " . $args{'function'} . "\n"; print " \n"; print " "; output_highlight ($args{'purpose'}); print " \n"; print "\n"; print "\n"; print " Synopsis\n"; print " \n"; print " " . $args{'functiontype'} . " "; print "" . $args{'function'} . " \n"; $count = 0; if ($#{$args{'parameterlist'}} >= 0) { foreach $parameter (@{$args{'parameterlist'}}) { $type = $args{'parametertypes'}{$parameter}; if ($type =~ m/([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)/) { # pointer-to-function print " $1$parameter)\n"; print " $2\n"; } else { print " " . $type; print " $parameter\n"; } } } else { print " \n"; } print " \n"; print "\n"; # print parameters print "\n Arguments\n"; if ($#{$args{'parameterlist'}} >= 0) { print " \n"; foreach $parameter (@{$args{'parameterlist'}}) { my $parameter_name = $parameter; $parameter_name =~ s/\[.*//; print " \n $parameter\n"; print " \n \n"; $lineprefix=" "; output_highlight($args{'parameterdescs'}{$parameter_name}); print " \n \n \n"; } print " \n"; } else { print " \n None\n \n"; } print "\n"; output_section_xml(@_); print "\n\n"; } # output struct in XML DocBook sub output_struct_xml(%) { my %args = %{$_[0]}; my ($parameter, $section); my $id; $id = "API-struct-" . $args{'struct'}; $id =~ s/[^A-Za-z0-9]/-/g; print "\n"; print "\n"; print " LINUX\n"; print " Kernel Hackers Manual\n"; print " $man_date\n"; print "\n"; print "\n"; print " " . $args{'type'} . " " . $args{'struct'} . "\n"; print " 9\n"; print " " . $kernelversion . "\n"; print "\n"; print "\n"; print " " . $args{'type'} . " " . $args{'struct'} . "\n"; print " \n"; print " "; output_highlight ($args{'purpose'}); print " \n"; print "\n"; print "\n"; print " Synopsis\n"; print " \n"; print $args{'type'} . " " . $args{'struct'} . " {\n"; foreach $parameter (@{$args{'parameterlist'}}) { if ($parameter =~ /^#/) { my $prm = $parameter; # convert data read & converted thru xml_escape() into &xyz; format: # This allows us to have #define macros interspersed in a struct. $prm =~ s/\\\\\\/\&/g; print "$prm\n"; next; } my $parameter_name = $parameter; $parameter_name =~ s/\[.*//; defined($args{'parameterdescs'}{$parameter_name}) || next; ($args{'parameterdescs'}{$parameter_name} ne $undescribed) || next; $type = $args{'parametertypes'}{$parameter}; if ($type =~ m/([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)/) { # pointer-to-function print " $1 $parameter) ($2);\n"; } elsif ($type =~ m/^(.*?)\s*(:.*)/) { # bitfield print " $1 $parameter$2;\n"; } else { print " " . $type . " " . $parameter . ";\n"; } } print "};"; print " \n"; print "\n"; print " \n"; print " Members\n"; if ($#{$args{'parameterlist'}} >= 0) { print " \n"; foreach $parameter (@{$args{'parameterlist'}}) { ($parameter =~ /^#/) && next; my $parameter_name = $parameter; $parameter_name =~ s/\[.*//; defined($args{'parameterdescs'}{$parameter_name}) || next; ($args{'parameterdescs'}{$parameter_name} ne $undescribed) || next; print " "; print " $parameter\n"; print " \n"; output_highlight($args{'parameterdescs'}{$parameter_name}); print " \n"; print " \n"; } print " \n"; } else { print " \n None\n \n"; } print " \n"; output_section_xml(@_); print "\n\n"; } # output enum in XML DocBook sub output_enum_xml(%) { my %args = %{$_[0]}; my ($parameter, $section); my $count; my $id; $id = "API-enum-" . $args{'enum'}; $id =~ s/[^A-Za-z0-9]/-/g; print "\n"; print "\n"; print " LINUX\n"; print " Kernel Hackers Manual\n"; print " $man_date\n"; print "\n"; print "\n"; print " enum " . $args{'enum'} . "\n"; print " 9\n"; print " " . $kernelversion . "\n"; print "\n"; print "\n"; print " enum " . $args{'enum'} . "\n"; print " \n"; print " "; output_highlight ($args{'purpose'}); print " \n"; print "\n"; print "\n"; print " Synopsis\n"; print " \n"; print "enum " . $args{'enum'} . " {\n"; $count = 0; foreach $parameter (@{$args{'parameterlist'}}) { print " $parameter"; if ($count != $#{$args{'parameterlist'}}) { $count++; print ","; } print "\n"; } print "};"; print " \n"; print "\n"; print "\n"; print " Constants\n"; print " \n"; foreach $parameter (@{$args{'parameterlist'}}) { my $parameter_name = $parameter; $parameter_name =~ s/\[.*//; print " "; print " $parameter\n"; print " \n"; output_highlight($args{'parameterdescs'}{$parameter_name}); print " \n"; print " \n"; } print " \n"; print "\n"; output_section_xml(@_); print "\n\n"; } # output typedef in XML DocBook sub output_typedef_xml(%) { my %args = %{$_[0]}; my ($parameter, $section); my $id; $id = "API-typedef-" . $args{'typedef'}; $id =~ s/[^A-Za-z0-9]/-/g; print "\n"; print "\n"; print " LINUX\n"; print " Kernel Hackers Manual\n"; print " $man_date\n"; print "\n"; print "\n"; print " typedef " . $args{'typedef'} . "\n"; print " 9\n"; print "\n"; print "\n"; print " typedef " . $args{'typedef'} . "\n"; print " \n"; print " "; output_highlight ($args{'purpose'}); print " \n"; print "\n"; print "\n"; print " Synopsis\n"; print " typedef " . $args{'typedef'} . ";\n"; print "\n"; output_section_xml(@_); print "\n\n"; } # output in XML DocBook sub output_blockhead_xml(%) { my %args = %{$_[0]}; my ($parameter, $section); my $count; my $id = $args{'module'}; $id =~ s/[^A-Za-z0-9]/-/g; # print out each section $lineprefix=" "; foreach $section (@{$args{'sectionlist'}}) { if (!$args{'content-only'}) { print "\n $section\n"; } if ($section =~ m/EXAMPLE/i) { print "\n"; $output_preformatted = 1; } else { print "\n"; } output_highlight($args{'sections'}{$section}); $output_preformatted = 0; if ($section =~ m/EXAMPLE/i) { print "\n"; } else { print ""; } if (!$args{'content-only'}) { print "\n\n"; } } print "\n\n"; } # output in XML DocBook sub output_function_gnome { my %args = %{$_[0]}; my ($parameter, $section); my $count; my $id; $id = $args{'module'} . "-" . $args{'function'}; $id =~ s/[^A-Za-z0-9]/-/g; print "\n"; print " " . $args{'function'} . "\n"; print " \n"; print " " . $args{'functiontype'} . " "; print "" . $args{'function'} . " "; print "\n"; $count = 0; if ($#{$args{'parameterlist'}} >= 0) { foreach $parameter (@{$args{'parameterlist'}}) { $type = $args{'parametertypes'}{$parameter}; if ($type =~ m/([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)/) { # pointer-to-function print " $1 $parameter)\n"; print " $2\n"; } else { print " " . $type; print " $parameter\n"; } } } else { print " \n"; } print " \n"; if ($#{$args{'parameterlist'}} >= 0) { print " \n"; print "\n"; print "\n"; print "\n"; print "\n"; foreach $parameter (@{$args{'parameterlist'}}) { my $parameter_name = $parameter; $parameter_name =~ s/\[.*//; print " $parameter\n"; print " \n"; $lineprefix=" "; output_highlight($args{'parameterdescs'}{$parameter_name}); print " \n"; } print " \n"; } else { print " \n None\n \n"; } # print out each section $lineprefix=" "; foreach $section (@{$args{'sectionlist'}}) { print "\n $section\n"; if ($section =~ m/EXAMPLE/i) { print "\n"; $output_preformatted = 1; } else { } print "\n"; output_highlight($args{'sections'}{$section}); $output_preformatted = 0; print "\n"; if ($section =~ m/EXAMPLE/i) { print "\n"; } else { } print " \n"; } print "\n\n"; } ## # output function in man sub output_function_man(%) { my %args = %{$_[0]}; my ($parameter, $section); my $count; print ".TH \"$args{'function'}\" 9 \"$args{'function'}\" \"$man_date\" \"Kernel Hacker's Manual\" LINUX\n"; print ".SH NAME\n"; print $args{'function'} . " \\- " . $args{'purpose'} . "\n"; print ".SH SYNOPSIS\n"; if ($args{'functiontype'} ne "") { print ".B \"" . $args{'functiontype'} . "\" " . $args{'function'} . "\n"; } else { print ".B \"" . $args{'function'} . "\n"; } $count = 0; my $parenth = "("; my $post = ","; foreach my $parameter (@{$args{'parameterlist'}}) { if ($count == $#{$args{'parameterlist'}}) { $post = ");"; } $type = $args{'parametertypes'}{$parameter}; if ($type =~ m/([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)/) { # pointer-to-function print ".BI \"" . $parenth . $1 . "\" " . $parameter . " \") (" . $2 . ")" . $post . "\"\n"; } else { $type =~ s/([^\*])$/$1 /; print ".BI \"" . $parenth . $type . "\" " . $parameter . " \"" . $post . "\"\n"; } $count++; $parenth = ""; } print ".SH ARGUMENTS\n"; foreach $parameter (@{$args{'parameterlist'}}) { my $parameter_name = $parameter; $parameter_name =~ s/\[.*//; print ".IP \"" . $parameter . "\" 12\n"; output_highlight($args{'parameterdescs'}{$parameter_name}); } foreach $section (@{$args{'sectionlist'}}) { print ".SH \"", uc $section, "\"\n"; output_highlight($args{'sections'}{$section}); } } ## # output enum in man sub output_enum_man(%) { my %args = %{$_[0]}; my ($parameter, $section); my $count; print ".TH \"$args{'module'}\" 9 \"enum $args{'enum'}\" \"$man_date\" \"API Manual\" LINUX\n"; print ".SH NAME\n"; print "enum " . $args{'enum'} . " \\- " . $args{'purpose'} . "\n"; print ".SH SYNOPSIS\n"; print "enum " . $args{'enum'} . " {\n"; $count = 0; foreach my $parameter (@{$args{'parameterlist'}}) { print ".br\n.BI \" $parameter\"\n"; if ($count == $#{$args{'parameterlist'}}) { print "\n};\n"; last; } else { print ", \n.br\n"; } $count++; } print ".SH Constants\n"; foreach $parameter (@{$args{'parameterlist'}}) { my $parameter_name = $parameter; $parameter_name =~ s/\[.*//; print ".IP \"" . $parameter . "\" 12\n"; output_highlight($args{'parameterdescs'}{$parameter_name}); } foreach $section (@{$args{'sectionlist'}}) { print ".SH \"$section\"\n"; output_highlight($args{'sections'}{$section}); } } ## # output struct in man sub output_struct_man(%) { my %args = %{$_[0]}; my ($parameter, $section); print ".TH \"$args{'module'}\" 9 \"" . $args{'type'} . " " . $args{'struct'} . "\" \"$man_date\" \"API Manual\" LINUX\n"; print ".SH NAME\n"; print $args{'type'} . " " . $args{'struct'} . " \\- " . $args{'purpose'} . "\n"; print ".SH SYNOPSIS\n"; print $args{'type'} . " " . $args{'struct'} . " {\n.br\n"; foreach my $parameter (@{$args{'parameterlist'}}) { if ($parameter =~ /^#/) { print ".BI \"$parameter\"\n.br\n"; next; } my $parameter_name = $parameter; $parameter_name =~ s/\[.*//; ($args{'parameterdescs'}{$parameter_name} ne $undescribed) || next; $type = $args{'parametertypes'}{$parameter}; if ($type =~ m/([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)/) { # pointer-to-function print ".BI \" " . $1 . "\" " . $parameter . " \") (" . $2 . ")" . "\"\n;\n"; } elsif ($type =~ m/^(.*?)\s*(:.*)/) { # bitfield print ".BI \" " . $1 . "\ \" " . $parameter . $2 . " \"" . "\"\n;\n"; } else { $type =~ s/([^\*])$/$1 /; print ".BI \" " . $type . "\" " . $parameter . " \"" . "\"\n;\n"; } print "\n.br\n"; } print "};\n.br\n"; print ".SH Members\n"; foreach $parameter (@{$args{'parameterlist'}}) { ($parameter =~ /^#/) && next; my $parameter_name = $parameter; $parameter_name =~ s/\[.*//; ($args{'parameterdescs'}{$parameter_name} ne $undescribed) || next; print ".IP \"" . $parameter . "\" 12\n"; output_highlight($args{'parameterdescs'}{$parameter_name}); } foreach $section (@{$args{'sectionlist'}}) { print ".SH \"$section\"\n"; output_highlight($args{'sections'}{$section}); } } ## # output typedef in man sub output_typedef_man(%) { my %args = %{$_[0]}; my ($parameter, $section); print ".TH \"$args{'module'}\" 9 \"$args{'typedef'}\" \"$man_date\" \"API Manual\" LINUX\n"; print ".SH NAME\n"; print "typedef " . $args{'typedef'} . " \\- " . $args{'purpose'} . "\n"; foreach $section (@{$args{'sectionlist'}}) { print ".SH \"$section\"\n"; output_highlight($args{'sections'}{$section}); } } sub output_blockhead_man(%) { my %args = %{$_[0]}; my ($parameter, $section); my $count; print ".TH \"$args{'module'}\" 9 \"$args{'module'}\" \"$man_date\" \"API Manual\" LINUX\n"; foreach $section (@{$args{'sectionlist'}}) { print ".SH \"$section\"\n"; output_highlight($args{'sections'}{$section}); } } ## # output in text sub output_function_text(%) { my %args = %{$_[0]}; my ($parameter, $section); my $start; print "Name:\n\n"; print $args{'function'} . " - " . $args{'purpose'} . "\n"; print "\nSynopsis:\n\n"; if ($args{'functiontype'} ne "") { $start = $args{'functiontype'} . " " . $args{'function'} . " ("; } else { $start = $args{'function'} . " ("; } print $start; my $count = 0; foreach my $parameter (@{$args{'parameterlist'}}) { $type = $args{'parametertypes'}{$parameter}; if ($type =~ m/([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)/) { # pointer-to-function print $1 . $parameter . ") (" . $2; } else { print $type . " " . $parameter; } if ($count != $#{$args{'parameterlist'}}) { $count++; print ",\n"; print " " x length($start); } else { print ");\n\n"; } } print "Arguments:\n\n"; foreach $parameter (@{$args{'parameterlist'}}) { my $parameter_name = $parameter; $parameter_name =~ s/\[.*//; print $parameter . "\n\t" . $args{'parameterdescs'}{$parameter_name} . "\n"; } output_section_text(@_); } #output sections in text sub output_section_text(%) { my %args = %{$_[0]}; my $section; print "\n"; foreach $section (@{$args{'sectionlist'}}) { print "$section:\n\n"; output_highlight($args{'sections'}{$section}); } print "\n\n"; } # output enum in text sub output_enum_text(%) { my %args = %{$_[0]}; my ($parameter); my $count; print "Enum:\n\n"; print "enum " . $args{'enum'} . " - " . $args{'purpose'} . "\n\n"; print "enum " . $args{'enum'} . " {\n"; $count = 0; foreach $parameter (@{$args{'parameterlist'}}) { print "\t$parameter"; if ($count != $#{$args{'parameterlist'}}) { $count++; print ","; } print "\n"; } print "};\n\n"; print "Constants:\n\n"; foreach $parameter (@{$args{'parameterlist'}}) { print "$parameter\n\t"; print $args{'parameterdescs'}{$parameter} . "\n"; } output_section_text(@_); } # output typedef in text sub output_typedef_text(%) { my %args = %{$_[0]}; my ($parameter); my $count; print "Typedef:\n\n"; print "typedef " . $args{'typedef'} . " - " . $args{'purpose'} . "\n"; output_section_text(@_); } # output struct as text sub output_struct_text(%) { my %args = %{$_[0]}; my ($parameter); print $args{'type'} . " " . $args{'struct'} . " - " . $args{'purpose'} . "\n\n"; print $args{'type'} . " " . $args{'struct'} . " {\n"; foreach $parameter (@{$args{'parameterlist'}}) { if ($parameter =~ /^#/) { print "$parameter\n"; next; } my $parameter_name = $parameter; $parameter_name =~ s/\[.*//; ($args{'parameterdescs'}{$parameter_name} ne $undescribed) || next; $type = $args{'parametertypes'}{$parameter}; if ($type =~ m/([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)/) { # pointer-to-function print "\t$1 $parameter) ($2);\n"; } elsif ($type =~ m/^(.*?)\s*(:.*)/) { # bitfield print "\t$1 $parameter$2;\n"; } else { print "\t" . $type . " " . $parameter . ";\n"; } } print "};\n\n"; print "Members:\n\n"; foreach $parameter (@{$args{'parameterlist'}}) { ($parameter =~ /^#/) && next; my $parameter_name = $parameter; $parameter_name =~ s/\[.*//; ($args{'parameterdescs'}{$parameter_name} ne $undescribed) || next; print "$parameter\n\t"; print $args{'parameterdescs'}{$parameter_name} . "\n"; } print "\n"; output_section_text(@_); } sub output_blockhead_text(%) { my %args = %{$_[0]}; my ($parameter, $section); foreach $section (@{$args{'sectionlist'}}) { print " $section:\n"; print " -> "; output_highlight($args{'sections'}{$section}); } } ## # output in restructured text # # # This could use some work; it's used to output the DOC: sections, and # starts by putting out the name of the doc section itself, but that tends # to duplicate a header already in the template file. # sub output_blockhead_rst(%) { my %args = %{$_[0]}; my ($parameter, $section); foreach $section (@{$args{'sectionlist'}}) { if ($output_selection != OUTPUT_INCLUDE) { print "**$section**\n\n"; } print_lineno($section_start_lines{$section}); output_highlight_rst($args{'sections'}{$section}); print "\n"; } } sub output_highlight_rst { my $contents = join "\n",@_; my $line; # undo the evil effects of xml_escape() earlier $contents = xml_unescape($contents); eval $dohighlight; die $@ if $@; foreach $line (split "\n", $contents) { print $lineprefix . $line . "\n"; } } sub output_function_rst(%) { my %args = %{$_[0]}; my ($parameter, $section); my $oldprefix = $lineprefix; my $start = ""; if ($args{'typedef'}) { print ".. c:type:: ". $args{'function'} . "\n\n"; print_lineno($declaration_start_line); print " **Typedef**: "; $lineprefix = ""; output_highlight_rst($args{'purpose'}); $start = "\n\n**Syntax**\n\n ``"; } else { print ".. c:function:: "; } if ($args{'functiontype'} ne "") { $start .= $args{'functiontype'} . " " . $args{'function'} . " ("; } else { $start .= $args{'function'} . " ("; } print $start; my $count = 0; foreach my $parameter (@{$args{'parameterlist'}}) { if ($count ne 0) { print ", "; } $count++; $type = $args{'parametertypes'}{$parameter}; if ($type =~ m/([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)/) { # pointer-to-function print $1 . $parameter . ") (" . $2; } else { print $type . " " . $parameter; } } if ($args{'typedef'}) { print ");``\n\n"; } else { print ")\n\n"; print_lineno($declaration_start_line); $lineprefix = " "; output_highlight_rst($args{'purpose'}); print "\n"; } print "**Parameters**\n\n"; $lineprefix = " "; foreach $parameter (@{$args{'parameterlist'}}) { my $parameter_name = $parameter; #$parameter_name =~ s/\[.*//; $type = $args{'parametertypes'}{$parameter}; if ($type ne "") { print "``$type $parameter``\n"; } else { print "``$parameter``\n"; } print_lineno($parameterdesc_start_lines{$parameter_name}); if (defined($args{'parameterdescs'}{$parameter_name}) && $args{'parameterdescs'}{$parameter_name} ne $undescribed) { output_highlight_rst($args{'parameterdescs'}{$parameter_name}); } else { print " *undescribed*\n"; } print "\n"; } $lineprefix = $oldprefix; output_section_rst(@_); } sub output_section_rst(%) { my %args = %{$_[0]}; my $section; my $oldprefix = $lineprefix; $lineprefix = ""; foreach $section (@{$args{'sectionlist'}}) { print "**$section**\n\n"; print_lineno($section_start_lines{$section}); output_highlight_rst($args{'sections'}{$section}); print "\n"; } print "\n"; $lineprefix = $oldprefix; } sub output_enum_rst(%) { my %args = %{$_[0]}; my ($parameter); my $oldprefix = $lineprefix; my $count; my $name = "enum " . $args{'enum'}; print "\n\n.. c:type:: " . $name . "\n\n"; print_lineno($declaration_start_line); $lineprefix = " "; output_highlight_rst($args{'purpose'}); print "\n"; print "**Constants**\n\n"; $lineprefix = " "; foreach $parameter (@{$args{'parameterlist'}}) { print "``$parameter``\n"; if ($args{'parameterdescs'}{$parameter} ne $undescribed) { output_highlight_rst($args{'parameterdescs'}{$parameter}); } else { print " *undescribed*\n"; } print "\n"; } $lineprefix = $oldprefix; output_section_rst(@_); } sub output_typedef_rst(%) { my %args = %{$_[0]}; my ($parameter); my $oldprefix = $lineprefix; my $name = "typedef " . $args{'typedef'}; print "\n\n.. c:type:: " . $name . "\n\n"; print_lineno($declaration_start_line); $lineprefix = " "; output_highlight_rst($args{'purpose'}); print "\n"; $lineprefix = $oldprefix; output_section_rst(@_); } sub output_struct_rst(%) { my %args = %{$_[0]}; my ($parameter); my $oldprefix = $lineprefix; my $name = $args{'type'} . " " . $args{'struct'}; print "\n\n.. c:type:: " . $name . "\n\n"; print_lineno($declaration_start_line); $lineprefix = " "; output_highlight_rst($args{'purpose'}); print "\n"; print "**Definition**\n\n"; print "::\n\n"; print " " . $args{'type'} . " " . $args{'struct'} . " {\n"; foreach $parameter (@{$args{'parameterlist'}}) { if ($parameter =~ /^#/) { print " " . "$parameter\n"; next; } my $parameter_name = $parameter; $parameter_name =~ s/\[.*//; ($args{'parameterdescs'}{$parameter_name} ne $undescribed) || next; $type = $args{'parametertypes'}{$parameter}; if ($type =~ m/([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)/) { # pointer-to-function print " $1 $parameter) ($2);\n"; } elsif ($type =~ m/^(.*?)\s*(:.*)/) { # bitfield print " $1 $parameter$2;\n"; } else { print " " . $type . " " . $parameter . ";\n"; } } print " };\n\n"; print "**Members**\n\n"; $lineprefix = " "; foreach $parameter (@{$args{'parameterlist'}}) { ($parameter =~ /^#/) && next; my $parameter_name = $parameter; $parameter_name =~ s/\[.*//; ($args{'parameterdescs'}{$parameter_name} ne $undescribed) || next; $type = $args{'parametertypes'}{$parameter}; print_lineno($parameterdesc_start_lines{$parameter_name}); print "``" . $parameter . "``\n"; output_highlight_rst($args{'parameterdescs'}{$parameter_name}); print "\n"; } print "\n"; $lineprefix = $oldprefix; output_section_rst(@_); } ## list mode output functions sub output_function_list(%) { my %args = %{$_[0]}; print $args{'function'} . "\n"; } # output enum in list sub output_enum_list(%) { my %args = %{$_[0]}; print $args{'enum'} . "\n"; } # output typedef in list sub output_typedef_list(%) { my %args = %{$_[0]}; print $args{'typedef'} . "\n"; } # output struct as list sub output_struct_list(%) { my %args = %{$_[0]}; print $args{'struct'} . "\n"; } sub output_blockhead_list(%) { my %args = %{$_[0]}; my ($parameter, $section); foreach $section (@{$args{'sectionlist'}}) { print "DOC: $section\n"; } } ## # generic output function for all types (function, struct/union, typedef, enum); # calls the generated, variable output_ function name based on # functype and output_mode sub output_declaration { no strict 'refs'; my $name = shift; my $functype = shift; my $func = "output_${functype}_$output_mode"; if (($output_selection == OUTPUT_ALL) || (($output_selection == OUTPUT_INCLUDE || $output_selection == OUTPUT_EXPORTED) && defined($function_table{$name})) || (($output_selection == OUTPUT_EXCLUDE || $output_selection == OUTPUT_INTERNAL) && !($functype eq "function" && defined($function_table{$name})))) { &$func(@_); $section_counter++; } } ## # generic output function - calls the right one based on current output mode. sub output_blockhead { no strict 'refs'; my $func = "output_blockhead_" . $output_mode; &$func(@_); $section_counter++; } ## # takes a declaration (struct, union, enum, typedef) and # invokes the right handler. NOT called for functions. sub dump_declaration($$) { no strict 'refs'; my ($prototype, $file) = @_; my $func = "dump_" . $decl_type; &$func(@_); } sub dump_union($$) { dump_struct(@_); } sub dump_struct($$) { my $x = shift; my $file = shift; my $nested; if ($x =~ /(struct|union)\s+(\w+)\s*{(.*)}/) { #my $decl_type = $1; $declaration_name = $2; my $members = $3; # ignore embedded structs or unions $members =~ s/({.*})//g; $nested = $1; # ignore members marked private: $members =~ s/\/\*\s*private:.*?\/\*\s*public:.*?\*\///gosi; $members =~ s/\/\*\s*private:.*//gosi; # strip comments: $members =~ s/\/\*.*?\*\///gos; $nested =~ s/\/\*.*?\*\///gos; # strip kmemcheck_bitfield_{begin,end}.*; $members =~ s/kmemcheck_bitfield_.*?;//gos; # strip attributes $members =~ s/__attribute__\s*\(\([a-z,_\*\s\(\)]*\)\)//i; $members =~ s/__aligned\s*\([^;]*\)//gos; $members =~ s/\s*CRYPTO_MINALIGN_ATTR//gos; # replace DECLARE_BITMAP $members =~ s/DECLARE_BITMAP\s*\(([^,)]+), ([^,)]+)\)/unsigned long $1\[BITS_TO_LONGS($2)\]/gos; create_parameterlist($members, ';', $file); check_sections($file, $declaration_name, "struct", $sectcheck, $struct_actual, $nested); output_declaration($declaration_name, 'struct', {'struct' => $declaration_name, 'module' => $modulename, 'parameterlist' => \@parameterlist, 'parameterdescs' => \%parameterdescs, 'parametertypes' => \%parametertypes, 'sectionlist' => \@sectionlist, 'sections' => \%sections, 'purpose' => $declaration_purpose, 'type' => $decl_type }); } else { print STDERR "${file}:$.: error: Cannot parse struct or union!\n"; ++$errors; } } sub dump_enum($$) { my $x = shift; my $file = shift; $x =~ s@/\*.*?\*/@@gos; # strip comments. # strip #define macros inside enums $x =~ s@#\s*((define|ifdef)\s+|endif)[^;]*;@@gos; if ($x =~ /enum\s+(\w+)\s*{(.*)}/) { $declaration_name = $1; my $members = $2; foreach my $arg (split ',', $members) { $arg =~ s/^\s*(\w+).*/$1/; push @parameterlist, $arg; if (!$parameterdescs{$arg}) { $parameterdescs{$arg} = $undescribed; print STDERR "${file}:$.: warning: Enum value '$arg' ". "not described in enum '$declaration_name'\n"; } } output_declaration($declaration_name, 'enum', {'enum' => $declaration_name, 'module' => $modulename, 'parameterlist' => \@parameterlist, 'parameterdescs' => \%parameterdescs, 'sectionlist' => \@sectionlist, 'sections' => \%sections, 'purpose' => $declaration_purpose }); } else { print STDERR "${file}:$.: error: Cannot parse enum!\n"; ++$errors; } } sub dump_typedef($$) { my $x = shift; my $file = shift; $x =~ s@/\*.*?\*/@@gos; # strip comments. # Parse function prototypes if ($x =~ /typedef\s+(\w+)\s*\(\*\s*(\w\S+)\s*\)\s*\((.*)\);/ || $x =~ /typedef\s+(\w+)\s*(\w\S+)\s*\s*\((.*)\);/) { # Function typedefs $return_type = $1; $declaration_name = $2; my $args = $3; create_parameterlist($args, ',', $file); output_declaration($declaration_name, 'function', {'function' => $declaration_name, 'typedef' => 1, 'module' => $modulename, 'functiontype' => $return_type, 'parameterlist' => \@parameterlist, 'parameterdescs' => \%parameterdescs, 'parametertypes' => \%parametertypes, 'sectionlist' => \@sectionlist, 'sections' => \%sections, 'purpose' => $declaration_purpose }); return; } while (($x =~ /\(*.\)\s*;$/) || ($x =~ /\[*.\]\s*;$/)) { $x =~ s/\(*.\)\s*;$/;/; $x =~ s/\[*.\]\s*;$/;/; } if ($x =~ /typedef.*\s+(\w+)\s*;/) { $declaration_name = $1; output_declaration($declaration_name, 'typedef', {'typedef' => $declaration_name, 'module' => $modulename, 'sectionlist' => \@sectionlist, 'sections' => \%sections, 'purpose' => $declaration_purpose }); } else { print STDERR "${file}:$.: error: Cannot parse typedef!\n"; ++$errors; } } sub save_struct_actual($) { my $actual = shift; # strip all spaces from the actual param so that it looks like one string item $actual =~ s/\s*//g; $struct_actual = $struct_actual . $actual . " "; } sub create_parameterlist($$$) { my $args = shift; my $splitter = shift; my $file = shift; my $type; my $param; # temporarily replace commas inside function pointer definition while ($args =~ /(\([^\),]+),/) { $args =~ s/(\([^\),]+),/$1#/g; } foreach my $arg (split($splitter, $args)) { # strip comments $arg =~ s/\/\*.*\*\///; # strip leading/trailing spaces $arg =~ s/^\s*//; $arg =~ s/\s*$//; $arg =~ s/\s+/ /; if ($arg =~ /^#/) { # Treat preprocessor directive as a typeless variable just to fill # corresponding data structures "correctly". Catch it later in # output_* subs. push_parameter($arg, "", $file); } elsif ($arg =~ m/\(.+\)\s*\(/) { # pointer-to-function $arg =~ tr/#/,/; $arg =~ m/[^\(]+\(\*?\s*(\w*)\s*\)/; $param = $1; $type = $arg; $type =~ s/([^\(]+\(\*?)\s*$param/$1/; save_struct_actual($param); push_parameter($param, $type, $file); } elsif ($arg) { $arg =~ s/\s*:\s*/:/g; $arg =~ s/\s*\[/\[/g; my @args = split('\s*,\s*', $arg); if ($args[0] =~ m/\*/) { $args[0] =~ s/(\*+)\s*/ $1/; } my @first_arg; if ($args[0] =~ /^(.*\s+)(.*?\[.*\].*)$/) { shift @args; push(@first_arg, split('\s+', $1)); push(@first_arg, $2); } else { @first_arg = split('\s+', shift @args); } unshift(@args, pop @first_arg); $type = join " ", @first_arg; foreach $param (@args) { if ($param =~ m/^(\*+)\s*(.*)/) { save_struct_actual($2); push_parameter($2, "$type $1", $file); } elsif ($param =~ m/(.*?):(\d+)/) { if ($type ne "") { # skip unnamed bit-fields save_struct_actual($1); push_parameter($1, "$type:$2", $file) } } else { save_struct_actual($param); push_parameter($param, $type, $file); } } } } } sub push_parameter($$$) { my $param = shift; my $type = shift; my $file = shift; if (($anon_struct_union == 1) && ($type eq "") && ($param eq "}")) { return; # ignore the ending }; from anon. struct/union } $anon_struct_union = 0; my $param_name = $param; $param_name =~ s/\[.*//; if ($type eq "" && $param =~ /\.\.\.$/) { if (!$param =~ /\w\.\.\.$/) { # handles unnamed variable parameters $param = "..."; } if (!defined $parameterdescs{$param} || $parameterdescs{$param} eq "") { $parameterdescs{$param} = "variable arguments"; } } elsif ($type eq "" && ($param eq "" or $param eq "void")) { $param="void"; $parameterdescs{void} = "no arguments"; } elsif ($type eq "" && ($param eq "struct" or $param eq "union")) # handle unnamed (anonymous) union or struct: { $type = $param; $param = "{unnamed_" . $param . "}"; $parameterdescs{$param} = "anonymous\n"; $anon_struct_union = 1; } # warn if parameter has no description # (but ignore ones starting with # as these are not parameters # but inline preprocessor statements); # also ignore unnamed structs/unions; if (!$anon_struct_union) { if (!defined $parameterdescs{$param_name} && $param_name !~ /^#/) { $parameterdescs{$param_name} = $undescribed; if (($type eq 'function') || ($type eq 'enum')) { print STDERR "${file}:$.: warning: Function parameter ". "or member '$param' not " . "described in '$declaration_name'\n"; } print STDERR "${file}:$.: warning:" . " No description found for parameter '$param'\n"; ++$warnings; } } $param = xml_escape($param); # strip spaces from $param so that it is one continuous string # on @parameterlist; # this fixes a problem where check_sections() cannot find # a parameter like "addr[6 + 2]" because it actually appears # as "addr[6", "+", "2]" on the parameter list; # but it's better to maintain the param string unchanged for output, # so just weaken the string compare in check_sections() to ignore # "[blah" in a parameter string; ###$param =~ s/\s*//g; push @parameterlist, $param; $parametertypes{$param} = $type; } sub check_sections($$$$$$) { my ($file, $decl_name, $decl_type, $sectcheck, $prmscheck, $nested) = @_; my @sects = split ' ', $sectcheck; my @prms = split ' ', $prmscheck; my $err; my ($px, $sx); my $prm_clean; # strip trailing "[array size]" and/or beginning "*" foreach $sx (0 .. $#sects) { $err = 1; foreach $px (0 .. $#prms) { $prm_clean = $prms[$px]; $prm_clean =~ s/\[.*\]//; $prm_clean =~ s/__attribute__\s*\(\([a-z,_\*\s\(\)]*\)\)//i; # ignore array size in a parameter string; # however, the original param string may contain # spaces, e.g.: addr[6 + 2] # and this appears in @prms as "addr[6" since the # parameter list is split at spaces; # hence just ignore "[..." for the sections check; $prm_clean =~ s/\[.*//; ##$prm_clean =~ s/^\**//; if ($prm_clean eq $sects[$sx]) { $err = 0; last; } } if ($err) { if ($decl_type eq "function") { print STDERR "${file}:$.: warning: " . "Excess function parameter " . "'$sects[$sx]' " . "description in '$decl_name'\n"; ++$warnings; } else { if ($nested !~ m/\Q$sects[$sx]\E/) { print STDERR "${file}:$.: warning: " . "Excess struct/union/enum/typedef member " . "'$sects[$sx]' " . "description in '$decl_name'\n"; ++$warnings; } } } } } ## # Checks the section describing the return value of a function. sub check_return_section { my $file = shift; my $declaration_name = shift; my $return_type = shift; # Ignore an empty return type (It's a macro) # Ignore functions with a "void" return type. (But don't ignore "void *") if (($return_type eq "") || ($return_type =~ /void\s*\w*\s*$/)) { return; } if (!defined($sections{$section_return}) || $sections{$section_return} eq "") { print STDERR "${file}:$.: warning: " . "No description found for return value of " . "'$declaration_name'\n"; ++$warnings; } } ## # takes a function prototype and the name of the current file being # processed and spits out all the details stored in the global # arrays/hashes. sub dump_function($$) { my $prototype = shift; my $file = shift; my $noret = 0; $prototype =~ s/^static +//; $prototype =~ s/^extern +//; $prototype =~ s/^asmlinkage +//; $prototype =~ s/^inline +//; $prototype =~ s/^__inline__ +//; $prototype =~ s/^__inline +//; $prototype =~ s/^__always_inline +//; $prototype =~ s/^noinline +//; $prototype =~ s/__init +//; $prototype =~ s/__init_or_module +//; $prototype =~ s/__meminit +//; $prototype =~ s/__must_check +//; $prototype =~ s/__weak +//; my $define = $prototype =~ s/^#\s*define\s+//; #ak added $prototype =~ s/__attribute__\s*\(\([a-z,]*\)\)//; # Yes, this truly is vile. We are looking for: # 1. Return type (may be nothing if we're looking at a macro) # 2. Function name # 3. Function parameters. # # All the while we have to watch out for function pointer parameters # (which IIRC is what the two sections are for), C types (these # regexps don't even start to express all the possibilities), and # so on. # # If you mess with these regexps, it's a good idea to check that # the following functions' documentation still comes out right: # - parport_register_device (function pointer parameters) # - atomic_set (macro) # - pci_match_device, __copy_to_user (long return type) if ($define && $prototype =~ m/^()([a-zA-Z0-9_~:]+)\s+/) { # This is an object-like macro, it has no return type and no parameter # list. # Function-like macros are not allowed to have spaces between # declaration_name and opening parenthesis (notice the \s+). $return_type = $1; $declaration_name = $2; $noret = 1; } elsif ($prototype =~ m/^()([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ || $prototype =~ m/^(\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ || $prototype =~ m/^(\w+\s*\*)\s*([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ || $prototype =~ m/^(\w+\s+\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ || $prototype =~ m/^(\w+\s+\w+\s*\*+)\s*([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ || $prototype =~ m/^(\w+\s+\w+\s+\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ || $prototype =~ m/^(\w+\s+\w+\s+\w+\s*\*)\s*([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ || $prototype =~ m/^()([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ || $prototype =~ m/^(\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ || $prototype =~ m/^(\w+\s*\*)\s*([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ || $prototype =~ m/^(\w+\s+\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ || $prototype =~ m/^(\w+\s+\w+\s*\*)\s*([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ || $prototype =~ m/^(\w+\s+\w+\s+\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ || $prototype =~ m/^(\w+\s+\w+\s+\w+\s*\*)\s*([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ || $prototype =~ m/^(\w+\s+\w+\s+\w+\s+\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ || $prototype =~ m/^(\w+\s+\w+\s+\w+\s+\w+\s*\*)\s*([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ || $prototype =~ m/^(\w+\s+\w+\s*\*\s*\w+\s*\*\s*)\s*([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/) { $return_type = $1; $declaration_name = $2; my $args = $3; create_parameterlist($args, ',', $file); } else { print STDERR "${file}:$.: warning: cannot understand function prototype: '$prototype'\n"; return; } my $prms = join " ", @parameterlist; check_sections($file, $declaration_name, "function", $sectcheck, $prms, ""); # This check emits a lot of warnings at the moment, because many # functions don't have a 'Return' doc section. So until the number # of warnings goes sufficiently down, the check is only performed in # verbose mode. # TODO: always perform the check. if ($verbose && !$noret) { check_return_section($file, $declaration_name, $return_type); } output_declaration($declaration_name, 'function', {'function' => $declaration_name, 'module' => $modulename, 'functiontype' => $return_type, 'parameterlist' => \@parameterlist, 'parameterdescs' => \%parameterdescs, 'parametertypes' => \%parametertypes, 'sectionlist' => \@sectionlist, 'sections' => \%sections, 'purpose' => $declaration_purpose }); } sub reset_state { $function = ""; %parameterdescs = (); %parametertypes = (); @parameterlist = (); %sections = (); @sectionlist = (); $sectcheck = ""; $struct_actual = ""; $prototype = ""; $state = STATE_NORMAL; $inline_doc_state = STATE_INLINE_NA; } sub tracepoint_munge($) { my $file = shift; my $tracepointname = 0; my $tracepointargs = 0; if ($prototype =~ m/TRACE_EVENT\((.*?),/) { $tracepointname = $1; } if ($prototype =~ m/DEFINE_SINGLE_EVENT\((.*?),/) { $tracepointname = $1; } if ($prototype =~ m/DEFINE_EVENT\((.*?),(.*?),/) { $tracepointname = $2; } $tracepointname =~ s/^\s+//; #strip leading whitespace if ($prototype =~ m/TP_PROTO\((.*?)\)/) { $tracepointargs = $1; } if (($tracepointname eq 0) || ($tracepointargs eq 0)) { print STDERR "${file}:$.: warning: Unrecognized tracepoint format: \n". "$prototype\n"; } else { $prototype = "static inline void trace_$tracepointname($tracepointargs)"; } } sub syscall_munge() { my $void = 0; $prototype =~ s@[\r\n\t]+@ @gos; # strip newlines/CR's/tabs ## if ($prototype =~ m/SYSCALL_DEFINE0\s*\(\s*(a-zA-Z0-9_)*\s*\)/) { if ($prototype =~ m/SYSCALL_DEFINE0/) { $void = 1; ## $prototype = "long sys_$1(void)"; } $prototype =~ s/SYSCALL_DEFINE.*\(/long sys_/; # fix return type & func name if ($prototype =~ m/long (sys_.*?),/) { $prototype =~ s/,/\(/; } elsif ($void) { $prototype =~ s/\)/\(void\)/; } # now delete all of the odd-number commas in $prototype # so that arg types & arg names don't have a comma between them my $count = 0; my $len = length($prototype); if ($void) { $len = 0; # skip the for-loop } for (my $ix = 0; $ix < $len; $ix++) { if (substr($prototype, $ix, 1) eq ',') { $count++; if ($count % 2 == 1) { substr($prototype, $ix, 1) = ' '; } } } } sub process_proto_function($$) { my $x = shift; my $file = shift; $x =~ s@\/\/.*$@@gos; # strip C99-style comments to end of line if ($x =~ m#\s*/\*\s+MACDOC\s*#io || ($x =~ /^#/ && $x !~ /^#\s*define/)) { # do nothing } elsif ($x =~ /([^\{]*)/) { $prototype .= $1; } if (($x =~ /\{/) || ($x =~ /\#\s*define/) || ($x =~ /;/)) { $prototype =~ s@/\*.*?\*/@@gos; # strip comments. $prototype =~ s@[\r\n]+@ @gos; # strip newlines/cr's. $prototype =~ s@^\s+@@gos; # strip leading spaces if ($prototype =~ /SYSCALL_DEFINE/) { syscall_munge(); } if ($prototype =~ /TRACE_EVENT/ || $prototype =~ /DEFINE_EVENT/ || $prototype =~ /DEFINE_SINGLE_EVENT/) { tracepoint_munge($file); } dump_function($prototype, $file); reset_state(); } } sub process_proto_type($$) { my $x = shift; my $file = shift; $x =~ s@[\r\n]+@ @gos; # strip newlines/cr's. $x =~ s@^\s+@@gos; # strip leading spaces $x =~ s@\s+$@@gos; # strip trailing spaces $x =~ s@\/\/.*$@@gos; # strip C99-style comments to end of line if ($x =~ /^#/) { # To distinguish preprocessor directive from regular declaration later. $x .= ";"; } while (1) { if ( $x =~ /([^{};]*)([{};])(.*)/ ) { $prototype .= $1 . $2; ($2 eq '{') && $brcount++; ($2 eq '}') && $brcount--; if (($2 eq ';') && ($brcount == 0)) { dump_declaration($prototype, $file); reset_state(); last; } $x = $3; } else { $prototype .= $x; last; } } } # xml_escape: replace <, >, and & in the text stream; # # however, formatting controls that are generated internally/locally in the # kernel-doc script are not escaped here; instead, they begin life like # $blankline_html (4 of '\' followed by a mnemonic + ':'), then these strings # are converted to their mnemonic-expected output, without the 4 * '\' & ':', # just before actual output; (this is done by local_unescape()) sub xml_escape($) { my $text = shift; if (($output_mode eq "text") || ($output_mode eq "man")) { return $text; } $text =~ s/\&/\\\\\\amp;/g; $text =~ s/\/\\\\\\gt;/g; return $text; } # xml_unescape: reverse the effects of xml_escape sub xml_unescape($) { my $text = shift; if (($output_mode eq "text") || ($output_mode eq "man")) { return $text; } $text =~ s/\\\\\\amp;/\&/g; $text =~ s/\\\\\\lt;//g; return $text; } # convert local escape strings to html # local escape strings look like: '\\\\menmonic:' (that's 4 backslashes) sub local_unescape($) { my $text = shift; if (($output_mode eq "text") || ($output_mode eq "man")) { return $text; } $text =~ s/\\\\\\\\lt://g; return $text; } sub map_filename($) { my $file; my ($orig_file) = @_; if (defined($ENV{'SRCTREE'})) { $file = "$ENV{'SRCTREE'}" . "/" . $orig_file; } else { $file = $orig_file; } if (defined($source_map{$file})) { $file = $source_map{$file}; } return $file; } sub process_export_file($) { my ($orig_file) = @_; my $file = map_filename($orig_file); if (!open(IN,"<$file")) { print STDERR "Error: Cannot open file $file\n"; ++$errors; return; } while () { if (/$export_symbol/) { $function_table{$2} = 1; } } close(IN); } sub process_file($) { my $file; my $identifier; my $func; my $descr; my $in_purpose = 0; my $initial_section_counter = $section_counter; my ($orig_file) = @_; my $leading_space; $file = map_filename($orig_file); if (!open(IN,"<$file")) { print STDERR "Error: Cannot open file $file\n"; ++$errors; return; } $. = 1; $section_counter = 0; while () { while (s/\\\s*$//) { $_ .= ; } if ($state == STATE_NORMAL) { if (/$doc_start/o) { $state = STATE_NAME; # next line is always the function name $in_doc_sect = 0; $declaration_start_line = $. + 1; } } elsif ($state == STATE_NAME) {# this line is the function name (always) if (/$doc_block/o) { $state = STATE_DOCBLOCK; $contents = ""; $new_start_line = $. + 1; if ( $1 eq "" ) { $section = $section_intro; } else { $section = $1; } } elsif (/$doc_decl/o) { $identifier = $1; if (/\s*([\w\s]+?)\s*-/) { $identifier = $1; } $state = STATE_FIELD; # if there's no @param blocks need to set up default section # here $contents = ""; $section = $section_default; $new_start_line = $. + 1; if (/-(.*)/) { # strip leading/trailing/multiple spaces $descr= $1; $descr =~ s/^\s*//; $descr =~ s/\s*$//; $descr =~ s/\s+/ /g; $declaration_purpose = xml_escape($descr); $in_purpose = 1; } else { $declaration_purpose = ""; } if (($declaration_purpose eq "") && $verbose) { print STDERR "${file}:$.: warning: missing initial short description on line:\n"; print STDERR $_; ++$warnings; } if ($identifier =~ m/^struct/) { $decl_type = 'struct'; } elsif ($identifier =~ m/^union/) { $decl_type = 'union'; } elsif ($identifier =~ m/^enum/) { $decl_type = 'enum'; } elsif ($identifier =~ m/^typedef/) { $decl_type = 'typedef'; } else { $decl_type = 'function'; } if ($verbose) { print STDERR "${file}:$.: info: Scanning doc for $identifier\n"; } } else { print STDERR "${file}:$.: warning: Cannot understand $_ on line $.", " - I thought it was a doc line\n"; ++$warnings; $state = STATE_NORMAL; } } elsif ($state == STATE_FIELD) { # look for head: lines, and include content if (/$doc_sect/i) { # case insensitive for supported section names $newsection = $1; $newcontents = $2; # map the supported section names to the canonical names if ($newsection =~ m/^description$/i) { $newsection = $section_default; } elsif ($newsection =~ m/^context$/i) { $newsection = $section_context; } elsif ($newsection =~ m/^returns?$/i) { $newsection = $section_return; } elsif ($newsection =~ m/^\@return$/) { # special: @return is a section, not a param description $newsection = $section_return; } if (($contents ne "") && ($contents ne "\n")) { if (!$in_doc_sect && $verbose) { print STDERR "${file}:$.: warning: contents before sections\n"; ++$warnings; } dump_section($file, $section, xml_escape($contents)); $section = $section_default; } $in_doc_sect = 1; $in_purpose = 0; $contents = $newcontents; $new_start_line = $.; while ((substr($contents, 0, 1) eq " ") || substr($contents, 0, 1) eq "\t") { $contents = substr($contents, 1); } if ($contents ne "") { $contents .= "\n"; } $section = $newsection; $leading_space = undef; } elsif (/$doc_end/) { if (($contents ne "") && ($contents ne "\n")) { dump_section($file, $section, xml_escape($contents)); $section = $section_default; $contents = ""; } # look for doc_com + + doc_end: if ($_ =~ m'\s*\*\s*[a-zA-Z_0-9:\.]+\*/') { print STDERR "${file}:$.: warning: suspicious ending line: $_"; ++$warnings; } $prototype = ""; $state = STATE_PROTO; $brcount = 0; # print STDERR "end of doc comment, looking for prototype\n"; } elsif (/$doc_content/) { # miguel-style comment kludge, look for blank lines after # @parameter line to signify start of description if ($1 eq "") { if ($section =~ m/^@/ || $section eq $section_context) { dump_section($file, $section, xml_escape($contents)); $section = $section_default; $contents = ""; $new_start_line = $.; } else { $contents .= "\n"; } $in_purpose = 0; } elsif ($in_purpose == 1) { # Continued declaration purpose chomp($declaration_purpose); $declaration_purpose .= " " . xml_escape($1); $declaration_purpose =~ s/\s+/ /g; } else { my $cont = $1; if ($section =~ m/^@/ || $section eq $section_context) { if (!defined $leading_space) { if ($cont =~ m/^(\s+)/) { $leading_space = $1; } else { $leading_space = ""; } } $cont =~ s/^$leading_space//; } $contents .= $cont . "\n"; } } else { # i dont know - bad line? ignore. print STDERR "${file}:$.: warning: bad line: $_"; ++$warnings; } } elsif ($state == STATE_INLINE) { # scanning for inline parameters # First line (state 1) needs to be a @parameter if ($inline_doc_state == STATE_INLINE_NAME && /$doc_inline_sect/o) { $section = $1; $contents = $2; $new_start_line = $.; if ($contents ne "") { while ((substr($contents, 0, 1) eq " ") || substr($contents, 0, 1) eq "\t") { $contents = substr($contents, 1); } $contents .= "\n"; } $inline_doc_state = STATE_INLINE_TEXT; # Documentation block end */ } elsif (/$doc_inline_end/) { if (($contents ne "") && ($contents ne "\n")) { dump_section($file, $section, xml_escape($contents)); $section = $section_default; $contents = ""; } $state = STATE_PROTO; $inline_doc_state = STATE_INLINE_NA; # Regular text } elsif (/$doc_content/) { if ($inline_doc_state == STATE_INLINE_TEXT) { $contents .= $1 . "\n"; # nuke leading blank lines if ($contents =~ /^\s*$/) { $contents = ""; } } elsif ($inline_doc_state == STATE_INLINE_NAME) { $inline_doc_state = STATE_INLINE_ERROR; print STDERR "${file}:$.: warning: "; print STDERR "Incorrect use of kernel-doc format: $_"; ++$warnings; } } } elsif ($state == STATE_PROTO) { # scanning for function '{' (end of prototype) if (/$doc_inline_oneline/) { $section = $1; $contents = $2; if ($contents ne "") { $contents .= "\n"; dump_section($file, $section, xml_escape($contents)); $section = $section_default; $contents = ""; } } elsif (/$doc_inline_start/) { $state = STATE_INLINE; $inline_doc_state = STATE_INLINE_NAME; } elsif ($decl_type eq 'function') { process_proto_function($_, $file); } else { process_proto_type($_, $file); } } elsif ($state == STATE_DOCBLOCK) { if (/$doc_end/) { dump_doc_section($file, $section, xml_escape($contents)); $section = $section_default; $contents = ""; $function = ""; %parameterdescs = (); %parametertypes = (); @parameterlist = (); %sections = (); @sectionlist = (); $prototype = ""; $state = STATE_NORMAL; } elsif (/$doc_content/) { if ( $1 eq "" ) { $contents .= $blankline; } else { $contents .= $1 . "\n"; } } } } if ($initial_section_counter == $section_counter) { print STDERR "${file}:1: warning: no structured comments found\n"; if (($output_selection == OUTPUT_INCLUDE) && ($show_not_found == 1)) { print STDERR " Was looking for '$_'.\n" for keys %function_table; } if ($output_mode eq "xml") { # The template wants at least one RefEntry here; make one. print "\n"; print " \n"; print " \n"; print " ${orig_file}\n"; print " \n"; print " \n"; print " Document generation inconsistency\n"; print " \n"; print " \n"; print " \n"; print " \n"; print " Oops\n"; print " \n"; print " \n"; print " \n"; print " The template for this document tried to insert\n"; print " the structured comment from the file\n"; print " ${orig_file} at this point,\n"; print " but none was found.\n"; print " This dummy section is inserted to allow\n"; print " generation to continue.\n"; print " \n"; print " \n"; print " \n"; print "\n"; } } } $kernelversion = get_kernel_version(); # generate a sequence of code that will splice in highlighting information # using the s// operator. for (my $k = 0; $k < @highlights; $k++) { my $pattern = $highlights[$k][0]; my $result = $highlights[$k][1]; # print STDERR "scanning pattern:$pattern, highlight:($result)\n"; $dohighlight .= "\$contents =~ s:$pattern:$result:gs;\n"; } # Read the file that maps relative names to absolute names for # separate source and object directories and for shadow trees. if (open(SOURCE_MAP, "<.tmp_filelist.txt")) { my ($relname, $absname); while() { chop(); ($relname, $absname) = (split())[0..1]; $relname =~ s:^/+::; $source_map{$relname} = $absname; } close(SOURCE_MAP); } if ($output_selection == OUTPUT_EXPORTED || $output_selection == OUTPUT_INTERNAL) { push(@export_file_list, @ARGV); foreach (@export_file_list) { chomp; process_export_file($_); } } foreach (@ARGV) { chomp; process_file($_); } if ($verbose && $errors) { print STDERR "$errors errors\n"; } if ($verbose && $warnings) { print STDERR "$warnings warnings\n"; } exit($errors); multipath-tools-0.7.4/libdmmp/docs/libdmmp.h.3000066400000000000000000000073011320314174000211750ustar00rootroot00000000000000.TH "libdmmp.h" 3 "January 2016" "Device Mapper Multipath API - libdmmp Manual" .SH NAME libdmmp.h \- Device Mapper Multipath API. .SH SYNOPSIS #include .SH "DESCRIPTION" All the libdmmp public functions ships its own man pages. Use 'man 3 ' to check the detail usage. .SH "USAGE" To use libdmmp in your project, we suggest to use the 'pkg-config' way: * Add this line into your configure.ac: PKG_CHECK_MODULES([LIBDMMP], [libdmmp]) * Add these lines into your Makefile.am: foo_LDFLAGS += $(LIBDMMP_LIBS) foo_CFLAGS += $(LIBDMMP_CFLAGS) .SH LOG HANDLING The log handler function could be set via 'dmmp_context_log_func_set()'. The log priority could be set via 'dmmp_context_log_priority_set()'. By default, the log priorities is 'DMMP_LOG_PRIORITY_WARNING'. By default, the log handler is print log to STDERR, and its code is listed below in case you want to create your own log handler. static int _DMMP_LOG_STRERR_ALIGN_WIDTH = 80; static void _log_stderr(struct dmmp_context *ctx, enum dmmp_log_priority priority, const char *file, int line, const char *func_name, const char *format, va_list args) { int printed_bytes = 0; printed_bytes += fprintf(stderr, "libdmmp %s: ", dmmp_log_priority_str(priority)); printed_bytes += vfprintf(stderr, format, args); userdata = dmmp_context_userdata_get(ctx); if (userdata != NULL) fprintf(stderr, "(with user data at memory address %p)", userdata); if (printed_bytes < _DMMP_LOG_STRERR_ALIGN_WIDTH) { fprintf(stderr, "%*s # %s:%s():%d\n", _DMMP_LOG_STRERR_ALIGN_WIDTH - printed_bytes, "", file, func_name, line); } else { fprintf(stderr, " # %s:%s():%d\n", file, func_name, line); } } .SH "SAMPLE CODE" #include int main(int argc, char *argv[]) { struct dmmp_context *ctx = NULL; struct dmmp_mpath **dmmp_mps = NULL; struct dmmp_path_group **dmmp_pgs = NULL; struct dmmp_path **dmmp_ps = NULL; uint32_t dmmp_mp_count = 0; uint32_t dmmp_pg_count = 0; uint32_t dmmp_p_count = 0; const char *name = NULL; const char *wwid = NULL; uint32_t i = 0; int rc = DMMP_OK; ctx = dmmp_context_new(); dmmp_context_log_priority_set(ctx, DMMP_LOG_PRIORITY_DEBUG); // By default, log will be printed to STDERR, you could // change that via dmmp_context_log_func_set() rc = dmmp_mpath_array_get(ctx, &dmmp_mps, &dmmp_mp_count); if (rc != DMMP_OK) { printf("dmmp_mpath_array_get() failed with %d: %s", rc, dmmp_strerror(rc)); goto out; } for (i = 0; i < dmmp_mp_count; ++i) { name = dmmp_mpath_name_get(dmmp_mps[i]); wwid = dmmp_mpath_wwid_get(dmmp_mps[i]); printf("dmmp_mpath_array_get(): Got mpath: %s %s\n", name, wwid); // You could use dmmp_path_group_array_get() to retrieve // path group information and then invoke dmmp_path_array_get() // for path information. } out: dmmp_context_free(ctx); dmmp_mpath_array_free(dmmp_mps, dmmp_mp_count); if (rc != DMMP_OK) exit(1); exit(0); } .SH "LICENSE" GPLv2+ .SH "BUG" Please report bug to multipath-tools-0.7.4/libdmmp/docs/split-man.pl000066400000000000000000000020321320314174000214740ustar00rootroot00000000000000#!/usr/bin/perl # Originally From: # https://www.kernel.org/doc/Documentation/kernel-doc-nano-HOWTO.txt # # Changes: # * Create manpage section 3 instead of 9. # * Replace 'Kernel Hackers Manual' to # 'Device Mapper Multipath API - libdmmp Manual' # * Remove LINUX from header. # * Remove DMMP_DLL_EXPORT. $man_sec_num = 3; $title = 'Device Mapper Multipath API - libdmmp Manual'; if ( $#ARGV < 0 ) { die "where do I put the results?\n"; } mkdir $ARGV[0], 0777; $state = 0; while () { if (/^\.TH \"[^\"]*\" 9 \"([^\"]*)\"/) { if ( $state == 1 ) { close OUT } $state = 1; $fn = "$ARGV[0]/$1.$man_sec_num"; print STDERR "Creating $fn\n"; open OUT, ">$fn" or die "can't open $fn: $!\n"; # Change man page code from 9 to $man_sec_num; s/^\.TH (\"[^\"]*\") 9 \"([^\"]*)\"/\.TH $1 $man_sec_num \"$2\"/; s/Kernel Hacker's Manual/$title/g; s/LINUX//g; print OUT $_; } elsif ( $state != 0 ) { print OUT $_; } } close OUT; multipath-tools-0.7.4/libdmmp/libdmmp.c000066400000000000000000000277761320314174000201210ustar00rootroot00000000000000/* * Copyright (C) 2015 - 2017 Red Hat, Inc. * * 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 3 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, see . * * Author: Gris Ge * Todd Gill */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "libdmmp/libdmmp.h" #include "libdmmp_private.h" #define _DEFAULT_UXSOCK_TIMEOUT 60000 /* ^ 60 seconds. On system with 10k sdX, dmmp_mpath_array_get() * only take 3.5 seconds, so this default value should be OK for most users. */ #define _DMMP_IPC_SHOW_JSON_CMD "show maps json" #define _DMMP_JSON_MAJOR_KEY "major_version" #define _DMMP_JSON_MAJOR_VERSION 0 #define _DMMP_JSON_MAPS_KEY "maps" #define _ERRNO_STR_BUFF_SIZE 256 #define _IPC_MAX_CMD_LEN 512 /* ^ Was _MAX_CMD_LEN in ./libmultipath/uxsock.h */ struct dmmp_context { void (*log_func)(struct dmmp_context *ctx, int priority, const char *file, int line, const char *func_name, const char *format, va_list args); int log_priority; void *userdata; unsigned int tmo; }; /* * The multipathd daemon are using "uxsock_timeout" to define timeout value, * if timeout at daemon side, we will get message "timeout\n". * To unify this timeout with `dmmp_context_timeout_set()`, this function * will keep retry mpath_process_cmd() tile meet the time of * dmmp_context_timeout_get(). * Need to free `*output` string manually. */ static int _process_cmd(struct dmmp_context *ctx, int fd, const char *cmd, char **output); static int _ipc_connect(struct dmmp_context *ctx, int *fd); _dmmp_getter_func_gen(dmmp_context_log_priority_get, struct dmmp_context, ctx, log_priority, int); _dmmp_getter_func_gen(dmmp_context_userdata_get, struct dmmp_context, ctx, userdata, void *); _dmmp_getter_func_gen(dmmp_context_timeout_get, struct dmmp_context, ctx, tmo, unsigned int); _dmmp_array_free_func_gen(dmmp_mpath_array_free, struct dmmp_mpath, _dmmp_mpath_free); void _dmmp_log(struct dmmp_context *ctx, int priority, const char *file, int line, const char *func_name, const char *format, ...) { va_list args; va_start(args, format); ctx->log_func(ctx, priority, file, line, func_name, format, args); va_end(args); } struct dmmp_context *dmmp_context_new(void) { struct dmmp_context *ctx = NULL; ctx = (struct dmmp_context *) malloc(sizeof(struct dmmp_context)); if (ctx == NULL) return NULL; ctx->log_func = _dmmp_log_stderr; ctx->log_priority = DMMP_LOG_PRIORITY_DEFAULT; ctx->userdata = NULL; ctx->tmo = _DEFAULT_UXSOCK_TIMEOUT; return ctx; } void dmmp_context_free(struct dmmp_context *ctx) { free(ctx); } void dmmp_context_log_priority_set(struct dmmp_context *ctx, int priority) { assert(ctx != NULL); ctx->log_priority = priority; } void dmmp_context_timeout_set(struct dmmp_context *ctx, unsigned int tmo) { assert(ctx != NULL); ctx->tmo = tmo; } void dmmp_context_log_func_set (struct dmmp_context *ctx, void (*log_func)(struct dmmp_context *ctx, int priority, const char *file, int line, const char *func_name, const char *format, va_list args)) { assert(ctx != NULL); ctx->log_func = log_func; } void dmmp_context_userdata_set(struct dmmp_context *ctx, void *userdata) { assert(ctx != NULL); ctx->userdata = userdata; } int dmmp_mpath_array_get(struct dmmp_context *ctx, struct dmmp_mpath ***dmmp_mps, uint32_t *dmmp_mp_count) { struct dmmp_mpath *dmmp_mp = NULL; int rc = DMMP_OK; char *j_str = NULL; json_object *j_obj = NULL; json_object *j_obj_map = NULL; enum json_tokener_error j_err = json_tokener_success; json_tokener *j_token = NULL; struct array_list *ar_maps = NULL; uint32_t i = 0; int cur_json_major_version = -1; int ar_maps_len = -1; int ipc_fd = -1; assert(ctx != NULL); assert(dmmp_mps != NULL); assert(dmmp_mp_count != NULL); *dmmp_mps = NULL; *dmmp_mp_count = 0; _good(_ipc_connect(ctx, &ipc_fd), rc, out); _good(_process_cmd(ctx, ipc_fd, _DMMP_IPC_SHOW_JSON_CMD, &j_str), rc, out); _debug(ctx, "Got json output from multipathd: '%s'", j_str); j_token = json_tokener_new(); if (j_token == NULL) { rc = DMMP_ERR_BUG; _error(ctx, "BUG: json_tokener_new() retuned NULL"); goto out; } j_obj = json_tokener_parse_ex(j_token, j_str, strlen(j_str) + 1); if (j_obj == NULL) { rc = DMMP_ERR_IPC_ERROR; j_err = json_tokener_get_error(j_token); _error(ctx, "Failed to parse JSON output from multipathd IPC: " "%s", json_tokener_error_desc(j_err)); goto out; } _json_obj_get_value(ctx, j_obj, cur_json_major_version, _DMMP_JSON_MAJOR_KEY, json_type_int, json_object_get_int, rc, out); if (cur_json_major_version != _DMMP_JSON_MAJOR_VERSION) { rc = DMMP_ERR_INCOMPATIBLE; _error(ctx, "Incompatible multipathd JSON major version %d, " "should be %d", cur_json_major_version, _DMMP_JSON_MAJOR_VERSION); goto out; } _debug(ctx, "multipathd JSON major version(%d) check pass", _DMMP_JSON_MAJOR_VERSION); _json_obj_get_value(ctx, j_obj, ar_maps, _DMMP_JSON_MAPS_KEY, json_type_array, json_object_get_array, rc, out); if (ar_maps == NULL) { rc = DMMP_ERR_BUG; _error(ctx, "BUG: Got NULL map array from " "_json_obj_get_value()"); goto out; } ar_maps_len = array_list_length(ar_maps); if (ar_maps_len < 0) { rc = DMMP_ERR_BUG; _error(ctx, "BUG: Got negative length for ar_maps"); goto out; } else if (ar_maps_len == 0) goto out; else *dmmp_mp_count = ar_maps_len & UINT32_MAX; *dmmp_mps = (struct dmmp_mpath **) malloc(sizeof(struct dmmp_mpath *) * (*dmmp_mp_count)); _dmmp_alloc_null_check(ctx, dmmp_mps, rc, out); for (; i < *dmmp_mp_count; ++i) (*dmmp_mps)[i] = NULL; for (i = 0; i < *dmmp_mp_count; ++i) { j_obj_map = array_list_get_idx(ar_maps, i); if (j_obj_map == NULL) { rc = DMMP_ERR_BUG; _error(ctx, "BUG: array_list_get_idx() return NULL"); goto out; } dmmp_mp = _dmmp_mpath_new(); _dmmp_alloc_null_check(ctx, dmmp_mp, rc, out); (*dmmp_mps)[i] = dmmp_mp; _good(_dmmp_mpath_update(ctx, dmmp_mp, j_obj_map), rc, out); } out: if (ipc_fd >= 0) mpath_disconnect(ipc_fd); free(j_str); if (j_token != NULL) json_tokener_free(j_token); if (j_obj != NULL) json_object_put(j_obj); if (rc != DMMP_OK) { dmmp_mpath_array_free(*dmmp_mps, *dmmp_mp_count); *dmmp_mps = NULL; *dmmp_mp_count = 0; } return rc; } static int _process_cmd(struct dmmp_context *ctx, int fd, const char *cmd, char **output) { int errno_save = 0; int rc = DMMP_OK; char errno_str_buff[_ERRNO_STR_BUFF_SIZE]; struct timespec start_ts; struct timespec cur_ts; unsigned int ipc_tmo = 0; bool flag_check_tmo = false; unsigned int elapsed = 0; assert(output != NULL); assert(ctx != NULL); assert(cmd != NULL); *output = NULL; if (clock_gettime(CLOCK_MONOTONIC, &start_ts) != 0) { _error(ctx, "BUG: Failed to get CLOCK_MONOTONIC time " "via clock_gettime(), error %d", errno); return DMMP_ERR_BUG; } ipc_tmo = ctx->tmo; if (ctx->tmo == 0) ipc_tmo = _DEFAULT_UXSOCK_TIMEOUT; invoke: _debug(ctx, "Invoking IPC command '%s' with IPC tmo %u miliseconds", cmd, ipc_tmo); flag_check_tmo = false; if (mpath_process_cmd(fd, cmd, output, ipc_tmo) != 0) { errno_save = errno; memset(errno_str_buff, 0, _ERRNO_STR_BUFF_SIZE); strerror_r(errno_save, errno_str_buff, _ERRNO_STR_BUFF_SIZE); if (errno_save == ETIMEDOUT) { flag_check_tmo = true; } else { _error(ctx, "IPC failed when process command '%s' with " "error %d(%s)", cmd, errno_save, errno_str_buff); _debug(ctx, "%s", *output); rc = DMMP_ERR_IPC_ERROR; goto out; } } if ((*output != NULL) && (strncmp(*output, "timeout", strlen("timeout")) == 0)) flag_check_tmo = true; if (flag_check_tmo == true) { free(*output); *output = NULL; if (ctx->tmo == 0) { _debug(ctx, "IPC timeout, but user requested infinite " "timeout"); goto invoke; } if (clock_gettime(CLOCK_MONOTONIC, &cur_ts) != 0) { _error(ctx, "BUG: Failed to get CLOCK_MONOTONIC time " "via clock_gettime(), error %d", errno); rc = DMMP_ERR_BUG; goto out; } elapsed = (cur_ts.tv_sec - start_ts.tv_sec) * 1000 + (cur_ts.tv_nsec - start_ts.tv_nsec) / 1000000; if (elapsed >= ctx->tmo) { rc = DMMP_ERR_IPC_TIMEOUT; _error(ctx, "Timeout, try to increase it via " "dmmp_context_timeout_set()"); goto out; } if (ctx->tmo != 0) ipc_tmo = ctx->tmo - elapsed; _debug(ctx, "IPC timeout, but user requested timeout has not " "reached yet, still have %u milliseconds", ipc_tmo); goto invoke; } else { if ((*output == NULL) || (strlen(*output) == 0)) { _error(ctx, "IPC return empty reply for command %s", cmd); rc = DMMP_ERR_IPC_ERROR; goto out; } } out: if (rc != DMMP_OK) { free(*output); *output = NULL; } return rc; } static int _ipc_connect(struct dmmp_context *ctx, int *fd) { int rc = DMMP_OK; int errno_save = 0; char errno_str_buff[_ERRNO_STR_BUFF_SIZE]; assert(ctx != NULL); assert(fd != NULL); *fd = -1; *fd = mpath_connect(); if (*fd == -1) { errno_save = errno; memset(errno_str_buff, 0, _ERRNO_STR_BUFF_SIZE); strerror_r(errno_save, errno_str_buff, _ERRNO_STR_BUFF_SIZE); if (errno_save == ECONNREFUSED) { rc = DMMP_ERR_NO_DAEMON; _error(ctx, "Socket connection refuse. " "Maybe multipathd daemon is not running"); } else { _error(ctx, "IPC failed with error %d(%s)", errno_save, errno_str_buff); rc = DMMP_ERR_IPC_ERROR; } } return rc; } int dmmp_flush_mpath(struct dmmp_context *ctx, const char *mpath_name) { int rc = DMMP_OK; struct dmmp_mpath **dmmp_mps = NULL; uint32_t dmmp_mp_count = 0; uint32_t i = 0; bool found = false; int ipc_fd = -1; char cmd[_IPC_MAX_CMD_LEN]; char *output = NULL; assert(ctx != NULL); assert(mpath_name != NULL); snprintf(cmd, _IPC_MAX_CMD_LEN, "del map %s", mpath_name); if (strlen(cmd) == _IPC_MAX_CMD_LEN - 1) { rc = DMMP_ERR_INVALID_ARGUMENT; _error(ctx, "Invalid mpath name %s", mpath_name); goto out; } _good(_ipc_connect(ctx, &ipc_fd), rc, out); _good(_process_cmd(ctx, ipc_fd, cmd, &output), rc, out); /* _process_cmd() already make sure output is not NULL */ if (strncmp(output, "fail", strlen("fail")) == 0) { /* Check whether specified mpath exits */ _good(dmmp_mpath_array_get(ctx, &dmmp_mps, &dmmp_mp_count), rc, out); for (i = 0; i < dmmp_mp_count; ++i) { if (strcmp(dmmp_mpath_name_get(dmmp_mps[i]), mpath_name) == 0) { found = true; break; } } if (found == false) { rc = DMMP_ERR_MPATH_NOT_FOUND; _error(ctx, "Specified mpath %s not found", mpath_name); goto out; } rc = DMMP_ERR_MPATH_BUSY; _error(ctx, "Specified mpath is in use"); } else if (strncmp(output, "ok", strlen("ok")) != 0) { rc = DMMP_ERR_BUG; _error(ctx, "Got unexpected output for cmd '%s': '%s'", cmd, output); } out: if (ipc_fd >= 0) mpath_disconnect(ipc_fd); dmmp_mpath_array_free(dmmp_mps, dmmp_mp_count); free(output); return rc; } int dmmp_reconfig(struct dmmp_context *ctx) { int rc = DMMP_OK; int ipc_fd = -1; char *output = NULL; char cmd[_IPC_MAX_CMD_LEN]; snprintf(cmd, _IPC_MAX_CMD_LEN, "%s", "reconfigure"); _good(_ipc_connect(ctx, &ipc_fd), rc, out); _good(_process_cmd(ctx, ipc_fd, cmd, &output), rc, out); out: if (ipc_fd >= 0) mpath_disconnect(ipc_fd); free(output); return rc; } multipath-tools-0.7.4/libdmmp/libdmmp.pc.in000066400000000000000000000003031320314174000206570ustar00rootroot00000000000000includedir=__INCLUDEDIR__ libdir=__LIBDIR__ Name: libdmmp Version: __VERSION__ Description: Device mapper multipath management library Requires: Libs: -L${libdir} -ldmmp Cflags: -I${includedir} multipath-tools-0.7.4/libdmmp/libdmmp/000077500000000000000000000000001320314174000177325ustar00rootroot00000000000000multipath-tools-0.7.4/libdmmp/libdmmp/libdmmp.h000066400000000000000000000460041320314174000215330ustar00rootroot00000000000000/* * Copyright (C) 2015 - 2017 Red Hat, Inc. * * 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 3 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, see . * * Author: Gris Ge * Todd Gill */ #ifndef _LIB_DMMP_H_ #define _LIB_DMMP_H_ #include #include #ifdef __cplusplus extern "C" { #endif #define DMMP_DLL_EXPORT __attribute__ ((visibility ("default"))) #define DMMP_DLL_LOCAL __attribute__ ((visibility ("hidden"))) #define DMMP_OK 0 #define DMMP_ERR_BUG 1 #define DMMP_ERR_NO_MEMORY 2 #define DMMP_ERR_IPC_TIMEOUT 3 #define DMMP_ERR_IPC_ERROR 4 #define DMMP_ERR_NO_DAEMON 5 #define DMMP_ERR_INCOMPATIBLE 6 #define DMMP_ERR_MPATH_BUSY 7 #define DMMP_ERR_MPATH_NOT_FOUND 8 #define DMMP_ERR_INVALID_ARGUMENT 9 /* * Use the syslog severity level as log priority */ #define DMMP_LOG_PRIORITY_ERROR 3 #define DMMP_LOG_PRIORITY_WARNING 4 #define DMMP_LOG_PRIORITY_INFO 6 #define DMMP_LOG_PRIORITY_DEBUG 7 #define DMMP_LOG_PRIORITY_DEFAULT DMMP_LOG_PRIORITY_WARNING /** * dmmp_log_priority_str() - Convert log priority to string. * * Convert log priority to string (const char *). * * @priority: * int. Log priority. * * Return: * const char *. Valid string are: * * * "ERROR" for DMMP_LOG_PRIORITY_ERROR * * * "WARN " for DMMP_LOG_PRIORITY_WARNING * * * "INFO " for DMMP_LOG_PRIORITY_INFO * * * "DEBUG" for DMMP_LOG_PRIORITY_DEBUG * * * "Invalid argument" for invalid log priority. */ DMMP_DLL_EXPORT const char *dmmp_log_priority_str(int priority); struct DMMP_DLL_EXPORT dmmp_context; struct DMMP_DLL_EXPORT dmmp_mpath; struct DMMP_DLL_EXPORT dmmp_path_group; #define DMMP_PATH_GROUP_STATUS_UNKNOWN 0 #define DMMP_PATH_GROUP_STATUS_ENABLED 1 #define DMMP_PATH_GROUP_STATUS_DISABLED 2 #define DMMP_PATH_GROUP_STATUS_ACTIVE 3 struct DMMP_DLL_EXPORT dmmp_path; #define DMMP_PATH_STATUS_UNKNOWN 0 //#define DMMP_PATH_STATUS_UNCHECKED 1 // ^ print.h does not expose this. #define DMMP_PATH_STATUS_DOWN 2 #define DMMP_PATH_STATUS_UP 3 #define DMMP_PATH_STATUS_SHAKY 4 #define DMMP_PATH_STATUS_GHOST 5 #define DMMP_PATH_STATUS_PENDING 6 #define DMMP_PATH_STATUS_TIMEOUT 7 //#define DMMP_PATH_STATUS_REMOVED 8 // ^ print.h does not expose this. #define DMMP_PATH_STATUS_DELAYED 9 /** * dmmp_strerror() - Convert error code to string. * * Convert error code (int) to string (const char *): * * * DMMP_OK -- "OK" * * * DMMP_ERR_BUG -- "BUG of libdmmp library" * * * DMMP_ERR_NO_MEMORY -- "Out of memory" * * * DMMP_ERR_IPC_TIMEOUT -- "Timeout when communicate with multipathd, * try to set bigger timeout value via dmmp_context_timeout_set ()" * * * DMMP_ERR_IPC_ERROR -- "Error when communicate with multipathd daemon" * * * DMMP_ERR_NO_DAEMON -- "The multipathd daemon not started" * * * DMMP_ERR_INCOMPATIBLE -- "The multipathd daemon version is not * compatible with current library" * * * Other invalid error number -- "Invalid argument" * * @rc: * int. Return code by libdmmp functions. When provided error code is not a * valid error code, return "Invalid argument". * * Return: * const char *. The meaning of provided error code. * */ DMMP_DLL_EXPORT const char *dmmp_strerror(int rc); /** * dmmp_context_new() - Create struct dmmp_context. * * The default logging level (DMMP_LOG_PRIORITY_DEFAULT) is * DMMP_LOG_PRIORITY_WARNING which means only warning and error message will be * forward to log handler function. The default log handler function will print * log message to STDERR, to change so, please use dmmp_context_log_func_set() * to set your own log handler, check manpage libdmmp.h(3) for detail. * * Return: * Pointer of 'struct dmmp_context'. Should be freed by * dmmp_context_free(). */ DMMP_DLL_EXPORT struct dmmp_context *dmmp_context_new(void); /** * dmmp_context_free() - Release the memory of struct dmmp_context. * * Release the memory of struct dmmp_context, but the userdata memory defined * via dmmp_context_userdata_set() will not be touched. * * @ctx: * Pointer of 'struct dmmp_context'. * Return: * void */ DMMP_DLL_EXPORT void dmmp_context_free(struct dmmp_context *ctx); /** * dmmp_context_timeout_set() - Set IPC timeout. * * By default, the IPC to multipathd daemon will timeout after 60 seconds. * * @ctx: * Pointer of 'struct dmmp_context'. * If this pointer is NULL, your program will be terminated by assert. * * @tmo: * Timeout in milliseconds(1 seconds equal 1000 milliseconds). * 0 means infinite, function only return when error or pass. * * Return: * void */ DMMP_DLL_EXPORT void dmmp_context_timeout_set(struct dmmp_context *ctx, unsigned int tmo); /** * dmmp_context_timeout_get() - Get IPC timeout. * * Retrieve timeout value of IPC connection to multipathd daemon. * * @ctx: * Pointer of 'struct dmmp_context'. * If this pointer is NULL, your program will be terminated by assert. * * Return: * unsigned int. Timeout in milliseconds. */ DMMP_DLL_EXPORT unsigned int dmmp_context_timeout_get(struct dmmp_context *ctx); /** * dmmp_context_log_priority_set() - Set log priority. * * * When library generates log message, only equal or more important(less value) * message will be forwarded to log handler function. Valid log priority values * are: * * * DMMP_LOG_PRIORITY_ERROR -- 3 * * * DMMP_LOG_PRIORITY_WARNING -- 4 * * * DMMP_LOG_PRIORITY_INFO -- 5 * * * DMMP_LOG_PRIORITY_DEBUG -- 7 * * @ctx: * Pointer of 'struct dmmp_context'. * If this pointer is NULL, your program will be terminated by assert. * * @priority: * int, log priority. * * Return: * void */ DMMP_DLL_EXPORT void dmmp_context_log_priority_set(struct dmmp_context *ctx, int priority); /** * dmmp_context_log_priority_get() - Get log priority. * * Retrieve current log priority. Valid log priority values are: * * * DMMP_LOG_PRIORITY_ERROR -- 3 * * * DMMP_LOG_PRIORITY_WARNING -- 4 * * * DMMP_LOG_PRIORITY_INFO -- 5 * * * DMMP_LOG_PRIORITY_DEBUG -- 7 * * @ctx: * Pointer of 'struct dmmp_context'. * If this pointer is NULL, your program will be terminated by assert. * * Return: * int, log priority. */ DMMP_DLL_EXPORT int dmmp_context_log_priority_get(struct dmmp_context *ctx); /** * dmmp_context_log_func_set() - Set log handler function. * * Set custom log handler. The log handler will be invoked when log message * is equal or more important(less value) than log priority setting. * Please check manpage libdmmp.h(3) for detail usage. * * @ctx: * Pointer of 'struct dmmp_context'. * If this pointer is NULL, your program will be terminated by assert. * @log_func: * Pointer of log handler function. * * Return: * void */ DMMP_DLL_EXPORT void dmmp_context_log_func_set (struct dmmp_context *ctx, void (*log_func) (struct dmmp_context *ctx, int priority, const char *file, int line, const char *func_name, const char *format, va_list args)); /** * dmmp_context_userdata_set() - Set user data pointer. * * Store user data pointer into 'struct dmmp_context'. * * @ctx: * Pointer of 'struct dmmp_context'. * If this pointer is NULL, your program will be terminated by assert. * @userdata: * Pointer of user defined data. * * Return: * void */ DMMP_DLL_EXPORT void dmmp_context_userdata_set(struct dmmp_context *ctx, void *userdata); /** * dmmp_context_userdata_get() - Get user data pointer. * * Retrieve user data pointer from 'struct dmmp_context'. * * @ctx: * Pointer of 'struct dmmp_context'. * If this pointer is NULL, your program will be terminated by assert. * * Return: * void *. Pointer of user defined data. */ DMMP_DLL_EXPORT void *dmmp_context_userdata_get(struct dmmp_context *ctx); /** * dmmp_mpath_array_get() - Query all existing multipath devices. * * Query all existing multipath devices and store them into a pointer array. * The memory of 'dmmp_mps' should be freed via dmmp_mpath_array_free(). * * @ctx: * Pointer of 'struct dmmp_context'. * If this pointer is NULL, your program will be terminated by assert. * @dmmp_mps: * Output pointer array of 'struct dmmp_mpath'. * If this pointer is NULL, your program will be terminated by assert. * @dmmp_mp_count: * Output pointer of uint32_t. Hold the size of 'dmmp_mps' pointer array. * If this pointer is NULL, your program will be terminated by assert. * * Return: * int. Valid error codes are: * * * DMMP_OK * * * DMMP_ERR_BUG * * * DMMP_ERR_NO_MEMORY * * * DMMP_ERR_NO_DAEMON * * * DMMP_ERR_INCONSISTENT_DATA * * Error number could be converted to string by dmmp_strerror(). */ DMMP_DLL_EXPORT int dmmp_mpath_array_get(struct dmmp_context *ctx, struct dmmp_mpath ***dmmp_mps, uint32_t *dmmp_mp_count); /** * dmmp_mpath_array_free() - Free 'struct dmmp_mpath' pointer array. * * Free the 'dmmp_mps' pointer array generated by dmmp_mpath_array_get(). * If provided 'dmmp_mps' pointer is NULL or dmmp_mp_count == 0, do nothing. * * @dmmp_mps: * Pointer of 'struct dmmp_mpath' array. * @dmmp_mp_count: * uint32_t, the size of 'dmmp_mps' pointer array. * * Return: * void */ DMMP_DLL_EXPORT void dmmp_mpath_array_free(struct dmmp_mpath **dmmp_mps, uint32_t dmmp_mp_count); /** * dmmp_mpath_wwid_get() - Retrieve WWID of certain mpath. * * @dmmp_mp: * Pointer of 'struct dmmp_mpath'. * If this pointer is NULL, your program will be terminated by assert. * * Return: * const char *. No need to free this memory, the resources will get * freed when dmmp_mpath_array_free(). */ DMMP_DLL_EXPORT const char *dmmp_mpath_wwid_get(struct dmmp_mpath *dmmp_mp); /** * dmmp_mpath_name_get() - Retrieve name(alias) of certain mpath. * * Retrieve the name (also known as alias) of certain mpath. * When the config 'user_friendly_names' been set 'no', the name will be * identical to WWID retrieved by dmmp_mpath_wwid_get(). * * @dmmp_mp: * Pointer of 'struct dmmp_mpath'. * If this pointer is NULL, your program will be terminated by assert. * * Return: * const char *. No need to free this memory, the resources will get * freed when dmmp_mpath_array_free(). */ DMMP_DLL_EXPORT const char *dmmp_mpath_name_get(struct dmmp_mpath *dmmp_mp); /** * dmmp_mpath_kdev_name_get() - Retrieve kernel DEVNAME of certain mpath. * * Retrieve DEVNAME name used by kernel uevent of specified mpath. * Example: 'dm-1'. * * @dmmp_mp: * Pointer of 'struct dmmp_mpath'. * If this pointer is NULL, your program will be terminated by assert. * * Return: * const char *. No need to free this memory, the resources will get * freed when dmmp_mpath_array_free(). */ DMMP_DLL_EXPORT const char *dmmp_mpath_kdev_name_get (struct dmmp_mpath *dmmp_mp); /** * dmmp_path_group_array_get() - Retrieve path groups pointer array. * * Retrieve the path groups of certain mpath. * * The memory of output pointer array is hold by 'struct dmmp_mpath', no * need to free this memory, the resources will got freed when * dmmp_mpath_array_free(). * * @dmmp_mp: * Pointer of 'struct dmmp_mpath'. * If this pointer is NULL, your program will be terminated by assert. * @dmmp_pgs: * Output pointer of 'struct dmmp_path_group' pointer array. * If this pointer is NULL, your program will be terminated by assert. * @dmmp_pg_count: * Output pointer of uint32_t. Hold the size of 'dmmp_pgs' pointer array. * If this pointer is NULL, your program will be terminated by assert. * * Return: * void */ DMMP_DLL_EXPORT void dmmp_path_group_array_get (struct dmmp_mpath *dmmp_mp, struct dmmp_path_group ***dmmp_pgs, uint32_t *dmmp_pg_count); /** * dmmp_path_group_id_get() - Retrieve path group ID. * * Retrieve the path group ID which could be used to switch active path group * via command: * * multipathd -k'switch multipath mpathb group $id' * * @dmmp_pg: * Pointer of 'struct dmmp_path_group'. * If this pointer is NULL, your program will be terminated by assert. * * Return: * uint32_t. */ DMMP_DLL_EXPORT uint32_t dmmp_path_group_id_get (struct dmmp_path_group *dmmp_pg); /** * dmmp_path_group_priority_get() - Retrieve path group priority. * * The enabled path group with highest priority will be next active path group * if active path group down. * * @dmmp_pg: * Pointer of 'struct dmmp_path_group'. * If this pointer is NULL, your program will be terminated by assert. * * Return: * uint32_t. */ DMMP_DLL_EXPORT uint32_t dmmp_path_group_priority_get (struct dmmp_path_group *dmmp_pg); /** * dmmp_path_group_status_get() - Retrieve path group status. * * The valid path group statuses are: * * * DMMP_PATH_GROUP_STATUS_UNKNOWN * * * DMMP_PATH_GROUP_STATUS_ENABLED -- standby to be active * * * DMMP_PATH_GROUP_STATUS_DISABLED -- disabled due to all path down * * * DMMP_PATH_GROUP_STATUS_ACTIVE -- selected to handle I/O * * @dmmp_pg: * Pointer of 'struct dmmp_path_group'. * If this pointer is NULL, your program will be terminated by assert. * * Return: * uint32_t. */ DMMP_DLL_EXPORT uint32_t dmmp_path_group_status_get (struct dmmp_path_group *dmmp_pg); /** * dmmp_path_group_status_str() - Convert path group status to string. * * Convert path group status uint32_t to string (const char *). * * @pg_status: * uint32_t. Path group status. * When provided value is not a valid path group status, return "Invalid * argument". * * Return: * const char *. Valid string are: * * * "Invalid argument" * * * "undef" * * * "enabled" * * * "disabled" * * * "active" */ DMMP_DLL_EXPORT const char *dmmp_path_group_status_str(uint32_t pg_status); /** * dmmp_path_group_selector_get() - Retrieve path group selector. * * Path group selector determine which path in active path group will be * use to next I/O. * * @dmmp_pg: * Pointer of 'struct dmmp_path_group'. * If this pointer is NULL, your program will be terminated by assert. * * Return: * const char *. */ DMMP_DLL_EXPORT const char *dmmp_path_group_selector_get (struct dmmp_path_group *dmmp_pg); /** * dmmp_path_array_get() - Retrieve path pointer array. * * The memory of output pointer array is hold by 'struct dmmp_mpath', no * need to free this memory, the resources will got freed when * dmmp_mpath_array_free(). * * @dmmp_pg: * Pointer of 'struct dmmp_path_group'. * If this pointer is NULL, your program will be terminated by assert. * @dmmp_ps: * Output pointer of 'struct dmmp_path' pointer array. * If this pointer is NULL, your program will be terminated by assert. * @dmmp_p_count: * Output pointer of uint32_t. Hold the size of 'dmmp_ps' pointer array. * If this pointer is NULL, your program will be terminated by assert. * * Return: * void */ DMMP_DLL_EXPORT void dmmp_path_array_get(struct dmmp_path_group *dmmp_pg, struct dmmp_path ***dmmp_ps, uint32_t *dmmp_p_count); /** * dmmp_path_blk_name_get() - Retrieve block name. * * Retrieve block name of certain path. The example of block names are 'sda', * 'nvme0n1'. * * @dmmp_p: * Pointer of 'struct dmmp_path'. * If this pointer is NULL, your program will be terminated by assert. * * Return: * const char *. No need to free this memory, the resources will get * freed when dmmp_mpath_array_free(). */ DMMP_DLL_EXPORT const char *dmmp_path_blk_name_get(struct dmmp_path *dmmp_p); /** * dmmp_path_status_get() - Retrieve the path status. * * The valid path statuses are: * * * DMMP_PATH_STATUS_UNKNOWN * * * DMMP_PATH_STATUS_DOWN * * Path is down and you shouldn't try to send commands to it. * * * DMMP_PATH_STATUS_UP * * Path is up and I/O can be sent to it. * * * DMMP_PATH_STATUS_SHAKY * * Only emc_clariion checker when path not available for "normal" * operations. * * * DMMP_PATH_STATUS_GHOST * * Only hp_sw and rdac checkers. Indicates a "passive/standby" * path on active/passive HP arrays. These paths will return valid * answers to certain SCSI commands (tur, read_capacity, inquiry, * start_stop), but will fail I/O commands. The path needs an * initialization command to be sent to it in order for I/Os to * succeed. * * * DMMP_PATH_STATUS_PENDING * * Available for all async checkers when a check IO is in flight. * * * DMMP_PATH_STATUS_TIMEOUT * * Only tur checker when command timed out. * * * DMMP_PATH_STATUS_DELAYED * * If a path fails after being up for less than delay_watch_checks checks, * when it comes back up again, it will not be marked as up until it has * been up for delay_wait_checks checks. During this time, it is marked as * "delayed". * * @dmmp_p: * Pointer of 'struct dmmp_path'. * If this pointer is NULL, your program will be terminated by assert. * * Return: * uint32_t. */ DMMP_DLL_EXPORT uint32_t dmmp_path_status_get(struct dmmp_path *dmmp_p); /** * dmmp_path_status_str() - Convert path status to string. * * Convert path status uint32_t to string (const char *): * * * DMMP_PATH_STATUS_UNKNOWN -- "undef" * * * DMMP_PATH_STATUS_DOWN -- "faulty" * * * DMMP_PATH_STATUS_UP -- "ready" * * * DMMP_PATH_STATUS_SHAKY -- "shaky" * * * DMMP_PATH_STATUS_GHOST -- "ghost" * * * DMMP_PATH_STATUS_PENDING -- "pending" * * * DMMP_PATH_STATUS_TIMEOUT -- "timeout" * * * DMMP_PATH_STATUS_REMOVED -- "removed" * * * DMMP_PATH_STATUS_DELAYED -- "delayed" * * @path_status: * uint32_t. Path status. * When provided value is not a valid path status, return * "Invalid argument". * * Return: * const char *. The meaning of status value. */ DMMP_DLL_EXPORT const char *dmmp_path_status_str(uint32_t path_status); /** * dmmp_flush_mpath() - Flush specified multipath device map if unused. * * Flush a multipath device map specified as parameter, if unused. * * @ctx: * Pointer of 'struct dmmp_context'. * If this pointer is NULL, your program will be terminated by assert. * @mpath_name: * const char *. The name of multipath device map. * * Return: * int. Valid error codes are: * * * DMMP_OK * * * DMMP_ERR_BUG * * * DMMP_ERR_NO_MEMORY * * * DMMP_ERR_NO_DAEMON * * * DMMP_ERR_MPATH_BUSY * * * DMMP_ERR_MPATH_NOT_FOUND * * * DMMP_ERR_INVALID_ARGUMENT * * Error number could be converted to string by dmmp_strerror(). */ DMMP_DLL_EXPORT int dmmp_flush_mpath(struct dmmp_context *ctx, const char *mpath_name); /** * dmmp_reconfig() - Instruct multipathd daemon to do reconfiguration. * * Instruct multipathd daemon to do reconfiguration. * * @ctx: * Pointer of 'struct dmmp_context'. * If this pointer is NULL, your program will be terminated by assert. * * Return: * int. Valid error codes are: * * * DMMP_OK * * * DMMP_ERR_BUG * * * DMMP_ERR_NO_MEMORY * * * DMMP_ERR_NO_DAEMON * * Error number could be converted to string by dmmp_strerror(). */ DMMP_DLL_EXPORT int dmmp_reconfig(struct dmmp_context *ctx); #ifdef __cplusplus } /* End of extern "C" */ #endif #endif /* End of _LIB_DMMP_H_ */ multipath-tools-0.7.4/libdmmp/libdmmp_misc.c000066400000000000000000000060771320314174000211230ustar00rootroot00000000000000/* * Copyright (C) 2015 - 2017 Red Hat, Inc. * * 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 3 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, see . * * Author: Gris Ge * Todd Gill */ #include #include #include #include #include #include #include #include #include "libdmmp/libdmmp.h" #include "libdmmp_private.h" #define _DMMP_LOG_STRERR_ALIGN_WIDTH 80 /* ^ Only used in _dmmp_log_stderr() for pretty log output. * When provided log message is less than 80 bytes, fill it with space, then * print code file name, function name, line after the 80th bytes. */ static const struct _num_str_conv _DMMP_RC_MSG_CONV[] = { {DMMP_OK, "OK"}, {DMMP_ERR_NO_MEMORY, "Out of memory"}, {DMMP_ERR_BUG, "BUG of libdmmp library"}, {DMMP_ERR_IPC_TIMEOUT, "Timeout when communicate with multipathd, " "try to increase it via " "dmmp_context_timeout_set()"}, {DMMP_ERR_IPC_ERROR, "Error when communicate with multipathd daemon"}, {DMMP_ERR_NO_DAEMON, "The multipathd daemon not started"}, {DMMP_ERR_INCOMPATIBLE, "Incompatible multipathd daemon version"}, {DMMP_ERR_MPATH_BUSY, "Specified multipath device map is in use"}, {DMMP_ERR_MPATH_NOT_FOUND, "Specified multipath not found"}, {DMMP_ERR_INVALID_ARGUMENT, "Invalid argument"}, }; _dmmp_str_func_gen(dmmp_strerror, int, rc, _DMMP_RC_MSG_CONV); static const struct _num_str_conv _DMMP_PRI_CONV[] = { {DMMP_LOG_PRIORITY_DEBUG, "DEBUG"}, {DMMP_LOG_PRIORITY_INFO, "INFO"}, {DMMP_LOG_PRIORITY_WARNING, "WARNING"}, {DMMP_LOG_PRIORITY_ERROR, "ERROR"}, }; _dmmp_str_func_gen(dmmp_log_priority_str, int, priority, _DMMP_PRI_CONV); void _dmmp_log_stderr(struct dmmp_context *ctx, int priority, const char *file, int line, const char *func_name, const char *format, va_list args) { int printed_bytes = 0; void *userdata = NULL; printed_bytes += fprintf(stderr, "libdmmp %s: ", dmmp_log_priority_str(priority)); printed_bytes += vfprintf(stderr, format, args); userdata = dmmp_context_userdata_get(ctx); if (userdata != NULL) fprintf(stderr, "(userdata address: %p)", userdata); /* ^ Just demonstrate how userdata could be used and * bypass clang static analyzer about unused ctx argument warning */ if (printed_bytes < _DMMP_LOG_STRERR_ALIGN_WIDTH) { fprintf(stderr, "%*s # %s:%s():%d\n", _DMMP_LOG_STRERR_ALIGN_WIDTH - printed_bytes, "", file, func_name, line); } else { fprintf(stderr, " # %s:%s():%d\n", file, func_name, line); } } multipath-tools-0.7.4/libdmmp/libdmmp_mp.c000066400000000000000000000106721320314174000206000ustar00rootroot00000000000000/* * Copyright (C) 2015 - 2016 Red Hat, Inc. * * 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 3 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, see . * * Author: Gris Ge * Todd Gill */ #include #include #include #include #include #include #include #include "libdmmp/libdmmp.h" #include "libdmmp_private.h" struct dmmp_mpath { char *wwid; char *alias; uint32_t dmmp_pg_count; struct dmmp_path_group **dmmp_pgs; char *kdev_name; }; _dmmp_getter_func_gen(dmmp_mpath_name_get, struct dmmp_mpath, dmmp_mp, alias, const char *); _dmmp_getter_func_gen(dmmp_mpath_wwid_get, struct dmmp_mpath, dmmp_mp, wwid, const char *); _dmmp_getter_func_gen(dmmp_mpath_kdev_name_get, struct dmmp_mpath, dmmp_mp, kdev_name, const char *); struct dmmp_mpath *_dmmp_mpath_new(void) { struct dmmp_mpath *dmmp_mp = NULL; dmmp_mp = (struct dmmp_mpath *) malloc(sizeof(struct dmmp_mpath)); if (dmmp_mp != NULL) { dmmp_mp->wwid = NULL; dmmp_mp->alias = NULL; dmmp_mp->dmmp_pg_count = 0; dmmp_mp->dmmp_pgs = NULL; } return dmmp_mp; } int _dmmp_mpath_update(struct dmmp_context *ctx, struct dmmp_mpath *dmmp_mp, json_object *j_obj_map) { int rc = DMMP_OK; const char *wwid = NULL; const char *alias = NULL; struct array_list *ar_pgs = NULL; int ar_pgs_len = -1; uint32_t i = 0; struct dmmp_path_group *dmmp_pg = NULL; const char *kdev_name = NULL; assert(ctx != NULL); assert(dmmp_mp != NULL); assert(j_obj_map != NULL); _json_obj_get_value(ctx, j_obj_map, wwid, "uuid", json_type_string, json_object_get_string, rc, out); _json_obj_get_value(ctx, j_obj_map, alias, "name", json_type_string, json_object_get_string, rc, out); _json_obj_get_value(ctx, j_obj_map, kdev_name, "sysfs", json_type_string, json_object_get_string, rc, out); _dmmp_null_or_empty_str_check(ctx, wwid, rc, out); _dmmp_null_or_empty_str_check(ctx, alias, rc, out); dmmp_mp->wwid = strdup(wwid); _dmmp_alloc_null_check(ctx, dmmp_mp->wwid, rc, out); dmmp_mp->alias = strdup(alias); _dmmp_alloc_null_check(ctx, dmmp_mp->alias, rc, out); dmmp_mp->kdev_name = strdup(kdev_name); _dmmp_alloc_null_check(ctx, dmmp_mp->kdev_name, rc, out); _json_obj_get_value(ctx, j_obj_map, ar_pgs, "path_groups", json_type_array, json_object_get_array, rc, out); ar_pgs_len = array_list_length(ar_pgs); if (ar_pgs_len < 0) { rc = DMMP_ERR_BUG; _error(ctx, "BUG: Got negative length for ar_pgs"); goto out; } else if (ar_pgs_len == 0) goto out; else dmmp_mp->dmmp_pg_count = ar_pgs_len & UINT32_MAX; dmmp_mp->dmmp_pgs = (struct dmmp_path_group **) malloc(sizeof(struct dmmp_path_group *) * dmmp_mp->dmmp_pg_count); _dmmp_alloc_null_check(ctx, dmmp_mp->dmmp_pgs, rc, out); for (; i < dmmp_mp->dmmp_pg_count; ++i) dmmp_mp->dmmp_pgs[i] = NULL; for (i = 0; i < dmmp_mp->dmmp_pg_count; ++i) { dmmp_pg = _dmmp_path_group_new(); _dmmp_alloc_null_check(ctx, dmmp_pg, rc, out); dmmp_mp->dmmp_pgs[i] = dmmp_pg; _good(_dmmp_path_group_update(ctx, dmmp_pg, array_list_get_idx(ar_pgs, i)), rc, out); } _debug(ctx, "Got mpath wwid: '%s', alias: '%s'", dmmp_mp->wwid, dmmp_mp->alias); out: if (rc != DMMP_OK) _dmmp_mpath_free(dmmp_mp); return rc; } void _dmmp_mpath_free(struct dmmp_mpath *dmmp_mp) { if (dmmp_mp == NULL) return ; free((char *) dmmp_mp->alias); free((char *) dmmp_mp->wwid); free((char *) dmmp_mp->kdev_name); if (dmmp_mp->dmmp_pgs != NULL) _dmmp_path_group_array_free(dmmp_mp->dmmp_pgs, dmmp_mp->dmmp_pg_count); free(dmmp_mp); } void dmmp_path_group_array_get(struct dmmp_mpath *dmmp_mp, struct dmmp_path_group ***dmmp_pgs, uint32_t *dmmp_pg_count) { assert(dmmp_mp != NULL); assert(dmmp_pgs != NULL); assert(dmmp_pg_count != NULL); *dmmp_pgs = dmmp_mp->dmmp_pgs; *dmmp_pg_count = dmmp_mp->dmmp_pg_count; } multipath-tools-0.7.4/libdmmp/libdmmp_path.c000066400000000000000000000064161320314174000211210ustar00rootroot00000000000000/* * Copyright (C) 2015 - 2016 Red Hat, Inc. * * 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 3 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, see . * * Author: Gris Ge * Todd Gill */ #include #include #include #include #include #include "libdmmp/libdmmp.h" #include "libdmmp_private.h" #define _DMMP_SHOW_PS_INDEX_BLK_NAME 0 #define _DMMP_SHOW_PS_INDEX_SATAUS 1 #define _DMMP_SHOW_PS_INDEX_WWID 2 #define _DMMP_SHOW_PS_INDEX_PGID 3 struct dmmp_path { char *blk_name; uint32_t status; }; static const struct _num_str_conv _DMMP_PATH_STATUS_CONV[] = { {DMMP_PATH_STATUS_UNKNOWN, "undef"}, {DMMP_PATH_STATUS_UP, "ready"}, {DMMP_PATH_STATUS_DOWN, "faulty"}, {DMMP_PATH_STATUS_SHAKY, "shaky"}, {DMMP_PATH_STATUS_GHOST, "ghost"}, {DMMP_PATH_STATUS_PENDING, "i/o pending"}, {DMMP_PATH_STATUS_TIMEOUT, "i/o timeout"}, {DMMP_PATH_STATUS_DELAYED, "delayed"}, }; _dmmp_str_func_gen(dmmp_path_status_str, uint32_t, path_status, _DMMP_PATH_STATUS_CONV); _dmmp_str_conv_func_gen(_dmmp_path_status_str_conv, ctx, path_status_str, uint32_t, DMMP_PATH_STATUS_UNKNOWN, _DMMP_PATH_STATUS_CONV); _dmmp_getter_func_gen(dmmp_path_blk_name_get, struct dmmp_path, dmmp_p, blk_name, const char *); _dmmp_getter_func_gen(dmmp_path_status_get, struct dmmp_path, dmmp_p, status, uint32_t); struct dmmp_path *_dmmp_path_new(void) { struct dmmp_path *dmmp_p = NULL; dmmp_p = (struct dmmp_path *) malloc(sizeof(struct dmmp_path)); if (dmmp_p != NULL) { dmmp_p->blk_name = NULL; dmmp_p->status = DMMP_PATH_STATUS_UNKNOWN; } return dmmp_p; } int _dmmp_path_update(struct dmmp_context *ctx, struct dmmp_path *dmmp_p, json_object *j_obj_p) { int rc = DMMP_OK; const char *blk_name = NULL; const char *status_str = NULL; assert(ctx != NULL); assert(dmmp_p != NULL); assert(j_obj_p != NULL); _json_obj_get_value(ctx, j_obj_p, blk_name, "dev", json_type_string, json_object_get_string, rc, out); _json_obj_get_value(ctx, j_obj_p, status_str, "chk_st", json_type_string, json_object_get_string, rc, out); _dmmp_null_or_empty_str_check(ctx, blk_name, rc, out); _dmmp_null_or_empty_str_check(ctx, status_str, rc, out); dmmp_p->blk_name = strdup(blk_name); _dmmp_alloc_null_check(ctx, dmmp_p->blk_name, rc, out); dmmp_p->status = _dmmp_path_status_str_conv(ctx, status_str); _debug(ctx, "Got path blk_name: '%s'", dmmp_p->blk_name); _debug(ctx, "Got path status: %s(%" PRIu32 ")", dmmp_path_status_str(dmmp_p->status), dmmp_p->status); out: if (rc != DMMP_OK) _dmmp_path_free(dmmp_p); return rc; } void _dmmp_path_free(struct dmmp_path *dmmp_p) { if (dmmp_p == NULL) return; free(dmmp_p->blk_name); free(dmmp_p); } multipath-tools-0.7.4/libdmmp/libdmmp_pg.c000066400000000000000000000140731320314174000205710ustar00rootroot00000000000000/* * Copyright (C) 2015 - 2016 Red Hat, Inc. * * 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 3 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, see . * * Author: Gris Ge * Todd Gill */ #include #include #include #include #include #include #include "libdmmp/libdmmp.h" #include "libdmmp_private.h" #define _DMMP_SHOW_PGS_CMD "show groups raw format %w|%g|%p|%t|%s" #define _DMMP_SHOW_PG_INDEX_WWID 0 #define _DMMP_SHOW_PG_INDEX_PG_ID 1 #define _DMMP_SHOW_PG_INDEX_PRI 2 #define _DMMP_SHOW_PG_INDEX_STATUS 3 #define _DMMP_SHOW_PG_INDEX_SELECTOR 4 struct dmmp_path_group { uint32_t id; /* ^ pgindex of struct path, will be used for path group switch */ uint32_t status; uint32_t priority; char *selector; uint32_t dmmp_p_count; struct dmmp_path **dmmp_ps; }; static const struct _num_str_conv _DMMP_PATH_GROUP_STATUS_CONV[] = { {DMMP_PATH_GROUP_STATUS_UNKNOWN, "undef"}, {DMMP_PATH_GROUP_STATUS_ACTIVE, "active"}, {DMMP_PATH_GROUP_STATUS_DISABLED, "disabled"}, {DMMP_PATH_GROUP_STATUS_ENABLED, "enabled"}, }; _dmmp_str_func_gen(dmmp_path_group_status_str, uint32_t, pg_status, _DMMP_PATH_GROUP_STATUS_CONV); _dmmp_str_conv_func_gen(_dmmp_path_group_status_str_conv, ctx, pg_status_str, uint32_t, DMMP_PATH_GROUP_STATUS_UNKNOWN, _DMMP_PATH_GROUP_STATUS_CONV); _dmmp_getter_func_gen(dmmp_path_group_id_get, struct dmmp_path_group, dmmp_pg, id, uint32_t); _dmmp_getter_func_gen(dmmp_path_group_status_get, struct dmmp_path_group, dmmp_pg, status, uint32_t); _dmmp_getter_func_gen(dmmp_path_group_priority_get, struct dmmp_path_group, dmmp_pg, priority, uint32_t); _dmmp_getter_func_gen(dmmp_path_group_selector_get, struct dmmp_path_group, dmmp_pg, selector, const char *); _dmmp_array_free_func_gen(_dmmp_path_group_array_free, struct dmmp_path_group, _dmmp_path_group_free); struct dmmp_path_group *_dmmp_path_group_new(void) { struct dmmp_path_group *dmmp_pg = NULL; dmmp_pg = (struct dmmp_path_group *) malloc(sizeof(struct dmmp_path_group)); if (dmmp_pg != NULL) { dmmp_pg->id = _DMMP_PATH_GROUP_ID_UNKNOWN; dmmp_pg->status = DMMP_PATH_GROUP_STATUS_UNKNOWN; dmmp_pg->priority = 0; dmmp_pg->selector = NULL; dmmp_pg->dmmp_p_count = 0; dmmp_pg->dmmp_ps = NULL; } return dmmp_pg; } int _dmmp_path_group_update(struct dmmp_context *ctx, struct dmmp_path_group *dmmp_pg, json_object *j_obj_pg) { int rc = DMMP_OK; uint32_t id = 0; int priority_int = -1 ; const char *status_str = NULL; const char *selector = NULL; struct array_list *ar_ps = NULL; int ar_ps_len = -1; uint32_t i = 0; struct dmmp_path *dmmp_p = NULL; assert(ctx != NULL); assert(dmmp_pg != NULL); assert(j_obj_pg != NULL); _json_obj_get_value(ctx, j_obj_pg, status_str, "dm_st", json_type_string, json_object_get_string, rc, out); _json_obj_get_value(ctx, j_obj_pg, selector, "selector", json_type_string, json_object_get_string, rc, out); _json_obj_get_value(ctx, j_obj_pg, priority_int, "pri", json_type_int, json_object_get_int, rc, out); _json_obj_get_value(ctx, j_obj_pg, id, "group", json_type_int, json_object_get_int, rc, out); dmmp_pg->priority = (priority_int <= 0) ? 0 : priority_int & UINT32_MAX; _dmmp_null_or_empty_str_check(ctx, status_str, rc, out); _dmmp_null_or_empty_str_check(ctx, selector, rc, out); dmmp_pg->selector = strdup(selector); _dmmp_alloc_null_check(ctx, dmmp_pg->selector, rc, out); dmmp_pg->id = id; if (dmmp_pg->id == _DMMP_PATH_GROUP_ID_UNKNOWN) { rc = DMMP_ERR_BUG; _error(ctx, "BUG: Got unknown(%d) path group ID", _DMMP_PATH_GROUP_ID_UNKNOWN); goto out; } dmmp_pg->status = _dmmp_path_group_status_str_conv(ctx, status_str); _json_obj_get_value(ctx, j_obj_pg, ar_ps, "paths", json_type_array, json_object_get_array, rc, out); ar_ps_len = array_list_length(ar_ps); if (ar_ps_len < 0) { rc = DMMP_ERR_BUG; _error(ctx, "BUG: Got negative length for ar_ps"); goto out; } else if (ar_ps_len == 0) goto out; else dmmp_pg->dmmp_p_count = ar_ps_len & UINT32_MAX; dmmp_pg->dmmp_ps = (struct dmmp_path **) malloc(sizeof(struct dmmp_path *) * dmmp_pg->dmmp_p_count); _dmmp_alloc_null_check(ctx, dmmp_pg->dmmp_ps, rc, out); for (; i < dmmp_pg->dmmp_p_count; ++i) dmmp_pg->dmmp_ps[i] = NULL; for (i = 0; i < dmmp_pg->dmmp_p_count; ++i) { dmmp_p = _dmmp_path_new(); _dmmp_alloc_null_check(ctx, dmmp_p, rc, out); dmmp_pg->dmmp_ps[i] = dmmp_p; _good(_dmmp_path_update(ctx, dmmp_p, array_list_get_idx(ar_ps, i)), rc, out); } _debug(ctx, "Got path group id: %" PRIu32 "", dmmp_pg->id); _debug(ctx, "Got path group priority: %" PRIu32 "", dmmp_pg->priority); _debug(ctx, "Got path group status: %s(%" PRIu32 ")", dmmp_path_group_status_str(dmmp_pg->status), dmmp_pg->status); _debug(ctx, "Got path group selector: '%s'", dmmp_pg->selector); out: if (rc != DMMP_OK) _dmmp_path_group_free(dmmp_pg); return rc; } void _dmmp_path_group_free(struct dmmp_path_group *dmmp_pg) { uint32_t i = 0; if (dmmp_pg == NULL) return; free((char *) dmmp_pg->selector); if (dmmp_pg->dmmp_ps != NULL) { for (i = 0; i < dmmp_pg->dmmp_p_count; ++i) { _dmmp_path_free(dmmp_pg->dmmp_ps[i]); } free(dmmp_pg->dmmp_ps); } free(dmmp_pg); } void dmmp_path_array_get(struct dmmp_path_group *mp_pg, struct dmmp_path ***mp_paths, uint32_t *dmmp_p_count) { assert(mp_pg != NULL); assert(mp_paths != NULL); assert(dmmp_p_count != NULL); *mp_paths = mp_pg->dmmp_ps; *dmmp_p_count = mp_pg->dmmp_p_count; } multipath-tools-0.7.4/libdmmp/libdmmp_private.h000066400000000000000000000144721320314174000216450ustar00rootroot00000000000000/* * Copyright (C) 2015 - 2016 Red Hat, Inc. * * 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 3 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, see . * * Author: Gris Ge * Todd Gill */ #ifndef _LIB_DMMP_PRIVATE_H_ #define _LIB_DMMP_PRIVATE_H_ /* * Notes: * Internal/Private functions does not check input argument but using * assert() to abort if NULL pointer found in argument. */ #include #include #include #include #include "libdmmp/libdmmp.h" #ifdef __cplusplus extern "C" { #endif #define _good(rc, rc_val, out) \ do { \ rc_val = rc; \ if (rc_val != DMMP_OK) \ goto out; \ } while(0) #define _DMMP_PATH_GROUP_ID_UNKNOWN 0 struct DMMP_DLL_LOCAL _num_str_conv; struct _num_str_conv { const uint32_t value; const char *str; }; #define _dmmp_str_func_gen(func_name, var_type, var, conv_array) \ const char *func_name(var_type var) { \ size_t i = 0; \ uint32_t tmp_var = var & UINT32_MAX; \ /* In the whole libdmmp, we don't have negative value */ \ for (; i < sizeof(conv_array)/sizeof(conv_array[0]); ++i) { \ if ((conv_array[i].value) == tmp_var) \ return conv_array[i].str; \ } \ return "Invalid argument"; \ } #define _dmmp_str_conv_func_gen(func_name, ctx, var_name, out_type, \ unknown_value, conv_array) \ static out_type func_name(struct dmmp_context *ctx, const char *var_name) { \ size_t i = 0; \ for (; i < sizeof(conv_array)/sizeof(conv_array[0]); ++i) { \ if (strcmp(conv_array[i].str, var_name) == 0) \ return conv_array[i].value; \ } \ _warn(ctx, "Got unknown " #var_name ": '%s'", var_name); \ return unknown_value; \ } #define _json_obj_get_value(ctx, j_obj, out_value, key, value_type, \ value_func, rc, out) \ do { \ json_type j_type = json_type_null; \ json_object *j_obj_tmp = NULL; \ if (json_object_object_get_ex(j_obj, key, &j_obj_tmp) != TRUE) { \ _error(ctx, "Invalid JSON output from multipathd IPC: " \ "key '%s' not found", key); \ rc = DMMP_ERR_IPC_ERROR; \ goto out; \ } \ if (j_obj_tmp == NULL) { \ _error(ctx, "BUG: Got NULL j_obj_tmp from " \ "json_object_object_get_ex() while it return TRUE"); \ rc = DMMP_ERR_BUG; \ goto out; \ } \ j_type = json_object_get_type(j_obj_tmp); \ if (j_type != value_type) { \ _error(ctx, "Invalid value type for key'%s' of JSON output " \ "from multipathd IPC. Should be %s(%d), " \ "but got %s(%d)", key, json_type_to_name(value_type), \ value_type, json_type_to_name(j_type), j_type); \ rc = DMMP_ERR_IPC_ERROR; \ goto out; \ } \ out_value = value_func(j_obj_tmp); \ } while(0); DMMP_DLL_LOCAL int _dmmp_ipc_exec(struct dmmp_context *ctx, const char *cmd, char **output); DMMP_DLL_LOCAL struct dmmp_mpath *_dmmp_mpath_new(void); DMMP_DLL_LOCAL struct dmmp_path_group *_dmmp_path_group_new(void); DMMP_DLL_LOCAL struct dmmp_path *_dmmp_path_new(void); DMMP_DLL_LOCAL int _dmmp_mpath_update(struct dmmp_context *ctx, struct dmmp_mpath *dmmp_mp, json_object *j_obj_map); DMMP_DLL_LOCAL int _dmmp_path_group_update(struct dmmp_context *ctx, struct dmmp_path_group *dmmp_pg, json_object *j_obj_pg); DMMP_DLL_LOCAL int _dmmp_path_update(struct dmmp_context *ctx, struct dmmp_path *dmmp_p, json_object *j_obj_p); DMMP_DLL_LOCAL void _dmmp_mpath_free(struct dmmp_mpath *dmmp_mp); DMMP_DLL_LOCAL void _dmmp_path_group_free(struct dmmp_path_group *dmmp_pg); DMMP_DLL_LOCAL void _dmmp_path_group_array_free (struct dmmp_path_group **dmmp_pgs, uint32_t dmmp_pg_count); DMMP_DLL_LOCAL void _dmmp_path_free(struct dmmp_path *dmmp_p); DMMP_DLL_LOCAL void _dmmp_log(struct dmmp_context *ctx, int priority, const char *file, int line, const char *func_name, const char *format, ...); DMMP_DLL_LOCAL void _dmmp_log_err_str(struct dmmp_context *ctx, int rc); DMMP_DLL_LOCAL void _dmmp_log_stderr(struct dmmp_context *ctx, int priority, const char *file, int line, const char *func_name, const char *format, va_list args); #define _dmmp_log_cond(ctx, prio, arg...) \ do { \ if (dmmp_context_log_priority_get(ctx) >= prio) \ _dmmp_log(ctx, prio, __FILE__, __LINE__, __FUNCTION__, \ ## arg); \ } while (0) #define _debug(ctx, arg...) \ _dmmp_log_cond(ctx, DMMP_LOG_PRIORITY_DEBUG, ## arg) #define _info(ctx, arg...) \ _dmmp_log_cond(ctx, DMMP_LOG_PRIORITY_INFO, ## arg) #define _warn(ctx, arg...) \ _dmmp_log_cond(ctx, DMMP_LOG_PRIORITY_WARNING, ## arg) #define _error(ctx, arg...) \ _dmmp_log_cond(ctx, DMMP_LOG_PRIORITY_ERROR, ## arg) /* * Check pointer returned by malloc() or strdup(), if NULL, set * rc as DMMP_ERR_NO_MEMORY, report error and goto goto_out. */ #define _dmmp_alloc_null_check(ctx, ptr, rc, goto_out) \ do { \ if (ptr == NULL) { \ rc = DMMP_ERR_NO_MEMORY; \ _error(ctx, dmmp_strerror(rc)); \ goto goto_out; \ } \ } while(0) #define _dmmp_null_or_empty_str_check(ctx, var, rc, goto_out) \ do { \ if (var == NULL) { \ rc = DMMP_ERR_BUG; \ _error(ctx, "BUG: Got NULL " #var); \ goto goto_out; \ } \ if (strlen(var) == 0) { \ rc = DMMP_ERR_BUG; \ _error(ctx, "BUG: Got empty " #var); \ goto goto_out; \ } \ } while(0) #define _dmmp_getter_func_gen(func_name, struct_name, struct_data, \ prop_name, prop_type) \ prop_type func_name(struct_name *struct_data) \ { \ assert(struct_data != NULL); \ return struct_data->prop_name; \ } #define _dmmp_array_free_func_gen(func_name, struct_name, struct_free_func) \ void func_name(struct_name **ptr_array, uint32_t ptr_count) \ { \ uint32_t i = 0; \ if (ptr_array == NULL) \ return; \ for (; i < ptr_count; ++i) \ struct_free_func(ptr_array[i]); \ free(ptr_array); \ } #ifdef __cplusplus } /* End of extern "C" */ #endif #endif /* End of _LIB_DMMP_PRIVATE_H_ */ multipath-tools-0.7.4/libdmmp/test/000077500000000000000000000000001320314174000172655ustar00rootroot00000000000000multipath-tools-0.7.4/libdmmp/test/Makefile000066400000000000000000000013451320314174000207300ustar00rootroot00000000000000# Makefile # # Copyright (C) 2015-2016 Gris Ge # include ../../Makefile.inc _libdmmpdir=../$(libdmmpdir) _mpathcmddir=../$(mpathcmddir) TEST_EXEC = libdmmp_test SPD_TEST_EXEC = libdmmp_speed_test CFLAGS += -I$(_libdmmpdir) LDFLAGS += -L$(_libdmmpdir) -ldmmp all: $(TEST_EXEC) $(SPD_TEST_EXEC) check: $(TEST_EXEC) $(SPD_TEST_EXEC) sudo env LD_LIBRARY_PATH=$(_libdmmpdir):$(_mpathcmddir) \ valgrind --quiet --leak-check=full \ --show-reachable=no --show-possibly-lost=no \ --trace-children=yes --error-exitcode=1 \ ./$(TEST_EXEC) $(MAKE) speed_test speed_test: $(SPD_TEST_EXEC) sudo env LD_LIBRARY_PATH=$(_libdmmpdir):$(_mpathcmddir) \ time -p ./$(SPD_TEST_EXEC) clean: rm -f $(TEST_EXEC) $(SPD_TEST_EXEC) multipath-tools-0.7.4/libdmmp/test/libdmmp_speed_test.c000066400000000000000000000026211320314174000232750ustar00rootroot00000000000000/* * Copyright (C) 2015-2016 Red Hat, Inc. * * 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 3 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, see . * * Author: Gris Ge */ #include #include #include #include #include #include #include #include int main(int argc, char *argv[]) { struct dmmp_context *ctx = NULL; struct dmmp_mpath **dmmp_mps = NULL; uint32_t dmmp_mp_count = 0; int rc = EXIT_SUCCESS; ctx = dmmp_context_new(); dmmp_context_log_priority_set(ctx, DMMP_LOG_PRIORITY_WARNING); if (dmmp_mpath_array_get(ctx, &dmmp_mps, &dmmp_mp_count) != 0) { printf("FAILED\n"); rc = EXIT_FAILURE; } else { printf("Got %" PRIu32 " mpath\n", dmmp_mp_count); dmmp_mpath_array_free(dmmp_mps, dmmp_mp_count); } dmmp_context_free(ctx); exit(rc); } multipath-tools-0.7.4/libdmmp/test/libdmmp_test.c000066400000000000000000000132671320314174000221250ustar00rootroot00000000000000/* * Copyright (C) 2015-2017 Red Hat, Inc. * * 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 3 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, see . * * Author: Gris Ge */ #include #include #include #include #include #include #include #include #include #define FAIL(rc, out, ...) \ do { \ rc = EXIT_FAILURE; \ fprintf(stderr, "FAIL: "__VA_ARGS__ ); \ goto out; \ } while(0) #define PASS(...) fprintf(stdout, "PASS: "__VA_ARGS__ ); #define FILE_NAME_SIZE 256 #define TMO 60000 /* Forcing timeout to 60 seconds */ int test_paths(struct dmmp_path_group *mp_pg) { struct dmmp_path **mp_ps = NULL; uint32_t mp_p_count = 0; uint32_t i = 0; const char *blk_name = NULL; int rc = EXIT_SUCCESS; dmmp_path_array_get(mp_pg, &mp_ps, &mp_p_count); if (mp_p_count == 0) FAIL(rc, out, "dmmp_path_array_get(): Got no path\n"); for (i = 0; i < mp_p_count; ++i) { blk_name = dmmp_path_blk_name_get(mp_ps[i]); if (blk_name == NULL) FAIL(rc, out, "dmmp_path_blk_name_get(): Got NULL\n"); PASS("dmmp_path_blk_name_get(): %s\n", blk_name); PASS("dmmp_path_status_get(): %" PRIu32 " -- %s\n", dmmp_path_status_get(mp_ps[i]), dmmp_path_status_str(dmmp_path_status_get(mp_ps[i]))); } out: return rc; } int test_path_groups(struct dmmp_mpath *dmmp_mp) { struct dmmp_path_group **dmmp_pgs = NULL; uint32_t dmmp_pg_count = 0; uint32_t i = 0; int rc = EXIT_SUCCESS; dmmp_path_group_array_get(dmmp_mp, &dmmp_pgs, &dmmp_pg_count); if ((dmmp_pg_count == 0) && (dmmp_pgs != NULL)) FAIL(rc, out, "dmmp_path_group_array_get(): mp_pgs is not NULL " "but mp_pg_count is 0\n"); if ((dmmp_pg_count != 0) && (dmmp_pgs == NULL)) FAIL(rc, out, "dmmp_path_group_array_get(): mp_pgs is NULL " "but mp_pg_count is not 0\n"); if (dmmp_pg_count == 0) FAIL(rc, out, "dmmp_path_group_array_get(): " "Got 0 path group\n"); PASS("dmmp_path_group_array_get(): Got %" PRIu32 " path groups\n", dmmp_pg_count); for (i = 0; i < dmmp_pg_count; ++i) { PASS("dmmp_path_group_id_get(): %" PRIu32 "\n", dmmp_path_group_id_get(dmmp_pgs[i])); PASS("dmmp_path_group_priority_get(): %" PRIu32 "\n", dmmp_path_group_priority_get(dmmp_pgs[i])); PASS("dmmp_path_group_status_get(): %" PRIu32 " -- %s\n", dmmp_path_group_status_get(dmmp_pgs[i]), dmmp_path_group_status_str (dmmp_path_group_status_get(dmmp_pgs[i]))); PASS("dmmp_path_group_selector_get(): %s\n", dmmp_path_group_selector_get(dmmp_pgs[i])); rc = test_paths(dmmp_pgs[i]); if (rc != 0) goto out; } out: return rc; } int main(int argc, char *argv[]) { struct dmmp_context *ctx = NULL; struct dmmp_mpath **dmmp_mps = NULL; uint32_t dmmp_mp_count = 0; uint32_t old_dmmp_mp_count = 0; const char *name = NULL; const char *wwid = NULL; const char *kdev = NULL; uint32_t i = 0; int rc = EXIT_SUCCESS; const char *old_name = NULL; bool found = false; ctx = dmmp_context_new(); dmmp_context_log_priority_set(ctx, DMMP_LOG_PRIORITY_DEBUG); dmmp_context_userdata_set(ctx, ctx); dmmp_context_userdata_set(ctx, NULL); dmmp_context_timeout_set(ctx, TMO); if (dmmp_context_timeout_get(ctx) != TMO) FAIL(rc, out, "dmmp_context_timeout_set(): Failed to set " "timeout to %u\n", TMO); if (dmmp_mpath_array_get(ctx, &dmmp_mps, &dmmp_mp_count) != 0) FAIL(rc, out, "dmmp_mpath_array_get(): rc != 0\n"); if (dmmp_mp_count == 0) FAIL(rc, out, "dmmp_mpath_array_get(): " "Got no multipath devices\n"); PASS("dmmp_mpath_array_get(): Got %" PRIu32 " mpath\n", dmmp_mp_count); for (i = 0; i < dmmp_mp_count; ++i) { name = dmmp_mpath_name_get(dmmp_mps[i]); wwid = dmmp_mpath_wwid_get(dmmp_mps[i]); kdev = dmmp_mpath_kdev_name_get(dmmp_mps[i]); if ((name == NULL) ||(wwid == NULL) || (kdev == NULL)) FAIL(rc, out, "dmmp_mpath_array_get(): Got NULL name or wwid"); PASS("dmmp_mpath_array_get(): Got mpath(%s): %s %s\n", kdev, name, wwid); rc = test_path_groups(dmmp_mps[i]); if (rc != 0) goto out; } old_name = strdup(name); if (old_name == NULL) FAIL(rc, out, "strdup(): no memory\n"); old_dmmp_mp_count = dmmp_mp_count; dmmp_mpath_array_free(dmmp_mps, dmmp_mp_count); if (dmmp_flush_mpath(ctx, old_name) != DMMP_OK) FAIL(rc, out, "dmmp_flush_mpath(): Failed\n"); PASS("dmmp_flush_mpath(): OK\n"); if (dmmp_reconfig(ctx) != DMMP_OK) FAIL(rc, out, "dmmp_reconfig(): Failed\n"); PASS("dmmp_reconfig(): OK\n"); if (dmmp_mpath_array_get(ctx, &dmmp_mps, &dmmp_mp_count) != 0) FAIL(rc, out, "dmmp_mpath_array_get(): rc != 0\n"); if (dmmp_mp_count == 0) FAIL(rc, out, "dmmp_mpath_array_get(): " "Got no multipath devices\n"); if (dmmp_mp_count != old_dmmp_mp_count) FAIL(rc, out, "Got different mpath count after reconfig: " "old %" PRIu32 ", new %" PRIu32 "\n", old_dmmp_mp_count, dmmp_mp_count); for (i = 0; i < dmmp_mp_count; ++i) { if (strcmp(old_name, dmmp_mpath_name_get(dmmp_mps[i])) == 0) { found = true; break; } } if (found == false) FAIL(rc, out, "dmmp_reconfig() does not recreate deleted " "mpath %s\n", old_name); out: dmmp_context_free(ctx); exit(rc); } multipath-tools-0.7.4/libmpathcmd/000077500000000000000000000000001320314174000171465ustar00rootroot00000000000000multipath-tools-0.7.4/libmpathcmd/Makefile000066400000000000000000000013241320314174000206060ustar00rootroot00000000000000include ../Makefile.inc SONAME = 0 DEVLIB = libmpathcmd.so LIBS = $(DEVLIB).$(SONAME) CFLAGS += $(LIB_CFLAGS) OBJS = mpath_cmd.o all: $(LIBS) $(LIBS): $(OBJS) $(CC) $(LDFLAGS) $(SHARED_FLAGS) -Wl,-soname=$@ -o $@ $(OBJS) $(LIBDEPS) $(LN) $@ $(DEVLIB) install: $(LIBS) $(INSTALL_PROGRAM) -d $(DESTDIR)$(syslibdir) $(INSTALL_PROGRAM) -m 755 $(LIBS) $(DESTDIR)$(syslibdir)/$(LIBS) $(LN) $(LIBS) $(DESTDIR)$(syslibdir)/$(DEVLIB) $(INSTALL_PROGRAM) -d $(DESTDIR)$(includedir) $(INSTALL_PROGRAM) -m 644 mpath_cmd.h $(DESTDIR)$(includedir) uninstall: $(RM) $(DESTDIR)$(syslibdir)/$(LIBS) $(RM) $(DESTDIR)$(syslibdir)/$(DEVLIB) $(RM) $(DESTDIR)$(includedir)/mpath_cmd.h clean: $(RM) core *.a *.o *.so *.so.* *.gz multipath-tools-0.7.4/libmpathcmd/mpath_cmd.c000066400000000000000000000060261320314174000212520ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include "mpath_cmd.h" /* * keep reading until its all read */ static ssize_t read_all(int fd, void *buf, size_t len, unsigned int timeout) { size_t total = 0; ssize_t n; int ret; struct pollfd pfd; while (len) { pfd.fd = fd; pfd.events = POLLIN; ret = poll(&pfd, 1, timeout); if (!ret) { errno = ETIMEDOUT; return -1; } else if (ret < 0) { if (errno == EINTR) continue; return -1; } else if (!(pfd.revents & POLLIN)) continue; n = recv(fd, buf, len, 0); if (n < 0) { if ((errno == EINTR) || (errno == EAGAIN)) continue; return -1; } if (!n) return total; buf = n + (char *)buf; len -= n; total += n; } return total; } /* * keep writing until it's all sent */ static size_t write_all(int fd, const void *buf, size_t len) { size_t total = 0; while (len) { ssize_t n = send(fd, buf, len, MSG_NOSIGNAL); if (n < 0) { if ((errno == EINTR) || (errno == EAGAIN)) continue; return total; } if (!n) return total; buf = n + (char *)buf; len -= n; total += n; } return total; } /* * connect to a unix domain socket */ int mpath_connect(void) { int fd, len; struct sockaddr_un addr; memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_LOCAL; addr.sun_path[0] = '\0'; len = strlen(DEFAULT_SOCKET) + 1 + sizeof(sa_family_t); strncpy(&addr.sun_path[1], DEFAULT_SOCKET, len); fd = socket(AF_LOCAL, SOCK_STREAM, 0); if (fd == -1) return -1; if (connect(fd, (struct sockaddr *)&addr, len) == -1) { close(fd); return -1; } return fd; } int mpath_disconnect(int fd) { return close(fd); } ssize_t mpath_recv_reply_len(int fd, unsigned int timeout) { size_t len; ssize_t ret; ret = read_all(fd, &len, sizeof(len), timeout); if (ret < 0) return ret; if (ret != sizeof(len)) { errno = EIO; return -1; } return len; } int mpath_recv_reply_data(int fd, char *reply, size_t len, unsigned int timeout) { ssize_t ret; ret = read_all(fd, reply, len, timeout); if (ret < 0) return ret; if (ret != len) { errno = EIO; return -1; } reply[len - 1] = '\0'; return 0; } int mpath_recv_reply(int fd, char **reply, unsigned int timeout) { int err; ssize_t len; *reply = NULL; len = mpath_recv_reply_len(fd, timeout); if (len <= 0) return len; *reply = malloc(len); if (!*reply) return -1; err = mpath_recv_reply_data(fd, *reply, len, timeout); if (err) { free(*reply); *reply = NULL; return -1; } return 0; } int mpath_send_cmd(int fd, const char *cmd) { size_t len; if (cmd != NULL) len = strlen(cmd) + 1; else len = 0; if (write_all(fd, &len, sizeof(len)) != sizeof(len)) return -1; if (len && write_all(fd, cmd, len) != len) return -1; return 0; } int mpath_process_cmd(int fd, const char *cmd, char **reply, unsigned int timeout) { if (mpath_send_cmd(fd, cmd) != 0) return -1; return mpath_recv_reply(fd, reply, timeout); } multipath-tools-0.7.4/libmpathcmd/mpath_cmd.h000066400000000000000000000066551320314174000212670ustar00rootroot00000000000000/* * Copyright (C) 2015 Red Hat, Inc. * * This file is part of the device-mapper multipath userspace tools. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #ifndef LIB_MPATH_CMD_H #define LIB_MPATH_CMD_H #ifdef __cpluscplus extern "C" { #endif #define DEFAULT_SOCKET "/org/kernel/linux/storage/multipathd" #define DEFAULT_REPLY_TIMEOUT 4000 /* * DESCRIPTION: * Connect to the running multipathd daemon. On systems with the * multipathd.socket systemd unit file installed, this command will * start multipathd if it is not already running. This function * must be run before any of the others in this library * * RETURNS: * A file descriptor on success. -1 on failure (with errno set). */ int mpath_connect(void); /* * DESCRIPTION: * Disconnect from the multipathd daemon. This function must be * run after after processing all the multipath commands. * * RETURNS: * 0 on success. -1 on failure (with errno set). */ int mpath_disconnect(int fd); /* * DESCRIPTION * Send multipathd a command and return the reply. This function * does the same as calling mpath_send_cmd() and then * mpath_recv_reply() * * RETURNS: * 0 on successs, and reply will either be NULL (if there was no * reply data), or point to the reply string, which must be freed by * the caller. -1 on failure (with errno set). */ int mpath_process_cmd(int fd, const char *cmd, char **reply, unsigned int timeout); /* * DESCRIPTION: * Send a command to multipathd * * RETURNS: * 0 on success. -1 on failure (with errno set) */ int mpath_send_cmd(int fd, const char *cmd); /* * DESCRIPTION: * Return a reply from multipathd for a previously sent command. * This is equivalent to calling mpath_recv_reply_len(), allocating * a buffer of the appropriate size, and then calling * mpath_recv_reply_data() with that buffer. * * RETURNS: * 0 on success, and reply will either be NULL (if there was no * reply data), or point to the reply string, which must be freed by * the caller, -1 on failure (with errno set). */ int mpath_recv_reply(int fd, char **reply, unsigned int timeout); /* * DESCRIPTION: * Return the size of the upcoming reply data from the sent multipath * command. This must be called before calling mpath_recv_reply_data(). * * RETURNS: * The required size of the reply data buffer on success. -1 on * failure (with errno set). */ ssize_t mpath_recv_reply_len(int fd, unsigned int timeout); /* * DESCRIPTION: * Return the reply data from the sent multipath command. * mpath_recv_reply_len must be called first. reply must point to a * buffer of len size. * * RETURNS: * 0 on success, and reply will contain the reply data string. -1 * on failure (with errno set). */ int mpath_recv_reply_data(int fd, char *reply, size_t len, unsigned int timeout); #ifdef __cplusplus } #endif #endif /* LIB_MPATH_CMD_H */ multipath-tools-0.7.4/libmpathpersist/000077500000000000000000000000001320314174000200745ustar00rootroot00000000000000multipath-tools-0.7.4/libmpathpersist/Makefile000066400000000000000000000027151320314174000215410ustar00rootroot00000000000000include ../Makefile.inc SONAME = 0 DEVLIB = libmpathpersist.so LIBS = $(DEVLIB).$(SONAME) CFLAGS += $(LIB_CFLAGS) -I$(multipathdir) -I$(mpathpersistdir) -I$(mpathcmddir) LIBDEPS += -lpthread -ldevmapper -ldl -L$(multipathdir) -lmultipath \ -L$(mpathcmddir) -lmpathcmd OBJS = mpath_persist.o mpath_updatepr.o mpath_pr_ioctl.o all: $(LIBS) $(LIBS): $(OBJS) $(CC) $(LDFLAGS) $(SHARED_FLAGS) $(LIBDEPS) -Wl,-soname=$@ -o $@ $(OBJS) $(LN) $(LIBS) $(DEVLIB) $(GZIP) mpath_persistent_reserve_in.3 > mpath_persistent_reserve_in.3.gz $(GZIP) mpath_persistent_reserve_out.3 > mpath_persistent_reserve_out.3.gz install: $(LIBS) $(INSTALL_PROGRAM) -d $(DESTDIR)$(syslibdir) $(INSTALL_PROGRAM) -m 755 $(LIBS) $(DESTDIR)$(syslibdir)/$(LIBS) $(INSTALL_PROGRAM) -m 755 -d $(DESTDIR)$(syslibdir) $(INSTALL_PROGRAM) -m 755 -d $(DESTDIR)$(man3dir) $(INSTALL_PROGRAM) -m 755 -d $(DESTDIR)$(includedir) $(LN) $(LIBS) $(DESTDIR)$(syslibdir)/$(DEVLIB) $(INSTALL_PROGRAM) -m 644 mpath_persistent_reserve_in.3.gz $(DESTDIR)$(man3dir) $(INSTALL_PROGRAM) -m 644 mpath_persistent_reserve_out.3.gz $(DESTDIR)$(man3dir) $(INSTALL_PROGRAM) -m 644 mpath_persist.h $(DESTDIR)$(includedir) uninstall: $(RM) $(DESTDIR)$(syslibdir)/$(LIBS) $(RM) $(DESTDIR)$(man3dir)/mpath_persistent_reserve_in.3.gz $(RM) $(DESTDIR)$(man3dir)/mpath_persistent_reserve_out.3.gz $(RM) $(DESTDIR)$(includedir)/mpath_persist.h $(RM) $(DESTDIR)$(syslibdir)/$(DEVLIB) clean: $(RM) core *.a *.o *.so *.so.* *.gz multipath-tools-0.7.4/libmpathpersist/mpath_persist.c000066400000000000000000000566441320314174000231410ustar00rootroot00000000000000#include #include "defaults.h" #include #include #include #include "vector.h" #include "checkers.h" #include "structs.h" #include "structs_vec.h" #include #include "prio.h" #include #include "devmapper.h" #include "debug.h" #include "config.h" #include "switchgroup.h" #include "discovery.h" #include "dmparser.h" #include #include "propsel.h" #include "util.h" #include "mpath_persist.h" #include "mpathpr.h" #include "mpath_pr_ioctl.h" #include #include #include #include #include #include #define __STDC_FORMAT_MACROS 1 extern struct udev *udev; struct config * mpath_lib_init (void) { struct config *conf; conf = load_config(DEFAULT_CONFIGFILE); if (!conf) { condlog(0, "Failed to initialize multipath config."); return NULL; } if (conf->max_fds) { struct rlimit fd_limit; fd_limit.rlim_cur = conf->max_fds; fd_limit.rlim_max = conf->max_fds; if (setrlimit(RLIMIT_NOFILE, &fd_limit) < 0) condlog(0, "can't set open fds limit to %d : %s", conf->max_fds, strerror(errno)); } return conf; } int mpath_lib_exit (struct config *conf) { dm_lib_release(); dm_lib_exit(); cleanup_prio(); cleanup_checkers(); free_config(conf); conf = NULL; return 0; } static int updatepaths (struct multipath * mpp) { int i, j; struct pathgroup * pgp; struct path * pp; struct config *conf; if (!mpp->pg) return 0; vector_foreach_slot (mpp->pg, pgp, i){ if (!pgp->paths) continue; vector_foreach_slot (pgp->paths, pp, j){ if (!strlen(pp->dev)){ if (devt2devname(pp->dev, FILE_NAME_SIZE, pp->dev_t)){ /* * path is not in sysfs anymore */ pp->state = PATH_DOWN; continue; } pp->mpp = mpp; conf = get_multipath_config(); pathinfo(pp, conf, DI_ALL); put_multipath_config(conf); continue; } pp->mpp = mpp; if (pp->state == PATH_UNCHECKED || pp->state == PATH_WILD) { conf = get_multipath_config(); pathinfo(pp, conf, DI_CHECKER); put_multipath_config(conf); } if (pp->priority == PRIO_UNDEF) { conf = get_multipath_config(); pathinfo(pp, conf, DI_PRIO); put_multipath_config(conf); } } } return 0; } int mpath_prin_activepath (struct multipath *mpp, int rq_servact, struct prin_resp * resp, int noisy) { int i,j, ret = MPATH_PR_DMMP_ERROR; struct pathgroup *pgp = NULL; struct path *pp = NULL; vector_foreach_slot (mpp->pg, pgp, j){ vector_foreach_slot (pgp->paths, pp, i){ if (!((pp->state == PATH_UP) || (pp->state == PATH_GHOST))){ condlog(2, "%s: %s not available. Skip.", mpp->wwid, pp->dev); condlog(3, "%s: status = %d.", mpp->wwid, pp->state); continue; } condlog(3, "%s: sending pr in command to %s ", mpp->wwid, pp->dev); ret = mpath_send_prin_activepath(pp->dev, rq_servact, resp, noisy); switch(ret) { case MPATH_PR_SUCCESS: case MPATH_PR_SENSE_INVALID_OP: return ret; default: continue; } } } return ret; } int mpath_persistent_reserve_in (int fd, int rq_servact, struct prin_resp *resp, int noisy, int verbose) { struct stat info; vector curmp = NULL; vector pathvec = NULL; char * alias; struct multipath * mpp; int map_present; int major, minor; int ret; struct config *conf; conf = get_multipath_config(); conf->verbosity = verbose; put_multipath_config(conf); if (fstat( fd, &info) != 0){ condlog(0, "stat error %d", fd); return MPATH_PR_FILE_ERROR; } if(!S_ISBLK(info.st_mode)){ condlog(0, "Failed to get major:minor. fd = %d", fd); return MPATH_PR_FILE_ERROR; } major = major(info.st_rdev); minor = minor(info.st_rdev); condlog(4, "Device %d:%d: ", major, minor); /* get alias from major:minor*/ alias = dm_mapname(major, minor); if (!alias){ condlog(0, "%d:%d failed to get device alias.", major, minor); return MPATH_PR_DMMP_ERROR; } condlog(3, "alias = %s", alias); map_present = dm_map_present(alias); if (map_present && !dm_is_mpath(alias)){ condlog( 0, "%s: not a multipath device.", alias); ret = MPATH_PR_DMMP_ERROR; goto out; } /* * allocate core vectors to store paths and multipaths */ curmp = vector_alloc (); pathvec = vector_alloc (); if (!curmp || !pathvec){ condlog (0, "%s: vector allocation failed.", alias); ret = MPATH_PR_DMMP_ERROR; if (curmp) vector_free(curmp); if (pathvec) vector_free(pathvec); goto out; } if (path_discovery(pathvec, DI_SYSFS | DI_CHECKER) < 0) { ret = MPATH_PR_DMMP_ERROR; goto out1; } /* get info of all paths from the dm device */ if (get_mpvec (curmp, pathvec, alias)){ condlog(0, "%s: failed to get device info.", alias); ret = MPATH_PR_DMMP_ERROR; goto out1; } mpp = find_mp_by_alias(curmp, alias); if (!mpp){ condlog(0, "%s: devmap not registered.", alias); ret = MPATH_PR_DMMP_ERROR; goto out1; } ret = mpath_prin_activepath(mpp, rq_servact, resp, noisy); out1: free_multipathvec(curmp, KEEP_PATHS); free_pathvec(pathvec, FREE_PATHS); out: FREE(alias); return ret; } int mpath_persistent_reserve_out ( int fd, int rq_servact, int rq_scope, unsigned int rq_type, struct prout_param_descriptor *paramp, int noisy, int verbose) { struct stat info; vector curmp = NULL; vector pathvec = NULL; char * alias; struct multipath * mpp; int map_present; int major, minor; int ret; uint64_t prkey; struct config *conf; conf = get_multipath_config(); conf->verbosity = verbose; put_multipath_config(conf); if (fstat( fd, &info) != 0){ condlog(0, "stat error fd=%d", fd); return MPATH_PR_FILE_ERROR; } if(!S_ISBLK(info.st_mode)){ condlog(3, "Failed to get major:minor. fd=%d", fd); return MPATH_PR_FILE_ERROR; } major = major(info.st_rdev); minor = minor(info.st_rdev); condlog(4, "Device %d:%d", major, minor); /* get WWN of the device from major:minor*/ alias = dm_mapname(major, minor); if (!alias){ return MPATH_PR_DMMP_ERROR; } condlog(3, "alias = %s", alias); map_present = dm_map_present(alias); if (map_present && !dm_is_mpath(alias)){ condlog(3, "%s: not a multipath device.", alias); ret = MPATH_PR_DMMP_ERROR; goto out; } /* * allocate core vectors to store paths and multipaths */ curmp = vector_alloc (); pathvec = vector_alloc (); if (!curmp || !pathvec){ condlog (0, "%s: vector allocation failed.", alias); ret = MPATH_PR_DMMP_ERROR; if (curmp) vector_free(curmp); if (pathvec) vector_free(pathvec); goto out; } if (path_discovery(pathvec, DI_SYSFS | DI_CHECKER) < 0) { ret = MPATH_PR_DMMP_ERROR; goto out1; } /* get info of all paths from the dm device */ if (get_mpvec(curmp, pathvec, alias)){ condlog(0, "%s: failed to get device info.", alias); ret = MPATH_PR_DMMP_ERROR; goto out1; } mpp = find_mp_by_alias(curmp, alias); if (!mpp) { condlog(0, "%s: devmap not registered.", alias); ret = MPATH_PR_DMMP_ERROR; goto out1; } conf = get_multipath_config(); select_reservation_key(conf, mpp); put_multipath_config(conf); memcpy(&prkey, paramp->sa_key, 8); if (mpp->prkey_source == PRKEY_SOURCE_FILE && prkey && ((!get_be64(mpp->reservation_key) && rq_servact == MPATH_PROUT_REG_SA) || rq_servact == MPATH_PROUT_REG_IGN_SA)) { memcpy(&mpp->reservation_key, paramp->sa_key, 8); if (update_prkey(alias, get_be64(mpp->reservation_key))) { condlog(0, "%s: failed to set prkey for multipathd.", alias); ret = MPATH_PR_DMMP_ERROR; goto out1; } } if (memcmp(paramp->key, &mpp->reservation_key, 8) && memcmp(paramp->sa_key, &mpp->reservation_key, 8)) { condlog(0, "%s: configured reservation key doesn't match: 0x%" PRIx64, alias, get_be64(mpp->reservation_key)); ret = MPATH_PR_SYNTAX_ERROR; goto out1; } switch(rq_servact) { case MPATH_PROUT_REG_SA: case MPATH_PROUT_REG_IGN_SA: ret= mpath_prout_reg(mpp, rq_servact, rq_scope, rq_type, paramp, noisy); break; case MPATH_PROUT_RES_SA : case MPATH_PROUT_PREE_SA : case MPATH_PROUT_PREE_AB_SA : case MPATH_PROUT_CLEAR_SA: ret = mpath_prout_common(mpp, rq_servact, rq_scope, rq_type, paramp, noisy); break; case MPATH_PROUT_REL_SA: ret = mpath_prout_rel(mpp, rq_servact, rq_scope, rq_type, paramp, noisy); break; default: ret = MPATH_PR_OTHER; goto out1; } if ((ret == MPATH_PR_SUCCESS) && ((rq_servact == MPATH_PROUT_REG_SA) || (rq_servact == MPATH_PROUT_REG_IGN_SA))) { if (prkey == 0) { update_prflag(alias, 0); update_prkey(alias, 0); } else update_prflag(alias, 1); } else if ((ret == MPATH_PR_SUCCESS) && (rq_servact == MPATH_PROUT_CLEAR_SA)) { update_prflag(alias, 0); update_prkey(alias, 0); } out1: free_multipathvec(curmp, KEEP_PATHS); free_pathvec(pathvec, FREE_PATHS); out: FREE(alias); return ret; } int get_mpvec (vector curmp, vector pathvec, char * refwwid) { int i; struct multipath *mpp; char params[PARAMS_SIZE], status[PARAMS_SIZE]; if (dm_get_maps (curmp)){ return 1; } vector_foreach_slot (curmp, mpp, i){ /* * discard out of scope maps */ if (mpp->alias && refwwid && strncmp (mpp->alias, refwwid, WWID_SIZE - 1)){ free_multipath (mpp, KEEP_PATHS); vector_del_slot (curmp, i); i--; continue; } dm_get_map(mpp->alias, &mpp->size, params); condlog(3, "params = %s", params); dm_get_status(mpp->alias, status); condlog(3, "status = %s", status); disassemble_map (pathvec, params, mpp, 0); /* * disassemble_map() can add new paths to pathvec. * If not in "fast list mode", we need to fetch information * about them */ updatepaths(mpp); mpp->bestpg = select_path_group (mpp); disassemble_status (status, mpp); } return MPATH_PR_SUCCESS ; } int mpath_send_prin_activepath (char * dev, int rq_servact, struct prin_resp * resp, int noisy) { int rc; rc = prin_do_scsi_ioctl(dev, rq_servact, resp, noisy); return (rc); } int mpath_prout_reg(struct multipath *mpp,int rq_servact, int rq_scope, unsigned int rq_type, struct prout_param_descriptor * paramp, int noisy) { int i, j; struct pathgroup *pgp = NULL; struct path *pp = NULL; int rollback = 0; int active_pathcount=0; int rc; int count=0; int status = MPATH_PR_SUCCESS; uint64_t sa_key = 0; if (!mpp) return MPATH_PR_DMMP_ERROR; active_pathcount = pathcount(mpp, PATH_UP) + pathcount(mpp, PATH_GHOST); if (active_pathcount == 0) { condlog (0, "%s: no path available", mpp->wwid); return MPATH_PR_DMMP_ERROR; } if ( paramp->sa_flags & MPATH_F_ALL_TG_PT_MASK ) { condlog (1, "Warning: ALL_TG_PT is set. Configuration not supported"); } struct threadinfo thread[active_pathcount]; memset(thread, 0, sizeof(thread)); /* init thread parameter */ for (i =0; i< active_pathcount; i++){ thread[i].param.rq_servact = rq_servact; thread[i].param.rq_scope = rq_scope; thread[i].param.rq_type = rq_type; thread[i].param.paramp = paramp; thread[i].param.noisy = noisy; thread[i].param.status = MPATH_PR_SKIP; condlog (3, "THRED ID [%d] INFO]", i); condlog (3, "rq_servact=%d ", thread[i].param.rq_servact); condlog (3, "rq_scope=%d ", thread[i].param.rq_scope); condlog (3, "rq_type=%d ", thread[i].param.rq_type); condlog (3, "rkey="); condlog (3, "paramp->sa_flags =%02x ", thread[i].param.paramp->sa_flags); condlog (3, "noisy=%d ", thread[i].param.noisy); condlog (3, "status=%d ", thread[i].param.status); } pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); vector_foreach_slot (mpp->pg, pgp, j){ vector_foreach_slot (pgp->paths, pp, i){ if (!((pp->state == PATH_UP) || (pp->state == PATH_GHOST))){ condlog (1, "%s: %s path not up. Skip.", mpp->wwid, pp->dev); continue; } strncpy(thread[count].param.dev, pp->dev, FILE_NAME_SIZE - 1); if (count && (thread[count].param.paramp->sa_flags & MPATH_F_SPEC_I_PT_MASK)){ /* * Clearing SPEC_I_PT as transportids are already registered by now. */ thread[count].param.paramp->sa_flags &= (~MPATH_F_SPEC_I_PT_MASK); } condlog (3, "%s: sending pr out command to %s", mpp->wwid, pp->dev); rc = pthread_create(&thread[count].id, &attr, mpath_prout_pthread_fn, (void *)(&thread[count].param)); if (rc){ condlog (0, "%s: failed to create thread %d", mpp->wwid, rc); thread[count].param.status = MPATH_PR_THREAD_ERROR; } count = count + 1; } } for( i=0; i < active_pathcount ; i++){ if (thread[i].param.status != MPATH_PR_THREAD_ERROR) { rc = pthread_join(thread[i].id, NULL); if (rc){ condlog (0, "%s: Thread[%d] failed to join thread %d", mpp->wwid, i, rc); } } if (!rollback && (thread[i].param.status == MPATH_PR_RESERV_CONFLICT)){ rollback = 1; sa_key = 0; for (i = 0; i < 8; ++i){ if (i > 0) sa_key <<= 8; sa_key |= paramp->sa_key[i]; } status = MPATH_PR_RESERV_CONFLICT ; } if (!rollback && (status == MPATH_PR_SUCCESS)){ status = thread[i].param.status; } } if (rollback && ((rq_servact == MPATH_PROUT_REG_SA) && sa_key != 0 )){ condlog (3, "%s: ERROR: initiating pr out rollback", mpp->wwid); for( i=0 ; i < active_pathcount ; i++){ if(thread[i].param.status == MPATH_PR_SUCCESS) { memcpy(&thread[i].param.paramp->key, &thread[i].param.paramp->sa_key, 8); memset(&thread[i].param.paramp->sa_key, 0, 8); thread[i].param.status = MPATH_PR_SUCCESS; rc = pthread_create(&thread[i].id, &attr, mpath_prout_pthread_fn, (void *)(&thread[i].param)); if (rc){ condlog (0, "%s: failed to create thread for rollback. %d", mpp->wwid, rc); thread[i].param.status = MPATH_PR_THREAD_ERROR; } } else thread[i].param.status = MPATH_PR_SKIP; } for(i=0; i < active_pathcount ; i++){ if (thread[i].param.status != MPATH_PR_SKIP && thread[i].param.status != MPATH_PR_THREAD_ERROR) { rc = pthread_join(thread[i].id, NULL); if (rc){ condlog (3, "%s: failed to join thread while rolling back %d", mpp->wwid, i); } } } } pthread_attr_destroy(&attr); return (status); } void * mpath_prout_pthread_fn(void *p) { int ret; struct prout_param * param = (struct prout_param *)p; ret = prout_do_scsi_ioctl( param->dev,param->rq_servact, param->rq_scope, param->rq_type, param->paramp, param->noisy); param->status = ret; pthread_exit(NULL); } int mpath_prout_common(struct multipath *mpp,int rq_servact, int rq_scope, unsigned int rq_type, struct prout_param_descriptor* paramp, int noisy) { int i,j, ret; struct pathgroup *pgp = NULL; struct path *pp = NULL; vector_foreach_slot (mpp->pg, pgp, j){ vector_foreach_slot (pgp->paths, pp, i){ if (!((pp->state == PATH_UP) || (pp->state == PATH_GHOST))){ condlog (1, "%s: %s path not up. Skip", mpp->wwid, pp->dev); continue; } condlog (3, "%s: sending pr out command to %s", mpp->wwid, pp->dev); ret = send_prout_activepath(pp->dev, rq_servact, rq_scope, rq_type, paramp, noisy); return ret ; } } return MPATH_PR_SUCCESS; } int send_prout_activepath(char * dev, int rq_servact, int rq_scope, unsigned int rq_type, struct prout_param_descriptor * paramp, int noisy) { struct prout_param param; param.rq_servact = rq_servact; param.rq_scope = rq_scope; param.rq_type = rq_type; param.paramp = paramp; param.noisy = noisy; param.status = -1; pthread_t thread; pthread_attr_t attr; int rc; memset(&thread, 0, sizeof(thread)); strncpy(param.dev, dev, FILE_NAME_SIZE - 1); /* Initialize and set thread joinable attribute */ pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); rc = pthread_create(&thread, &attr, mpath_prout_pthread_fn, (void *)(¶m)); if (rc){ condlog (3, "%s: failed to create thread %d", dev, rc); return MPATH_PR_THREAD_ERROR; } /* Free attribute and wait for the other threads */ pthread_attr_destroy(&attr); rc = pthread_join(thread, NULL); return (param.status); } int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope, unsigned int rq_type, struct prout_param_descriptor * paramp, int noisy) { int i, j; int num = 0; struct pathgroup *pgp = NULL; struct path *pp = NULL; int active_pathcount = 0; pthread_attr_t attr; int rc, found = 0; int count = 0; int status = MPATH_PR_SUCCESS; struct prin_resp resp; struct prout_param_descriptor *pamp; struct prin_resp *pr_buff; int length; struct transportid *pptr; if (!mpp) return MPATH_PR_DMMP_ERROR; active_pathcount = pathcount (mpp, PATH_UP) + pathcount (mpp, PATH_GHOST); struct threadinfo thread[active_pathcount]; memset(thread, 0, sizeof(thread)); for (i = 0; i < active_pathcount; i++){ thread[i].param.rq_servact = rq_servact; thread[i].param.rq_scope = rq_scope; thread[i].param.rq_type = rq_type; thread[i].param.paramp = paramp; thread[i].param.noisy = noisy; thread[i].param.status = MPATH_PR_SKIP; condlog (3, " path count = %d", i); condlog (3, "rq_servact=%d ", thread[i].param.rq_servact); condlog (3, "rq_scope=%d ", thread[i].param.rq_scope); condlog (3, "rq_type=%d ", thread[i].param.rq_type); condlog (3, "noisy=%d ", thread[i].param.noisy); condlog (3, "status=%d ", thread[i].param.status); } pthread_attr_init (&attr); pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_JOINABLE); vector_foreach_slot (mpp->pg, pgp, j){ vector_foreach_slot (pgp->paths, pp, i){ if (!((pp->state == PATH_UP) || (pp->state == PATH_GHOST))){ condlog (1, "%s: %s path not up.", mpp->wwid, pp->dev); continue; } strncpy(thread[count].param.dev, pp->dev, FILE_NAME_SIZE - 1); condlog (3, "%s: sending pr out command to %s", mpp->wwid, pp->dev); rc = pthread_create (&thread[count].id, &attr, mpath_prout_pthread_fn, (void *) (&thread[count].param)); if (rc) { condlog (0, "%s: failed to create thread. %d", mpp->wwid, rc); thread[count].param.status = MPATH_PR_THREAD_ERROR; } count = count + 1; } } pthread_attr_destroy (&attr); for (i = 0; i < active_pathcount; i++){ if (thread[i].param.status != MPATH_PR_THREAD_ERROR) { rc = pthread_join (thread[i].id, NULL); if (rc){ condlog (1, "%s: failed to join thread. %d", mpp->wwid, rc); } } } for (i = 0; i < active_pathcount; i++){ /* check thread status here and return the status */ if (thread[i].param.status == MPATH_PR_RESERV_CONFLICT) status = MPATH_PR_RESERV_CONFLICT; else if (status == MPATH_PR_SUCCESS && thread[i].param.status != MPATH_PR_RESERV_CONFLICT) status = thread[i].param.status; } status = mpath_prin_activepath (mpp, MPATH_PRIN_RRES_SA, &resp, noisy); if (status != MPATH_PR_SUCCESS){ condlog (0, "%s: pr in read reservation command failed.", mpp->wwid); return MPATH_PR_OTHER; } num = resp.prin_descriptor.prin_readresv.additional_length / 8; if (num == 0){ condlog (2, "%s: Path holding reservation is released.", mpp->wwid); return MPATH_PR_SUCCESS; } condlog (2, "%s: Path holding reservation is not avialable.", mpp->wwid); pr_buff = mpath_alloc_prin_response(MPATH_PRIN_RFSTAT_SA); if (!pr_buff){ condlog (0, "%s: failed to alloc pr in response buffer.", mpp->wwid); return MPATH_PR_OTHER; } status = mpath_prin_activepath (mpp, MPATH_PRIN_RFSTAT_SA, pr_buff, noisy); if (status != MPATH_PR_SUCCESS){ condlog (0, "%s: pr in read full status command failed.", mpp->wwid); goto out; } num = pr_buff->prin_descriptor.prin_readfd.number_of_descriptor; if (0 == num){ goto out; } length = sizeof (struct prout_param_descriptor) + (sizeof (struct transportid *)); pamp = (struct prout_param_descriptor *)malloc (length); if (!pamp){ condlog (0, "%s: failed to alloc pr out parameter.", mpp->wwid); goto out1; } memset(pamp, 0, length); pamp->trnptid_list[0] = (struct transportid *) malloc (sizeof (struct transportid)); if (!pamp->trnptid_list[0]){ condlog (0, "%s: failed to alloc pr out transportid.", mpp->wwid); goto out1; } if (get_be64(mpp->reservation_key)){ memcpy (pamp->key, &mpp->reservation_key, 8); condlog (3, "%s: reservation key set.", mpp->wwid); } status = mpath_prout_common (mpp, MPATH_PROUT_CLEAR_SA, rq_scope, rq_type, pamp, noisy); if (status) { condlog(0, "%s: failed to send CLEAR_SA", mpp->wwid); goto out1; } pamp->num_transportid = 1; pptr=pamp->trnptid_list[0]; for (i = 0; i < num; i++){ if (get_be64(mpp->reservation_key) && memcmp(pr_buff->prin_descriptor.prin_readfd.descriptors[i]->key, &mpp->reservation_key, 8)){ /*register with tarnsport id*/ memset(pamp, 0, length); pamp->trnptid_list[0] = pptr; memset (pamp->trnptid_list[0], 0, sizeof (struct transportid)); memcpy (pamp->sa_key, pr_buff->prin_descriptor.prin_readfd.descriptors[i]->key, 8); pamp->sa_flags = MPATH_F_SPEC_I_PT_MASK; pamp->num_transportid = 1; memcpy (pamp->trnptid_list[0], &pr_buff->prin_descriptor.prin_readfd.descriptors[i]->trnptid, sizeof (struct transportid)); status = mpath_prout_common (mpp, MPATH_PROUT_REG_SA, 0, rq_type, pamp, noisy); pamp->sa_flags = 0; memcpy (pamp->key, pr_buff->prin_descriptor.prin_readfd.descriptors[i]->key, 8); memset (pamp->sa_key, 0, 8); pamp->num_transportid = 0; status = mpath_prout_common (mpp, MPATH_PROUT_REG_SA, 0, rq_type, pamp, noisy); } else { if (get_be64(mpp->reservation_key)) found = 1; } } if (found){ memset (pamp, 0, length); memcpy (pamp->sa_key, &mpp->reservation_key, 8); memset (pamp->key, 0, 8); status = mpath_prout_reg(mpp, MPATH_PROUT_REG_SA, rq_scope, rq_type, pamp, noisy); } free(pptr); out1: free (pamp); out: free (pr_buff); return (status); } void * mpath_alloc_prin_response(int prin_sa) { void * ptr = NULL; int size=0; switch (prin_sa) { case MPATH_PRIN_RKEY_SA: size = sizeof(struct prin_readdescr); break; case MPATH_PRIN_RRES_SA: size = sizeof(struct prin_resvdescr); break; case MPATH_PRIN_RCAP_SA: size=sizeof(struct prin_capdescr); break; case MPATH_PRIN_RFSTAT_SA: size = sizeof(struct print_fulldescr_list) + sizeof(struct prin_fulldescr *)*MPATH_MX_TIDS; break; } if (size > 0) { ptr = calloc(size, 1); } return ptr; } int update_map_pr(struct multipath *mpp) { int noisy=0; struct prin_resp *resp; int i, ret, isFound; if (!get_be64(mpp->reservation_key)) { /* Nothing to do. Assuming pr mgmt feature is disabled*/ condlog(3, "%s: reservation_key not set in multipath.conf", mpp->alias); return MPATH_PR_SUCCESS; } resp = mpath_alloc_prin_response(MPATH_PRIN_RKEY_SA); if (!resp) { condlog(0,"%s : failed to alloc resp in update_map_pr", mpp->alias); return MPATH_PR_OTHER; } ret = mpath_prin_activepath(mpp, MPATH_PRIN_RKEY_SA, resp, noisy); if (ret != MPATH_PR_SUCCESS ) { condlog(0,"%s : pr in read keys service action failed Error=%d", mpp->alias, ret); free(resp); return ret; } if (resp->prin_descriptor.prin_readkeys.additional_length == 0 ) { condlog(0,"%s: No key found. Device may not be registered. ", mpp->alias); free(resp); return MPATH_PR_SUCCESS; } condlog(2, "%s: Multipath reservation_key: 0x%" PRIx64 " ", mpp->alias, get_be64(mpp->reservation_key)); isFound =0; for (i = 0; i < resp->prin_descriptor.prin_readkeys.additional_length/8; i++ ) { condlog(2, "%s: PR IN READKEYS[%d] reservation key:", mpp->alias, i); dumpHex((char *)&resp->prin_descriptor.prin_readkeys.key_list[i*8], 8 , 1); if (!memcmp(&mpp->reservation_key, &resp->prin_descriptor.prin_readkeys.key_list[i*8], 8)) { condlog(2, "%s: reservation key found in pr in readkeys response", mpp->alias); isFound =1; } } if (isFound) { mpp->prflag = 1; condlog(2, "%s: prflag flag set.", mpp->alias ); } free(resp); return MPATH_PR_SUCCESS; } multipath-tools-0.7.4/libmpathpersist/mpath_persist.h000066400000000000000000000203441320314174000231320ustar00rootroot00000000000000/* version - 1.0 */ #ifndef MPATH_PERSIST_LIB_H #define MPATH_PERSIST_LIB_H #ifdef __cplusplus extern "C" { #endif #include #define MPATH_MAX_PARAM_LEN 8192 #define MPATH_MX_TIDS 32 /* Max number of transport ids"*/ #define MPATH_MX_TID_LEN 256 /* Max length of transport id */ /* PRIN Service Actions */ #define MPATH_PRIN_RKEY_SA 0x00 /* READ KEYS SA*/ #define MPATH_PRIN_RRES_SA 0x01 /* READ RESERVATION SA*/ #define MPATH_PRIN_RCAP_SA 0x02 /* REPORT CAPABILITIES SA*/ #define MPATH_PRIN_RFSTAT_SA 0x03 /* READ FULL STATUS SA*/ /* PROUT Service Actions */ #define MPATH_PROUT_REG_SA 0x00 /* REGISTER SA */ #define MPATH_PROUT_RES_SA 0x01 /* RESERVE SA*/ #define MPATH_PROUT_REL_SA 0x02 /* RELEASE SA*/ #define MPATH_PROUT_CLEAR_SA 0x03 /* CLEAR SA*/ #define MPATH_PROUT_PREE_SA 0x04 /* PREEMPT SA*/ #define MPATH_PROUT_PREE_AB_SA 0x05 /* PREEMPT AND ABORT SA*/ #define MPATH_PROUT_REG_IGN_SA 0x06 /* REGISTER AND IGNORE EXISTING KEY SA*/ #define MPATH_PROUT_REG_MOV_SA 0x07 /* REGISTER AND MOVE SA*/ #define MPATH_LU_SCOPE 0x00 /* LU_SCOPE */ /* Persistent reservations type */ #define MPATH_PRTPE_WE 0x01 /* Write Exclusive */ #define MPATH_PRTPE_EA 0x03 /* Exclusive Access*/ #define MPATH_PRTPE_WE_RO 0x05 /* WriteExclusive Registrants Only */ #define MPATH_PRTPE_EA_RO 0x06 /* Exclusive Access. Registrants Only*/ #define MPATH_PRTPE_WE_AR 0x07 /* Write Exclusive. All Registrants*/ #define MPATH_PRTPE_EA_AR 0x08 /* Exclusive Access. All Registrants */ /* PR RETURN_STATUS */ #define MPATH_PR_SKIP -1 /* skipping this path */ #define MPATH_PR_SUCCESS 0 #define MPATH_PR_SYNTAX_ERROR 1 /* syntax error or invalid parameter */ /* status for check condition */ #define MPATH_PR_SENSE_NOT_READY 2 /* [sk,asc,ascq: 0x2,*,*] */ #define MPATH_PR_SENSE_MEDIUM_ERROR 3 /* [sk,asc,ascq: 0x3,*,*] */ #define MPATH_PR_SENSE_HARDWARE_ERROR 4 /* [sk,asc,ascq: 0x4,*,*] */ #define MPATH_PR_ILLEGAL_REQ 5 /* [sk,asc,ascq: 0x5,*,*]*/ #define MPATH_PR_SENSE_UNIT_ATTENTION 6 /* [sk,asc,ascq: 0x6,*,*] */ #define MPATH_PR_SENSE_INVALID_OP 7 /* [sk,asc,ascq: 0x5,0x20,0x0]*/ #define MPATH_PR_SENSE_ABORTED_COMMAND 8 /* [sk,asc,ascq: 0xb,*,*] */ #define MPATH_PR_NO_SENSE 9 /* [sk,asc,ascq: 0x0,*,*] */ #define MPATH_PR_SENSE_MALFORMED 10 /* Response to SCSI command malformed */ #define MPATH_PR_RESERV_CONFLICT 11 /* Reservation conflict on the device */ #define MPATH_PR_FILE_ERROR 12 /* file (device node) problems(e.g. not found)*/ #define MPATH_PR_DMMP_ERROR 13 /* DMMP related error.(e.g Error in getting dm info */ #define MPATH_PR_THREAD_ERROR 14 /* pthreads error (e.g. unable to create new thread) */ #define MPATH_PR_OTHER 15 /*other error/warning has occurred(transport or driver error) */ /* PR MASK */ #define MPATH_F_APTPL_MASK 0x01 /* APTPL MASK*/ #define MPATH_F_ALL_TG_PT_MASK 0x04 /* ALL_TG_PT MASK*/ #define MPATH_F_SPEC_I_PT_MASK 0x08 /* SPEC_I_PT MASK*/ #define MPATH_PR_TYPE_MASK 0x0f /* TYPE MASK*/ #define MPATH_PR_SCOPE_MASK 0xf0 /* SCOPE MASK*/ /*Transport ID PROTOCOL IDENTIFIER values */ #define MPATH_PROTOCOL_ID_FC 0x00 #define MPATH_PROTOCOL_ID_ISCSI 0x05 #define MPATH_PROTOCOL_ID_SAS 0x06 /*Transport ID FORMATE CODE */ #define MPATH_WWUI_DEVICE_NAME 0x00 /* World wide unique initiator device name */ #define MPATH_WWUI_PORT_IDENTIFIER 0x40 /* World wide unique initiator port identifier */ extern unsigned int mpath_mx_alloc_len; struct prin_readdescr { uint32_t prgeneration; uint32_t additional_length; /* The value should be either 0 or divisible by 8. 0 indicates no registered reservation key. */ uint8_t key_list[MPATH_MAX_PARAM_LEN]; }; struct prin_resvdescr { uint32_t prgeneration; uint32_t additional_length; /* The value should be either 0 or 10h. 0 indicates there is no reservation held. 10h indicates the key[8] and scope_type have valid values */ uint8_t key[8]; uint32_t _obsolete; uint8_t _reserved; uint8_t scope_type; /* Use PR SCOPE AND TYPE MASK specified above */ uint16_t _obsolete1; }; struct prin_capdescr { uint16_t length; uint8_t flags[2]; uint16_t pr_type_mask; uint16_t _reserved; }; struct transportid { uint8_t format_code; uint8_t protocol_id; union { uint8_t n_port_name[8]; /* FC transport*/ uint8_t sas_address[8]; /* SAS transport */ uint8_t iscsi_name[256]; /* ISCSI transport */ }; }; struct prin_fulldescr { uint8_t key[8]; uint8_t flag; /* All_tg_pt and reservation holder */ uint8_t scope_type; /* Use PR SCOPE AND TYPE MASK specified above. Meaningful only for reservation holder */ uint16_t rtpi; struct transportid trnptid; }; struct print_fulldescr_list { uint32_t prgeneration; uint32_t number_of_descriptor; uint8_t private_buffer[MPATH_MAX_PARAM_LEN]; /*Private buffer for list storage*/ struct prin_fulldescr *descriptors[]; }; struct prin_resp { union { struct prin_readdescr prin_readkeys; /* for PRIN read keys SA*/ struct prin_resvdescr prin_readresv; /* for PRIN read reservation SA*/ struct prin_capdescr prin_readcap; /* for PRIN Report Capabilities SA*/ struct print_fulldescr_list prin_readfd; /* for PRIN read full status SA*/ }prin_descriptor; }; struct prout_param_descriptor { /* PROUT parameter descriptor */ uint8_t key[8]; uint8_t sa_key[8]; uint32_t _obsolete; uint8_t sa_flags; uint8_t _reserved; uint16_t _obsolete1; uint8_t private_buffer[MPATH_MAX_PARAM_LEN]; /*private buffer for list storage*/ uint32_t num_transportid; /* Number of Transport ID listed in trnptid_list[]*/ struct transportid *trnptid_list[]; }; /* Function declarations */ /* * DESCRIPTION : * Initialize device mapper multipath configuration. This function must be invoked first * before performing reservation management functions. * RESTRICTIONS: * * RETURNS: struct config ->Success, NULL->Failed. */ extern struct config * mpath_lib_init (void); /* * DESCRIPTION : * Release device mapper multipath configuration. This function must be invoked after * performing reservation management functions. * RESTRICTIONS: * * RETURNS: 0->Success, 1->Failed. */ extern int mpath_lib_exit (struct config *conf); /* * DESCRIPTION : * This function sends PRIN command to the DM device and get the response. * * @fd: The file descriptor of a multipath device. Input argument. * @rq_servact: PRIN command service action. Input argument * @resp: The response from PRIN service action. The resp is a struct specified above. The caller should * manage the memory allocation of this struct * @noisy: Turn on debugging trace: Input argument. 0->Disable, 1->Enable * @verbose: Set verbosity level. Input argument. value:[0-3]. 0->disabled, 3->Max verbose * * RESTRICTIONS: * * RETURNS: MPATH_PR_SUCCESS if PR command successful else returns any of the status specified * above in RETURN_STATUS. * */ extern int mpath_persistent_reserve_in (int fd, int rq_servact, struct prin_resp *resp, int noisy, int verbose); /* * DESCRIPTION : * This function sends PROUT command to the DM device and get the response. * * @fd: The file descriptor of a multipath device. Input argument. * @rq_servact: PROUT command service action. Input argument * @rq_scope: Persistent reservation scope. The value should be always LU_SCOPE (0h). * @rq_type: Persistent reservation type. The valid values of persistent reservation types are * 5h (Write exclusive - registrants only) * 6h (Exclusive access - registrants only) * 7h (Write exclusive - All registrants) * 8h (Exclusive access - All registrants). * @paramp: PROUT command parameter data. The paramp is a struct which describes PROUT * parameter list. The caller should manage the memory allocation of this struct. * @noisy: Turn on debugging trace: Input argument.0->Disable, 1->Enable. * @verbose: Set verbosity level. Input argument. value:0 to 3. 0->disabled, 3->Max verbose * * RESTRICTIONS: * * RETURNS: MPATH_PR_SUCCESS if PR command successful else returns any of the status specified * above in RETURN_STATUS. */ extern int mpath_persistent_reserve_out ( int fd, int rq_servact, int rq_scope, unsigned int rq_type, struct prout_param_descriptor *paramp, int noisy, int verbose); #ifdef __cplusplus } #endif #endif /*MPATH_PERSIST_LIB_H*/ multipath-tools-0.7.4/libmpathpersist/mpath_persistent_reserve_in.3000066400000000000000000000066571320314174000260100ustar00rootroot00000000000000.\" ---------------------------------------------------------------------------- .\" Update the date below if you make any significant change. .\" Make sure there are no errors with: .\" groff -z -wall -b -e -t libmpathpersist/mpath_persistent_reserve_in.3 .\" .\" ---------------------------------------------------------------------------- . .TH MPATH_PERSISTENT_RESERVE_IN 3 2016-11-01 "Linux" . . .\" ---------------------------------------------------------------------------- .SH NAME .\" ---------------------------------------------------------------------------- . mpath_persistent_reserve_in . . .\" ---------------------------------------------------------------------------- .SH SYNOPSIS .\" ---------------------------------------------------------------------------- . .B #include .P .BI "int mpath_persistent_reserve_in" "(int fd, int rq_servact, struct prin_resp *resp, int noisy, int verbose)" .P . . .\" ---------------------------------------------------------------------------- .SH DESCRIPTION .\" ---------------------------------------------------------------------------- . The function in the \fBmpath_persistent_reserve_in ()\fR sends PRIN command to the DM device and gets the response. .TP .B Parameters: .RS .TP 12 .I fd The file descriptor of a multipath device. Input argument. .TP .I rq_servact PRIN command service action. Input argument. .TP .I resp The response from PRIN service action. The caller should manage the memory allocation of this structure. .TP .I noisy Turn on debugging trace: Input argument. 0->Disable, 1->Enable. .TP .I verbose Set verbosity level. Input argument. value:[0-3]. 0->Crits and Errors, 1->Warnings, 2->Info, 3->Debug. .RE . . .\" ---------------------------------------------------------------------------- .SH RETURNS .\" ---------------------------------------------------------------------------- . .TP 12 .B MPATH_PR_SUCCESS If PR command successful. .TP .B MPATH_PR_SYNTAX_ERROR If syntax error or invalid parameter. .TP .B MPATH_PR_SENSE_NOT_READY If command fails with [sk,asc,ascq: 0x2,*,*]. .TP .B MPATH_PR_SENSE_MEDIUM_ERROR If command fails with [sk,asc,ascq: 0x3,*,*]. .TP .B MPATH_PR_SENSE_HARDWARE_ERROR If command fails with [sk,asc,ascq: 0x4,*,*]. .TP .B MPATH_PR_SENSE_INVALID_OP If command fails with [sk,asc,ascq: 0x5,0x20,0x0]. .TP .B MPATH_PR_ILLEGAL_REQ If command fails with [sk,asc,ascq: 0x5,*,*]. .TP .B MPATH_PR_SENSE_UNIT_ATTENTION If command fails with [sk,asc,ascq: 0x6,*,*]. .TP .B MPATH_PR_SENSE_ABORTED_COMMAND If command fails with [sk,asc,ascq: 0xb,*,*]. .TP .B MPATH_PR_NO_SENSE If command fails with [sk,asc,ascq: 0x0,*,*]. .TP .B MPATH_PR_SENSE_MALFORMED If command fails with SCSI command malformed. .TP .B MPATH_PR_FILE_ERROR If command fails while accessing file (device node) problems(e.g. not found). .TP .B MPATH_PR_DMMP_ERROR If Device Mapper related error.(e.g Error in getting dm info). .TP .B MPATH_PR_OTHER If other error/warning has occurred(e.g transport or driver error). . . .\" ---------------------------------------------------------------------------- .SH "SEE ALSO" .\" ---------------------------------------------------------------------------- . .BR mpathpersist (8). . . .\" ---------------------------------------------------------------------------- .SH AUTHORS .\" ---------------------------------------------------------------------------- . \fImultipath-tools\fR was developed by Christophe Varoqui and others. .\" EOF multipath-tools-0.7.4/libmpathpersist/mpath_persistent_reserve_out.3000066400000000000000000000077371320314174000262110ustar00rootroot00000000000000.\" ---------------------------------------------------------------------------- .\" Update the date below if you make any significant change. .\" Make sure there are no errors with: .\" groff -z -wall -b -e -t libmpathpersist/mpath_persistent_reserve_out.3 .\" .\" ---------------------------------------------------------------------------- . .TH MPATH_PERSISTENT_RESERVE_OUT 3 2016-11-01 "Linux" . . .\" ---------------------------------------------------------------------------- .SH NAME .\" ---------------------------------------------------------------------------- . mpath_persistent_reserve_out . . .\" ---------------------------------------------------------------------------- .SH SYNOPSIS .\" ---------------------------------------------------------------------------- . .B #include .P .BI "int mpath_persistent_reserve_out" "(int fd, int rq_servact, struct prin_resp *resp, int noisy, int verbose)" .P . . .\" ---------------------------------------------------------------------------- .SH DESCRIPTION .\" ---------------------------------------------------------------------------- . The function in the \fBmpath_persistent_reserve_out ()\fR sends PROUT command to the DM device and gets the response. .TP .B Parameters: .RS .TP 12 .I fd The file descriptor of a multipath device. Input argument. .TP .I rq_servact PROUT command service action. Input argument. .TP .I rq_scope Persistent reservation scope. The value should be always LU_SCOPE (0h). .TP .I rq_type Persistent reservation type. The valid values of persistent reservation types are: .RS .IP 5h (Write exclusive - registrants only). .IP 6h (Exclusive access - registrants only). .IP 7h (Write exclusive - All registrants). .IP 8h (Exclusive access - All registrants). .RE .TP .I paramp PROUT command parameter data. The paramp is a struct which describes PROUT parameter list. Caller should manage the memory allocation of this structure. .TP .I noisy Turn on debugging trace: Input argument. 0->Disable, 1->Enable. .TP .I verbose Set verbosity level. Input argument. value: 0 to 3. 0->Crits and Errors, 1->Warnings, 2->Info, 3->Debug. .RE . . .\" ---------------------------------------------------------------------------- .SH RETURNS .\" ---------------------------------------------------------------------------- . .TP 12 .B MPATH_PR_SUCCESS If PR command successful else returns any one of the status mentioned below. .TP .B MPATH_PR_SYNTAX_ERROR If syntax error or invalid parameter. .TP .B MPATH_PR_SENSE_NOT_READY If command fails with [sk,asc,ascq: 0x2,*,*]. .TP .B MPATH_PR_SENSE_MEDIUM_ERROR If command fails with [sk,asc,ascq: 0x3,*,*]. .TP .B MPATH_PR_SENSE_HARDWARE_ERROR If command fails with [sk,asc,ascq: 0x4,*,*]. .TP .B MPATH_PR_SENSE_INVALID_OP If command fails with [sk,asc,ascq: 0x5,0x20,0x0]. .TP .B MPATH_PR_ILLEGAL_REQ If command fails with [sk,asc,ascq: 0x5,*,*]. .TP .B MPATH_PR_SENSE_UNIT_ATTENTION If command fails with [sk,asc,ascq: 0x6,*,*]. .TP .B MPATH_PR_SENSE_ABORTED_COMMAND If command fails with [sk,asc,ascq: 0xb,*,*]. .TP .B MPATH_PR_NO_SENSE If command fails with [sk,asc,ascq: 0x0,*,*]. .TP .B MPATH_PR_SENSE_MALFORMED If command fails with SCSI command malformed. .TP .B MPATH_PR_FILE_ERROR If command fails while accessing file (device node) problems(e.g. not found). .TP .B MPATH_PR_DMMP_ERROR If Device Mapper related error.(e.g Error in getting dm info). .TP .B MPATH_PR_OTHER If other error/warning has occurred(e.g transport or driver error). .TP .B MPATH_PR_RESERV_CONFLICT If command fails with reservation conflict. . . .\" ---------------------------------------------------------------------------- .SH "SEE ALSO" .\" ---------------------------------------------------------------------------- . .BR mpathpersist (8). . . .\" ---------------------------------------------------------------------------- .SH AUTHORS .\" ---------------------------------------------------------------------------- . \fImultipath-tools\fR was developed by Christophe Varoqui and others. .\" EOF multipath-tools-0.7.4/libmpathpersist/mpath_pr_ioctl.c000066400000000000000000000345551320314174000232600ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include "mpath_pr_ioctl.h" #include "mpath_persist.h" #include "debug.h" #define FILE_NAME_SIZE 256 #define TIMEOUT 2000 #define MAXRETRY 5 int prin_do_scsi_ioctl(char * dev, int rq_servact, struct prin_resp *resp, int noisy); void mpath_format_readkeys(struct prin_resp *pr_buff, int len , int noisy); void mpath_format_readfullstatus(struct prin_resp *pr_buff, int len, int noisy); int mpath_translate_response (char * dev, struct sg_io_hdr io_hdr, SenseData_t *Sensedata, int noisy); void dumpHex(const char* str, int len, int no_ascii); int prout_do_scsi_ioctl( char * dev, int rq_servact, int rq_scope, unsigned int rq_type, struct prout_param_descriptor *paramp, int noisy); uint32_t format_transportids(struct prout_param_descriptor *paramp); void mpath_reverse_uint32_byteorder(uint32_t *num); void mpath_reverse_uint16_byteorder(uint16_t *num); void decode_transport_id(struct prin_fulldescr *fdesc, unsigned char * p, int length); int get_prin_length(int rq_servact); int mpath_isLittleEndian(void); unsigned int mpath_mx_alloc_len; int prout_do_scsi_ioctl(char * dev, int rq_servact, int rq_scope, unsigned int rq_type, struct prout_param_descriptor *paramp, int noisy) { int status, paramlen = 24, ret = 0; uint32_t translen=0; int retry = MAXRETRY; SenseData_t Sensedata; struct sg_io_hdr io_hdr; char devname[FILE_NAME_SIZE]; int fd = -1; snprintf(devname, FILE_NAME_SIZE, "/dev/%s",dev); fd = open(devname, O_WRONLY); if(fd < 0){ condlog (1, "%s: unable to open device.", dev); return MPATH_PR_FILE_ERROR; } unsigned char cdb[MPATH_PROUT_CMDLEN] = {MPATH_PROUT_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0}; if (paramp->sa_flags & MPATH_F_SPEC_I_PT_MASK) { translen = format_transportids(paramp); paramlen = 24 + translen; } else paramlen = 24; if ( rq_servact > 0) cdb[1] = (unsigned char)(rq_servact & 0x1f); cdb[2] = (((rq_scope & 0xf) << 4) | (rq_type & 0xf)); cdb[7] = (unsigned char)((paramlen >> 8) & 0xff); cdb[8] = (unsigned char)(paramlen & 0xff); retry : condlog(3, "%s: rq_servact = %d", dev, rq_servact); condlog(3, "%s: rq_scope = %d ", dev, rq_scope); condlog(3, "%s: rq_type = %d ", dev, rq_type); condlog(3, "%s: paramlen = %d", dev, paramlen); if (noisy) { condlog(3, "%s: Persistent Reservation OUT parameter:", dev); dumpHex((const char *)paramp, paramlen,1); } memset(&Sensedata, 0, sizeof(SenseData_t)); memset(&io_hdr,0 , sizeof( struct sg_io_hdr)); io_hdr.interface_id = 'S'; io_hdr.cmd_len = MPATH_PROUT_CMDLEN; io_hdr.cmdp = cdb; io_hdr.sbp = (void *)&Sensedata; io_hdr.mx_sb_len = sizeof (SenseData_t); io_hdr.timeout = TIMEOUT; if (paramlen > 0) { io_hdr.dxferp = (void *)paramp; io_hdr.dxfer_len = paramlen; io_hdr.dxfer_direction = SG_DXFER_TO_DEV ; } else { io_hdr.dxfer_direction = SG_DXFER_NONE; } ret = ioctl(fd, SG_IO, &io_hdr); if (ret < 0) { condlog(0, "%s: ioctl failed %d", dev, ret); close(fd); return ret; } condlog(2, "%s: Duration=%u (ms)", dev, io_hdr.duration); status = mpath_translate_response(dev, io_hdr, &Sensedata, noisy); condlog(3, "%s: status = %d", dev, status); if (status == MPATH_PR_SENSE_UNIT_ATTENTION && (retry > 0)) { --retry; condlog(2, "%s: retrying for Unit Attention. Remaining retries = %d", dev, retry); goto retry; } if (((status == MPATH_PR_SENSE_NOT_READY )&& (Sensedata.ASC == 0x04)&& (Sensedata.ASCQ == 0x07))&& (retry > 0)) { usleep(1000); --retry; condlog(2, "%s: retrying for sense 02/04/07." " Remaining retries = %d", dev, retry); goto retry; } close(fd); return status; } uint32_t format_transportids(struct prout_param_descriptor *paramp) { int i = 0, len; uint32_t buff_offset = 4; memset(paramp->private_buffer, 0, MPATH_MAX_PARAM_LEN); for (i=0; i < paramp->num_transportid; i++ ) { paramp->private_buffer[buff_offset] = (uint8_t)((paramp->trnptid_list[i]->format_code & 0xff)| (paramp->trnptid_list[i]->protocol_id & 0xff)); buff_offset += 1; switch(paramp->trnptid_list[i]->protocol_id) { case MPATH_PROTOCOL_ID_FC: buff_offset += 7; memcpy(¶mp->private_buffer[buff_offset], ¶mp->trnptid_list[i]->n_port_name, 8); buff_offset +=8 ; buff_offset +=8 ; break; case MPATH_PROTOCOL_ID_SAS: buff_offset += 3; memcpy(¶mp->private_buffer[buff_offset], ¶mp->trnptid_list[i]->sas_address, 8); buff_offset += 12; break; case MPATH_PROTOCOL_ID_ISCSI: buff_offset += 1; len = (paramp->trnptid_list[i]->iscsi_name[1] & 0xff)+2; memcpy(¶mp->private_buffer[buff_offset], ¶mp->trnptid_list[i]->iscsi_name,len); buff_offset += len ; break; } } buff_offset -= 4; paramp->private_buffer[0] = (unsigned char)((buff_offset >> 24) & 0xff); paramp->private_buffer[1] = (unsigned char)((buff_offset >> 16) & 0xff); paramp->private_buffer[2] = (unsigned char)((buff_offset >> 8) & 0xff); paramp->private_buffer[3] = (unsigned char)(buff_offset & 0xff); buff_offset += 4; return buff_offset; } void mpath_format_readkeys( struct prin_resp *pr_buff, int len, int noisy) { mpath_reverse_uint32_byteorder(&pr_buff->prin_descriptor.prin_readkeys.prgeneration); mpath_reverse_uint32_byteorder(&pr_buff->prin_descriptor.prin_readkeys.additional_length); } void mpath_format_readresv(struct prin_resp *pr_buff, int len, int noisy) { mpath_reverse_uint32_byteorder(&pr_buff->prin_descriptor.prin_readkeys.prgeneration); mpath_reverse_uint32_byteorder(&pr_buff->prin_descriptor.prin_readkeys.additional_length); return; } void mpath_format_reportcapabilities(struct prin_resp *pr_buff, int len, int noisy) { mpath_reverse_uint16_byteorder(&pr_buff->prin_descriptor.prin_readcap.length); mpath_reverse_uint16_byteorder(&pr_buff->prin_descriptor.prin_readcap.pr_type_mask); return; } void mpath_format_readfullstatus(struct prin_resp *pr_buff, int len, int noisy) { int num, k, tid_len_len=0; uint32_t fdesc_count=0; unsigned char *p; char *ppbuff; uint32_t additional_length; mpath_reverse_uint32_byteorder(&pr_buff->prin_descriptor.prin_readfd.prgeneration); mpath_reverse_uint32_byteorder(&pr_buff->prin_descriptor.prin_readfd.number_of_descriptor); if (0 == pr_buff->prin_descriptor.prin_readfd.number_of_descriptor) { return ; } if (pr_buff->prin_descriptor.prin_readfd.number_of_descriptor == 0) { condlog(2, "No registration or resrvation found."); return; } additional_length = pr_buff->prin_descriptor.prin_readfd.number_of_descriptor; char tempbuff[MPATH_MAX_PARAM_LEN]; struct prin_fulldescr fdesc; memset(&fdesc, 0, sizeof(struct prin_fulldescr)); memcpy( tempbuff, pr_buff->prin_descriptor.prin_readfd.private_buffer,MPATH_MAX_PARAM_LEN ); memset(&pr_buff->prin_descriptor.prin_readfd.private_buffer, 0, MPATH_MAX_PARAM_LEN); p =(unsigned char *)tempbuff; ppbuff = (char *)pr_buff->prin_descriptor.prin_readfd.private_buffer; for (k = 0; k < additional_length; k += num, p += num) { memcpy(&fdesc.key, p, 8 ); fdesc.flag = p[12]; fdesc.scope_type = p[13]; fdesc.rtpi = ((p[18] << 8) | p[19]); tid_len_len = ((p[20] << 24) | (p[21] << 16) | (p[22] << 8) | p[23]); if (tid_len_len > 0) decode_transport_id( &fdesc, &p[24], tid_len_len); num = 24 + tid_len_len; memcpy(ppbuff, &fdesc, sizeof(struct prin_fulldescr)); pr_buff->prin_descriptor.prin_readfd.descriptors[fdesc_count]= (struct prin_fulldescr *)ppbuff; ppbuff += sizeof(struct prin_fulldescr); ++fdesc_count; } pr_buff->prin_descriptor.prin_readfd.number_of_descriptor = fdesc_count; return; } void decode_transport_id(struct prin_fulldescr *fdesc, unsigned char * p, int length) { int num, k; int jump; for (k = 0, jump = 24; k < length; k += jump, p += jump) { fdesc->trnptid.format_code = ((p[0] >> 6) & 0x3); fdesc->trnptid.protocol_id = (p[0] & 0xf); switch (fdesc->trnptid.protocol_id) { case MPATH_PROTOCOL_ID_FC: memcpy(&fdesc->trnptid.n_port_name, &p[8], 8); jump = 24; break; case MPATH_PROTOCOL_ID_ISCSI: num = ((p[2] << 8) | p[3]); memcpy(&fdesc->trnptid.iscsi_name, &p[4], num); jump = (((num + 4) < 24) ? 24 : num + 4); break; case MPATH_PROTOCOL_ID_SAS: memcpy(&fdesc->trnptid.sas_address, &p[4], 8); jump = 24; break; default: jump = 24; break; } } } int prin_do_scsi_ioctl(char * dev, int rq_servact, struct prin_resp * resp, int noisy) { int ret, status, got, fd; int mx_resp_len; SenseData_t Sensedata; int retry = MAXRETRY; struct sg_io_hdr io_hdr; char devname[FILE_NAME_SIZE]; unsigned char cdb[MPATH_PRIN_CMDLEN] = {MPATH_PRIN_CMD, 0, 0, 0, 0, 0, 0, 0, 0, 0}; snprintf(devname, FILE_NAME_SIZE, "/dev/%s",dev); fd = open(devname, O_WRONLY); if(fd < 0){ condlog(0, "%s: Unable to open device ", dev); return MPATH_PR_FILE_ERROR; } if (mpath_mx_alloc_len) mx_resp_len = mpath_mx_alloc_len; else mx_resp_len = get_prin_length(rq_servact); if (mx_resp_len == 0) { status = MPATH_PR_SYNTAX_ERROR; goto out; } cdb[1] = (unsigned char)(rq_servact & 0x1f); cdb[7] = (unsigned char)((mx_resp_len >> 8) & 0xff); cdb[8] = (unsigned char)(mx_resp_len & 0xff); retry : memset(&Sensedata, 0, sizeof(SenseData_t)); memset(&io_hdr,0 , sizeof( struct sg_io_hdr)); io_hdr.interface_id = 'S'; io_hdr.cmd_len = MPATH_PRIN_CMDLEN; io_hdr.mx_sb_len = sizeof (SenseData_t); io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; io_hdr.cmdp = cdb; io_hdr.sbp = (void *)&Sensedata; io_hdr.timeout = TIMEOUT; io_hdr.dxfer_len = mx_resp_len; io_hdr.dxferp = (void *)resp; ret =ioctl(fd, SG_IO, &io_hdr); if (ret < 0){ condlog(0, "%s: IOCTL failed %d", dev, ret); status = MPATH_PR_OTHER; goto out; } got = mx_resp_len - io_hdr.resid; condlog(2, "%s: duration = %u (ms)", dev, io_hdr.duration); condlog(2, "%s: persistent reservation in: requested %d bytes but got %d bytes)", dev, mx_resp_len, got); status = mpath_translate_response(dev, io_hdr, &Sensedata, noisy); if (status == MPATH_PR_SENSE_UNIT_ATTENTION && (retry > 0)) { --retry; condlog(2, "%s: retrying for Unit Attention. Remaining retries = %d", dev, retry); goto retry; } if (((status == MPATH_PR_SENSE_NOT_READY )&& (Sensedata.ASC == 0x04)&& (Sensedata.ASCQ == 0x07))&& (retry > 0)) { usleep(1000); --retry; condlog(2, "%s: retrying for 02/04/07. Remaining retries = %d", dev, retry); goto retry; } if (status != MPATH_PR_SUCCESS) goto out; if (noisy) dumpHex((const char *)resp, got , 1); switch (rq_servact) { case MPATH_PRIN_RKEY_SA : mpath_format_readkeys(resp, got, noisy); break; case MPATH_PRIN_RRES_SA : mpath_format_readresv(resp, got, noisy); break; case MPATH_PRIN_RCAP_SA : mpath_format_reportcapabilities(resp, got, noisy); break; case MPATH_PRIN_RFSTAT_SA : mpath_format_readfullstatus(resp, got, noisy); } out: close(fd); return status; } int mpath_translate_response (char * dev, struct sg_io_hdr io_hdr, SenseData_t *Sensedata, int noisy) { condlog(3, "%s: status driver:%02x host:%02x scsi:%02x", dev, io_hdr.driver_status, io_hdr.host_status ,io_hdr.status); io_hdr.status &= 0x7e; if ((0 == io_hdr.status) && (0 == io_hdr.host_status) && (0 == io_hdr.driver_status)) return MPATH_PR_SUCCESS; switch(io_hdr.status) { case SAM_STAT_GOOD: break; case SAM_STAT_CHECK_CONDITION: condlog(2, "%s: Sense_Key=%02x, ASC=%02x ASCQ=%02x", dev, Sensedata->Sense_Key, Sensedata->ASC, Sensedata->ASCQ); switch(Sensedata->Sense_Key) { case NO_SENSE: return MPATH_PR_NO_SENSE; case RECOVERED_ERROR: return MPATH_PR_SUCCESS; case NOT_READY: return MPATH_PR_SENSE_NOT_READY; case MEDIUM_ERROR: return MPATH_PR_SENSE_MEDIUM_ERROR; case BLANK_CHECK: return MPATH_PR_OTHER; case HARDWARE_ERROR: return MPATH_PR_SENSE_HARDWARE_ERROR; case ILLEGAL_REQUEST: return MPATH_PR_ILLEGAL_REQ; case UNIT_ATTENTION: return MPATH_PR_SENSE_UNIT_ATTENTION; case DATA_PROTECT: case COPY_ABORTED: return MPATH_PR_OTHER; case ABORTED_COMMAND: return MPATH_PR_SENSE_ABORTED_COMMAND; default : return MPATH_PR_OTHER; } case SAM_STAT_RESERVATION_CONFLICT: return MPATH_PR_RESERV_CONFLICT; default : return MPATH_PR_OTHER; } switch(io_hdr.host_status) { case DID_OK : break; default : return MPATH_PR_OTHER; } switch(io_hdr.driver_status) { case DRIVER_OK: break; default : return MPATH_PR_OTHER; } return MPATH_PR_SUCCESS; } int mpath_isLittleEndian(void) { int num = 1; if(*(char *)&num == 1) { condlog(2, "Little-Endian"); } else { condlog(2, "Big-Endian"); } return 0; } void mpath_reverse_uint16_byteorder(uint16_t *num) { uint16_t byte0, byte1; byte0 = (*num & 0x000000FF) >> 0 ; byte1 = (*num & 0x0000FF00) >> 8 ; *num = ((byte0 << 8) | (byte1 << 0)); } void mpath_reverse_uint32_byteorder(uint32_t *num) { uint32_t byte0, byte1, byte2, byte3; byte0 = (*num & 0x000000FF) >> 0 ; byte1 = (*num & 0x0000FF00) >> 8 ; byte2 = (*num & 0x00FF0000) >> 16 ; byte3 = (*num & 0xFF000000) >> 24 ; *num = ((byte0 << 24) | (byte1 << 16) | (byte2 << 8) | (byte3 << 0)); } void dumpHex(const char* str, int len, int log) { const char * p = str; unsigned char c; char buff[82]; const int bpstart = 5; int bpos = bpstart; int k; if (len <= 0) return; memset(buff, ' ', 80); buff[80] = '\0'; for (k = 0; k < len; k++) { c = *p++; bpos += 3; if (bpos == (bpstart + (9 * 3))) bpos++; sprintf(&buff[bpos], "%.2x", (int)(unsigned char)c); buff[bpos + 2] = ' '; if ((k > 0) && (0 == ((k + 1) % 16))) { if (log) condlog(0, "%.76s" , buff); else printf("%.76s" , buff); bpos = bpstart; memset(buff, ' ', 80); } } if (bpos > bpstart) { buff[bpos + 2] = '\0'; if (log) condlog(0, "%s", buff); else printf("%s\n" , buff); } return; } int get_prin_length(int rq_servact) { int mx_resp_len; switch (rq_servact) { case MPATH_PRIN_RKEY_SA: mx_resp_len = sizeof(struct prin_readdescr); break; case MPATH_PRIN_RRES_SA : mx_resp_len = sizeof(struct prin_resvdescr); break; case MPATH_PRIN_RCAP_SA : mx_resp_len = sizeof(struct prin_capdescr); break; case MPATH_PRIN_RFSTAT_SA: mx_resp_len = sizeof(struct print_fulldescr_list) + sizeof(struct prin_fulldescr *)*32; break; default: condlog(0, "invalid service action, %d", rq_servact); mx_resp_len = 0; break; } return mx_resp_len; } multipath-tools-0.7.4/libmpathpersist/mpath_pr_ioctl.h000066400000000000000000000063141320314174000232550ustar00rootroot00000000000000#define MPATH_XFER_HOST_DEV 0 /*data transfer from initiator to target */ #define MPATH_XFER_DEV_HOST 1 /*data transfer from target to initiator */ #define MPATH_XFER_NONE 2 /*no data transfer */ #define MPATH_XFER_UNKNOWN 3 /*data transfer direction is unknown */ #if 0 static const char * pr_type_strs[] = { "obsolete [0]", "Write Exclusive", "obsolete [2]", "Exclusive Access", "obsolete [4]", "Write Exclusive, registrants only", "Exclusive Access, registrants only", "Write Exclusive, all registrants", "Exclusive Access, all registrants", "obsolete [9]", "obsolete [0xa]", "obsolete [0xb]", "obsolete [0xc]", "obsolete [0xd]", "obsolete [0xe]", "obsolete [0xf]", }; #endif typedef unsigned int LWORD; /* unsigned numeric, bit patterns */ typedef unsigned char BYTE; /* unsigned numeric, bit patterns */ typedef struct SenseData { BYTE Error_Code; BYTE Segment_Number; /* not applicable to DAC */ BYTE Sense_Key; BYTE Information[ 4 ]; BYTE Additional_Len; LWORD Command_Specific_Info; BYTE ASC; BYTE ASCQ; BYTE Field_Replaceable_Unit; BYTE Sense_Key_Specific_Info[ 3 ]; BYTE Recovery_Action[ 2 ]; BYTE Total_Errors; BYTE Total_Retries; BYTE ASC_Stack_1; BYTE ASCQ_Stack_1; BYTE ASC_Stack_2; BYTE ASCQ_Stack_2; BYTE Additional_FRU_Info[ 8 ]; BYTE Error_Specific_Info[ 3 ]; BYTE Error_Detection_Point[ 4 ]; BYTE Original_CDB[10]; BYTE Host_ID; BYTE Host_Descriptor[ 2 ]; BYTE Serial_Number[ 16 ]; BYTE Array_SW_Revision[ 4 ]; BYTE Data_Xfer_Operation; BYTE LUN_Number; BYTE LUN_Status; BYTE Drive_ID; BYTE Xfer_Start_Drive_ID; BYTE Drive_SW_Revision[ 4 ]; BYTE Drive_Product_ID[ 16 ]; BYTE PowerUp_Status[ 2 ]; BYTE RAID_Level; BYTE Drive_Sense_ID[ 2 ]; BYTE Drive_Sense_Data[ 32 ]; BYTE Reserved2[24]; } SenseData_t; #define MPATH_PRIN_CMD 0x5e #define MPATH_PRIN_CMDLEN 10 #define MPATH_PROUT_CMD 0x5f #define MPATH_PROUT_CMDLEN 10 #define DID_OK 0x00 /* * Status codes */ #define SAM_STAT_GOOD 0x00 #define SAM_STAT_CHECK_CONDITION 0x02 #define SAM_STAT_CONDITION_MET 0x04 #define SAM_STAT_BUSY 0x08 #define SAM_STAT_INTERMEDIATE 0x10 #define SAM_STAT_INTERMEDIATE_CONDITION_MET 0x14 #define SAM_STAT_RESERVATION_CONFLICT 0x18 #define SAM_STAT_COMMAND_TERMINATED 0x22 /* obsolete in SAM-3 */ #define SAM_STAT_TASK_SET_FULL 0x28 #define SAM_STAT_ACA_ACTIVE 0x30 #define SAM_STAT_TASK_ABORTED 0x40 #define STATUS_MASK 0x3e /* * SENSE KEYS */ #define NO_SENSE 0x00 #define RECOVERED_ERROR 0x01 #define NOT_READY 0x02 #define MEDIUM_ERROR 0x03 #define HARDWARE_ERROR 0x04 #define ILLEGAL_REQUEST 0x05 #define UNIT_ATTENTION 0x06 #define DATA_PROTECT 0x07 #define BLANK_CHECK 0x08 #define COPY_ABORTED 0x0a #define ABORTED_COMMAND 0x0b #define VOLUME_OVERFLOW 0x0d #define MISCOMPARE 0x0e /* Driver status */ #define DRIVER_OK 0x00 multipath-tools-0.7.4/libmpathpersist/mpath_updatepr.c000066400000000000000000000026661320314174000232670ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include "debug.h" #include "mpath_cmd.h" #include "uxsock.h" #include "memory.h" #include "mpathpr.h" static int do_update_pr(char *alias, char *arg) { int fd; char str[256]; char *reply; int ret = 0; fd = mpath_connect(); if (fd == -1) { condlog (0, "ux socket connect error"); return -1; } snprintf(str,sizeof(str),"map %s %s", alias, arg); condlog (2, "%s: pr message=%s", alias, str); if (send_packet(fd, str) != 0) { condlog(2, "%s: message=%s send error=%d", alias, str, errno); mpath_disconnect(fd); return -1; } ret = recv_packet(fd, &reply, DEFAULT_REPLY_TIMEOUT); if (ret < 0) { condlog(2, "%s: message=%s recv error=%d", alias, str, errno); ret = -1; } else { condlog (2, "%s: message=%s reply=%s", alias, str, reply); if (reply && strncmp(reply,"ok", 2) == 0) ret = 0; else ret = -1; } free(reply); mpath_disconnect(fd); return ret; } int update_prflag(char *mapname, int set) { return do_update_pr(mapname, (set)? "setprstatus" : "unsetprstatus"); } int update_prkey(char *mapname, uint64_t prkey) { char str[256]; if (prkey) sprintf(str, "setprkey key %" PRIx64, prkey); else sprintf(str, "unsetprkey"); return do_update_pr(mapname, str); } multipath-tools-0.7.4/libmpathpersist/mpathpr.h000066400000000000000000000033261320314174000217240ustar00rootroot00000000000000#ifndef MPATHPR_H #define MPATHPR_H #include "structs.h" /* FILE_NAME_SIZE */ struct prin_param { char dev[FILE_NAME_SIZE]; int rq_servact; struct prin_resp *resp; int noisy; int status; }; struct prout_param { char dev[FILE_NAME_SIZE]; int rq_servact; int rq_scope; unsigned int rq_type; struct prout_param_descriptor *paramp; int noisy; int status; }; struct threadinfo { int status; pthread_t id; struct prout_param param; }; int prin_do_scsi_ioctl(char * dev, int rq_servact, struct prin_resp * resp, int noisy); int prout_do_scsi_ioctl( char * dev, int rq_servact, int rq_scope, unsigned int rq_type, struct prout_param_descriptor *paramp, int noisy); void * _mpath_pr_update (void *arg); int mpath_send_prin_activepath (char * dev, int rq_servact, struct prin_resp * resp, int noisy); int get_mpvec (vector curmp, vector pathvec, char * refwwid); void * mpath_prout_pthread_fn(void *p); void dumpHex(const char* , int len, int no_ascii); int mpath_prout_reg(struct multipath *mpp,int rq_servact, int rq_scope, unsigned int rq_type, struct prout_param_descriptor * paramp, int noisy); int mpath_prout_common(struct multipath *mpp,int rq_servact, int rq_scope, unsigned int rq_type, struct prout_param_descriptor * paramp, int noisy); int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope, unsigned int rq_type, struct prout_param_descriptor * paramp, int noisy); int send_prout_activepath(char * dev, int rq_servact, int rq_scope, unsigned int rq_type, struct prout_param_descriptor * paramp, int noisy); int update_prflag(char *mapname, int set); int update_prkey(char *mapname, uint64_t prkey); void * mpath_alloc_prin_response(int prin_sa); int update_map_pr(struct multipath *mpp); #endif multipath-tools-0.7.4/libmultipath/000077500000000000000000000000001320314174000173605ustar00rootroot00000000000000multipath-tools-0.7.4/libmultipath/Makefile000066400000000000000000000034671320314174000210320ustar00rootroot00000000000000# # Copyright (C) 2003 Christophe Varoqui, # include ../Makefile.inc SONAME = 0 DEVLIB = libmultipath.so LIBS = $(DEVLIB).$(SONAME) CFLAGS += $(LIB_CFLAGS) -I$(mpathcmddir) LIBDEPS += -lpthread -ldl -ldevmapper -ludev -L$(mpathcmddir) -lmpathcmd -lurcu -laio ifdef SYSTEMD CFLAGS += -DUSE_SYSTEMD=$(SYSTEMD) ifeq ($(shell test $(SYSTEMD) -gt 209 && echo 1), 1) LIBDEPS += -lsystemd else LIBDEPS += -lsystemd-daemon endif endif ifneq ($(call check_func,dm_task_no_flush,/usr/include/libdevmapper.h),0) CFLAGS += -DLIBDM_API_FLUSH -D_GNU_SOURCE endif ifneq ($(call check_func,dm_task_set_cookie,/usr/include/libdevmapper.h),0) CFLAGS += -DLIBDM_API_COOKIE endif ifneq ($(call check_func,udev_monitor_set_receive_buffer_size,/usr/include/libudev.h),0) CFLAGS += -DLIBUDEV_API_RECVBUF endif ifneq ($(call check_func,dm_task_deferred_remove,/usr/include/libdevmapper.h),0) CFLAGS += -DLIBDM_API_DEFERRED endif OBJS = memory.o parser.o vector.o devmapper.o callout.o \ hwtable.o blacklist.o util.o dmparser.o config.o \ structs.o discovery.o propsel.o dict.o \ pgpolicies.o debug.o defaults.o uevent.o time-util.o \ switchgroup.o uxsock.o print.o alias.o log_pthread.o \ log.o configure.o structs_vec.o sysfs.o prio.o checkers.o \ lock.o waiter.o file.o wwids.o prioritizers/alua_rtpg.o prkey.o \ io_err_stat.o all: $(LIBS) $(LIBS): $(OBJS) $(CC) $(LDFLAGS) $(SHARED_FLAGS) -Wl,-soname=$@ -o $@ $(OBJS) $(LIBDEPS) $(LN) $@ $(DEVLIB) install: $(INSTALL_PROGRAM) -d $(DESTDIR)$(syslibdir) $(INSTALL_PROGRAM) -m 755 $(LIBS) $(DESTDIR)$(syslibdir)/$(LIBS) $(INSTALL_PROGRAM) -m 755 -d $(DESTDIR)$(libdir) $(LN) $(LIBS) $(DESTDIR)$(syslibdir)/$(DEVLIB) uninstall: $(RM) $(DESTDIR)$(syslibdir)/$(LIBS) $(RM) $(DESTDIR)$(syslibdir)/$(DEVLIB) clean: $(RM) core *.a *.o *.so *.so.* *.gz multipath-tools-0.7.4/libmultipath/alias.c000066400000000000000000000201331320314174000206140ustar00rootroot00000000000000/* * Copyright (c) 2005 Christophe Varoqui * Copyright (c) 2005 Benjamin Marzinski, Redhat */ #include #include #include #include #include #include #include "debug.h" #include "uxsock.h" #include "alias.h" #include "file.h" #include "vector.h" #include "checkers.h" #include "structs.h" /* * significant parts of this file were taken from iscsi-bindings.c of the * linux-iscsi project. * Copyright (C) 2002 Cisco Systems, Inc. * * 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. * * See the file COPYING included with this distribution for more details. */ int valid_alias(char *alias) { if (strchr(alias, '/') != NULL) return 0; return 1; } static int format_devname(char *name, int id, int len, char *prefix) { int pos; int prefix_len = strlen(prefix); memset(name,0, len); strcpy(name, prefix); for (pos = len - 1; pos >= prefix_len; pos--) { id--; name[pos] = 'a' + id % 26; if (id < 26) break; id /= 26; } memmove(name + prefix_len, name + pos, len - pos); name[prefix_len + len - pos] = '\0'; return (prefix_len + len - pos); } static int scan_devname(char *alias, char *prefix) { char *c; int i, n = 0; if (!prefix || strncmp(alias, prefix, strlen(prefix))) return -1; if (strlen(alias) == strlen(prefix)) return -1; if (strlen(alias) > strlen(prefix) + 7) /* id of 'aaaaaaaa' overflows int */ return -1; c = alias + strlen(prefix); while (*c != '\0' && *c != ' ' && *c != '\t') { if (*c < 'a' || *c > 'z') return -1; i = *c - 'a'; n = ( n * 26 ) + i; if (n < 0) return -1; c++; n++; } return n; } static int lookup_binding(FILE *f, char *map_wwid, char **map_alias, char *prefix) { char buf[LINE_MAX]; unsigned int line_nr = 0; int id = 1; int biggest_id = 1; int smallest_bigger_id = INT_MAX; *map_alias = NULL; rewind(f); while (fgets(buf, LINE_MAX, f)) { char *c, *alias, *wwid; int curr_id; line_nr++; c = strpbrk(buf, "#\n\r"); if (c) *c = '\0'; alias = strtok(buf, " \t"); if (!alias) /* blank line */ continue; curr_id = scan_devname(alias, prefix); if (curr_id == id) id++; if (curr_id > biggest_id) biggest_id = curr_id; if (curr_id > id && curr_id < smallest_bigger_id) smallest_bigger_id = curr_id; wwid = strtok(NULL, " \t"); if (!wwid){ condlog(3, "Ignoring malformed line %u in bindings file", line_nr); continue; } if (strcmp(wwid, map_wwid) == 0){ condlog(3, "Found matching wwid [%s] in bindings file." " Setting alias to %s", wwid, alias); *map_alias = strdup(alias); if (*map_alias == NULL) condlog(0, "Cannot copy alias from bindings " "file : %s", strerror(errno)); return 0; } } condlog(3, "No matching wwid [%s] in bindings file.", map_wwid); if (id < 0) { condlog(0, "no more available user_friendly_names"); return 0; } if (id < smallest_bigger_id) return id; return biggest_id + 1; } static int rlookup_binding(FILE *f, char *buff, char *map_alias, char *prefix) { char line[LINE_MAX]; unsigned int line_nr = 0; buff[0] = '\0'; while (fgets(line, LINE_MAX, f)) { char *c, *alias, *wwid; line_nr++; c = strpbrk(line, "#\n\r"); if (c) *c = '\0'; alias = strtok(line, " \t"); if (!alias) /* blank line */ continue; wwid = strtok(NULL, " \t"); if (!wwid){ condlog(3, "Ignoring malformed line %u in bindings file", line_nr); continue; } if (strlen(wwid) > WWID_SIZE - 1) { condlog(3, "Ignoring too large wwid at %u in bindings file", line_nr); continue; } if (strcmp(alias, map_alias) == 0){ condlog(3, "Found matching alias [%s] in bindings file." "\nSetting wwid to %s", alias, wwid); strncpy(buff, wwid, WWID_SIZE); buff[WWID_SIZE - 1] = '\0'; return 0; } } condlog(3, "No matching alias [%s] in bindings file.", map_alias); return -1; } static char * allocate_binding(int fd, char *wwid, int id, char *prefix) { char buf[LINE_MAX]; off_t offset; char *alias, *c; int i; if (id < 0) { condlog(0, "Bindings file full. Cannot allocate new binding"); return NULL; } i = format_devname(buf, id, LINE_MAX, prefix); c = buf + i; snprintf(c,LINE_MAX - i, " %s\n", wwid); buf[LINE_MAX - 1] = '\0'; offset = lseek(fd, 0, SEEK_END); if (offset < 0){ condlog(0, "Cannot seek to end of bindings file : %s", strerror(errno)); return NULL; } if (write(fd, buf, strlen(buf)) != strlen(buf)){ condlog(0, "Cannot write binding to bindings file : %s", strerror(errno)); /* clear partial write */ if (ftruncate(fd, offset)) condlog(0, "Cannot truncate the header : %s", strerror(errno)); return NULL; } c = strchr(buf, ' '); if (c) *c = '\0'; alias = strdup(buf); if (alias == NULL) condlog(0, "cannot copy new alias from bindings file : %s", strerror(errno)); else condlog(3, "Created new binding [%s] for WWID [%s]", alias, wwid); return alias; } char * use_existing_alias (char *wwid, char *file, char *alias_old, char *prefix, int bindings_read_only) { char *alias = NULL; int id = 0; int fd, can_write; char buff[WWID_SIZE]; FILE *f; fd = open_file(file, &can_write, BINDINGS_FILE_HEADER); if (fd < 0) return NULL; f = fdopen(fd, "r"); if (!f) { condlog(0, "cannot fdopen on bindings file descriptor"); close(fd); return NULL; } /* lookup the binding. if it exsists, the wwid will be in buff * either way, id contains the id for the alias */ rlookup_binding(f, buff, alias_old, prefix); if (strlen(buff) > 0) { /* if buff is our wwid, it's already * allocated correctly */ if (strcmp(buff, wwid) == 0) alias = STRDUP(alias_old); else { alias = NULL; condlog(0, "alias %s already bound to wwid %s, cannot reuse", alias_old, buff); } goto out; } id = lookup_binding(f, wwid, &alias, NULL); if (alias) { condlog(3, "Use existing binding [%s] for WWID [%s]", alias, wwid); goto out; } /* allocate the existing alias in the bindings file */ id = scan_devname(alias_old, prefix); if (id <= 0) goto out; if (fflush(f) != 0) { condlog(0, "cannot fflush bindings file stream : %s", strerror(errno)); goto out; } if (can_write && !bindings_read_only) { alias = allocate_binding(fd, wwid, id, prefix); condlog(0, "Allocated existing binding [%s] for WWID [%s]", alias, wwid); } out: fclose(f); return alias; } char * get_user_friendly_alias(char *wwid, char *file, char *prefix, int bindings_read_only) { char *alias; int fd, id; FILE *f; int can_write; if (!wwid || *wwid == '\0') { condlog(3, "Cannot find binding for empty WWID"); return NULL; } fd = open_file(file, &can_write, BINDINGS_FILE_HEADER); if (fd < 0) return NULL; f = fdopen(fd, "r"); if (!f) { condlog(0, "cannot fdopen on bindings file descriptor : %s", strerror(errno)); close(fd); return NULL; } id = lookup_binding(f, wwid, &alias, prefix); if (id < 0) { fclose(f); return NULL; } if (fflush(f) != 0) { condlog(0, "cannot fflush bindings file stream : %s", strerror(errno)); free(alias); fclose(f); return NULL; } if (!alias && can_write && !bindings_read_only && id) alias = allocate_binding(fd, wwid, id, prefix); fclose(f); return alias; } int get_user_friendly_wwid(char *alias, char *buff, char *file) { int fd, unused; FILE *f; if (!alias || *alias == '\0') { condlog(3, "Cannot find binding for empty alias"); return -1; } fd = open_file(file, &unused, BINDINGS_FILE_HEADER); if (fd < 0) return -1; f = fdopen(fd, "r"); if (!f) { condlog(0, "cannot fdopen on bindings file descriptor : %s", strerror(errno)); close(fd); return -1; } rlookup_binding(f, buff, alias, NULL); if (!strlen(buff)) { fclose(f); return -1; } fclose(f); return 0; } multipath-tools-0.7.4/libmultipath/alias.h000066400000000000000000000011001320314174000206120ustar00rootroot00000000000000#define BINDINGS_FILE_HEADER \ "# Multipath bindings, Version : 1.0\n" \ "# NOTE: this file is automatically maintained by the multipath program.\n" \ "# You should not need to edit this file in normal circumstances.\n" \ "#\n" \ "# Format:\n" \ "# alias wwid\n" \ "#\n" int valid_alias(char *alias); char *get_user_friendly_alias(char *wwid, char *file, char *prefix, int bindings_readonly); int get_user_friendly_wwid(char *alias, char *buff, char *file); char *use_existing_alias (char *wwid, char *file, char *alias_old, char *prefix, int bindings_read_only); multipath-tools-0.7.4/libmultipath/blacklist.c000066400000000000000000000211111320314174000214700ustar00rootroot00000000000000/* * Copyright (c) 2004, 2005 Christophe Varoqui */ #include #include #include "checkers.h" #include "memory.h" #include "vector.h" #include "util.h" #include "debug.h" #include "structs.h" #include "config.h" #include "blacklist.h" int store_ble(vector blist, char * str, int origin) { struct blentry * ble; if (!str) return 0; if (!blist) goto out; ble = MALLOC(sizeof(struct blentry)); if (!ble) goto out; if (regcomp(&ble->regex, str, REG_EXTENDED|REG_NOSUB)) goto out1; if (!vector_alloc_slot(blist)) goto out1; ble->str = str; ble->origin = origin; vector_set_slot(blist, ble); return 0; out1: FREE(ble); out: FREE(str); return 1; } int alloc_ble_device(vector blist) { struct blentry_device * ble = MALLOC(sizeof(struct blentry_device)); if (!ble) return 1; if (!blist || !vector_alloc_slot(blist)) { FREE(ble); return 1; } vector_set_slot(blist, ble); return 0; } int set_ble_device(vector blist, char * vendor, char * product, int origin) { struct blentry_device * ble; if (!blist) return 1; ble = VECTOR_LAST_SLOT(blist); if (!ble) return 1; if (vendor) { if (regcomp(&ble->vendor_reg, vendor, REG_EXTENDED|REG_NOSUB)) { FREE(vendor); if (product) FREE(product); return 1; } ble->vendor = vendor; } if (product) { if (regcomp(&ble->product_reg, product, REG_EXTENDED|REG_NOSUB)) { FREE(product); if (vendor) { ble->vendor = NULL; FREE(vendor); } return 1; } ble->product = product; } ble->origin = origin; return 0; } int _blacklist_exceptions (vector elist, const char * str) { int i; struct blentry * ele; vector_foreach_slot (elist, ele, i) { if (!regexec(&ele->regex, str, 0, NULL, 0)) return 1; } return 0; } int _blacklist (vector blist, const char * str) { int i; struct blentry * ble; vector_foreach_slot (blist, ble, i) { if (!regexec(&ble->regex, str, 0, NULL, 0)) return 1; } return 0; } int _blacklist_exceptions_device(vector elist, char * vendor, char * product) { int i; struct blentry_device * ble; vector_foreach_slot (elist, ble, i) { if (!ble->vendor && !ble->product) continue; if ((!ble->vendor || !regexec(&ble->vendor_reg, vendor, 0, NULL, 0)) && (!ble->product || !regexec(&ble->product_reg, product, 0, NULL, 0))) return 1; } return 0; } int _blacklist_device (vector blist, char * vendor, char * product) { int i; struct blentry_device * ble; vector_foreach_slot (blist, ble, i) { if (!ble->vendor && !ble->product) continue; if ((!ble->vendor || !regexec(&ble->vendor_reg, vendor, 0, NULL, 0)) && (!ble->product || !regexec(&ble->product_reg, product, 0, NULL, 0))) return 1; } return 0; } int setup_default_blist (struct config * conf) { struct blentry * ble; struct hwentry *hwe; char * str; int i; str = STRDUP("^(ram|raw|loop|fd|md|dm-|sr|scd|st|dcssblk)[0-9]"); if (!str) return 1; if (store_ble(conf->blist_devnode, str, ORIGIN_DEFAULT)) return 1; str = STRDUP("^(td|hd|vd)[a-z]"); if (!str) return 1; if (store_ble(conf->blist_devnode, str, ORIGIN_DEFAULT)) return 1; str = STRDUP("(SCSI_IDENT_|ID_WWN)"); if (!str) return 1; if (store_ble(conf->elist_property, str, ORIGIN_DEFAULT)) return 1; vector_foreach_slot (conf->hwtable, hwe, i) { if (hwe->bl_product) { if (_blacklist_device(conf->blist_device, hwe->vendor, hwe->bl_product)) continue; if (alloc_ble_device(conf->blist_device)) return 1; ble = VECTOR_SLOT(conf->blist_device, VECTOR_SIZE(conf->blist_device) - 1); if (set_ble_device(conf->blist_device, STRDUP(hwe->vendor), STRDUP(hwe->bl_product), ORIGIN_DEFAULT)) { FREE(ble); vector_del_slot(conf->blist_device, VECTOR_SIZE(conf->blist_device) - 1); return 1; } } } return 0; } #define LOG_BLIST(M,S) \ if (vendor && product) \ condlog(3, "%s: (%s:%s) %s %s", \ dev, vendor, product, (M), (S)); \ else if (wwid && !dev) \ condlog(3, "%s: %s %s", wwid, (M), (S)); \ else if (wwid) \ condlog(3, "%s: %s %s %s", dev, (M), wwid, (S)); \ else if (env) \ condlog(3, "%s: %s %s %s", dev, (M), env, (S)); \ else \ condlog(3, "%s: %s %s", dev, (M), (S)) void log_filter (const char *dev, char *vendor, char *product, char *wwid, const char *env, int r) { /* * Try to sort from most likely to least. */ switch (r) { case MATCH_NOTHING: break; case MATCH_DEVICE_BLIST: LOG_BLIST("vendor/product", "blacklisted"); break; case MATCH_WWID_BLIST: LOG_BLIST("wwid", "blacklisted"); break; case MATCH_DEVNODE_BLIST: LOG_BLIST("device node name", "blacklisted"); break; case MATCH_PROPERTY_BLIST: LOG_BLIST("udev property", "blacklisted"); break; case MATCH_DEVICE_BLIST_EXCEPT: LOG_BLIST("vendor/product", "whitelisted"); break; case MATCH_WWID_BLIST_EXCEPT: LOG_BLIST("wwid", "whitelisted"); break; case MATCH_DEVNODE_BLIST_EXCEPT: LOG_BLIST("device node name", "whitelisted"); break; case MATCH_PROPERTY_BLIST_EXCEPT: LOG_BLIST("udev property", "whitelisted"); break; case MATCH_PROPERTY_BLIST_MISSING: LOG_BLIST("blacklisted,", "udev property missing"); break; } } int _filter_device (vector blist, vector elist, char * vendor, char * product) { if (!vendor || !product) return 0; if (_blacklist_exceptions_device(elist, vendor, product)) return MATCH_DEVICE_BLIST_EXCEPT; if (_blacklist_device(blist, vendor, product)) return MATCH_DEVICE_BLIST; return 0; } int filter_device (vector blist, vector elist, char * vendor, char * product) { int r = _filter_device(blist, elist, vendor, product); log_filter(NULL, vendor, product, NULL, NULL, r); return r; } int _filter_devnode (vector blist, vector elist, char * dev) { if (!dev) return 0; if (_blacklist_exceptions(elist, dev)) return MATCH_DEVNODE_BLIST_EXCEPT; if (_blacklist(blist, dev)) return MATCH_DEVNODE_BLIST; return 0; } int filter_devnode (vector blist, vector elist, char * dev) { int r = _filter_devnode(blist, elist, dev); log_filter(dev, NULL, NULL, NULL, NULL, r); return r; } int _filter_wwid (vector blist, vector elist, char * wwid) { if (!wwid) return 0; if (_blacklist_exceptions(elist, wwid)) return MATCH_WWID_BLIST_EXCEPT; if (_blacklist(blist, wwid)) return MATCH_WWID_BLIST; return 0; } int filter_wwid (vector blist, vector elist, char * wwid, char * dev) { int r = _filter_wwid(blist, elist, wwid); log_filter(dev, NULL, NULL, wwid, NULL, r); return r; } int _filter_path (struct config * conf, struct path * pp) { int r; r = filter_property(conf, pp->udev); if (r > 0) return r; r = _filter_devnode(conf->blist_devnode, conf->elist_devnode,pp->dev); if (r > 0) return r; r = _filter_device(conf->blist_device, conf->elist_device, pp->vendor_id, pp->product_id); if (r > 0) return r; r = _filter_wwid(conf->blist_wwid, conf->elist_wwid, pp->wwid); return r; } int filter_path (struct config * conf, struct path * pp) { int r=_filter_path(conf, pp); log_filter(pp->dev, pp->vendor_id, pp->product_id, pp->wwid, NULL, r); return r; } int _filter_property (struct config *conf, const char *env) { if (_blacklist_exceptions(conf->elist_property, env)) return MATCH_PROPERTY_BLIST_EXCEPT; if (_blacklist(conf->blist_property, env)) return MATCH_PROPERTY_BLIST; return 0; } int filter_property(struct config * conf, struct udev_device * udev) { const char *devname = udev_device_get_sysname(udev); struct udev_list_entry *list_entry; int r; if (!udev) return 0; udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(udev)) { const char *env; env = udev_list_entry_get_name(list_entry); if (!env) continue; r = _filter_property(conf, env); if (r) { log_filter(devname, NULL, NULL, NULL, env, r); return r; } } /* * This is the inverse of the 'normal' matching; * the environment variable _has_ to match. */ log_filter(devname, NULL, NULL, NULL, NULL, MATCH_PROPERTY_BLIST_MISSING); return MATCH_PROPERTY_BLIST_MISSING; } void free_blacklist (vector blist) { struct blentry * ble; int i; if (!blist) return; vector_foreach_slot (blist, ble, i) { if (ble) { regfree(&ble->regex); FREE(ble->str); FREE(ble); } } vector_free(blist); } void free_blacklist_device (vector blist) { struct blentry_device * ble; int i; if (!blist) return; vector_foreach_slot (blist, ble, i) { if (ble) { if (ble->vendor) { regfree(&ble->vendor_reg); FREE(ble->vendor); } if (ble->product) { regfree(&ble->product_reg); FREE(ble->product); } FREE(ble); } } vector_free(blist); } multipath-tools-0.7.4/libmultipath/blacklist.h000066400000000000000000000022541320314174000215040ustar00rootroot00000000000000#ifndef _BLACKLIST_H #define _BLACKLIST_H #include #include #define MATCH_NOTHING 0 #define MATCH_WWID_BLIST 1 #define MATCH_DEVICE_BLIST 2 #define MATCH_DEVNODE_BLIST 3 #define MATCH_PROPERTY_BLIST 4 #define MATCH_PROPERTY_BLIST_MISSING 5 #define MATCH_WWID_BLIST_EXCEPT -MATCH_WWID_BLIST #define MATCH_DEVICE_BLIST_EXCEPT -MATCH_DEVICE_BLIST #define MATCH_DEVNODE_BLIST_EXCEPT -MATCH_DEVNODE_BLIST #define MATCH_PROPERTY_BLIST_EXCEPT -MATCH_PROPERTY_BLIST struct blentry { char * str; regex_t regex; int origin; }; struct blentry_device { char * vendor; char * product; regex_t vendor_reg; regex_t product_reg; int origin; }; int setup_default_blist (struct config *); int alloc_ble_device (vector); int filter_devnode (vector, vector, char *); int filter_wwid (vector, vector, char *, char *); int filter_device (vector, vector, char *, char *); int filter_path (struct config *, struct path *); int filter_property(struct config *, struct udev_device *); int store_ble (vector, char *, int); int set_ble_device (vector, char *, char *, int); void free_blacklist (vector); void free_blacklist_device (vector); #endif /* _BLACKLIST_H */ multipath-tools-0.7.4/libmultipath/byteorder.h000066400000000000000000000020401320314174000215240ustar00rootroot00000000000000#ifndef BYTEORDER_H_INCLUDED #define BYTEORDER_H_INCLUDED #ifdef __linux__ # include # include #else # error unsupported #endif #if BYTE_ORDER == LITTLE_ENDIAN # define le16_to_cpu(x) (x) # define be16_to_cpu(x) bswap_16(x) # define le32_to_cpu(x) (x) # define le64_to_cpu(x) (x) # define be32_to_cpu(x) bswap_32(x) # define be64_to_cpu(x) bswap_64(x) #elif BYTE_ORDER == BIG_ENDIAN # define le16_to_cpu(x) bswap_16(x) # define be16_to_cpu(x) (x) # define le32_to_cpu(x) bswap_32(x) # define le64_to_cpu(x) bswap_64(x) # define be32_to_cpu(x) (x) # define be64_to_cpu(x) (x) #else # error unsupported #endif #define cpu_to_le16(x) le16_to_cpu(x) #define cpu_to_be16(x) be16_to_cpu(x) #define cpu_to_le32(x) le32_to_cpu(x) #define cpu_to_be32(x) be32_to_cpu(x) #define cpu_to_le64(x) le64_to_cpu(x) #define cpu_to_be64(x) be64_to_cpu(x) struct be64 { uint64_t _v; }; #define get_be64(x) be64_to_cpu((x)._v) #define put_be64(x, y) do { (x)._v = cpu_to_be64(y); } while (0) #endif /* BYTEORDER_H_INCLUDED */ multipath-tools-0.7.4/libmultipath/callout.c000066400000000000000000000072271320314174000211770ustar00rootroot00000000000000/* * Source: copy of the udev package source file * * Copyrights of the source file apply * Copyright (c) 2004 Christophe Varoqui */ #include #include #include #include #include #include #include #include #include #include "checkers.h" #include "vector.h" #include "structs.h" #include "util.h" #include "debug.h" int execute_program(char *path, char *value, int len) { int retval; int count; int status; int fds[2], null_fd; pid_t pid; char *pos; char arg[CALLOUT_MAX_SIZE]; int argc = sizeof(arg) / 2; char *argv[argc + 1]; int i; i = 0; if (strchr(path, ' ')) { strlcpy(arg, path, sizeof(arg)); pos = arg; while (pos != NULL && i < argc) { if (pos[0] == '\'') { /* don't separate if in apostrophes */ pos++; argv[i] = strsep(&pos, "\'"); while (pos[0] == ' ') pos++; } else { argv[i] = strsep(&pos, " "); } i++; } } else { argv[i++] = path; } argv[i] = NULL; retval = pipe(fds); if (retval != 0) { condlog(0, "error creating pipe for callout: %s", strerror(errno)); return -1; } pid = fork(); switch(pid) { case 0: /* child */ close(STDOUT_FILENO); /* dup write side of pipe to STDOUT */ if (dup(fds[1]) < 0) return -1; /* Ignore writes to stderr */ null_fd = open("/dev/null", O_WRONLY); if (null_fd > 0) { int err_fd __attribute__ ((unused)); close(STDERR_FILENO); err_fd = dup(null_fd); close(null_fd); } retval = execv(argv[0], argv); condlog(0, "error execing %s : %s", argv[0], strerror(errno)); exit(-1); case -1: condlog(0, "fork failed: %s", strerror(errno)); close(fds[0]); close(fds[1]); return -1; default: /* parent reads from fds[0] */ close(fds[1]); retval = 0; i = 0; while (1) { count = read(fds[0], value + i, len - i-1); if (count <= 0) break; i += count; if (i >= len-1) { condlog(0, "not enough space for response from %s", argv[0]); retval = -1; break; } } if (count < 0) { condlog(0, "no response from %s", argv[0]); retval = -1; } if (i > 0 && value[i-1] == '\n') i--; value[i] = '\0'; wait(&status); close(fds[0]); retval = -1; if (WIFEXITED(status)) { status = WEXITSTATUS(status); if (status == 0) retval = 0; else condlog(0, "%s exited with %d", argv[0], status); } else if (WIFSIGNALED(status)) condlog(0, "%s was terminated by signal %d", argv[0], WTERMSIG(status)); else condlog(0, "%s terminated abnormally", argv[0]); } return retval; } int apply_format(char * string, char * cmd, struct path * pp) { char * pos; char * dst; char * p; char * q; int len; int myfree; if (!string) return 1; if (!cmd) return 1; dst = cmd; p = dst; pos = strchr(string, '%'); myfree = CALLOUT_MAX_SIZE; if (!pos) { strcpy(dst, string); return 0; } len = (int) (pos - string) + 1; myfree -= len; if (myfree < 2) return 1; snprintf(p, len, "%s", string); p += len - 1; pos++; switch (*pos) { case 'n': len = strlen(pp->dev) + 1; myfree -= len; if (myfree < 2) return 1; snprintf(p, len, "%s", pp->dev); for (q = p; q < p + len; q++) { if (q && *q == '!') *q = '/'; } p += len - 1; break; case 'd': len = strlen(pp->dev_t) + 1; myfree -= len; if (myfree < 2) return 1; snprintf(p, len, "%s", pp->dev_t); p += len - 1; break; default: break; } pos++; if (!*pos) { condlog(3, "formatted callout = %s", dst); return 0; } len = strlen(pos) + 1; myfree -= len; if (myfree < 2) return 1; snprintf(p, len, "%s", pos); condlog(3, "reformatted callout = %s", dst); return 0; } multipath-tools-0.7.4/libmultipath/callout.h000066400000000000000000000002341320314174000211730ustar00rootroot00000000000000#ifndef _CALLOUT_H #define _CALLOUT_H int execute_program(char *, char *, int); int apply_format (char *, char *, struct path *); #endif /* _CALLOUT_H */ multipath-tools-0.7.4/libmultipath/checkers.c000066400000000000000000000130601320314174000213130ustar00rootroot00000000000000#include #include #include #include #include #include "debug.h" #include "checkers.h" #include "vector.h" char *checker_state_names[] = { "wild", "unchecked", "down", "up", "shaky", "ghost", "pending", "timeout", "removed", "delayed", }; static LIST_HEAD(checkers); char * checker_state_name (int i) { return checker_state_names[i]; } int init_checkers (char *multipath_dir) { if (!add_checker(multipath_dir, DEFAULT_CHECKER)) return 1; return 0; } struct checker * alloc_checker (void) { struct checker *c; c = MALLOC(sizeof(struct checker)); if (c) { INIT_LIST_HEAD(&c->node); c->refcount = 1; c->fd = -1; } return c; } void free_checker (struct checker * c) { if (!c) return; c->refcount--; if (c->refcount) { condlog(3, "%s checker refcount %d", c->name, c->refcount); return; } condlog(3, "unloading %s checker", c->name); list_del(&c->node); if (c->handle) { if (dlclose(c->handle) != 0) { condlog(0, "Cannot unload checker %s: %s", c->name, dlerror()); } } FREE(c); } void cleanup_checkers (void) { struct checker * checker_loop; struct checker * checker_temp; list_for_each_entry_safe(checker_loop, checker_temp, &checkers, node) { free_checker(checker_loop); } } struct checker * checker_lookup (char * name) { struct checker * c; if (!name || !strlen(name)) return NULL; list_for_each_entry(c, &checkers, node) { if (!strncmp(name, c->name, CHECKER_NAME_LEN)) return c; } return NULL; } struct checker * add_checker (char *multipath_dir, char * name) { char libname[LIB_CHECKER_NAMELEN]; struct stat stbuf; struct checker * c; char *errstr; c = alloc_checker(); if (!c) return NULL; snprintf(c->name, CHECKER_NAME_LEN, "%s", name); if (!strncmp(c->name, NONE, 4)) goto done; snprintf(libname, LIB_CHECKER_NAMELEN, "%s/libcheck%s.so", multipath_dir, name); if (stat(libname,&stbuf) < 0) { condlog(0,"Checker '%s' not found in %s", name, multipath_dir); goto out; } condlog(3, "loading %s checker", libname); c->handle = dlopen(libname, RTLD_NOW); if (!c->handle) { if ((errstr = dlerror()) != NULL) condlog(0, "A dynamic linking error occurred: (%s)", errstr); goto out; } c->check = (int (*)(struct checker *)) dlsym(c->handle, "libcheck_check"); errstr = dlerror(); if (errstr != NULL) condlog(0, "A dynamic linking error occurred: (%s)", errstr); if (!c->check) goto out; c->init = (int (*)(struct checker *)) dlsym(c->handle, "libcheck_init"); errstr = dlerror(); if (errstr != NULL) condlog(0, "A dynamic linking error occurred: (%s)", errstr); if (!c->init) goto out; c->free = (void (*)(struct checker *)) dlsym(c->handle, "libcheck_free"); errstr = dlerror(); if (errstr != NULL) condlog(0, "A dynamic linking error occurred: (%s)", errstr); if (!c->free) goto out; c->repair = (void (*)(struct checker *)) dlsym(c->handle, "libcheck_repair"); errstr = dlerror(); if (errstr != NULL) condlog(0, "A dynamic linking error occurred: (%s)", errstr); if (!c->repair) goto out; done: c->fd = -1; c->sync = 1; list_add(&c->node, &checkers); return c; out: free_checker(c); return NULL; } void checker_set_fd (struct checker * c, int fd) { if (!c) return; c->fd = fd; } void checker_set_sync (struct checker * c) { if (!c) return; c->sync = 1; } void checker_set_async (struct checker * c) { if (!c) return; c->sync = 0; } void checker_enable (struct checker * c) { if (!c) return; c->disable = 0; } void checker_disable (struct checker * c) { if (!c) return; c->disable = 1; } int checker_init (struct checker * c, void ** mpctxt_addr) { if (!c) return 1; c->mpcontext = mpctxt_addr; if (c->init) return c->init(c); return 0; } void checker_clear (struct checker *c) { memset(c, 0x0, sizeof(struct checker)); c->fd = -1; } void checker_put (struct checker * dst) { struct checker * src; if (!dst || !strlen(dst->name)) return; src = checker_lookup(dst->name); if (dst->free) dst->free(dst); checker_clear(dst); free_checker(src); } void checker_repair (struct checker * c) { if (!checker_selected(c)) return; c->message[0] = '\0'; if (c->disable) { MSG(c, "checker disabled"); return; } if (c->repair) c->repair(c); } int checker_check (struct checker * c, int path_state) { int r; if (!c) return PATH_WILD; c->message[0] = '\0'; if (c->disable) { MSG(c, "checker disabled"); return PATH_UNCHECKED; } if (!strncmp(c->name, NONE, 4)) return path_state; if (c->fd < 0) { MSG(c, "no usable fd"); return PATH_WILD; } r = c->check(c); return r; } int checker_selected (struct checker * c) { if (!c) return 0; if (!strncmp(c->name, NONE, 4)) return 1; return (c->check) ? 1 : 0; } char * checker_name (struct checker * c) { if (!c) return NULL; return c->name; } char * checker_message (struct checker * c) { if (!c) return NULL; return c->message; } void checker_clear_message (struct checker *c) { if (!c) return; c->message[0] = '\0'; } void checker_get (char *multipath_dir, struct checker * dst, char * name) { struct checker * src = NULL; if (!dst) return; if (name && strlen(name)) { src = checker_lookup(name); if (!src) src = add_checker(multipath_dir, name); } if (!src) { dst->check = NULL; return; } dst->fd = src->fd; dst->sync = src->sync; strncpy(dst->name, src->name, CHECKER_NAME_LEN); strncpy(dst->message, src->message, CHECKER_MSG_LEN); dst->repair = src->repair; dst->check = src->check; dst->init = src->init; dst->free = src->free; dst->handle = NULL; src->refcount++; } multipath-tools-0.7.4/libmultipath/checkers.h000066400000000000000000000113031320314174000213160ustar00rootroot00000000000000#ifndef _CHECKERS_H #define _CHECKERS_H #include "list.h" #include "memory.h" #include "defaults.h" /* * * Userspace (multipath/multipathd) path states * * PATH_WILD: * - Use: None of the checkers (returned if we don't have an fd) * - Description: Corner case where "fd < 0" for path fd (see checker_check()) * * PATH_UNCHECKED: * - Use: Only in directio checker * - Description: set when fcntl(F_GETFL) fails to return flags or O_DIRECT * not include in flags, or O_DIRECT read fails * - Notes: * - multipathd: uses it to skip over paths in sync_map_state() * - multipath: used in update_paths(); if state==PATH_UNCHECKED, call * pathinfo() * * PATH_DOWN: * - Use: All checkers (directio, emc_clariion, hp_sw, readsector0, tur) * - Description: Either a) SG_IO ioctl failed, or b) check condition on some * SG_IO ioctls that succeed (tur, readsector0 checkers); path is down and * you shouldn't try to send commands to it * * PATH_UP: * - Use: All checkers (directio, emc_clariion, hp_sw, readsector0, tur) * - Description: Path is up and I/O can be sent to it * * PATH_SHAKY: * - Use: Only emc_clariion * - Description: Indicates path not available for "normal" operations * * PATH_GHOST: * - Use: Only hp_sw and rdac * - Description: Indicates a "passive/standby" path on active/passive HP * arrays. These paths will return valid answers to certain SCSI commands * (tur, read_capacity, inquiry, start_stop), but will fail I/O commands. * The path needs an initialization command to be sent to it in order for * I/Os to succeed. * * PATH_PENDING: * - Use: All async checkers * - Description: Indicates a check IO is in flight. * * PATH_TIMEOUT: * - Use: Only tur checker * - Description: Command timed out * * PATH REMOVED: * - Use: All checkers * - Description: Device has been removed from the system * * PATH_DELAYED: * - Use: None of the checkers (returned if the path is being delayed before * reintegration. * - Description: If a path fails after being up for less than * delay_watch_checks checks, when it comes back up again, it will not * be marked as up until it has been up for delay_wait_checks checks. * During this time, it is marked as "delayed" */ enum path_check_state { PATH_WILD, PATH_UNCHECKED, PATH_DOWN, PATH_UP, PATH_SHAKY, PATH_GHOST, PATH_PENDING, PATH_TIMEOUT, PATH_REMOVED, PATH_DELAYED, PATH_MAX_STATE }; #define DIRECTIO "directio" #define TUR "tur" #define HP_SW "hp_sw" #define RDAC "rdac" #define EMC_CLARIION "emc_clariion" #define READSECTOR0 "readsector0" #define CCISS_TUR "cciss_tur" #define NONE "none" #define RBD "rbd" #define ASYNC_TIMEOUT_SEC 30 /* * strings lengths */ #define CHECKER_NAME_LEN 16 #define CHECKER_MSG_LEN 256 #define CHECKER_DEV_LEN 256 #define LIB_CHECKER_NAMELEN 256 struct checker { struct list_head node; void *handle; int refcount; int fd; int sync; unsigned int timeout; int disable; char name[CHECKER_NAME_LEN]; char message[CHECKER_MSG_LEN]; /* comm with callers */ void * context; /* store for persistent data */ void ** mpcontext; /* store for persistent data shared multipath-wide. Use MALLOC if you want to stuff data in. */ int (*check)(struct checker *); void (*repair)(struct checker *); /* called if check returns PATH_DOWN to bring path into usable state */ int (*init)(struct checker *); /* to allocate the context */ void (*free)(struct checker *); /* to free the context */ }; #define MSG(c, fmt, args...) snprintf((c)->message, CHECKER_MSG_LEN, fmt, ##args); char * checker_state_name (int); int init_checkers (char *); void cleanup_checkers (void); struct checker * add_checker (char *, char *); struct checker * checker_lookup (char *); int checker_init (struct checker *, void **); void checker_clear (struct checker *); void checker_put (struct checker *); void checker_reset (struct checker *); void checker_set_sync (struct checker *); void checker_set_async (struct checker *); void checker_set_fd (struct checker *, int); void checker_enable (struct checker *); void checker_disable (struct checker *); void checker_repair (struct checker *); int checker_check (struct checker *, int); int checker_selected (struct checker *); char * checker_name (struct checker *); char * checker_message (struct checker *); void checker_clear_message (struct checker *c); void checker_get (char *, struct checker *, char *); /* Functions exported by path checker dynamic libraries (.so) */ int libcheck_check(struct checker *); int libcheck_init(struct checker *); void libcheck_free(struct checker *); void libcheck_repair(struct checker *); #endif /* _CHECKERS_H */ multipath-tools-0.7.4/libmultipath/checkers/000077500000000000000000000000001320314174000211475ustar00rootroot00000000000000multipath-tools-0.7.4/libmultipath/checkers/Makefile000066400000000000000000000015761320314174000226200ustar00rootroot00000000000000# # Copyright (C) 2003 Christophe Varoqui, # include ../../Makefile.inc CFLAGS += $(LIB_CFLAGS) -I.. # If you add or remove a checker also update multipath/multipath.conf.5 LIBS= \ libcheckcciss_tur.so \ libcheckreadsector0.so \ libchecktur.so \ libcheckdirectio.so \ libcheckemc_clariion.so \ libcheckhp_sw.so \ libcheckrdac.so ifneq ($(call check_file,/usr/include/rados/librados.h),0) LIBS += libcheckrbd.so endif all: $(LIBS) libcheckrbd.so: rbd.o $(CC) $(LDFLAGS) $(SHARED_FLAGS) -o $@ $^ -lrados -ludev libcheckdirectio.so: libsg.o directio.o $(CC) $(LDFLAGS) $(SHARED_FLAGS) -o $@ $^ -laio libcheck%.so: libsg.o %.o $(CC) $(LDFLAGS) $(SHARED_FLAGS) -o $@ $^ install: $(INSTALL_PROGRAM) -m 755 $(LIBS) $(DESTDIR)$(libdir) uninstall: for file in $(LIBS); do $(RM) $(DESTDIR)$(libdir)/$$file; done clean: $(RM) core *.a *.o *.gz *.so multipath-tools-0.7.4/libmultipath/checkers/cciss.h000066400000000000000000000056121320314174000224300ustar00rootroot00000000000000#ifndef CCISS_H #define CCISS_H #include #include #define CCISS_IOC_MAGIC 'B' /* * transfer direction */ #define XFER_NONE 0x00 #define XFER_WRITE 0x01 #define XFER_READ 0x02 #define XFER_RSVD 0x03 /* * task attribute */ #define ATTR_UNTAGGED 0x00 #define ATTR_SIMPLE 0x04 #define ATTR_HEADOFQUEUE 0x05 #define ATTR_ORDERED 0x06 #define ATTR_ACA 0x07 /* * cdb type */ #define TYPE_CMD 0x00 #define TYPE_MSG 0x01 #define SENSEINFOBYTES 32 /* * Type defs used in the following structs */ #define BYTE __u8 #define WORD __u16 #define HWORD __u16 #define DWORD __u32 #pragma pack(1) //Command List Structure typedef union _SCSI3Addr_struct { struct { BYTE Dev; BYTE Bus:6; BYTE Mode:2; // b00 } PeripDev; struct { BYTE DevLSB; BYTE DevMSB:6; BYTE Mode:2; // b01 } LogDev; struct { BYTE Dev:5; BYTE Bus:3; BYTE Targ:6; BYTE Mode:2; // b10 } LogUnit; } SCSI3Addr_struct; typedef struct _PhysDevAddr_struct { DWORD TargetId:24; DWORD Bus:6; DWORD Mode:2; SCSI3Addr_struct Target[2]; //2 level target device addr } PhysDevAddr_struct; typedef struct _LogDevAddr_struct { DWORD VolId:30; DWORD Mode:2; BYTE reserved[4]; } LogDevAddr_struct; typedef union _LUNAddr_struct { BYTE LunAddrBytes[8]; SCSI3Addr_struct SCSI3Lun[4]; PhysDevAddr_struct PhysDev; LogDevAddr_struct LogDev; } LUNAddr_struct; typedef struct _RequestBlock_struct { BYTE CDBLen; struct { BYTE Type:3; BYTE Attribute:3; BYTE Direction:2; } Type; HWORD Timeout; BYTE CDB[16]; } RequestBlock_struct; typedef union _MoreErrInfo_struct{ struct { BYTE Reserved[3]; BYTE Type; DWORD ErrorInfo; } Common_Info; struct{ BYTE Reserved[2]; BYTE offense_size;//size of offending entry BYTE offense_num; //byte # of offense 0-base DWORD offense_value; } Invalid_Cmd; } MoreErrInfo_struct; typedef struct _ErrorInfo_struct { BYTE ScsiStatus; BYTE SenseLen; HWORD CommandStatus; DWORD ResidualCnt; MoreErrInfo_struct MoreErrInfo; BYTE SenseInfo[SENSEINFOBYTES]; } ErrorInfo_struct; #pragma pack() typedef struct _IOCTL_Command_struct { LUNAddr_struct LUN_info; RequestBlock_struct Request; ErrorInfo_struct error_info; WORD buf_size; /* size in bytes of the buf */ BYTE *buf; } IOCTL_Command_struct; typedef struct _LogvolInfo_struct{ __u32 LunID; int num_opens; /* number of opens on the logical volume */ int num_parts; /* number of partitions configured on logvol */ } LogvolInfo_struct; #define CCISS_PASSTHRU _IOWR(CCISS_IOC_MAGIC, 11, IOCTL_Command_struct) #define CCISS_GETLUNINFO _IOR(CCISS_IOC_MAGIC, 17, LogvolInfo_struct) int cciss_init( struct checker *); void cciss_free (struct checker * c); int cciss_tur( struct checker *); #endif multipath-tools-0.7.4/libmultipath/checkers/cciss_tur.c000066400000000000000000000073111320314174000233130ustar00rootroot00000000000000/* ***************************************************************************** * * * (C) Copyright 2007 Hewlett-Packard Development Company, L.P * * * * 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, see . * * * ***************************************************************************** */ /* * This program originally derived from and inspired by * Christophe Varoqui's tur.c, part of libchecker. */ #include #include #include #include #include #include #include #include #include #include "checkers.h" #include "cciss.h" #define TUR_CMD_LEN 6 #define HEAVY_CHECK_COUNT 10 #define MSG_CCISS_TUR_UP "cciss_tur checker reports path is up" #define MSG_CCISS_TUR_DOWN "cciss_tur checker reports path is down" struct cciss_tur_checker_context { void * dummy; }; int libcheck_init (struct checker * c) { return 0; } void libcheck_free (struct checker * c) { return; } void libcheck_repair (struct checker * c) { return; } int libcheck_check(struct checker * c) { int rc; int ret; unsigned int lun = 0; struct cciss_tur_checker_context * ctxt = NULL; LogvolInfo_struct lvi; // logical "volume" info IOCTL_Command_struct cic; // cciss ioctl command if ((c->fd) < 0) { MSG(c,"no usable fd"); ret = -1; goto out; } rc = ioctl(c->fd, CCISS_GETLUNINFO, &lvi); if ( rc != 0) { perror("Error: "); fprintf(stderr, "cciss TUR failed in CCISS_GETLUNINFO: %s\n", strerror(errno)); MSG(c,MSG_CCISS_TUR_DOWN); ret = PATH_DOWN; goto out; } else { lun = lvi.LunID; } memset(&cic, 0, sizeof(cic)); cic.LUN_info.LogDev.VolId = lun & 0x3FFFFFFF; cic.LUN_info.LogDev.Mode = 0x01; /* logical volume addressing */ cic.Request.CDBLen = 6; /* need to try just 2 bytes here */ cic.Request.Type.Type = TYPE_CMD; // It is a command. cic.Request.Type.Attribute = ATTR_SIMPLE; cic.Request.Type.Direction = XFER_NONE; cic.Request.Timeout = 0; cic.Request.CDB[0] = 0; cic.Request.CDB[1] = 0; cic.Request.CDB[2] = 0; cic.Request.CDB[3] = 0; cic.Request.CDB[4] = 0; cic.Request.CDB[5] = 0; rc = ioctl(c->fd, CCISS_PASSTHRU, &cic); if (rc < 0) { fprintf(stderr, "cciss TUR failed: %s\n", strerror(errno)); MSG(c,MSG_CCISS_TUR_DOWN); ret = PATH_DOWN; goto out; } if ((cic.error_info.CommandStatus | cic.error_info.ScsiStatus )) { MSG(c,MSG_CCISS_TUR_DOWN); ret = PATH_DOWN; goto out; } MSG(c,MSG_CCISS_TUR_UP); ret = PATH_UP; out: /* * caller told us he doesn't want to keep the context : * free it */ if (!c->context) free(ctxt); return(ret); } multipath-tools-0.7.4/libmultipath/checkers/directio.c000066400000000000000000000107161320314174000231220ustar00rootroot00000000000000/* * Copyright (c) 2005 Hannes Reinecke, Suse */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include "checkers.h" #include "../libmultipath/debug.h" #define MSG_DIRECTIO_UNKNOWN "directio checker is not available" #define MSG_DIRECTIO_UP "directio checker reports path is up" #define MSG_DIRECTIO_DOWN "directio checker reports path is down" #define MSG_DIRECTIO_PENDING "directio checker is waiting on aio" #define LOG(prio, fmt, args...) condlog(prio, "directio: " fmt, ##args) struct directio_context { int running; int reset_flags; int blksize; unsigned char * buf; unsigned char * ptr; io_context_t ioctx; struct iocb io; }; int libcheck_init (struct checker * c) { unsigned long pgsize = getpagesize(); struct directio_context * ct; long flags; ct = malloc(sizeof(struct directio_context)); if (!ct) return 1; memset(ct, 0, sizeof(struct directio_context)); if (io_setup(1, &ct->ioctx) != 0) { condlog(1, "io_setup failed"); free(ct); return 1; } if (ioctl(c->fd, BLKBSZGET, &ct->blksize) < 0) { MSG(c, "cannot get blocksize, set default"); ct->blksize = 512; } if (ct->blksize > 4096) { /* * Sanity check for DASD; BSZGET is broken */ ct->blksize = 4096; } if (!ct->blksize) goto out; ct->buf = (unsigned char *)malloc(ct->blksize + pgsize); if (!ct->buf) goto out; flags = fcntl(c->fd, F_GETFL); if (flags < 0) goto out; if (!(flags & O_DIRECT)) { flags |= O_DIRECT; if (fcntl(c->fd, F_SETFL, flags) < 0) goto out; ct->reset_flags = 1; } ct->ptr = (unsigned char *) (((unsigned long)ct->buf + pgsize - 1) & (~(pgsize - 1))); /* Successfully initialized, return the context. */ c->context = (void *) ct; return 0; out: if (ct->buf) free(ct->buf); io_destroy(ct->ioctx); free(ct); return 1; } void libcheck_free (struct checker * c) { struct directio_context * ct = (struct directio_context *)c->context; long flags; if (!ct) return; if (ct->reset_flags) { if ((flags = fcntl(c->fd, F_GETFL)) >= 0) { int ret __attribute__ ((unused)); flags &= ~O_DIRECT; /* No point in checking for errors */ ret = fcntl(c->fd, F_SETFL, flags); } } if (ct->buf) free(ct->buf); io_destroy(ct->ioctx); free(ct); } void libcheck_repair (struct checker * c) { return; } static int check_state(int fd, struct directio_context *ct, int sync, int timeout_secs) { struct timespec timeout = { .tv_nsec = 5 }; struct io_event event; struct stat sb; int rc = PATH_UNCHECKED; long r; if (fstat(fd, &sb) == 0) { LOG(4, "called for %x", (unsigned) sb.st_rdev); } if (sync > 0) { LOG(4, "called in synchronous mode"); timeout.tv_sec = timeout_secs; timeout.tv_nsec = 0; } if (!ct->running) { struct iocb *ios[1] = { &ct->io }; LOG(3, "starting new request"); memset(&ct->io, 0, sizeof(struct iocb)); io_prep_pread(&ct->io, fd, ct->ptr, ct->blksize, 0); if (io_submit(ct->ioctx, 1, ios) != 1) { LOG(3, "io_submit error %i", errno); return PATH_UNCHECKED; } } ct->running++; errno = 0; r = io_getevents(ct->ioctx, 1L, 1L, &event, &timeout); if (r < 0 ) { LOG(3, "async io getevents returned %li (errno=%s)", r, strerror(errno)); ct->running = 0; rc = PATH_UNCHECKED; } else if (r < 1L) { if (ct->running > timeout_secs || sync) { struct iocb *ios[1] = { &ct->io }; LOG(3, "abort check on timeout"); r = io_cancel(ct->ioctx, ios[0], &event); /* * Only reset ct->running if we really * could abort the pending I/O */ if (r) LOG(3, "io_cancel error %i", errno); else ct->running = 0; rc = PATH_DOWN; } else { LOG(3, "async io pending"); rc = PATH_PENDING; } } else { LOG(3, "io finished %lu/%lu", event.res, event.res2); ct->running = 0; rc = (event.res == ct->blksize) ? PATH_UP : PATH_DOWN; } return rc; } int libcheck_check (struct checker * c) { int ret; struct directio_context * ct = (struct directio_context *)c->context; if (!ct) return PATH_UNCHECKED; ret = check_state(c->fd, ct, c->sync, c->timeout); switch (ret) { case PATH_UNCHECKED: MSG(c, MSG_DIRECTIO_UNKNOWN); break; case PATH_DOWN: MSG(c, MSG_DIRECTIO_DOWN); break; case PATH_UP: MSG(c, MSG_DIRECTIO_UP); break; case PATH_PENDING: MSG(c, MSG_DIRECTIO_PENDING); break; default: break; } return ret; } multipath-tools-0.7.4/libmultipath/checkers/directio.h000066400000000000000000000002611320314174000231210ustar00rootroot00000000000000#ifndef _DIRECTIO_H #define _DIRECTIO_H int directio (struct checker *); int directio_init (struct checker *); void directio_free (struct checker *); #endif /* _DIRECTIO_H */ multipath-tools-0.7.4/libmultipath/checkers/emc_clariion.c000066400000000000000000000142371320314174000237460ustar00rootroot00000000000000/* * Copyright (c) 2004, 2005 Lars Marowsky-Bree */ #include #include #include #include #include #include #include #include #include #include "../libmultipath/sg_include.h" #include "libsg.h" #include "checkers.h" #include "debug.h" #include "memory.h" #define INQUIRY_CMD 0x12 #define INQUIRY_CMDLEN 6 #define HEAVY_CHECK_COUNT 10 /* * Mechanism to track CLARiiON inactive snapshot LUs. * This is done so that we can fail passive paths * to an inactive snapshot LU even though since a * simple read test would return 02/04/03 instead * of 05/25/01 sensekey/ASC/ASCQ data. */ #define IS_INACTIVE_SNAP(c) (c->mpcontext ? \ ((struct emc_clariion_checker_LU_context *) \ (*c->mpcontext))->inactive_snap \ : 0) #define SET_INACTIVE_SNAP(c) if (c->mpcontext) \ ((struct emc_clariion_checker_LU_context *)\ (*c->mpcontext))->inactive_snap = 1 #define CLR_INACTIVE_SNAP(c) if (c->mpcontext) \ ((struct emc_clariion_checker_LU_context *)\ (*c->mpcontext))->inactive_snap = 0 struct emc_clariion_checker_path_context { char wwn[16]; unsigned wwn_set; }; struct emc_clariion_checker_LU_context { int inactive_snap; }; void hexadecimal_to_ascii(char * wwn, char *wwnstr) { int i,j, nbl; for (i=0,j=0;i<16;i++) { wwnstr[j++] = ((nbl = ((wwn[i]&0xf0) >> 4)) <= 9) ? '0' + nbl : 'a' + (nbl - 10); wwnstr[j++] = ((nbl = (wwn[i]&0x0f)) <= 9) ? '0' + nbl : 'a' + (nbl - 10); } wwnstr[32]=0; } int libcheck_init (struct checker * c) { /* * Allocate and initialize the path specific context. */ c->context = MALLOC(sizeof(struct emc_clariion_checker_path_context)); if (!c->context) return 1; ((struct emc_clariion_checker_path_context *)c->context)->wwn_set = 0; /* * Allocate and initialize the multi-path global context. */ if (c->mpcontext && *c->mpcontext == NULL) { void * mpctxt = malloc(sizeof(int)); *c->mpcontext = mpctxt; CLR_INACTIVE_SNAP(c); } return 0; } void libcheck_free (struct checker * c) { free(c->context); } void libcheck_repair (struct checker * c) { return; } int libcheck_check (struct checker * c) { unsigned char sense_buffer[128] = { 0, }; unsigned char sb[SENSE_BUFF_LEN] = { 0, }, *sbb; unsigned char inqCmdBlk[INQUIRY_CMDLEN] = {INQUIRY_CMD, 1, 0xC0, 0, sizeof(sense_buffer), 0}; struct sg_io_hdr io_hdr; struct emc_clariion_checker_path_context * ct = (struct emc_clariion_checker_path_context *)c->context; char wwnstr[33]; int ret; memset(&io_hdr, 0, sizeof (struct sg_io_hdr)); memset(sense_buffer, 0, 128); memset(sb, 0, SENSE_BUFF_LEN); io_hdr.interface_id = 'S'; io_hdr.cmd_len = sizeof (inqCmdBlk); io_hdr.mx_sb_len = sizeof (sb); io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; io_hdr.dxfer_len = sizeof (sense_buffer); io_hdr.dxferp = sense_buffer; io_hdr.cmdp = inqCmdBlk; io_hdr.sbp = sb; io_hdr.timeout = c->timeout * 1000; io_hdr.pack_id = 0; if (ioctl(c->fd, SG_IO, &io_hdr) < 0) { MSG(c, "emc_clariion_checker: sending query command failed"); return PATH_DOWN; } if (io_hdr.info & SG_INFO_OK_MASK) { MSG(c, "emc_clariion_checker: query command indicates error"); return PATH_DOWN; } if (/* Verify the code page - right page & revision */ sense_buffer[1] != 0xc0 || sense_buffer[9] != 0x00) { MSG(c, "emc_clariion_checker: Path unit report page in " "unknown format"); return PATH_DOWN; } if ( /* Effective initiator type */ sense_buffer[27] != 0x03 /* * Failover mode should be set to 1 (PNR failover mode) * or 4 (ALUA failover mode). */ || (((sense_buffer[28] & 0x07) != 0x04) && ((sense_buffer[28] & 0x07) != 0x06)) /* Arraycommpath should be set to 1 */ || (sense_buffer[30] & 0x04) != 0x04) { MSG(c, "emc_clariion_checker: Path not correctly configured " "for failover"); return PATH_DOWN; } if ( /* LUN operations should indicate normal operations */ sense_buffer[48] != 0x00) { MSG(c, "emc_clariion_checker: Path not available for normal " "operations"); return PATH_SHAKY; } if ( /* LUN should at least be bound somewhere and not be LUNZ */ sense_buffer[4] == 0x00) { MSG(c, "emc_clariion_checker: Logical Unit is unbound " "or LUNZ"); return PATH_DOWN; } /* * store the LUN WWN there and compare that it indeed did not * change in between, to protect against the path suddenly * pointing somewhere else. */ if (ct->wwn_set) { if (memcmp(ct->wwn, &sense_buffer[10], 16) != 0) { MSG(c, "emc_clariion_checker: Logical Unit WWN " "has changed!"); return PATH_DOWN; } } else { memcpy(ct->wwn, &sense_buffer[10], 16); ct->wwn_set = 1; } /* * Issue read on active path to determine if inactive snapshot. */ if (sense_buffer[4] == 2) {/* if active path */ unsigned char buf[4096]; memset(buf, 0, 4096); ret = sg_read(c->fd, &buf[0], 4096, sbb = &sb[0], SENSE_BUFF_LEN, c->timeout); if (ret == PATH_DOWN) { hexadecimal_to_ascii(ct->wwn, wwnstr); /* * Check for inactive snapshot LU this way. Must * fail these. */ if (((sbb[2]&0xf) == 5) && (sbb[12] == 0x25) && (sbb[13]==1)) { /* * Do this so that we can fail even the * passive paths which will return * 02/04/03 not 05/25/01 on read. */ SET_INACTIVE_SNAP(c); condlog(3, "emc_clariion_checker: Active " "path to inactive snapshot WWN %s.", wwnstr); } else MSG(c, "emc_clariion_checker: Read " "error for WWN %s. Sense data are " "0x%x/0x%x/0x%x.", wwnstr, sbb[2]&0xf, sbb[12], sbb[13]); } else { MSG(c, "emc_clariion_checker: Active path is " "healthy."); /* * Remove the path from the set of paths to inactive * snapshot LUs if it was in this list since the * snapshot is no longer inactive. */ CLR_INACTIVE_SNAP(c); } } else { if (IS_INACTIVE_SNAP(c)) { hexadecimal_to_ascii(ct->wwn, wwnstr); condlog(3, "emc_clariion_checker: Passive " "path to inactive snapshot WWN %s.", wwnstr); ret = PATH_DOWN; } else { MSG(c, "emc_clariion_checker: Passive path is healthy."); ret = PATH_UP; /* not ghost */ } } return ret; } multipath-tools-0.7.4/libmultipath/checkers/emc_clariion.h000066400000000000000000000003111320314174000237370ustar00rootroot00000000000000#ifndef _EMC_CLARIION_H #define _EMC_CLARIION_H int emc_clariion (struct checker *); int emc_clariion_init (struct checker *); void emc_clariion_free (struct checker *); #endif /* _EMC_CLARIION_H */ multipath-tools-0.7.4/libmultipath/checkers/hp_sw.c000066400000000000000000000066131320314174000224410ustar00rootroot00000000000000/* * Copyright (c) 2005 Christophe Varoqui */ #include #include #include #include #include #include #include #include #include #include "checkers.h" #include "../libmultipath/sg_include.h" #define TUR_CMD_LEN 6 #define INQUIRY_CMDLEN 6 #define INQUIRY_CMD 0x12 #define SENSE_BUFF_LEN 32 #define SCSI_CHECK_CONDITION 0x2 #define SCSI_COMMAND_TERMINATED 0x22 #define SG_ERR_DRIVER_SENSE 0x08 #define RECOVERED_ERROR 0x01 #define MX_ALLOC_LEN 255 #define HEAVY_CHECK_COUNT 10 #define MSG_HP_SW_UP "hp_sw checker reports path is up" #define MSG_HP_SW_DOWN "hp_sw checker reports path is down" #define MSG_HP_SW_GHOST "hp_sw checker reports path is ghost" struct sw_checker_context { void * dummy; }; int libcheck_init (struct checker * c) { return 0; } void libcheck_free (struct checker * c) { return; } void libcheck_repair (struct checker * c) { return; } static int do_inq(int sg_fd, int cmddt, int evpd, unsigned int pg_op, void *resp, int mx_resp_len, int noisy, unsigned int timeout) { unsigned char inqCmdBlk[INQUIRY_CMDLEN] = { INQUIRY_CMD, 0, 0, 0, 0, 0 }; unsigned char sense_b[SENSE_BUFF_LEN]; struct sg_io_hdr io_hdr; if (cmddt) inqCmdBlk[1] |= 2; if (evpd) inqCmdBlk[1] |= 1; inqCmdBlk[2] = (unsigned char) pg_op; inqCmdBlk[3] = (unsigned char)((mx_resp_len >> 8) & 0xff); inqCmdBlk[4] = (unsigned char) (mx_resp_len & 0xff); memset(&io_hdr, 0, sizeof (struct sg_io_hdr)); memset(sense_b, 0, SENSE_BUFF_LEN); io_hdr.interface_id = 'S'; io_hdr.cmd_len = sizeof (inqCmdBlk); io_hdr.mx_sb_len = sizeof (sense_b); io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; io_hdr.dxfer_len = mx_resp_len; io_hdr.dxferp = resp; io_hdr.cmdp = inqCmdBlk; io_hdr.sbp = sense_b; io_hdr.timeout = timeout * 1000; if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) return 1; /* treat SG_ERR here to get rid of sg_err.[ch] */ io_hdr.status &= 0x7e; if ((0 == io_hdr.status) && (0 == io_hdr.host_status) && (0 == io_hdr.driver_status)) return 0; if ((SCSI_CHECK_CONDITION == io_hdr.status) || (SCSI_COMMAND_TERMINATED == io_hdr.status) || (SG_ERR_DRIVER_SENSE == (0xf & io_hdr.driver_status))) { if (io_hdr.sbp && (io_hdr.sb_len_wr > 2)) { int sense_key; unsigned char * sense_buffer = io_hdr.sbp; if (sense_buffer[0] & 0x2) sense_key = sense_buffer[1] & 0xf; else sense_key = sense_buffer[2] & 0xf; if(RECOVERED_ERROR == sense_key) return 0; } } return 1; } static int do_tur (int fd, unsigned int timeout) { unsigned char turCmdBlk[TUR_CMD_LEN] = { 0x00, 0, 0, 0, 0, 0 }; struct sg_io_hdr io_hdr; unsigned char sense_buffer[32]; memset(&io_hdr, 0, sizeof (struct sg_io_hdr)); io_hdr.interface_id = 'S'; io_hdr.cmd_len = sizeof (turCmdBlk); io_hdr.mx_sb_len = sizeof (sense_buffer); io_hdr.dxfer_direction = SG_DXFER_NONE; io_hdr.cmdp = turCmdBlk; io_hdr.sbp = sense_buffer; io_hdr.timeout = timeout * 1000; io_hdr.pack_id = 0; if (ioctl(fd, SG_IO, &io_hdr) < 0) return 1; if (io_hdr.info & SG_INFO_OK_MASK) return 1; return 0; } int libcheck_check(struct checker * c) { char buff[MX_ALLOC_LEN]; if (0 != do_inq(c->fd, 0, 1, 0x80, buff, MX_ALLOC_LEN, 0, c->timeout)) { MSG(c, MSG_HP_SW_DOWN); return PATH_DOWN; } if (do_tur(c->fd, c->timeout)) { MSG(c, MSG_HP_SW_GHOST); return PATH_GHOST; } MSG(c, MSG_HP_SW_UP); return PATH_UP; } multipath-tools-0.7.4/libmultipath/checkers/hp_sw.h000066400000000000000000000002371320314174000224420ustar00rootroot00000000000000#ifndef _HP_SW_H #define _HP_SW_H int hp_sw (struct checker *); int hp_sw_init (struct checker *); void hp_sw_free (struct checker *); #endif /* _HP_SW_H */ multipath-tools-0.7.4/libmultipath/checkers/libsg.c000066400000000000000000000042641320314174000224210ustar00rootroot00000000000000/* * Copyright (c) 2004, 2005 Christophe Varoqui */ #include #include #include #include #include "checkers.h" #include "libsg.h" #include "../libmultipath/sg_include.h" int sg_read (int sg_fd, unsigned char * buff, int buff_len, unsigned char * sense, int sense_len, unsigned int timeout) { /* defaults */ int blocks; long long start_block = 0; int bs = 512; int cdbsz = 10; unsigned char rdCmd[cdbsz]; unsigned char *sbb = sense; struct sg_io_hdr io_hdr; int res; int rd_opcode[] = {0x8, 0x28, 0xa8, 0x88}; int sz_ind; struct stat filestatus; int retry_count = 3; if (fstat(sg_fd, &filestatus) != 0) return PATH_DOWN; bs = (filestatus.st_blksize > 4096)? 4096: filestatus.st_blksize; blocks = buff_len / bs; memset(rdCmd, 0, cdbsz); sz_ind = 1; rdCmd[0] = rd_opcode[sz_ind]; rdCmd[2] = (unsigned char)((start_block >> 24) & 0xff); rdCmd[3] = (unsigned char)((start_block >> 16) & 0xff); rdCmd[4] = (unsigned char)((start_block >> 8) & 0xff); rdCmd[5] = (unsigned char)(start_block & 0xff); rdCmd[7] = (unsigned char)((blocks >> 8) & 0xff); rdCmd[8] = (unsigned char)(blocks & 0xff); memset(&io_hdr, 0, sizeof(struct sg_io_hdr)); io_hdr.interface_id = 'S'; io_hdr.cmd_len = cdbsz; io_hdr.cmdp = rdCmd; io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; io_hdr.dxfer_len = bs * blocks; io_hdr.dxferp = buff; io_hdr.mx_sb_len = sense_len; io_hdr.sbp = sense; io_hdr.timeout = timeout * 1000; io_hdr.pack_id = (int)start_block; retry: memset(sense, 0, sense_len); while (((res = ioctl(sg_fd, SG_IO, &io_hdr)) < 0) && (EINTR == errno)); if (res < 0) { if (ENOMEM == errno) { return PATH_UP; } return PATH_DOWN; } if ((0 == io_hdr.status) && (0 == io_hdr.host_status) && (0 == io_hdr.driver_status)) { return PATH_UP; } else { int key = 0; if (io_hdr.sb_len_wr > 3) { if (sbb[0] == 0x72 || sbb[0] == 0x73) key = sbb[1] & 0x0f; else if (io_hdr.sb_len_wr > 13 && ((sbb[0] & 0x7f) == 0x70 || (sbb[0] & 0x7f) == 0x71)) key = sbb[2] & 0x0f; } /* * Retry if UNIT_ATTENTION check condition. */ if (key == 0x6) { if (--retry_count) goto retry; } return PATH_DOWN; } } multipath-tools-0.7.4/libmultipath/checkers/libsg.h000066400000000000000000000003241320314174000224170ustar00rootroot00000000000000#ifndef _LIBSG_H #define _LIBSG_H #define SENSE_BUFF_LEN 32 int sg_read (int sg_fd, unsigned char * buff, int buff_len, unsigned char * sense, int sense_len, unsigned int timeout); #endif /* _LIBSG_H */ multipath-tools-0.7.4/libmultipath/checkers/rbd.c000066400000000000000000000337151320314174000220730ustar00rootroot00000000000000/* * Copyright (c) 2016 Red Hat * Copyright (c) 2004 Christophe Varoqui * * Code based off of tur.c and ceph's krbd.cc */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "rados/librados.h" #include "structs.h" #include "checkers.h" #include "../libmultipath/debug.h" #include "../libmultipath/util.h" #include "../libmultipath/time-util.h" #include "../libmultipath/util.h" struct rbd_checker_context; typedef int (thread_fn)(struct rbd_checker_context *ct, char *msg); #define RBD_MSG(msg, fmt, args...) snprintf(msg, CHECKER_MSG_LEN, fmt, ##args); #define RBD_FEATURE_EXCLUSIVE_LOCK (1 << 2) struct rbd_checker_context { int rbd_bus_id; char *client_addr; char *config_info; char *snap; char *pool; char *image; char *username; int remapped; int blacklisted; unsigned lock_on_read:1; rados_t cluster; int state; int running; time_t time; thread_fn *fn; pthread_t thread; pthread_mutex_t lock; pthread_cond_t active; pthread_spinlock_t hldr_lock; int holders; char message[CHECKER_MSG_LEN]; }; int libcheck_init(struct checker * c) { struct rbd_checker_context *ct; struct udev_device *block_dev; struct udev_device *bus_dev; struct udev *udev; struct stat sb; const char *block_name, *addr, *config_info, *features_str; const char *image, *pool, *snap, *username; uint64_t features = 0; char sysfs_path[PATH_SIZE]; int ret; ct = malloc(sizeof(struct rbd_checker_context)); if (!ct) return 1; memset(ct, 0, sizeof(struct rbd_checker_context)); ct->holders = 1; pthread_cond_init_mono(&ct->active); pthread_mutex_init(&ct->lock, NULL); pthread_spin_init(&ct->hldr_lock, PTHREAD_PROCESS_PRIVATE); c->context = ct; /* * The rbd block layer sysfs device is not linked to the rbd bus * device that we interact with, so figure that out now. */ if (fstat(c->fd, &sb) != 0) goto free_ct; udev = udev_new(); if (!udev) goto free_ct; block_dev = udev_device_new_from_devnum(udev, 'b', sb.st_rdev); if (!block_dev) goto free_udev; block_name = udev_device_get_sysname(block_dev); ret = sscanf(block_name, "rbd%d", &ct->rbd_bus_id); udev_device_unref(block_dev); if (ret != 1) goto free_udev; snprintf(sysfs_path, sizeof(sysfs_path), "/sys/bus/rbd/devices/%d", ct->rbd_bus_id); bus_dev = udev_device_new_from_syspath(udev, sysfs_path); if (!bus_dev) goto free_udev; addr = udev_device_get_sysattr_value(bus_dev, "client_addr"); if (!addr) { condlog(0, "rbd%d: Could not find client_addr in rbd sysfs. " "Try updating kernel", ct->rbd_bus_id); goto free_dev; } ct->client_addr = strdup(addr); if (!ct->client_addr) goto free_dev; features_str = udev_device_get_sysattr_value(bus_dev, "features"); if (!features_str) goto free_addr; features = strtoll(features_str, NULL, 16); if (!(features & RBD_FEATURE_EXCLUSIVE_LOCK)) { condlog(3, "rbd%d: Exclusive lock not set.", ct->rbd_bus_id); goto free_addr; } config_info = udev_device_get_sysattr_value(bus_dev, "config_info"); if (!config_info) goto free_addr; if (!strstr(config_info, "noshare")) { condlog(3, "rbd%d: Only nonshared clients supported.", ct->rbd_bus_id); goto free_addr; } if (strstr(config_info, "lock_on_read")) ct->lock_on_read = 1; ct->config_info = strdup(config_info); if (!ct->config_info) goto free_addr; username = strstr(config_info, "name="); if (username) { char *end; int len; username += 5; end = strchr(username, ','); if (!end) goto free_info; len = end - username; ct->username = malloc(len + 1); if (!ct->username) goto free_info; strncpy(ct->username, username, len); ct->username[len] = '\0'; } image = udev_device_get_sysattr_value(bus_dev, "name"); if (!image) goto free_username; ct->image = strdup(image); if (!ct->image) goto free_username; pool = udev_device_get_sysattr_value(bus_dev, "pool"); if (!pool) goto free_image; ct->pool = strdup(pool); if (!ct->pool) goto free_image; snap = udev_device_get_sysattr_value(bus_dev, "current_snap"); if (!snap) goto free_pool; if (strcmp("-", snap)) { ct->snap = strdup(snap); if (!ct->snap) goto free_pool; } if (rados_create(&ct->cluster, NULL) < 0) { condlog(0, "rbd%d: Could not create rados cluster", ct->rbd_bus_id); goto free_snap; } if (rados_conf_read_file(ct->cluster, NULL) < 0) { condlog(0, "rbd%d: Could not read rados conf", ct->rbd_bus_id); goto shutdown_rados; } ret = rados_connect(ct->cluster); if (ret < 0) { condlog(0, "rbd%d: Could not connect to rados cluster", ct->rbd_bus_id); goto shutdown_rados; } udev_device_unref(bus_dev); udev_unref(udev); condlog(3, "rbd%d checker init %s %s/%s@%s %s", ct->rbd_bus_id, ct->client_addr, ct->pool, ct->image, ct->snap ? ct->snap : "-", ct->username ? ct->username : "none"); return 0; shutdown_rados: rados_shutdown(ct->cluster); free_snap: if (ct->snap) free(ct->snap); free_pool: free(ct->pool); free_image: free(ct->image); free_username: if (ct->username) free(ct->username); free_info: free(ct->config_info); free_addr: free(ct->client_addr); free_dev: udev_device_unref(bus_dev); free_udev: udev_unref(udev); free_ct: free(ct); return 1; } static void cleanup_context(struct rbd_checker_context *ct) { pthread_mutex_destroy(&ct->lock); pthread_cond_destroy(&ct->active); pthread_spin_destroy(&ct->hldr_lock); rados_shutdown(ct->cluster); if (ct->username) free(ct->username); if (ct->snap) free(ct->snap); free(ct->pool); free(ct->image); free(ct->config_info); free(ct->client_addr); free(ct); } void libcheck_free(struct checker * c) { if (c->context) { struct rbd_checker_context *ct = c->context; int holders; pthread_t thread; pthread_spin_lock(&ct->hldr_lock); ct->holders--; holders = ct->holders; thread = ct->thread; pthread_spin_unlock(&ct->hldr_lock); if (holders) pthread_cancel(thread); else cleanup_context(ct); c->context = NULL; } } static int rbd_is_blacklisted(struct rbd_checker_context *ct, char *msg) { char *addr_tok, *start, *save; char *cmd[2]; char *blklist, *stat; size_t blklist_len, stat_len; int ret; char *end; cmd[0] = "{\"prefix\": \"osd blacklist ls\"}"; cmd[1] = NULL; ret = rados_mon_command(ct->cluster, (const char **)cmd, 1, "", 0, &blklist, &blklist_len, &stat, &stat_len); if (ret < 0) { RBD_MSG(msg, "checker failed: mon command failed %d", ret); return ret; } if (!blklist || !blklist_len) goto free_bufs; /* * parse list of addrs with the format * ipv4:port/nonce date time\n * or * [ipv6]:port/nonce date time\n */ ret = 0; for (start = blklist; ; start = NULL) { addr_tok = strtok_r(start, "\n", &save); if (!addr_tok || !strlen(addr_tok)) break; end = strchr(addr_tok, ' '); if (!end) { RBD_MSG(msg, "checker failed: invalid blacklist %s", addr_tok); break; } *end = '\0'; if (!strcmp(addr_tok, ct->client_addr)) { ct->blacklisted = 1; RBD_MSG(msg, "%s is blacklisted", ct->client_addr); ret = 1; break; } } free_bufs: rados_buffer_free(blklist); rados_buffer_free(stat); return ret; } static int rbd_check(struct rbd_checker_context *ct, char *msg) { if (ct->blacklisted || rbd_is_blacklisted(ct, msg) == 1) return PATH_DOWN; RBD_MSG(msg, "checker reports path is up"); /* * Path may have issues, but the ceph cluster is at least * accepting IO, so we can attempt to do IO. * * TODO: in future versions, we can run other tests to * verify OSDs and networks. */ return PATH_UP; } static int sysfs_write_rbd_bus(const char *which, const char *buf, size_t buf_len) { char sysfs_path[PATH_SIZE]; int fd; int r; /* we require newer kernels so single_major should always be there */ snprintf(sysfs_path, sizeof(sysfs_path), "/sys/bus/rbd/%s_single_major", which); fd = open(sysfs_path, O_WRONLY); if (fd < 0) return -errno; r = safe_write(fd, buf, buf_len); close(fd); return r; } static int rbd_remap(struct rbd_checker_context *ct) { char *argv[11]; pid_t pid; int ret = 0, i = 0; int status; pid = fork(); switch (pid) { case 0: argv[i++] = "rbd"; argv[i++] = "map"; if (ct->lock_on_read) argv[i++] = "-o noshare,lock_on_read"; else argv[i++] = "-o noshare"; if (ct->username) { argv[i++] = "--id"; argv[i++] = ct->username; } argv[i++] = "--pool"; argv[i++] = ct->pool; if (ct->snap) { argv[i++] = "--snap"; argv[i++] = ct->snap; } argv[i++] = ct->image; argv[i] = NULL; ret = execvp(argv[0], argv); condlog(0, "rbd%d: Error executing rbd: %s", ct->rbd_bus_id, strerror(errno)); exit(-1); case -1: condlog(0, "rbd%d: fork failed: %s", ct->rbd_bus_id, strerror(errno)); return -1; default: ret = -1; wait(&status); if (WIFEXITED(status)) { status = WEXITSTATUS(status); if (status == 0) ret = 0; else condlog(0, "rbd%d: failed with %d", ct->rbd_bus_id, status); } } return ret; } static int sysfs_write_rbd_remove(const char *buf, int buf_len) { return sysfs_write_rbd_bus("remove", buf, buf_len); } static int rbd_rm_blacklist(struct rbd_checker_context *ct) { char *cmd[2]; char *stat, *cmd_str; size_t stat_len; int ret; ret = asprintf(&cmd_str, "{\"prefix\": \"osd blacklist\", \"blacklistop\": \"rm\", \"addr\": \"%s\"}", ct->client_addr); if (ret == -1) return -ENOMEM; cmd[0] = cmd_str; cmd[1] = NULL; ret = rados_mon_command(ct->cluster, (const char **)cmd, 1, "", 0, NULL, NULL, &stat, &stat_len); if (ret < 0) { condlog(1, "rbd%d: repair failed to remove blacklist for %s %d", ct->rbd_bus_id, ct->client_addr, ret); goto free_cmd; } condlog(1, "rbd%d: repair rm blacklist for %s", ct->rbd_bus_id, ct->client_addr); free(stat); free_cmd: free(cmd_str); return ret; } static int rbd_repair(struct rbd_checker_context *ct, char *msg) { char del[17]; int ret; if (!ct->blacklisted) return PATH_UP; if (!ct->remapped) { ret = rbd_remap(ct); if (ret) { RBD_MSG(msg, "repair failed to remap. Err %d", ret); return PATH_DOWN; } } ct->remapped = 1; snprintf(del, sizeof(del), "%d force", ct->rbd_bus_id); ret = sysfs_write_rbd_remove(del, strlen(del) + 1); if (ret) { RBD_MSG(msg, "repair failed to clean up. Err %d", ret); return PATH_DOWN; } ret = rbd_rm_blacklist(ct); if (ret) { RBD_MSG(msg, "repair could not remove blacklist entry. Err %d", ret); return PATH_DOWN; } ct->remapped = 0; ct->blacklisted = 0; RBD_MSG(msg, "has been repaired"); return PATH_UP; } #define rbd_thread_cleanup_push(ct) pthread_cleanup_push(cleanup_func, ct) #define rbd_thread_cleanup_pop(ct) pthread_cleanup_pop(1) static void cleanup_func(void *data) { int holders; struct rbd_checker_context *ct = data; pthread_spin_lock(&ct->hldr_lock); ct->holders--; holders = ct->holders; ct->thread = 0; pthread_spin_unlock(&ct->hldr_lock); if (!holders) cleanup_context(ct); } static void *rbd_thread(void *ctx) { struct rbd_checker_context *ct = ctx; int state; condlog(3, "rbd%d: thread starting up", ct->rbd_bus_id); ct->message[0] = '\0'; /* This thread can be canceled, so setup clean up */ rbd_thread_cleanup_push(ct) /* checker start up */ pthread_mutex_lock(&ct->lock); ct->state = PATH_PENDING; pthread_mutex_unlock(&ct->lock); state = ct->fn(ct, ct->message); /* checker done */ pthread_mutex_lock(&ct->lock); ct->state = state; pthread_cond_signal(&ct->active); pthread_mutex_unlock(&ct->lock); condlog(3, "rbd%d: thead finished, state %s", ct->rbd_bus_id, checker_state_name(state)); rbd_thread_cleanup_pop(ct); return ((void *)0); } static void rbd_timeout(struct timespec *tsp) { clock_gettime(CLOCK_MONOTONIC, tsp); tsp->tv_nsec += 1000 * 1000; /* 1 millisecond */ normalize_timespec(tsp); } static int rbd_exec_fn(struct checker *c, thread_fn *fn) { struct rbd_checker_context *ct = c->context; struct timespec tsp; pthread_attr_t attr; int rbd_status, r; if (c->sync) return fn(ct, c->message); /* * Async mode */ r = pthread_mutex_lock(&ct->lock); if (r != 0) { condlog(2, "rbd%d: mutex lock failed with %d", ct->rbd_bus_id, r); MSG(c, "rbd%d: thread failed to initialize", ct->rbd_bus_id); return PATH_WILD; } if (ct->running) { /* Check if checker is still running */ if (ct->thread) { condlog(3, "rbd%d: thread not finished", ct->rbd_bus_id); rbd_status = PATH_PENDING; } else { /* checker done */ ct->running = 0; rbd_status = ct->state; strncpy(c->message, ct->message, CHECKER_MSG_LEN); c->message[CHECKER_MSG_LEN - 1] = '\0'; } pthread_mutex_unlock(&ct->lock); } else { /* Start new checker */ ct->state = PATH_UNCHECKED; ct->fn = fn; pthread_spin_lock(&ct->hldr_lock); ct->holders++; pthread_spin_unlock(&ct->hldr_lock); setup_thread_attr(&attr, 32 * 1024, 1); r = pthread_create(&ct->thread, &attr, rbd_thread, ct); if (r) { pthread_mutex_unlock(&ct->lock); ct->thread = 0; ct->holders--; condlog(3, "rbd%d failed to start rbd thread, using sync mode", ct->rbd_bus_id); return fn(ct, c->message); } pthread_attr_destroy(&attr); rbd_timeout(&tsp); r = pthread_cond_timedwait(&ct->active, &ct->lock, &tsp); rbd_status = ct->state; strncpy(c->message, ct->message,CHECKER_MSG_LEN); c->message[CHECKER_MSG_LEN -1] = '\0'; pthread_mutex_unlock(&ct->lock); if (ct->thread && (rbd_status == PATH_PENDING || rbd_status == PATH_UNCHECKED)) { condlog(3, "rbd%d: thread still running", ct->rbd_bus_id); ct->running = 1; rbd_status = PATH_PENDING; } } return rbd_status; } void libcheck_repair(struct checker * c) { struct rbd_checker_context *ct = c->context; if (!ct || !ct->blacklisted) return; rbd_exec_fn(c, rbd_repair); } int libcheck_check(struct checker * c) { struct rbd_checker_context *ct = c->context; if (!ct) return PATH_UNCHECKED; if (ct->blacklisted) return PATH_DOWN; return rbd_exec_fn(c, rbd_check); } multipath-tools-0.7.4/libmultipath/checkers/rdac.c000066400000000000000000000177031320314174000222340ustar00rootroot00000000000000/* * Copyright (c) 2005 Christophe Varoqui */ #include #include #include #include #include #include #include #include #include #include "checkers.h" #include "debug.h" #include "../libmultipath/sg_include.h" #define INQUIRY_CMDLEN 6 #define INQUIRY_CMD 0x12 #define MODE_SENSE_CMD 0x5a #define MODE_SELECT_CMD 0x55 #define MODE_SEN_SEL_CMDLEN 10 #define SENSE_BUFF_LEN 32 #define SCSI_CHECK_CONDITION 0x2 #define SCSI_COMMAND_TERMINATED 0x22 #define SG_ERR_DRIVER_SENSE 0x08 #define RECOVERED_ERROR 0x01 #define CURRENT_PAGE_CODE_VALUES 0 #define CHANGEABLE_PAGE_CODE_VALUES 1 #define MSG_RDAC_UP "rdac checker reports path is up" #define MSG_RDAC_DOWN "rdac checker reports path is down" #define MSG_RDAC_GHOST "rdac checker reports path is ghost" #define MSG_RDAC_DOWN_TYPE(STR) MSG_RDAC_DOWN": "STR #define RTPG_UNAVAILABLE 0x3 #define RTPG_OFFLINE 0xE #define RTPG_TRANSITIONING 0xF #define RTPG_UNAVAIL_NON_RESPONSIVE 0x2 #define RTPG_UNAVAIL_IN_RESET 0x3 #define RTPG_UNAVAIL_CFW_DL1 0x4 #define RTPG_UNAVAIL_CFW_DL2 0x5 #define RTPG_UNAVAIL_QUIESCED 0x6 #define RTPG_UNAVAIL_SERVICE_MODE 0x7 struct control_mode_page { unsigned char header[8]; unsigned char page_code; unsigned char page_len; unsigned char dontcare0[3]; unsigned char tas_bit; unsigned char dontcare1[6]; }; struct rdac_checker_context { void * dummy; }; int libcheck_init (struct checker * c) { unsigned char cmd[MODE_SEN_SEL_CMDLEN]; unsigned char sense_b[SENSE_BUFF_LEN]; struct sg_io_hdr io_hdr; struct control_mode_page current, changeable; int set = 0; memset(cmd, 0, MODE_SEN_SEL_CMDLEN); cmd[0] = MODE_SENSE_CMD; cmd[1] = 0x08; /* DBD bit on */ cmd[2] = 0xA + (CURRENT_PAGE_CODE_VALUES << 6); cmd[8] = (sizeof(struct control_mode_page) & 0xff); memset(&io_hdr, 0, sizeof(struct sg_io_hdr)); memset(sense_b, 0, SENSE_BUFF_LEN); memset(¤t, 0, sizeof(struct control_mode_page)); io_hdr.interface_id = 'S'; io_hdr.cmd_len = MODE_SEN_SEL_CMDLEN; io_hdr.mx_sb_len = sizeof(sense_b); io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; io_hdr.dxfer_len = (sizeof(struct control_mode_page) & 0xff); io_hdr.dxferp = ¤t; io_hdr.cmdp = cmd; io_hdr.sbp = sense_b; io_hdr.timeout = c->timeout * 1000; if (ioctl(c->fd, SG_IO, &io_hdr) < 0) goto out; /* check the TAS bit to see if it is already set */ if ((current.tas_bit >> 6) & 0x1) { set = 1; goto out; } /* get the changeble values */ cmd[2] = 0xA + (CHANGEABLE_PAGE_CODE_VALUES << 6); io_hdr.dxferp = &changeable; memset(&changeable, 0, sizeof(struct control_mode_page)); if (ioctl(c->fd, SG_IO, &io_hdr) < 0) goto out; /* if TAS bit is not settable exit */ if (((changeable.tas_bit >> 6) & 0x1) == 0) goto out; /* Now go ahead and set it */ memset(cmd, 0, MODE_SEN_SEL_CMDLEN); cmd[0] = MODE_SELECT_CMD; cmd[1] = 0x1; /* set SP bit on */ cmd[8] = (sizeof(struct control_mode_page) & 0xff); /* use the same buffer as current, only set the tas bit */ current.page_code = 0xA; current.page_len = 0xA; current.tas_bit |= (1 << 6); io_hdr.dxfer_direction = SG_DXFER_TO_DEV; io_hdr.dxferp = ¤t; if (ioctl(c->fd, SG_IO, &io_hdr) < 0) goto out; /* Success */ set = 1; out: if (set == 0) condlog(3, "rdac checker failed to set TAS bit"); return 0; } void libcheck_free (struct checker * c) { return; } void libcheck_repair (struct checker * c) { return; } static int do_inq(int sg_fd, unsigned int pg_op, void *resp, int mx_resp_len, unsigned int timeout) { unsigned char inqCmdBlk[INQUIRY_CMDLEN] = { INQUIRY_CMD, 1, 0, 0, 0, 0 }; unsigned char sense_b[SENSE_BUFF_LEN]; struct sg_io_hdr io_hdr; int retry_rdac = 5; retry: inqCmdBlk[2] = (unsigned char) pg_op; inqCmdBlk[4] = (unsigned char) (mx_resp_len & 0xff); memset(&io_hdr, 0, sizeof (struct sg_io_hdr)); memset(sense_b, 0, SENSE_BUFF_LEN); io_hdr.interface_id = 'S'; io_hdr.cmd_len = sizeof (inqCmdBlk); io_hdr.mx_sb_len = sizeof (sense_b); io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; io_hdr.dxfer_len = mx_resp_len; io_hdr.dxferp = resp; io_hdr.cmdp = inqCmdBlk; io_hdr.sbp = sense_b; io_hdr.timeout = timeout * 1000; if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) return 1; /* treat SG_ERR here to get rid of sg_err.[ch] */ io_hdr.status &= 0x7e; if ((0 == io_hdr.status) && (0 == io_hdr.host_status) && (0 == io_hdr.driver_status)) return 0; /* check if we need to retry this error */ if (io_hdr.info & SG_INFO_OK_MASK) { switch (io_hdr.host_status) { case DID_BUS_BUSY: case DID_ERROR: case DID_SOFT_ERROR: case DID_TRANSPORT_DISRUPTED: /* Transport error, retry */ if (--retry_rdac) goto retry; break; default: break; } } if ((SCSI_CHECK_CONDITION == io_hdr.status) || (SCSI_COMMAND_TERMINATED == io_hdr.status) || (SG_ERR_DRIVER_SENSE == (0xf & io_hdr.driver_status))) { if (io_hdr.sbp && (io_hdr.sb_len_wr > 2)) { int sense_key; unsigned char * sense_buffer = io_hdr.sbp; if (sense_buffer[0] & 0x2) sense_key = sense_buffer[1] & 0xf; else sense_key = sense_buffer[2] & 0xf; if (RECOVERED_ERROR == sense_key) return 0; } } return 1; } struct volume_access_inq { char PQ_PDT; char dontcare0[7]; char avtcvp; char vol_ppp; char aas_cur; char vendor_specific_cur; char aas_alt; char vendor_specific_alt; char dontcare1[34]; }; const char *checker_msg_string(struct volume_access_inq *inq) { /* lun not connected */ if (((inq->PQ_PDT & 0xE0) == 0x20) || (inq->PQ_PDT & 0x7f)) return MSG_RDAC_DOWN_TYPE("lun not connected"); /* if no tpg data is available, give the generic path down message */ if (!(inq->avtcvp & 0x10)) return MSG_RDAC_DOWN; /* controller is booting up */ if (((inq->aas_cur & 0x0F) == RTPG_TRANSITIONING) && (inq->aas_alt & 0x0F) != RTPG_TRANSITIONING) return MSG_RDAC_DOWN_TYPE("ctlr is in startup sequence"); /* if not unavailable, give generic message */ if ((inq->aas_cur & 0x0F) != RTPG_UNAVAILABLE) return MSG_RDAC_DOWN; /* target port group unavailable */ switch (inq->vendor_specific_cur) { case RTPG_UNAVAIL_NON_RESPONSIVE: return MSG_RDAC_DOWN_TYPE("non-responsive to queries"); case RTPG_UNAVAIL_IN_RESET: return MSG_RDAC_DOWN_TYPE("ctlr held in reset"); case RTPG_UNAVAIL_CFW_DL1: case RTPG_UNAVAIL_CFW_DL2: return MSG_RDAC_DOWN_TYPE("ctlr firmware downloading"); case RTPG_UNAVAIL_QUIESCED: return MSG_RDAC_DOWN_TYPE("ctlr quiesced by admin request"); case RTPG_UNAVAIL_SERVICE_MODE: return MSG_RDAC_DOWN_TYPE("ctlr is in service mode"); default: return MSG_RDAC_DOWN_TYPE("ctlr is unavailable"); } } int libcheck_check(struct checker * c) { struct volume_access_inq inq; int ret, inqfail; inqfail = 0; memset(&inq, 0, sizeof(struct volume_access_inq)); if (0 != do_inq(c->fd, 0xC9, &inq, sizeof(struct volume_access_inq), c->timeout)) { ret = PATH_DOWN; inqfail = 1; goto done; } else if (((inq.PQ_PDT & 0xE0) == 0x20) || (inq.PQ_PDT & 0x7f)) { /* LUN not connected*/ ret = PATH_DOWN; goto done; } /* If TPGDE bit set, evaluate TPG information */ if ((inq.avtcvp & 0x10)) { switch (inq.aas_cur & 0x0F) { /* Never use the path if it reports unavailable */ case RTPG_UNAVAILABLE: ret = PATH_DOWN; goto done; /* * If both controllers report transitioning, it * means mode select or STPG is being processed. * * If this controller alone is transitioning, it's * booting and we shouldn't use it yet. */ case RTPG_TRANSITIONING: if ((inq.aas_alt & 0xF) != RTPG_TRANSITIONING) { ret = PATH_DOWN; goto done; } break; } } /* If owner set or ioship mode is enabled return PATH_UP always */ if ((inq.avtcvp & 0x1) || ((inq.avtcvp >> 5) & 0x1)) ret = PATH_UP; else ret = PATH_GHOST; done: switch (ret) { case PATH_DOWN: MSG(c, "%s", (inqfail) ? MSG_RDAC_DOWN_TYPE("inquiry failed") : checker_msg_string(&inq)); break; case PATH_UP: MSG(c, MSG_RDAC_UP); break; case PATH_GHOST: MSG(c, MSG_RDAC_GHOST); break; } return ret; } multipath-tools-0.7.4/libmultipath/checkers/rdac.h000066400000000000000000000002261320314174000222310ustar00rootroot00000000000000#ifndef _RDAC_H #define _RDAC_H int rdac(struct checker *); int rdac_init(struct checker *); void rdac_free(struct checker *); #endif /* _RDAC_H */ multipath-tools-0.7.4/libmultipath/checkers/readsector0.c000066400000000000000000000015151320314174000235300ustar00rootroot00000000000000/* * Copyright (c) 2004, 2005 Christophe Varoqui */ #include #include "checkers.h" #include "libsg.h" #define MSG_READSECTOR0_UP "readsector0 checker reports path is up" #define MSG_READSECTOR0_DOWN "readsector0 checker reports path is down" struct readsector0_checker_context { void * dummy; }; int libcheck_init (struct checker * c) { return 0; } void libcheck_free (struct checker * c) { return; } void libcheck_repair (struct checker * c) { return; } int libcheck_check (struct checker * c) { unsigned char buf[4096]; unsigned char sbuf[SENSE_BUFF_LEN]; int ret; ret = sg_read(c->fd, &buf[0], 4096, &sbuf[0], SENSE_BUFF_LEN, c->timeout); switch (ret) { case PATH_DOWN: MSG(c, MSG_READSECTOR0_DOWN); break; case PATH_UP: MSG(c, MSG_READSECTOR0_UP); break; default: break; } return ret; } multipath-tools-0.7.4/libmultipath/checkers/readsector0.h000066400000000000000000000003031320314174000235270ustar00rootroot00000000000000#ifndef _READSECTOR0_H #define _READSECTOR0_H int readsector0 (struct checker *); int readsector0_init (struct checker *); void readsector0_free (struct checker *); #endif /* _READSECTOR0_H */ multipath-tools-0.7.4/libmultipath/checkers/tur.c000066400000000000000000000237001320314174000221270ustar00rootroot00000000000000/* * Some code borrowed from sg-utils. * * Copyright (c) 2004 Christophe Varoqui */ #include #include #include #include #include #include #include #include #include #include #include #include #include "checkers.h" #include "../libmultipath/debug.h" #include "../libmultipath/sg_include.h" #include "../libmultipath/util.h" #include "../libmultipath/time-util.h" #include "../libmultipath/util.h" #define TUR_CMD_LEN 6 #define HEAVY_CHECK_COUNT 10 #define MSG_TUR_UP "tur checker reports path is up" #define MSG_TUR_DOWN "tur checker reports path is down" #define MSG_TUR_GHOST "tur checker reports path is in standby state" #define MSG_TUR_RUNNING "tur checker still running" #define MSG_TUR_TIMEOUT "tur checker timed out" #define MSG_TUR_FAILED "tur checker failed to initialize" struct tur_checker_context { dev_t devt; int state; int running; int fd; unsigned int timeout; time_t time; pthread_t thread; pthread_mutex_t lock; pthread_cond_t active; pthread_spinlock_t hldr_lock; int holders; char message[CHECKER_MSG_LEN]; }; static const char *tur_devt(char *devt_buf, int size, struct tur_checker_context *ct) { dev_t devt; pthread_mutex_lock(&ct->lock); devt = ct->devt; pthread_mutex_unlock(&ct->lock); snprintf(devt_buf, size, "%d:%d", major(devt), minor(devt)); return devt_buf; } int libcheck_init (struct checker * c) { struct tur_checker_context *ct; pthread_mutexattr_t attr; ct = malloc(sizeof(struct tur_checker_context)); if (!ct) return 1; memset(ct, 0, sizeof(struct tur_checker_context)); ct->state = PATH_UNCHECKED; ct->fd = -1; ct->holders = 1; pthread_cond_init_mono(&ct->active); pthread_mutexattr_init(&attr); pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); pthread_mutex_init(&ct->lock, &attr); pthread_mutexattr_destroy(&attr); pthread_spin_init(&ct->hldr_lock, PTHREAD_PROCESS_PRIVATE); c->context = ct; return 0; } static void cleanup_context(struct tur_checker_context *ct) { pthread_mutex_destroy(&ct->lock); pthread_cond_destroy(&ct->active); pthread_spin_destroy(&ct->hldr_lock); free(ct); } void libcheck_free (struct checker * c) { if (c->context) { struct tur_checker_context *ct = c->context; int holders; pthread_t thread; pthread_spin_lock(&ct->hldr_lock); ct->holders--; holders = ct->holders; thread = ct->thread; pthread_spin_unlock(&ct->hldr_lock); if (holders) pthread_cancel(thread); else cleanup_context(ct); c->context = NULL; } return; } void libcheck_repair (struct checker * c) { return; } #define TUR_MSG(fmt, args...) \ do { \ char msg[CHECKER_MSG_LEN]; \ \ snprintf(msg, sizeof(msg), fmt, ##args); \ copy_message(cb_arg, msg); \ } while (0) static int tur_check(int fd, unsigned int timeout, void (*copy_message)(void *, const char *), void *cb_arg) { struct sg_io_hdr io_hdr; unsigned char turCmdBlk[TUR_CMD_LEN] = { 0x00, 0, 0, 0, 0, 0 }; unsigned char sense_buffer[32]; int retry_tur = 5; retry: memset(&io_hdr, 0, sizeof (struct sg_io_hdr)); memset(&sense_buffer, 0, 32); io_hdr.interface_id = 'S'; io_hdr.cmd_len = sizeof (turCmdBlk); io_hdr.mx_sb_len = sizeof (sense_buffer); io_hdr.dxfer_direction = SG_DXFER_NONE; io_hdr.cmdp = turCmdBlk; io_hdr.sbp = sense_buffer; io_hdr.timeout = timeout * 1000; io_hdr.pack_id = 0; if (ioctl(fd, SG_IO, &io_hdr) < 0) { TUR_MSG(MSG_TUR_DOWN); return PATH_DOWN; } if ((io_hdr.status & 0x7e) == 0x18) { /* * SCSI-3 arrays might return * reservation conflict on TUR */ TUR_MSG(MSG_TUR_UP); return PATH_UP; } if (io_hdr.info & SG_INFO_OK_MASK) { int key = 0, asc, ascq; switch (io_hdr.host_status) { case DID_OK: case DID_NO_CONNECT: case DID_BAD_TARGET: case DID_ABORT: case DID_TRANSPORT_FAILFAST: break; default: /* Driver error, retry */ if (--retry_tur) goto retry; break; } if (io_hdr.sb_len_wr > 3) { if (io_hdr.sbp[0] == 0x72 || io_hdr.sbp[0] == 0x73) { key = io_hdr.sbp[1] & 0x0f; asc = io_hdr.sbp[2]; ascq = io_hdr.sbp[3]; } else if (io_hdr.sb_len_wr > 13 && ((io_hdr.sbp[0] & 0x7f) == 0x70 || (io_hdr.sbp[0] & 0x7f) == 0x71)) { key = io_hdr.sbp[2] & 0x0f; asc = io_hdr.sbp[12]; ascq = io_hdr.sbp[13]; } } if (key == 0x6) { /* Unit Attention, retry */ if (--retry_tur) goto retry; } else if (key == 0x2) { /* Not Ready */ /* Note: Other ALUA states are either UP or DOWN */ if( asc == 0x04 && ascq == 0x0b){ /* * LOGICAL UNIT NOT ACCESSIBLE, * TARGET PORT IN STANDBY STATE */ TUR_MSG(MSG_TUR_GHOST); return PATH_GHOST; } } TUR_MSG(MSG_TUR_DOWN); return PATH_DOWN; } TUR_MSG(MSG_TUR_UP); return PATH_UP; } #define tur_thread_cleanup_push(ct) pthread_cleanup_push(cleanup_func, ct) #define tur_thread_cleanup_pop(ct) pthread_cleanup_pop(1) static void cleanup_func(void *data) { int holders; struct tur_checker_context *ct = data; pthread_spin_lock(&ct->hldr_lock); ct->holders--; holders = ct->holders; ct->thread = 0; pthread_spin_unlock(&ct->hldr_lock); if (!holders) cleanup_context(ct); } static int tur_running(struct tur_checker_context *ct) { pthread_t thread; pthread_spin_lock(&ct->hldr_lock); thread = ct->thread; pthread_spin_unlock(&ct->hldr_lock); return thread != 0; } static void copy_msg_to_tcc(void *ct_p, const char *msg) { struct tur_checker_context *ct = ct_p; pthread_mutex_lock(&ct->lock); strlcpy(ct->message, msg, sizeof(ct->message)); pthread_mutex_unlock(&ct->lock); } static void *tur_thread(void *ctx) { struct tur_checker_context *ct = ctx; int state; char devt[32]; condlog(3, "%s: tur checker starting up", tur_devt(devt, sizeof(devt), ct)); /* This thread can be canceled, so setup clean up */ tur_thread_cleanup_push(ct); /* TUR checker start up */ pthread_mutex_lock(&ct->lock); ct->state = PATH_PENDING; ct->message[0] = '\0'; pthread_mutex_unlock(&ct->lock); state = tur_check(ct->fd, ct->timeout, copy_msg_to_tcc, ct->message); pthread_testcancel(); /* TUR checker done */ pthread_mutex_lock(&ct->lock); ct->state = state; pthread_cond_signal(&ct->active); pthread_mutex_unlock(&ct->lock); condlog(3, "%s: tur checker finished, state %s", tur_devt(devt, sizeof(devt), ct), checker_state_name(state)); tur_thread_cleanup_pop(ct); return ((void *)0); } static void tur_timeout(struct timespec *tsp) { clock_gettime(CLOCK_MONOTONIC, tsp); tsp->tv_nsec += 1000 * 1000; /* 1 millisecond */ normalize_timespec(tsp); } static void tur_set_async_timeout(struct checker *c) { struct tur_checker_context *ct = c->context; struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); ct->time = now.tv_sec + c->timeout; } static int tur_check_async_timeout(struct checker *c) { struct tur_checker_context *ct = c->context; struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); return (now.tv_sec > ct->time); } static void copy_msg_to_checker(void *c_p, const char *msg) { struct checker *c = c_p; strlcpy(c->message, msg, sizeof(c->message)); } int libcheck_check(struct checker * c) { struct tur_checker_context *ct = c->context; struct timespec tsp; struct stat sb; pthread_attr_t attr; int tur_status, r; char devt[32]; if (!ct) return PATH_UNCHECKED; if (fstat(c->fd, &sb) == 0) { pthread_mutex_lock(&ct->lock); ct->devt = sb.st_rdev; pthread_mutex_unlock(&ct->lock); } if (c->sync) return tur_check(c->fd, c->timeout, copy_msg_to_checker, c); /* * Async mode */ r = pthread_mutex_lock(&ct->lock); if (r != 0) { condlog(2, "%s: tur mutex lock failed with %d", tur_devt(devt, sizeof(devt), ct), r); MSG(c, MSG_TUR_FAILED); return PATH_WILD; } if (ct->running) { /* * Check if TUR checker is still running. Hold hldr_lock * around the pthread_cancel() call to avoid that * pthread_cancel() gets called after the (detached) TUR * thread has exited. */ pthread_spin_lock(&ct->hldr_lock); if (ct->thread) { if (tur_check_async_timeout(c)) { condlog(3, "%s: tur checker timeout", tur_devt(devt, sizeof(devt), ct)); pthread_cancel(ct->thread); ct->running = 0; MSG(c, MSG_TUR_TIMEOUT); tur_status = PATH_TIMEOUT; } else { condlog(3, "%s: tur checker not finished", tur_devt(devt, sizeof(devt), ct)); ct->running++; tur_status = PATH_PENDING; } } else { /* TUR checker done */ ct->running = 0; tur_status = ct->state; strlcpy(c->message, ct->message, sizeof(c->message)); } pthread_spin_unlock(&ct->hldr_lock); pthread_mutex_unlock(&ct->lock); } else { if (tur_running(ct)) { /* pthread cancel failed. continue in sync mode */ pthread_mutex_unlock(&ct->lock); condlog(3, "%s: tur thread not responding", tur_devt(devt, sizeof(devt), ct)); return PATH_TIMEOUT; } /* Start new TUR checker */ ct->state = PATH_UNCHECKED; ct->fd = c->fd; ct->timeout = c->timeout; pthread_spin_lock(&ct->hldr_lock); ct->holders++; pthread_spin_unlock(&ct->hldr_lock); tur_set_async_timeout(c); setup_thread_attr(&attr, 32 * 1024, 1); r = pthread_create(&ct->thread, &attr, tur_thread, ct); pthread_attr_destroy(&attr); if (r) { pthread_spin_lock(&ct->hldr_lock); ct->holders--; pthread_spin_unlock(&ct->hldr_lock); pthread_mutex_unlock(&ct->lock); ct->thread = 0; condlog(3, "%s: failed to start tur thread, using" " sync mode", tur_devt(devt, sizeof(devt), ct)); return tur_check(c->fd, c->timeout, copy_msg_to_checker, c); } tur_timeout(&tsp); r = pthread_cond_timedwait(&ct->active, &ct->lock, &tsp); tur_status = ct->state; strlcpy(c->message, ct->message, sizeof(c->message)); pthread_mutex_unlock(&ct->lock); if (tur_running(ct) && (tur_status == PATH_PENDING || tur_status == PATH_UNCHECKED)) { condlog(3, "%s: tur checker still running", tur_devt(devt, sizeof(devt), ct)); ct->running = 1; tur_status = PATH_PENDING; } } return tur_status; } multipath-tools-0.7.4/libmultipath/checkers/tur.h000066400000000000000000000002231320314174000221270ustar00rootroot00000000000000#ifndef _TUR_H #define _TUR_H int tur (struct checker *); int tur_init (struct checker *); void tur_free (struct checker *); #endif /* _TUR_H */ multipath-tools-0.7.4/libmultipath/config.c000066400000000000000000000355101320314174000207750ustar00rootroot00000000000000/* * Copyright (c) 2004, 2005 Christophe Varoqui * Copyright (c) 2005 Benjamin Marzinski, Redhat * Copyright (c) 2005 Edward Goggin, EMC */ #include #include #include #include #include #include #include "checkers.h" #include "memory.h" #include "util.h" #include "debug.h" #include "parser.h" #include "dict.h" #include "hwtable.h" #include "vector.h" #include "structs.h" #include "config.h" #include "blacklist.h" #include "defaults.h" #include "prio.h" #include "devmapper.h" #include "mpath_cmd.h" #include "propsel.h" static int hwe_strmatch (struct hwentry *hwe1, struct hwentry *hwe2) { if ((hwe2->vendor && !hwe1->vendor) || (hwe1->vendor && (!hwe2->vendor || strcmp(hwe1->vendor, hwe2->vendor)))) return 1; if ((hwe2->product && !hwe1->product) || (hwe1->product && (!hwe2->product || strcmp(hwe1->product, hwe2->product)))) return 1; if ((hwe2->revision && !hwe1->revision) || (hwe1->revision && (!hwe2->revision || strcmp(hwe1->revision, hwe2->revision)))) return 1; return 0; } static struct hwentry * find_hwe_strmatch (vector hwtable, struct hwentry *hwe) { int i; struct hwentry *tmp, *ret = NULL; vector_foreach_slot (hwtable, tmp, i) { if (hwe_strmatch(tmp, hwe)) continue; ret = tmp; break; } return ret; } static int hwe_regmatch (struct hwentry *hwe1, struct hwentry *hwe2) { regex_t vre, pre, rre; int retval = 1; if (hwe1->vendor && regcomp(&vre, hwe1->vendor, REG_EXTENDED|REG_NOSUB)) goto out; if (hwe1->product && regcomp(&pre, hwe1->product, REG_EXTENDED|REG_NOSUB)) goto out_vre; if (hwe1->revision && regcomp(&rre, hwe1->revision, REG_EXTENDED|REG_NOSUB)) goto out_pre; if ((hwe2->vendor || hwe2->product || hwe2->revision) && (!hwe1->vendor || !hwe2->vendor || !regexec(&vre, hwe2->vendor, 0, NULL, 0)) && (!hwe1->product || !hwe2->product || !regexec(&pre, hwe2->product, 0, NULL, 0)) && (!hwe1->revision || !hwe2->revision || !regexec(&rre, hwe2->revision, 0, NULL, 0))) retval = 0; if (hwe1->revision) regfree(&rre); out_pre: if (hwe1->product) regfree(&pre); out_vre: if (hwe1->vendor) regfree(&vre); out: return retval; } struct hwentry * find_hwe (vector hwtable, char * vendor, char * product, char * revision) { int i; struct hwentry hwe, *tmp, *ret = NULL; hwe.vendor = vendor; hwe.product = product; hwe.revision = revision; /* * Search backwards here. * User modified entries are attached at the end of * the list, so we have to check them first before * continuing to the generic entries */ vector_foreach_slot_backwards (hwtable, tmp, i) { if (hwe_regmatch(tmp, &hwe)) continue; ret = tmp; break; } return ret; } struct mpentry *find_mpe(vector mptable, char *wwid) { int i; struct mpentry * mpe; if (!wwid) return NULL; vector_foreach_slot (mptable, mpe, i) if (mpe->wwid && !strcmp(mpe->wwid, wwid)) return mpe; return NULL; } char *get_mpe_wwid(vector mptable, char *alias) { int i; struct mpentry * mpe; if (!alias) return NULL; vector_foreach_slot (mptable, mpe, i) if (mpe->alias && strcmp(mpe->alias, alias) == 0) return mpe->wwid; return NULL; } void free_hwe (struct hwentry * hwe) { if (!hwe) return; if (hwe->vendor) FREE(hwe->vendor); if (hwe->product) FREE(hwe->product); if (hwe->revision) FREE(hwe->revision); if (hwe->getuid) FREE(hwe->getuid); if (hwe->uid_attribute) FREE(hwe->uid_attribute); if (hwe->features) FREE(hwe->features); if (hwe->hwhandler) FREE(hwe->hwhandler); if (hwe->selector) FREE(hwe->selector); if (hwe->checker_name) FREE(hwe->checker_name); if (hwe->prio_name) FREE(hwe->prio_name); if (hwe->prio_args) FREE(hwe->prio_args); if (hwe->alias_prefix) FREE(hwe->alias_prefix); if (hwe->bl_product) FREE(hwe->bl_product); FREE(hwe); } void free_hwtable (vector hwtable) { int i; struct hwentry * hwe; if (!hwtable) return; vector_foreach_slot (hwtable, hwe, i) free_hwe(hwe); vector_free(hwtable); } void free_mpe (struct mpentry * mpe) { if (!mpe) return; if (mpe->wwid) FREE(mpe->wwid); if (mpe->selector) FREE(mpe->selector); if (mpe->getuid) FREE(mpe->getuid); if (mpe->uid_attribute) FREE(mpe->uid_attribute); if (mpe->alias) FREE(mpe->alias); if (mpe->prio_name) FREE(mpe->prio_name); if (mpe->prio_args) FREE(mpe->prio_args); FREE(mpe); } void free_mptable (vector mptable) { int i; struct mpentry * mpe; if (!mptable) return; vector_foreach_slot (mptable, mpe, i) free_mpe(mpe); vector_free(mptable); } struct mpentry * alloc_mpe (void) { struct mpentry * mpe = (struct mpentry *) MALLOC(sizeof(struct mpentry)); return mpe; } struct hwentry * alloc_hwe (void) { struct hwentry * hwe = (struct hwentry *) MALLOC(sizeof(struct hwentry)); return hwe; } static char * set_param_str(char * str) { char * dst; int len; if (!str) return NULL; len = strlen(str); if (!len) return NULL; dst = (char *)MALLOC(len + 1); if (!dst) return NULL; strcpy(dst, str); return dst; } #define merge_str(s) \ if (!dst->s && src->s) { \ if (!(dst->s = set_param_str(src->s))) \ return 1; \ } #define merge_num(s) \ if (!dst->s && src->s) \ dst->s = src->s static int merge_hwe (struct hwentry * dst, struct hwentry * src) { char id[SCSI_VENDOR_SIZE+SCSI_PRODUCT_SIZE]; merge_str(vendor); merge_str(product); merge_str(revision); merge_str(getuid); merge_str(uid_attribute); merge_str(features); merge_str(hwhandler); merge_str(selector); merge_str(checker_name); merge_str(prio_name); merge_str(prio_args); merge_str(alias_prefix); merge_str(bl_product); merge_num(pgpolicy); merge_num(pgfailback); merge_num(rr_weight); merge_num(no_path_retry); merge_num(minio); merge_num(minio_rq); merge_num(flush_on_last_del); merge_num(fast_io_fail); merge_num(dev_loss); merge_num(user_friendly_names); merge_num(retain_hwhandler); merge_num(detect_prio); merge_num(detect_checker); merge_num(deferred_remove); merge_num(delay_watch_checks); merge_num(delay_wait_checks); merge_num(skip_kpartx); merge_num(max_sectors_kb); snprintf(id, sizeof(id), "%s/%s", dst->vendor, dst->product); reconcile_features_with_options(id, &dst->features, &dst->no_path_retry, &dst->retain_hwhandler); return 0; } int store_hwe (vector hwtable, struct hwentry * dhwe) { struct hwentry * hwe; if (find_hwe_strmatch(hwtable, dhwe)) return 0; if (!(hwe = alloc_hwe())) return 1; if (!dhwe->vendor || !(hwe->vendor = set_param_str(dhwe->vendor))) goto out; if (!dhwe->product || !(hwe->product = set_param_str(dhwe->product))) goto out; if (dhwe->revision && !(hwe->revision = set_param_str(dhwe->revision))) goto out; if (dhwe->uid_attribute && !(hwe->uid_attribute = set_param_str(dhwe->uid_attribute))) goto out; if (dhwe->getuid && !(hwe->getuid = set_param_str(dhwe->getuid))) goto out; if (dhwe->features && !(hwe->features = set_param_str(dhwe->features))) goto out; if (dhwe->hwhandler && !(hwe->hwhandler = set_param_str(dhwe->hwhandler))) goto out; if (dhwe->selector && !(hwe->selector = set_param_str(dhwe->selector))) goto out; if (dhwe->checker_name && !(hwe->checker_name = set_param_str(dhwe->checker_name))) goto out; if (dhwe->prio_name && !(hwe->prio_name = set_param_str(dhwe->prio_name))) goto out; if (dhwe->prio_args && !(hwe->prio_args = set_param_str(dhwe->prio_args))) goto out; if (dhwe->alias_prefix && !(hwe->alias_prefix = set_param_str(dhwe->alias_prefix))) goto out; hwe->pgpolicy = dhwe->pgpolicy; hwe->pgfailback = dhwe->pgfailback; hwe->rr_weight = dhwe->rr_weight; hwe->no_path_retry = dhwe->no_path_retry; hwe->minio = dhwe->minio; hwe->minio_rq = dhwe->minio_rq; hwe->flush_on_last_del = dhwe->flush_on_last_del; hwe->fast_io_fail = dhwe->fast_io_fail; hwe->dev_loss = dhwe->dev_loss; hwe->user_friendly_names = dhwe->user_friendly_names; hwe->retain_hwhandler = dhwe->retain_hwhandler; hwe->detect_prio = dhwe->detect_prio; hwe->detect_checker = dhwe->detect_checker; if (dhwe->bl_product && !(hwe->bl_product = set_param_str(dhwe->bl_product))) goto out; if (!vector_alloc_slot(hwtable)) goto out; vector_set_slot(hwtable, hwe); return 0; out: free_hwe(hwe); return 1; } static void factorize_hwtable (vector hw, int n) { struct hwentry *hwe1, *hwe2; int i, j; restart: vector_foreach_slot(hw, hwe1, i) { if (i == n) break; j = n; vector_foreach_slot_after(hw, hwe2, j) { /* drop invalid device configs */ if (!hwe2->vendor || !hwe2->product) { condlog(0, "device config missing vendor or product parameter"); vector_del_slot(hw, j--); free_hwe(hwe2); continue; } if (hwe_regmatch(hwe1, hwe2)) continue; /* dup */ merge_hwe(hwe2, hwe1); if (hwe_strmatch(hwe2, hwe1) == 0) { vector_del_slot(hw, i); free_hwe(hwe1); n -= 1; /* * Play safe here; we have modified * the original vector so the outer * vector_foreach_slot() might * become confused. */ goto restart; } } } return; } struct config * alloc_config (void) { return (struct config *)MALLOC(sizeof(struct config)); } void free_config (struct config * conf) { if (!conf) return; if (conf->multipath_dir) FREE(conf->multipath_dir); if (conf->selector) FREE(conf->selector); if (conf->uid_attribute) FREE(conf->uid_attribute); if (conf->uid_attrs) FREE(conf->uid_attrs); if (conf->getuid) FREE(conf->getuid); if (conf->features) FREE(conf->features); if (conf->hwhandler) FREE(conf->hwhandler); if (conf->bindings_file) FREE(conf->bindings_file); if (conf->wwids_file) FREE(conf->wwids_file); if (conf->prkeys_file) FREE(conf->prkeys_file); if (conf->prio_name) FREE(conf->prio_name); if (conf->alias_prefix) FREE(conf->alias_prefix); if (conf->partition_delim) FREE(conf->partition_delim); if (conf->prio_args) FREE(conf->prio_args); if (conf->checker_name) FREE(conf->checker_name); if (conf->config_dir) FREE(conf->config_dir); free_blacklist(conf->blist_devnode); free_blacklist(conf->blist_wwid); free_blacklist(conf->blist_property); free_blacklist_device(conf->blist_device); free_blacklist(conf->elist_devnode); free_blacklist(conf->elist_wwid); free_blacklist(conf->elist_property); free_blacklist_device(conf->elist_device); free_mptable(conf->mptable); free_hwtable(conf->hwtable); free_hwe(conf->overrides); free_keywords(conf->keywords); FREE(conf); } /* if multipath fails to process the config directory, it should continue, * with just a warning message */ static void process_config_dir(struct config *conf, vector keywords, char *dir) { struct dirent **namelist; int i, n; char path[LINE_MAX]; int old_hwtable_size; if (dir[0] != '/') { condlog(1, "config_dir '%s' must be a fully qualified path", dir); return; } n = scandir(dir, &namelist, NULL, alphasort); if (n < 0) { if (errno == ENOENT) condlog(3, "No configuration dir '%s'", dir); else condlog(0, "couldn't open configuration dir '%s': %s", dir, strerror(errno)); return; } for (i = 0; i < n; i++) { if (!strstr(namelist[i]->d_name, ".conf")) continue; old_hwtable_size = VECTOR_SIZE(conf->hwtable); snprintf(path, LINE_MAX, "%s/%s", dir, namelist[i]->d_name); path[LINE_MAX-1] = '\0'; process_file(conf, path); if (VECTOR_SIZE(conf->hwtable) > old_hwtable_size) factorize_hwtable(conf->hwtable, old_hwtable_size); } } struct config * load_config (char * file) { struct config *conf = alloc_config(); if (!conf) return NULL; /* * internal defaults */ if (!conf->verbosity) conf->verbosity = DEFAULT_VERBOSITY; get_sys_max_fds(&conf->max_fds); conf->bindings_file = set_default(DEFAULT_BINDINGS_FILE); conf->wwids_file = set_default(DEFAULT_WWIDS_FILE); conf->prkeys_file = set_default(DEFAULT_PRKEYS_FILE); conf->multipath_dir = set_default(DEFAULT_MULTIPATHDIR); conf->attribute_flags = 0; conf->reassign_maps = DEFAULT_REASSIGN_MAPS; conf->checkint = DEFAULT_CHECKINT; conf->max_checkint = 0; conf->force_sync = DEFAULT_FORCE_SYNC; conf->partition_delim = DEFAULT_PARTITION_DELIM; conf->processed_main_config = 0; conf->find_multipaths = DEFAULT_FIND_MULTIPATHS; conf->uxsock_timeout = DEFAULT_REPLY_TIMEOUT; conf->retrigger_tries = DEFAULT_RETRIGGER_TRIES; conf->retrigger_delay = DEFAULT_RETRIGGER_DELAY; conf->uev_wait_timeout = DEFAULT_UEV_WAIT_TIMEOUT; conf->disable_changed_wwids = DEFAULT_DISABLE_CHANGED_WWIDS; conf->remove_retries = 0; /* * preload default hwtable */ if (conf->hwtable == NULL) { conf->hwtable = vector_alloc(); if (!conf->hwtable) goto out; } if (setup_default_hwtable(conf->hwtable)) goto out; /* * read the config file */ conf->keywords = vector_alloc(); init_keywords(conf->keywords); if (filepresent(file)) { int builtin_hwtable_size; builtin_hwtable_size = VECTOR_SIZE(conf->hwtable); if (process_file(conf, file)) { condlog(0, "error parsing config file"); goto out; } if (VECTOR_SIZE(conf->hwtable) > builtin_hwtable_size) { /* * remove duplica in hwtable. config file * takes precedence over build-in hwtable */ factorize_hwtable(conf->hwtable, builtin_hwtable_size); } } conf->processed_main_config = 1; if (conf->config_dir == NULL) conf->config_dir = set_default(DEFAULT_CONFIG_DIR); if (conf->config_dir && conf->config_dir[0] != '\0') process_config_dir(conf, conf->keywords, conf->config_dir); /* * fill the voids left in the config file */ if (conf->max_checkint == 0) conf->max_checkint = MAX_CHECKINT(conf->checkint); if (conf->blist_devnode == NULL) { conf->blist_devnode = vector_alloc(); if (!conf->blist_devnode) goto out; } if (conf->blist_wwid == NULL) { conf->blist_wwid = vector_alloc(); if (!conf->blist_wwid) goto out; } if (conf->blist_device == NULL) { conf->blist_device = vector_alloc(); if (!conf->blist_device) goto out; } if (conf->blist_property == NULL) { conf->blist_property = vector_alloc(); if (!conf->blist_property) goto out; } if (conf->elist_devnode == NULL) { conf->elist_devnode = vector_alloc(); if (!conf->elist_devnode) goto out; } if (conf->elist_wwid == NULL) { conf->elist_wwid = vector_alloc(); if (!conf->elist_wwid) goto out; } if (conf->elist_device == NULL) { conf->elist_device = vector_alloc(); if (!conf->elist_device) goto out; } if (conf->elist_property == NULL) { conf->elist_property = vector_alloc(); if (!conf->elist_property) goto out; } if (setup_default_blist(conf)) goto out; if (conf->mptable == NULL) { conf->mptable = vector_alloc(); if (!conf->mptable) goto out; } if (conf->bindings_file == NULL) conf->bindings_file = set_default(DEFAULT_BINDINGS_FILE); if (!conf->multipath_dir || !conf->bindings_file || !conf->wwids_file || !conf->prkeys_file) goto out; return conf; out: free_config(conf); return NULL; } multipath-tools-0.7.4/libmultipath/config.h000066400000000000000000000113171320314174000210010ustar00rootroot00000000000000#ifndef _CONFIG_H #define _CONFIG_H #include #include #include #include #include "byteorder.h" #define ORIGIN_DEFAULT 0 #define ORIGIN_CONFIG 1 /* * In kernel, fast_io_fail == 0 means immediate failure on rport delete. * OTOH '0' means not-configured in various places in multipath-tools. */ #define MP_FAST_IO_FAIL_UNSET (0) #define MP_FAST_IO_FAIL_OFF (-1) #define MP_FAST_IO_FAIL_ZERO (-2) enum devtypes { DEV_NONE, DEV_DEVT, DEV_DEVNODE, DEV_DEVMAP, DEV_UEVENT }; enum mpath_cmds { CMD_NONE, CMD_CREATE, CMD_DRY_RUN, CMD_LIST_SHORT, CMD_LIST_LONG, CMD_VALID_PATH, CMD_REMOVE_WWID, CMD_RESET_WWIDS, CMD_ADD_WWID, CMD_USABLE_PATHS, }; enum force_reload_types { FORCE_RELOAD_NONE, FORCE_RELOAD_YES, FORCE_RELOAD_WEAK, }; struct hwentry { char * vendor; char * product; char * revision; char * uid_attribute; char * getuid; char * features; char * hwhandler; char * selector; char * checker_name; char * prio_name; char * prio_args; char * alias_prefix; int pgpolicy; int pgfailback; int rr_weight; int no_path_retry; int minio; int minio_rq; int flush_on_last_del; int fast_io_fail; unsigned int dev_loss; int user_friendly_names; int retain_hwhandler; int detect_prio; int detect_checker; int deferred_remove; int delay_watch_checks; int delay_wait_checks; int marginal_path_err_sample_time; int marginal_path_err_rate_threshold; int marginal_path_err_recheck_gap_time; int marginal_path_double_failed_time; int skip_kpartx; int max_sectors_kb; char * bl_product; }; struct mpentry { char * wwid; char * alias; char * uid_attribute; char * getuid; char * selector; char * features; char * prio_name; char * prio_args; int prkey_source; struct be64 reservation_key; int pgpolicy; int pgfailback; int rr_weight; int no_path_retry; int minio; int minio_rq; int flush_on_last_del; int attribute_flags; int user_friendly_names; int deferred_remove; int delay_watch_checks; int delay_wait_checks; int marginal_path_err_sample_time; int marginal_path_err_rate_threshold; int marginal_path_err_recheck_gap_time; int marginal_path_double_failed_time; int skip_kpartx; int max_sectors_kb; uid_t uid; gid_t gid; mode_t mode; }; struct config { struct rcu_head rcu; int verbosity; int pgpolicy_flag; int pgpolicy; int minio; int minio_rq; int checkint; int max_checkint; int pgfailback; int remove; int rr_weight; int no_path_retry; int user_friendly_names; int bindings_read_only; int max_fds; int force_reload; int queue_without_daemon; int ignore_wwids; int checker_timeout; int flush_on_last_del; int attribute_flags; int fast_io_fail; unsigned int dev_loss; int log_checker_err; int allow_queueing; int find_multipaths; uid_t uid; gid_t gid; mode_t mode; int reassign_maps; int retain_hwhandler; int detect_prio; int detect_checker; int force_sync; int deferred_remove; int processed_main_config; int delay_watch_checks; int delay_wait_checks; int marginal_path_err_sample_time; int marginal_path_err_rate_threshold; int marginal_path_err_recheck_gap_time; int marginal_path_double_failed_time; int uxsock_timeout; int strict_timing; int retrigger_tries; int retrigger_delay; int ignore_new_devs; int delayed_reconfig; int uev_wait_timeout; int skip_kpartx; int disable_changed_wwids; int remove_retries; int max_sectors_kb; unsigned int version[3]; char * multipath_dir; char * selector; char * uid_attrs; char * uid_attribute; char * getuid; char * features; char * hwhandler; char * bindings_file; char * wwids_file; char * prkeys_file; char * prio_name; char * prio_args; char * checker_name; char * alias_prefix; char * partition_delim; char * config_dir; int prkey_source; struct be64 reservation_key; vector keywords; vector mptable; vector hwtable; struct hwentry *overrides; vector blist_devnode; vector blist_wwid; vector blist_device; vector blist_property; vector elist_devnode; vector elist_wwid; vector elist_device; vector elist_property; }; extern struct udev * udev; struct hwentry * find_hwe (vector hwtable, char * vendor, char * product, char *revision); struct mpentry * find_mpe (vector mptable, char * wwid); char * get_mpe_wwid (vector mptable, char * alias); struct hwentry * alloc_hwe (void); struct mpentry * alloc_mpe (void); void free_hwe (struct hwentry * hwe); void free_hwtable (vector hwtable); void free_mpe (struct mpentry * mpe); void free_mptable (vector mptable); int store_hwe (vector hwtable, struct hwentry *); struct config *load_config (char * file); struct config * alloc_config (void); void free_config (struct config * conf); extern struct config *get_multipath_config(void); extern void put_multipath_config(struct config *); #endif multipath-tools-0.7.4/libmultipath/configure.c000066400000000000000000000757021320314174000215200ustar00rootroot00000000000000/* * Copyright (c) 2003, 2004, 2005 Christophe Varoqui * Copyright (c) 2005 Benjamin Marzinski, Redhat * Copyright (c) 2005 Kiyoshi Ueda, NEC * Copyright (c) 2005 Patrick Caulfield, Redhat * Copyright (c) 2005 Edward Goggin, EMC */ #include #include #include #include #include #include #include #include #include "mpath_cmd.h" #include "checkers.h" #include "vector.h" #include "memory.h" #include "devmapper.h" #include "defaults.h" #include "structs.h" #include "structs_vec.h" #include "dmparser.h" #include "config.h" #include "blacklist.h" #include "propsel.h" #include "discovery.h" #include "debug.h" #include "switchgroup.h" #include "print.h" #include "configure.h" #include "pgpolicies.h" #include "dict.h" #include "alias.h" #include "prio.h" #include "util.h" #include "uxsock.h" #include "wwids.h" #include "sysfs.h" /* group paths in pg by host adapter */ int group_by_host_adapter(struct pathgroup *pgp, vector adapters) { struct adapter_group *agp; struct host_group *hgp; struct path *pp, *pp1; char adapter_name1[SLOT_NAME_SIZE]; char adapter_name2[SLOT_NAME_SIZE]; int i, j; int found_hostgroup = 0; while (VECTOR_SIZE(pgp->paths) > 0) { pp = VECTOR_SLOT(pgp->paths, 0); if (sysfs_get_host_adapter_name(pp, adapter_name1)) goto out; /* create a new host adapter group */ agp = alloc_adaptergroup(); if (!agp) goto out; agp->pgp = pgp; strncpy(agp->adapter_name, adapter_name1, SLOT_NAME_SIZE - 1); store_adaptergroup(adapters, agp); /* create a new host port group */ hgp = alloc_hostgroup(); if (!hgp) goto out; if (store_hostgroup(agp->host_groups, hgp)) goto out; hgp->host_no = pp->sg_id.host_no; agp->num_hosts++; if (store_path(hgp->paths, pp)) goto out; hgp->num_paths++; /* delete path from path group */ vector_del_slot(pgp->paths, 0); /* add all paths belonging to same host adapter */ vector_foreach_slot(pgp->paths, pp1, i) { if (sysfs_get_host_adapter_name(pp1, adapter_name2)) goto out; if (strcmp(adapter_name1, adapter_name2) == 0) { found_hostgroup = 0; vector_foreach_slot(agp->host_groups, hgp, j) { if (hgp->host_no == pp1->sg_id.host_no) { if (store_path(hgp->paths, pp1)) goto out; hgp->num_paths++; found_hostgroup = 1; break; } } if (!found_hostgroup) { /* this path belongs to new host port * within this adapter */ hgp = alloc_hostgroup(); if (!hgp) goto out; if (store_hostgroup(agp->host_groups, hgp)) goto out; agp->num_hosts++; if (store_path(hgp->paths, pp1)) goto out; hgp->host_no = pp1->sg_id.host_no; hgp->num_paths++; } /* delete paths from original path_group * as they are added into adapter group now */ vector_del_slot(pgp->paths, i); i--; } } } return 0; out: /* add back paths into pg as re-ordering failed */ vector_foreach_slot(adapters, agp, i) { vector_foreach_slot(agp->host_groups, hgp, j) { while (VECTOR_SIZE(hgp->paths) > 0) { pp = VECTOR_SLOT(hgp->paths, 0); if (store_path(pgp->paths, pp)) condlog(3, "failed to restore " "path %s into path group", pp->dev); vector_del_slot(hgp->paths, 0); } } } free_adaptergroup(adapters); return 1; } /* re-order paths in pg by alternating adapters and host ports * for optimized selection */ int order_paths_in_pg_by_alt_adapters(struct pathgroup *pgp, vector adapters, int total_paths) { int next_adapter_index = 0; struct adapter_group *agp; struct host_group *hgp; struct path *pp; while (total_paths > 0) { agp = VECTOR_SLOT(adapters, next_adapter_index); if (!agp) { condlog(0, "can't get adapter group %d", next_adapter_index); return 1; } hgp = VECTOR_SLOT(agp->host_groups, agp->next_host_index); if (!hgp) { condlog(0, "can't get host group %d of adapter group %d", next_adapter_index, agp->next_host_index); return 1; } if (!hgp->num_paths) { agp->next_host_index++; agp->next_host_index %= agp->num_hosts; next_adapter_index++; next_adapter_index %= VECTOR_SIZE(adapters); continue; } pp = VECTOR_SLOT(hgp->paths, 0); if (store_path(pgp->paths, pp)) return 1; total_paths--; vector_del_slot(hgp->paths, 0); hgp->num_paths--; agp->next_host_index++; agp->next_host_index %= agp->num_hosts; next_adapter_index++; next_adapter_index %= VECTOR_SIZE(adapters); } /* all paths are added into path_group * in crafted child order */ return 0; } /* round-robin: order paths in path group to alternate * between all host adapters */ int rr_optimize_path_order(struct pathgroup *pgp) { vector adapters; struct path *pp; int total_paths; int i; total_paths = VECTOR_SIZE(pgp->paths); vector_foreach_slot(pgp->paths, pp, i) { if (pp->sg_id.proto_id != SCSI_PROTOCOL_FCP && pp->sg_id.proto_id != SCSI_PROTOCOL_SAS && pp->sg_id.proto_id != SCSI_PROTOCOL_ISCSI && pp->sg_id.proto_id != SCSI_PROTOCOL_SRP) { /* return success as default path order * is maintained in path group */ return 0; } } adapters = vector_alloc(); if (!adapters) return 0; /* group paths in path group by host adapters */ if (group_by_host_adapter(pgp, adapters)) { /* already freed adapters */ condlog(3, "Failed to group paths by adapters"); return 0; } /* re-order paths in pg to alternate between adapters and host ports */ if (order_paths_in_pg_by_alt_adapters(pgp, adapters, total_paths)) { condlog(3, "Failed to re-order paths in pg by adapters " "and host ports"); free_adaptergroup(adapters); /* return failure as original paths are * removed form pgp */ return 1; } free_adaptergroup(adapters); return 0; } int setup_map(struct multipath *mpp, char *params, int params_size) { struct pathgroup * pgp; struct config *conf; int i; /* * don't bother if devmap size is unknown */ if (mpp->size <= 0) { condlog(3, "%s: devmap size is unknown", mpp->alias); return 1; } /* * free features, selector, and hwhandler properties if they are being reused */ free_multipath_attributes(mpp); /* * properties selectors */ conf = get_multipath_config(); select_pgfailback(conf, mpp); select_pgpolicy(conf, mpp); select_selector(conf, mpp); select_hwhandler(conf, mpp); select_no_path_retry(conf, mpp); select_retain_hwhandler(conf, mpp); select_features(conf, mpp); select_rr_weight(conf, mpp); select_minio(conf, mpp); select_mode(conf, mpp); select_uid(conf, mpp); select_gid(conf, mpp); select_fast_io_fail(conf, mpp); select_dev_loss(conf, mpp); select_reservation_key(conf, mpp); select_deferred_remove(conf, mpp); select_delay_watch_checks(conf, mpp); select_delay_wait_checks(conf, mpp); select_marginal_path_err_sample_time(conf, mpp); select_marginal_path_err_rate_threshold(conf, mpp); select_marginal_path_err_recheck_gap_time(conf, mpp); select_marginal_path_double_failed_time(conf, mpp); select_skip_kpartx(conf, mpp); select_max_sectors_kb(conf, mpp); sysfs_set_scsi_tmo(mpp, conf->checkint); put_multipath_config(conf); /* * assign paths to path groups -- start with no groups and all paths * in mpp->paths */ if (mpp->pg) { vector_foreach_slot (mpp->pg, pgp, i) free_pathgroup(pgp, KEEP_PATHS); vector_free(mpp->pg); mpp->pg = NULL; } if (mpp->pgpolicyfn && mpp->pgpolicyfn(mpp)) return 1; mpp->nr_active = pathcount(mpp, PATH_UP) + pathcount(mpp, PATH_GHOST); /* * ponders each path group and determine highest prio pg * to switch over (default to first) */ mpp->bestpg = select_path_group(mpp); /* re-order paths in all path groups in an optimized way * for round-robin path selectors to get maximum throughput. */ if (!strncmp(mpp->selector, "round-robin", 11)) { vector_foreach_slot(mpp->pg, pgp, i) { if (VECTOR_SIZE(pgp->paths) <= 2) continue; if (rr_optimize_path_order(pgp)) { condlog(2, "cannot re-order paths for " "optimization: %s", mpp->alias); return 1; } } } /* * transform the mp->pg vector of vectors of paths * into a mp->params strings to feed the device-mapper */ if (assemble_map(mpp, params, params_size)) { condlog(0, "%s: problem assembing map", mpp->alias); return 1; } return 0; } static void compute_pgid(struct pathgroup * pgp) { struct path * pp; int i; vector_foreach_slot (pgp->paths, pp, i) pgp->id ^= (long)pp; } static int pgcmp (struct multipath * mpp, struct multipath * cmpp) { int i, j; struct pathgroup * pgp; struct pathgroup * cpgp; int r = 0; if (!mpp) return 0; vector_foreach_slot (mpp->pg, pgp, i) { compute_pgid(pgp); vector_foreach_slot (cmpp->pg, cpgp, j) { if (pgp->id == cpgp->id && !pathcmp(pgp, cpgp)) { r = 0; break; } r++; } if (r) return r; } return r; } static struct udev_device * get_udev_for_mpp(const struct multipath *mpp) { dev_t devnum; struct udev_device *udd; if (!mpp || !mpp->dmi) { condlog(1, "%s called with empty mpp", __func__); return NULL; } devnum = makedev(mpp->dmi->major, mpp->dmi->minor); udd = udev_device_new_from_devnum(udev, 'b', devnum); if (!udd) { condlog(1, "failed to get udev device for %s", mpp->alias); return NULL; } return udd; } static void trigger_udev_change(const struct multipath *mpp) { static const char change[] = "change"; struct udev_device *udd = get_udev_for_mpp(mpp); if (!udd) return; condlog(3, "triggering %s uevent for %s", change, mpp->alias); sysfs_attr_set_value(udd, "uevent", change, sizeof(change)-1); udev_device_unref(udd); } static int is_mpp_known_to_udev(const struct multipath *mpp) { struct udev_device *udd = get_udev_for_mpp(mpp); int ret = (udd != NULL); udev_device_unref(udd); return ret; } static int sysfs_set_max_sectors_kb(struct multipath *mpp, int is_reload) { struct pathgroup * pgp; struct path *pp; char buff[11]; int i, j, ret, err = 0; struct udev_device *udd; int max_sectors_kb; if (mpp->max_sectors_kb == MAX_SECTORS_KB_UNDEF) return 0; max_sectors_kb = mpp->max_sectors_kb; if (is_reload) { if (!mpp->dmi && dm_get_info(mpp->alias, &mpp->dmi) != 0) { condlog(1, "failed to get dm info for %s", mpp->alias); return 1; } udd = get_udev_for_mpp(mpp); if (!udd) { condlog(1, "failed to get udev device to set max_sectors_kb for %s", mpp->alias); return 1; } ret = sysfs_attr_get_value(udd, "queue/max_sectors_kb", buff, sizeof(buff)); udev_device_unref(udd); if (ret <= 0) { condlog(1, "failed to get current max_sectors_kb from %s", mpp->alias); return 1; } if (sscanf(buff, "%u\n", &max_sectors_kb) != 1) { condlog(1, "can't parse current max_sectors_kb from %s", mpp->alias); return 1; } } snprintf(buff, 11, "%d", max_sectors_kb); vector_foreach_slot (mpp->pg, pgp, i) { vector_foreach_slot(pgp->paths, pp, j) { ret = sysfs_attr_set_value(pp->udev, "queue/max_sectors_kb", buff, strlen(buff)); if (ret < 0) { condlog(1, "failed setting max_sectors_kb on %s : %s", pp->dev, strerror(-ret)); err = 1; } } } return err; } static void select_action (struct multipath * mpp, vector curmp, int force_reload) { struct multipath * cmpp; struct multipath * cmpp_by_name; char * mpp_feat, * cmpp_feat; cmpp = find_mp_by_wwid(curmp, mpp->wwid); cmpp_by_name = find_mp_by_alias(curmp, mpp->alias); if (!cmpp_by_name) { if (cmpp) { condlog(2, "%s: rename %s to %s", mpp->wwid, cmpp->alias, mpp->alias); strncpy(mpp->alias_old, cmpp->alias, WWID_SIZE - 1); mpp->action = ACT_RENAME; if (force_reload) { mpp->force_udev_reload = 1; mpp->action = ACT_FORCERENAME; } return; } mpp->action = ACT_CREATE; condlog(3, "%s: set ACT_CREATE (map does not exist)", mpp->alias); return; } if (!cmpp) { condlog(2, "%s: remove (wwid changed)", mpp->alias); dm_flush_map(mpp->alias); strncpy(cmpp_by_name->wwid, mpp->wwid, WWID_SIZE - 1); drop_multipath(curmp, cmpp_by_name->wwid, KEEP_PATHS); mpp->action = ACT_CREATE; condlog(3, "%s: set ACT_CREATE (map wwid change)", mpp->alias); return; } if (cmpp != cmpp_by_name) { condlog(2, "%s: unable to rename %s to %s (%s is used by %s)", mpp->wwid, cmpp->alias, mpp->alias, mpp->alias, cmpp_by_name->wwid); /* reset alias to existing alias */ FREE(mpp->alias); mpp->alias = STRDUP(cmpp->alias); mpp->action = ACT_IMPOSSIBLE; return; } if (pathcount(mpp, PATH_UP) == 0) { mpp->action = ACT_IMPOSSIBLE; condlog(3, "%s: set ACT_IMPOSSIBLE (no usable path)", mpp->alias); return; } if (force_reload) { mpp->force_udev_reload = 1; mpp->action = ACT_RELOAD; condlog(3, "%s: set ACT_RELOAD (forced by user)", mpp->alias); return; } if (cmpp->size != mpp->size) { mpp->force_udev_reload = 1; mpp->action = ACT_RESIZE; condlog(3, "%s: set ACT_RESIZE (size change)", mpp->alias); return; } if (mpp->no_path_retry != NO_PATH_RETRY_UNDEF && mpp->no_path_retry != cmpp->no_path_retry) { mpp->action = ACT_RELOAD; condlog(3, "%s: set ACT_RELOAD (no_path_retry change)", mpp->alias); return; } if (mpp->retain_hwhandler != RETAIN_HWHANDLER_ON && (strlen(cmpp->hwhandler) != strlen(mpp->hwhandler) || strncmp(cmpp->hwhandler, mpp->hwhandler, strlen(mpp->hwhandler)))) { mpp->action = ACT_RELOAD; condlog(3, "%s: set ACT_RELOAD (hwhandler change)", mpp->alias); return; } if (mpp->retain_hwhandler != RETAIN_HWHANDLER_UNDEF && mpp->retain_hwhandler != cmpp->retain_hwhandler && get_linux_version_code() < KERNEL_VERSION(4, 3, 0)) { mpp->action = ACT_RELOAD; condlog(3, "%s: set ACT_RELOAD (retain_hwhandler change)", mpp->alias); return; } cmpp_feat = STRDUP(cmpp->features); mpp_feat = STRDUP(mpp->features); if (cmpp_feat && mpp_feat) { remove_feature(&mpp_feat, "queue_if_no_path"); remove_feature(&mpp_feat, "retain_attached_hw_handler"); remove_feature(&cmpp_feat, "queue_if_no_path"); remove_feature(&cmpp_feat, "retain_attached_hw_handler"); if (strncmp(mpp_feat, cmpp_feat, PARAMS_SIZE)) { mpp->action = ACT_RELOAD; condlog(3, "%s: set ACT_RELOAD (features change)", mpp->alias); } } FREE(cmpp_feat); FREE(mpp_feat); if (!cmpp->selector || strncmp(cmpp->selector, mpp->selector, strlen(mpp->selector))) { mpp->action = ACT_RELOAD; condlog(3, "%s: set ACT_RELOAD (selector change)", mpp->alias); return; } if (cmpp->minio != mpp->minio) { mpp->action = ACT_RELOAD; condlog(3, "%s: set ACT_RELOAD (minio change, %u->%u)", mpp->alias, cmpp->minio, mpp->minio); return; } if (!cmpp->pg || VECTOR_SIZE(cmpp->pg) != VECTOR_SIZE(mpp->pg)) { mpp->action = ACT_RELOAD; condlog(3, "%s: set ACT_RELOAD (path group number change)", mpp->alias); return; } if (pgcmp(mpp, cmpp)) { mpp->action = ACT_RELOAD; condlog(3, "%s: set ACT_RELOAD (path group topology change)", mpp->alias); return; } if (cmpp->nextpg != mpp->bestpg) { mpp->action = ACT_SWITCHPG; condlog(3, "%s: set ACT_SWITCHPG (next path group change)", mpp->alias); return; } if (!is_mpp_known_to_udev(cmpp)) { mpp->action = ACT_RELOAD; condlog(3, "%s: set ACT_RELOAD (udev device not initialized)", mpp->alias); return; } mpp->action = ACT_NOTHING; condlog(3, "%s: set ACT_NOTHING (map unchanged)", mpp->alias); return; } int reinstate_paths(struct multipath *mpp) { int i, j; struct pathgroup * pgp; struct path * pp; if (!mpp->pg) return 0; vector_foreach_slot (mpp->pg, pgp, i) { if (!pgp->paths) continue; vector_foreach_slot (pgp->paths, pp, j) { if (pp->state != PATH_UP && (pgp->status == PGSTATE_DISABLED || pgp->status == PGSTATE_ACTIVE)) continue; if (pp->dmstate == PSTATE_FAILED) { if (dm_reinstate_path(mpp->alias, pp->dev_t)) condlog(0, "%s: error reinstating", pp->dev); } } } return 0; } static int lock_multipath (struct multipath * mpp, int lock) { struct pathgroup * pgp; struct path * pp; int i, j; int x, y; if (!mpp || !mpp->pg) return 0; vector_foreach_slot (mpp->pg, pgp, i) { if (!pgp->paths) continue; vector_foreach_slot(pgp->paths, pp, j) { if (lock && flock(pp->fd, LOCK_SH | LOCK_NB) && errno == EWOULDBLOCK) goto fail; else if (!lock) flock(pp->fd, LOCK_UN); } } return 0; fail: vector_foreach_slot (mpp->pg, pgp, x) { if (x > i) return 1; if (!pgp->paths) continue; vector_foreach_slot(pgp->paths, pp, y) { if (x == i && y >= j) return 1; flock(pp->fd, LOCK_UN); } } return 1; } /* * Return value: */ #define DOMAP_RETRY -1 #define DOMAP_FAIL 0 #define DOMAP_OK 1 #define DOMAP_EXIST 2 #define DOMAP_DRY 3 int domap(struct multipath *mpp, char *params, int is_daemon) { int r = DOMAP_FAIL; struct config *conf; /* * last chance to quit before touching the devmaps */ if (mpp->action == ACT_DRY_RUN) { conf = get_multipath_config(); print_multipath_topology(mpp, conf->verbosity); put_multipath_config(conf); return DOMAP_DRY; } if (mpp->action == ACT_CREATE && dm_map_present(mpp->alias)) { condlog(3, "%s: map already present", mpp->alias); mpp->action = ACT_RELOAD; } switch (mpp->action) { case ACT_REJECT: case ACT_NOTHING: case ACT_IMPOSSIBLE: return DOMAP_EXIST; case ACT_SWITCHPG: dm_switchgroup(mpp->alias, mpp->bestpg); /* * we may have avoided reinstating paths because there where in * active or disabled PG. Now that the topology has changed, * retry. */ reinstate_paths(mpp); return DOMAP_EXIST; case ACT_CREATE: if (lock_multipath(mpp, 1)) { condlog(3, "%s: failed to create map (in use)", mpp->alias); return DOMAP_RETRY; } sysfs_set_max_sectors_kb(mpp, 0); r = dm_addmap_create(mpp, params); lock_multipath(mpp, 0); break; case ACT_RELOAD: sysfs_set_max_sectors_kb(mpp, 1); r = dm_addmap_reload(mpp, params, 0); break; case ACT_RESIZE: sysfs_set_max_sectors_kb(mpp, 1); r = dm_addmap_reload(mpp, params, 1); break; case ACT_RENAME: conf = get_multipath_config(); r = dm_rename(mpp->alias_old, mpp->alias, conf->partition_delim, mpp->skip_kpartx); put_multipath_config(conf); break; case ACT_FORCERENAME: conf = get_multipath_config(); r = dm_rename(mpp->alias_old, mpp->alias, conf->partition_delim, mpp->skip_kpartx); put_multipath_config(conf); if (r) { sysfs_set_max_sectors_kb(mpp, 1); r = dm_addmap_reload(mpp, params, 0); } break; default: break; } if (r == DOMAP_OK) { /* * DM_DEVICE_CREATE, DM_DEVICE_RENAME, or DM_DEVICE_RELOAD * succeeded */ mpp->force_udev_reload = 0; if (mpp->action == ACT_CREATE) remember_wwid(mpp->wwid); if (!is_daemon) { /* multipath client mode */ dm_switchgroup(mpp->alias, mpp->bestpg); } else { /* multipath daemon mode */ mpp->stat_map_loads++; condlog(2, "%s: load table [0 %llu %s %s]", mpp->alias, mpp->size, TGT_MPATH, params); /* * Required action is over, reset for the stateful daemon. * But don't do it for creation as we use in the caller the * mpp->action to figure out whether to start the watievent checker. */ if (mpp->action != ACT_CREATE) mpp->action = ACT_NOTHING; else { conf = get_multipath_config(); mpp->wait_for_udev = 1; mpp->uev_wait_tick = conf->uev_wait_timeout; put_multipath_config(conf); } } dm_setgeometry(mpp); return DOMAP_OK; } return DOMAP_FAIL; } static int deadmap (struct multipath * mpp) { int i, j; struct pathgroup * pgp; struct path * pp; if (!mpp->pg) return 1; vector_foreach_slot (mpp->pg, pgp, i) { if (!pgp->paths) continue; vector_foreach_slot (pgp->paths, pp, j) if (strlen(pp->dev)) return 0; /* alive */ } return 1; /* dead */ } int check_daemon(void) { int fd; char *reply; int ret = 0; unsigned int timeout; struct config *conf; fd = mpath_connect(); if (fd == -1) return 0; if (send_packet(fd, "show daemon") != 0) goto out; conf = get_multipath_config(); timeout = conf->uxsock_timeout; put_multipath_config(conf); if (recv_packet(fd, &reply, timeout) != 0) goto out; if (strstr(reply, "shutdown")) goto out_free; ret = 1; out_free: FREE(reply); out: mpath_disconnect(fd); return ret; } /* * The force_reload parameter determines how coalesce_paths treats existing maps. * FORCE_RELOAD_NONE: existing maps aren't touched at all * FORCE_RELOAD_YES: all maps are rebuilt from scratch and (re)loaded in DM * FORCE_RELOAD_WEAK: existing maps are compared to the current conf and only * reloaded in DM if there's a difference. This is useful during startup. */ int coalesce_paths (struct vectors * vecs, vector newmp, char * refwwid, int force_reload, enum mpath_cmds cmd) { int r = 1; int k, i; int is_daemon = (cmd == CMD_NONE) ? 1 : 0; char params[PARAMS_SIZE]; struct multipath * mpp; struct path * pp1; struct path * pp2; vector curmp = vecs->mpvec; vector pathvec = vecs->pathvec; struct config *conf; int allow_queueing; /* ignore refwwid if it's empty */ if (refwwid && !strlen(refwwid)) refwwid = NULL; if (force_reload != FORCE_RELOAD_NONE) { vector_foreach_slot (pathvec, pp1, k) { pp1->mpp = NULL; } } vector_foreach_slot (pathvec, pp1, k) { /* skip this path for some reason */ /* 1. if path has no unique id or wwid blacklisted */ conf = get_multipath_config(); if (strlen(pp1->wwid) == 0 || filter_path(conf, pp1) > 0) { put_multipath_config(conf); orphan_path(pp1, "wwid blacklisted"); continue; } put_multipath_config(conf); /* 2. if path already coalesced */ if (pp1->mpp) continue; /* 3. if path has disappeared */ if (pp1->state == PATH_REMOVED) { orphan_path(pp1, "path removed"); continue; } /* 4. path is out of scope */ if (refwwid && strncmp(pp1->wwid, refwwid, WWID_SIZE - 1)) continue; /* If find_multipaths was selected check if the path is valid */ if (!refwwid && !should_multipath(pp1, pathvec)) { orphan_path(pp1, "only one path"); continue; } /* * at this point, we know we really got a new mp */ mpp = add_map_with_path(vecs, pp1, 0); if (!mpp) { orphan_path(pp1, "failed to create multipath device"); continue; } if (pp1->priority == PRIO_UNDEF) mpp->action = ACT_REJECT; if (!mpp->paths) { condlog(0, "%s: skip coalesce (no paths)", mpp->alias); remove_map(mpp, vecs, 0); continue; } for (i = k + 1; i < VECTOR_SIZE(pathvec); i++) { pp2 = VECTOR_SLOT(pathvec, i); if (strcmp(pp1->wwid, pp2->wwid)) continue; if (!mpp->size && pp2->size) mpp->size = pp2->size; if (mpp->size && pp2->size && pp2->size != mpp->size) { /* * ouch, avoid feeding that to the DM */ condlog(0, "%s: size %llu, expected %llu. " "Discard", pp2->dev_t, pp2->size, mpp->size); mpp->action = ACT_REJECT; } if (pp2->priority == PRIO_UNDEF) mpp->action = ACT_REJECT; } verify_paths(mpp, vecs); params[0] = '\0'; if (setup_map(mpp, params, PARAMS_SIZE)) { remove_map(mpp, vecs, 0); continue; } if (cmd == CMD_DRY_RUN) mpp->action = ACT_DRY_RUN; if (mpp->action == ACT_UNDEF) select_action(mpp, curmp, force_reload == FORCE_RELOAD_YES ? 1 : 0); r = domap(mpp, params, is_daemon); if (r == DOMAP_FAIL || r == DOMAP_RETRY) { condlog(3, "%s: domap (%u) failure " "for create/reload map", mpp->alias, r); if (r == DOMAP_FAIL || is_daemon) { condlog(2, "%s: %s map", mpp->alias, (mpp->action == ACT_CREATE)? "ignoring" : "removing"); remove_map(mpp, vecs, 0); continue; } else /* if (r == DOMAP_RETRY) */ return r; } if (r == DOMAP_DRY) continue; if (r == DOMAP_EXIST && mpp->action == ACT_NOTHING && force_reload == FORCE_RELOAD_WEAK) /* * First time we're called, and no changes applied. * domap() was a noop. But we can't be sure that * udev has already finished setting up this device * (udev in initrd may have been shut down while * processing this device or its children). * Trigger a change event, just in case. */ trigger_udev_change(find_mp_by_wwid(curmp, mpp->wwid)); conf = get_multipath_config(); allow_queueing = conf->allow_queueing; put_multipath_config(conf); if (!is_daemon && !allow_queueing && !check_daemon()) { if (mpp->no_path_retry != NO_PATH_RETRY_UNDEF && mpp->no_path_retry != NO_PATH_RETRY_FAIL) condlog(3, "%s: multipathd not running, unset " "queue_if_no_path feature", mpp->alias); if (!dm_queue_if_no_path(mpp->alias, 0)) remove_feature(&mpp->features, "queue_if_no_path"); } else if (mpp->no_path_retry != NO_PATH_RETRY_UNDEF) { if (mpp->no_path_retry == NO_PATH_RETRY_FAIL) { condlog(3, "%s: unset queue_if_no_path feature", mpp->alias); if (!dm_queue_if_no_path(mpp->alias, 0)) remove_feature(&mpp->features, "queue_if_no_path"); } else { condlog(3, "%s: set queue_if_no_path feature", mpp->alias); if (!dm_queue_if_no_path(mpp->alias, 1)) add_feature(&mpp->features, "queue_if_no_path"); } } if (!is_daemon && mpp->action != ACT_NOTHING) { conf = get_multipath_config(); print_multipath_topology(mpp, conf->verbosity); put_multipath_config(conf); } if (newmp) { if (mpp->action != ACT_REJECT) { if (!vector_alloc_slot(newmp)) return 1; vector_set_slot(newmp, mpp); } else remove_map(mpp, vecs, 0); } } /* * Flush maps with only dead paths (ie not in sysfs) * Keep maps with only failed paths */ if (newmp) { vector_foreach_slot (newmp, mpp, i) { char alias[WWID_SIZE]; if (!deadmap(mpp)) continue; strncpy(alias, mpp->alias, WWID_SIZE - 1); vector_del_slot(newmp, i); i--; remove_map(mpp, vecs, 0); if (dm_flush_map(alias)) condlog(2, "%s: remove failed (dead)", alias); else condlog(2, "%s: remove (dead)", alias); } } return 0; } struct udev_device *get_udev_device(const char *dev, enum devtypes dev_type) { struct udev_device *ud = NULL; const char *base; if (dev == NULL || *dev == '\0') return NULL; switch (dev_type) { case DEV_DEVNODE: case DEV_DEVMAP: /* This should be GNU basename, compiler will warn if not */ base = basename(dev); if (*base == '\0') break; ud = udev_device_new_from_subsystem_sysname(udev, "block", base); break; case DEV_DEVT: ud = udev_device_new_from_devnum(udev, 'b', parse_devt(dev)); break; case DEV_UEVENT: ud = udev_device_new_from_environment(udev); break; default: condlog(0, "Internal error: get_udev_device called with invalid type %d\n", dev_type); break; } if (ud == NULL) condlog(2, "get_udev_device: failed to look up %s with type %d", dev, dev_type); return ud; } /* * returns: * 0 - success * 1 - failure * 2 - blacklist */ int get_refwwid(enum mpath_cmds cmd, char *dev, enum devtypes dev_type, vector pathvec, char **wwid) { int ret = 1; struct path * pp; char buff[FILE_NAME_SIZE]; char * refwwid = NULL, tmpwwid[WWID_SIZE]; int flags = DI_SYSFS | DI_WWID; struct config *conf; if (!wwid) return 1; *wwid = NULL; if (dev_type == DEV_NONE) return 1; if (cmd != CMD_REMOVE_WWID) flags |= DI_BLACKLIST; if (dev_type == DEV_DEVNODE) { if (basenamecpy(dev, buff, FILE_NAME_SIZE) == 0) { condlog(1, "basename failed for '%s' (%s)", dev, buff); return 1; } pp = find_path_by_dev(pathvec, buff); if (!pp) { struct udev_device *udevice = get_udev_device(buff, dev_type); if (!udevice) return 1; conf = get_multipath_config(); ret = store_pathinfo(pathvec, conf, udevice, flags, &pp); put_multipath_config(conf); udev_device_unref(udevice); if (!pp) { if (ret == 1) condlog(0, "%s: can't store path info", dev); return ret; } } conf = get_multipath_config(); if (pp->udev && pp->uid_attribute && filter_property(conf, pp->udev) > 0) { put_multipath_config(conf); return 2; } put_multipath_config(conf); refwwid = pp->wwid; goto out; } if (dev_type == DEV_DEVT) { strchop(dev); if (devt2devname(buff, FILE_NAME_SIZE, dev)) { condlog(0, "%s: cannot find block device\n", dev); return 1; } pp = find_path_by_dev(pathvec, buff); if (!pp) { struct udev_device *udevice = get_udev_device(dev, dev_type); if (!udevice) return 1; conf = get_multipath_config(); ret = store_pathinfo(pathvec, conf, udevice, flags, &pp); put_multipath_config(conf); udev_device_unref(udevice); if (!pp) { if (ret == 1) condlog(0, "%s can't store path info", buff); return ret; } } conf = get_multipath_config(); if (pp->udev && pp->uid_attribute && filter_property(conf, pp->udev) > 0) { put_multipath_config(conf); return 2; } put_multipath_config(conf); refwwid = pp->wwid; goto out; } if (dev_type == DEV_UEVENT) { struct udev_device *udevice = get_udev_device(dev, dev_type); if (!udevice) return 1; conf = get_multipath_config(); ret = store_pathinfo(pathvec, conf, udevice, flags, &pp); udev_device_unref(udevice); if (!pp) { if (ret == 1) condlog(0, "%s: can't store path info", dev); put_multipath_config(conf); return ret; } if (pp->udev && pp->uid_attribute && filter_property(conf, pp->udev) > 0) { put_multipath_config(conf); return 2; } put_multipath_config(conf); refwwid = pp->wwid; goto out; } if (dev_type == DEV_DEVMAP) { conf = get_multipath_config(); if (((dm_get_uuid(dev, tmpwwid)) == 0) && (strlen(tmpwwid))) { refwwid = tmpwwid; goto check; } /* * may be a binding */ if (get_user_friendly_wwid(dev, tmpwwid, conf->bindings_file) == 0) { refwwid = tmpwwid; put_multipath_config(conf); goto check; } /* * or may be an alias */ refwwid = get_mpe_wwid(conf->mptable, dev); /* * or directly a wwid */ if (!refwwid) refwwid = dev; check: if (refwwid && strlen(refwwid)) { if (filter_wwid(conf->blist_wwid, conf->elist_wwid, refwwid, NULL) > 0) { put_multipath_config(conf); return 2; } } put_multipath_config(conf); } out: if (refwwid && strlen(refwwid)) { *wwid = STRDUP(refwwid); return 0; } return 1; } int reload_map(struct vectors *vecs, struct multipath *mpp, int refresh, int is_daemon) { char params[PARAMS_SIZE] = {0}; struct path *pp; int i, r; update_mpp_paths(mpp, vecs->pathvec); if (refresh) { vector_foreach_slot (mpp->paths, pp, i) { struct config *conf = get_multipath_config(); r = pathinfo(pp, conf, DI_PRIO); put_multipath_config(conf); if (r) { condlog(2, "%s: failed to refresh pathinfo", mpp->alias); return 1; } } } if (setup_map(mpp, params, PARAMS_SIZE)) { condlog(0, "%s: failed to setup map", mpp->alias); return 1; } select_action(mpp, vecs->mpvec, 1); r = domap(mpp, params, is_daemon); if (r == DOMAP_FAIL || r == DOMAP_RETRY) { condlog(3, "%s: domap (%u) failure " "for reload map", mpp->alias, r); return 1; } if (mpp->no_path_retry != NO_PATH_RETRY_UNDEF) { if (mpp->no_path_retry == NO_PATH_RETRY_FAIL) dm_queue_if_no_path(mpp->alias, 0); else dm_queue_if_no_path(mpp->alias, 1); } return 0; } multipath-tools-0.7.4/libmultipath/configure.h000066400000000000000000000022541320314174000215150ustar00rootroot00000000000000/* * configurator actions */ #define ACT_NOTHING_STR "unchanged" #define ACT_REJECT_STR "reject" #define ACT_RELOAD_STR "reload" #define ACT_SWITCHPG_STR "switchpg" #define ACT_RENAME_STR "rename" #define ACT_CREATE_STR "create" #define ACT_RESIZE_STR "resize" enum actions { ACT_UNDEF, ACT_NOTHING, ACT_REJECT, ACT_RELOAD, ACT_SWITCHPG, ACT_RENAME, ACT_CREATE, ACT_RESIZE, ACT_FORCERENAME, ACT_DRY_RUN, ACT_IMPOSSIBLE, }; #define FLUSH_ONE 1 #define FLUSH_ALL 2 struct vectors; int setup_map (struct multipath * mpp, char * params, int params_size ); int domap (struct multipath * mpp, char * params, int is_daemon); int reinstate_paths (struct multipath *mpp); int coalesce_paths (struct vectors *vecs, vector curmp, char * refwwid, int force_reload, enum mpath_cmds cmd); int get_refwwid (enum mpath_cmds cmd, char * dev, enum devtypes dev_type, vector pathvec, char **wwid); int reload_map(struct vectors *vecs, struct multipath *mpp, int refresh, int is_daemon); int sysfs_get_host_adapter_name(struct path *pp, char *adapter_name); struct udev_device *get_udev_device(const char *dev, enum devtypes dev_type); multipath-tools-0.7.4/libmultipath/debug.c000066400000000000000000000016671320314174000206240ustar00rootroot00000000000000/* * Copyright (c) 2005 Christophe Varoqui */ #include #include #include #include "log_pthread.h" #include #include #include "../third-party/valgrind/drd.h" #include "vector.h" #include "config.h" #include "defaults.h" void dlog (int sink, int prio, const char * fmt, ...) { va_list ap; int thres; struct config *conf; va_start(ap, fmt); conf = get_multipath_config(); ANNOTATE_IGNORE_READS_BEGIN(); thres = (conf) ? conf->verbosity : DEFAULT_VERBOSITY; ANNOTATE_IGNORE_READS_END(); put_multipath_config(conf); if (prio <= thres) { if (sink < 1) { if (sink == 0) { time_t t = time(NULL); struct tm *tb = localtime(&t); char buff[16]; strftime(buff, sizeof(buff), "%b %d %H:%M:%S", tb); buff[sizeof(buff)-1] = '\0'; fprintf(stdout, "%s | ", buff); } vfprintf(stdout, fmt, ap); } else log_safe(prio + 3, fmt, ap); } va_end(ap); } multipath-tools-0.7.4/libmultipath/debug.h000066400000000000000000000004061320314174000206170ustar00rootroot00000000000000void dlog (int sink, int prio, const char * fmt, ...) __attribute__((format(printf, 3, 4))); #include #include #include "log_pthread.h" extern int logsink; #define condlog(prio, fmt, args...) \ dlog(logsink, prio, fmt "\n", ##args) multipath-tools-0.7.4/libmultipath/defaults.c000066400000000000000000000003731320314174000213360ustar00rootroot00000000000000/* * Copyright (c) 2005 Christophe Varoqui */ #include #include "memory.h" char * set_default (char * str) { int len; char * p; len = strlen(str); p = MALLOC(len + 1); if (!p) return NULL; strncat(p, str, len); return p; } multipath-tools-0.7.4/libmultipath/defaults.h000066400000000000000000000040431320314174000213410ustar00rootroot00000000000000/* * If you add or modify a value also update multipath/multipath.conf.5 * and the TEMPLATE in libmultipath/hwtable.c */ #define DEFAULT_UID_ATTRIBUTE "ID_SERIAL" #define DEFAULT_UDEVDIR "/dev" #define DEFAULT_MULTIPATHDIR "/" LIB_STRING "/multipath" #define DEFAULT_SELECTOR "service-time 0" #define DEFAULT_ALIAS_PREFIX "mpath" #define DEFAULT_FEATURES "0" #define DEFAULT_HWHANDLER "0" #define DEFAULT_MINIO 1000 #define DEFAULT_MINIO_RQ 1 #define DEFAULT_PGPOLICY FAILOVER #define DEFAULT_FAILBACK -FAILBACK_MANUAL #define DEFAULT_RR_WEIGHT RR_WEIGHT_NONE #define DEFAULT_NO_PATH_RETRY NO_PATH_RETRY_UNDEF #define DEFAULT_VERBOSITY 2 #define DEFAULT_REASSIGN_MAPS 0 #define DEFAULT_FIND_MULTIPATHS 0 #define DEFAULT_FAST_IO_FAIL 5 #define DEFAULT_DEV_LOSS_TMO 600 #define DEFAULT_RETAIN_HWHANDLER RETAIN_HWHANDLER_ON #define DEFAULT_DETECT_PRIO DETECT_PRIO_ON #define DEFAULT_DETECT_CHECKER DETECT_CHECKER_ON #define DEFAULT_DEFERRED_REMOVE DEFERRED_REMOVE_OFF #define DEFAULT_DELAY_CHECKS NU_NO #define DEFAULT_ERR_CHECKS NU_NO #define DEFAULT_UEVENT_STACKSIZE 256 #define DEFAULT_RETRIGGER_DELAY 10 #define DEFAULT_RETRIGGER_TRIES 3 #define DEFAULT_UEV_WAIT_TIMEOUT 30 #define DEFAULT_PRIO PRIO_CONST #define DEFAULT_PRIO_ARGS "" #define DEFAULT_CHECKER TUR #define DEFAULT_FLUSH FLUSH_DISABLED #define DEFAULT_USER_FRIENDLY_NAMES USER_FRIENDLY_NAMES_OFF #define DEFAULT_FORCE_SYNC 0 #define DEFAULT_PARTITION_DELIM NULL #define DEFAULT_SKIP_KPARTX SKIP_KPARTX_OFF #define DEFAULT_DISABLE_CHANGED_WWIDS 0 #define DEFAULT_MAX_SECTORS_KB MAX_SECTORS_KB_UNDEF #define DEFAULT_CHECKINT 5 #define MAX_CHECKINT(a) (a << 2) #define MAX_DEV_LOSS_TMO 0x7FFFFFFF #define DEFAULT_PIDFILE "/" RUN_DIR "/multipathd.pid" #define DEFAULT_SOCKET "/org/kernel/linux/storage/multipathd" #define DEFAULT_CONFIGFILE "/etc/multipath.conf" #define DEFAULT_BINDINGS_FILE "/etc/multipath/bindings" #define DEFAULT_WWIDS_FILE "/etc/multipath/wwids" #define DEFAULT_PRKEYS_FILE "/etc/multipath/prkeys" #define DEFAULT_CONFIG_DIR "/etc/multipath/conf.d" char * set_default (char * str); multipath-tools-0.7.4/libmultipath/devmapper.c000066400000000000000000000750561320314174000215240ustar00rootroot00000000000000/* * snippets copied from device-mapper dmsetup.c * Copyright (c) 2004, 2005 Christophe Varoqui * Copyright (c) 2005 Kiyoshi Ueda, NEC * Copyright (c) 2005 Patrick Caulfield, Redhat */ #include #include #include #include #include #include #include #include #include #include "checkers.h" #include "vector.h" #include "structs.h" #include "debug.h" #include "memory.h" #include "devmapper.h" #include "sysfs.h" #include "config.h" #include "log_pthread.h" #include #include #define MAX_WAIT 5 #define LOOPS_PER_SEC 5 #define UUID_PREFIX "mpath-" #define UUID_PREFIX_LEN 6 static int dm_conf_verbosity; #ifdef LIBDM_API_DEFERRED static int dm_cancel_remove_partmaps(const char * mapname); #endif static int do_foreach_partmaps(const char * mapname, int (*partmap_func)(const char *, void *), void *data); #ifndef LIBDM_API_COOKIE static inline int dm_task_set_cookie(struct dm_task *dmt, uint32_t *c, int a) { return 1; } void dm_udev_wait(unsigned int c) { } void dm_udev_set_sync_support(int c) { } #endif static void dm_write_log (int level, const char *file, int line, const char *f, ...) { va_list ap; int thres; if (level > 6) level = 6; thres = dm_conf_verbosity; if (thres <= 3 || level > thres) return; va_start(ap, f); if (logsink < 1) { if (logsink == 0) { time_t t = time(NULL); struct tm *tb = localtime(&t); char buff[16]; strftime(buff, sizeof(buff), "%b %d %H:%M:%S", tb); buff[sizeof(buff)-1] = '\0'; fprintf(stdout, "%s | ", buff); } fprintf(stdout, "libdevmapper: %s(%i): ", file, line); vfprintf(stdout, f, ap); fprintf(stdout, "\n"); } else { condlog(level, "libdevmapper: %s(%i): ", file, line); log_safe(level + 3, f, ap); } va_end(ap); return; } void dm_init(int v) { dm_log_init(&dm_write_log); dm_log_init_verbose(v + 3); } static int dm_lib_prereq (void) { char version[64]; int v[3]; #if defined(LIBDM_API_DEFERRED) int minv[3] = {1, 2, 89}; #elif defined(DM_SUBSYSTEM_UDEV_FLAG0) int minv[3] = {1, 2, 82}; #elif defined(LIBDM_API_COOKIE) int minv[3] = {1, 2, 38}; #else int minv[3] = {1, 2, 8}; #endif dm_get_library_version(version, sizeof(version)); condlog(3, "libdevmapper version %s", version); if (sscanf(version, "%d.%d.%d ", &v[0], &v[1], &v[2]) != 3) { condlog(0, "invalid libdevmapper version %s", version); return 1; } if VERSION_GE(v, minv) return 0; condlog(0, "libdevmapper version must be >= %d.%.2d.%.2d", minv[0], minv[1], minv[2]); return 1; } int dm_drv_version (unsigned int * version, char * str) { int r = 2; struct dm_task *dmt; struct dm_versions *target; struct dm_versions *last_target; unsigned int *v; version[0] = 0; version[1] = 0; version[2] = 0; if (!(dmt = dm_task_create(DM_DEVICE_LIST_VERSIONS))) return 1; dm_task_no_open_count(dmt); if (!dm_task_run(dmt)) { condlog(0, "Can not communicate with kernel DM"); goto out; } target = dm_task_get_versions(dmt); do { last_target = target; if (!strncmp(str, target->name, strlen(str))) { r = 1; break; } target = (void *) target + target->next; } while (last_target != target); if (r == 2) { condlog(0, "DM %s kernel driver not loaded", str); goto out; } v = target->version; version[0] = v[0]; version[1] = v[1]; version[2] = v[2]; r = 0; out: dm_task_destroy(dmt); return r; } static int dm_drv_prereq (unsigned int *ver) { unsigned int minv[3] = {1, 0, 3}; unsigned int version[3] = {0, 0, 0}; unsigned int * v = version; if (dm_drv_version(v, TGT_MPATH)) { /* in doubt return not capable */ return 1; } /* test request based multipath capability */ condlog(3, "DM multipath kernel driver v%u.%u.%u", v[0], v[1], v[2]); if (VERSION_GE(v, minv)) { ver[0] = v[0]; ver[1] = v[1]; ver[2] = v[2]; return 0; } condlog(0, "DM multipath kernel driver must be >= v%u.%u.%u", minv[0], minv[1], minv[2]); return 1; } static int dm_prereq(unsigned int *v) { if (dm_lib_prereq()) return 1; return dm_drv_prereq(v); } static int libmp_dm_udev_sync = 0; void libmp_udev_set_sync_support(int on) { libmp_dm_udev_sync = !!on; } void libmp_dm_init(void) { struct config *conf; conf = get_multipath_config(); dm_init(conf->verbosity); if (dm_prereq(conf->version)) exit(1); put_multipath_config(conf); dm_udev_set_sync_support(libmp_dm_udev_sync); } struct dm_task* libmp_dm_task_create(int task) { static pthread_once_t dm_initialized = PTHREAD_ONCE_INIT; pthread_once(&dm_initialized, libmp_dm_init); return dm_task_create(task); } #define do_deferred(x) ((x) == DEFERRED_REMOVE_ON || (x) == DEFERRED_REMOVE_IN_PROGRESS) static int dm_simplecmd (int task, const char *name, int no_flush, int need_sync, uint16_t udev_flags, int deferred_remove) { int r = 0; int udev_wait_flag = ((need_sync || udev_flags) && (task == DM_DEVICE_RESUME || task == DM_DEVICE_REMOVE)); uint32_t cookie = 0; struct dm_task *dmt; if (!(dmt = libmp_dm_task_create (task))) return 0; if (!dm_task_set_name (dmt, name)) goto out; dm_task_no_open_count(dmt); dm_task_skip_lockfs(dmt); /* for DM_DEVICE_RESUME */ #ifdef LIBDM_API_FLUSH if (no_flush) dm_task_no_flush(dmt); /* for DM_DEVICE_SUSPEND/RESUME */ #endif #ifdef LIBDM_API_DEFERRED if (do_deferred(deferred_remove)) dm_task_deferred_remove(dmt); #endif if (udev_wait_flag && !dm_task_set_cookie(dmt, &cookie, DM_UDEV_DISABLE_LIBRARY_FALLBACK | udev_flags)) goto out; r = dm_task_run (dmt); if (udev_wait_flag) dm_udev_wait(cookie); out: dm_task_destroy (dmt); return r; } int dm_simplecmd_flush (int task, const char *name, uint16_t udev_flags) { return dm_simplecmd(task, name, 0, 1, udev_flags, 0); } int dm_simplecmd_noflush (int task, const char *name, uint16_t udev_flags) { return dm_simplecmd(task, name, 1, 1, udev_flags, 0); } static int dm_device_remove (const char *name, int needsync, int deferred_remove) { return dm_simplecmd(DM_DEVICE_REMOVE, name, 0, needsync, 0, deferred_remove); } static int dm_addmap (int task, const char *target, struct multipath *mpp, char * params, int ro, uint16_t udev_flags) { int r = 0; struct dm_task *dmt; char *prefixed_uuid = NULL; uint32_t cookie = 0; /* Need to add this here to allow 0 to be passed in udev_flags */ udev_flags |= DM_UDEV_DISABLE_LIBRARY_FALLBACK; if (!(dmt = libmp_dm_task_create (task))) return 0; if (!dm_task_set_name (dmt, mpp->alias)) goto addout; if (!dm_task_add_target (dmt, 0, mpp->size, target, params)) goto addout; if (ro) dm_task_set_ro(dmt); if (task == DM_DEVICE_CREATE) { if (strlen(mpp->wwid) > 0) { prefixed_uuid = MALLOC(UUID_PREFIX_LEN + strlen(mpp->wwid) + 1); if (!prefixed_uuid) { condlog(0, "cannot create prefixed uuid : %s", strerror(errno)); goto addout; } sprintf(prefixed_uuid, UUID_PREFIX "%s", mpp->wwid); if (!dm_task_set_uuid(dmt, prefixed_uuid)) goto freeout; } dm_task_skip_lockfs(dmt); #ifdef LIBDM_API_FLUSH dm_task_no_flush(dmt); #endif } if (mpp->attribute_flags & (1 << ATTR_MODE) && !dm_task_set_mode(dmt, mpp->mode)) goto freeout; if (mpp->attribute_flags & (1 << ATTR_UID) && !dm_task_set_uid(dmt, mpp->uid)) goto freeout; if (mpp->attribute_flags & (1 << ATTR_GID) && !dm_task_set_gid(dmt, mpp->gid)) goto freeout; condlog(4, "%s: %s [0 %llu %s %s]", mpp->alias, task == DM_DEVICE_RELOAD ? "reload" : "addmap", mpp->size, target, params); dm_task_no_open_count(dmt); if (task == DM_DEVICE_CREATE && !dm_task_set_cookie(dmt, &cookie, udev_flags)) goto freeout; r = dm_task_run (dmt); if (task == DM_DEVICE_CREATE) dm_udev_wait(cookie); freeout: if (prefixed_uuid) FREE(prefixed_uuid); addout: dm_task_destroy (dmt); return r; } static uint16_t build_udev_flags(const struct multipath *mpp, int reload) { /* DM_UDEV_DISABLE_LIBRARY_FALLBACK is added in dm_addmap */ return (mpp->skip_kpartx == SKIP_KPARTX_ON ? MPATH_UDEV_NO_KPARTX_FLAG : 0) | (mpp->nr_active == 0 ? MPATH_UDEV_NO_PATHS_FLAG : 0) | (reload && !mpp->force_udev_reload ? MPATH_UDEV_RELOAD_FLAG : 0); } int dm_addmap_create (struct multipath *mpp, char * params) { int ro; uint16_t udev_flags = build_udev_flags(mpp, 0); for (ro = 0; ro <= 1; ro++) { int err; if (dm_addmap(DM_DEVICE_CREATE, TGT_MPATH, mpp, params, ro, udev_flags)) return 1; /* * DM_DEVICE_CREATE is actually DM_DEV_CREATE + DM_TABLE_LOAD. * Failing the second part leaves an empty map. Clean it up. */ err = errno; if (dm_map_present(mpp->alias)) { condlog(3, "%s: failed to load map (a path might be in use)", mpp->alias); dm_flush_map_nosync(mpp->alias); } if (err != EROFS) { condlog(3, "%s: failed to load map, error %d", mpp->alias, err); break; } } return 0; } #define ADDMAP_RW 0 #define ADDMAP_RO 1 int dm_addmap_reload(struct multipath *mpp, char *params, int flush) { int r = 0; uint16_t udev_flags = build_udev_flags(mpp, 1); /* * DM_DEVICE_RELOAD cannot wait on a cookie, as * the cookie will only ever be released after an * DM_DEVICE_RESUME. So call DM_DEVICE_RESUME * after each successful call to DM_DEVICE_RELOAD. */ if (!mpp->force_readonly) r = dm_addmap(DM_DEVICE_RELOAD, TGT_MPATH, mpp, params, ADDMAP_RW, 0); if (!r) { if (!mpp->force_readonly && errno != EROFS) return 0; r = dm_addmap(DM_DEVICE_RELOAD, TGT_MPATH, mpp, params, ADDMAP_RO, 0); } if (r) r = dm_simplecmd(DM_DEVICE_RESUME, mpp->alias, !flush, 1, udev_flags, 0); if (r) return r; /* If the resume failed, dm will leave the device suspended, and * drop the new table, so doing a second resume will try using * the original table */ if (dm_is_suspended(mpp->alias)) dm_simplecmd(DM_DEVICE_RESUME, mpp->alias, !flush, 1, udev_flags, 0); return 0; } static int do_get_info(const char *name, struct dm_info *info) { int r = -1; struct dm_task *dmt; if (!(dmt = libmp_dm_task_create(DM_DEVICE_INFO))) return r; if (!dm_task_set_name(dmt, name)) goto out; dm_task_no_open_count(dmt); if (!dm_task_run(dmt)) goto out; if (!dm_task_get_info(dmt, info)) goto out; if (!info->exists) goto out; r = 0; out: dm_task_destroy(dmt); return r; } int dm_map_present(const char * str) { struct dm_info info; return (do_get_info(str, &info) == 0); } int dm_get_map(const char *name, unsigned long long *size, char *outparams) { int r = 1; struct dm_task *dmt; uint64_t start, length; char *target_type = NULL; char *params = NULL; if (!(dmt = libmp_dm_task_create(DM_DEVICE_TABLE))) return 1; if (!dm_task_set_name(dmt, name)) goto out; dm_task_no_open_count(dmt); if (!dm_task_run(dmt)) goto out; /* Fetch 1st target */ dm_get_next_target(dmt, NULL, &start, &length, &target_type, ¶ms); if (size) *size = length; if (!outparams) { r = 0; goto out; } if (snprintf(outparams, PARAMS_SIZE, "%s", params) <= PARAMS_SIZE) r = 0; out: dm_task_destroy(dmt); return r; } static int dm_get_prefixed_uuid(const char *name, char *uuid) { struct dm_task *dmt; const char *uuidtmp; int r = 1; dmt = libmp_dm_task_create(DM_DEVICE_INFO); if (!dmt) return 1; if (!dm_task_set_name (dmt, name)) goto uuidout; if (!dm_task_run(dmt)) goto uuidout; uuidtmp = dm_task_get_uuid(dmt); if (uuidtmp) strcpy(uuid, uuidtmp); else uuid[0] = '\0'; r = 0; uuidout: dm_task_destroy(dmt); return r; } int dm_get_uuid(const char *name, char *uuid) { if (dm_get_prefixed_uuid(name, uuid)) return 1; if (!strncmp(uuid, UUID_PREFIX, UUID_PREFIX_LEN)) memmove(uuid, uuid + UUID_PREFIX_LEN, strlen(uuid + UUID_PREFIX_LEN) + 1); return 0; } static int is_mpath_part(const char *part_name, const char *map_name) { char *p; char part_uuid[WWID_SIZE], map_uuid[WWID_SIZE]; if (dm_get_prefixed_uuid(part_name, part_uuid)) return 0; if (dm_get_prefixed_uuid(map_name, map_uuid)) return 0; if (strncmp(part_uuid, "part", 4) != 0) return 0; p = strstr(part_uuid, UUID_PREFIX); if (p && !strcmp(p, map_uuid)) return 1; return 0; } int dm_get_status(char *name, char *outstatus) { int r = 1; struct dm_task *dmt; uint64_t start, length; char *target_type = NULL; char *status = NULL; if (!(dmt = libmp_dm_task_create(DM_DEVICE_STATUS))) return 1; if (!dm_task_set_name(dmt, name)) goto out; dm_task_no_open_count(dmt); if (!dm_task_run(dmt)) goto out; /* Fetch 1st target */ dm_get_next_target(dmt, NULL, &start, &length, &target_type, &status); if (!status) { condlog(2, "get null status."); goto out; } if (snprintf(outstatus, PARAMS_SIZE, "%s", status) <= PARAMS_SIZE) r = 0; out: if (r) condlog(0, "%s: error getting map status string", name); dm_task_destroy(dmt); return r; } /* * returns: * 1 : match * 0 : no match * -1 : empty map, or more than 1 target */ int dm_type(const char *name, char *type) { int r = 0; struct dm_task *dmt; uint64_t start, length; char *target_type = NULL; char *params; if (!(dmt = libmp_dm_task_create(DM_DEVICE_TABLE))) return 0; if (!dm_task_set_name(dmt, name)) goto out; dm_task_no_open_count(dmt); if (!dm_task_run(dmt)) goto out; /* Fetch 1st target */ if (dm_get_next_target(dmt, NULL, &start, &length, &target_type, ¶ms) != NULL) /* multiple targets */ r = -1; else if (!target_type) r = -1; else if (!strcmp(target_type, type)) r = 1; out: dm_task_destroy(dmt); return r; } int dm_is_mpath(const char *name) { int r = 0; struct dm_task *dmt; struct dm_info info; uint64_t start, length; char *target_type = NULL; char *params; const char *uuid; if (!(dmt = libmp_dm_task_create(DM_DEVICE_TABLE))) return 0; if (!dm_task_set_name(dmt, name)) goto out; dm_task_no_open_count(dmt); if (!dm_task_run(dmt)) goto out; if (!dm_task_get_info(dmt, &info) || !info.exists) goto out; uuid = dm_task_get_uuid(dmt); if (!uuid || strncmp(uuid, UUID_PREFIX, UUID_PREFIX_LEN) != 0) goto out; /* Fetch 1st target */ dm_get_next_target(dmt, NULL, &start, &length, &target_type, ¶ms); if (!target_type || strcmp(target_type, TGT_MPATH) != 0) goto out; r = 1; out: dm_task_destroy(dmt); return r; } static int dm_dev_t (const char * mapname, char * dev_t, int len) { struct dm_info info; if (do_get_info(mapname, &info) != 0) return 1; if (snprintf(dev_t, len, "%i:%i", info.major, info.minor) > len) return 1; return 0; } int dm_get_opencount (const char * mapname) { int r = -1; struct dm_task *dmt; struct dm_info info; if (!(dmt = libmp_dm_task_create(DM_DEVICE_INFO))) return 0; if (!dm_task_set_name(dmt, mapname)) goto out; if (!dm_task_run(dmt)) goto out; if (!dm_task_get_info(dmt, &info)) goto out; if (!info.exists) goto out; r = info.open_count; out: dm_task_destroy(dmt); return r; } int dm_get_major_minor(const char *name, int *major, int *minor) { struct dm_info info; if (do_get_info(name, &info) != 0) return -1; *major = info.major; *minor = info.minor; return 0; } static int has_partmap(const char *name, void *data) { return 1; } static int partmap_in_use(const char *name, void *data) { int part_count, *ret_count = (int *)data; int open_count = dm_get_opencount(name); if (ret_count) (*ret_count)++; part_count = 0; if (open_count) { if (do_foreach_partmaps(name, partmap_in_use, &part_count)) return 1; if (open_count != part_count) { condlog(2, "%s: map in use", name); return 1; } } return 0; } int _dm_flush_map (const char * mapname, int need_sync, int deferred_remove, int need_suspend, int retries) { int r; int queue_if_no_path = 0; int udev_flags = 0; unsigned long long mapsize; char params[PARAMS_SIZE] = {0}; if (!dm_is_mpath(mapname)) return 0; /* nothing to do */ /* if the device currently has no partitions, do not run kpartx on it if you fail to delete it */ if (do_foreach_partmaps(mapname, has_partmap, NULL) == 0) udev_flags |= MPATH_UDEV_NO_KPARTX_FLAG; /* If you aren't doing a deferred remove, make sure that no * devices are in use */ if (!do_deferred(deferred_remove) && partmap_in_use(mapname, NULL)) return 1; if (need_suspend && !dm_get_map(mapname, &mapsize, params) && strstr(params, "queue_if_no_path")) { if (!dm_queue_if_no_path((char *)mapname, 0)) queue_if_no_path = 1; else /* Leave queue_if_no_path alone if unset failed */ queue_if_no_path = -1; } if (dm_remove_partmaps(mapname, need_sync, deferred_remove)) return 1; if (!do_deferred(deferred_remove) && dm_get_opencount(mapname)) { condlog(2, "%s: map in use", mapname); return 1; } do { if (need_suspend && queue_if_no_path != -1) dm_simplecmd_flush(DM_DEVICE_SUSPEND, mapname, 0); r = dm_device_remove(mapname, need_sync, deferred_remove); if (r) { if (do_deferred(deferred_remove) && dm_map_present(mapname)) { condlog(4, "multipath map %s remove deferred", mapname); return 2; } condlog(4, "multipath map %s removed", mapname); return 0; } else { condlog(2, "failed to remove multipath map %s", mapname); if (need_suspend && queue_if_no_path != -1) { dm_simplecmd_noflush(DM_DEVICE_RESUME, mapname, udev_flags); } } if (retries) sleep(1); } while (retries-- > 0); if (queue_if_no_path == 1) dm_queue_if_no_path((char *)mapname, 1); return 1; } #ifdef LIBDM_API_DEFERRED int dm_flush_map_nopaths(const char * mapname, int deferred_remove) { return _dm_flush_map(mapname, 1, deferred_remove, 0, 0); } #else int dm_flush_map_nopaths(const char * mapname, int deferred_remove) { return _dm_flush_map(mapname, 1, 0, 0, 0); } #endif int dm_flush_maps (int retries) { int r = 0; struct dm_task *dmt; struct dm_names *names; unsigned next = 0; if (!(dmt = libmp_dm_task_create (DM_DEVICE_LIST))) return 0; dm_task_no_open_count(dmt); if (!dm_task_run (dmt)) goto out; if (!(names = dm_task_get_names (dmt))) goto out; if (!names->dev) goto out; do { r |= dm_suspend_and_flush_map(names->name, retries); next = names->next; names = (void *) names + next; } while (next); out: dm_task_destroy (dmt); return r; } int dm_message(const char * mapname, char * message) { int r = 1; struct dm_task *dmt; if (!(dmt = libmp_dm_task_create(DM_DEVICE_TARGET_MSG))) return 1; if (!dm_task_set_name(dmt, mapname)) goto out; if (!dm_task_set_sector(dmt, 0)) goto out; if (!dm_task_set_message(dmt, message)) goto out; dm_task_no_open_count(dmt); if (!dm_task_run(dmt)) goto out; r = 0; out: if (r) condlog(0, "DM message failed [%s]", message); dm_task_destroy(dmt); return r; } int dm_fail_path(char * mapname, char * path) { char message[32]; if (snprintf(message, 32, "fail_path %s", path) > 32) return 1; return dm_message(mapname, message); } int dm_reinstate_path(char * mapname, char * path) { char message[32]; if (snprintf(message, 32, "reinstate_path %s", path) > 32) return 1; return dm_message(mapname, message); } int dm_queue_if_no_path(char *mapname, int enable) { char *message; if (enable) message = "queue_if_no_path"; else message = "fail_if_no_path"; return dm_message(mapname, message); } static int dm_groupmsg (char * msg, char * mapname, int index) { char message[32]; if (snprintf(message, 32, "%s_group %i", msg, index) > 32) return 1; return dm_message(mapname, message); } int dm_switchgroup(char * mapname, int index) { return dm_groupmsg("switch", mapname, index); } int dm_enablegroup(char * mapname, int index) { return dm_groupmsg("enable", mapname, index); } int dm_disablegroup(char * mapname, int index) { return dm_groupmsg("disable", mapname, index); } struct multipath *dm_get_multipath(const char *name) { struct multipath *mpp = NULL; mpp = alloc_multipath(); if (!mpp) return NULL; mpp->alias = STRDUP(name); if (!mpp->alias) goto out; if (dm_get_map(name, &mpp->size, NULL)) goto out; dm_get_uuid(name, mpp->wwid); dm_get_info(name, &mpp->dmi); return mpp; out: free_multipath(mpp, KEEP_PATHS); return NULL; } int dm_get_maps (vector mp) { struct multipath * mpp; int r = 1; struct dm_task *dmt; struct dm_names *names; unsigned next = 0; if (!mp) return 1; if (!(dmt = libmp_dm_task_create(DM_DEVICE_LIST))) return 1; dm_task_no_open_count(dmt); if (!dm_task_run(dmt)) goto out; if (!(names = dm_task_get_names(dmt))) goto out; if (!names->dev) { r = 0; /* this is perfectly valid */ goto out; } do { if (!dm_is_mpath(names->name)) goto next; mpp = dm_get_multipath(names->name); if (!mpp) goto out; if (!vector_alloc_slot(mp)) goto out; vector_set_slot(mp, mpp); mpp = NULL; next: next = names->next; names = (void *) names + next; } while (next); r = 0; goto out; out: dm_task_destroy (dmt); return r; } int dm_geteventnr (char *name) { struct dm_info info; if (do_get_info(name, &info) != 0) return -1; return info.event_nr; } int dm_is_suspended(const char *name) { struct dm_info info; if (do_get_info(name, &info) != 0) return -1; return info.suspended; } char * dm_mapname(int major, int minor) { char * response = NULL; const char *map; struct dm_task *dmt; int r; int loop = MAX_WAIT * LOOPS_PER_SEC; if (!(dmt = libmp_dm_task_create(DM_DEVICE_STATUS))) return NULL; if (!dm_task_set_major(dmt, major) || !dm_task_set_minor(dmt, minor)) goto bad; dm_task_no_open_count(dmt); /* * device map might not be ready when we get here from * daemon uev_trigger -> uev_add_map */ while (--loop) { r = dm_task_run(dmt); if (r) break; usleep(1000 * 1000 / LOOPS_PER_SEC); } if (!r) { condlog(0, "%i:%i: timeout fetching map name", major, minor); goto bad; } map = dm_task_get_name(dmt); if (map && strlen(map)) response = STRDUP((char *)dm_task_get_name(dmt)); dm_task_destroy(dmt); return response; bad: dm_task_destroy(dmt); condlog(0, "%i:%i: error fetching map name", major, minor); return NULL; } static int do_foreach_partmaps (const char * mapname, int (*partmap_func)(const char *, void *), void *data) { struct dm_task *dmt; struct dm_names *names; unsigned next = 0; char params[PARAMS_SIZE]; unsigned long long size; char dev_t[32]; int r = 1; char *p; if (!(dmt = libmp_dm_task_create(DM_DEVICE_LIST))) return 1; dm_task_no_open_count(dmt); if (!dm_task_run(dmt)) goto out; if (!(names = dm_task_get_names(dmt))) goto out; if (!names->dev) { r = 0; /* this is perfectly valid */ goto out; } if (dm_dev_t(mapname, &dev_t[0], 32)) goto out; do { if ( /* * if there is only a single "linear" target */ (dm_type(names->name, TGT_PART) == 1) && /* * and the uuid of the target is a partition of the * uuid of the multipath device */ is_mpath_part(names->name, mapname) && /* * and we can fetch the map table from the kernel */ !dm_get_map(names->name, &size, ¶ms[0]) && /* * and the table maps over the multipath map */ (p = strstr(params, dev_t)) && !isdigit(*(p + strlen(dev_t))) ) { if (partmap_func(names->name, data) != 0) goto out; } next = names->next; names = (void *) names + next; } while (next); r = 0; out: dm_task_destroy (dmt); return r; } struct remove_data { int need_sync; int deferred_remove; }; static int remove_partmap(const char *name, void *data) { struct remove_data *rd = (struct remove_data *)data; if (dm_get_opencount(name)) { dm_remove_partmaps(name, rd->need_sync, rd->deferred_remove); if (!do_deferred(rd->deferred_remove) && dm_get_opencount(name)) { condlog(2, "%s: map in use", name); return 1; } } condlog(4, "partition map %s removed", name); dm_device_remove(name, rd->need_sync, rd->deferred_remove); return 0; } int dm_remove_partmaps (const char * mapname, int need_sync, int deferred_remove) { struct remove_data rd = { need_sync, deferred_remove }; return do_foreach_partmaps(mapname, remove_partmap, &rd); } #ifdef LIBDM_API_DEFERRED static int cancel_remove_partmap (const char *name, void *unused) { if (dm_get_opencount(name)) dm_cancel_remove_partmaps(name); if (dm_message(name, "@cancel_deferred_remove") != 0) condlog(0, "%s: can't cancel deferred remove: %s", name, strerror(errno)); return 0; } static int dm_get_deferred_remove (char * mapname) { struct dm_info info; if (do_get_info(mapname, &info) != 0) return -1; return info.deferred_remove; } static int dm_cancel_remove_partmaps(const char * mapname) { return do_foreach_partmaps(mapname, cancel_remove_partmap, NULL); } int dm_cancel_deferred_remove (struct multipath *mpp) { int r = 0; if (!dm_get_deferred_remove(mpp->alias)) return 0; if (mpp->deferred_remove == DEFERRED_REMOVE_IN_PROGRESS) mpp->deferred_remove = DEFERRED_REMOVE_ON; dm_cancel_remove_partmaps(mpp->alias); r = dm_message(mpp->alias, "@cancel_deferred_remove"); if (r) condlog(0, "%s: can't cancel deferred remove: %s", mpp->alias, strerror(errno)); else condlog(2, "%s: canceled deferred remove", mpp->alias); return r; } #else int dm_cancel_deferred_remove (struct multipath *mpp) { return 0; } #endif static struct dm_info * alloc_dminfo (void) { return MALLOC(sizeof(struct dm_info)); } int dm_get_info (const char * mapname, struct dm_info ** dmi) { if (!mapname) return 1; if (!*dmi) *dmi = alloc_dminfo(); if (!*dmi) return 1; if (do_get_info(mapname, *dmi) != 0) { memset(*dmi, 0, sizeof(struct dm_info)); FREE(*dmi); *dmi = NULL; return 1; } return 0; } struct rename_data { const char *old; char *new; char *delim; }; static int rename_partmap (const char *name, void *data) { char buff[PARAMS_SIZE]; int offset; struct rename_data *rd = (struct rename_data *)data; if (strncmp(name, rd->old, strlen(rd->old)) != 0) return 0; for (offset = strlen(rd->old); name[offset] && !(isdigit(name[offset])); offset++); /* do nothing */ snprintf(buff, PARAMS_SIZE, "%s%s%s", rd->new, rd->delim, name + offset); dm_rename(name, buff, rd->delim, SKIP_KPARTX_OFF); condlog(4, "partition map %s renamed", name); return 0; } int dm_rename_partmaps (const char * old, char * new, char *delim) { struct rename_data rd; rd.old = old; rd.new = new; if (delim) rd.delim = delim; else { if (isdigit(new[strlen(new)-1])) rd.delim = "p"; else rd.delim = ""; } return do_foreach_partmaps(old, rename_partmap, &rd); } int dm_rename (const char * old, char * new, char *delim, int skip_kpartx) { int r = 0; struct dm_task *dmt; uint32_t cookie = 0; uint16_t udev_flags = DM_UDEV_DISABLE_LIBRARY_FALLBACK | ((skip_kpartx == SKIP_KPARTX_ON)? MPATH_UDEV_NO_KPARTX_FLAG : 0); if (dm_rename_partmaps(old, new, delim)) return r; if (!(dmt = libmp_dm_task_create(DM_DEVICE_RENAME))) return r; if (!dm_task_set_name(dmt, old)) goto out; if (!dm_task_set_newname(dmt, new)) goto out; dm_task_no_open_count(dmt); if (!dm_task_set_cookie(dmt, &cookie, udev_flags)) goto out; r = dm_task_run(dmt); dm_udev_wait(cookie); out: dm_task_destroy(dmt); return r; } void dm_reassign_deps(char *table, char *dep, char *newdep) { char *p, *n; char *newtable; newtable = strdup(table); if (!newtable) return; p = strstr(newtable, dep); n = table + (p - newtable); strcpy(n, newdep); n += strlen(newdep); p += strlen(dep); strcat(n, p); free(newtable); } int dm_reassign_table(const char *name, char *old, char *new) { int r = 0, modified = 0; uint64_t start, length; struct dm_task *dmt, *reload_dmt; char *target, *params = NULL; char *buff; void *next = NULL; if (!(dmt = libmp_dm_task_create(DM_DEVICE_TABLE))) return 0; if (!dm_task_set_name(dmt, name)) goto out; dm_task_no_open_count(dmt); if (!dm_task_run(dmt)) goto out; if (!(reload_dmt = libmp_dm_task_create(DM_DEVICE_RELOAD))) goto out; if (!dm_task_set_name(reload_dmt, name)) goto out_reload; do { next = dm_get_next_target(dmt, next, &start, &length, &target, ¶ms); buff = strdup(params); if (!buff) { condlog(3, "%s: failed to replace target %s, " "out of memory", name, target); goto out_reload; } if (strcmp(target, TGT_MPATH) && strstr(params, old)) { condlog(3, "%s: replace target %s %s", name, target, buff); dm_reassign_deps(buff, old, new); condlog(3, "%s: with target %s %s", name, target, buff); modified++; } dm_task_add_target(reload_dmt, start, length, target, buff); free(buff); } while (next); if (modified) { dm_task_no_open_count(reload_dmt); if (!dm_task_run(reload_dmt)) { condlog(3, "%s: failed to reassign targets", name); goto out_reload; } dm_simplecmd_noflush(DM_DEVICE_RESUME, name, MPATH_UDEV_RELOAD_FLAG); } r = 1; out_reload: dm_task_destroy(reload_dmt); out: dm_task_destroy(dmt); return r; } /* * Reassign existing device-mapper table(s) to not use * the block devices but point to the multipathed * device instead */ int dm_reassign(const char *mapname) { struct dm_deps *deps; struct dm_task *dmt; struct dm_info info; char dev_t[32], dm_dep[32]; int r = 0, i; if (dm_dev_t(mapname, &dev_t[0], 32)) { condlog(3, "%s: failed to get device number", mapname); return 1; } if (!(dmt = libmp_dm_task_create(DM_DEVICE_DEPS))) { condlog(3, "%s: couldn't make dm task", mapname); return 0; } if (!dm_task_set_name(dmt, mapname)) goto out; dm_task_no_open_count(dmt); if (!dm_task_run(dmt)) goto out; if (!dm_task_get_info(dmt, &info)) goto out; if (!(deps = dm_task_get_deps(dmt))) goto out; if (!info.exists) goto out; for (i = 0; i < deps->count; i++) { sprintf(dm_dep, "%d:%d", major(deps->device[i]), minor(deps->device[i])); sysfs_check_holders(dm_dep, dev_t); } r = 1; out: dm_task_destroy (dmt); return r; } int dm_setgeometry(struct multipath *mpp) { struct dm_task *dmt; struct path *pp; char heads[4], sectors[4]; char cylinders[10], start[32]; int r = 0; if (!mpp) return 1; pp = first_path(mpp); if (!pp) { condlog(3, "%s: no path for geometry", mpp->alias); return 1; } if (pp->geom.cylinders == 0 || pp->geom.heads == 0 || pp->geom.sectors == 0) { condlog(3, "%s: invalid geometry on %s", mpp->alias, pp->dev); return 1; } if (!(dmt = libmp_dm_task_create(DM_DEVICE_SET_GEOMETRY))) return 0; if (!dm_task_set_name(dmt, mpp->alias)) goto out; dm_task_no_open_count(dmt); /* What a sick interface ... */ snprintf(heads, 4, "%u", pp->geom.heads); snprintf(sectors, 4, "%u", pp->geom.sectors); snprintf(cylinders, 10, "%u", pp->geom.cylinders); snprintf(start, 32, "%lu", pp->geom.start); if (!dm_task_set_geometry(dmt, cylinders, heads, sectors, start)) { condlog(3, "%s: Failed to set geometry", mpp->alias); goto out; } r = dm_task_run(dmt); out: dm_task_destroy(dmt); return r; } multipath-tools-0.7.4/libmultipath/devmapper.h000066400000000000000000000053351320314174000215220ustar00rootroot00000000000000#ifndef _DEVMAPPER_H #define _DEVMAPPER_H #include "structs.h" #define TGT_MPATH "multipath" #define TGT_PART "linear" #ifdef DM_SUBSYSTEM_UDEV_FLAG0 #define MPATH_UDEV_RELOAD_FLAG DM_SUBSYSTEM_UDEV_FLAG0 #else #define MPATH_UDEV_RELOAD_FLAG 0 #endif #ifdef DM_SUBSYSTEM_UDEV_FLAG1 #define MPATH_UDEV_NO_KPARTX_FLAG DM_SUBSYSTEM_UDEV_FLAG1 #else #define MPATH_UDEV_NO_KPARTX_FLAG 0 #endif #ifdef DM_SUBSYSTEM_UDEV_FLAG2 #define MPATH_UDEV_NO_PATHS_FLAG DM_SUBSYSTEM_UDEV_FLAG2 #else #define MPATH_UDEV_NO_PATHS_FLAG 0 #endif void dm_init(int verbosity); void libmp_dm_init(void); void libmp_udev_set_sync_support(int on); struct dm_task *libmp_dm_task_create(int task); int dm_drv_version (unsigned int * version, char * str); int dm_simplecmd_flush (int, const char *, uint16_t); int dm_simplecmd_noflush (int, const char *, uint16_t); int dm_addmap_create (struct multipath *mpp, char *params); int dm_addmap_reload (struct multipath *mpp, char *params, int flush); int dm_map_present (const char *); int dm_get_map(const char *, unsigned long long *, char *); int dm_get_status(char *, char *); int dm_type(const char *, char *); int dm_is_mpath(const char *); int _dm_flush_map (const char *, int, int, int, int); int dm_flush_map_nopaths(const char * mapname, int deferred_remove); #define dm_flush_map(mapname) _dm_flush_map(mapname, 1, 0, 0, 0) #define dm_flush_map_nosync(mapname) _dm_flush_map(mapname, 0, 0, 0, 0) #define dm_suspend_and_flush_map(mapname, retries) \ _dm_flush_map(mapname, 1, 0, 1, retries) int dm_cancel_deferred_remove(struct multipath *mpp); int dm_flush_maps (int retries); int dm_fail_path(char * mapname, char * path); int dm_reinstate_path(char * mapname, char * path); int dm_queue_if_no_path(char *mapname, int enable); int dm_switchgroup(char * mapname, int index); int dm_enablegroup(char * mapname, int index); int dm_disablegroup(char * mapname, int index); int dm_get_maps (vector mp); int dm_geteventnr (char *name); int dm_is_suspended(const char *name); int dm_get_major_minor (const char *name, int *major, int *minor); char * dm_mapname(int major, int minor); int dm_remove_partmaps (const char * mapname, int need_sync, int deferred_remove); int dm_get_uuid(const char *name, char *uuid); int dm_get_info (const char * mapname, struct dm_info ** dmi); int dm_rename (const char * old, char * new, char * delim, int skip_kpartx); int dm_reassign(const char * mapname); int dm_reassign_table(const char *name, char *old, char *new); int dm_setgeometry(struct multipath *mpp); struct multipath *dm_get_multipath(const char *name); #define VERSION_GE(v, minv) ( \ (v[0] > minv[0]) || \ ((v[0] == minv[0]) && (v[1] > minv[1])) || \ ((v[0] == minv[0]) && (v[1] == minv[1]) && (v[2] >= minv[2])) \ ) #endif /* _DEVMAPPER_H */ multipath-tools-0.7.4/libmultipath/dict.c000066400000000000000000001514121320314174000204530ustar00rootroot00000000000000/* * Based on Alexandre Cassen template for keepalived * Copyright (c) 2004, 2005, 2006 Christophe Varoqui * Copyright (c) 2005 Benjamin Marzinski, Redhat * Copyright (c) 2005 Kiyoshi Ueda, NEC */ #include #include #include #include "checkers.h" #include "vector.h" #include "hwtable.h" #include "structs.h" #include "parser.h" #include "config.h" #include "debug.h" #include "memory.h" #include "pgpolicies.h" #include "blacklist.h" #include "defaults.h" #include "prio.h" #include "util.h" #include #include #include "mpath_cmd.h" static int set_int(vector strvec, void *ptr) { int *int_ptr = (int *)ptr; char * buff; buff = VECTOR_SLOT(strvec, 1); *int_ptr = atoi(buff); return 0; } static int set_str(vector strvec, void *ptr) { char **str_ptr = (char **)ptr; if (*str_ptr) FREE(*str_ptr); *str_ptr = set_value(strvec); if (!*str_ptr) return 1; return 0; } static int set_yes_no(vector strvec, void *ptr) { char * buff; int *int_ptr = (int *)ptr; buff = set_value(strvec); if (!buff) return 1; if (strcmp(buff, "yes") == 0 || strcmp(buff, "1") == 0) *int_ptr = YN_YES; else *int_ptr = YN_NO; FREE(buff); return 0; } static int set_yes_no_undef(vector strvec, void *ptr) { char * buff; int *int_ptr = (int *)ptr; buff = set_value(strvec); if (!buff) return 1; if (strcmp(buff, "no") == 0 || strcmp(buff, "0") == 0) *int_ptr = YNU_NO; else if (strcmp(buff, "yes") == 0 || strcmp(buff, "1") == 0) *int_ptr = YNU_YES; else *int_ptr = YNU_UNDEF; FREE(buff); return 0; } static int print_int (char *buff, int len, void *ptr) { int *int_ptr = (int *)ptr; return snprintf(buff, len, "%i", *int_ptr); } static int print_nonzero (char *buff, int len, void *ptr) { int *int_ptr = (int *)ptr; if (!*int_ptr) return 0; return snprintf(buff, len, "%i", *int_ptr); } static int print_str (char *buff, int len, void *ptr) { char **str_ptr = (char **)ptr; if (!*str_ptr) return 0; return snprintf(buff, len, "\"%s\"", *str_ptr); } static int print_yes_no (char *buff, int len, void *ptr) { int *int_ptr = (int *)ptr; return snprintf(buff, len, "\"%s\"", (*int_ptr == YN_NO)? "no" : "yes"); } static int print_yes_no_undef (char *buff, int len, void *ptr) { int *int_ptr = (int *)ptr; if (!*int_ptr) return 0; return snprintf(buff, len, "\"%s\"", (*int_ptr == YNU_NO)? "no" : "yes"); } #define declare_def_handler(option, function) \ static int \ def_ ## option ## _handler (struct config *conf, vector strvec) \ { \ return function (strvec, &conf->option); \ } #define declare_def_snprint(option, function) \ static int \ snprint_def_ ## option (struct config *conf, char * buff, int len, void * data) \ { \ return function (buff, len, &conf->option); \ } #define declare_def_snprint_defint(option, function, value) \ static int \ snprint_def_ ## option (struct config *conf, char * buff, int len, void * data) \ { \ int i = value; \ if (!conf->option) \ return function (buff, len, &i); \ return function (buff, len, &conf->option); \ } #define declare_def_snprint_defstr(option, function, value) \ static int \ snprint_def_ ## option (struct config *conf, char * buff, int len, void * data) \ { \ char *s = value; \ if (!conf->option) \ return function (buff, len, &s); \ return function (buff, len, &conf->option); \ } #define declare_hw_handler(option, function) \ static int \ hw_ ## option ## _handler (struct config *conf, vector strvec) \ { \ struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable); \ if (!hwe) \ return 1; \ return function (strvec, &hwe->option); \ } #define declare_hw_snprint(option, function) \ static int \ snprint_hw_ ## option (struct config *conf, char * buff, int len, void * data) \ { \ struct hwentry * hwe = (struct hwentry *)data; \ return function (buff, len, &hwe->option); \ } #define declare_ovr_handler(option, function) \ static int \ ovr_ ## option ## _handler (struct config *conf, vector strvec) \ { \ if (!conf->overrides) \ return 1; \ return function (strvec, &conf->overrides->option); \ } #define declare_ovr_snprint(option, function) \ static int \ snprint_ovr_ ## option (struct config *conf, char * buff, int len, void * data) \ { \ return function (buff, len, &conf->overrides->option); \ } #define declare_mp_handler(option, function) \ static int \ mp_ ## option ## _handler (struct config *conf, vector strvec) \ { \ struct mpentry * mpe = VECTOR_LAST_SLOT(conf->mptable); \ if (!mpe) \ return 1; \ return function (strvec, &mpe->option); \ } #define declare_mp_snprint(option, function) \ static int \ snprint_mp_ ## option (struct config *conf, char * buff, int len, void * data) \ { \ struct mpentry * mpe = (struct mpentry *)data; \ return function (buff, len, &mpe->option); \ } declare_def_handler(checkint, set_int) declare_def_snprint(checkint, print_int) declare_def_handler(max_checkint, set_int) declare_def_snprint(max_checkint, print_int) declare_def_handler(verbosity, set_int) declare_def_snprint(verbosity, print_int) declare_def_handler(reassign_maps, set_yes_no) declare_def_snprint(reassign_maps, print_yes_no) declare_def_handler(multipath_dir, set_str) declare_def_snprint(multipath_dir, print_str) declare_def_handler(partition_delim, set_str) declare_def_snprint(partition_delim, print_str) declare_def_handler(find_multipaths, set_yes_no) declare_def_snprint(find_multipaths, print_yes_no) declare_def_handler(selector, set_str) declare_def_snprint_defstr(selector, print_str, DEFAULT_SELECTOR) declare_hw_handler(selector, set_str) declare_hw_snprint(selector, print_str) declare_ovr_handler(selector, set_str) declare_ovr_snprint(selector, print_str) declare_mp_handler(selector, set_str) declare_mp_snprint(selector, print_str) declare_def_handler(uid_attrs, set_str) declare_def_snprint(uid_attrs, print_str) declare_def_handler(uid_attribute, set_str) declare_def_snprint_defstr(uid_attribute, print_str, DEFAULT_UID_ATTRIBUTE) declare_ovr_handler(uid_attribute, set_str) declare_ovr_snprint(uid_attribute, print_str) declare_hw_handler(uid_attribute, set_str) declare_hw_snprint(uid_attribute, print_str) declare_def_handler(getuid, set_str) declare_def_snprint(getuid, print_str) declare_ovr_handler(getuid, set_str) declare_ovr_snprint(getuid, print_str) declare_hw_handler(getuid, set_str) declare_hw_snprint(getuid, print_str) declare_def_handler(prio_name, set_str) declare_def_snprint_defstr(prio_name, print_str, DEFAULT_PRIO) declare_ovr_handler(prio_name, set_str) declare_ovr_snprint(prio_name, print_str) declare_hw_handler(prio_name, set_str) declare_hw_snprint(prio_name, print_str) declare_mp_handler(prio_name, set_str) declare_mp_snprint(prio_name, print_str) declare_def_handler(alias_prefix, set_str) declare_def_snprint_defstr(alias_prefix, print_str, DEFAULT_ALIAS_PREFIX) declare_ovr_handler(alias_prefix, set_str) declare_ovr_snprint(alias_prefix, print_str) declare_hw_handler(alias_prefix, set_str) declare_hw_snprint(alias_prefix, print_str) declare_def_handler(prio_args, set_str) declare_def_snprint_defstr(prio_args, print_str, DEFAULT_PRIO_ARGS) declare_ovr_handler(prio_args, set_str) declare_ovr_snprint(prio_args, print_str) declare_hw_handler(prio_args, set_str) declare_hw_snprint(prio_args, print_str) declare_mp_handler(prio_args, set_str) declare_mp_snprint(prio_args, print_str) declare_def_handler(features, set_str) declare_def_snprint_defstr(features, print_str, DEFAULT_FEATURES) declare_ovr_handler(features, set_str) declare_ovr_snprint(features, print_str) declare_hw_handler(features, set_str) declare_hw_snprint(features, print_str) declare_mp_handler(features, set_str) declare_mp_snprint(features, print_str) declare_def_handler(checker_name, set_str) declare_def_snprint_defstr(checker_name, print_str, DEFAULT_CHECKER) declare_ovr_handler(checker_name, set_str) declare_ovr_snprint(checker_name, print_str) declare_hw_handler(checker_name, set_str) declare_hw_snprint(checker_name, print_str) declare_def_handler(minio, set_int) declare_def_snprint_defint(minio, print_int, DEFAULT_MINIO) declare_ovr_handler(minio, set_int) declare_ovr_snprint(minio, print_nonzero) declare_hw_handler(minio, set_int) declare_hw_snprint(minio, print_nonzero) declare_mp_handler(minio, set_int) declare_mp_snprint(minio, print_nonzero) declare_def_handler(minio_rq, set_int) declare_def_snprint_defint(minio_rq, print_int, DEFAULT_MINIO_RQ) declare_ovr_handler(minio_rq, set_int) declare_ovr_snprint(minio_rq, print_nonzero) declare_hw_handler(minio_rq, set_int) declare_hw_snprint(minio_rq, print_nonzero) declare_mp_handler(minio_rq, set_int) declare_mp_snprint(minio_rq, print_nonzero) declare_def_handler(queue_without_daemon, set_yes_no) static int snprint_def_queue_without_daemon (struct config *conf, char * buff, int len, void * data) { switch (conf->queue_without_daemon) { case QUE_NO_DAEMON_OFF: return snprintf(buff, len, "\"no\""); case QUE_NO_DAEMON_ON: return snprintf(buff, len, "\"yes\""); case QUE_NO_DAEMON_FORCE: return snprintf(buff, len, "\"forced\""); } return 0; } declare_def_handler(checker_timeout, set_int) declare_def_snprint(checker_timeout, print_nonzero) declare_def_handler(flush_on_last_del, set_yes_no_undef) declare_def_snprint_defint(flush_on_last_del, print_yes_no_undef, YNU_NO) declare_ovr_handler(flush_on_last_del, set_yes_no_undef) declare_ovr_snprint(flush_on_last_del, print_yes_no_undef) declare_hw_handler(flush_on_last_del, set_yes_no_undef) declare_hw_snprint(flush_on_last_del, print_yes_no_undef) declare_mp_handler(flush_on_last_del, set_yes_no_undef) declare_mp_snprint(flush_on_last_del, print_yes_no_undef) declare_def_handler(user_friendly_names, set_yes_no_undef) declare_def_snprint_defint(user_friendly_names, print_yes_no_undef, YNU_NO) declare_ovr_handler(user_friendly_names, set_yes_no_undef) declare_ovr_snprint(user_friendly_names, print_yes_no_undef) declare_hw_handler(user_friendly_names, set_yes_no_undef) declare_hw_snprint(user_friendly_names, print_yes_no_undef) declare_mp_handler(user_friendly_names, set_yes_no_undef) declare_mp_snprint(user_friendly_names, print_yes_no_undef) declare_def_handler(bindings_file, set_str) declare_def_snprint(bindings_file, print_str) declare_def_handler(wwids_file, set_str) declare_def_snprint(wwids_file, print_str) declare_def_handler(prkeys_file, set_str) declare_def_snprint(prkeys_file, print_str) declare_def_handler(retain_hwhandler, set_yes_no_undef) declare_def_snprint_defint(retain_hwhandler, print_yes_no_undef, YNU_NO) declare_ovr_handler(retain_hwhandler, set_yes_no_undef) declare_ovr_snprint(retain_hwhandler, print_yes_no_undef) declare_hw_handler(retain_hwhandler, set_yes_no_undef) declare_hw_snprint(retain_hwhandler, print_yes_no_undef) declare_def_handler(detect_prio, set_yes_no_undef) declare_def_snprint_defint(detect_prio, print_yes_no_undef, YNU_NO) declare_ovr_handler(detect_prio, set_yes_no_undef) declare_ovr_snprint(detect_prio, print_yes_no_undef) declare_hw_handler(detect_prio, set_yes_no_undef) declare_hw_snprint(detect_prio, print_yes_no_undef) declare_def_handler(detect_checker, set_yes_no_undef) declare_def_snprint_defint(detect_checker, print_yes_no_undef, YNU_NO) declare_ovr_handler(detect_checker, set_yes_no_undef) declare_ovr_snprint(detect_checker, print_yes_no_undef) declare_hw_handler(detect_checker, set_yes_no_undef) declare_hw_snprint(detect_checker, print_yes_no_undef) declare_def_handler(force_sync, set_yes_no) declare_def_snprint(force_sync, print_yes_no) declare_def_handler(deferred_remove, set_yes_no_undef) declare_def_snprint_defint(deferred_remove, print_yes_no_undef, YNU_NO) declare_ovr_handler(deferred_remove, set_yes_no_undef) declare_ovr_snprint(deferred_remove, print_yes_no_undef) declare_hw_handler(deferred_remove, set_yes_no_undef) declare_hw_snprint(deferred_remove, print_yes_no_undef) declare_mp_handler(deferred_remove, set_yes_no_undef) declare_mp_snprint(deferred_remove, print_yes_no_undef) declare_def_handler(retrigger_tries, set_int) declare_def_snprint(retrigger_tries, print_int) declare_def_handler(retrigger_delay, set_int) declare_def_snprint(retrigger_delay, print_int) declare_def_handler(uev_wait_timeout, set_int) declare_def_snprint(uev_wait_timeout, print_int) declare_def_handler(strict_timing, set_yes_no) declare_def_snprint(strict_timing, print_yes_no) declare_def_handler(skip_kpartx, set_yes_no_undef) declare_def_snprint_defint(skip_kpartx, print_yes_no_undef, YNU_NO) declare_ovr_handler(skip_kpartx, set_yes_no_undef) declare_ovr_snprint(skip_kpartx, print_yes_no_undef) declare_hw_handler(skip_kpartx, set_yes_no_undef) declare_hw_snprint(skip_kpartx, print_yes_no_undef) declare_mp_handler(skip_kpartx, set_yes_no_undef) declare_mp_snprint(skip_kpartx, print_yes_no_undef) declare_def_handler(disable_changed_wwids, set_yes_no) declare_def_snprint(disable_changed_wwids, print_yes_no) declare_def_handler(remove_retries, set_int) declare_def_snprint(remove_retries, print_int) declare_def_handler(max_sectors_kb, set_int) declare_def_snprint(max_sectors_kb, print_nonzero) declare_ovr_handler(max_sectors_kb, set_int) declare_ovr_snprint(max_sectors_kb, print_nonzero) declare_hw_handler(max_sectors_kb, set_int) declare_hw_snprint(max_sectors_kb, print_nonzero) declare_mp_handler(max_sectors_kb, set_int) declare_mp_snprint(max_sectors_kb, print_nonzero) static int def_config_dir_handler(struct config *conf, vector strvec) { /* this is only valid in the main config file */ if (conf->processed_main_config) return 0; return set_str(strvec, &conf->config_dir); } declare_def_snprint(config_dir, print_str) #define declare_def_attr_handler(option, function) \ static int \ def_ ## option ## _handler (struct config *conf, vector strvec) \ { \ return function (strvec, &conf->option, &conf->attribute_flags);\ } #define declare_def_attr_snprint(option, function) \ static int \ snprint_def_ ## option (struct config *conf, char * buff, int len, void * data) \ { \ return function (buff, len, &conf->option, \ &conf->attribute_flags); \ } #define declare_mp_attr_handler(option, function) \ static int \ mp_ ## option ## _handler (struct config *conf, vector strvec) \ { \ struct mpentry * mpe = VECTOR_LAST_SLOT(conf->mptable); \ if (!mpe) \ return 1; \ return function (strvec, &mpe->option, &mpe->attribute_flags); \ } #define declare_mp_attr_snprint(option, function) \ static int \ snprint_mp_ ## option (struct config *conf, char * buff, int len, void * data) \ { \ struct mpentry * mpe = (struct mpentry *)data; \ return function (buff, len, &mpe->option, \ &mpe->attribute_flags); \ } static int set_mode(vector strvec, void *ptr, int *flags) { mode_t mode; mode_t *mode_ptr = (mode_t *)ptr; char *buff; buff = set_value(strvec); if (!buff) return 1; if (sscanf(buff, "%o", &mode) == 1 && mode <= 0777) { *flags |= (1 << ATTR_MODE); *mode_ptr = mode; } FREE(buff); return 0; } static int set_uid(vector strvec, void *ptr, int *flags) { uid_t uid; uid_t *uid_ptr = (uid_t *)ptr; char *buff; char passwd_buf[1024]; struct passwd info, *found; buff = set_value(strvec); if (!buff) return 1; if (getpwnam_r(buff, &info, passwd_buf, 1024, &found) == 0 && found) { *flags |= (1 << ATTR_UID); *uid_ptr = info.pw_uid; } else if (sscanf(buff, "%u", &uid) == 1){ *flags |= (1 << ATTR_UID); *uid_ptr = uid; } FREE(buff); return 0; } static int set_gid(vector strvec, void *ptr, int *flags) { gid_t gid; gid_t *gid_ptr = (gid_t *)ptr; char *buff; char passwd_buf[1024]; struct passwd info, *found; buff = set_value(strvec); if (!buff) return 1; if (getpwnam_r(buff, &info, passwd_buf, 1024, &found) == 0 && found) { *flags |= (1 << ATTR_GID); *gid_ptr = info.pw_gid; } else if (sscanf(buff, "%u", &gid) == 1){ *flags |= (1 << ATTR_GID); *gid_ptr = gid; } FREE(buff); return 0; } static int print_mode(char * buff, int len, void *ptr, int *flags) { mode_t *mode_ptr = (mode_t *)ptr; if ((*flags & (1 << ATTR_MODE)) == 0) return 0; return snprintf(buff, len, "0%o", *mode_ptr); } static int print_uid(char * buff, int len, void *ptr, int *flags) { uid_t *uid_ptr = (uid_t *)ptr; if ((*flags & (1 << ATTR_UID)) == 0) return 0; return snprintf(buff, len, "0%o", *uid_ptr); } static int print_gid(char * buff, int len, void *ptr, int *flags) { gid_t *gid_ptr = (gid_t *)ptr; if ((*flags & (1 << ATTR_GID)) == 0) return 0; return snprintf(buff, len, "0%o", *gid_ptr); } declare_def_attr_handler(mode, set_mode) declare_def_attr_snprint(mode, print_mode) declare_mp_attr_handler(mode, set_mode) declare_mp_attr_snprint(mode, print_mode) declare_def_attr_handler(uid, set_uid) declare_def_attr_snprint(uid, print_uid) declare_mp_attr_handler(uid, set_uid) declare_mp_attr_snprint(uid, print_uid) declare_def_attr_handler(gid, set_gid) declare_def_attr_snprint(gid, print_gid) declare_mp_attr_handler(gid, set_gid) declare_mp_attr_snprint(gid, print_gid) static int set_fast_io_fail(vector strvec, void *ptr) { char * buff; int *int_ptr = (int *)ptr; buff = set_value(strvec); if (!buff) return 1; if (strcmp(buff, "off") == 0) *int_ptr = MP_FAST_IO_FAIL_OFF; else if (sscanf(buff, "%d", int_ptr) != 1 || *int_ptr < MP_FAST_IO_FAIL_ZERO) *int_ptr = MP_FAST_IO_FAIL_UNSET; else if (*int_ptr == 0) *int_ptr = MP_FAST_IO_FAIL_ZERO; FREE(buff); return 0; } int print_fast_io_fail(char * buff, int len, void *ptr) { int *int_ptr = (int *)ptr; if (*int_ptr == MP_FAST_IO_FAIL_UNSET) return 0; if (*int_ptr == MP_FAST_IO_FAIL_OFF) return snprintf(buff, len, "\"off\""); if (*int_ptr == MP_FAST_IO_FAIL_ZERO) return snprintf(buff, len, "0"); return snprintf(buff, len, "%d", *int_ptr); } declare_def_handler(fast_io_fail, set_fast_io_fail) declare_def_snprint_defint(fast_io_fail, print_fast_io_fail, DEFAULT_FAST_IO_FAIL) declare_ovr_handler(fast_io_fail, set_fast_io_fail) declare_ovr_snprint(fast_io_fail, print_fast_io_fail) declare_hw_handler(fast_io_fail, set_fast_io_fail) declare_hw_snprint(fast_io_fail, print_fast_io_fail) static int set_dev_loss(vector strvec, void *ptr) { char * buff; unsigned int *uint_ptr = (unsigned int *)ptr; buff = set_value(strvec); if (!buff) return 1; if (!strcmp(buff, "infinity")) *uint_ptr = MAX_DEV_LOSS_TMO; else if (sscanf(buff, "%u", uint_ptr) != 1) *uint_ptr = 0; FREE(buff); return 0; } int print_dev_loss(char * buff, int len, void *ptr) { unsigned int *uint_ptr = (unsigned int *)ptr; if (!*uint_ptr) return 0; if (*uint_ptr >= MAX_DEV_LOSS_TMO) return snprintf(buff, len, "\"infinity\""); return snprintf(buff, len, "%u", *uint_ptr); } declare_def_handler(dev_loss, set_dev_loss) declare_def_snprint(dev_loss, print_dev_loss) declare_ovr_handler(dev_loss, set_dev_loss) declare_ovr_snprint(dev_loss, print_dev_loss) declare_hw_handler(dev_loss, set_dev_loss) declare_hw_snprint(dev_loss, print_dev_loss) static int set_pgpolicy(vector strvec, void *ptr) { char * buff; int *int_ptr = (int *)ptr; buff = set_value(strvec); if (!buff) return 1; *int_ptr = get_pgpolicy_id(buff); FREE(buff); return 0; } int print_pgpolicy(char * buff, int len, void *ptr) { char str[POLICY_NAME_SIZE]; int pgpolicy = *(int *)ptr; if (!pgpolicy) return 0; get_pgpolicy_name(str, POLICY_NAME_SIZE, pgpolicy); return snprintf(buff, len, "\"%s\"", str); } declare_def_handler(pgpolicy, set_pgpolicy) declare_def_snprint_defint(pgpolicy, print_pgpolicy, DEFAULT_PGPOLICY) declare_ovr_handler(pgpolicy, set_pgpolicy) declare_ovr_snprint(pgpolicy, print_pgpolicy) declare_hw_handler(pgpolicy, set_pgpolicy) declare_hw_snprint(pgpolicy, print_pgpolicy) declare_mp_handler(pgpolicy, set_pgpolicy) declare_mp_snprint(pgpolicy, print_pgpolicy) int get_sys_max_fds(int *max_fds) { FILE *file; int nr_open; int ret = 1; file = fopen("/proc/sys/fs/nr_open", "r"); if (!file) { fprintf(stderr, "Cannot open /proc/sys/fs/nr_open : %s\n", strerror(errno)); return 1; } if (fscanf(file, "%d", &nr_open) != 1) { fprintf(stderr, "Cannot read max open fds from /proc/sys/fs/nr_open"); if (ferror(file)) fprintf(stderr, " : %s\n", strerror(errno)); else fprintf(stderr, "\n"); } else { *max_fds = nr_open; ret = 0; } fclose(file); return ret; } static int max_fds_handler(struct config *conf, vector strvec) { char * buff; int r = 0, max_fds; buff = set_value(strvec); if (!buff) return 1; r = get_sys_max_fds(&max_fds); if (r) { /* Assume safe limit */ max_fds = 4096; } if (strlen(buff) == 3 && !strcmp(buff, "max")) conf->max_fds = max_fds; else conf->max_fds = atoi(buff); if (conf->max_fds > max_fds) conf->max_fds = max_fds; FREE(buff); return r; } static int snprint_max_fds (struct config *conf, char * buff, int len, void * data) { int r = 0, max_fds; if (!conf->max_fds) return 0; r = get_sys_max_fds(&max_fds); if (!r && conf->max_fds >= max_fds) return snprintf(buff, len, "\"max\""); else return snprintf(buff, len, "%d", conf->max_fds); } static int set_rr_weight(vector strvec, void *ptr) { int *int_ptr = (int *)ptr; char * buff; buff = set_value(strvec); if (!buff) return 1; if (!strcmp(buff, "priorities")) *int_ptr = RR_WEIGHT_PRIO; if (!strcmp(buff, "uniform")) *int_ptr = RR_WEIGHT_NONE; FREE(buff); return 0; } int print_rr_weight (char * buff, int len, void *ptr) { int *int_ptr = (int *)ptr; if (!*int_ptr) return 0; if (*int_ptr == RR_WEIGHT_PRIO) return snprintf(buff, len, "\"priorities\""); if (*int_ptr == RR_WEIGHT_NONE) return snprintf(buff, len, "\"uniform\""); return 0; } declare_def_handler(rr_weight, set_rr_weight) declare_def_snprint_defint(rr_weight, print_rr_weight, RR_WEIGHT_NONE) declare_ovr_handler(rr_weight, set_rr_weight) declare_ovr_snprint(rr_weight, print_rr_weight) declare_hw_handler(rr_weight, set_rr_weight) declare_hw_snprint(rr_weight, print_rr_weight) declare_mp_handler(rr_weight, set_rr_weight) declare_mp_snprint(rr_weight, print_rr_weight) static int set_pgfailback(vector strvec, void *ptr) { int *int_ptr = (int *)ptr; char * buff; buff = set_value(strvec); if (!buff) return 1; if (strlen(buff) == 6 && !strcmp(buff, "manual")) *int_ptr = -FAILBACK_MANUAL; else if (strlen(buff) == 9 && !strcmp(buff, "immediate")) *int_ptr = -FAILBACK_IMMEDIATE; else if (strlen(buff) == 10 && !strcmp(buff, "followover")) *int_ptr = -FAILBACK_FOLLOWOVER; else *int_ptr = atoi(buff); FREE(buff); return 0; } int print_pgfailback (char * buff, int len, void *ptr) { int *int_ptr = (int *)ptr; switch(*int_ptr) { case FAILBACK_UNDEF: return 0; case -FAILBACK_MANUAL: return snprintf(buff, len, "\"manual\""); case -FAILBACK_IMMEDIATE: return snprintf(buff, len, "\"immediate\""); case -FAILBACK_FOLLOWOVER: return snprintf(buff, len, "\"followover\""); default: return snprintf(buff, len, "%i", *int_ptr); } } declare_def_handler(pgfailback, set_pgfailback) declare_def_snprint_defint(pgfailback, print_pgfailback, DEFAULT_FAILBACK) declare_ovr_handler(pgfailback, set_pgfailback) declare_ovr_snprint(pgfailback, print_pgfailback) declare_hw_handler(pgfailback, set_pgfailback) declare_hw_snprint(pgfailback, print_pgfailback) declare_mp_handler(pgfailback, set_pgfailback) declare_mp_snprint(pgfailback, print_pgfailback) static int set_no_path_retry(vector strvec, void *ptr) { int *int_ptr = (int *)ptr; char * buff; buff = set_value(strvec); if (!buff) return 1; if (!strcmp(buff, "fail") || !strcmp(buff, "0")) *int_ptr = NO_PATH_RETRY_FAIL; else if (!strcmp(buff, "queue")) *int_ptr = NO_PATH_RETRY_QUEUE; else if ((*int_ptr = atoi(buff)) < 1) *int_ptr = NO_PATH_RETRY_UNDEF; FREE(buff); return 0; } int print_no_path_retry(char * buff, int len, void *ptr) { int *int_ptr = (int *)ptr; switch(*int_ptr) { case NO_PATH_RETRY_UNDEF: return 0; case NO_PATH_RETRY_FAIL: return snprintf(buff, len, "\"fail\""); case NO_PATH_RETRY_QUEUE: return snprintf(buff, len, "\"queue\""); default: return snprintf(buff, len, "%i", *int_ptr); } } declare_def_handler(no_path_retry, set_no_path_retry) declare_def_snprint(no_path_retry, print_no_path_retry) declare_ovr_handler(no_path_retry, set_no_path_retry) declare_ovr_snprint(no_path_retry, print_no_path_retry) declare_hw_handler(no_path_retry, set_no_path_retry) declare_hw_snprint(no_path_retry, print_no_path_retry) declare_mp_handler(no_path_retry, set_no_path_retry) declare_mp_snprint(no_path_retry, print_no_path_retry) static int def_log_checker_err_handler(struct config *conf, vector strvec) { char * buff; buff = set_value(strvec); if (!buff) return 1; if (strlen(buff) == 4 && !strcmp(buff, "once")) conf->log_checker_err = LOG_CHKR_ERR_ONCE; else if (strlen(buff) == 6 && !strcmp(buff, "always")) conf->log_checker_err = LOG_CHKR_ERR_ALWAYS; free(buff); return 0; } static int snprint_def_log_checker_err (struct config *conf, char * buff, int len, void * data) { if (conf->log_checker_err == LOG_CHKR_ERR_ONCE) return snprintf(buff, len, "once"); return snprintf(buff, len, "always"); } static int set_reservation_key(vector strvec, struct be64 *be64_ptr, int *source_ptr) { char *buff; uint64_t prkey; buff = set_value(strvec); if (!buff) return 1; if (strcmp(buff, "file") == 0) { *source_ptr = PRKEY_SOURCE_FILE; put_be64(*be64_ptr, 0); FREE(buff); return 0; } if (parse_prkey(buff, &prkey) != 0) { FREE(buff); return 1; } *source_ptr = PRKEY_SOURCE_CONF; put_be64(*be64_ptr, prkey); FREE(buff); return 0; } int print_reservation_key(char * buff, int len, struct be64 key, int source) { if (source == PRKEY_SOURCE_NONE) return 0; if (source == PRKEY_SOURCE_FILE) return snprintf(buff, len, "file"); return snprintf(buff, len, "0x%" PRIx64, get_be64(key)); } static int def_reservation_key_handler(struct config *conf, vector strvec) { return set_reservation_key(strvec, &conf->reservation_key, &conf->prkey_source); } static int snprint_def_reservation_key (struct config *conf, char * buff, int len, void * data) { return print_reservation_key(buff, len, conf->reservation_key, conf->prkey_source); } static int mp_reservation_key_handler(struct config *conf, vector strvec) { struct mpentry * mpe = VECTOR_LAST_SLOT(conf->mptable); if (!mpe) return 1; return set_reservation_key(strvec, &mpe->reservation_key, &mpe->prkey_source); } static int snprint_mp_reservation_key (struct config *conf, char * buff, int len, void * data) { struct mpentry * mpe = (struct mpentry *)data; return print_reservation_key(buff, len, mpe->reservation_key, mpe->prkey_source); } static int set_off_int_undef(vector strvec, void *ptr) { int *int_ptr = (int *)ptr; char * buff; buff = set_value(strvec); if (!buff) return 1; if (!strcmp(buff, "no") || !strcmp(buff, "0")) *int_ptr = NU_NO; else if ((*int_ptr = atoi(buff)) < 1) *int_ptr = NU_UNDEF; FREE(buff); return 0; } int print_off_int_undef(char * buff, int len, void *ptr) { int *int_ptr = (int *)ptr; switch(*int_ptr) { case NU_UNDEF: return 0; case NU_NO: return snprintf(buff, len, "\"no\""); default: return snprintf(buff, len, "%i", *int_ptr); } } declare_def_handler(delay_watch_checks, set_off_int_undef) declare_def_snprint(delay_watch_checks, print_off_int_undef) declare_ovr_handler(delay_watch_checks, set_off_int_undef) declare_ovr_snprint(delay_watch_checks, print_off_int_undef) declare_hw_handler(delay_watch_checks, set_off_int_undef) declare_hw_snprint(delay_watch_checks, print_off_int_undef) declare_mp_handler(delay_watch_checks, set_off_int_undef) declare_mp_snprint(delay_watch_checks, print_off_int_undef) declare_def_handler(delay_wait_checks, set_off_int_undef) declare_def_snprint(delay_wait_checks, print_off_int_undef) declare_ovr_handler(delay_wait_checks, set_off_int_undef) declare_ovr_snprint(delay_wait_checks, print_off_int_undef) declare_hw_handler(delay_wait_checks, set_off_int_undef) declare_hw_snprint(delay_wait_checks, print_off_int_undef) declare_mp_handler(delay_wait_checks, set_off_int_undef) declare_mp_snprint(delay_wait_checks, print_off_int_undef) declare_def_handler(marginal_path_err_sample_time, set_off_int_undef) declare_def_snprint_defint(marginal_path_err_sample_time, print_off_int_undef, DEFAULT_ERR_CHECKS) declare_ovr_handler(marginal_path_err_sample_time, set_off_int_undef) declare_ovr_snprint(marginal_path_err_sample_time, print_off_int_undef) declare_hw_handler(marginal_path_err_sample_time, set_off_int_undef) declare_hw_snprint(marginal_path_err_sample_time, print_off_int_undef) declare_mp_handler(marginal_path_err_sample_time, set_off_int_undef) declare_mp_snprint(marginal_path_err_sample_time, print_off_int_undef) declare_def_handler(marginal_path_err_rate_threshold, set_off_int_undef) declare_def_snprint_defint(marginal_path_err_rate_threshold, print_off_int_undef, DEFAULT_ERR_CHECKS) declare_ovr_handler(marginal_path_err_rate_threshold, set_off_int_undef) declare_ovr_snprint(marginal_path_err_rate_threshold, print_off_int_undef) declare_hw_handler(marginal_path_err_rate_threshold, set_off_int_undef) declare_hw_snprint(marginal_path_err_rate_threshold, print_off_int_undef) declare_mp_handler(marginal_path_err_rate_threshold, set_off_int_undef) declare_mp_snprint(marginal_path_err_rate_threshold, print_off_int_undef) declare_def_handler(marginal_path_err_recheck_gap_time, set_off_int_undef) declare_def_snprint_defint(marginal_path_err_recheck_gap_time, print_off_int_undef, DEFAULT_ERR_CHECKS) declare_ovr_handler(marginal_path_err_recheck_gap_time, set_off_int_undef) declare_ovr_snprint(marginal_path_err_recheck_gap_time, print_off_int_undef) declare_hw_handler(marginal_path_err_recheck_gap_time, set_off_int_undef) declare_hw_snprint(marginal_path_err_recheck_gap_time, print_off_int_undef) declare_mp_handler(marginal_path_err_recheck_gap_time, set_off_int_undef) declare_mp_snprint(marginal_path_err_recheck_gap_time, print_off_int_undef) declare_def_handler(marginal_path_double_failed_time, set_off_int_undef) declare_def_snprint_defint(marginal_path_double_failed_time, print_off_int_undef, DEFAULT_ERR_CHECKS) declare_ovr_handler(marginal_path_double_failed_time, set_off_int_undef) declare_ovr_snprint(marginal_path_double_failed_time, print_off_int_undef) declare_hw_handler(marginal_path_double_failed_time, set_off_int_undef) declare_hw_snprint(marginal_path_double_failed_time, print_off_int_undef) declare_mp_handler(marginal_path_double_failed_time, set_off_int_undef) declare_mp_snprint(marginal_path_double_failed_time, print_off_int_undef) static int def_uxsock_timeout_handler(struct config *conf, vector strvec) { unsigned int uxsock_timeout; char *buff; buff = set_value(strvec); if (!buff) return 1; if (sscanf(buff, "%u", &uxsock_timeout) == 1 && uxsock_timeout > DEFAULT_REPLY_TIMEOUT) conf->uxsock_timeout = uxsock_timeout; else conf->uxsock_timeout = DEFAULT_REPLY_TIMEOUT; free(buff); return 0; } /* * blacklist block handlers */ static int blacklist_handler(struct config *conf, vector strvec) { if (!conf->blist_devnode) conf->blist_devnode = vector_alloc(); if (!conf->blist_wwid) conf->blist_wwid = vector_alloc(); if (!conf->blist_device) conf->blist_device = vector_alloc(); if (!conf->blist_property) conf->blist_property = vector_alloc(); if (!conf->blist_devnode || !conf->blist_wwid || !conf->blist_device || !conf->blist_property) return 1; return 0; } static int blacklist_exceptions_handler(struct config *conf, vector strvec) { if (!conf->elist_devnode) conf->elist_devnode = vector_alloc(); if (!conf->elist_wwid) conf->elist_wwid = vector_alloc(); if (!conf->elist_device) conf->elist_device = vector_alloc(); if (!conf->elist_property) conf->elist_property = vector_alloc(); if (!conf->elist_devnode || !conf->elist_wwid || !conf->elist_device || !conf->elist_property) return 1; return 0; } #define declare_ble_handler(option) \ static int \ ble_ ## option ## _handler (struct config *conf, vector strvec) \ { \ char * buff; \ \ if (!conf->option) \ return 1; \ \ buff = set_value(strvec); \ if (!buff) \ return 1; \ \ return store_ble(conf->option, buff, ORIGIN_CONFIG); \ } #define declare_ble_device_handler(name, option, vend, prod) \ static int \ ble_ ## option ## _ ## name ## _handler (struct config *conf, vector strvec) \ { \ char * buff; \ \ if (!conf->option) \ return 1; \ \ buff = set_value(strvec); \ if (!buff) \ return 1; \ \ return set_ble_device(conf->option, vend, prod, ORIGIN_CONFIG); \ } declare_ble_handler(blist_devnode) declare_ble_handler(elist_devnode) declare_ble_handler(blist_wwid) declare_ble_handler(elist_wwid) declare_ble_handler(blist_property) declare_ble_handler(elist_property) static int snprint_def_uxsock_timeout(struct config *conf, char * buff, int len, void * data) { return snprintf(buff, len, "%u", conf->uxsock_timeout); } static int snprint_ble_simple (struct config *conf, char * buff, int len, void * data) { struct blentry * ble = (struct blentry *)data; return snprintf(buff, len, "\"%s\"", ble->str); } static int ble_device_handler(struct config *conf, vector strvec) { return alloc_ble_device(conf->blist_device); } static int ble_except_device_handler(struct config *conf, vector strvec) { return alloc_ble_device(conf->elist_device); } declare_ble_device_handler(vendor, blist_device, buff, NULL) declare_ble_device_handler(vendor, elist_device, buff, NULL) declare_ble_device_handler(product, blist_device, NULL, buff) declare_ble_device_handler(product, elist_device, NULL, buff) static int snprint_bled_vendor (struct config *conf, char * buff, int len, void * data) { struct blentry_device * bled = (struct blentry_device *)data; return snprintf(buff, len, "\"%s\"", bled->vendor); } static int snprint_bled_product (struct config *conf, char * buff, int len, void * data) { struct blentry_device * bled = (struct blentry_device *)data; return snprintf(buff, len, "\"%s\"", bled->product); } /* * devices block handlers */ static int devices_handler(struct config *conf, vector strvec) { if (!conf->hwtable) conf->hwtable = vector_alloc(); if (!conf->hwtable) return 1; return 0; } static int device_handler(struct config *conf, vector strvec) { struct hwentry * hwe; hwe = alloc_hwe(); if (!hwe) return 1; if (!vector_alloc_slot(conf->hwtable)) { free_hwe(hwe); return 1; } vector_set_slot(conf->hwtable, hwe); return 0; } declare_hw_handler(vendor, set_str) declare_hw_snprint(vendor, print_str) declare_hw_handler(product, set_str) declare_hw_snprint(product, print_str) declare_hw_handler(revision, set_str) declare_hw_snprint(revision, print_str) declare_hw_handler(bl_product, set_str) declare_hw_snprint(bl_product, print_str) declare_hw_handler(hwhandler, set_str) declare_hw_snprint(hwhandler, print_str) /* * overrides handlers */ static int overrides_handler(struct config *conf, vector strvec) { if (!conf->overrides) conf->overrides = alloc_hwe(); if (!conf->overrides) return 1; return 0; } /* * multipaths block handlers */ static int multipaths_handler(struct config *conf, vector strvec) { if (!conf->mptable) conf->mptable = vector_alloc(); if (!conf->mptable) return 1; return 0; } static int multipath_handler(struct config *conf, vector strvec) { struct mpentry * mpe; mpe = alloc_mpe(); if (!mpe) return 1; if (!vector_alloc_slot(conf->mptable)) { free_mpe(mpe); return 1; } vector_set_slot(conf->mptable, mpe); return 0; } declare_mp_handler(wwid, set_str) declare_mp_snprint(wwid, print_str) declare_mp_handler(alias, set_str) declare_mp_snprint(alias, print_str) /* * deprecated handlers */ static int deprecated_handler(struct config *conf, vector strvec) { char * buff; buff = set_value(strvec); if (!buff) return 1; FREE(buff); return 0; } static int snprint_deprecated (struct config *conf, char * buff, int len, void * data) { return 0; } #define __deprecated /* * If you add or remove a keyword also update multipath/multipath.conf.5 */ void init_keywords(vector keywords) { install_keyword_root("defaults", NULL); install_keyword("verbosity", &def_verbosity_handler, &snprint_def_verbosity); install_keyword("polling_interval", &def_checkint_handler, &snprint_def_checkint); install_keyword("max_polling_interval", &def_max_checkint_handler, &snprint_def_max_checkint); install_keyword("reassign_maps", &def_reassign_maps_handler, &snprint_def_reassign_maps); install_keyword("multipath_dir", &def_multipath_dir_handler, &snprint_def_multipath_dir); install_keyword("path_selector", &def_selector_handler, &snprint_def_selector); install_keyword("path_grouping_policy", &def_pgpolicy_handler, &snprint_def_pgpolicy); install_keyword("uid_attrs", &def_uid_attrs_handler, &snprint_def_uid_attrs); install_keyword("uid_attribute", &def_uid_attribute_handler, &snprint_def_uid_attribute); install_keyword("getuid_callout", &def_getuid_handler, &snprint_def_getuid); install_keyword("prio", &def_prio_name_handler, &snprint_def_prio_name); install_keyword("prio_args", &def_prio_args_handler, &snprint_def_prio_args); install_keyword("features", &def_features_handler, &snprint_def_features); install_keyword("path_checker", &def_checker_name_handler, &snprint_def_checker_name); install_keyword("checker", &def_checker_name_handler, NULL); install_keyword("alias_prefix", &def_alias_prefix_handler, &snprint_def_alias_prefix); install_keyword("failback", &def_pgfailback_handler, &snprint_def_pgfailback); install_keyword("rr_min_io", &def_minio_handler, &snprint_def_minio); install_keyword("rr_min_io_rq", &def_minio_rq_handler, &snprint_def_minio_rq); install_keyword("max_fds", &max_fds_handler, &snprint_max_fds); install_keyword("rr_weight", &def_rr_weight_handler, &snprint_def_rr_weight); install_keyword("no_path_retry", &def_no_path_retry_handler, &snprint_def_no_path_retry); install_keyword("queue_without_daemon", &def_queue_without_daemon_handler, &snprint_def_queue_without_daemon); install_keyword("checker_timeout", &def_checker_timeout_handler, &snprint_def_checker_timeout); install_keyword("pg_timeout", &deprecated_handler, &snprint_deprecated); install_keyword("flush_on_last_del", &def_flush_on_last_del_handler, &snprint_def_flush_on_last_del); install_keyword("user_friendly_names", &def_user_friendly_names_handler, &snprint_def_user_friendly_names); install_keyword("mode", &def_mode_handler, &snprint_def_mode); install_keyword("uid", &def_uid_handler, &snprint_def_uid); install_keyword("gid", &def_gid_handler, &snprint_def_gid); install_keyword("fast_io_fail_tmo", &def_fast_io_fail_handler, &snprint_def_fast_io_fail); install_keyword("dev_loss_tmo", &def_dev_loss_handler, &snprint_def_dev_loss); install_keyword("bindings_file", &def_bindings_file_handler, &snprint_def_bindings_file); install_keyword("wwids_file", &def_wwids_file_handler, &snprint_def_wwids_file); install_keyword("prkeys_file", &def_prkeys_file_handler, &snprint_def_prkeys_file); install_keyword("log_checker_err", &def_log_checker_err_handler, &snprint_def_log_checker_err); install_keyword("reservation_key", &def_reservation_key_handler, &snprint_def_reservation_key); install_keyword("retain_attached_hw_handler", &def_retain_hwhandler_handler, &snprint_def_retain_hwhandler); install_keyword("detect_prio", &def_detect_prio_handler, &snprint_def_detect_prio); install_keyword("detect_checker", &def_detect_checker_handler, &snprint_def_detect_checker); install_keyword("force_sync", &def_force_sync_handler, &snprint_def_force_sync); install_keyword("strict_timing", &def_strict_timing_handler, &snprint_def_strict_timing); install_keyword("deferred_remove", &def_deferred_remove_handler, &snprint_def_deferred_remove); install_keyword("partition_delimiter", &def_partition_delim_handler, &snprint_def_partition_delim); install_keyword("config_dir", &def_config_dir_handler, &snprint_def_config_dir); install_keyword("delay_watch_checks", &def_delay_watch_checks_handler, &snprint_def_delay_watch_checks); install_keyword("delay_wait_checks", &def_delay_wait_checks_handler, &snprint_def_delay_wait_checks); install_keyword("marginal_path_err_sample_time", &def_marginal_path_err_sample_time_handler, &snprint_def_marginal_path_err_sample_time); install_keyword("marginal_path_err_rate_threshold", &def_marginal_path_err_rate_threshold_handler, &snprint_def_marginal_path_err_rate_threshold); install_keyword("marginal_path_err_recheck_gap_time", &def_marginal_path_err_recheck_gap_time_handler, &snprint_def_marginal_path_err_recheck_gap_time); install_keyword("marginal_path_double_failed_time", &def_marginal_path_double_failed_time_handler, &snprint_def_marginal_path_double_failed_time); install_keyword("find_multipaths", &def_find_multipaths_handler, &snprint_def_find_multipaths); install_keyword("uxsock_timeout", &def_uxsock_timeout_handler, &snprint_def_uxsock_timeout); install_keyword("retrigger_tries", &def_retrigger_tries_handler, &snprint_def_retrigger_tries); install_keyword("retrigger_delay", &def_retrigger_delay_handler, &snprint_def_retrigger_delay); install_keyword("missing_uev_wait_timeout", &def_uev_wait_timeout_handler, &snprint_def_uev_wait_timeout); install_keyword("skip_kpartx", &def_skip_kpartx_handler, &snprint_def_skip_kpartx); install_keyword("disable_changed_wwids", &def_disable_changed_wwids_handler, &snprint_def_disable_changed_wwids); install_keyword("remove_retries", &def_remove_retries_handler, &snprint_def_remove_retries); install_keyword("max_sectors_kb", &def_max_sectors_kb_handler, &snprint_def_max_sectors_kb); __deprecated install_keyword("default_selector", &def_selector_handler, NULL); __deprecated install_keyword("default_path_grouping_policy", &def_pgpolicy_handler, NULL); __deprecated install_keyword("default_uid_attribute", &def_uid_attribute_handler, NULL); __deprecated install_keyword("default_getuid_callout", &def_getuid_handler, NULL); __deprecated install_keyword("default_features", &def_features_handler, NULL); __deprecated install_keyword("default_path_checker", &def_checker_name_handler, NULL); install_keyword_root("blacklist", &blacklist_handler); install_keyword_multi("devnode", &ble_blist_devnode_handler, &snprint_ble_simple); install_keyword_multi("wwid", &ble_blist_wwid_handler, &snprint_ble_simple); install_keyword_multi("property", &ble_blist_property_handler, &snprint_ble_simple); install_keyword_multi("device", &ble_device_handler, NULL); install_sublevel(); install_keyword("vendor", &ble_blist_device_vendor_handler, &snprint_bled_vendor); install_keyword("product", &ble_blist_device_product_handler, &snprint_bled_product); install_sublevel_end(); install_keyword_root("blacklist_exceptions", &blacklist_exceptions_handler); install_keyword_multi("devnode", &ble_elist_devnode_handler, &snprint_ble_simple); install_keyword_multi("wwid", &ble_elist_wwid_handler, &snprint_ble_simple); install_keyword_multi("property", &ble_elist_property_handler, &snprint_ble_simple); install_keyword_multi("device", &ble_except_device_handler, NULL); install_sublevel(); install_keyword("vendor", &ble_elist_device_vendor_handler, &snprint_bled_vendor); install_keyword("product", &ble_elist_device_product_handler, &snprint_bled_product); install_sublevel_end(); #if 0 __deprecated install_keyword_root("devnode_blacklist", &blacklist_handler); __deprecated install_keyword("devnode", &ble_devnode_handler, &snprint_ble_simple); __deprecated install_keyword("wwid", &ble_wwid_handler, &snprint_ble_simple); __deprecated install_keyword("device", &ble_device_handler, NULL); __deprecated install_sublevel(); __deprecated install_keyword("vendor", &ble_vendor_handler, &snprint_bled_vendor); __deprecated install_keyword("product", &ble_product_handler, &snprint_bled_product); __deprecated install_sublevel_end(); #endif /* * If you add or remove a "device subsection" keyword also update * multipath/multipath.conf.5 and the TEMPLATE in libmultipath/hwtable.c */ install_keyword_root("devices", &devices_handler); install_keyword_multi("device", &device_handler, NULL); install_sublevel(); install_keyword("vendor", &hw_vendor_handler, &snprint_hw_vendor); install_keyword("product", &hw_product_handler, &snprint_hw_product); install_keyword("revision", &hw_revision_handler, &snprint_hw_revision); install_keyword("product_blacklist", &hw_bl_product_handler, &snprint_hw_bl_product); install_keyword("path_grouping_policy", &hw_pgpolicy_handler, &snprint_hw_pgpolicy); install_keyword("uid_attribute", &hw_uid_attribute_handler, &snprint_hw_uid_attribute); install_keyword("getuid_callout", &hw_getuid_handler, &snprint_hw_getuid); install_keyword("path_selector", &hw_selector_handler, &snprint_hw_selector); install_keyword("path_checker", &hw_checker_name_handler, &snprint_hw_checker_name); install_keyword("checker", &hw_checker_name_handler, NULL); install_keyword("alias_prefix", &hw_alias_prefix_handler, &snprint_hw_alias_prefix); install_keyword("features", &hw_features_handler, &snprint_hw_features); install_keyword("hardware_handler", &hw_hwhandler_handler, &snprint_hw_hwhandler); install_keyword("prio", &hw_prio_name_handler, &snprint_hw_prio_name); install_keyword("prio_args", &hw_prio_args_handler, &snprint_hw_prio_args); install_keyword("failback", &hw_pgfailback_handler, &snprint_hw_pgfailback); install_keyword("rr_weight", &hw_rr_weight_handler, &snprint_hw_rr_weight); install_keyword("no_path_retry", &hw_no_path_retry_handler, &snprint_hw_no_path_retry); install_keyword("rr_min_io", &hw_minio_handler, &snprint_hw_minio); install_keyword("rr_min_io_rq", &hw_minio_rq_handler, &snprint_hw_minio_rq); install_keyword("pg_timeout", &deprecated_handler, &snprint_deprecated); install_keyword("flush_on_last_del", &hw_flush_on_last_del_handler, &snprint_hw_flush_on_last_del); install_keyword("fast_io_fail_tmo", &hw_fast_io_fail_handler, &snprint_hw_fast_io_fail); install_keyword("dev_loss_tmo", &hw_dev_loss_handler, &snprint_hw_dev_loss); install_keyword("user_friendly_names", &hw_user_friendly_names_handler, &snprint_hw_user_friendly_names); install_keyword("retain_attached_hw_handler", &hw_retain_hwhandler_handler, &snprint_hw_retain_hwhandler); install_keyword("detect_prio", &hw_detect_prio_handler, &snprint_hw_detect_prio); install_keyword("detect_checker", &hw_detect_checker_handler, &snprint_hw_detect_checker); install_keyword("deferred_remove", &hw_deferred_remove_handler, &snprint_hw_deferred_remove); install_keyword("delay_watch_checks", &hw_delay_watch_checks_handler, &snprint_hw_delay_watch_checks); install_keyword("delay_wait_checks", &hw_delay_wait_checks_handler, &snprint_hw_delay_wait_checks); install_keyword("marginal_path_err_sample_time", &hw_marginal_path_err_sample_time_handler, &snprint_hw_marginal_path_err_sample_time); install_keyword("marginal_path_err_rate_threshold", &hw_marginal_path_err_rate_threshold_handler, &snprint_hw_marginal_path_err_rate_threshold); install_keyword("marginal_path_err_recheck_gap_time", &hw_marginal_path_err_recheck_gap_time_handler, &snprint_hw_marginal_path_err_recheck_gap_time); install_keyword("marginal_path_double_failed_time", &hw_marginal_path_double_failed_time_handler, &snprint_hw_marginal_path_double_failed_time); install_keyword("skip_kpartx", &hw_skip_kpartx_handler, &snprint_hw_skip_kpartx); install_keyword("max_sectors_kb", &hw_max_sectors_kb_handler, &snprint_hw_max_sectors_kb); install_sublevel_end(); install_keyword_root("overrides", &overrides_handler); install_keyword("path_grouping_policy", &ovr_pgpolicy_handler, &snprint_ovr_pgpolicy); install_keyword("uid_attribute", &ovr_uid_attribute_handler, &snprint_ovr_uid_attribute); install_keyword("getuid_callout", &ovr_getuid_handler, &snprint_ovr_getuid); install_keyword("path_selector", &ovr_selector_handler, &snprint_ovr_selector); install_keyword("path_checker", &ovr_checker_name_handler, &snprint_ovr_checker_name); install_keyword("checker", &ovr_checker_name_handler, NULL); install_keyword("alias_prefix", &ovr_alias_prefix_handler, &snprint_ovr_alias_prefix); install_keyword("features", &ovr_features_handler, &snprint_ovr_features); install_keyword("prio", &ovr_prio_name_handler, &snprint_ovr_prio_name); install_keyword("prio_args", &ovr_prio_args_handler, &snprint_ovr_prio_args); install_keyword("failback", &ovr_pgfailback_handler, &snprint_ovr_pgfailback); install_keyword("rr_weight", &ovr_rr_weight_handler, &snprint_ovr_rr_weight); install_keyword("no_path_retry", &ovr_no_path_retry_handler, &snprint_ovr_no_path_retry); install_keyword("rr_min_io", &ovr_minio_handler, &snprint_ovr_minio); install_keyword("rr_min_io_rq", &ovr_minio_rq_handler, &snprint_ovr_minio_rq); install_keyword("flush_on_last_del", &ovr_flush_on_last_del_handler, &snprint_ovr_flush_on_last_del); install_keyword("fast_io_fail_tmo", &ovr_fast_io_fail_handler, &snprint_ovr_fast_io_fail); install_keyword("dev_loss_tmo", &ovr_dev_loss_handler, &snprint_ovr_dev_loss); install_keyword("user_friendly_names", &ovr_user_friendly_names_handler, &snprint_ovr_user_friendly_names); install_keyword("retain_attached_hw_handler", &ovr_retain_hwhandler_handler, &snprint_ovr_retain_hwhandler); install_keyword("detect_prio", &ovr_detect_prio_handler, &snprint_ovr_detect_prio); install_keyword("detect_checker", &ovr_detect_checker_handler, &snprint_ovr_detect_checker); install_keyword("deferred_remove", &ovr_deferred_remove_handler, &snprint_ovr_deferred_remove); install_keyword("delay_watch_checks", &ovr_delay_watch_checks_handler, &snprint_ovr_delay_watch_checks); install_keyword("delay_wait_checks", &ovr_delay_wait_checks_handler, &snprint_ovr_delay_wait_checks); install_keyword("marginal_path_err_sample_time", &ovr_marginal_path_err_sample_time_handler, &snprint_ovr_marginal_path_err_sample_time); install_keyword("marginal_path_err_rate_threshold", &ovr_marginal_path_err_rate_threshold_handler, &snprint_ovr_marginal_path_err_rate_threshold); install_keyword("marginal_path_err_recheck_gap_time", &ovr_marginal_path_err_recheck_gap_time_handler, &snprint_ovr_marginal_path_err_recheck_gap_time); install_keyword("marginal_path_double_failed_time", &ovr_marginal_path_double_failed_time_handler, &snprint_ovr_marginal_path_double_failed_time); install_keyword("skip_kpartx", &ovr_skip_kpartx_handler, &snprint_ovr_skip_kpartx); install_keyword("max_sectors_kb", &ovr_max_sectors_kb_handler, &snprint_ovr_max_sectors_kb); install_keyword_root("multipaths", &multipaths_handler); install_keyword_multi("multipath", &multipath_handler, NULL); install_sublevel(); install_keyword("wwid", &mp_wwid_handler, &snprint_mp_wwid); install_keyword("alias", &mp_alias_handler, &snprint_mp_alias); install_keyword("path_grouping_policy", &mp_pgpolicy_handler, &snprint_mp_pgpolicy); install_keyword("path_selector", &mp_selector_handler, &snprint_mp_selector); install_keyword("prio", &mp_prio_name_handler, &snprint_mp_prio_name); install_keyword("prio_args", &mp_prio_args_handler, &snprint_mp_prio_args); install_keyword("failback", &mp_pgfailback_handler, &snprint_mp_pgfailback); install_keyword("rr_weight", &mp_rr_weight_handler, &snprint_mp_rr_weight); install_keyword("no_path_retry", &mp_no_path_retry_handler, &snprint_mp_no_path_retry); install_keyword("rr_min_io", &mp_minio_handler, &snprint_mp_minio); install_keyword("rr_min_io_rq", &mp_minio_rq_handler, &snprint_mp_minio_rq); install_keyword("pg_timeout", &deprecated_handler, &snprint_deprecated); install_keyword("flush_on_last_del", &mp_flush_on_last_del_handler, &snprint_mp_flush_on_last_del); install_keyword("features", &mp_features_handler, &snprint_mp_features); install_keyword("mode", &mp_mode_handler, &snprint_mp_mode); install_keyword("uid", &mp_uid_handler, &snprint_mp_uid); install_keyword("gid", &mp_gid_handler, &snprint_mp_gid); install_keyword("reservation_key", &mp_reservation_key_handler, &snprint_mp_reservation_key); install_keyword("user_friendly_names", &mp_user_friendly_names_handler, &snprint_mp_user_friendly_names); install_keyword("deferred_remove", &mp_deferred_remove_handler, &snprint_mp_deferred_remove); install_keyword("delay_watch_checks", &mp_delay_watch_checks_handler, &snprint_mp_delay_watch_checks); install_keyword("delay_wait_checks", &mp_delay_wait_checks_handler, &snprint_mp_delay_wait_checks); install_keyword("marginal_path_err_sample_time", &mp_marginal_path_err_sample_time_handler, &snprint_mp_marginal_path_err_sample_time); install_keyword("marginal_path_err_rate_threshold", &mp_marginal_path_err_rate_threshold_handler, &snprint_mp_marginal_path_err_rate_threshold); install_keyword("marginal_path_err_recheck_gap_time", &mp_marginal_path_err_recheck_gap_time_handler, &snprint_mp_marginal_path_err_recheck_gap_time); install_keyword("marginal_path_double_failed_time", &mp_marginal_path_double_failed_time_handler, &snprint_mp_marginal_path_double_failed_time); install_keyword("skip_kpartx", &mp_skip_kpartx_handler, &snprint_mp_skip_kpartx); install_keyword("max_sectors_kb", &mp_max_sectors_kb_handler, &snprint_mp_max_sectors_kb); install_sublevel_end(); } multipath-tools-0.7.4/libmultipath/dict.h000066400000000000000000000012211320314174000204500ustar00rootroot00000000000000#ifndef _DICT_H #define _DICT_H #ifndef _VECTOR_H #include "vector.h" #endif #include "byteorder.h" void init_keywords(vector keywords); int get_sys_max_fds(int *); int print_rr_weight (char * buff, int len, void *ptr); int print_pgfailback (char * buff, int len, void *ptr); int print_pgpolicy(char * buff, int len, void *ptr); int print_no_path_retry(char * buff, int len, void *ptr); int print_fast_io_fail(char * buff, int len, void *ptr); int print_dev_loss(char * buff, int len, void *ptr); int print_reservation_key(char * buff, int len, struct be64 key, int source); int print_off_int_undef(char * buff, int len, void *ptr); #endif /* _DICT_H */ multipath-tools-0.7.4/libmultipath/discovery.c000066400000000000000000001365101320314174000215410ustar00rootroot00000000000000/* * Copyright (c) 2004, 2005, 2006 Christophe Varoqui * Copyright (c) 2005 Stefan Bader, IBM * Copyright (c) 2005 Mike Anderson */ #include #include #include #include #include #include #include #include #include #include #include #include "checkers.h" #include "vector.h" #include "memory.h" #include "util.h" #include "structs.h" #include "config.h" #include "blacklist.h" #include "callout.h" #include "debug.h" #include "propsel.h" #include "sg_include.h" #include "sysfs.h" #include "discovery.h" #include "prio.h" #include "defaults.h" #include "prioritizers/alua_rtpg.h" int alloc_path_with_pathinfo (struct config *conf, struct udev_device *udevice, char *wwid, int flag, struct path **pp_ptr) { int err = PATHINFO_FAILED; struct path * pp; const char * devname; if (pp_ptr) *pp_ptr = NULL; devname = udev_device_get_sysname(udevice); if (!devname) return PATHINFO_FAILED; pp = alloc_path(); if (!pp) return PATHINFO_FAILED; if(wwid) strncpy(pp->wwid, wwid, sizeof(pp->wwid)); if (safe_sprintf(pp->dev, "%s", devname)) { condlog(0, "pp->dev too small"); } else { pp->udev = udev_device_ref(udevice); err = pathinfo(pp, conf, flag | DI_BLACKLIST); } if (err || !pp_ptr) free_path(pp); else if (pp_ptr) *pp_ptr = pp; return err; } int store_pathinfo (vector pathvec, struct config *conf, struct udev_device *udevice, int flag, struct path **pp_ptr) { int err = PATHINFO_FAILED; struct path * pp; const char * devname; if (pp_ptr) *pp_ptr = NULL; devname = udev_device_get_sysname(udevice); if (!devname) return PATHINFO_FAILED; pp = alloc_path(); if (!pp) return PATHINFO_FAILED; if(safe_sprintf(pp->dev, "%s", devname)) { condlog(0, "pp->dev too small"); goto out; } pp->udev = udev_device_ref(udevice); err = pathinfo(pp, conf, flag); if (err) goto out; err = store_path(pathvec, pp); if (err) goto out; out: if (err) free_path(pp); else if (pp_ptr) *pp_ptr = pp; return err; } static int path_discover (vector pathvec, struct config * conf, struct udev_device *udevice, int flag) { struct path * pp; const char * devname; devname = udev_device_get_sysname(udevice); if (!devname) return PATHINFO_FAILED; pp = find_path_by_dev(pathvec, (char *)devname); if (!pp) { char devt[BLK_DEV_SIZE]; dev_t devnum = udev_device_get_devnum(udevice); snprintf(devt, BLK_DEV_SIZE, "%d:%d", major(devnum), minor(devnum)); pp = find_path_by_devt(pathvec, devt); if (!pp) return store_pathinfo(pathvec, conf, udevice, flag, NULL); } return pathinfo(pp, conf, flag); } int path_discovery (vector pathvec, int flag) { struct udev_enumerate *udev_iter; struct udev_list_entry *entry; struct udev_device *udevice; struct config *conf; const char *devpath; int num_paths = 0, total_paths = 0; udev_iter = udev_enumerate_new(udev); if (!udev_iter) return -ENOMEM; udev_enumerate_add_match_subsystem(udev_iter, "block"); udev_enumerate_add_match_is_initialized(udev_iter); udev_enumerate_scan_devices(udev_iter); udev_list_entry_foreach(entry, udev_enumerate_get_list_entry(udev_iter)) { const char *devtype; devpath = udev_list_entry_get_name(entry); condlog(4, "Discover device %s", devpath); udevice = udev_device_new_from_syspath(udev, devpath); if (!udevice) { condlog(4, "%s: no udev information", devpath); continue; } devtype = udev_device_get_devtype(udevice); if(devtype && !strncmp(devtype, "disk", 4)) { total_paths++; conf = get_multipath_config(); if (path_discover(pathvec, conf, udevice, flag) == PATHINFO_OK) num_paths++; put_multipath_config(conf); } udev_device_unref(udevice); } udev_enumerate_unref(udev_iter); condlog(4, "Discovered %d/%d paths", num_paths, total_paths); return (total_paths - num_paths); } #define declare_sysfs_get_str(fname) \ ssize_t \ sysfs_get_##fname (struct udev_device * udev, char * buff, size_t len) \ { \ int l; \ const char * attr; \ const char * devname; \ \ if (!udev) \ return -ENOSYS; \ \ devname = udev_device_get_sysname(udev); \ \ attr = udev_device_get_sysattr_value(udev, #fname); \ if (!attr) { \ condlog(3, "%s: attribute %s not found in sysfs", \ devname, #fname); \ return -ENXIO; \ } \ for (l = strlen(attr); l >= 1 && isspace(attr[l-1]); l--); \ if (l > len) { \ condlog(3, "%s: overflow in attribute %s", \ devname, #fname); \ return -EINVAL; \ } \ strlcpy(buff, attr, len); \ return strchop(buff); \ } declare_sysfs_get_str(devtype); declare_sysfs_get_str(vendor); declare_sysfs_get_str(model); declare_sysfs_get_str(rev); ssize_t sysfs_get_vpd (struct udev_device * udev, int pg, unsigned char * buff, size_t len) { ssize_t attr_len; char attrname[9]; const char * devname; if (!udev) { condlog(3, "No udev device given\n"); return -ENOSYS; } devname = udev_device_get_sysname(udev); sprintf(attrname, "vpd_pg%02x", pg); attr_len = sysfs_bin_attr_get_value(udev, attrname, buff, len); if (attr_len < 0) { condlog(3, "%s: attribute %s not found in sysfs", devname, attrname); return attr_len; } return attr_len; } int sysfs_get_timeout(struct path *pp, unsigned int *timeout) { const char *attr = NULL; const char *subsys; struct udev_device *parent; char *eptr; unsigned long t; if (!pp->udev || pp->bus != SYSFS_BUS_SCSI) return -ENOSYS; parent = pp->udev; while (parent) { subsys = udev_device_get_subsystem(parent); attr = udev_device_get_sysattr_value(parent, "timeout"); if (subsys && attr) break; parent = udev_device_get_parent(parent); } if (!attr) { condlog(3, "%s: No timeout value in sysfs", pp->dev); return -ENXIO; } t = strtoul(attr, &eptr, 0); if (attr == eptr || t == ULONG_MAX) { condlog(3, "%s: Cannot parse timeout attribute '%s'", pp->dev, attr); return -EINVAL; } if (t > UINT_MAX) { condlog(3, "%s: Overflow in timeout value '%s'", pp->dev, attr); return -ERANGE; } *timeout = t; return 0; } int sysfs_get_tgt_nodename (struct path *pp, char * node) { const char *tgtname, *value; struct udev_device *parent, *tgtdev; int host, channel, tgtid = -1; parent = udev_device_get_parent_with_subsystem_devtype(pp->udev, "scsi", "scsi_device"); if (!parent) return 1; /* Check for SAS */ value = udev_device_get_sysattr_value(parent, "sas_address"); if (value) { tgtdev = udev_device_get_parent(parent); while (tgtdev) { tgtname = udev_device_get_sysname(tgtdev); if (sscanf(tgtname, "end_device-%d:%d", &host, &tgtid) == 2) break; tgtdev = udev_device_get_parent(tgtdev); tgtid = -1; } if (tgtid >= 0) { pp->sg_id.proto_id = SCSI_PROTOCOL_SAS; pp->sg_id.transport_id = tgtid; strncpy(node, value, NODE_NAME_SIZE); return 0; } } /* Check for USB */ tgtdev = udev_device_get_parent(parent); while (tgtdev) { value = udev_device_get_subsystem(tgtdev); if (value && !strcmp(value, "usb")) { pp->sg_id.proto_id = SCSI_PROTOCOL_UNSPEC; tgtname = udev_device_get_sysname(tgtdev); strncpy(node, tgtname, strlen(tgtname)); condlog(3, "%s: skip USB device %s", pp->dev, node); return 1; } tgtdev = udev_device_get_parent(tgtdev); } parent = udev_device_get_parent_with_subsystem_devtype(pp->udev, "scsi", "scsi_target"); if (!parent) return 1; /* Check for FibreChannel */ tgtdev = udev_device_get_parent(parent); value = udev_device_get_sysname(tgtdev); if (sscanf(value, "rport-%d:%d-%d", &host, &channel, &tgtid) == 3) { tgtdev = udev_device_new_from_subsystem_sysname(udev, "fc_remote_ports", value); if (tgtdev) { condlog(3, "SCSI target %d:%d:%d -> " "FC rport %d:%d-%d", pp->sg_id.host_no, pp->sg_id.channel, pp->sg_id.scsi_id, host, channel, tgtid); value = udev_device_get_sysattr_value(tgtdev, "node_name"); if (value) { pp->sg_id.proto_id = SCSI_PROTOCOL_FCP; pp->sg_id.transport_id = tgtid; strncpy(node, value, NODE_NAME_SIZE); udev_device_unref(tgtdev); return 0; } else udev_device_unref(tgtdev); } } /* Check for iSCSI */ parent = pp->udev; tgtname = NULL; while (parent) { tgtname = udev_device_get_sysname(parent); if (tgtname && sscanf(tgtname , "session%d", &tgtid) == 1) break; parent = udev_device_get_parent(parent); tgtname = NULL; tgtid = -1; } if (parent && tgtname) { tgtdev = udev_device_new_from_subsystem_sysname(udev, "iscsi_session", tgtname); if (tgtdev) { const char *value; value = udev_device_get_sysattr_value(tgtdev, "targetname"); if (value) { pp->sg_id.proto_id = SCSI_PROTOCOL_ISCSI; pp->sg_id.transport_id = tgtid; strncpy(node, value, NODE_NAME_SIZE); udev_device_unref(tgtdev); return 0; } else udev_device_unref(tgtdev); } } /* Check for libata */ parent = pp->udev; tgtname = NULL; while (parent) { tgtname = udev_device_get_sysname(parent); if (tgtname && sscanf(tgtname, "ata%d", &tgtid) == 1) break; parent = udev_device_get_parent(parent); tgtname = NULL; } if (tgtname) { pp->sg_id.proto_id = SCSI_PROTOCOL_ATA; pp->sg_id.transport_id = tgtid; snprintf(node, NODE_NAME_SIZE, "ata-%d.00", tgtid); return 0; } /* Unknown SCSI transport. Keep fingers crossed */ pp->sg_id.proto_id = SCSI_PROTOCOL_UNSPEC; return 0; } int sysfs_get_host_adapter_name(struct path *pp, char *adapter_name) { int proto_id; if (!pp || !adapter_name) return 1; proto_id = pp->sg_id.proto_id; if (proto_id != SCSI_PROTOCOL_FCP && proto_id != SCSI_PROTOCOL_SAS && proto_id != SCSI_PROTOCOL_ISCSI && proto_id != SCSI_PROTOCOL_SRP) { return 1; } /* iscsi doesn't have adapter info in sysfs * get ip_address for grouping paths */ if (pp->sg_id.proto_id == SCSI_PROTOCOL_ISCSI) return sysfs_get_iscsi_ip_address(pp, adapter_name); /* fetch adapter pci name for other protocols */ return sysfs_get_host_pci_name(pp, adapter_name); } int sysfs_get_host_pci_name(struct path *pp, char *pci_name) { struct udev_device *hostdev, *parent; char host_name[HOST_NAME_LEN]; const char *driver_name, *value; if (!pp || !pci_name) return 1; sprintf(host_name, "host%d", pp->sg_id.host_no); hostdev = udev_device_new_from_subsystem_sysname(udev, "scsi_host", host_name); if (!hostdev) return 1; parent = udev_device_get_parent(hostdev); while (parent) { driver_name = udev_device_get_driver(parent); if (!driver_name) { parent = udev_device_get_parent(parent); continue; } if (!strcmp(driver_name, "pcieport")) break; parent = udev_device_get_parent(parent); } if (parent) { /* pci_device found */ value = udev_device_get_sysname(parent); strncpy(pci_name, value, SLOT_NAME_SIZE); udev_device_unref(hostdev); return 0; } udev_device_unref(hostdev); return 1; } int sysfs_get_iscsi_ip_address(struct path *pp, char *ip_address) { struct udev_device *hostdev; char host_name[HOST_NAME_LEN]; const char *value; sprintf(host_name, "host%d", pp->sg_id.host_no); hostdev = udev_device_new_from_subsystem_sysname(udev, "iscsi_host", host_name); if (hostdev) { value = udev_device_get_sysattr_value(hostdev, "ipaddress"); if (value) { strncpy(ip_address, value, SLOT_NAME_SIZE); udev_device_unref(hostdev); return 0; } else udev_device_unref(hostdev); } return 1; } int sysfs_get_asymmetric_access_state(struct path *pp, char *buff, int buflen) { struct udev_device *parent = pp->udev; char value[16], *eptr; unsigned long preferred; while (parent) { const char *subsys = udev_device_get_subsystem(parent); if (subsys && !strncmp(subsys, "scsi", 4)) break; parent = udev_device_get_parent(parent); } if (!parent) return -1; if (sysfs_attr_get_value(parent, "access_state", buff, buflen) <= 0) return -1; if (sysfs_attr_get_value(parent, "preferred_path", value, 16) <= 0) return 0; preferred = strtoul(value, &eptr, 0); if (value == eptr || preferred == ULONG_MAX) { /* Parse error, ignore */ return 0; } return preferred; } static void sysfs_set_rport_tmo(struct multipath *mpp, struct path *pp) { struct udev_device *rport_dev = NULL; char value[16], *eptr; char rport_id[32]; unsigned long long tmo = 0; int ret; sprintf(rport_id, "rport-%d:%d-%d", pp->sg_id.host_no, pp->sg_id.channel, pp->sg_id.transport_id); rport_dev = udev_device_new_from_subsystem_sysname(udev, "fc_remote_ports", rport_id); if (!rport_dev) { condlog(1, "%s: No fc_remote_port device for '%s'", pp->dev, rport_id); return; } condlog(4, "target%d:%d:%d -> %s", pp->sg_id.host_no, pp->sg_id.channel, pp->sg_id.scsi_id, rport_id); /* * read the current dev_loss_tmo value from sysfs */ ret = sysfs_attr_get_value(rport_dev, "dev_loss_tmo", value, 16); if (ret <= 0) { condlog(0, "%s: failed to read dev_loss_tmo value, " "error %d", rport_id, -ret); goto out; } tmo = strtoull(value, &eptr, 0); if (value == eptr || tmo == ULLONG_MAX) { condlog(0, "%s: Cannot parse dev_loss_tmo " "attribute '%s'", rport_id, value); goto out; } /* * This is tricky. * dev_loss_tmo will be limited to 600 if fast_io_fail * is _not_ set. * fast_io_fail will be limited by the current dev_loss_tmo * setting. * So to get everything right we first need to increase * dev_loss_tmo to the fast_io_fail setting (if present), * then set fast_io_fail, and _then_ set dev_loss_tmo * to the correct value. */ if (mpp->fast_io_fail != MP_FAST_IO_FAIL_UNSET && mpp->fast_io_fail != MP_FAST_IO_FAIL_ZERO && mpp->fast_io_fail != MP_FAST_IO_FAIL_OFF) { /* Check if we need to temporarily increase dev_loss_tmo */ if (mpp->fast_io_fail >= tmo) { /* Increase dev_loss_tmo temporarily */ snprintf(value, 16, "%u", mpp->fast_io_fail + 1); ret = sysfs_attr_set_value(rport_dev, "dev_loss_tmo", value, strlen(value)); if (ret <= 0) { if (ret == -EBUSY) condlog(3, "%s: rport blocked", rport_id); else condlog(0, "%s: failed to set " "dev_loss_tmo to %s, error %d", rport_id, value, -ret); goto out; } } } else if (mpp->dev_loss > DEFAULT_DEV_LOSS_TMO && mpp->no_path_retry != NO_PATH_RETRY_QUEUE) { condlog(3, "%s: limiting dev_loss_tmo to %d, since " "fast_io_fail is not set", rport_id, DEFAULT_DEV_LOSS_TMO); mpp->dev_loss = DEFAULT_DEV_LOSS_TMO; } if (mpp->fast_io_fail != MP_FAST_IO_FAIL_UNSET) { if (mpp->fast_io_fail == MP_FAST_IO_FAIL_OFF) sprintf(value, "off"); else if (mpp->fast_io_fail == MP_FAST_IO_FAIL_ZERO) sprintf(value, "0"); else snprintf(value, 16, "%u", mpp->fast_io_fail); ret = sysfs_attr_set_value(rport_dev, "fast_io_fail_tmo", value, strlen(value)); if (ret <= 0) { if (ret == -EBUSY) condlog(3, "%s: rport blocked", rport_id); else condlog(0, "%s: failed to set fast_io_fail_tmo to %s, error %d", rport_id, value, -ret); } } if (mpp->dev_loss > 0) { snprintf(value, 16, "%u", mpp->dev_loss); ret = sysfs_attr_set_value(rport_dev, "dev_loss_tmo", value, strlen(value)); if (ret <= 0) { if (ret == -EBUSY) condlog(3, "%s: rport blocked", rport_id); else condlog(0, "%s: failed to set dev_loss_tmo to %s, error %d", rport_id, value, -ret); } } out: udev_device_unref(rport_dev); } static void sysfs_set_session_tmo(struct multipath *mpp, struct path *pp) { struct udev_device *session_dev = NULL; char session_id[64]; char value[11]; sprintf(session_id, "session%d", pp->sg_id.transport_id); session_dev = udev_device_new_from_subsystem_sysname(udev, "iscsi_session", session_id); if (!session_dev) { condlog(1, "%s: No iscsi session for '%s'", pp->dev, session_id); return; } condlog(4, "target%d:%d:%d -> %s", pp->sg_id.host_no, pp->sg_id.channel, pp->sg_id.scsi_id, session_id); if (mpp->dev_loss) { condlog(3, "%s: ignoring dev_loss_tmo on iSCSI", pp->dev); } if (mpp->fast_io_fail != MP_FAST_IO_FAIL_UNSET) { if (mpp->fast_io_fail == MP_FAST_IO_FAIL_OFF) { condlog(3, "%s: can't switch off fast_io_fail_tmo " "on iSCSI", pp->dev); } else if (mpp->fast_io_fail == MP_FAST_IO_FAIL_ZERO) { condlog(3, "%s: can't set fast_io_fail_tmo to '0'" "on iSCSI", pp->dev); } else { snprintf(value, 11, "%u", mpp->fast_io_fail); if (sysfs_attr_set_value(session_dev, "recovery_tmo", value, 11) <= 0) { condlog(3, "%s: Failed to set recovery_tmo, " " error %d", pp->dev, errno); } } } udev_device_unref(session_dev); return; } static void sysfs_set_nexus_loss_tmo(struct multipath *mpp, struct path *pp) { struct udev_device *sas_dev = NULL; char end_dev_id[64]; char value[11]; sprintf(end_dev_id, "end_device-%d:%d", pp->sg_id.host_no, pp->sg_id.transport_id); sas_dev = udev_device_new_from_subsystem_sysname(udev, "sas_end_device", end_dev_id); if (!sas_dev) { condlog(1, "%s: No SAS end device for '%s'", pp->dev, end_dev_id); return; } condlog(4, "target%d:%d:%d -> %s", pp->sg_id.host_no, pp->sg_id.channel, pp->sg_id.scsi_id, end_dev_id); if (mpp->dev_loss) { snprintf(value, 11, "%u", mpp->dev_loss); if (sysfs_attr_set_value(sas_dev, "I_T_nexus_loss_timeout", value, 11) <= 0) condlog(3, "%s: failed to update " "I_T Nexus loss timeout, error %d", pp->dev, errno); } udev_device_unref(sas_dev); return; } int sysfs_set_scsi_tmo (struct multipath *mpp, int checkint) { struct path *pp; int i; int dev_loss_tmo = mpp->dev_loss; if (mpp->no_path_retry > 0) { uint64_t no_path_retry_tmo = mpp->no_path_retry * checkint; if (no_path_retry_tmo > MAX_DEV_LOSS_TMO) no_path_retry_tmo = MAX_DEV_LOSS_TMO; if (no_path_retry_tmo > dev_loss_tmo) dev_loss_tmo = no_path_retry_tmo; condlog(3, "%s: update dev_loss_tmo to %u", mpp->alias, dev_loss_tmo); } else if (mpp->no_path_retry == NO_PATH_RETRY_QUEUE) { dev_loss_tmo = MAX_DEV_LOSS_TMO; condlog(3, "%s: update dev_loss_tmo to %u", mpp->alias, dev_loss_tmo); } mpp->dev_loss = dev_loss_tmo; if (mpp->dev_loss && mpp->fast_io_fail >= (int)mpp->dev_loss) { condlog(3, "%s: turning off fast_io_fail (%d is not smaller than dev_loss_tmo)", mpp->alias, mpp->fast_io_fail); mpp->fast_io_fail = MP_FAST_IO_FAIL_OFF; } if (!mpp->dev_loss && mpp->fast_io_fail == MP_FAST_IO_FAIL_UNSET) return 0; vector_foreach_slot(mpp->paths, pp, i) { if (pp->sg_id.proto_id == SCSI_PROTOCOL_FCP) sysfs_set_rport_tmo(mpp, pp); if (pp->sg_id.proto_id == SCSI_PROTOCOL_ISCSI) sysfs_set_session_tmo(mpp, pp); if (pp->sg_id.proto_id == SCSI_PROTOCOL_SAS) sysfs_set_nexus_loss_tmo(mpp, pp); } return 0; } int do_inq(int sg_fd, int cmddt, int evpd, unsigned int pg_op, void *resp, int mx_resp_len) { unsigned char inqCmdBlk[INQUIRY_CMDLEN] = { INQUIRY_CMD, 0, 0, 0, 0, 0 }; unsigned char sense_b[SENSE_BUFF_LEN]; struct sg_io_hdr io_hdr; if (cmddt) inqCmdBlk[1] |= 2; if (evpd) inqCmdBlk[1] |= 1; inqCmdBlk[2] = (unsigned char) pg_op; inqCmdBlk[3] = (unsigned char)((mx_resp_len >> 8) & 0xff); inqCmdBlk[4] = (unsigned char) (mx_resp_len & 0xff); memset(&io_hdr, 0, sizeof (struct sg_io_hdr)); memset(sense_b, 0, SENSE_BUFF_LEN); io_hdr.interface_id = 'S'; io_hdr.cmd_len = sizeof (inqCmdBlk); io_hdr.mx_sb_len = sizeof (sense_b); io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; io_hdr.dxfer_len = mx_resp_len; io_hdr.dxferp = resp; io_hdr.cmdp = inqCmdBlk; io_hdr.sbp = sense_b; io_hdr.timeout = DEF_TIMEOUT; if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) return -1; /* treat SG_ERR here to get rid of sg_err.[ch] */ io_hdr.status &= 0x7e; if ((0 == io_hdr.status) && (0 == io_hdr.host_status) && (0 == io_hdr.driver_status)) return 0; if ((SCSI_CHECK_CONDITION == io_hdr.status) || (SCSI_COMMAND_TERMINATED == io_hdr.status) || (SG_ERR_DRIVER_SENSE == (0xf & io_hdr.driver_status))) { if (io_hdr.sbp && (io_hdr.sb_len_wr > 2)) { int sense_key; unsigned char * sense_buffer = io_hdr.sbp; if (sense_buffer[0] & 0x2) sense_key = sense_buffer[1] & 0xf; else sense_key = sense_buffer[2] & 0xf; if(RECOVERED_ERROR == sense_key) return 0; } } return -1; } static int get_serial (char * str, int maxlen, int fd) { int len = 0; char buff[MX_ALLOC_LEN + 1] = {0}; if (fd < 0) return 1; if (0 == do_inq(fd, 0, 1, 0x80, buff, MX_ALLOC_LEN)) { len = buff[3]; if (len >= maxlen) return 1; if (len > 0) { memcpy(str, buff + 4, len); str[len] = '\0'; } return 0; } return 1; } static void detect_alua(struct path * pp, struct config *conf) { int ret; int tpgs; unsigned int timeout = conf->checker_timeout; if ((tpgs = get_target_port_group_support(pp->fd, timeout)) <= 0) { pp->tpgs = TPGS_NONE; return; } ret = get_target_port_group(pp, timeout); if (ret < 0 || get_asymmetric_access_state(pp->fd, ret, timeout) < 0) { pp->tpgs = TPGS_NONE; return; } pp->tpgs = tpgs; } #define DEFAULT_SGIO_LEN 254 static int sgio_get_vpd (unsigned char * buff, int maxlen, int fd) { int len = DEFAULT_SGIO_LEN; if (fd < 0) { errno = EBADF; return -1; } retry: if (0 == do_inq(fd, 0, 1, 0x83, buff, len)) { len = buff[3] + (buff[2] << 8); if (len >= maxlen) return len; if (len > DEFAULT_SGIO_LEN) goto retry; return 0; } return -1; } static int get_geometry(struct path *pp) { if (pp->fd < 0) return 1; if (ioctl(pp->fd, HDIO_GETGEO, &pp->geom)) { condlog(2, "%s: HDIO_GETGEO failed with %d", pp->dev, errno); memset(&pp->geom, 0, sizeof(pp->geom)); return 1; } condlog(3, "%s: %u cyl, %u heads, %u sectors/track, start at %lu", pp->dev, pp->geom.cylinders, pp->geom.heads, pp->geom.sectors, pp->geom.start); return 0; } static int parse_vpd_pg80(const unsigned char *in, char *out, size_t out_len) { char *p = NULL; int len = in[3] + (in[2] << 8); if (len >= out_len) { condlog(2, "vpd pg80 overflow, %d/%d bytes required", len, (int)out_len); len = out_len; } if (len > 0) { memcpy(out, in + 4, len); out[len] = '\0'; } /* * Strip trailing whitspaces */ p = out + len - 1; while (p > out && *p == ' ') { *p = '\0'; p--; len --; } return len; } static int parse_vpd_pg83(const unsigned char *in, size_t in_len, char *out, size_t out_len) { unsigned char *d; unsigned char *vpd = NULL; int len = -ENODATA, vpd_type, vpd_len, prio = -1, i, naa_prio; d = (unsigned char *)in + 4; while (d < (unsigned char *)in + in_len) { /* Select 'association: LUN' */ if ((d[1] & 0x30) != 0) { d += d[3] + 4; continue; } switch (d[1] & 0xf) { case 0x3: /* NAA: Prio 5 */ switch (d[4] >> 4) { case 6: /* IEEE Registered Extended: Prio 8 */ naa_prio = 8; break; case 5: /* IEEE Registered: Prio 7 */ naa_prio = 7; break; case 2: /* IEEE Extended: Prio 6 */ naa_prio = 6; break; case 3: /* IEEE Locally assigned: Prio 1 */ naa_prio = 1; break; default: /* Default: no priority */ naa_prio = -1; break; } if (prio < naa_prio) { prio = naa_prio; vpd = d; } break; case 0x8: /* SCSI Name: Prio 4 */ if (memcmp(d + 4, "eui.", 4) && memcmp(d + 4, "naa.", 4) && memcmp(d + 4, "iqn.", 4)) continue; if (prio < 4) { prio = 4; vpd = d; } break; case 0x2: /* EUI-64: Prio 3 */ if (prio < 3) { prio = 3; vpd = d; } break; case 0x1: /* T-10 Vendor ID: Prio 2 */ if (prio < 2) { prio = 2; vpd = d; } break; } d += d[3] + 4; } if (prio > 0) { vpd_type = vpd[1] & 0xf; vpd_len = vpd[3]; vpd += 4; if (vpd_type == 0x2 || vpd_type == 0x3) { int i; len = sprintf(out, "%d", vpd_type); for (i = 0; i < vpd_len; i++) { len += sprintf(out + len, "%02x", vpd[i]); if (len >= out_len) break; } } else if (vpd_type == 0x8) { if (!memcmp("eui.", vpd, 4)) { out[0] = '2'; len = 1; vpd += 4; vpd_len -= 4; for (i = 0; i < vpd_len; i++) { len += sprintf(out + len, "%c", tolower(vpd[i])); if (len >= out_len) break; } len = vpd_len + 1; out[len] = '\0'; } else if (!memcmp("naa.", vpd, 4)) { out[0] = '3'; len = 1; vpd += 4; vpd_len -= 4; for (i = 0; i < vpd_len; i++) { len += sprintf(out + len, "%c", tolower(vpd[i])); if (len >= out_len) break; } len = vpd_len + 1; out[len] = '\0'; } else { out[0] = '8'; len = 1; vpd += 4; vpd_len -= 4; if (vpd_len > out_len + 2) vpd_len = out_len - 2; memcpy(out, vpd, vpd_len); len = vpd_len + 1; out[len] = '\0'; } } else if (vpd_type == 0x1) { unsigned char *p; int p_len; out[0] = '1'; len = 1; p = vpd; while ((p = memchr(vpd, ' ', vpd_len))) { p_len = p - vpd; if (len + p_len > out_len - 1) p_len = out_len - len - 2; memcpy(out + len, vpd, p_len); len += p_len; if (len >= out_len - 1) { out[len] = '\0'; break; } out[len] = '_'; len ++; vpd = p; vpd_len -= p_len; while (vpd && *vpd == ' ') { vpd++; vpd_len --; } } if (len > 1 && out[len - 1] == '_') { out[len - 1] = '\0'; len--; } } } return len; } static int get_vpd_sysfs (struct udev_device *parent, int pg, char * str, int maxlen) { int len, buff_len; unsigned char buff[4096]; memset(buff, 0x0, 4096); if (!parent || sysfs_get_vpd(parent, pg, buff, 4096) <= 0) { condlog(3, "failed to read sysfs vpd pg%02x", pg); return -EINVAL; } if (buff[1] != pg) { condlog(3, "vpd pg%02x error, invalid vpd page %02x", pg, buff[1]); return -ENODATA; } buff_len = (buff[2] << 8) + buff[3] + 4; if (buff_len > 4096) condlog(3, "vpd pg%02x page truncated", pg); if (pg == 0x80) len = parse_vpd_pg80(buff, str, maxlen); else if (pg == 0x83) len = parse_vpd_pg83(buff, buff_len, str, maxlen); else len = -ENOSYS; return len; } int get_vpd_sgio (int fd, int pg, char * str, int maxlen) { int len, buff_len; unsigned char buff[4096]; memset(buff, 0x0, 4096); if (sgio_get_vpd(buff, 4096, fd) <= 0) { condlog(3, "failed to issue vpd inquiry for pg%02x", pg); return -errno; } if (buff[1] != pg) { condlog(3, "vpd pg%02x error, invalid vpd page %02x", pg, buff[1]); return -ENODATA; } buff_len = (buff[2] << 8) + buff[3] + 4; if (buff_len > 4096) condlog(3, "vpd pg%02x page truncated", pg); if (pg == 0x80) len = parse_vpd_pg80(buff, str, maxlen); else if (pg == 0x83) len = parse_vpd_pg83(buff, buff_len, str, maxlen); else len = -ENOSYS; return len; } static int scsi_sysfs_pathinfo (struct path * pp, vector hwtable) { struct udev_device *parent; const char *attr_path = NULL; parent = pp->udev; while (parent) { const char *subsys = udev_device_get_subsystem(parent); if (subsys && !strncmp(subsys, "scsi", 4)) { attr_path = udev_device_get_sysname(parent); if (!attr_path) break; if (sscanf(attr_path, "%i:%i:%i:%i", &pp->sg_id.host_no, &pp->sg_id.channel, &pp->sg_id.scsi_id, &pp->sg_id.lun) == 4) break; } parent = udev_device_get_parent(parent); } if (!attr_path || pp->sg_id.host_no == -1) return 1; if (sysfs_get_vendor(parent, pp->vendor_id, SCSI_VENDOR_SIZE) <= 0) return 1; condlog(3, "%s: vendor = %s", pp->dev, pp->vendor_id); if (sysfs_get_model(parent, pp->product_id, SCSI_PRODUCT_SIZE) <= 0) return 1; condlog(3, "%s: product = %s", pp->dev, pp->product_id); if (sysfs_get_rev(parent, pp->rev, SCSI_REV_SIZE) < 0) return 1; condlog(3, "%s: rev = %s", pp->dev, pp->rev); /* * set the hwe configlet pointer */ pp->hwe = find_hwe(hwtable, pp->vendor_id, pp->product_id, pp->rev); /* * host / bus / target / lun */ condlog(3, "%s: h:b:t:l = %i:%i:%i:%i", pp->dev, pp->sg_id.host_no, pp->sg_id.channel, pp->sg_id.scsi_id, pp->sg_id.lun); /* * target node name */ if(sysfs_get_tgt_nodename(pp, pp->tgt_node_name)) return 1; condlog(3, "%s: tgt_node_name = %s", pp->dev, pp->tgt_node_name); return 0; } static int nvme_sysfs_pathinfo (struct path * pp, vector hwtable) { struct udev_device *parent; const char *attr_path = NULL; attr_path = udev_device_get_sysname(pp->udev); if (!attr_path) return 1; if (sscanf(attr_path, "nvme%dn%d", &pp->sg_id.host_no, &pp->sg_id.scsi_id) != 2) return 1; pp->sg_id.channel = 0; pp->sg_id.lun = 0; parent = udev_device_get_parent(pp->udev); if (!parent) return 1; snprintf(pp->vendor_id, SCSI_VENDOR_SIZE, "NVME"); snprintf(pp->product_id, SCSI_PRODUCT_SIZE, "%s", udev_device_get_sysattr_value(parent, "model")); snprintf(pp->serial, SERIAL_SIZE, "%s", udev_device_get_sysattr_value(parent, "serial")); snprintf(pp->rev, SCSI_REV_SIZE, "%s", udev_device_get_sysattr_value(parent, "firmware_rev")); condlog(3, "%s: vendor = %s", pp->dev, pp->vendor_id); condlog(3, "%s: product = %s", pp->dev, pp->product_id); condlog(3, "%s: serial = %s", pp->dev, pp->serial); condlog(3, "%s: rev = %s", pp->dev, pp->rev); pp->hwe = find_hwe(hwtable, pp->vendor_id, pp->product_id, NULL); return 0; } static int rbd_sysfs_pathinfo (struct path * pp, vector hwtable) { sprintf(pp->vendor_id, "Ceph"); sprintf(pp->product_id, "RBD"); condlog(3, "%s: vendor = %s product = %s", pp->dev, pp->vendor_id, pp->product_id); /* * set the hwe configlet pointer */ pp->hwe = find_hwe(hwtable, pp->vendor_id, pp->product_id, NULL); return 0; } static int ccw_sysfs_pathinfo (struct path * pp, vector hwtable) { struct udev_device *parent; char attr_buff[NAME_SIZE]; const char *attr_path; parent = pp->udev; while (parent) { const char *subsys = udev_device_get_subsystem(parent); if (subsys && !strncmp(subsys, "ccw", 3)) break; parent = udev_device_get_parent(parent); } if (!parent) return 1; sprintf(pp->vendor_id, "IBM"); condlog(3, "%s: vendor = %s", pp->dev, pp->vendor_id); if (sysfs_get_devtype(parent, attr_buff, FILE_NAME_SIZE) <= 0) return 1; if (!strncmp(attr_buff, "3370", 4)) { sprintf(pp->product_id,"S/390 DASD FBA"); } else if (!strncmp(attr_buff, "9336", 4)) { sprintf(pp->product_id,"S/390 DASD FBA"); } else { sprintf(pp->product_id,"S/390 DASD ECKD"); } condlog(3, "%s: product = %s", pp->dev, pp->product_id); /* * set the hwe configlet pointer */ pp->hwe = find_hwe(hwtable, pp->vendor_id, pp->product_id, NULL); /* * host / bus / target / lun */ attr_path = udev_device_get_sysname(parent); pp->sg_id.lun = 0; if (sscanf(attr_path, "%i.%i.%x", &pp->sg_id.host_no, &pp->sg_id.channel, &pp->sg_id.scsi_id) == 3) { condlog(3, "%s: h:b:t:l = %i:%i:%i:%i", pp->dev, pp->sg_id.host_no, pp->sg_id.channel, pp->sg_id.scsi_id, pp->sg_id.lun); } return 0; } static int cciss_sysfs_pathinfo (struct path * pp, vector hwtable) { const char * attr_path = NULL; struct udev_device *parent; parent = pp->udev; while (parent) { const char *subsys = udev_device_get_subsystem(parent); if (subsys && !strncmp(subsys, "cciss", 5)) { attr_path = udev_device_get_sysname(parent); if (!attr_path) break; if (sscanf(attr_path, "c%id%i", &pp->sg_id.host_no, &pp->sg_id.scsi_id) == 2) break; } parent = udev_device_get_parent(parent); } if (!attr_path || pp->sg_id.host_no == -1) return 1; if (sysfs_get_vendor(parent, pp->vendor_id, SCSI_VENDOR_SIZE) <= 0) return 1; condlog(3, "%s: vendor = %s", pp->dev, pp->vendor_id); if (sysfs_get_model(parent, pp->product_id, SCSI_PRODUCT_SIZE) <= 0) return 1; condlog(3, "%s: product = %s", pp->dev, pp->product_id); if (sysfs_get_rev(parent, pp->rev, SCSI_REV_SIZE) <= 0) return 1; condlog(3, "%s: rev = %s", pp->dev, pp->rev); /* * set the hwe configlet pointer */ pp->hwe = find_hwe(hwtable, pp->vendor_id, pp->product_id, pp->rev); /* * host / bus / target / lun */ pp->sg_id.lun = 0; pp->sg_id.channel = 0; condlog(3, "%s: h:b:t:l = %i:%i:%i:%i", pp->dev, pp->sg_id.host_no, pp->sg_id.channel, pp->sg_id.scsi_id, pp->sg_id.lun); return 0; } static int common_sysfs_pathinfo (struct path * pp) { dev_t devt; if (!pp) return 1; if (!pp->udev) { condlog(4, "%s: udev not initialised", pp->dev); return 1; } devt = udev_device_get_devnum(pp->udev); snprintf(pp->dev_t, BLK_DEV_SIZE, "%d:%d", major(devt), minor(devt)); condlog(3, "%s: dev_t = %s", pp->dev, pp->dev_t); if (sysfs_get_size(pp, &pp->size)) return 1; condlog(3, "%s: size = %llu", pp->dev, pp->size); return 0; } int path_offline (struct path * pp) { struct udev_device * parent; char buff[SCSI_STATE_SIZE]; int err; const char *subsys_type; if (pp->bus == SYSFS_BUS_SCSI) { subsys_type = "scsi"; } else if (pp->bus == SYSFS_BUS_NVME) { subsys_type = "nvme"; } else { return PATH_UP; } parent = pp->udev; while (parent) { const char *subsys = udev_device_get_subsystem(parent); if (subsys && !strncmp(subsys, subsys_type, 4)) break; parent = udev_device_get_parent(parent); } if (!parent) { condlog(1, "%s: failed to get sysfs information", pp->dev); return PATH_REMOVED; } memset(buff, 0x0, SCSI_STATE_SIZE); err = sysfs_attr_get_value(parent, "state", buff, SCSI_STATE_SIZE); if (err <= 0) { if (err == -ENXIO) return PATH_REMOVED; else return PATH_DOWN; } condlog(3, "%s: path state = %s", pp->dev, buff); if (pp->bus == SYSFS_BUS_SCSI) { if (!strncmp(buff, "offline", 7)) { pp->offline = 1; return PATH_DOWN; } pp->offline = 0; if (!strncmp(buff, "blocked", 7) || !strncmp(buff, "quiesce", 7)) return PATH_PENDING; else if (!strncmp(buff, "running", 7)) return PATH_UP; } else if (pp->bus == SYSFS_BUS_NVME) { if (!strncmp(buff, "dead", 4)) { pp->offline = 1; return PATH_DOWN; } pp->offline = 0; if (!strncmp(buff, "new", 3) || !strncmp(buff, "deleting", 8)) return PATH_PENDING; else if (!strncmp(buff, "live", 4)) return PATH_UP; } return PATH_DOWN; } int sysfs_pathinfo(struct path * pp, vector hwtable) { if (common_sysfs_pathinfo(pp)) return 1; pp->bus = SYSFS_BUS_UNDEF; if (!strncmp(pp->dev,"cciss",5)) pp->bus = SYSFS_BUS_CCISS; if (!strncmp(pp->dev,"dasd", 4)) pp->bus = SYSFS_BUS_CCW; if (!strncmp(pp->dev,"sd", 2)) pp->bus = SYSFS_BUS_SCSI; if (!strncmp(pp->dev,"rbd", 3)) pp->bus = SYSFS_BUS_RBD; if (!strncmp(pp->dev,"nvme", 4)) pp->bus = SYSFS_BUS_NVME; if (pp->bus == SYSFS_BUS_UNDEF) return 0; else if (pp->bus == SYSFS_BUS_SCSI) { if (scsi_sysfs_pathinfo(pp, hwtable)) return 1; } else if (pp->bus == SYSFS_BUS_CCW) { if (ccw_sysfs_pathinfo(pp, hwtable)) return 1; } else if (pp->bus == SYSFS_BUS_CCISS) { if (cciss_sysfs_pathinfo(pp, hwtable)) return 1; } else if (pp->bus == SYSFS_BUS_RBD) { if (rbd_sysfs_pathinfo(pp, hwtable)) return 1; } else if (pp->bus == SYSFS_BUS_NVME) { if (nvme_sysfs_pathinfo(pp, hwtable)) return 1; } return 0; } static int scsi_ioctl_pathinfo (struct path * pp, struct config *conf, int mask) { struct udev_device *parent; const char *attr_path = NULL; if (pp->tpgs == TPGS_UNDEF) detect_alua(pp, conf); if (!(mask & DI_SERIAL)) return 0; parent = pp->udev; while (parent) { const char *subsys = udev_device_get_subsystem(parent); if (subsys && !strncmp(subsys, "scsi", 4)) { attr_path = udev_device_get_sysname(parent); if (!attr_path) break; if (sscanf(attr_path, "%i:%i:%i:%i", &pp->sg_id.host_no, &pp->sg_id.channel, &pp->sg_id.scsi_id, &pp->sg_id.lun) == 4) break; } parent = udev_device_get_parent(parent); } if (!attr_path || pp->sg_id.host_no == -1) return 0; if (get_vpd_sysfs(parent, 0x80, pp->serial, SERIAL_SIZE) <= 0) { if (get_serial(pp->serial, SERIAL_SIZE, pp->fd)) { condlog(2, "%s: fail to get serial", pp->dev); return 0; } } condlog(3, "%s: serial = %s", pp->dev, pp->serial); return 0; } static int cciss_ioctl_pathinfo (struct path * pp, int mask) { if (mask & DI_SERIAL) { get_serial(pp->serial, SERIAL_SIZE, pp->fd); condlog(3, "%s: serial = %s", pp->dev, pp->serial); } return 0; } int get_state (struct path * pp, struct config *conf, int daemon, int oldstate) { struct checker * c = &pp->checker; int state; condlog(3, "%s: get_state", pp->dev); if (!checker_selected(c)) { if (daemon) { if (pathinfo(pp, conf, DI_SYSFS) != PATHINFO_OK) { condlog(3, "%s: couldn't get sysfs pathinfo", pp->dev); return PATH_UNCHECKED; } } select_detect_checker(conf, pp); select_checker(conf, pp); if (!checker_selected(c)) { condlog(3, "%s: No checker selected", pp->dev); return PATH_UNCHECKED; } checker_set_fd(c, pp->fd); if (checker_init(c, pp->mpp?&pp->mpp->mpcontext:NULL)) { checker_clear(c); condlog(3, "%s: checker init failed", pp->dev); return PATH_UNCHECKED; } } checker_clear_message(c); if (daemon) { if (conf->force_sync == 0) checker_set_async(c); else checker_set_sync(c); } if (!conf->checker_timeout && sysfs_get_timeout(pp, &(c->timeout)) <= 0) c->timeout = DEF_TIMEOUT; state = checker_check(c, oldstate); condlog(3, "%s: %s state = %s", pp->dev, checker_name(c), checker_state_name(state)); if (state != PATH_UP && state != PATH_GHOST && strlen(checker_message(c))) condlog(3, "%s: checker msg is \"%s\"", pp->dev, checker_message(c)); return state; } static int get_prio (struct path * pp) { struct prio * p; struct config *conf; if (!pp) return 0; p = &pp->prio; if (!prio_selected(p)) { conf = get_multipath_config(); select_detect_prio(conf, pp); select_prio(conf, pp); put_multipath_config(conf); if (!prio_selected(p)) { condlog(3, "%s: no prio selected", pp->dev); pp->priority = PRIO_UNDEF; return 1; } } conf = get_multipath_config(); pp->priority = prio_getprio(p, pp, conf->checker_timeout); put_multipath_config(conf); if (pp->priority < 0) { condlog(3, "%s: %s prio error", pp->dev, prio_name(p)); pp->priority = PRIO_UNDEF; return 1; } condlog(3, "%s: %s prio = %u", pp->dev, prio_name(p), pp->priority); return 0; } /* * Mangle string of length *len starting at start * by removing character sequence "00" (hex for a 0 byte), * starting at end, backwards. * Changes the value of *len if characters were removed. * Returns a pointer to the position where "end" was moved to. */ static char *skip_zeroes_backward(char* start, int *len, char *end) { char *p = end; while (p >= start + 2 && *(p - 1) == '0' && *(p - 2) == '0') p -= 2; if (p == end) return p; memmove(p, end, start + *len + 1 - end); *len -= end - p; return p; } /* * Fix for NVME wwids looking like this: * nvme.0000-3163653363666438366239656630386200-4c696e75780000000000000000000000000000000000000000000000000000000000000000000000-00000002 * which are encountered in some combinations of Linux NVME host and target. * The '00' are hex-encoded 0-bytes which are forbidden in the serial (SN) * and model (MN) fields. Discard them. * If a WWID of the above type is found, sets pp->wwid and returns a value > 0. * Otherwise, returns 0. */ static int fix_broken_nvme_wwid(struct path *pp, const char *value, int size) { static const char _nvme[] = "nvme."; int len, i; char mangled[256]; char *p; len = strlen(value); if (len >= sizeof(mangled)) return 0; /* Check that value starts with "nvme.%04x-" */ if (memcmp(value, _nvme, sizeof(_nvme) - 1) || value[9] != '-') return 0; for (i = 5; i < 9; i++) if (!isxdigit(value[i])) return 0; memcpy(mangled, value, len + 1); /* search end of "model" part and strip trailing '00' */ p = memrchr(mangled, '-', len); if (p == NULL) return 0; p = skip_zeroes_backward(mangled, &len, p); /* search end of "serial" part */ p = memrchr(mangled, '-', p - mangled); if (p == NULL || memrchr(mangled, '-', p - mangled) != mangled + 9) /* We expect exactly 3 '-' in the value */ return 0; p = skip_zeroes_backward(mangled, &len, p); if (len >= size) return 0; memcpy(pp->wwid, mangled, len + 1); condlog(2, "%s: over-long WWID shortened to %s", pp->dev, pp->wwid); return len; } static int get_udev_uid(struct path * pp, char *uid_attribute, struct udev_device *udev) { ssize_t len; const char *value; value = udev_device_get_property_value(udev, uid_attribute); if (!value || strlen(value) == 0) value = getenv(uid_attribute); if (value && strlen(value)) { len = strlcpy(pp->wwid, value, WWID_SIZE); if (len >= WWID_SIZE) { len = fix_broken_nvme_wwid(pp, value, WWID_SIZE); if (len > 0) return len; condlog(0, "%s: wwid overflow", pp->dev); len = WWID_SIZE; } } else { condlog(3, "%s: no %s attribute", pp->dev, uid_attribute); len = -EINVAL; } return len; } static int get_rbd_uid(struct path * pp) { struct udev_device *rbd_bus_dev; int ret, rbd_bus_id; const char *pool, *image, *snap; char sysfs_path[PATH_SIZE]; uint64_t snap_id, max_snap_id = -3; ret = sscanf(pp->dev, "rbd%d", &rbd_bus_id); if (ret != 1) return -EINVAL; snprintf(sysfs_path, sizeof(sysfs_path), "/sys/bus/rbd/devices/%d", rbd_bus_id); rbd_bus_dev = udev_device_new_from_syspath(udev, sysfs_path); if (!rbd_bus_dev) return -ENODEV; ret = -EINVAL; pool = udev_device_get_sysattr_value(rbd_bus_dev, "pool_id"); if (!pool) goto free_dev; image = udev_device_get_sysattr_value(rbd_bus_dev, "image_id"); if (!image) goto free_dev; snap = udev_device_get_sysattr_value(rbd_bus_dev, "snap_id"); if (!snap) goto free_dev; snap_id = strtoull(snap, NULL, 19); if (snap_id >= max_snap_id) ret = snprintf(pp->wwid, WWID_SIZE, "%s-%s", pool, image); else ret = snprintf(pp->wwid, WWID_SIZE, "%s-%s-%s", pool, image, snap); if (ret >= WWID_SIZE) { condlog(0, "%s: wwid overflow", pp->dev); ret = -EOVERFLOW; } free_dev: udev_device_unref(rbd_bus_dev); return ret; } static int get_vpd_uid(struct path * pp) { struct udev_device *parent = pp->udev; while (parent) { const char *subsys = udev_device_get_subsystem(parent); if (subsys && !strncmp(subsys, "scsi", 4)) break; parent = udev_device_get_parent(parent); } return get_vpd_sysfs(parent, 0x83, pp->wwid, WWID_SIZE); } int get_uid (struct path * pp, int path_state, struct udev_device *udev) { char *c; const char *origin = "unknown"; ssize_t len = 0; struct config *conf; if (!pp->uid_attribute && !pp->getuid) { conf = get_multipath_config(); select_getuid(conf, pp); put_multipath_config(conf); } if (!udev) { condlog(1, "%s: no udev information", pp->dev); return 1; } memset(pp->wwid, 0, WWID_SIZE); if (pp->getuid) { char buff[CALLOUT_MAX_SIZE]; /* Use 'getuid' callout, deprecated */ condlog(1, "%s: using deprecated getuid callout", pp->dev); if (path_state != PATH_UP) { condlog(3, "%s: path inaccessible", pp->dev); len = -EWOULDBLOCK; } else if (apply_format(pp->getuid, &buff[0], pp)) { condlog(0, "error formatting uid callout command"); len = -EINVAL; } else if (execute_program(buff, pp->wwid, WWID_SIZE)) { condlog(3, "error calling out %s", buff); len = -EIO; } else len = strlen(pp->wwid); origin = "callout"; } else if (pp->bus == SYSFS_BUS_RBD) { len = get_rbd_uid(pp); origin = "sysfs"; } else { int retrigger; if (pp->uid_attribute) { len = get_udev_uid(pp, pp->uid_attribute, udev); origin = "udev"; if (len <= 0) condlog(1, "%s: failed to get udev uid: %s", pp->dev, strerror(-len)); } else { len = get_vpd_uid(pp); origin = "sysfs"; } conf = get_multipath_config(); retrigger = conf->retrigger_tries; put_multipath_config(conf); if (len <= 0 && pp->retriggers >= retrigger && !strcmp(pp->uid_attribute, DEFAULT_UID_ATTRIBUTE)) { len = get_vpd_uid(pp); origin = "sysfs"; pp->uid_attribute = NULL; if (len < 0 && path_state == PATH_UP) { condlog(1, "%s: failed to get sysfs uid: %s", pp->dev, strerror(-len)); len = get_vpd_sgio(pp->fd, 0x83, pp->wwid, WWID_SIZE); origin = "sgio"; } } } if ( len < 0 ) { condlog(1, "%s: failed to get %s uid: %s", pp->dev, origin, strerror(-len)); memset(pp->wwid, 0x0, WWID_SIZE); } else { /* Strip any trailing blanks */ c = strchr(pp->wwid, '\0'); c--; while (c && c >= pp->wwid && *c == ' ') { *c = '\0'; c--; } } condlog(3, "%s: uid = %s (%s)", pp->dev, *pp->wwid == '\0' ? "" : pp->wwid, origin); return 0; } int pathinfo(struct path *pp, struct config *conf, int mask) { int path_state; if (!pp || !conf) return PATHINFO_FAILED; /* * For behavior backward-compatibility with multipathd, * the blacklisting by filter_property|devnode() is not * limited by DI_BLACKLIST and occurs before this debug * message with the mask value. */ if (pp->udev && filter_property(conf, pp->udev) > 0) return PATHINFO_SKIPPED; if (filter_devnode(conf->blist_devnode, conf->elist_devnode, pp->dev) > 0) return PATHINFO_SKIPPED; condlog(3, "%s: mask = 0x%x", pp->dev, mask); /* * Sanity check: we need the device number to * avoid inconsistent information in * find_path_by_dev()/find_path_by_devt() */ if (!strlen(pp->dev_t) && !(mask & DI_SYSFS)) { condlog(1, "%s: empty device number", pp->dev); mask |= DI_SYSFS; } /* * fetch info available in sysfs */ if (mask & DI_SYSFS && sysfs_pathinfo(pp, conf->hwtable)) return PATHINFO_FAILED; if (mask & DI_BLACKLIST && mask & DI_SYSFS) { if (filter_device(conf->blist_device, conf->elist_device, pp->vendor_id, pp->product_id) > 0) { return PATHINFO_SKIPPED; } } path_state = path_offline(pp); if (path_state == PATH_REMOVED) goto blank; else if (mask & DI_NOIO) { /* * Avoid any IO on the device itself. * Behave like DI_CHECKER in the "path unavailable" case. */ pp->chkrstate = pp->state = path_state; return PATHINFO_OK; } /* * fetch info not available through sysfs */ if (pp->fd < 0) pp->fd = open(udev_device_get_devnode(pp->udev), O_RDONLY); if (pp->fd < 0) { condlog(4, "Couldn't open node for %s: %s", pp->dev, strerror(errno)); goto blank; } if (mask & DI_SERIAL) get_geometry(pp); if (path_state == PATH_UP && pp->bus == SYSFS_BUS_SCSI && scsi_ioctl_pathinfo(pp, conf, mask)) goto blank; if (pp->bus == SYSFS_BUS_CCISS && cciss_ioctl_pathinfo(pp, mask)) goto blank; if (mask & DI_CHECKER) { if (path_state == PATH_UP) { pp->chkrstate = pp->state = get_state(pp, conf, 0, path_state); if (pp->state == PATH_UNCHECKED || pp->state == PATH_WILD) goto blank; if (pp->state == PATH_TIMEOUT) pp->state = PATH_DOWN; if (pp->state == PATH_UP && !pp->size) { condlog(3, "%s: device size is 0, " "path unusable", pp->dev); pp->state = PATH_GHOST; } } else { condlog(3, "%s: path inaccessible", pp->dev); pp->chkrstate = pp->state = path_state; if (path_state == PATH_PENDING || path_state == PATH_DOWN) pp->priority = 0; } } if ((mask & DI_WWID) && !strlen(pp->wwid)) { get_uid(pp, path_state, pp->udev); if (!strlen(pp->wwid)) { pp->initialized = INIT_MISSING_UDEV; pp->tick = conf->retrigger_delay; return PATHINFO_OK; } else pp->tick = 1; } if (mask & DI_BLACKLIST && mask & DI_WWID) { if (filter_wwid(conf->blist_wwid, conf->elist_wwid, pp->wwid, pp->dev) > 0) { return PATHINFO_SKIPPED; } } /* * Retrieve path priority, even for PATH_DOWN paths if it has never * been successfully obtained before. */ if ((mask & DI_PRIO) && path_state == PATH_UP && strlen(pp->wwid)) { if (pp->state != PATH_DOWN || pp->priority == PRIO_UNDEF) { get_prio(pp); } } if ((mask & DI_ALL) == DI_ALL) pp->initialized = INIT_OK; return PATHINFO_OK; blank: /* * Recoverable error, for example faulty or offline path */ memset(pp->wwid, 0, WWID_SIZE); pp->chkrstate = pp->state = PATH_DOWN; pp->initialized = INIT_FAILED; return PATHINFO_OK; } multipath-tools-0.7.4/libmultipath/discovery.h000066400000000000000000000042501320314174000215410ustar00rootroot00000000000000#ifndef DISCOVERY_H #define DISCOVERY_H #define SYSFS_PATH_SIZE 255 #define INQUIRY_CMDLEN 6 #define INQUIRY_CMD 0x12 #define SENSE_BUFF_LEN 32 #define RECOVERED_ERROR 0x01 #define MX_ALLOC_LEN 255 #define TUR_CMD_LEN 6 #ifndef BLKGETSIZE #define BLKGETSIZE _IO(0x12,96) #endif #ifndef DEF_TIMEOUT #define DEF_TIMEOUT 30 #endif /* * excerpt from sg_err.h */ #define SCSI_CHECK_CONDITION 0x2 #define SCSI_COMMAND_TERMINATED 0x22 #define SG_ERR_DRIVER_SENSE 0x08 #define PATHINFO_OK 0 #define PATHINFO_FAILED 1 #define PATHINFO_SKIPPED 2 struct config; int path_discovery (vector pathvec, int flag); int do_tur (char *); int path_offline (struct path *); int get_state (struct path * pp, struct config * conf, int daemon, int state); int get_vpd_sgio (int fd, int pg, char * str, int maxlen); int pathinfo (struct path * pp, struct config * conf, int mask); int alloc_path_with_pathinfo (struct config *conf, struct udev_device *udevice, char *wwid, int flag, struct path **pp_ptr); int store_pathinfo (vector pathvec, struct config *conf, struct udev_device *udevice, int flag, struct path **pp_ptr); int sysfs_set_scsi_tmo (struct multipath *mpp, int checkint); int sysfs_get_timeout(struct path *pp, unsigned int *timeout); int sysfs_get_host_pci_name(struct path *pp, char *pci_name); int sysfs_get_iscsi_ip_address(struct path *pp, char *ip_address); ssize_t sysfs_get_vpd (struct udev_device * udev, int pg, unsigned char * buff, size_t len); int sysfs_get_asymmetric_access_state(struct path *pp, char *buff, int buflen); int get_uid(struct path * pp, int path_state, struct udev_device *udev); /* * discovery bitmask */ enum discovery_mode { __DI_SYSFS, __DI_SERIAL, __DI_CHECKER, __DI_PRIO, __DI_WWID, __DI_BLACKLIST, __DI_NOIO, }; #define DI_SYSFS (1 << __DI_SYSFS) #define DI_SERIAL (1 << __DI_SERIAL) #define DI_CHECKER (1 << __DI_CHECKER) #define DI_PRIO (1 << __DI_PRIO) #define DI_WWID (1 << __DI_WWID) #define DI_BLACKLIST (1 << __DI_BLACKLIST) #define DI_NOIO (1 << __DI_NOIO) /* Avoid IO on the device */ #define DI_ALL (DI_SYSFS | DI_SERIAL | DI_CHECKER | DI_PRIO | \ DI_WWID) #endif /* DISCOVERY_H */ multipath-tools-0.7.4/libmultipath/dmparser.c000066400000000000000000000246141320314174000213500ustar00rootroot00000000000000/* * Copyright (c) 2004, 2005 Christophe Varoqui * Copyright (c) 2005 Stefan Bader, IBM * Copyright (c) 2005 Edward Goggin, EMC */ #include #include #include #include "checkers.h" #include "vector.h" #include "memory.h" #include "structs.h" #include "util.h" #include "debug.h" #define WORD_SIZE 64 static int merge_words (char ** dst, char * word, int space) { char * p = *dst; int len; len = strlen(*dst) + strlen(word) + space; *dst = REALLOC(*dst, len + 1); if (!*dst) { free(p); return 1; } p = *dst; while (*p != '\0') p++; while (space) { *p = ' '; p++; space--; } strncpy(p, word, strlen(word) + 1); return 0; } #define APPEND(p, end, args...) \ ({ \ int ret; \ \ ret = snprintf(p, end - p, ##args); \ if (ret < 0) { \ condlog(0, "%s: conversion error", mp->alias); \ goto err; \ } \ p += ret; \ if (p >= end) { \ condlog(0, "%s: params too small", mp->alias); \ goto err; \ } \ }) /* * Transforms the path group vector into a proper device map string */ int assemble_map (struct multipath * mp, char * params, int len) { int i, j; int minio; int nr_priority_groups, initial_pg_nr; char * p, * f; const char *const end = params + len; char no_path_retry[] = "queue_if_no_path"; char retain_hwhandler[] = "retain_attached_hw_handler"; struct pathgroup * pgp; struct path * pp; minio = mp->minio; p = params; nr_priority_groups = VECTOR_SIZE(mp->pg); initial_pg_nr = (nr_priority_groups ? mp->bestpg : 0); f = STRDUP(mp->features); /* * We have to set 'queue_if_no_path' here even * to avoid path failures during map reload. */ if (mp->no_path_retry == NO_PATH_RETRY_FAIL) { /* remove queue_if_no_path settings */ condlog(3, "%s: remove queue_if_no_path from '%s'", mp->alias, mp->features); remove_feature(&f, no_path_retry); } else if (mp->no_path_retry != NO_PATH_RETRY_UNDEF) { add_feature(&f, no_path_retry); } if (mp->retain_hwhandler == RETAIN_HWHANDLER_ON && get_linux_version_code() < KERNEL_VERSION(4, 3, 0)) add_feature(&f, retain_hwhandler); APPEND(p, end, "%s %s %i %i", f, mp->hwhandler, nr_priority_groups, initial_pg_nr); vector_foreach_slot (mp->pg, pgp, i) { pgp = VECTOR_SLOT(mp->pg, i); APPEND(p, end, " %s %i 1", mp->selector, VECTOR_SIZE(pgp->paths)); vector_foreach_slot (pgp->paths, pp, j) { int tmp_minio = minio; if (mp->rr_weight == RR_WEIGHT_PRIO && pp->priority > 0) tmp_minio = minio * pp->priority; if (!strlen(pp->dev_t) ) { condlog(0, "dev_t not set for '%s'", pp->dev); goto err; } APPEND(p, end, " %s %d", pp->dev_t, tmp_minio); } } APPEND(p, end, "\n"); FREE(f); condlog(3, "%s: assembled map [%s]", mp->alias, params); return 0; err: FREE(f); return 1; } #undef APPEND int disassemble_map(vector pathvec, char *params, struct multipath *mpp, int is_daemon) { char * word; char * p; int i, j, k; int num_features = 0; int num_hwhandler = 0; int num_pg = 0; int num_pg_args = 0; int num_paths = 0; int num_paths_args = 0; int def_minio = 0; int no_path_retry = NO_PATH_RETRY_UNDEF; struct path * pp; struct pathgroup * pgp; p = params; condlog(3, "%s: disassemble map [%s]", mpp->alias, params); /* * features */ p += get_word(p, &mpp->features); if (!mpp->features) return 1; num_features = atoi(mpp->features); no_path_retry = mpp->no_path_retry; mpp->no_path_retry = NO_PATH_RETRY_UNDEF; for (i = 0; i < num_features; i++) { p += get_word(p, &word); if (!word) return 1; if (merge_words(&mpp->features, word, 1)) { FREE(word); return 1; } setup_feature(mpp, word); FREE(word); } /* * Reset no_path_retry. * - if not set from features * - if queue_if_no_path is set from features but * no_path_retry > 0 is selected. */ if ((mpp->no_path_retry == NO_PATH_RETRY_UNDEF || mpp->no_path_retry == NO_PATH_RETRY_QUEUE) && mpp->no_path_retry != no_path_retry) mpp->no_path_retry = no_path_retry; /* * hwhandler */ p += get_word(p, &mpp->hwhandler); if (!mpp->hwhandler) return 1; num_hwhandler = atoi(mpp->hwhandler); for (i = 0; i < num_hwhandler; i++) { p += get_word(p, &word); if (!word) return 1; if (merge_words(&mpp->hwhandler, word, 1)) { FREE(word); return 1; } FREE(word); } /* * nb of path groups */ p += get_word(p, &word); if (!word) return 1; num_pg = atoi(word); FREE(word); if (num_pg > 0) { if (!mpp->pg) { mpp->pg = vector_alloc(); if (!mpp->pg) return 1; } } else { free_pgvec(mpp->pg, KEEP_PATHS); mpp->pg = NULL; } /* * first pg to try */ p += get_word(p, &word); if (!word) goto out; mpp->nextpg = atoi(word); FREE(word); for (i = 0; i < num_pg; i++) { /* * selector */ if (!mpp->selector) { p += get_word(p, &mpp->selector); if (!mpp->selector) goto out; /* * selector args */ p += get_word(p, &word); if (!word) goto out; num_pg_args = atoi(word); if (merge_words(&mpp->selector, word, 1)) { goto out1; } FREE(word); } else { p += get_word(p, NULL); p += get_word(p, NULL); } for (j = 0; j < num_pg_args; j++) p += get_word(p, NULL); /* * paths */ pgp = alloc_pathgroup(); if (!pgp) goto out; if (store_pathgroup(mpp->pg, pgp)) goto out; p += get_word(p, &word); if (!word) goto out; num_paths = atoi(word); FREE(word); p += get_word(p, &word); if (!word) goto out; num_paths_args = atoi(word); FREE(word); for (j = 0; j < num_paths; j++) { char devname[FILE_NAME_SIZE]; pp = NULL; p += get_word(p, &word); if (!word) goto out; if (devt2devname(devname, FILE_NAME_SIZE, word)) { condlog(2, "%s: cannot find block device", word); devname[0] = '\0'; } if (pathvec) { if (strlen(devname)) pp = find_path_by_dev(pathvec, devname); else pp = find_path_by_devt(pathvec, word); } if (!pp) { pp = alloc_path(); if (!pp) goto out1; strncpy(pp->dev_t, word, BLK_DEV_SIZE - 1); strncpy(pp->dev, devname, FILE_NAME_SIZE - 1); if (strlen(mpp->wwid)) { strncpy(pp->wwid, mpp->wwid, WWID_SIZE - 1); } /* Only call this in multipath client mode */ if (!is_daemon && store_path(pathvec, pp)) goto out1; } else { if (!strlen(pp->wwid) && strlen(mpp->wwid)) strncpy(pp->wwid, mpp->wwid, WWID_SIZE - 1); } FREE(word); if (store_path(pgp->paths, pp)) goto out; /* * Update wwid for multipaths which are not setup * in the get_dm_mpvec() code path */ if (!strlen(mpp->wwid)) strncpy(mpp->wwid, pp->wwid, WWID_SIZE - 1); /* * Update wwid for paths which may not have been * active at the time the getuid callout was run */ else if (!strlen(pp->wwid)) strncpy(pp->wwid, mpp->wwid, WWID_SIZE - 1); /* * Do not allow in-use patch to change wwid */ else if (strcmp(pp->wwid, mpp->wwid) != 0) { condlog(0, "%s: path wwid appears to have changed. Using map wwid.\n", pp->dev_t); strncpy(pp->wwid, mpp->wwid, WWID_SIZE); } pgp->id ^= (long)pp; pp->pgindex = i + 1; for (k = 0; k < num_paths_args; k++) if (k == 0) { p += get_word(p, &word); def_minio = atoi(word); FREE(word); if (!strncmp(mpp->selector, "round-robin", 11)) { if (mpp->rr_weight == RR_WEIGHT_PRIO && pp->priority > 0) def_minio /= pp->priority; } if (def_minio != mpp->minio) mpp->minio = def_minio; } else p += get_word(p, NULL); } } return 0; out1: FREE(word); out: free_pgvec(mpp->pg, KEEP_PATHS); mpp->pg = NULL; return 1; } int disassemble_status(char *params, struct multipath *mpp) { char * word; char * p; int i, j, k; int num_feature_args; int num_hwhandler_args; int num_pg; int num_pg_args; int num_paths; int def_minio = 0; struct path * pp; struct pathgroup * pgp; p = params; condlog(3, "%s: disassemble status [%s]", mpp->alias, params); /* * features */ p += get_word(p, &word); if (!word) return 1; num_feature_args = atoi(word); FREE(word); for (i = 0; i < num_feature_args; i++) { if (i == 1) { p += get_word(p, &word); if (!word) return 1; mpp->queuedio = atoi(word); FREE(word); continue; } /* unknown */ p += get_word(p, NULL); } /* * hwhandler */ p += get_word(p, &word); if (!word) return 1; num_hwhandler_args = atoi(word); FREE(word); for (i = 0; i < num_hwhandler_args; i++) p += get_word(p, NULL); /* * nb of path groups */ p += get_word(p, &word); if (!word) return 1; num_pg = atoi(word); FREE(word); if (num_pg == 0) return 0; /* * next pg to try */ p += get_word(p, NULL); if (VECTOR_SIZE(mpp->pg) < num_pg) return 1; for (i = 0; i < num_pg; i++) { pgp = VECTOR_SLOT(mpp->pg, i); /* * PG status */ p += get_word(p, &word); if (!word) return 1; switch (*word) { case 'D': pgp->status = PGSTATE_DISABLED; break; case 'A': pgp->status = PGSTATE_ACTIVE; break; case 'E': pgp->status = PGSTATE_ENABLED; break; default: pgp->status = PGSTATE_UNDEF; break; } FREE(word); /* * PG Status (discarded, would be '0' anyway) */ p += get_word(p, NULL); p += get_word(p, &word); if (!word) return 1; num_paths = atoi(word); FREE(word); p += get_word(p, &word); if (!word) return 1; num_pg_args = atoi(word); FREE(word); if (VECTOR_SIZE(pgp->paths) < num_paths) return 1; for (j = 0; j < num_paths; j++) { pp = VECTOR_SLOT(pgp->paths, j); /* * path */ p += get_word(p, NULL); /* * path status */ p += get_word(p, &word); if (!word) return 1; switch (*word) { case 'F': pp->dmstate = PSTATE_FAILED; break; case 'A': pp->dmstate = PSTATE_ACTIVE; break; default: break; } FREE(word); /* * fail count */ p += get_word(p, &word); if (!word) return 1; pp->failcount = atoi(word); FREE(word); /* * selector args */ for (k = 0; k < num_pg_args; k++) { if (!strncmp(mpp->selector, "least-pending", 13)) { p += get_word(p, &word); if (sscanf(word,"%d:*d", &def_minio) == 1 && def_minio != mpp->minio) mpp->minio = def_minio; } else p += get_word(p, NULL); } } } return 0; } multipath-tools-0.7.4/libmultipath/dmparser.h000066400000000000000000000002501320314174000213430ustar00rootroot00000000000000int assemble_map (struct multipath *, char *, int); int disassemble_map (vector, char *, struct multipath *, int); int disassemble_status (char *, struct multipath *); multipath-tools-0.7.4/libmultipath/file.c000066400000000000000000000075631320314174000204560ustar00rootroot00000000000000/* * Copyright (c) 2005 Christophe Varoqui * Copyright (c) 2005 Benjamin Marzinski, Redhat */ #include #include #include #include #include #include #include #include #include #include #include "file.h" #include "debug.h" #include "uxsock.h" /* * significant parts of this file were taken from iscsi-bindings.c of the * linux-iscsi project. * Copyright (C) 2002 Cisco Systems, Inc. * * 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. * * See the file COPYING included with this distribution for more details. */ static int ensure_directories_exist(char *str, mode_t dir_mode) { char *pathname; char *end; int err; pathname = strdup(str); if (!pathname){ condlog(0, "Cannot copy file pathname %s : %s", str, strerror(errno)); return -1; } end = pathname; /* skip leading slashes */ while (end && *end && (*end == '/')) end++; while ((end = strchr(end, '/'))) { /* if there is another slash, make the dir. */ *end = '\0'; err = mkdir(pathname, dir_mode); if (err && errno != EEXIST) { condlog(0, "Cannot make directory [%s] : %s", pathname, strerror(errno)); free(pathname); return -1; } if (!err) condlog(3, "Created dir [%s]", pathname); *end = '/'; end++; } free(pathname); return 0; } static void sigalrm(int sig) { /* do nothing */ } static int lock_file(int fd, char *file_name) { struct sigaction act, oldact; sigset_t set, oldset; struct flock lock; int err; memset(&lock, 0, sizeof(lock)); lock.l_type = F_WRLCK; lock.l_whence = SEEK_SET; act.sa_handler = sigalrm; sigemptyset(&act.sa_mask); act.sa_flags = 0; sigemptyset(&set); sigaddset(&set, SIGALRM); sigaction(SIGALRM, &act, &oldact); pthread_sigmask(SIG_UNBLOCK, &set, &oldset); alarm(FILE_TIMEOUT); err = fcntl(fd, F_SETLKW, &lock); alarm(0); if (err) { if (errno != EINTR) condlog(0, "Cannot lock %s : %s", file_name, strerror(errno)); else condlog(0, "%s is locked. Giving up.", file_name); } pthread_sigmask(SIG_SETMASK, &oldset, NULL); sigaction(SIGALRM, &oldact, NULL); return err; } int open_file(char *file, int *can_write, char *header) { int fd; struct stat s; if (ensure_directories_exist(file, 0700)) return -1; *can_write = 1; fd = open(file, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); if (fd < 0) { if (errno == EROFS) { *can_write = 0; condlog(3, "Cannot open file [%s] read/write. " " trying readonly", file); fd = open(file, O_RDONLY); if (fd < 0) { condlog(0, "Cannot open file [%s] " "readonly : %s", file, strerror(errno)); return -1; } } else { condlog(0, "Cannot open file [%s] : %s", file, strerror(errno)); return -1; } } if (*can_write && lock_file(fd, file) < 0) goto fail; memset(&s, 0, sizeof(s)); if (fstat(fd, &s) < 0){ condlog(0, "Cannot stat file %s : %s", file, strerror(errno)); goto fail; } if (s.st_size == 0) { if (*can_write == 0) goto fail; /* If file is empty, write the header */ size_t len = strlen(header); if (write(fd, header, len) != len) { condlog(0, "Cannot write header to file %s : %s", file, strerror(errno)); /* cleanup partially written header */ if (ftruncate(fd, 0)) condlog(0, "Cannot truncate header : %s", strerror(errno)); goto fail; } fsync(fd); condlog(3, "Initialized new file [%s]", file); } return fd; fail: close(fd); return -1; } multipath-tools-0.7.4/libmultipath/file.h000066400000000000000000000003011320314174000204420ustar00rootroot00000000000000/* * Copyright (c) 2010 Benjamin Marzinski, Redhat */ #ifndef _FILE_H #define _FILE_H #define FILE_TIMEOUT 30 int open_file(char *file, int *can_write, char *header); #endif /* _FILE_H */ multipath-tools-0.7.4/libmultipath/hwtable.c000066400000000000000000000677741320314174000211770ustar00rootroot00000000000000#include #include "checkers.h" #include "vector.h" #include "defaults.h" #include "structs.h" #include "config.h" #include "pgpolicies.h" #include "prio.h" /* * Tuning suggestions on these parameters should go to * dm-devel@redhat.com (subscribers-only, see README) * * You are welcome to claim maintainership over a controller * family. Please mail the currently enlisted maintainer and * the upstream package maintainer. * * Please, use the TEMPLATE below to add new hardware. * * WARNING: * * Devices with a proprietary handler must also be included in * the kernel side. Currently at drivers/scsi/scsi_dh.c * * Moreover, if a device needs a special treatment by the SCSI * subsystem it should be included in drivers/scsi/scsi_devinfo.c */ #if 0 /* * Copy this TEMPLATE to add new hardware. * * Keep only mandatory(.vendor and .product) and modified attributes. * Attributes with default values must be removed. * .vendor, .product, .revision and .bl_product are POSIX Extended regex. * * COMPANY_NAME * * Maintainer : XXX * Mail : XXX */ { /* If product-ID is different from marketing name add a comment */ .vendor = "VENDOR", .product = "PRODUCT", .revision = "REVISION", .bl_product = "BL_PRODUCT", .pgpolicy = FAILOVER, .uid_attribute = "ID_SERIAL", .selector = "service-time 0", .checker_name = TUR, .alias_prefix = "mpath", .features = "0", .hwhandler = "0", .prio_name = PRIO_CONST, .prio_args = "", .pgfailback = -FAILBACK_MANUAL, .rr_weight = RR_WEIGHT_NONE, .no_path_retry = NO_PATH_RETRY_UNDEF, .minio = 1000, .minio_rq = 1, .flush_on_last_del = FLUSH_DISABLED, .user_friendly_names = USER_FRIENDLY_NAMES_OFF, .fast_io_fail = 5, .dev_loss = 600, .retain_hwhandler = RETAIN_HWHANDLER_ON, .detect_prio = DETECT_PRIO_ON, .detect_checker = DETECT_CHECKER_ON, .deferred_remove = DEFERRED_REMOVE_OFF, .delay_watch_checks = DELAY_CHECKS_OFF, .delay_wait_checks = DELAY_CHECKS_OFF, .skip_kpartx = SKIP_KPARTX_OFF, .max_sectors_kb = MAX_SECTORS_KB_UNDEF, }, #endif static struct hwentry default_hw[] = { /* * Apple * * Maintainer : Shyam Sundar * Mail : g.shyamsundar@yahoo.co.in */ { .vendor = "APPLE", .product = "Xserve RAID", .pgpolicy = MULTIBUS, }, /* * HPE */ { .vendor = "3PARdata", .product = "VV", .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .hwhandler = "1 alua", .prio_name = PRIO_ALUA, .no_path_retry = 18, .fast_io_fail = 10, .dev_loss = MAX_DEV_LOSS_TMO, }, { /* RA8000 / ESA12000 */ .vendor = "DEC", .product = "HSG80", .no_path_retry = NO_PATH_RETRY_QUEUE, .hwhandler = "1 hp_sw", .pgpolicy = GROUP_BY_PRIO, .checker_name = HP_SW, .prio_name = PRIO_HP_SW, }, { /* VIRTUAL ARRAY 7400 */ .vendor = "HP", .product = "A6189A", .pgpolicy = MULTIBUS, .no_path_retry = 12, }, { /* MSA 1000/1500 and EVA 3000/5000, with old firmware */ .vendor = "(COMPAQ|HP)", .product = "(MSA|HSV)1[01]0", .hwhandler = "1 hp_sw", .pgpolicy = GROUP_BY_PRIO, .no_path_retry = 12, .checker_name = HP_SW, .prio_name = PRIO_HP_SW, }, { /* MSA 1000/1500 with new firmware */ .vendor = "(COMPAQ|HP)", .product = "MSA VOLUME", .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .no_path_retry = 12, .prio_name = PRIO_ALUA, }, { /* EVA 3000/5000 with new firmware, EVA 4000/6000/8000 */ .vendor = "(COMPAQ|HP)", .product = "(HSV1[01]1|HSV2[01]0|HSV3[046]0|HSV4[05]0)", .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .no_path_retry = 12, .prio_name = PRIO_ALUA, }, { /* MSA2000 family with old firmware */ .vendor = "HP", .product = "(MSA2[02]12fc|MSA2012i)", .pgpolicy = MULTIBUS, .no_path_retry = 18, }, { /* MSA2000 family with new firmware */ .vendor = "HP", .product = "(MSA2012sa|MSA23(12|24)(fc|i|sa)|MSA2000s VOLUME)", .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .no_path_retry = 18, .prio_name = PRIO_ALUA, }, { /* MSA 1040, 2040 and 2050 families */ .vendor = "HP", .product = "MSA [12]0[45]0 SA[NS]", .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .no_path_retry = 18, .prio_name = PRIO_ALUA, }, { /* SAN Virtualization Services Platform */ .vendor = "HP", .product = "HSVX700", .hwhandler = "1 alua", .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .no_path_retry = 12, .prio_name = PRIO_ALUA, }, { /* Smart Array */ .vendor = "HP", .product = "LOGICAL VOLUME", .pgpolicy = MULTIBUS, .no_path_retry = 12, }, { /* P2000 family */ .vendor = "HP", .product = "(P2000 G3 FC|P2000G3 FC/iSCSI|P2000 G3 SAS|P2000 G3 iSCSI)", .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .no_path_retry = 18, .prio_name = PRIO_ALUA, }, { /* StoreVirtual 4000 and 3200 families */ .vendor = "LEFTHAND", .product = "(P4000|iSCSIDisk|FCDISK)", .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .no_path_retry = 18, .prio_name = PRIO_ALUA, }, /* * DataDirect Networks */ { .vendor = "DDN", .product = "SAN DataDirector", .pgpolicy = MULTIBUS, }, { .vendor = "DDN", .product = "^EF3010", .pgpolicy = MULTIBUS, .no_path_retry = 30, }, { .vendor = "DDN", .product = "^(EF3015|S2A|SFA)", .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .prio_name = PRIO_ALUA, .no_path_retry = 30, }, /* * Dell EMC */ { /* Symmetrix / DMX / VMAX */ .vendor = "EMC", .product = "SYMMETRIX", .pgpolicy = MULTIBUS, .no_path_retry = 6, }, { /* DGC CLARiiON CX/AX / EMC VNX and Unity */ .vendor = "^DGC", .product = "^(RAID|DISK|VRAID)", .bl_product = "LUNZ", .hwhandler = "1 emc", .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .no_path_retry = (300 / DEFAULT_CHECKINT), .checker_name = EMC_CLARIION, .prio_name = PRIO_EMC, }, { /* Invista / VPLEX */ .vendor = "EMC", .product = "Invista", .bl_product = "LUNZ", .pgpolicy = MULTIBUS, .no_path_retry = 5, }, { .vendor = "XtremIO", .product = "XtremApp", .pgpolicy = MULTIBUS, }, { /* * Dell SC Series, formerly Compellent * * Maintainer : Sean McGinnis * Mail : sean_mcginnis@dell.com */ .vendor = "COMPELNT", .product = "Compellent Vol", .pgpolicy = MULTIBUS, .no_path_retry = NO_PATH_RETRY_QUEUE, }, { /* MD Series */ .vendor = "DELL", .product = "^MD3", .bl_product = "Universal Xport", .pgpolicy = GROUP_BY_PRIO, .checker_name = RDAC, .features = "2 pg_init_retries 50", .hwhandler = "1 rdac", .prio_name = PRIO_RDAC, .pgfailback = -FAILBACK_IMMEDIATE, .no_path_retry = 30, }, /* * Fujitsu */ { .vendor = "FSC", .product = "CentricStor", .pgpolicy = GROUP_BY_SERIAL, }, { .vendor = "FUJITSU", .product = "ETERNUS_DX(H|L|M|400|8000)", .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .no_path_retry = 10, .prio_name = PRIO_ALUA, }, { /* FibreCAT S80 */ .vendor = "(EUROLOGC|EuroLogc)", .product = "FC2502", .pgpolicy = MULTIBUS, }, { .vendor = "FUJITSU", .product = "E[234]000", .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .no_path_retry = 10, .prio_name = PRIO_ALUA, }, { .vendor = "FUJITSU", .product = "E[68]000", .pgpolicy = MULTIBUS, .no_path_retry = 10, }, /* * Hitachi * * Maintainer : Matthias Rudolph * Mail : matthias.rudolph@hds.com */ { /* USP-V, HUS VM, VSP, VSP G1000 and VSP GX00 families */ .vendor = "(HITACHI|HP)", .product = "^OPEN-", .pgpolicy = MULTIBUS, }, { /* AMS 2000 and HUS 100 families */ .vendor = "(HITACHI|HP)", .product = "^DF", .no_path_retry = NO_PATH_RETRY_QUEUE, .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .prio_name = PRIO_HDS, }, /* * IBM * * Maintainer : Hannes Reinecke * Mail : hare@suse.de */ { .vendor = "IBM", .product = "ProFibre 4000R", .pgpolicy = MULTIBUS, }, { /* DS4300 / FAStT600 */ .vendor = "IBM", .product = "^1722-600", .bl_product = "Universal Xport", .pgpolicy = GROUP_BY_PRIO, .checker_name = RDAC, .features = "2 pg_init_retries 50", .hwhandler = "1 rdac", .prio_name = PRIO_RDAC, .pgfailback = -FAILBACK_IMMEDIATE, .no_path_retry = 30, }, { /* DS4100 / FAStT100 */ .vendor = "IBM", .product = "^1724", .bl_product = "Universal Xport", .pgpolicy = GROUP_BY_PRIO, .checker_name = RDAC, .features = "2 pg_init_retries 50", .hwhandler = "1 rdac", .prio_name = PRIO_RDAC, .pgfailback = -FAILBACK_IMMEDIATE, .no_path_retry = 30, }, { /* DS3000 / DS3200 / DS3300 / DS3400 / Boot DS */ .vendor = "IBM", .product = "^1726", .bl_product = "Universal Xport", .pgpolicy = GROUP_BY_PRIO, .checker_name = RDAC, .features = "2 pg_init_retries 50", .hwhandler = "1 rdac", .prio_name = PRIO_RDAC, .pgfailback = -FAILBACK_IMMEDIATE, .no_path_retry = 30, }, { /* DS4400 / DS4500 / FAStT700 / FAStT900 */ .vendor = "IBM", .product = "^1742", .bl_product = "Universal Xport", .pgpolicy = GROUP_BY_PRIO, .checker_name = RDAC, .features = "2 pg_init_retries 50", .hwhandler = "1 rdac", .prio_name = PRIO_RDAC, .pgfailback = -FAILBACK_IMMEDIATE, .no_path_retry = 30, }, { /* DS3500 / DS3512 / DS3524 */ .vendor = "IBM", .product = "^1746", .bl_product = "Universal Xport", .pgpolicy = GROUP_BY_PRIO, .checker_name = RDAC, .features = "2 pg_init_retries 50", .hwhandler = "1 rdac", .prio_name = PRIO_RDAC, .pgfailback = -FAILBACK_IMMEDIATE, .no_path_retry = 30, }, { /* DCS3860 */ .vendor = "IBM", .product = "^1813", .bl_product = "Universal Xport", .pgpolicy = GROUP_BY_PRIO, .checker_name = RDAC, .features = "2 pg_init_retries 50", .hwhandler = "1 rdac", .prio_name = PRIO_RDAC, .pgfailback = -FAILBACK_IMMEDIATE, .no_path_retry = 30, }, { /* DS3950 / DS4200 / DS4700 / DS5020 */ .vendor = "IBM", .product = "^1814", .bl_product = "Universal Xport", .pgpolicy = GROUP_BY_PRIO, .checker_name = RDAC, .features = "2 pg_init_retries 50", .hwhandler = "1 rdac", .prio_name = PRIO_RDAC, .pgfailback = -FAILBACK_IMMEDIATE, .no_path_retry = 30, }, { /* DS4800 */ .vendor = "IBM", .product = "^1815", .bl_product = "Universal Xport", .pgpolicy = GROUP_BY_PRIO, .checker_name = RDAC, .features = "2 pg_init_retries 50", .hwhandler = "1 rdac", .prio_name = PRIO_RDAC, .pgfailback = -FAILBACK_IMMEDIATE, .no_path_retry = 30, }, { /* DS5000 / DS5100 / DS5300 / DCS3700 */ .vendor = "IBM", .product = "^1818", .bl_product = "Universal Xport", .pgpolicy = GROUP_BY_PRIO, .checker_name = RDAC, .features = "2 pg_init_retries 50", .hwhandler = "1 rdac", .prio_name = PRIO_RDAC, .pgfailback = -FAILBACK_IMMEDIATE, .no_path_retry = 30, }, { /* Netfinity Fibre Channel RAID Controller Unit */ .vendor = "IBM", .product = "^3526", .bl_product = "Universal Xport", .pgpolicy = GROUP_BY_PRIO, .checker_name = RDAC, .features = "2 pg_init_retries 50", .hwhandler = "1 rdac", .prio_name = PRIO_RDAC, .pgfailback = -FAILBACK_IMMEDIATE, .no_path_retry = 30, }, { /* FAStT200 and FAStT500 */ .vendor = "IBM", .product = "^(3542|3552)", .bl_product = "Universal Xport", .pgpolicy = GROUP_BY_PRIO, .checker_name = RDAC, .features = "2 pg_init_retries 50", .hwhandler = "1 rdac", .prio_name = PRIO_RDAC, .pgfailback = -FAILBACK_IMMEDIATE, .no_path_retry = 30, }, { /* Enterprise Storage Server / Shark family */ .vendor = "IBM", .product = "^2105", .no_path_retry = NO_PATH_RETRY_QUEUE, .pgpolicy = MULTIBUS, }, { /* DS6000 / DS6800 */ .vendor = "IBM", .product = "^1750500", .no_path_retry = NO_PATH_RETRY_QUEUE, .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .prio_name = PRIO_ALUA, }, { /* DS8000 family */ .vendor = "IBM", .product = "^2107900", .no_path_retry = NO_PATH_RETRY_QUEUE, .pgpolicy = MULTIBUS, }, { /* Storwize family / SAN Volume Controller / Flex System V7000 / FlashSystem V840/V9000 */ .vendor = "IBM", .product = "^2145", .no_path_retry = NO_PATH_RETRY_QUEUE, .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .prio_name = PRIO_ALUA, }, { .vendor = "IBM", .product = "S/390 DASD ECKD", .bl_product = "S/390", .uid_attribute = "ID_UID", .no_path_retry = NO_PATH_RETRY_QUEUE, .pgpolicy = MULTIBUS, }, { .vendor = "IBM", .product = "S/390 DASD FBA", .bl_product = "S/390", .uid_attribute = "ID_UID", .no_path_retry = NO_PATH_RETRY_QUEUE, .pgpolicy = MULTIBUS, }, { /* Power RAID */ .vendor = "IBM", .product = "^IPR", .no_path_retry = NO_PATH_RETRY_QUEUE, .hwhandler = "1 alua", .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .prio_name = PRIO_ALUA, }, { /* SAS RAID Controller Module (RSSM) */ .vendor = "IBM", .product = "1820N00", .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .no_path_retry = NO_PATH_RETRY_QUEUE, .prio_name = PRIO_ALUA, }, { /* XIV Storage System / FlashSystem A9000/A9000R */ .vendor = "(XIV|IBM)", .product = "(NEXTRA|2810XIV)", .no_path_retry = NO_PATH_RETRY_QUEUE, .pgpolicy = MULTIBUS, }, { /* TMS RamSan / FlashSystem 710/720/810/820/840/900 */ .vendor = "(TMS|IBM)", .product = "(RamSan|FlashSystem)", .pgpolicy = MULTIBUS, }, { /* DDN */ .vendor = "IBM", .product = "^(DCS9900|2851)", .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .prio_name = PRIO_ALUA, .no_path_retry = 30, }, /* * IBM Power Virtual SCSI Devices * * Maintainer : Brian King * Mail : brking@linux.vnet.ibm.com */ { .vendor = "AIX", .product = "VDASD", .pgpolicy = MULTIBUS, .no_path_retry = (300 / DEFAULT_CHECKINT), }, { /* 3303 NVDISK */ .vendor = "IBM", .product = "3303[ ]+NVDISK", .no_path_retry = (300 / DEFAULT_CHECKINT), }, { .vendor = "AIX", .product = "NVDISK", .hwhandler = "1 alua", .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .no_path_retry = (300 / DEFAULT_CHECKINT), .prio_name = PRIO_ALUA, }, /* * NetApp */ { /* * ONTAP family * * Maintainer : Martin George * Mail : marting@netapp.com */ .vendor = "NETAPP", .product = "LUN", .features = "2 pg_init_retries 50", .no_path_retry = NO_PATH_RETRY_QUEUE, .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .flush_on_last_del = FLUSH_ENABLED, .dev_loss = MAX_DEV_LOSS_TMO, .prio_name = PRIO_ONTAP, }, { /* * SANtricity(RDAC) family * * Maintainer : Sean Stewart * Mail : sean.stewart@netapp.com */ .vendor = "(NETAPP|LSI|ENGENIO)", .product = "INF-01-00", .bl_product = "Universal Xport", .pgpolicy = GROUP_BY_PRIO, .checker_name = RDAC, .features = "2 pg_init_retries 50", .hwhandler = "1 rdac", .prio_name = PRIO_RDAC, .pgfailback = -FAILBACK_IMMEDIATE, .no_path_retry = 30, }, { /* * SolidFir family * * Maintainer : PJ Waskiewicz * Mail : pj.waskiewicz@netapp.com */ .vendor = "SolidFir", .product = "SSD SAN", .pgpolicy = MULTIBUS, .no_path_retry = 24, }, /* * Nexenta * * Maintainer : Yacine Kheddache * Mail : yacine@alyseo.com */ { .vendor = "NEXENTA", .product = "COMSTAR", .pgpolicy = GROUP_BY_SERIAL, .no_path_retry = 30, }, /* * SGI */ { .vendor = "SGI", .product = "TP9100", .pgpolicy = MULTIBUS, }, { /* Total Performance family */ .vendor = "SGI", .product = "TP9[3457]00", .bl_product = "Universal Xport", .pgpolicy = GROUP_BY_PRIO, .checker_name = RDAC, .features = "2 pg_init_retries 50", .hwhandler = "1 rdac", .prio_name = PRIO_RDAC, .pgfailback = -FAILBACK_IMMEDIATE, .no_path_retry = 30, }, { /* InfiniteStorage family */ .vendor = "SGI", .product = "IS", .bl_product = "Universal Xport", .pgpolicy = GROUP_BY_PRIO, .checker_name = RDAC, .features = "2 pg_init_retries 50", .hwhandler = "1 rdac", .prio_name = PRIO_RDAC, .pgfailback = -FAILBACK_IMMEDIATE, .no_path_retry = 30, }, { /* DDN */ .vendor = "SGI", .product = "^DD[46]A-", .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .prio_name = PRIO_ALUA, .no_path_retry = 30, }, /* * NEC */ { /* M-Series */ .vendor = "NEC", .product = "DISK ARRAY", .hwhandler = "1 alua", .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .prio_name = PRIO_ALUA, }, /* * Oracle */ /* * Pillar Data / Oracle FS * * Maintainer : Srinivasan Ramani * Mail : srinivas.ramani@oracle.com */ { .vendor = "^Pillar", .product = "^Axiom", .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .prio_name = PRIO_ALUA, }, { .vendor = "^Oracle", .product = "^Oracle FS", .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .prio_name = PRIO_ALUA, }, /* Sun - StorageTek */ { /* B210, B220, B240 and B280 */ .vendor = "STK", .product = "BladeCtlr", .bl_product = "Universal Xport", .pgpolicy = GROUP_BY_PRIO, .checker_name = RDAC, .features = "2 pg_init_retries 50", .hwhandler = "1 rdac", .prio_name = PRIO_RDAC, .pgfailback = -FAILBACK_IMMEDIATE, .no_path_retry = 30, }, { /* 9176, D173, D178, D210, D220, D240 and D280 */ .vendor = "STK", .product = "OPENstorage", .bl_product = "Universal Xport", .pgpolicy = GROUP_BY_PRIO, .checker_name = RDAC, .features = "2 pg_init_retries 50", .hwhandler = "1 rdac", .prio_name = PRIO_RDAC, .pgfailback = -FAILBACK_IMMEDIATE, .no_path_retry = 30, }, { /* 6540 */ .vendor = "STK", .product = "FLEXLINE 380", .bl_product = "Universal Xport", .pgpolicy = GROUP_BY_PRIO, .checker_name = RDAC, .features = "2 pg_init_retries 50", .hwhandler = "1 rdac", .prio_name = PRIO_RDAC, .pgfailback = -FAILBACK_IMMEDIATE, .no_path_retry = 30, }, { /* (Dot Hill) 3310, 3320, 3510 and 3511 */ .vendor = "SUN", .product = "StorEdge 3", .pgpolicy = MULTIBUS, }, { .vendor = "SUN", .product = "STK6580_6780", .bl_product = "Universal Xport", .pgpolicy = GROUP_BY_PRIO, .checker_name = RDAC, .features = "2 pg_init_retries 50", .hwhandler = "1 rdac", .prio_name = PRIO_RDAC, .pgfailback = -FAILBACK_IMMEDIATE, .no_path_retry = 30, }, { /* 6130 / 6140 */ .vendor = "SUN", .product = "CSM[12]00_R", .bl_product = "Universal Xport", .pgpolicy = GROUP_BY_PRIO, .checker_name = RDAC, .features = "2 pg_init_retries 50", .hwhandler = "1 rdac", .prio_name = PRIO_RDAC, .pgfailback = -FAILBACK_IMMEDIATE, .no_path_retry = 30, }, { /* 2500 / 2510 / 2530 / 2540 */ .vendor = "SUN", .product = "LCSM100_[IEFS]", .bl_product = "Universal Xport", .pgpolicy = GROUP_BY_PRIO, .checker_name = RDAC, .features = "2 pg_init_retries 50", .hwhandler = "1 rdac", .prio_name = PRIO_RDAC, .pgfailback = -FAILBACK_IMMEDIATE, .no_path_retry = 30, }, { .vendor = "SUN", .product = "SUN_6180", .bl_product = "Universal Xport", .pgpolicy = GROUP_BY_PRIO, .checker_name = RDAC, .features = "2 pg_init_retries 50", .hwhandler = "1 rdac", .prio_name = PRIO_RDAC, .pgfailback = -FAILBACK_IMMEDIATE, .no_path_retry = 30, }, { /* ZFS Storage Appliances */ .vendor = "SUN", .product = "(Sun Storage|ZFS Storage|COMSTAR)", .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .prio_name = PRIO_ALUA, .no_path_retry = 30, }, /* * Pivot3 * * Maintainer : Bart Brooks * Mail : bartb@pivot3.com */ { .vendor = "PIVOT3", .product = "RAIGE VOLUME", .no_path_retry = NO_PATH_RETRY_QUEUE, .pgpolicy = MULTIBUS, }, { .vendor = "(NexGen|Pivot3)", .product = "(TierStore|vSTAC)", .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .prio_name = PRIO_ALUA, .no_path_retry = NO_PATH_RETRY_QUEUE, }, /* * Intel */ { .vendor = "(Intel|INTEL)", .product = "Multi-Flex", .bl_product = "VTrak V-LUN", .hwhandler = "1 alua", .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .no_path_retry = NO_PATH_RETRY_QUEUE, .prio_name = PRIO_ALUA, }, /* * Linux-IO Target */ { .vendor = "(LIO-ORG|SUSE)", .product = "RBD", .hwhandler = "1 alua", .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .no_path_retry = 12, .prio_name = PRIO_ALUA, }, /* * DataCore */ { .vendor = "DataCore", .product = "SANmelody", .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .no_path_retry = NO_PATH_RETRY_QUEUE, .prio_name = PRIO_ALUA, }, { /* SANsymphony */ .vendor = "DataCore", .product = "Virtual Disk", .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .no_path_retry = NO_PATH_RETRY_QUEUE, .prio_name = PRIO_ALUA, }, /* * Pure Storage */ { .vendor = "PURE", .product = "FlashArray", .pgpolicy = MULTIBUS, }, /* * Huawei */ { /* OceanStor V3 */ .vendor = "HUAWEI", .product = "XSG1", .pgpolicy = GROUP_BY_PRIO, .prio_name = PRIO_ALUA, }, /* * Red Hat * * Maintainer: Mike Christie * Mail: mchristi@redhat.com */ { .vendor = "Ceph", .product = "RBD", .checker_name = RBD, .deferred_remove = DEFERRED_REMOVE_ON, }, /* * Kove */ { .vendor = "KOVE", .product = "XPD", .pgpolicy = MULTIBUS, }, /* * Infinidat * * Maintainer: Arnon Yaari * Mail: arnony@infinidat.com */ { .vendor = "NFINIDAT", .product = "InfiniBox", .pgpolicy = GROUP_BY_PRIO, .pgfailback = 30, .prio_name = PRIO_ALUA, .selector = "round-robin 0", .rr_weight = RR_WEIGHT_PRIO, .no_path_retry = NO_PATH_RETRY_FAIL, .minio = 1, .minio_rq = 1, .flush_on_last_del = FLUSH_ENABLED, .fast_io_fail = 15, .dev_loss = 15, }, /* * Nimble Storage */ { .vendor = "Nimble", .product = "Server", .hwhandler = "1 alua", .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .prio_name = PRIO_ALUA, .no_path_retry = NO_PATH_RETRY_QUEUE, }, /* * Kaminario */ { .vendor = "KMNRIO", .product = "K2", .pgpolicy = MULTIBUS, }, /* * Tegile Systems */ { .vendor = "TEGILE", .product = "(ZEBI-(FC|ISCSI)|INTELLIFLASH)", .hwhandler = "1 alua", .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .prio_name = PRIO_ALUA, .no_path_retry = 10, }, /* * Imation/Nexsan */ { /* E-Series */ .vendor = "NEXSAN", .product = "NXS-B0", .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .prio_name = PRIO_ALUA, .no_path_retry = 15, }, { /* SATABeast / SATABoy */ .vendor = "NEXSAN", .product = "SATAB", .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .prio_name = PRIO_ALUA, .no_path_retry = 15, }, { /* NST / UNITY */ .vendor = "Nexsan", .product = "(NestOS|NST5000)", .hwhandler = "1 alua", .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .prio_name = PRIO_ALUA, .no_path_retry = 30, }, /* * Xiotech */ { /* Intelligent Storage Elements family */ .vendor = "(XIOTECH|XIOtech)", .product = "ISE", .pgpolicy = MULTIBUS, .no_path_retry = 12, }, { /* iglu blaze family */ .vendor = "(XIOTECH|XIOtech)", .product = "IGLU DISK", .pgpolicy = MULTIBUS, .no_path_retry = 30, }, { /* Magnitude family */ .vendor = "(XIOTECH|XIOtech)", .product = "Magnitude", .pgpolicy = MULTIBUS, .no_path_retry = 30, }, /* * Violin Memory */ { /* 3000 / 6000 Series */ .vendor = "VIOLIN", .product = "SAN ARRAY$", .pgpolicy = GROUP_BY_SERIAL, .no_path_retry = 30, }, { .vendor = "VIOLIN", .product = "SAN ARRAY ALUA", .hwhandler = "1 alua", .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .prio_name = PRIO_ALUA, .no_path_retry = 30, }, { /* FSP 7000 family */ .vendor = "VIOLIN", .product = "CONCERTO ARRAY", .pgpolicy = MULTIBUS, .no_path_retry = 30, }, /* * Promise Technology */ { .vendor = "Promise", .product = "VTrak", .bl_product = "VTrak V-LUN", .hwhandler = "1 alua", .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .prio_name = PRIO_ALUA, .no_path_retry = 30, }, { .vendor = "Promise", .product = "Vess", .bl_product = "Vess V-LUN", .hwhandler = "1 alua", .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .prio_name = PRIO_ALUA, .no_path_retry = 30, }, /* * Infortrend Technology */ { /* EonStor / ESVA */ .vendor = "^IFT", .product = ".*", .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .prio_name = PRIO_ALUA, .no_path_retry = 30, }, /* * Generic NVMe devices */ { .vendor = "NVME", .product = ".*", .uid_attribute = "ID_WWN", .checker_name = NONE, .retain_hwhandler = RETAIN_HWHANDLER_OFF, }, /* * Dot Hill Systems - Seagate Technology */ { /* SANnet family */ .vendor = "DotHill", .product = "SANnet", .pgpolicy = MULTIBUS, .no_path_retry = 30, }, { /* R/Evolution family */ .vendor = "DotHill", .product = "R/Evo", .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .prio_name = PRIO_ALUA, .no_path_retry = 30, }, { /* AssuredSAN family */ .vendor = "DotHill", .product = "^DH", .pgpolicy = GROUP_BY_PRIO, .pgfailback = -FAILBACK_IMMEDIATE, .prio_name = PRIO_ALUA, .no_path_retry = 30, }, /* * AccelStor */ { /* NeoSapphire */ .vendor = "AStor", .product = "NeoSapphire", .pgpolicy = MULTIBUS, .no_path_retry = 30, }, /* * EOL */ { .vendor = NULL, .product = NULL, }, }; int setup_default_hwtable(vector hw) { int r = 0; struct hwentry * hwe = default_hw; while (hwe->vendor) { r += store_hwe(hw, hwe); hwe++; } return r; } multipath-tools-0.7.4/libmultipath/hwtable.h000066400000000000000000000001471320314174000211610ustar00rootroot00000000000000#ifndef _HWTABLE_H #define _HWTABLE_H int setup_default_hwtable (vector hw); #endif /* _HWTABLE_H */ multipath-tools-0.7.4/libmultipath/io_err_stat.c000066400000000000000000000420101320314174000220330ustar00rootroot00000000000000/* * (C) Copyright HUAWEI Technology Corp. 2017, All Rights Reserved. * * io_err_stat.c * version 1.0 * * IO error stream statistic process for path failure event from kernel * * Author(s): Guan Junxiong 2017 * * This file is released under the GPL version 2, or any later version. */ #include #include #include #include #include #include #include #include #include #include #include "vector.h" #include "memory.h" #include "checkers.h" #include "config.h" #include "structs.h" #include "structs_vec.h" #include "devmapper.h" #include "debug.h" #include "lock.h" #include "time-util.h" #include "io_err_stat.h" #define IOTIMEOUT_SEC 60 #define TIMEOUT_NO_IO_NSEC 10000000 /*10ms = 10000000ns*/ #define FLAKY_PATHFAIL_THRESHOLD 2 #define CONCUR_NR_EVENT 32 #define PATH_IO_ERR_IN_CHECKING -1 #define PATH_IO_ERR_IN_POLLING_RECHECK -2 #define io_err_stat_log(prio, fmt, args...) \ condlog(prio, "io error statistic: " fmt, ##args) struct io_err_stat_pathvec { pthread_mutex_t mutex; vector pathvec; }; struct dio_ctx { struct timespec io_starttime; int blksize; void *buf; struct iocb io; }; struct io_err_stat_path { char devname[FILE_NAME_SIZE]; int fd; struct dio_ctx *dio_ctx_array; int io_err_nr; int io_nr; struct timespec start_time; int total_time; int err_rate_threshold; }; pthread_t io_err_stat_thr; pthread_attr_t io_err_stat_attr; static struct io_err_stat_pathvec *paths; struct vectors *vecs; io_context_t ioctx; static void cancel_inflight_io(struct io_err_stat_path *pp); static void rcu_unregister(void *param) { rcu_unregister_thread(); } struct io_err_stat_path *find_err_path_by_dev(vector pathvec, char *dev) { int i; struct io_err_stat_path *pp; if (!pathvec) return NULL; vector_foreach_slot(pathvec, pp, i) if (!strcmp(pp->devname, dev)) return pp; io_err_stat_log(4, "%s: not found in check queue", dev); return NULL; } static int init_each_dio_ctx(struct dio_ctx *ct, int blksize, unsigned long pgsize) { ct->blksize = blksize; if (posix_memalign(&ct->buf, pgsize, blksize)) return 1; memset(ct->buf, 0, blksize); ct->io_starttime.tv_sec = 0; ct->io_starttime.tv_nsec = 0; return 0; } static void deinit_each_dio_ctx(struct dio_ctx *ct) { if (ct->buf) free(ct->buf); } static int setup_directio_ctx(struct io_err_stat_path *p) { unsigned long pgsize = getpagesize(); char fpath[PATH_MAX]; int blksize = 0; int i; if (snprintf(fpath, PATH_MAX, "/dev/%s", p->devname) >= PATH_MAX) return 1; if (p->fd < 0) p->fd = open(fpath, O_RDONLY | O_DIRECT); if (p->fd < 0) return 1; p->dio_ctx_array = MALLOC(sizeof(struct dio_ctx) * CONCUR_NR_EVENT); if (!p->dio_ctx_array) goto fail_close; if (ioctl(p->fd, BLKBSZGET, &blksize) < 0) { io_err_stat_log(4, "%s:cannot get blocksize, set default 512", p->devname); blksize = 512; } if (!blksize) goto free_pdctx; for (i = 0; i < CONCUR_NR_EVENT; i++) { if (init_each_dio_ctx(p->dio_ctx_array + i, blksize, pgsize)) goto deinit; } return 0; deinit: for (i = 0; i < CONCUR_NR_EVENT; i++) deinit_each_dio_ctx(p->dio_ctx_array + i); free_pdctx: FREE(p->dio_ctx_array); fail_close: close(p->fd); return 1; } static void destroy_directio_ctx(struct io_err_stat_path *p) { int i; if (!p || !p->dio_ctx_array) return; cancel_inflight_io(p); for (i = 0; i < CONCUR_NR_EVENT; i++) deinit_each_dio_ctx(p->dio_ctx_array + i); FREE(p->dio_ctx_array); if (p->fd > 0) close(p->fd); } static struct io_err_stat_path *alloc_io_err_stat_path(void) { struct io_err_stat_path *p; p = (struct io_err_stat_path *)MALLOC(sizeof(*p)); if (!p) return NULL; memset(p->devname, 0, sizeof(p->devname)); p->io_err_nr = 0; p->io_nr = 0; p->total_time = 0; p->start_time.tv_sec = 0; p->start_time.tv_nsec = 0; p->err_rate_threshold = 0; p->fd = -1; return p; } static void free_io_err_stat_path(struct io_err_stat_path *p) { FREE(p); } static struct io_err_stat_pathvec *alloc_pathvec(void) { struct io_err_stat_pathvec *p; int r; p = (struct io_err_stat_pathvec *)MALLOC(sizeof(*p)); if (!p) return NULL; p->pathvec = vector_alloc(); if (!p->pathvec) goto out_free_struct_pathvec; r = pthread_mutex_init(&p->mutex, NULL); if (r) goto out_free_member_pathvec; return p; out_free_member_pathvec: vector_free(p->pathvec); out_free_struct_pathvec: FREE(p); return NULL; } static void free_io_err_pathvec(struct io_err_stat_pathvec *p) { struct io_err_stat_path *path; int i; if (!p) return; pthread_mutex_destroy(&p->mutex); if (!p->pathvec) { vector_foreach_slot(p->pathvec, path, i) { destroy_directio_ctx(path); free_io_err_stat_path(path); } vector_free(p->pathvec); } FREE(p); } /* * return value * 0: enqueue OK * 1: fails because of internal error * 2: fails because of existing already */ static int enqueue_io_err_stat_by_path(struct path *path) { struct io_err_stat_path *p; pthread_mutex_lock(&paths->mutex); p = find_err_path_by_dev(paths->pathvec, path->dev); if (p) { pthread_mutex_unlock(&paths->mutex); return 2; } pthread_mutex_unlock(&paths->mutex); p = alloc_io_err_stat_path(); if (!p) return 1; memcpy(p->devname, path->dev, sizeof(p->devname)); p->total_time = path->mpp->marginal_path_err_sample_time; p->err_rate_threshold = path->mpp->marginal_path_err_rate_threshold; if (setup_directio_ctx(p)) goto free_ioerr_path; pthread_mutex_lock(&paths->mutex); if (!vector_alloc_slot(paths->pathvec)) goto unlock_destroy; vector_set_slot(paths->pathvec, p); pthread_mutex_unlock(&paths->mutex); if (!path->io_err_disable_reinstate) { /* *fail the path in the kernel for the time of the to make *the test more reliable */ io_err_stat_log(3, "%s: fail dm path %s before checking", path->mpp->alias, path->dev); path->io_err_disable_reinstate = 1; dm_fail_path(path->mpp->alias, path->dev_t); update_queue_mode_del_path(path->mpp); /* * schedule path check as soon as possible to * update path state to delayed state */ path->tick = 1; } io_err_stat_log(2, "%s: enqueue path %s to check", path->mpp->alias, path->dev); return 0; unlock_destroy: pthread_mutex_unlock(&paths->mutex); destroy_directio_ctx(p); free_ioerr_path: free_io_err_stat_path(p); return 1; } int io_err_stat_handle_pathfail(struct path *path) { struct timespec curr_time; int res; if (path->io_err_disable_reinstate) { io_err_stat_log(3, "%s: reinstate is already disabled", path->dev); return 1; } if (path->io_err_pathfail_cnt < 0) return 1; if (!path->mpp) return 1; if (path->mpp->nr_active <= 1) return 1; if (path->mpp->marginal_path_double_failed_time <= 0 || path->mpp->marginal_path_err_sample_time <= 0 || path->mpp->marginal_path_err_recheck_gap_time <= 0 || path->mpp->marginal_path_err_rate_threshold < 0) { io_err_stat_log(4, "%s: parameter not set", path->mpp->alias); return 1; } if (path->mpp->marginal_path_err_sample_time < (2 * IOTIMEOUT_SEC)) { io_err_stat_log(2, "%s: marginal_path_err_sample_time should not less than %d", path->mpp->alias, 2 * IOTIMEOUT_SEC); return 1; } /* * The test should only be started for paths that have failed * repeatedly in a certain time frame, so that we have reason * to assume they're flaky. Without bother the admin to configure * the repeated count threshold and time frame, we assume a path * which fails at least twice within 60 seconds is flaky. */ if (clock_gettime(CLOCK_MONOTONIC, &curr_time) != 0) return 1; if (path->io_err_pathfail_cnt == 0) { path->io_err_pathfail_cnt++; path->io_err_pathfail_starttime = curr_time.tv_sec; io_err_stat_log(5, "%s: start path flakiness pre-checking", path->dev); return 0; } if ((curr_time.tv_sec - path->io_err_pathfail_starttime) > path->mpp->marginal_path_double_failed_time) { path->io_err_pathfail_cnt = 0; path->io_err_pathfail_starttime = curr_time.tv_sec; io_err_stat_log(5, "%s: restart path flakiness pre-checking", path->dev); } path->io_err_pathfail_cnt++; if (path->io_err_pathfail_cnt >= FLAKY_PATHFAIL_THRESHOLD) { res = enqueue_io_err_stat_by_path(path); if (!res) path->io_err_pathfail_cnt = PATH_IO_ERR_IN_CHECKING; else path->io_err_pathfail_cnt = 0; } return 0; } int hit_io_err_recheck_time(struct path *pp) { struct timespec curr_time; int r; if (pp->io_err_disable_reinstate == 0) return 1; if (clock_gettime(CLOCK_MONOTONIC, &curr_time) != 0) return 1; if (pp->io_err_pathfail_cnt != PATH_IO_ERR_IN_POLLING_RECHECK) return 1; if (pp->mpp->nr_active <= 0) { io_err_stat_log(2, "%s: recover path early", pp->dev); goto recover; } if ((curr_time.tv_sec - pp->io_err_dis_reinstate_time) > pp->mpp->marginal_path_err_recheck_gap_time) { io_err_stat_log(4, "%s: reschedule checking after %d seconds", pp->dev, pp->mpp->marginal_path_err_recheck_gap_time); /* * to reschedule io error checking again * if the path is good enough, we claim it is good * and can be reinsated as soon as possible in the * check_path routine. */ pp->io_err_dis_reinstate_time = curr_time.tv_sec; r = enqueue_io_err_stat_by_path(pp); /* * Enqueue fails because of internal error. * In this case , we recover this path * Or else, return 1 to set path state to PATH_SHAKY */ if (r == 1) { io_err_stat_log(3, "%s: enqueue fails, to recover", pp->dev); goto recover; } else if (!r) { pp->io_err_pathfail_cnt = PATH_IO_ERR_IN_CHECKING; } } return 1; recover: pp->io_err_pathfail_cnt = 0; pp->io_err_disable_reinstate = 0; pp->tick = 1; return 0; } static int delete_io_err_stat_by_addr(struct io_err_stat_path *p) { int i; i = find_slot(paths->pathvec, p); if (i != -1) vector_del_slot(paths->pathvec, i); destroy_directio_ctx(p); free_io_err_stat_path(p); return 0; } static void account_async_io_state(struct io_err_stat_path *pp, int rc) { switch (rc) { case PATH_DOWN: case PATH_TIMEOUT: pp->io_err_nr++; break; case PATH_UNCHECKED: case PATH_UP: case PATH_PENDING: break; default: break; } } static int poll_io_err_stat(struct vectors *vecs, struct io_err_stat_path *pp) { struct timespec currtime, difftime; struct path *path; double err_rate; if (clock_gettime(CLOCK_MONOTONIC, &currtime) != 0) return 1; timespecsub(&currtime, &pp->start_time, &difftime); if (difftime.tv_sec < pp->total_time) return 0; io_err_stat_log(4, "%s: check end", pp->devname); err_rate = pp->io_nr == 0 ? 0 : (pp->io_err_nr * 1000.0f) / pp->io_nr; io_err_stat_log(3, "%s: IO error rate (%.1f/1000)", pp->devname, err_rate); pthread_cleanup_push(cleanup_lock, &vecs->lock); lock(&vecs->lock); pthread_testcancel(); path = find_path_by_dev(vecs->pathvec, pp->devname); if (!path) { io_err_stat_log(4, "path %s not found'", pp->devname); } else if (err_rate <= pp->err_rate_threshold) { path->io_err_pathfail_cnt = 0; path->io_err_disable_reinstate = 0; io_err_stat_log(3, "%s: (%d/%d) good to enable reinstating", pp->devname, pp->io_err_nr, pp->io_nr); /* * schedule path check as soon as possible to * update path state. Do NOT reinstate dm path here */ path->tick = 1; } else if (path->mpp && path->mpp->nr_active > 1) { io_err_stat_log(3, "%s: keep failing the dm path %s", path->mpp->alias, path->dev); path->io_err_pathfail_cnt = PATH_IO_ERR_IN_POLLING_RECHECK; path->io_err_disable_reinstate = 1; path->io_err_dis_reinstate_time = currtime.tv_sec; io_err_stat_log(3, "%s: disable reinstating of %s", path->mpp->alias, path->dev); } else { path->io_err_pathfail_cnt = 0; path->io_err_disable_reinstate = 0; io_err_stat_log(3, "%s: there is orphan path, enable reinstating", pp->devname); } lock_cleanup_pop(vecs->lock); delete_io_err_stat_by_addr(pp); return 0; } static int send_each_async_io(struct dio_ctx *ct, int fd, char *dev) { int rc = -1; if (ct->io_starttime.tv_nsec == 0 && ct->io_starttime.tv_sec == 0) { struct iocb *ios[1] = { &ct->io }; if (clock_gettime(CLOCK_MONOTONIC, &ct->io_starttime) != 0) { ct->io_starttime.tv_sec = 0; ct->io_starttime.tv_nsec = 0; return rc; } io_prep_pread(&ct->io, fd, ct->buf, ct->blksize, 0); if (io_submit(ioctx, 1, ios) != 1) { io_err_stat_log(5, "%s: io_submit error %i", dev, errno); return rc; } rc = 0; } return rc; } static void send_batch_async_ios(struct io_err_stat_path *pp) { int i; struct dio_ctx *ct; struct timespec currtime, difftime; if (clock_gettime(CLOCK_MONOTONIC, &currtime) != 0) return; /* * Give a free time for all IO to complete or timeout */ if (pp->start_time.tv_sec != 0) { timespecsub(&currtime, &pp->start_time, &difftime); if (difftime.tv_sec + IOTIMEOUT_SEC >= pp->total_time) return; } for (i = 0; i < CONCUR_NR_EVENT; i++) { ct = pp->dio_ctx_array + i; if (!send_each_async_io(ct, pp->fd, pp->devname)) pp->io_nr++; } if (pp->start_time.tv_sec == 0 && pp->start_time.tv_nsec == 0 && clock_gettime(CLOCK_MONOTONIC, &pp->start_time)) { pp->start_time.tv_sec = 0; pp->start_time.tv_nsec = 0; } } static int try_to_cancel_timeout_io(struct dio_ctx *ct, struct timespec *t, char *dev) { struct timespec difftime; struct io_event event; int rc = PATH_UNCHECKED; int r; if (ct->io_starttime.tv_sec == 0) return rc; timespecsub(t, &ct->io_starttime, &difftime); if (difftime.tv_sec > IOTIMEOUT_SEC) { struct iocb *ios[1] = { &ct->io }; io_err_stat_log(5, "%s: abort check on timeout", dev); r = io_cancel(ioctx, ios[0], &event); if (r) io_err_stat_log(5, "%s: io_cancel error %i", dev, errno); ct->io_starttime.tv_sec = 0; ct->io_starttime.tv_nsec = 0; rc = PATH_TIMEOUT; } else { rc = PATH_PENDING; } return rc; } static void poll_async_io_timeout(void) { struct io_err_stat_path *pp; struct timespec curr_time; int rc = PATH_UNCHECKED; int i, j; if (clock_gettime(CLOCK_MONOTONIC, &curr_time) != 0) return; vector_foreach_slot(paths->pathvec, pp, i) { for (j = 0; j < CONCUR_NR_EVENT; j++) { rc = try_to_cancel_timeout_io(pp->dio_ctx_array + j, &curr_time, pp->devname); account_async_io_state(pp, rc); } } } static void cancel_inflight_io(struct io_err_stat_path *pp) { struct io_event event; int i, r; for (i = 0; i < CONCUR_NR_EVENT; i++) { struct dio_ctx *ct = pp->dio_ctx_array + i; struct iocb *ios[1] = { &ct->io }; if (ct->io_starttime.tv_sec == 0 && ct->io_starttime.tv_nsec == 0) continue; io_err_stat_log(5, "%s: abort infligh io", pp->devname); r = io_cancel(ioctx, ios[0], &event); if (r) io_err_stat_log(5, "%s: io_cancel error %d, %i", pp->devname, r, errno); ct->io_starttime.tv_sec = 0; ct->io_starttime.tv_nsec = 0; } } static inline int handle_done_dio_ctx(struct dio_ctx *ct, struct io_event *ev) { ct->io_starttime.tv_sec = 0; ct->io_starttime.tv_nsec = 0; return (ev->res == ct->blksize) ? PATH_UP : PATH_DOWN; } static void handle_async_io_done_event(struct io_event *io_evt) { struct io_err_stat_path *pp; struct dio_ctx *ct; int rc = PATH_UNCHECKED; int i, j; vector_foreach_slot(paths->pathvec, pp, i) { for (j = 0; j < CONCUR_NR_EVENT; j++) { ct = pp->dio_ctx_array + j; if (&ct->io == io_evt->obj) { rc = handle_done_dio_ctx(ct, io_evt); account_async_io_state(pp, rc); return; } } } } static void process_async_ios_event(int timeout_nsecs, char *dev) { struct io_event events[CONCUR_NR_EVENT]; int i, n; struct timespec timeout = { .tv_nsec = timeout_nsecs }; errno = 0; n = io_getevents(ioctx, 1L, CONCUR_NR_EVENT, events, &timeout); if (n < 0) { io_err_stat_log(3, "%s: async io events returned %d (errno=%s)", dev, n, strerror(errno)); } else { for (i = 0; i < n; i++) handle_async_io_done_event(&events[i]); } } static void service_paths(void) { struct io_err_stat_path *pp; int i; pthread_mutex_lock(&paths->mutex); vector_foreach_slot(paths->pathvec, pp, i) { send_batch_async_ios(pp); process_async_ios_event(TIMEOUT_NO_IO_NSEC, pp->devname); poll_async_io_timeout(); poll_io_err_stat(vecs, pp); } pthread_mutex_unlock(&paths->mutex); } static void *io_err_stat_loop(void *data) { vecs = (struct vectors *)data; pthread_cleanup_push(rcu_unregister, NULL); rcu_register_thread(); mlockall(MCL_CURRENT | MCL_FUTURE); while (1) { service_paths(); usleep(100000); } pthread_cleanup_pop(1); return NULL; } int start_io_err_stat_thread(void *data) { if (io_setup(CONCUR_NR_EVENT, &ioctx) != 0) { io_err_stat_log(4, "io_setup failed"); return 1; } paths = alloc_pathvec(); if (!paths) goto destroy_ctx; if (pthread_create(&io_err_stat_thr, &io_err_stat_attr, io_err_stat_loop, data)) { io_err_stat_log(0, "cannot create io_error statistic thread"); goto out_free; } io_err_stat_log(3, "thread started"); return 0; out_free: free_io_err_pathvec(paths); destroy_ctx: io_destroy(ioctx); io_err_stat_log(0, "failed to start io_error statistic thread"); return 1; } void stop_io_err_stat_thread(void) { pthread_cancel(io_err_stat_thr); pthread_kill(io_err_stat_thr, SIGUSR2); free_io_err_pathvec(paths); io_destroy(ioctx); } multipath-tools-0.7.4/libmultipath/io_err_stat.h000066400000000000000000000005151320314174000220440ustar00rootroot00000000000000#ifndef _IO_ERR_STAT_H #define _IO_ERR_STAT_H #include "vector.h" #include "lock.h" extern pthread_attr_t io_err_stat_attr; int start_io_err_stat_thread(void *data); void stop_io_err_stat_thread(void); int io_err_stat_handle_pathfail(struct path *path); int hit_io_err_recheck_time(struct path *pp); #endif /* _IO_ERR_STAT_H */ multipath-tools-0.7.4/libmultipath/list.h000066400000000000000000000253451320314174000205150ustar00rootroot00000000000000/* * Copied from the Linux kernel source tree, version 2.6.0-test1. * * Licensed under the GPL v2 as per the whole kernel source tree. * */ #ifndef _LIST_H #define _LIST_H #include /** * container_of - cast a member of a structure out to the containing structure * * @ptr: the pointer to the member. * @type: the type of the container struct this is embedded in. * @member: the name of the member within the struct. * */ #define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );}) /* * These are non-NULL pointers that will result in page faults * under normal circumstances, used to verify that nobody uses * non-initialized list entries. */ #define LIST_POISON1 ((void *) 0x00100100) #define LIST_POISON2 ((void *) 0x00200200) /* * Simple doubly linked list implementation. * * Some of the internal functions ("__xxx") are useful when * manipulating whole lists rather than single entries, as * sometimes we already know the next/prev entries and we can * generate better code by using them directly rather than * using the generic single-entry routines. */ struct list_head { struct list_head *next, *prev; }; #define LIST_HEAD_INIT(name) { &(name), &(name) } #define LIST_HEAD(name) \ struct list_head name = LIST_HEAD_INIT(name) #define INIT_LIST_HEAD(ptr) do { \ (ptr)->next = (ptr); (ptr)->prev = (ptr); \ } while (0) /* * Insert a new entry between two known consecutive entries. * * This is only for internal list manipulation where we know * the prev/next entries already! */ static inline void __list_add(struct list_head *new, struct list_head *prev, struct list_head *next) { next->prev = new; new->next = next; new->prev = prev; prev->next = new; } /** * list_add - add a new entry * @new: new entry to be added * @head: list head to add it after * * Insert a new entry after the specified head. * This is good for implementing stacks. */ static inline void list_add(struct list_head *new, struct list_head *head) { __list_add(new, head, head->next); } /** * list_add_tail - add a new entry * @new: new entry to be added * @head: list head to add it before * * Insert a new entry before the specified head. * This is useful for implementing queues. */ static inline void list_add_tail(struct list_head *new, struct list_head *head) { __list_add(new, head->prev, head); } /* * Delete a list entry by making the prev/next entries * point to each other. * * This is only for internal list manipulation where we know * the prev/next entries already! */ static inline void __list_del(struct list_head * prev, struct list_head * next) { next->prev = prev; prev->next = next; } /** * list_del - deletes entry from list. * @entry: the element to delete from the list. * Note: list_empty on entry does not return true after this, the entry is * in an undefined state. */ static inline void list_del(struct list_head *entry) { __list_del(entry->prev, entry->next); entry->next = LIST_POISON1; entry->prev = LIST_POISON2; } /** * list_del_init - deletes entry from list and reinitialize it. * @entry: the element to delete from the list. */ static inline void list_del_init(struct list_head *entry) { __list_del(entry->prev, entry->next); INIT_LIST_HEAD(entry); } /** * list_move - delete from one list and add as another's head * @list: the entry to move * @head: the head that will precede our entry */ static inline void list_move(struct list_head *list, struct list_head *head) { __list_del(list->prev, list->next); list_add(list, head); } /** * list_move_tail - delete from one list and add as another's tail * @list: the entry to move * @head: the head that will follow our entry */ static inline void list_move_tail(struct list_head *list, struct list_head *head) { __list_del(list->prev, list->next); list_add_tail(list, head); } /** * list_empty - tests whether a list is empty * @head: the list to test. */ static inline int list_empty(struct list_head *head) { return head->next == head; } static inline void __list_splice(const struct list_head *list, struct list_head *prev, struct list_head *next) { struct list_head *first = list->next; struct list_head *last = list->prev; first->prev = prev; prev->next = first; last->next = next; next->prev = last; } /** * list_splice - join two lists * @list: the new list to add. * @head: the place to add it in the first list. */ static inline void list_splice(struct list_head *list, struct list_head *head) { if (!list_empty(list)) __list_splice(list, head, head->next); } /** * list_splice_tail - join two lists, each list being a queue * @list: the new list to add. * @head: the place to add it in the first list. */ static inline void list_splice_tail(struct list_head *list, struct list_head *head) { if (!list_empty(list)) __list_splice(list, head->prev, head); } /** * list_splice_init - join two lists and reinitialise the emptied list. * @list: the new list to add. * @head: the place to add it in the first list. * * The list at @list is reinitialised */ static inline void list_splice_init(struct list_head *list, struct list_head *head) { if (!list_empty(list)) { __list_splice(list, head, head->next); INIT_LIST_HEAD(list); } } /** * list_splice_tail_init - join two lists and reinitialise the emptied list * @list: the new list to add. * @head: the place to add it in the first list. * * Each of the lists is a queue. * The list at @list is reinitialised */ static inline void list_splice_tail_init(struct list_head *list, struct list_head *head) { if (!list_empty(list)) { __list_splice(list, head->prev, head); INIT_LIST_HEAD(list); } } /** * list_entry - get the struct for this entry * @ptr: the &struct list_head pointer. * @type: the type of the struct this is embedded in. * @member: the name of the list_struct within the struct. */ #define list_entry(ptr, type, member) \ container_of(ptr, type, member) /** * list_for_each - iterate over a list * @pos: the &struct list_head to use as a loop counter. * @head: the head for your list. */ #define list_for_each(pos, head) \ for (pos = (head)->next; pos != (head); \ pos = pos->next) /** * __list_for_each - iterate over a list * @pos: the &struct list_head to use as a loop counter. * @head: the head for your list. * * This variant differs from list_for_each() in that it's the * simplest possible list iteration code. * Use this for code that knows the list to be very short (empty * or 1 entry) most of the time. */ #define __list_for_each(pos, head) \ for (pos = (head)->next; pos != (head); pos = pos->next) /** * list_for_each_prev - iterate over a list backwards * @pos: the &struct list_head to use as a loop counter. * @head: the head for your list. */ #define list_for_each_prev(pos, head) \ for (pos = (head)->prev; pos != (head); pos = pos->prev) /** * list_for_each_safe - iterate over a list safe against removal of list entry * @pos: the &struct list_head to use as a loop counter. * @n: another &struct list_head to use as temporary storage * @head: the head for your list. */ #define list_for_each_safe(pos, n, head) \ for (pos = (head)->next, n = pos->next; pos != (head); \ pos = n, n = pos->next) /** * list_for_each_entry - iterate over list of given type * @pos: the type * to use as a loop counter. * @head: the head for your list. * @member: the name of the list_struct within the struct. */ #define list_for_each_entry(pos, head, member) \ for (pos = list_entry((head)->next, typeof(*pos), member); \ &pos->member != (head); \ pos = list_entry(pos->member.next, typeof(*pos), member)) /** * list_for_each_entry_reverse - iterate backwards over list of given type. * @pos: the type * to use as a loop counter. * @head: the head for your list. * @member: the name of the list_struct within the struct. */ #define list_for_each_entry_reverse(pos, head, member) \ for (pos = list_entry((head)->prev, typeof(*pos), member); \ &pos->member != (head); \ pos = list_entry(pos->member.prev, typeof(*pos), member)) /** * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry * @pos: the type * to use as a loop counter. * @n: another type * to use as temporary storage * @head: the head for your list. * @member: the name of the list_struct within the struct. */ #define list_for_each_entry_safe(pos, n, head, member) \ for (pos = list_entry((head)->next, typeof(*pos), member), \ n = list_entry(pos->member.next, typeof(*pos), member); \ &pos->member != (head); \ pos = n, n = list_entry(n->member.next, typeof(*n), member)) /** * list_for_each_entry_reverse_safe - iterate backwards over list of given type safe against removal of list entry * @pos: the type * to use as a loop counter. * @n: another type * to use as temporary storage * @head: the head for your list. * @member: the name of the list_struct within the struct. */ #define list_for_each_entry_reverse_safe(pos, n, head, member) \ for (pos = list_entry((head)->prev, typeof(*pos), member), \ n = list_entry(pos->member.prev, typeof(*pos), member);\ &pos->member != (head); \ pos = n, n = list_entry(n->member.prev, typeof(*n), member)) /** * list_for_some_entry_safe - iterate list from the given begin node to the given end node safe against removal of list entry * @pos: the type * to use as a loop counter. * @n: another type * to use as temporary storage * @from: the begin node of the iteration. * @to: the end node of the iteration. * @member: the name of the list_struct within the struct. */ #define list_for_some_entry_safe(pos, n, from, to, member) \ for (pos = list_entry((from)->next, typeof(*pos), member), \ n = list_entry(pos->member.next, typeof(*pos), member); \ &pos->member != (to); \ pos = n, n = list_entry(n->member.next, typeof(*n), member)) /** * list_for_some_entry_reverse_safe - iterate backwards list from the given begin node to the given end node safe against removal of list entry * @pos: the type * to use as a loop counter. * @n: another type * to use as temporary storage * @from: the begin node of the iteration. * @to: the end node of the iteration. * @member: the name of the list_struct within the struct. */ #define list_for_some_entry_reverse_safe(pos, n, from, to, member) \ for (pos = list_entry((from)->prev, typeof(*pos), member), \ n = list_entry(pos->member.prev, typeof(*pos), member); \ &pos->member != (to); \ pos = n, n = list_entry(n->member.prev, typeof(*n), member)) #endif /* _LIST_H */ multipath-tools-0.7.4/libmultipath/lock.c000066400000000000000000000001501320314174000204500ustar00rootroot00000000000000#include "lock.h" void cleanup_lock (void * data) { struct mutex_lock *lock = data; unlock(lock); } multipath-tools-0.7.4/libmultipath/lock.h000066400000000000000000000007721320314174000204670ustar00rootroot00000000000000#ifndef _LOCK_H #define _LOCK_H #include struct mutex_lock { pthread_mutex_t mutex; }; static inline void lock(struct mutex_lock *a) { pthread_mutex_lock(&a->mutex); } static inline int timedlock(struct mutex_lock *a, struct timespec *tmo) { return pthread_mutex_timedlock(&a->mutex, tmo); } static inline void unlock(struct mutex_lock *a) { pthread_mutex_unlock(&a->mutex); } #define lock_cleanup_pop(a) pthread_cleanup_pop(1) void cleanup_lock (void * data); #endif /* _LOCK_H */ multipath-tools-0.7.4/libmultipath/log.c000066400000000000000000000100361320314174000203050ustar00rootroot00000000000000/* * Copyright (c) 2005 Christophe Varoqui * Copyright (c) 2005 Benjamin Marzinski, Redhat * Copyright (c) 2005 Jun'ichi Nomura, NEC */ #include #include #include #include #include #include #include "memory.h" #include "log.h" #define ALIGN(len, s) (((len)+(s)-1)/(s)*(s)) struct logarea* la; #if LOGDBG static void dump_logarea (void) { struct logmsg * msg; logdbg(stderr, "\n==== area: start addr = %p, end addr = %p ====\n", la->start, la->end); logdbg(stderr, "|addr |next |prio|msg\n"); for (msg = (struct logmsg *)la->head; (void *)msg != la->tail; msg = msg->next) logdbg(stderr, "|%p |%p |%i |%s\n", (void *)msg, msg->next, msg->prio, (char *)&msg->str); logdbg(stderr, "|%p |%p |%i |%s\n", (void *)msg, msg->next, msg->prio, (char *)&msg->str); logdbg(stderr, "\n\n"); } #endif static int logarea_init (int size) { logdbg(stderr,"enter logarea_init\n"); la = (struct logarea *)MALLOC(sizeof(struct logarea)); if (!la) return 1; if (size < MAX_MSG_SIZE) size = DEFAULT_AREA_SIZE; la->start = MALLOC(size); if (!la->start) { FREE(la); return 1; } memset(la->start, 0, size); la->empty = 1; la->end = la->start + size; la->head = la->start; la->tail = la->start; la->buff = MALLOC(MAX_MSG_SIZE + sizeof(struct logmsg)); if (!la->buff) { FREE(la->start); FREE(la); return 1; } return 0; } int log_init(char *program_name, int size) { logdbg(stderr,"enter log_init\n"); openlog(program_name, 0, LOG_DAEMON); if (logarea_init(size)) return 1; return 0; } void free_logarea (void) { FREE(la->start); FREE(la->buff); FREE(la); return; } void log_close (void) { free_logarea(); closelog(); return; } void log_reset (char *program_name) { closelog(); tzset(); openlog(program_name, 0, LOG_DAEMON); } int log_enqueue (int prio, const char * fmt, va_list ap) { int len, fwd; char buff[MAX_MSG_SIZE]; struct logmsg * msg; struct logmsg * lastmsg; lastmsg = (struct logmsg *)la->tail; if (!la->empty) { fwd = sizeof(struct logmsg) + strlen((char *)&lastmsg->str) * sizeof(char) + 1; la->tail += ALIGN(fwd, sizeof(void *)); } vsnprintf(buff, MAX_MSG_SIZE, fmt, ap); len = ALIGN(sizeof(struct logmsg) + strlen(buff) * sizeof(char) + 1, sizeof(void *)); /* not enough space on tail : rewind */ if (la->head <= la->tail && len > (la->end - la->tail)) { logdbg(stderr, "enqueue: rewind tail to %p\n", la->tail); if (la->head == la->start ) { logdbg(stderr, "enqueue: can not rewind tail, drop msg\n"); la->tail = lastmsg; return 1; /* can't reuse */ } la->tail = la->start; if (la->empty) la->head = la->start; } /* not enough space on head : drop msg */ if (la->head > la->tail && len >= (la->head - la->tail)) { logdbg(stderr, "enqueue: log area overrun, drop msg\n"); if (!la->empty) la->tail = lastmsg; return 1; } /* ok, we can stage the msg in the area */ la->empty = 0; msg = (struct logmsg *)la->tail; msg->prio = prio; memcpy((void *)&msg->str, buff, strlen(buff) + 1); lastmsg->next = la->tail; msg->next = la->head; logdbg(stderr, "enqueue: %p, %p, %i, %s\n", (void *)msg, msg->next, msg->prio, (char *)&msg->str); #if LOGDBG dump_logarea(); #endif return 0; } int log_dequeue (void * buff) { struct logmsg * src = (struct logmsg *)la->head; struct logmsg * dst = (struct logmsg *)buff; struct logmsg * lst = (struct logmsg *)la->tail; if (la->empty) return 1; int len = strlen((char *)&src->str) * sizeof(char) + sizeof(struct logmsg) + 1; dst->prio = src->prio; memcpy(dst, src, len); if (la->tail == la->head) la->empty = 1; /* we purge the last logmsg */ else { la->head = src->next; lst->next = la->head; } logdbg(stderr, "dequeue: %p, %p, %i, %s\n", (void *)src, src->next, src->prio, (char *)&src->str); memset((void *)src, 0, len); return 0; } /* * this one can block under memory pressure */ void log_syslog (void * buff) { struct logmsg * msg = (struct logmsg *)buff; syslog(msg->prio, "%s", (char *)&msg->str); } multipath-tools-0.7.4/libmultipath/log.h000066400000000000000000000013531320314174000203140ustar00rootroot00000000000000#ifndef LOG_H #define LOG_H #define DEFAULT_AREA_SIZE 16384 #define MAX_MSG_SIZE 256 #ifndef LOGLEVEL #define LOGLEVEL 5 #endif #if LOGDBG #define logdbg(file, fmt, args...) fprintf(file, fmt, ##args) #else #define logdbg(file, fmt, args...) do {} while (0) #endif struct logmsg { short int prio; void * next; char str[0]; }; struct logarea { int empty; void * head; void * tail; void * start; void * end; char * buff; }; extern struct logarea* la; int log_init (char * progname, int size); void log_close (void); void log_reset (char * progname); int log_enqueue (int prio, const char * fmt, va_list ap); int log_dequeue (void *); void log_syslog (void *); void dump_logmsg (void *); void free_logarea (void); #endif /* LOG_H */ multipath-tools-0.7.4/libmultipath/log_pthread.c000066400000000000000000000047441320314174000220250ustar00rootroot00000000000000/* * Copyright (c) 2005 Christophe Varoqui */ #include #include #include #include #include #include #include "memory.h" #include "log_pthread.h" #include "log.h" #include "lock.h" pthread_t log_thr; pthread_mutex_t logq_lock; pthread_mutex_t logev_lock; pthread_cond_t logev_cond; int logq_running; void log_safe (int prio, const char * fmt, va_list ap) { if (log_thr == (pthread_t)0) { vsyslog(prio, fmt, ap); return; } pthread_mutex_lock(&logq_lock); log_enqueue(prio, fmt, ap); pthread_mutex_unlock(&logq_lock); pthread_mutex_lock(&logev_lock); pthread_cond_signal(&logev_cond); pthread_mutex_unlock(&logev_lock); } void log_thread_flush (void) { int empty; do { pthread_mutex_lock(&logq_lock); empty = log_dequeue(la->buff); pthread_mutex_unlock(&logq_lock); if (!empty) log_syslog(la->buff); } while (empty == 0); } static void flush_logqueue (void) { int empty; do { pthread_mutex_lock(&logq_lock); empty = log_dequeue(la->buff); pthread_mutex_unlock(&logq_lock); if (!empty) log_syslog(la->buff); } while (empty == 0); } static void * log_thread (void * et) { int running; pthread_mutex_lock(&logev_lock); logq_running = 1; pthread_mutex_unlock(&logev_lock); mlockall(MCL_CURRENT | MCL_FUTURE); logdbg(stderr,"enter log_thread\n"); while (1) { pthread_mutex_lock(&logev_lock); pthread_cond_wait(&logev_cond, &logev_lock); running = logq_running; pthread_mutex_unlock(&logev_lock); if (!running) break; log_thread_flush(); } return NULL; } void log_thread_start (pthread_attr_t *attr) { logdbg(stderr,"enter log_thread_start\n"); pthread_mutex_init(&logq_lock, NULL); pthread_mutex_init(&logev_lock, NULL); pthread_cond_init(&logev_cond, NULL); if (log_init("multipathd", 0)) { fprintf(stderr,"can't initialize log buffer\n"); exit(1); } if (pthread_create(&log_thr, attr, log_thread, NULL)) { fprintf(stderr,"can't start log thread\n"); exit(1); } return; } void log_thread_stop (void) { logdbg(stderr,"enter log_thread_stop\n"); pthread_mutex_lock(&logev_lock); logq_running = 0; pthread_cond_signal(&logev_cond); pthread_mutex_unlock(&logev_lock); pthread_mutex_lock(&logq_lock); pthread_cancel(log_thr); pthread_mutex_unlock(&logq_lock); pthread_join(log_thr, NULL); log_thr = (pthread_t)0; flush_logqueue(); pthread_mutex_destroy(&logq_lock); pthread_mutex_destroy(&logev_lock); pthread_cond_destroy(&logev_cond); log_close(); } multipath-tools-0.7.4/libmultipath/log_pthread.h000066400000000000000000000006341320314174000220240ustar00rootroot00000000000000#ifndef _LOG_PTHREAD_H #define _LOG_PTHREAD_H #include extern pthread_t log_thr; extern pthread_mutex_t logq_lock; extern pthread_mutex_t logev_lock; extern pthread_cond_t logev_cond; extern int logq_running; void log_safe(int prio, const char * fmt, va_list ap); void log_thread_start(pthread_attr_t *attr); void log_thread_stop(void); void log_thread_flush(void); #endif /* _LOG_PTHREAD_H */ multipath-tools-0.7.4/libmultipath/memory.c000066400000000000000000000245411320314174000210420ustar00rootroot00000000000000/* * Part: Memory management framework. This framework is used to * find any memory leak. * * Version: $Id: memory.c,v 1.1.11 2005/03/01 01:22:13 acassen Exp $ * * Authors: Alexandre Cassen, * Jan Holmberg, * * 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. * * 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. * * Copyright (C) 2001-2005 Alexandre Cassen, */ #include #include "memory.h" /* * Memory management. in debug mode, * help finding eventual memory leak. * Allocation memory types manipulated are : * * +type+--------meaning--------+ * ! 0 ! Free slot ! * ! 1 ! Overrun ! * ! 2 ! free null ! * ! 3 ! realloc null ! * ! 4 ! Not previus allocated ! * ! 8 ! Last free list ! * ! 9 ! Allocated ! * +----+-----------------------+ * * global variabel debug bit 9 ( 512 ) used to * flag some memory error. * */ #ifdef _DEBUG_ typedef struct { int type; int line; char *func; char *file; void *ptr; unsigned long size; long csum; } MEMCHECK; /* Last free pointers */ static MEMCHECK free_list[256]; static MEMCHECK alloc_list[MAX_ALLOC_LIST]; static int number_alloc_list = 0; static int n = 0; /* Alloc list pointer */ static int f = 0; /* Free list pointer */ void * dbg_malloc(unsigned long size, char *file, char *function, int line) { void *buf; int i = 0; long check; buf = zalloc(size + sizeof (long)); check = 0xa5a5 + size; *(long *) ((char *) buf + size) = check; while (i < number_alloc_list) { if (alloc_list[i].type == 0) break; i++; } if (i == number_alloc_list) number_alloc_list++; assert(number_alloc_list < MAX_ALLOC_LIST); alloc_list[i].ptr = buf; alloc_list[i].size = size; alloc_list[i].file = file; alloc_list[i].func = function; alloc_list[i].line = line; alloc_list[i].csum = check; alloc_list[i].type = 9; if (debug & 1) printf("zalloc[%3d:%3d], %p, %4ld at %s, %3d, %s\n", i, number_alloc_list, buf, size, file, line, function); n++; return buf; } char * dbg_strdup(char *str, char *file, char *function, int line) { void *buf; int i = 0; long check; long size; size = strlen(str) + 1; buf = zalloc(size + sizeof (long)); strcat(buf, str); check = 0xa5a5 + size; *(long *) ((char *) buf + size) = check; while (i < number_alloc_list) { if (alloc_list[i].type == 0) break; i++; } if (i == number_alloc_list) number_alloc_list++; assert(number_alloc_list < MAX_ALLOC_LIST); alloc_list[i].ptr = buf; alloc_list[i].size = size; alloc_list[i].file = file; alloc_list[i].func = function; alloc_list[i].line = line; alloc_list[i].csum = check; alloc_list[i].type = 9; if (debug & 1) printf("strdup[%3d:%3d], %p, %4ld at %s, %3d, %s\n", i, number_alloc_list, buf, size, file, line, function); n++; return buf; } /* Display a buffer into a HEXA formatted output */ static void dump_buffer(char *buff, int count) { int i, j, c; int printnext = 1; if (count % 16) c = count + (16 - count % 16); else c = count; for (i = 0; i < c; i++) { if (printnext) { printnext--; printf("%.4x ", i & 0xffff); } if (i < count) printf("%3.2x", buff[i] & 0xff); else printf(" "); if (!((i + 1) % 8)) { if ((i + 1) % 16) printf(" -"); else { printf(" "); for (j = i - 15; j <= i; j++) if (j < count) { if ((buff[j] & 0xff) >= 0x20 && (buff[j] & 0xff) <= 0x7e) printf("%c", buff[j] & 0xff); else printf("."); } else printf(" "); printf("\n"); printnext = 1; } } } } int dbg_free(void *buffer, char *file, char *function, int line) { int i = 0; void *buf; /* If nullpointer remember */ if (buffer == NULL) { i = number_alloc_list++; assert(number_alloc_list < MAX_ALLOC_LIST); alloc_list[i].ptr = buffer; alloc_list[i].size = 0; alloc_list[i].file = file; alloc_list[i].func = function; alloc_list[i].line = line; alloc_list[i].type = 2; if (debug & 1) printf("free NULL in %s, %3d, %s\n", file, line, function); debug |= 512; /* Memory Error detect */ return n; } else buf = buffer; while (i < number_alloc_list) { if (alloc_list[i].type == 9 && alloc_list[i].ptr == buf) { if (* ((long *) ((char *) alloc_list[i].ptr + alloc_list[i].size)) == alloc_list[i].csum) alloc_list[i].type = 0; /* Release */ else { alloc_list[i].type = 1; /* Overrun */ if (debug & 1) { printf("free corrupt, buffer overrun [%3d:%3d], %p, %4ld at %s, %3d, %s\n", i, number_alloc_list, buf, alloc_list[i].size, file, line, function); dump_buffer(alloc_list[i].ptr, alloc_list[i].size + sizeof (long)); printf("Check_sum\n"); dump_buffer((char *) &alloc_list[i].csum, sizeof(long)); debug |= 512; /* Memory Error detect */ } } break; } i++; } /* Not found */ if (i == number_alloc_list) { printf("Free ERROR %p\n", buffer); number_alloc_list++; assert(number_alloc_list < MAX_ALLOC_LIST); alloc_list[i].ptr = buf; alloc_list[i].size = 0; alloc_list[i].file = file; alloc_list[i].func = function; alloc_list[i].line = line; alloc_list[i].type = 4; debug |= 512; return n; } if (buffer != NULL) xfree(buffer); if (debug & 1) printf("free [%3d:%3d], %p, %4ld at %s, %3d, %s\n", i, number_alloc_list, buf, alloc_list[i].size, file, line, function); free_list[f].file = file; free_list[f].line = line; free_list[f].func = function; free_list[f].ptr = buffer; free_list[f].type = 8; free_list[f].csum = i; /* Using this field for row id */ f++; f &= 255; n--; return n; } void dbg_free_final(char *banner) { unsigned int sum = 0, overrun = 0, badptr = 0; int i, j; i = 0; printf("\n---[ Memory dump for (%s)]---\n\n", banner); while (i < number_alloc_list) { switch (alloc_list[i].type) { case 3: badptr++; printf ("null pointer to realloc(nil,%ld)! at %s, %3d, %s\n", alloc_list[i].size, alloc_list[i].file, alloc_list[i].line, alloc_list[i].func); break; case 4: badptr++; printf ("pointer not found in table to free(%p) [%3d:%3d], at %s, %3d, %s\n", alloc_list[i].ptr, i, number_alloc_list, alloc_list[i].file, alloc_list[i].line, alloc_list[i].func); for (j = 0; j < 256; j++) if (free_list[j].ptr == alloc_list[i].ptr) if (free_list[j].type == 8) printf (" -> pointer already released at [%3d:%3d], at %s, %3d, %s\n", (int) free_list[j].csum, number_alloc_list, free_list[j].file, free_list[j].line, free_list[j].func); break; case 2: badptr++; printf("null pointer to free(nil)! at %s, %3d, %s\n", alloc_list[i].file, alloc_list[i].line, alloc_list[i].func); break; case 1: overrun++; printf("%p [%3d:%3d], %4ld buffer overrun!:\n", alloc_list[i].ptr, i, number_alloc_list, alloc_list[i].size); printf(" --> source of malloc: %s, %3d, %s\n", alloc_list[i].file, alloc_list[i].line, alloc_list[i].func); break; case 9: sum += alloc_list[i].size; printf("%p [%3d:%3d], %4ld not released!:\n", alloc_list[i].ptr, i, number_alloc_list, alloc_list[i].size); printf(" --> source of malloc: %s, %3d, %s\n", alloc_list[i].file, alloc_list[i].line, alloc_list[i].func); break; } i++; } printf("\n\n---[ Memory dump summary for (%s) ]---\n", banner); printf("Total number of bytes not freed...: %d\n", sum); printf("Number of entries not freed.......: %d\n", n); printf("Maximum allocated entries.........: %d\n", number_alloc_list); printf("Number of bad entries.............: %d\n", badptr); printf("Number of buffer overrun..........: %d\n\n", overrun); if (sum || n || badptr || overrun) printf("=> Program seems to have some memory problem !!!\n\n"); else printf("=> Program seems to be memory allocation safe...\n\n"); } void * dbg_realloc(void *buffer, unsigned long size, char *file, char *function, int line) { int i = 0; void *buf, *buf2; long check; if (buffer == NULL) { printf("realloc %p %s, %3d %s\n", buffer, file, line, function); i = number_alloc_list++; assert(number_alloc_list < MAX_ALLOC_LIST); alloc_list[i].ptr = NULL; alloc_list[i].size = 0; alloc_list[i].file = file; alloc_list[i].func = function; alloc_list[i].line = line; alloc_list[i].type = 3; return dbg_malloc(size, file, function, line); } buf = buffer; while (i < number_alloc_list) { if (alloc_list[i].ptr == buf) { buf = alloc_list[i].ptr; break; } i++; } /* not found */ if (i == number_alloc_list) { printf("realloc ERROR no matching zalloc %p \n", buffer); number_alloc_list++; assert(number_alloc_list < MAX_ALLOC_LIST); alloc_list[i].ptr = buf; alloc_list[i].size = 0; alloc_list[i].file = file; alloc_list[i].func = function; alloc_list[i].line = line; alloc_list[i].type = 9; debug |= 512; /* Memory Error detect */ return NULL; } buf2 = ((char *) buf) + alloc_list[i].size; if (*(long *) (buf2) != alloc_list[i].csum) { alloc_list[i].type = 1; debug |= 512; /* Memory Error detect */ } buf = realloc(buffer, size + sizeof (long)); check = 0xa5a5 + size; *(long *) ((char *) buf + size) = check; alloc_list[i].csum = check; if (debug & 1) printf("realloc [%3d:%3d] %p, %4ld %s %d %s -> %p %4ld %s %d %s\n", i, number_alloc_list, alloc_list[i].ptr, alloc_list[i].size, alloc_list[i].file, alloc_list[i].line, alloc_list[i].func, buf, size, file, line, function); alloc_list[i].ptr = buf; alloc_list[i].size = size; alloc_list[i].file = file; alloc_list[i].line = line; alloc_list[i].func = function; return buf; } #endif multipath-tools-0.7.4/libmultipath/memory.h000066400000000000000000000040421320314174000210410ustar00rootroot00000000000000/* * Part: memory.c include file. * * Version: $Id: memory.h,v 1.1.11 2005/03/01 01:22:13 acassen Exp $ * * Authors: Alexandre Cassen, * Jan Holmberg, * * 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. * * 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. * * Copyright (C) 2001-2005 Alexandre Cassen, */ #ifndef _MEMORY_H #define _MEMORY_H /* system includes */ #include #include #include #include /* Local defines */ #ifdef _DEBUG_ int debug; #define MAX_ALLOC_LIST 2048 #define MALLOC(n) ( dbg_malloc((n), \ (__FILE__), (char *)(__FUNCTION__), (__LINE__)) ) #define FREE(b) ( dbg_free((b), \ (__FILE__), (char *)(__FUNCTION__), (__LINE__)) ) #define REALLOC(b,n) ( dbg_realloc((b), (n), \ (__FILE__), (char *)(__FUNCTION__), (__LINE__)) ) #define STRDUP(n) ( dbg_strdup((n), \ (__FILE__), (char *)(__FUNCTION__), (__LINE__)) ) /* Memory debug prototypes defs */ extern void *dbg_malloc(unsigned long, char *, char *, int); extern int dbg_free(void *, char *, char *, int); extern void *dbg_realloc(void *, unsigned long, char *, char *, int); extern char *dbg_strdup(char *, char *, char *, int); extern void dbg_free_final(char *); #else #define MALLOC(n) (calloc(1,(n))) #define FREE(p) do { free(p); p = NULL; } while(0) #define REALLOC(p,n) (realloc((p),(n))) #define STRDUP(n) (strdup(n)) #endif /* Common defines */ #define FREE_PTR(P) if((P)) FREE((P)); #endif multipath-tools-0.7.4/libmultipath/parser.c000066400000000000000000000270761320314174000210340ustar00rootroot00000000000000/* * Part: Configuration file parser/reader. Place into the dynamic * data structure representation the conf file * * Version: $Id: parser.c,v 1.0.3 2003/05/11 02:28:03 acassen Exp $ * * Author: Alexandre Cassen, * * 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. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ #include #include #include "vector.h" #include "config.h" #include "parser.h" #include "memory.h" #include "debug.h" /* local vars */ static int sublevel = 0; static int line_nr; int keyword_alloc(vector keywords, char *string, int (*handler) (struct config *, vector), int (*print) (struct config *, char *, int, void *), int unique) { struct keyword *keyword; keyword = (struct keyword *) MALLOC(sizeof (struct keyword)); if (!keyword) return 1; if (!vector_alloc_slot(keywords)) { FREE(keyword); return 1; } keyword->string = string; keyword->handler = handler; keyword->print = print; keyword->unique = unique; vector_set_slot(keywords, keyword); return 0; } void install_sublevel(void) { sublevel++; } void install_sublevel_end(void) { sublevel--; } int _install_keyword(vector keywords, char *string, int (*handler) (struct config *, vector), int (*print) (struct config *, char *, int, void *), int unique) { int i = 0; struct keyword *keyword; /* fetch last keyword */ keyword = VECTOR_SLOT(keywords, VECTOR_SIZE(keywords) - 1); /* position to last sub level */ for (i = 0; i < sublevel; i++) keyword = VECTOR_SLOT(keyword->sub, VECTOR_SIZE(keyword->sub) - 1); /* First sub level allocation */ if (!keyword->sub) keyword->sub = vector_alloc(); if (!keyword->sub) return 1; /* add new sub keyword */ return keyword_alloc(keyword->sub, string, handler, print, unique); } void free_keywords(vector keywords) { struct keyword *keyword; int i; if (!keywords) return; for (i = 0; i < VECTOR_SIZE(keywords); i++) { keyword = VECTOR_SLOT(keywords, i); if (keyword->sub) free_keywords(keyword->sub); FREE(keyword); } vector_free(keywords); } struct keyword * find_keyword(vector keywords, vector v, char * name) { struct keyword *keyword; int i; int len; if (!name || !keywords) return NULL; if (!v) v = keywords; len = strlen(name); for (i = 0; i < VECTOR_SIZE(v); i++) { keyword = VECTOR_SLOT(v, i); if ((strlen(keyword->string) == len) && !strcmp(keyword->string, name)) return keyword; if (keyword->sub) { keyword = find_keyword(keywords, keyword->sub, name); if (keyword) return keyword; } } return NULL; } int snprint_keyword(char *buff, int len, char *fmt, struct keyword *kw, void *data) { int r; int fwd = 0; char *f = fmt; struct config *conf; if (!kw || !kw->print) return 0; do { if (fwd == len || *f == '\0') break; if (*f != '%') { *(buff + fwd) = *f; fwd++; continue; } f++; switch(*f) { case 'k': fwd += snprintf(buff + fwd, len - fwd, "%s", kw->string); break; case 'v': conf = get_multipath_config(); r = kw->print(conf, buff + fwd, len - fwd, data); put_multipath_config(conf); if (!r) { /* no output if no value */ buff[0] = '\0'; return 0; } fwd += r; break; } if (fwd > len) fwd = len; } while (*f++); return fwd; } vector alloc_strvec(char *string) { char *cp, *start, *token; int strlen; int in_string; vector strvec; if (!string) return NULL; cp = string; /* Skip white spaces */ while ((isspace((int) *cp) || !isascii((int) *cp)) && *cp != '\0') cp++; /* Return if there is only white spaces */ if (*cp == '\0') return NULL; /* Return if string begin with a comment */ if (*cp == '!' || *cp == '#') return NULL; /* Create a vector and alloc each command piece */ strvec = vector_alloc(); if (!strvec) return NULL; in_string = 0; while (1) { if (!vector_alloc_slot(strvec)) goto out; start = cp; if (*cp == '"') { cp++; token = MALLOC(2); if (!token) goto out; *(token) = '"'; *(token + 1) = '\0'; if (in_string) in_string = 0; else in_string = 1; } else if (!in_string && (*cp == '{' || *cp == '}')) { token = MALLOC(2); if (!token) goto out; *(token) = *cp; *(token + 1) = '\0'; cp++; } else { while ((in_string || (!isspace((int) *cp) && isascii((int) *cp) && *cp != '!' && *cp != '#' && *cp != '{' && *cp != '}')) && *cp != '\0' && *cp != '"') cp++; strlen = cp - start; token = MALLOC(strlen + 1); if (!token) goto out; memcpy(token, start, strlen); *(token + strlen) = '\0'; } vector_set_slot(strvec, token); while ((isspace((int) *cp) || !isascii((int) *cp)) && *cp != '\0') cp++; if (*cp == '\0' || *cp == '!' || *cp == '#') return strvec; } out: vector_free(strvec); return NULL; } static int read_line(FILE *stream, char *buf, int size) { char *p; if (fgets(buf, size, stream) == NULL) return 0; strtok_r(buf, "\n\r", &p); return 1; } void * set_value(vector strvec) { char *str = VECTOR_SLOT(strvec, 1); size_t size; int i = 0; int len = 0; char *alloc = NULL; char *tmp; if (!str) { condlog(0, "option '%s' missing value", (char *)VECTOR_SLOT(strvec, 0)); return NULL; } size = strlen(str); if (size == 0) { condlog(0, "option '%s' has empty value", (char *)VECTOR_SLOT(strvec, 0)); return NULL; } if (*str != '"') { alloc = MALLOC(sizeof (char) * (size + 1)); if (alloc) memcpy(alloc, str, size); else condlog(0, "can't allocate memeory for option '%s'", (char *)VECTOR_SLOT(strvec, 0)); return alloc; } /* Even empty quotes counts as a value (An empty string) */ alloc = (char *) MALLOC(sizeof (char)); if (!alloc) { condlog(0, "can't allocate memeory for option '%s'", (char *)VECTOR_SLOT(strvec, 0)); return NULL; } for (i = 2; i < VECTOR_SIZE(strvec); i++) { str = VECTOR_SLOT(strvec, i); if (!str) { free(alloc); condlog(0, "parse error for option '%s'", (char *)VECTOR_SLOT(strvec, 0)); return NULL; } if (*str == '"') break; tmp = alloc; /* The first +1 is for the NULL byte. The rest are for the * spaces between words */ len += strlen(str) + 1; alloc = REALLOC(alloc, sizeof (char) * len); if (!alloc) { FREE(tmp); condlog(0, "can't allocate memeory for option '%s'", (char *)VECTOR_SLOT(strvec, 0)); return NULL; } if (*alloc != '\0') strncat(alloc, " ", 1); strncat(alloc, str, strlen(str)); } return alloc; } /* non-recursive configuration stream handler */ static int kw_level = 0; int warn_on_duplicates(vector uniques, char *str, char *file) { char *tmp; int i; vector_foreach_slot(uniques, tmp, i) { if (!strcmp(str, tmp)) { condlog(1, "%s line %d, duplicate keyword: %s", file, line_nr, str); return 0; } } tmp = strdup(str); if (!tmp) return 1; if (!vector_alloc_slot(uniques)) { free(tmp); return 1; } vector_set_slot(uniques, tmp); return 0; } void free_uniques(vector uniques) { char *tmp; int i; vector_foreach_slot(uniques, tmp, i) free(tmp); vector_free(uniques); } int is_sublevel_keyword(char *str) { return (strcmp(str, "defaults") == 0 || strcmp(str, "blacklist") == 0 || strcmp(str, "blacklist_exceptions") == 0 || strcmp(str, "devices") == 0 || strcmp(str, "devices") == 0 || strcmp(str, "device") == 0 || strcmp(str, "multipaths") == 0 || strcmp(str, "multipath") == 0); } int validate_config_strvec(vector strvec, char *file) { char *str; int i; str = VECTOR_SLOT(strvec, 0); if (str == NULL) { condlog(0, "can't parse option on line %d of %s", line_nr, file); return -1; } if (*str == '}') { if (VECTOR_SIZE(strvec) > 1) condlog(0, "ignoring extra data starting with '%s' on line %d of %s", (char *)VECTOR_SLOT(strvec, 1), line_nr, file); return 0; } if (*str == '{') { condlog(0, "invalid keyword '%s' on line %d of %s", str, line_nr, file); return -1; } if (is_sublevel_keyword(str)) { str = VECTOR_SLOT(strvec, 1); if (str == NULL) condlog(0, "missing '{' on line %d of %s", line_nr, file); else if (*str != '{') condlog(0, "expecting '{' on line %d of %s. found '%s'", line_nr, file, str); else if (VECTOR_SIZE(strvec) > 2) condlog(0, "ignoring extra data starting with '%s' on line %d of %s", (char *)VECTOR_SLOT(strvec, 2), line_nr, file); return 0; } str = VECTOR_SLOT(strvec, 1); if (str == NULL) { condlog(0, "missing value for option '%s' on line %d of %s", (char *)VECTOR_SLOT(strvec, 0), line_nr, file); return -1; } if (*str != '"') { if (VECTOR_SIZE(strvec) > 2) condlog(0, "ignoring extra data starting with '%s' on line %d of %s", (char *)VECTOR_SLOT(strvec, 2), line_nr, file); return 0; } for (i = 2; i < VECTOR_SIZE(strvec); i++) { str = VECTOR_SLOT(strvec, i); if (str == NULL) { condlog(0, "can't parse value on line %d of %s", line_nr, file); return -1; } if (*str == '"') { if (VECTOR_SIZE(strvec) > i + 1) condlog(0, "ignoring extra data starting with '%s' on line %d of %s", (char *)VECTOR_SLOT(strvec, (i + 1)), line_nr, file); return 0; } } condlog(0, "missing closing quotes on line %d of %s", line_nr, file); return 0; } static int process_stream(struct config *conf, FILE *stream, vector keywords, char *file) { int i; int r = 0, t; struct keyword *keyword; char *str; char *buf; vector strvec; vector uniques; uniques = vector_alloc(); if (!uniques) return 1; buf = MALLOC(MAXBUF); if (!buf) { vector_free(uniques); return 1; } while (read_line(stream, buf, MAXBUF)) { line_nr++; strvec = alloc_strvec(buf); if (!strvec) continue; if (validate_config_strvec(strvec, file) != 0) { free_strvec(strvec); continue; } str = VECTOR_SLOT(strvec, 0); if (!strcmp(str, EOB)) { if (kw_level > 0) { free_strvec(strvec); break; } condlog(0, "unmatched '%s' at line %d of %s", EOB, line_nr, file); } for (i = 0; i < VECTOR_SIZE(keywords); i++) { keyword = VECTOR_SLOT(keywords, i); if (!strcmp(keyword->string, str)) { if (keyword->unique && warn_on_duplicates(uniques, str, file)) { r = 1; free_strvec(strvec); goto out; } if (keyword->handler) { t = (*keyword->handler) (conf, strvec); r += t; if (t) condlog(1, "multipath.conf +%d, parsing failed: %s", line_nr, buf); } if (keyword->sub) { kw_level++; r += process_stream(conf, stream, keyword->sub, file); kw_level--; } break; } } if (i >= VECTOR_SIZE(keywords)) condlog(1, "%s line %d, invalid keyword: %s", file, line_nr, str); free_strvec(strvec); } out: FREE(buf); free_uniques(uniques); return r; } /* Data initialization */ int process_file(struct config *conf, char *file) { int r; FILE *stream; if (!conf->keywords) { condlog(0, "No keywords alocated"); return 1; } stream = fopen(file, "r"); if (!stream) { condlog(0, "couldn't open configuration file '%s': %s", file, strerror(errno)); return 1; } /* Stream handling */ line_nr = 0; r = process_stream(conf, stream, conf->keywords, file); fclose(stream); //free_keywords(keywords); return r; } multipath-tools-0.7.4/libmultipath/parser.h000066400000000000000000000054601320314174000210320ustar00rootroot00000000000000/* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: cfreader.c include file. * * Version: $Id: parser.h,v 1.0.3 2003/05/11 02:28:03 acassen Exp $ * * Author: Alexandre Cassen, * * 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. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ #ifndef _PARSER_H #define _PARSER_H /* system includes */ #include #include #include #include #include #include /* local includes */ #include "vector.h" #include "config.h" /* Global definitions */ #define EOB "}" #define MAXBUF 1024 /* ketword definition */ struct keyword { char *string; int (*handler) (struct config *, vector); int (*print) (struct config *, char *, int, void *); vector sub; int unique; }; /* Reloading helpers */ #define SET_RELOAD (reload = 1) #define UNSET_RELOAD (reload = 0) #define RELOAD_DELAY 5 /* iterator helper */ #define iterate_sub_keywords(k,p,i) \ for (i = 0; i < (k)->sub->allocated && ((p) = (k)->sub->slot[i]); i++) /* Prototypes */ extern int keyword_alloc(vector keywords, char *string, int (*handler) (struct config *, vector), int (*print) (struct config *, char *, int, void *), int unique); #define install_keyword_root(str, h) keyword_alloc(keywords, str, h, NULL, 1) extern void install_sublevel(void); extern void install_sublevel_end(void); extern int _install_keyword(vector keywords, char *string, int (*handler) (struct config *, vector), int (*print) (struct config *, char *, int, void *), int unique); #define install_keyword(str, vec, pri) _install_keyword(keywords, str, vec, pri, 1) #define install_keyword_multi(str, vec, pri) _install_keyword(keywords, str, vec, pri, 0) extern void dump_keywords(vector keydump, int level); extern void free_keywords(vector keywords); extern vector alloc_strvec(char *string); extern void *set_value(vector strvec); extern int process_file(struct config *conf, char *conf_file); extern struct keyword * find_keyword(vector keywords, vector v, char * name); int snprint_keyword(char *buff, int len, char *fmt, struct keyword *kw, void *data); #endif multipath-tools-0.7.4/libmultipath/pgpolicies.c000066400000000000000000000154441320314174000216720ustar00rootroot00000000000000/* * Copyright (c) 2004, 2005 Christophe Varoqui */ #include #include #include #include "checkers.h" #include "util.h" #include "memory.h" #include "vector.h" #include "structs.h" #include "pgpolicies.h" #include "switchgroup.h" int get_pgpolicy_id(char * str) { if (0 == strncmp(str, "failover", 8)) return FAILOVER; if (0 == strncmp(str, "multibus", 8)) return MULTIBUS; if (0 == strncmp(str, "group_by_serial", 15)) return GROUP_BY_SERIAL; if (0 == strncmp(str, "group_by_prio", 13)) return GROUP_BY_PRIO; if (0 == strncmp(str, "group_by_node_name", 18)) return GROUP_BY_NODE_NAME; return IOPOLICY_UNDEF; } int get_pgpolicy_name(char * buff, int len, int id) { char * s; switch (id) { case FAILOVER: s = "failover"; break; case MULTIBUS: s = "multibus"; break; case GROUP_BY_SERIAL: s = "group_by_serial"; break; case GROUP_BY_PRIO: s = "group_by_prio"; break; case GROUP_BY_NODE_NAME: s = "group_by_node_name"; break; default: s = "undefined"; break; } return snprintf(buff, POLICY_NAME_SIZE, "%s", s); } void sort_pathgroups (struct multipath *mp) { int i, j; struct pathgroup * pgp1, * pgp2; if (!mp->pg) return; vector_foreach_slot(mp->pg, pgp1, i) { path_group_prio_update(pgp1); for (j = i - 1; j >= 0; j--) { pgp2 = VECTOR_SLOT(mp->pg, j); if (!pgp2) continue; if (pgp2->priority > pgp1->priority || (pgp2->priority == pgp1->priority && pgp2->enabled_paths >= pgp1->enabled_paths)) { vector_move_up(mp->pg, i, j + 1); break; } } if (j < 0 && i != 0) vector_move_up(mp->pg, i, 0); } } /* * One path group per unique tgt_node_name present in the path vector */ int group_by_node_name(struct multipath * mp) { int i, j; int * bitmap; struct path * pp; struct pathgroup * pgp; struct path * pp2; if (!mp->pg) mp->pg = vector_alloc(); if (!mp->pg) return 1; /* init the bitmap */ bitmap = (int *)MALLOC(VECTOR_SIZE(mp->paths) * sizeof (int)); if (!bitmap) goto out; for (i = 0; i < VECTOR_SIZE(mp->paths); i++) { if (bitmap[i]) continue; pp = VECTOR_SLOT(mp->paths, i); /* here, we really got a new pg */ pgp = alloc_pathgroup(); if (!pgp) goto out1; if (store_pathgroup(mp->pg, pgp)) goto out2; /* feed the first path */ if (store_path(pgp->paths, pp)) goto out2; bitmap[i] = 1; for (j = i + 1; j < VECTOR_SIZE(mp->paths); j++) { if (bitmap[j]) continue; pp2 = VECTOR_SLOT(mp->paths, j); if (!strncmp(pp->tgt_node_name, pp2->tgt_node_name, NODE_NAME_SIZE)) { if (store_path(pgp->paths, pp2)) goto out2; bitmap[j] = 1; } } } FREE(bitmap); sort_pathgroups(mp); free_pathvec(mp->paths, KEEP_PATHS); mp->paths = NULL; return 0; out2: free_pathgroup(pgp, KEEP_PATHS); out1: FREE(bitmap); out: free_pgvec(mp->pg, KEEP_PATHS); mp->pg = NULL; return 1; } /* * One path group per unique serial number present in the path vector */ int group_by_serial(struct multipath * mp) { int i, j; int * bitmap; struct path * pp; struct pathgroup * pgp; struct path * pp2; if (!mp->pg) mp->pg = vector_alloc(); if (!mp->pg) return 1; /* init the bitmap */ bitmap = (int *)MALLOC(VECTOR_SIZE(mp->paths) * sizeof (int)); if (!bitmap) goto out; for (i = 0; i < VECTOR_SIZE(mp->paths); i++) { if (bitmap[i]) continue; pp = VECTOR_SLOT(mp->paths, i); /* here, we really got a new pg */ pgp = alloc_pathgroup(); if (!pgp) goto out1; if (store_pathgroup(mp->pg, pgp)) goto out2; /* feed the first path */ if (store_path(pgp->paths, pp)) goto out2; bitmap[i] = 1; for (j = i + 1; j < VECTOR_SIZE(mp->paths); j++) { if (bitmap[j]) continue; pp2 = VECTOR_SLOT(mp->paths, j); if (0 == strcmp(pp->serial, pp2->serial)) { if (store_path(pgp->paths, pp2)) goto out2; bitmap[j] = 1; } } } FREE(bitmap); sort_pathgroups(mp); free_pathvec(mp->paths, KEEP_PATHS); mp->paths = NULL; return 0; out2: free_pathgroup(pgp, KEEP_PATHS); out1: FREE(bitmap); out: free_pgvec(mp->pg, KEEP_PATHS); mp->pg = NULL; return 1; } int one_path_per_group(struct multipath *mp) { int i; struct path * pp; struct pathgroup * pgp; if (!mp->pg) mp->pg = vector_alloc(); if (!mp->pg) return 1; for (i = 0; i < VECTOR_SIZE(mp->paths); i++) { pp = VECTOR_SLOT(mp->paths, i); pgp = alloc_pathgroup(); if (!pgp) goto out; if (store_pathgroup(mp->pg, pgp)) goto out1; if (store_path(pgp->paths, pp)) goto out1; } sort_pathgroups(mp); free_pathvec(mp->paths, KEEP_PATHS); mp->paths = NULL; return 0; out1: free_pathgroup(pgp, KEEP_PATHS); out: free_pgvec(mp->pg, KEEP_PATHS); mp->pg = NULL; return 1; } int one_group(struct multipath *mp) /* aka multibus */ { struct pathgroup * pgp; if (VECTOR_SIZE(mp->paths) < 0) return 0; if (!mp->pg) mp->pg = vector_alloc(); if (!mp->pg) return 1; if (VECTOR_SIZE(mp->paths) > 0) { pgp = alloc_pathgroup(); if (!pgp) goto out; vector_free(pgp->paths); if (store_pathgroup(mp->pg, pgp)) goto out1; pgp->paths = mp->paths; mp->paths = NULL; } return 0; out1: free_pathgroup(pgp, KEEP_PATHS); out: free_pgvec(mp->pg, KEEP_PATHS); mp->pg = NULL; return 1; } int group_by_prio(struct multipath *mp) { int i; unsigned int prio; struct path * pp; struct pathgroup * pgp; vector pathvec = NULL; if (!mp->pg) mp->pg = vector_alloc(); if (!mp->pg) return 1; pathvec = vector_alloc(); if (!pathvec) goto out; vector_foreach_slot(mp->paths, pp, i) { if (!vector_alloc_slot(pathvec)) goto out1; vector_set_slot(pathvec, pp); } while (VECTOR_SIZE(pathvec) > 0) { pp = VECTOR_SLOT(pathvec, 0); prio = pp->priority; /* * Find the position to insert the new path group. All groups * are ordered by the priority value (higher value first). */ vector_foreach_slot(mp->pg, pgp, i) { pp = VECTOR_SLOT(pgp->paths, 0); if (prio > pp->priority) break; } /* * Initialize the new path group. */ pgp = alloc_pathgroup(); if (!pgp) goto out1; if (store_path(pgp->paths, VECTOR_SLOT(pathvec, 0))) goto out2; vector_del_slot(pathvec, 0); /* * Store the new path group into the vector. */ if (i < VECTOR_SIZE(mp->pg)) { if (!vector_insert_slot(mp->pg, i, pgp)) goto out2; } else { if (store_pathgroup(mp->pg, pgp)) goto out2; } /* * add the other paths with the same prio */ vector_foreach_slot(pathvec, pp, i) { if (pp->priority == prio) { if (store_path(pgp->paths, pp)) goto out2; vector_del_slot(pathvec, i); i--; } } } free_pathvec(pathvec, KEEP_PATHS); free_pathvec(mp->paths, KEEP_PATHS); mp->paths = NULL; return 0; out2: free_pathgroup(pgp, KEEP_PATHS); out1: free_pathvec(pathvec, KEEP_PATHS); out: free_pgvec(mp->pg, KEEP_PATHS); mp->pg = NULL; return 1; } multipath-tools-0.7.4/libmultipath/pgpolicies.h000066400000000000000000000011111320314174000216610ustar00rootroot00000000000000#ifndef _PGPOLICIES_H #define _PGPOLICIES_H #if 0 #ifndef _MAIN_H #include "main.h" #endif #endif #define POLICY_NAME_SIZE 32 /* Storage controllers capabilities */ enum iopolicies { IOPOLICY_UNDEF, FAILOVER, MULTIBUS, GROUP_BY_SERIAL, GROUP_BY_PRIO, GROUP_BY_NODE_NAME }; int get_pgpolicy_id(char *); int get_pgpolicy_name (char *, int, int); /* * policies */ int one_path_per_group(struct multipath *); int one_group(struct multipath *); int group_by_serial(struct multipath *); int group_by_prio(struct multipath *); int group_by_node_name(struct multipath *); #endif multipath-tools-0.7.4/libmultipath/print.c000066400000000000000000001212271320314174000206650ustar00rootroot00000000000000/* * Copyright (c) 2005 Christophe Varoqui */ #include #include #include #include #include #include #include #include #include #include #include "checkers.h" #include "vector.h" #include "structs.h" #include "structs_vec.h" #include "dmparser.h" #include "config.h" #include "configure.h" #include "pgpolicies.h" #include "print.h" #include "defaults.h" #include "parser.h" #include "blacklist.h" #include "switchgroup.h" #include "devmapper.h" #include "uevent.h" #include "debug.h" #define MAX(x,y) (x > y) ? x : y #define TAIL (line + len - 1 - c) #define NOPAD s = c #define PAD(x) \ do { \ while ((int)(c - s) < (x) && (c < (line + len - 1))) \ *c++ = ' '; \ s = c; \ } while (0) static char * __endline(char *line, size_t len, char *c) { if (c > line) { if (c >= line + len) c = line + len - 1; *(c - 1) = '\n'; *c = '\0'; } return c; } #define PRINT(var, size, format, args...) \ do { \ fwd = snprintf(var, size, format, ##args); \ c += (fwd >= size) ? size : fwd; \ } while (0) /* * information printing helpers */ static int snprint_str (char * buff, size_t len, const char * str) { return snprintf(buff, len, "%s", str); } static int snprint_int (char * buff, size_t len, int val) { return snprintf(buff, len, "%i", val); } static int snprint_uint (char * buff, size_t len, unsigned int val) { return snprintf(buff, len, "%u", val); } static int snprint_size (char * buff, size_t len, unsigned long long size) { float s = (float)(size >> 1); /* start with KB */ char units[] = {'K','M','G','T','P'}; char *u = units; while (s >= 1024 && *u != 'P') { s = s / 1024; u++; } return snprintf(buff, len, "%.*f%c", s < 10, s, *u); } /* * multipath info printing functions */ static int snprint_name (char * buff, size_t len, struct multipath * mpp) { if (mpp->alias) return snprintf(buff, len, "%s", mpp->alias); else return snprintf(buff, len, "%s", mpp->wwid); } static int snprint_sysfs (char * buff, size_t len, struct multipath * mpp) { if (mpp->dmi) return snprintf(buff, len, "dm-%i", mpp->dmi->minor); else return snprintf(buff, len, "undef"); } static int snprint_ro (char * buff, size_t len, struct multipath * mpp) { if (!mpp->dmi) return snprintf(buff, len, "undef"); if (mpp->dmi->read_only) return snprintf(buff, len, "ro"); else return snprintf(buff, len, "rw"); } static int snprint_progress (char * buff, size_t len, int cur, int total) { char * c = buff; char * end = buff + len; if (total > 0) { int i = PROGRESS_LEN * cur / total; int j = PROGRESS_LEN - i; while (i-- > 0) { c += snprintf(c, len, "X"); if ((len = (end - c)) <= 1) goto out; } while (j-- > 0) { c += snprintf(c, len, "."); if ((len = (end - c)) <= 1) goto out; } } c += snprintf(c, len, " %i/%i", cur, total); out: buff[c - buff + 1] = '\0'; return (c - buff + 1); } static int snprint_failback (char * buff, size_t len, struct multipath * mpp) { if (mpp->pgfailback == -FAILBACK_IMMEDIATE) return snprintf(buff, len, "immediate"); if (mpp->pgfailback == -FAILBACK_FOLLOWOVER) return snprintf(buff, len, "followover"); if (!mpp->failback_tick) return snprintf(buff, len, "-"); else return snprint_progress(buff, len, mpp->failback_tick, mpp->pgfailback); } static int snprint_queueing (char * buff, size_t len, struct multipath * mpp) { if (mpp->no_path_retry == NO_PATH_RETRY_FAIL) return snprintf(buff, len, "off"); else if (mpp->no_path_retry == NO_PATH_RETRY_QUEUE) return snprintf(buff, len, "on"); else if (mpp->no_path_retry == NO_PATH_RETRY_UNDEF) return snprintf(buff, len, "-"); else if (mpp->no_path_retry > 0) { if (mpp->retry_tick > 0) return snprintf(buff, len, "%i sec", mpp->retry_tick); else if (mpp->retry_tick == 0 && mpp->nr_active > 0) return snprintf(buff, len, "%i chk", mpp->no_path_retry); else return snprintf(buff, len, "off"); } return 0; } static int snprint_nb_paths (char * buff, size_t len, struct multipath * mpp) { return snprint_int(buff, len, mpp->nr_active); } static int snprint_dm_map_state (char * buff, size_t len, struct multipath * mpp) { if (mpp->dmi && mpp->dmi->suspended) return snprintf(buff, len, "suspend"); else return snprintf(buff, len, "active"); } static int snprint_multipath_size (char * buff, size_t len, struct multipath * mpp) { return snprint_size(buff, len, mpp->size); } static int snprint_features (char * buff, size_t len, struct multipath * mpp) { return snprint_str(buff, len, mpp->features); } static int snprint_hwhandler (char * buff, size_t len, struct multipath * mpp) { return snprint_str(buff, len, mpp->hwhandler); } static int snprint_path_faults (char * buff, size_t len, struct multipath * mpp) { return snprint_uint(buff, len, mpp->stat_path_failures); } static int snprint_switch_grp (char * buff, size_t len, struct multipath * mpp) { return snprint_uint(buff, len, mpp->stat_switchgroup); } static int snprint_map_loads (char * buff, size_t len, struct multipath * mpp) { return snprint_uint(buff, len, mpp->stat_map_loads); } static int snprint_total_q_time (char * buff, size_t len, struct multipath * mpp) { return snprint_uint(buff, len, mpp->stat_total_queueing_time); } static int snprint_q_timeouts (char * buff, size_t len, struct multipath * mpp) { return snprint_uint(buff, len, mpp->stat_queueing_timeouts); } static int snprint_map_failures (char * buff, size_t len, struct multipath * mpp) { return snprint_uint(buff, len, mpp->stat_map_failures); } static int snprint_multipath_uuid (char * buff, size_t len, struct multipath * mpp) { return snprint_str(buff, len, mpp->wwid); } static int snprint_multipath_vpr (char * buff, size_t len, struct multipath * mpp) { struct pathgroup * pgp; struct path * pp; int i, j; vector_foreach_slot(mpp->pg, pgp, i) { if (!pgp) continue; vector_foreach_slot(pgp->paths, pp, j) { if (strlen(pp->vendor_id) && strlen(pp->product_id)) return snprintf(buff, len, "%s,%s", pp->vendor_id, pp->product_id); } } return snprintf(buff, len, "##,##"); } static int snprint_multipath_vend (char * buff, size_t len, struct multipath * mpp) { struct pathgroup * pgp; struct path * pp; int i, j; vector_foreach_slot(mpp->pg, pgp, i) { if (!pgp) continue; vector_foreach_slot(pgp->paths, pp, j) { if (strlen(pp->vendor_id)) return snprintf(buff, len, "%s", pp->vendor_id); } } return snprintf(buff, len, "##"); } static int snprint_multipath_prod (char * buff, size_t len, struct multipath * mpp) { struct pathgroup * pgp; struct path * pp; int i, j; vector_foreach_slot(mpp->pg, pgp, i) { if (!pgp) continue; vector_foreach_slot(pgp->paths, pp, j) { if (strlen(pp->product_id)) return snprintf(buff, len, "%s", pp->product_id); } } return snprintf(buff, len, "##"); } static int snprint_multipath_rev (char * buff, size_t len, struct multipath * mpp) { struct pathgroup * pgp; struct path * pp; int i, j; vector_foreach_slot(mpp->pg, pgp, i) { if (!pgp) continue; vector_foreach_slot(pgp->paths, pp, j) { if (strlen(pp->rev)) return snprintf(buff, len, "%s", pp->rev); } } return snprintf(buff, len, "##"); } static int snprint_action (char * buff, size_t len, struct multipath * mpp) { switch (mpp->action) { case ACT_REJECT: return snprint_str(buff, len, ACT_REJECT_STR); case ACT_RENAME: return snprint_str(buff, len, ACT_RENAME_STR); case ACT_RELOAD: return snprint_str(buff, len, ACT_RELOAD_STR); case ACT_CREATE: return snprint_str(buff, len, ACT_CREATE_STR); case ACT_SWITCHPG: return snprint_str(buff, len, ACT_SWITCHPG_STR); default: return 0; } } /* * path info printing functions */ static int snprint_path_uuid (char * buff, size_t len, struct path * pp) { return snprint_str(buff, len, pp->wwid); } static int snprint_hcil (char * buff, size_t len, struct path * pp) { if (!pp || pp->sg_id.host_no < 0) return snprintf(buff, len, "#:#:#:#"); return snprintf(buff, len, "%i:%i:%i:%i", pp->sg_id.host_no, pp->sg_id.channel, pp->sg_id.scsi_id, pp->sg_id.lun); } static int snprint_dev (char * buff, size_t len, struct path * pp) { if (!pp || !strlen(pp->dev)) return snprintf(buff, len, "-"); else return snprint_str(buff, len, pp->dev); } static int snprint_dev_t (char * buff, size_t len, struct path * pp) { if (!pp || !strlen(pp->dev)) return snprintf(buff, len, "#:#"); else return snprint_str(buff, len, pp->dev_t); } static int snprint_offline (char * buff, size_t len, struct path * pp) { if (!pp || !pp->mpp) return snprintf(buff, len, "unknown"); else if (pp->offline) return snprintf(buff, len, "offline"); else return snprintf(buff, len, "running"); } static int snprint_chk_state (char * buff, size_t len, struct path * pp) { if (!pp || !pp->mpp) return snprintf(buff, len, "undef"); switch (pp->state) { case PATH_UP: return snprintf(buff, len, "ready"); case PATH_DOWN: return snprintf(buff, len, "faulty"); case PATH_SHAKY: return snprintf(buff, len, "shaky"); case PATH_GHOST: return snprintf(buff, len, "ghost"); case PATH_PENDING: return snprintf(buff, len, "i/o pending"); case PATH_TIMEOUT: return snprintf(buff, len, "i/o timeout"); case PATH_DELAYED: return snprintf(buff, len, "delayed"); default: return snprintf(buff, len, "undef"); } } static int snprint_dm_path_state (char * buff, size_t len, struct path * pp) { if (!pp) return snprintf(buff, len, "undef"); switch (pp->dmstate) { case PSTATE_ACTIVE: return snprintf(buff, len, "active"); case PSTATE_FAILED: return snprintf(buff, len, "failed"); default: return snprintf(buff, len, "undef"); } } static int snprint_vpr (char * buff, size_t len, struct path * pp) { return snprintf(buff, len, "%s,%s", pp->vendor_id, pp->product_id); } static int snprint_next_check (char * buff, size_t len, struct path * pp) { if (!pp || !pp->mpp) return snprintf(buff, len, "orphan"); return snprint_progress(buff, len, pp->tick, pp->checkint); } static int snprint_pri (char * buff, size_t len, struct path * pp) { return snprint_int(buff, len, pp ? pp->priority : -1); } static int snprint_pg_selector (char * buff, size_t len, struct pathgroup * pgp) { return snprint_str(buff, len, pgp->selector); } static int snprint_pg_pri (char * buff, size_t len, struct pathgroup * pgp) { /* * path group priority is not updated for every path prio change, * but only on switch group code path. * * Printing is another reason to update. */ path_group_prio_update(pgp); return snprint_int(buff, len, pgp->priority); } static int snprint_pg_state (char * buff, size_t len, struct pathgroup * pgp) { switch (pgp->status) { case PGSTATE_ENABLED: return snprintf(buff, len, "enabled"); case PGSTATE_DISABLED: return snprintf(buff, len, "disabled"); case PGSTATE_ACTIVE: return snprintf(buff, len, "active"); default: return snprintf(buff, len, "undef"); } } static int snprint_path_size (char * buff, size_t len, struct path * pp) { return snprint_size(buff, len, pp->size); } int snprint_path_serial (char * buff, size_t len, struct path * pp) { return snprint_str(buff, len, pp->serial); } static int snprint_path_mpp (char * buff, size_t len, struct path * pp) { if (!pp->mpp) return snprintf(buff, len, "[orphan]"); if (!pp->mpp->alias) return snprintf(buff, len, "[unknown]"); return snprint_str(buff, len, pp->mpp->alias); } static int snprint_host_attr (char * buff, size_t len, struct path * pp, char *attr) { struct udev_device *host_dev = NULL; char host_id[32]; const char *value = NULL; int ret; if (pp->sg_id.proto_id != SCSI_PROTOCOL_FCP) return snprintf(buff, len, "[undef]"); sprintf(host_id, "host%d", pp->sg_id.host_no); host_dev = udev_device_new_from_subsystem_sysname(udev, "fc_host", host_id); if (!host_dev) { condlog(1, "%s: No fc_host device for '%s'", pp->dev, host_id); goto out; } value = udev_device_get_sysattr_value(host_dev, attr); if (value) ret = snprint_str(buff, len, value); udev_device_unref(host_dev); out: if (!value) ret = snprintf(buff, len, "[unknown]"); return ret; } int snprint_host_wwnn (char * buff, size_t len, struct path * pp) { return snprint_host_attr(buff, len, pp, "node_name"); } int snprint_host_wwpn (char * buff, size_t len, struct path * pp) { return snprint_host_attr(buff, len, pp, "port_name"); } int snprint_tgt_wwpn (char * buff, size_t len, struct path * pp) { struct udev_device *rport_dev = NULL; char rport_id[32]; const char *value = NULL; int ret; if (pp->sg_id.proto_id != SCSI_PROTOCOL_FCP) return snprintf(buff, len, "[undef]"); sprintf(rport_id, "rport-%d:%d-%d", pp->sg_id.host_no, pp->sg_id.channel, pp->sg_id.transport_id); rport_dev = udev_device_new_from_subsystem_sysname(udev, "fc_remote_ports", rport_id); if (!rport_dev) { condlog(1, "%s: No fc_remote_port device for '%s'", pp->dev, rport_id); goto out; } value = udev_device_get_sysattr_value(rport_dev, "port_name"); if (value) ret = snprint_str(buff, len, value); udev_device_unref(rport_dev); out: if (!value) ret = snprintf(buff, len, "[unknown]"); return ret; } int snprint_tgt_wwnn (char * buff, size_t len, struct path * pp) { if (pp->tgt_node_name[0] == '\0') return snprintf(buff, len, "[undef]"); return snprint_str(buff, len, pp->tgt_node_name); } static int snprint_host_adapter (char * buff, size_t len, struct path * pp) { char adapter[SLOT_NAME_SIZE]; if (sysfs_get_host_adapter_name(pp, adapter)) return snprintf(buff, len, "[undef]"); return snprint_str(buff, len, adapter); } static int snprint_path_checker (char * buff, size_t len, struct path * pp) { struct checker * c = &pp->checker; return snprint_str(buff, len, c->name); } struct multipath_data mpd[] = { {'n', "name", 0, snprint_name}, {'w', "uuid", 0, snprint_multipath_uuid}, {'d', "sysfs", 0, snprint_sysfs}, {'F', "failback", 0, snprint_failback}, {'Q', "queueing", 0, snprint_queueing}, {'N', "paths", 0, snprint_nb_paths}, {'r', "write_prot", 0, snprint_ro}, {'t', "dm-st", 0, snprint_dm_map_state}, {'S', "size", 0, snprint_multipath_size}, {'f', "features", 0, snprint_features}, {'x', "failures", 0, snprint_map_failures}, {'h', "hwhandler", 0, snprint_hwhandler}, {'A', "action", 0, snprint_action}, {'0', "path_faults", 0, snprint_path_faults}, {'1', "switch_grp", 0, snprint_switch_grp}, {'2', "map_loads", 0, snprint_map_loads}, {'3', "total_q_time", 0, snprint_total_q_time}, {'4', "q_timeouts", 0, snprint_q_timeouts}, {'s', "vend/prod/rev", 0, snprint_multipath_vpr}, {'v', "vend", 0, snprint_multipath_vend}, {'p', "prod", 0, snprint_multipath_prod}, {'e', "rev", 0, snprint_multipath_rev}, {0, NULL, 0 , NULL} }; struct path_data pd[] = { {'w', "uuid", 0, snprint_path_uuid}, {'i', "hcil", 0, snprint_hcil}, {'d', "dev", 0, snprint_dev}, {'D', "dev_t", 0, snprint_dev_t}, {'t', "dm_st", 0, snprint_dm_path_state}, {'o', "dev_st", 0, snprint_offline}, {'T', "chk_st", 0, snprint_chk_state}, {'s', "vend/prod/rev", 0, snprint_vpr}, {'c', "checker", 0, snprint_path_checker}, {'C', "next_check", 0, snprint_next_check}, {'p', "pri", 0, snprint_pri}, {'S', "size", 0, snprint_path_size}, {'z', "serial", 0, snprint_path_serial}, {'m', "multipath", 0, snprint_path_mpp}, {'N', "host WWNN", 0, snprint_host_wwnn}, {'n', "target WWNN", 0, snprint_tgt_wwnn}, {'R', "host WWPN", 0, snprint_host_wwpn}, {'r', "target WWPN", 0, snprint_tgt_wwpn}, {'a', "host adapter", 0, snprint_host_adapter}, {0, NULL, 0 , NULL} }; struct pathgroup_data pgd[] = { {'s', "selector", 0, snprint_pg_selector}, {'p', "pri", 0, snprint_pg_pri}, {'t', "dm_st", 0, snprint_pg_state}, {0, NULL, 0 , NULL} }; int snprint_wildcards (char * buff, int len) { int i, fwd = 0; fwd += snprintf(buff + fwd, len - fwd, "multipath format wildcards:\n"); for (i = 0; mpd[i].header; i++) fwd += snprintf(buff + fwd, len - fwd, "%%%c %s\n", mpd[i].wildcard, mpd[i].header); fwd += snprintf(buff + fwd, len - fwd, "\npath format wildcards:\n"); for (i = 0; pd[i].header; i++) fwd += snprintf(buff + fwd, len - fwd, "%%%c %s\n", pd[i].wildcard, pd[i].header); fwd += snprintf(buff + fwd, len - fwd, "\npathgroup format wildcards:\n"); for (i = 0; pgd[i].header; i++) fwd += snprintf(buff + fwd, len - fwd, "%%%c %s\n", pgd[i].wildcard, pgd[i].header); return fwd; } void get_path_layout (vector pathvec, int header) { int i, j; char buff[MAX_FIELD_LEN]; struct path * pp; for (j = 0; pd[j].header; j++) { if (header) pd[j].width = strlen(pd[j].header); else pd[j].width = 0; vector_foreach_slot (pathvec, pp, i) { pd[j].snprint(buff, MAX_FIELD_LEN, pp); pd[j].width = MAX(pd[j].width, strlen(buff)); } } } static void reset_multipath_layout (void) { int i; for (i = 0; mpd[i].header; i++) mpd[i].width = 0; } void get_multipath_layout (vector mpvec, int header) { int i, j; char buff[MAX_FIELD_LEN]; struct multipath * mpp; for (j = 0; mpd[j].header; j++) { if (header) mpd[j].width = strlen(mpd[j].header); else mpd[j].width = 0; vector_foreach_slot (mpvec, mpp, i) { mpd[j].snprint(buff, MAX_FIELD_LEN, mpp); mpd[j].width = MAX(mpd[j].width, strlen(buff)); } } } static struct multipath_data * mpd_lookup(char wildcard) { int i; for (i = 0; mpd[i].header; i++) if (mpd[i].wildcard == wildcard) return &mpd[i]; return NULL; } static struct path_data * pd_lookup(char wildcard) { int i; for (i = 0; pd[i].header; i++) if (pd[i].wildcard == wildcard) return &pd[i]; return NULL; } static struct pathgroup_data * pgd_lookup(char wildcard) { int i; for (i = 0; pgd[i].header; i++) if (pgd[i].wildcard == wildcard) return &pgd[i]; return NULL; } int snprint_multipath_header (char * line, int len, char * format) { char * c = line; /* line cursor */ char * s = line; /* for padding */ char * f = format; /* format string cursor */ int fwd; struct multipath_data * data; do { if (TAIL <= 0) break; if (*f != '%') { *c++ = *f; NOPAD; continue; } f++; if (!(data = mpd_lookup(*f))) continue; /* unknown wildcard */ PRINT(c, TAIL, "%s", data->header); PAD(data->width); } while (*f++); __endline(line, len, c); return (c - line); } int snprint_multipath (char * line, int len, char * format, struct multipath * mpp, int pad) { char * c = line; /* line cursor */ char * s = line; /* for padding */ char * f = format; /* format string cursor */ int fwd; struct multipath_data * data; char buff[MAX_FIELD_LEN] = {}; do { if (TAIL <= 0) break; if (*f != '%') { *c++ = *f; NOPAD; continue; } f++; if (!(data = mpd_lookup(*f))) continue; data->snprint(buff, MAX_FIELD_LEN, mpp); PRINT(c, TAIL, "%s", buff); if (pad) PAD(data->width); buff[0] = '\0'; } while (*f++); __endline(line, len, c); return (c - line); } int snprint_path_header (char * line, int len, char * format) { char * c = line; /* line cursor */ char * s = line; /* for padding */ char * f = format; /* format string cursor */ int fwd; struct path_data * data; do { if (TAIL <= 0) break; if (*f != '%') { *c++ = *f; NOPAD; continue; } f++; if (!(data = pd_lookup(*f))) continue; /* unknown wildcard */ PRINT(c, TAIL, "%s", data->header); PAD(data->width); } while (*f++); __endline(line, len, c); return (c - line); } int snprint_path (char * line, int len, char * format, struct path * pp, int pad) { char * c = line; /* line cursor */ char * s = line; /* for padding */ char * f = format; /* format string cursor */ int fwd; struct path_data * data; char buff[MAX_FIELD_LEN]; do { if (TAIL <= 0) break; if (*f != '%') { *c++ = *f; NOPAD; continue; } f++; if (!(data = pd_lookup(*f))) continue; data->snprint(buff, MAX_FIELD_LEN, pp); PRINT(c, TAIL, "%s", buff); if (pad) PAD(data->width); } while (*f++); __endline(line, len, c); return (c - line); } int snprint_pathgroup (char * line, int len, char * format, struct pathgroup * pgp) { char * c = line; /* line cursor */ char * s = line; /* for padding */ char * f = format; /* format string cursor */ int fwd; struct pathgroup_data * data; char buff[MAX_FIELD_LEN]; do { if (TAIL <= 0) break; if (*f != '%') { *c++ = *f; NOPAD; continue; } f++; if (!(data = pgd_lookup(*f))) continue; data->snprint(buff, MAX_FIELD_LEN, pgp); PRINT(c, TAIL, "%s", buff); PAD(data->width); } while (*f++); __endline(line, len, c); return (c - line); } void print_multipath_topology(struct multipath *mpp, int verbosity) { int resize; char *buff = NULL; char *old = NULL; int len, maxlen = MAX_LINE_LEN * MAX_LINES; buff = MALLOC(maxlen); do { if (!buff) { if (old) FREE(old); condlog(0, "couldn't allocate memory for list: %s\n", strerror(errno)); return; } len = snprint_multipath_topology(buff, maxlen, mpp, verbosity); resize = (len == maxlen - 1); if (resize) { maxlen *= 2; old = buff; buff = REALLOC(buff, maxlen); } } while (resize); printf("%s", buff); FREE(buff); } int snprint_multipath_topology(char *buff, int len, struct multipath *mpp, int verbosity) { int j, i, fwd = 0; struct path * pp = NULL; struct pathgroup * pgp = NULL; char style[64]; char * c = style; char fmt[64]; char * f; if (verbosity <= 0) return fwd; reset_multipath_layout(); if (verbosity == 1) return snprint_multipath(buff, len, "%n", mpp, 1); if(isatty(1)) c += sprintf(c, "%c[%dm", 0x1B, 1); /* bold on */ if (verbosity > 1 && mpp->action != ACT_NOTHING && mpp->action != ACT_UNDEF && mpp->action != ACT_IMPOSSIBLE) c += sprintf(c, "%%A: "); c += sprintf(c, "%%n"); if (strncmp(mpp->alias, mpp->wwid, WWID_SIZE)) c += sprintf(c, " (%%w)"); c += sprintf(c, " %%d %%s"); if(isatty(1)) c += sprintf(c, "%c[%dm", 0x1B, 0); /* bold off */ fwd += snprint_multipath(buff + fwd, len - fwd, style, mpp, 1); if (fwd >= len) return len; fwd += snprint_multipath(buff + fwd, len - fwd, PRINT_MAP_PROPS, mpp, 1); if (fwd >= len) return len; if (!mpp->pg) return fwd; vector_foreach_slot (mpp->pg, pgp, j) { f=fmt; pgp->selector = mpp->selector; /* hack */ if (j + 1 < VECTOR_SIZE(mpp->pg)) { strcpy(f, "|-+- " PRINT_PG_INDENT); } else strcpy(f, "`-+- " PRINT_PG_INDENT); fwd += snprint_pathgroup(buff + fwd, len - fwd, fmt, pgp); if (fwd >= len) return len; vector_foreach_slot (pgp->paths, pp, i) { f=fmt; if (*f != '|') *f=' '; f++; if (i + 1 < VECTOR_SIZE(pgp->paths)) strcpy(f, " |- " PRINT_PATH_INDENT); else strcpy(f, " `- " PRINT_PATH_INDENT); fwd += snprint_path(buff + fwd, len - fwd, fmt, pp, 1); if (fwd >= len) return len; } } return fwd; } static int snprint_json (char * buff, int len, int indent, char *json_str) { int fwd = 0, i; for (i = 0; i < indent; i++) { fwd += snprintf(buff + fwd, len - fwd, PRINT_JSON_INDENT); if (fwd >= len) return fwd; } fwd += snprintf(buff + fwd, len - fwd, "%s", json_str); return fwd; } static int snprint_json_header (char * buff, int len) { int fwd = 0; fwd += snprint_json(buff, len, 0, PRINT_JSON_START_ELEM); if (fwd >= len) return fwd; fwd += snprintf(buff + fwd, len - fwd, PRINT_JSON_START_VERSION, PRINT_JSON_MAJOR_VERSION, PRINT_JSON_MINOR_VERSION); return fwd; } static int snprint_json_elem_footer (char * buff, int len, int indent, int last) { int fwd = 0, i; for (i = 0; i < indent; i++) { fwd += snprintf(buff + fwd, len - fwd, PRINT_JSON_INDENT); if (fwd >= len) return fwd; } if (last == 1) fwd += snprintf(buff + fwd, len - fwd, "%s", PRINT_JSON_END_LAST_ELEM); else fwd += snprintf(buff + fwd, len - fwd, "%s", PRINT_JSON_END_ELEM); return fwd; } static int snprint_multipath_fields_json (char * buff, int len, struct multipath * mpp, int last) { int i, j, fwd = 0; struct path *pp; struct pathgroup *pgp; fwd += snprint_multipath(buff, len, PRINT_JSON_MAP, mpp, 0); if (fwd >= len) return fwd; fwd += snprint_json(buff + fwd, len - fwd, 2, PRINT_JSON_START_GROUPS); if (fwd >= len) return fwd; vector_foreach_slot (mpp->pg, pgp, i) { pgp->selector = mpp->selector; fwd += snprint_pathgroup(buff + fwd, len - fwd, PRINT_JSON_GROUP, pgp); if (fwd >= len) return fwd; fwd += snprintf(buff + fwd, len - fwd, PRINT_JSON_GROUP_NUM, i + 1); if (fwd >= len) return fwd; fwd += snprint_json(buff + fwd, len - fwd, 3, PRINT_JSON_START_PATHS); if (fwd >= len) return fwd; vector_foreach_slot (pgp->paths, pp, j) { fwd += snprint_path(buff + fwd, len - fwd, PRINT_JSON_PATH, pp, 0); if (fwd >= len) return fwd; fwd += snprint_json_elem_footer(buff + fwd, len - fwd, 3, j + 1 == VECTOR_SIZE(pgp->paths)); if (fwd >= len) return fwd; } fwd += snprint_json(buff + fwd, len - fwd, 0, PRINT_JSON_END_ARRAY); if (fwd >= len) return fwd; fwd += snprint_json_elem_footer(buff + fwd, len - fwd, 2, i + 1 == VECTOR_SIZE(mpp->pg)); if (fwd >= len) return fwd; } fwd += snprint_json(buff + fwd, len - fwd, 0, PRINT_JSON_END_ARRAY); if (fwd >= len) return fwd; fwd += snprint_json_elem_footer(buff + fwd, len - fwd, 1, last); return fwd; } int snprint_multipath_map_json (char * buff, int len, struct multipath * mpp, int last){ int fwd = 0; fwd += snprint_json_header(buff, len); if (fwd >= len) return len; fwd += snprint_json(buff + fwd, len - fwd, 0, PRINT_JSON_START_MAP); if (fwd >= len) return len; fwd += snprint_multipath_fields_json(buff + fwd, len - fwd, mpp, 1); if (fwd >= len) return len; fwd += snprint_json(buff + fwd, len - fwd, 0, "\n"); if (fwd >= len) return len; fwd += snprint_json(buff + fwd, len - fwd, 0, PRINT_JSON_END_LAST); if (fwd >= len) return len; return fwd; } int snprint_multipath_topology_json (char * buff, int len, struct vectors * vecs) { int i, fwd = 0; struct multipath * mpp; fwd += snprint_json_header(buff, len); if (fwd >= len) return len; fwd += snprint_json(buff + fwd, len - fwd, 1, PRINT_JSON_START_MAPS); if (fwd >= len) return len; vector_foreach_slot(vecs->mpvec, mpp, i) { fwd += snprint_multipath_fields_json(buff + fwd, len - fwd, mpp, i + 1 == VECTOR_SIZE(vecs->mpvec)); if (fwd >= len) return len; } fwd += snprint_json(buff + fwd, len - fwd, 0, PRINT_JSON_END_ARRAY); if (fwd >= len) return len; fwd += snprint_json(buff + fwd, len - fwd, 0, PRINT_JSON_END_LAST); if (fwd >= len) return len; return fwd; } static int snprint_hwentry (struct config *conf, char * buff, int len, struct hwentry * hwe) { int i; int fwd = 0; struct keyword * kw; struct keyword * rootkw; rootkw = find_keyword(conf->keywords, NULL, "devices"); if (!rootkw || !rootkw->sub) return 0; rootkw = find_keyword(conf->keywords, rootkw->sub, "device"); if (!rootkw) return 0; fwd += snprintf(buff + fwd, len - fwd, "\tdevice {\n"); if (fwd >= len) return len; iterate_sub_keywords(rootkw, kw, i) { fwd += snprint_keyword(buff + fwd, len - fwd, "\t\t%k %v\n", kw, hwe); if (fwd >= len) return len; } fwd += snprintf(buff + fwd, len - fwd, "\t}\n"); if (fwd >= len) return len; return fwd; } int snprint_hwtable(struct config *conf, char *buff, int len, vector hwtable) { int fwd = 0; int i; struct hwentry * hwe; struct keyword * rootkw; rootkw = find_keyword(conf->keywords, NULL, "devices"); if (!rootkw) return 0; fwd += snprintf(buff + fwd, len - fwd, "devices {\n"); if (fwd >= len) return len; vector_foreach_slot (hwtable, hwe, i) { fwd += snprint_hwentry(conf, buff + fwd, len - fwd, hwe); if (fwd >= len) return len; } fwd += snprintf(buff + fwd, len - fwd, "}\n"); if (fwd >= len) return len; return fwd; } static int snprint_mpentry (struct config *conf, char * buff, int len, struct mpentry * mpe) { int i; int fwd = 0; struct keyword * kw; struct keyword * rootkw; rootkw = find_keyword(conf->keywords, NULL, "multipath"); if (!rootkw) return 0; fwd += snprintf(buff + fwd, len - fwd, "\tmultipath {\n"); if (fwd >= len) return len; iterate_sub_keywords(rootkw, kw, i) { fwd += snprint_keyword(buff + fwd, len - fwd, "\t\t%k %v\n", kw, mpe); if (fwd >= len) return len; } fwd += snprintf(buff + fwd, len - fwd, "\t}\n"); if (fwd >= len) return len; return fwd; } int snprint_mptable(struct config *conf, char *buff, int len, vector mptable) { int fwd = 0; int i; struct mpentry * mpe; struct keyword * rootkw; rootkw = find_keyword(conf->keywords, NULL, "multipaths"); if (!rootkw) return 0; fwd += snprintf(buff + fwd, len - fwd, "multipaths {\n"); if (fwd >= len) return len; vector_foreach_slot (mptable, mpe, i) { fwd += snprint_mpentry(conf, buff + fwd, len - fwd, mpe); if (fwd >= len) return len; } fwd += snprintf(buff + fwd, len - fwd, "}\n"); if (fwd >= len) return len; return fwd; } int snprint_overrides(struct config *conf, char * buff, int len, struct hwentry *overrides) { int fwd = 0; int i; struct keyword *rootkw; struct keyword *kw; rootkw = find_keyword(conf->keywords, NULL, "overrides"); if (!rootkw) return 0; fwd += snprintf(buff + fwd, len - fwd, "overrides {\n"); if (fwd >= len) return len; if (!overrides) goto out; iterate_sub_keywords(rootkw, kw, i) { fwd += snprint_keyword(buff + fwd, len - fwd, "\t%k %v\n", kw, NULL); if (fwd >= len) return len; } out: fwd += snprintf(buff + fwd, len - fwd, "}\n"); if (fwd >= len) return len; return fwd; } int snprint_defaults(struct config *conf, char *buff, int len) { int fwd = 0; int i; struct keyword *rootkw; struct keyword *kw; rootkw = find_keyword(conf->keywords, NULL, "defaults"); if (!rootkw) return 0; fwd += snprintf(buff + fwd, len - fwd, "defaults {\n"); if (fwd >= len) return len; iterate_sub_keywords(rootkw, kw, i) { fwd += snprint_keyword(buff + fwd, len - fwd, "\t%k %v\n", kw, NULL); if (fwd >= len) return len; } fwd += snprintf(buff + fwd, len - fwd, "}\n"); if (fwd >= len) return len; return fwd; } static int snprint_blacklist_group (char *buff, int len, int *fwd, vector *vec) { int threshold = MAX_LINE_LEN; struct blentry * ble; int pos; int i; pos = *fwd; if (!VECTOR_SIZE(*vec)) { if ((len - pos - threshold) <= 0) return 0; pos += snprintf(buff + pos, len - pos, " \n"); } else vector_foreach_slot (*vec, ble, i) { if ((len - pos - threshold) <= 0) return 0; if (ble->origin == ORIGIN_CONFIG) pos += snprintf(buff + pos, len - pos, " (config file rule) "); else if (ble->origin == ORIGIN_DEFAULT) pos += snprintf(buff + pos, len - pos, " (default rule) "); pos += snprintf(buff + pos, len - pos, "%s\n", ble->str); } *fwd = pos; return pos; } static int snprint_blacklist_devgroup (char *buff, int len, int *fwd, vector *vec) { int threshold = MAX_LINE_LEN; struct blentry_device * bled; int pos; int i; pos = *fwd; if (!VECTOR_SIZE(*vec)) { if ((len - pos - threshold) <= 0) return 0; pos += snprintf(buff + pos, len - pos, " \n"); } else vector_foreach_slot (*vec, bled, i) { if ((len - pos - threshold) <= 0) return 0; if (bled->origin == ORIGIN_CONFIG) pos += snprintf(buff + pos, len - pos, " (config file rule) "); else if (bled->origin == ORIGIN_DEFAULT) pos += snprintf(buff + pos, len - pos, " (default rule) "); pos += snprintf(buff + pos, len - pos, "%s:%s\n", bled->vendor, bled->product); } *fwd = pos; return pos; } int snprint_blacklist_report(struct config *conf, char *buff, int len) { int threshold = MAX_LINE_LEN; int fwd = 0; if ((len - fwd - threshold) <= 0) return len; fwd += snprintf(buff + fwd, len - fwd, "device node rules:\n" "- blacklist:\n"); if (!snprint_blacklist_group(buff, len, &fwd, &conf->blist_devnode)) return len; if ((len - fwd - threshold) <= 0) return len; fwd += snprintf(buff + fwd, len - fwd, "- exceptions:\n"); if (snprint_blacklist_group(buff, len, &fwd, &conf->elist_devnode) == 0) return len; if ((len - fwd - threshold) <= 0) return len; fwd += snprintf(buff + fwd, len - fwd, "udev property rules:\n" "- blacklist:\n"); if (!snprint_blacklist_group(buff, len, &fwd, &conf->blist_property)) return len; if ((len - fwd - threshold) <= 0) return len; fwd += snprintf(buff + fwd, len - fwd, "- exceptions:\n"); if (snprint_blacklist_group(buff, len, &fwd, &conf->elist_property) == 0) return len; if ((len - fwd - threshold) <= 0) return len; fwd += snprintf(buff + fwd, len - fwd, "wwid rules:\n" "- blacklist:\n"); if (snprint_blacklist_group(buff, len, &fwd, &conf->blist_wwid) == 0) return len; if ((len - fwd - threshold) <= 0) return len; fwd += snprintf(buff + fwd, len - fwd, "- exceptions:\n"); if (snprint_blacklist_group(buff, len, &fwd, &conf->elist_wwid) == 0) return len; if ((len - fwd - threshold) <= 0) return len; fwd += snprintf(buff + fwd, len - fwd, "device rules:\n" "- blacklist:\n"); if (snprint_blacklist_devgroup(buff, len, &fwd, &conf->blist_device) == 0) return len; if ((len - fwd - threshold) <= 0) return len; fwd += snprintf(buff + fwd, len - fwd, "- exceptions:\n"); if (snprint_blacklist_devgroup(buff, len, &fwd, &conf->elist_device) == 0) return len; if (fwd > len) return len; return fwd; } int snprint_blacklist(struct config *conf, char *buff, int len) { int i; struct blentry * ble; struct blentry_device * bled; int fwd = 0; struct keyword *rootkw; struct keyword *kw; rootkw = find_keyword(conf->keywords, NULL, "blacklist"); if (!rootkw) return 0; fwd += snprintf(buff + fwd, len - fwd, "blacklist {\n"); if (fwd >= len) return len; vector_foreach_slot (conf->blist_devnode, ble, i) { kw = find_keyword(conf->keywords, rootkw->sub, "devnode"); if (!kw) return 0; fwd += snprint_keyword(buff + fwd, len - fwd, "\t%k %v\n", kw, ble); if (fwd >= len) return len; } vector_foreach_slot (conf->blist_wwid, ble, i) { kw = find_keyword(conf->keywords, rootkw->sub, "wwid"); if (!kw) return 0; fwd += snprint_keyword(buff + fwd, len - fwd, "\t%k %v\n", kw, ble); if (fwd >= len) return len; } vector_foreach_slot (conf->blist_property, ble, i) { kw = find_keyword(conf->keywords, rootkw->sub, "property"); if (!kw) return 0; fwd += snprint_keyword(buff + fwd, len - fwd, "\t%k %v\n", kw, ble); if (fwd >= len) return len; } rootkw = find_keyword(conf->keywords, rootkw->sub, "device"); if (!rootkw) return 0; vector_foreach_slot (conf->blist_device, bled, i) { fwd += snprintf(buff + fwd, len - fwd, "\tdevice {\n"); if (fwd >= len) return len; kw = find_keyword(conf->keywords, rootkw->sub, "vendor"); if (!kw) return 0; fwd += snprint_keyword(buff + fwd, len - fwd, "\t\t%k %v\n", kw, bled); if (fwd >= len) return len; kw = find_keyword(conf->keywords, rootkw->sub, "product"); if (!kw) return 0; fwd += snprint_keyword(buff + fwd, len - fwd, "\t\t%k %v\n", kw, bled); if (fwd >= len) return len; fwd += snprintf(buff + fwd, len - fwd, "\t}\n"); if (fwd >= len) return len; } fwd += snprintf(buff + fwd, len - fwd, "}\n"); if (fwd >= len) return len; return fwd; } int snprint_blacklist_except(struct config *conf, char *buff, int len) { int i; struct blentry * ele; struct blentry_device * eled; int fwd = 0; struct keyword *rootkw; struct keyword *kw; rootkw = find_keyword(conf->keywords, NULL, "blacklist_exceptions"); if (!rootkw) return 0; fwd += snprintf(buff + fwd, len - fwd, "blacklist_exceptions {\n"); if (fwd >= len) return len; vector_foreach_slot (conf->elist_devnode, ele, i) { kw = find_keyword(conf->keywords, rootkw->sub, "devnode"); if (!kw) return 0; fwd += snprint_keyword(buff + fwd, len - fwd, "\t%k %v\n", kw, ele); if (fwd >= len) return len; } vector_foreach_slot (conf->elist_wwid, ele, i) { kw = find_keyword(conf->keywords, rootkw->sub, "wwid"); if (!kw) return 0; fwd += snprint_keyword(buff + fwd, len - fwd, "\t%k %v\n", kw, ele); if (fwd >= len) return len; } vector_foreach_slot (conf->elist_property, ele, i) { kw = find_keyword(conf->keywords, rootkw->sub, "property"); if (!kw) return 0; fwd += snprint_keyword(buff + fwd, len - fwd, "\t%k %v\n", kw, ele); if (fwd >= len) return len; } rootkw = find_keyword(conf->keywords, rootkw->sub, "device"); if (!rootkw) return 0; vector_foreach_slot (conf->elist_device, eled, i) { fwd += snprintf(buff + fwd, len - fwd, "\tdevice {\n"); if (fwd >= len) return len; kw = find_keyword(conf->keywords, rootkw->sub, "vendor"); if (!kw) return 0; fwd += snprint_keyword(buff + fwd, len - fwd, "\t\t%k %v\n", kw, eled); if (fwd >= len) return len; kw = find_keyword(conf->keywords, rootkw->sub, "product"); if (!kw) return 0; fwd += snprint_keyword(buff + fwd, len - fwd, "\t\t%k %v\n", kw, eled); if (fwd >= len) return len; fwd += snprintf(buff + fwd, len - fwd, "\t}\n"); if (fwd >= len) return len; } fwd += snprintf(buff + fwd, len - fwd, "}\n"); if (fwd >= len) return len; return fwd; } int snprint_status(char *buff, int len, struct vectors *vecs) { int fwd = 0; int i; unsigned int count[PATH_MAX_STATE] = {0}; struct path * pp; vector_foreach_slot (vecs->pathvec, pp, i) { count[pp->state]++; } fwd += snprintf(buff + fwd, len - fwd, "path checker states:\n"); for (i=0; ipathvec, pp, i) if (pp->fd >= 0) monitored_count++; fwd += snprintf(buff + fwd, len - fwd, "\npaths: %d\nbusy: %s\n", monitored_count, is_uevent_busy()? "True" : "False"); if (fwd >= len) return len; return fwd; } int snprint_devices(struct config *conf, char * buff, int len, struct vectors *vecs) { DIR *blkdir; struct dirent *blkdev; struct stat statbuf; char devpath[PATH_MAX]; char *devptr; int threshold = MAX_LINE_LEN; int fwd = 0; int r; struct path * pp; if (!(blkdir = opendir("/sys/block"))) return 1; if ((len - fwd - threshold) <= 0) { closedir(blkdir); return len; } fwd += snprintf(buff + fwd, len - fwd, "available block devices:\n"); strcpy(devpath,"/sys/block/"); while ((blkdev = readdir(blkdir)) != NULL) { if ((strcmp(blkdev->d_name,".") == 0) || (strcmp(blkdev->d_name,"..") == 0)) continue; devptr = devpath + 11; *devptr = '\0'; strncat(devptr, blkdev->d_name, PATH_MAX-12); if (stat(devpath, &statbuf) < 0) continue; if (S_ISDIR(statbuf.st_mode) == 0) continue; if ((len - fwd - threshold) <= 0) { closedir(blkdir); return len; } fwd += snprintf(buff + fwd, len - fwd, " %s", devptr); pp = find_path_by_dev(vecs->pathvec, devptr); if (!pp) { r = filter_devnode(conf->blist_devnode, conf->elist_devnode, devptr); if (r > 0) fwd += snprintf(buff + fwd, len - fwd, " devnode blacklisted, unmonitored"); else if (r <= 0) fwd += snprintf(buff + fwd, len - fwd, " devnode whitelisted, unmonitored"); } else fwd += snprintf(buff + fwd, len - fwd, " devnode whitelisted, monitored"); fwd += snprintf(buff + fwd, len - fwd, "\n"); } closedir(blkdir); if (fwd >= len) return len; return fwd; } /* * stdout printing helpers */ void print_path(struct path *pp, char *style) { char line[MAX_LINE_LEN]; memset(&line[0], 0, MAX_LINE_LEN); snprint_path(&line[0], MAX_LINE_LEN, style, pp, 1); printf("%s", line); } void print_multipath(struct multipath *mpp, char *style) { char line[MAX_LINE_LEN]; memset(&line[0], 0, MAX_LINE_LEN); snprint_multipath(&line[0], MAX_LINE_LEN, style, mpp, 1); printf("%s", line); } void print_pathgroup(struct pathgroup *pgp, char *style) { char line[MAX_LINE_LEN]; memset(&line[0], 0, MAX_LINE_LEN); snprint_pathgroup(&line[0], MAX_LINE_LEN, style, pgp); printf("%s", line); } void print_map(struct multipath *mpp, char *params) { if (mpp->size && params) printf("0 %llu %s %s\n", mpp->size, TGT_MPATH, params); return; } void print_all_paths(vector pathvec, int banner) { print_all_paths_custo(pathvec, banner, PRINT_PATH_LONG); } void print_all_paths_custo(vector pathvec, int banner, char *fmt) { int i; struct path * pp; char line[MAX_LINE_LEN]; if (!VECTOR_SIZE(pathvec)) { if (banner) fprintf(stdout, "===== no paths =====\n"); return; } if (banner) fprintf(stdout, "===== paths list =====\n"); get_path_layout(pathvec, 1); snprint_path_header(line, MAX_LINE_LEN, fmt); fprintf(stdout, "%s", line); vector_foreach_slot (pathvec, pp, i) print_path(pp, fmt); } multipath-tools-0.7.4/libmultipath/print.h000066400000000000000000000120151320314174000206640ustar00rootroot00000000000000#define PRINT_PATH_LONG "%w %i %d %D %p %t %T %s %o" #define PRINT_PATH_INDENT "%i %d %D %t %T %o" #define PRINT_PATH_CHECKER "%i %d %D %p %t %T %o %C" #define PRINT_MAP_STATUS "%n %F %Q %N %t %r" #define PRINT_MAP_STATS "%n %0 %1 %2 %3 %4" #define PRINT_MAP_NAMES "%n %d %w" #define PRINT_MAP_PROPS "size=%S features='%f' hwhandler='%h' wp=%r" #define PRINT_PG_INDENT "policy='%s' prio=%p status=%t" #define PRINT_JSON_MULTIPLIER 5 #define PRINT_JSON_MAJOR_VERSION 0 #define PRINT_JSON_MINOR_VERSION 1 #define PRINT_JSON_START_VERSION " \"major_version\": %d,\n" \ " \"minor_version\": %d,\n" #define PRINT_JSON_START_ELEM "{\n" #define PRINT_JSON_START_MAP " \"map\":" #define PRINT_JSON_START_MAPS "\"maps\": [" #define PRINT_JSON_START_PATHS "\"paths\": [" #define PRINT_JSON_START_GROUPS "\"path_groups\": [" #define PRINT_JSON_END_ELEM "}," #define PRINT_JSON_END_LAST_ELEM "}" #define PRINT_JSON_END_LAST "}\n" #define PRINT_JSON_END_ARRAY "]\n" #define PRINT_JSON_INDENT " " #define PRINT_JSON_MAP "{\n" \ " \"name\" : \"%n\",\n" \ " \"uuid\" : \"%w\",\n" \ " \"sysfs\" : \"%d\",\n" \ " \"failback\" : \"%F\",\n" \ " \"queueing\" : \"%Q\",\n" \ " \"paths\" : %N,\n" \ " \"write_prot\" : \"%r\",\n" \ " \"dm_st\" : \"%t\",\n" \ " \"features\" : \"%f\",\n" \ " \"hwhandler\" : \"%h\",\n" \ " \"action\" : \"%A\",\n" \ " \"path_faults\" : %0,\n" \ " \"vend\" : \"%v\",\n" \ " \"prod\" : \"%p\",\n" \ " \"rev\" : \"%e\",\n" \ " \"switch_grp\" : %1,\n" \ " \"map_loads\" : %2,\n" \ " \"total_q_time\" : %3,\n" \ " \"q_timeouts\" : %4," #define PRINT_JSON_GROUP "{\n" \ " \"selector\" : \"%s\",\n" \ " \"pri\" : %p,\n" \ " \"dm_st\" : \"%t\"," #define PRINT_JSON_GROUP_NUM " \"group\" : %d,\n" #define PRINT_JSON_PATH "{\n" \ " \"dev\" : \"%d\",\n"\ " \"dev_t\" : \"%D\",\n" \ " \"dm_st\" : \"%t\",\n" \ " \"dev_st\" : \"%o\",\n" \ " \"chk_st\" : \"%T\",\n" \ " \"checker\" : \"%c\",\n" \ " \"pri\" : %p,\n" \ " \"host_wwnn\" : \"%N\",\n" \ " \"target_wwnn\" : \"%n\",\n" \ " \"host_wwpn\" : \"%R\",\n" \ " \"target_wwpn\" : \"%r\",\n" \ " \"host_adapter\" : \"%a\"" #define MAX_LINE_LEN 80 #define MAX_LINES 64 #define MAX_FIELD_LEN 128 #define PROGRESS_LEN 10 struct path_data { char wildcard; char * header; int width; int (*snprint)(char * buff, size_t len, struct path * pp); }; struct multipath_data { char wildcard; char * header; int width; int (*snprint)(char * buff, size_t len, struct multipath * mpp); }; struct pathgroup_data { char wildcard; char * header; int width; int (*snprint)(char * buff, size_t len, struct pathgroup * pgp); }; void get_path_layout (vector pathvec, int header); void get_multipath_layout (vector mpvec, int header); int snprint_path_header (char *, int, char *); int snprint_multipath_header (char *, int, char *); int snprint_path (char *, int, char *, struct path *, int); int snprint_multipath (char *, int, char *, struct multipath *, int); int snprint_multipath_topology (char *, int, struct multipath * mpp, int verbosity); int snprint_multipath_topology_json (char * buff, int len, struct vectors * vecs); int snprint_multipath_map_json (char * buff, int len, struct multipath * mpp, int last); int snprint_defaults (struct config *, char *, int); int snprint_blacklist (struct config *, char *, int); int snprint_blacklist_except (struct config *, char *, int); int snprint_blacklist_report (struct config *, char *, int); int snprint_wildcards (char *, int); int snprint_status (char *, int, struct vectors *); int snprint_devices (struct config *, char *, int, struct vectors *); int snprint_hwtable (struct config *, char *, int, vector); int snprint_mptable (struct config *, char *, int, vector); int snprint_overrides (struct config *, char *, int, struct hwentry *); int snprint_path_serial (char *, size_t, struct path *); int snprint_host_wwnn (char *, size_t, struct path *); int snprint_host_wwpn (char *, size_t, struct path *); int snprint_tgt_wwnn (char *, size_t, struct path *); int snprint_tgt_wwpn (char *, size_t, struct path *); void print_multipath_topology (struct multipath * mpp, int verbosity); void print_path (struct path * pp, char * style); void print_multipath (struct multipath * mpp, char * style); void print_pathgroup (struct pathgroup * pgp, char * style); void print_map (struct multipath * mpp, char * params); void print_all_paths (vector pathvec, int banner); void print_all_paths_custo (vector pathvec, int banner, char *fmt); void print_hwtable (vector hwtable); multipath-tools-0.7.4/libmultipath/prio.c000066400000000000000000000066661320314174000205130ustar00rootroot00000000000000#include #include #include #include #include #include "debug.h" #include "prio.h" static LIST_HEAD(prioritizers); unsigned int get_prio_timeout(unsigned int checker_timeout, unsigned int default_timeout) { if (checker_timeout) return checker_timeout * 1000; return default_timeout; } int init_prio (char *multipath_dir) { if (!add_prio(multipath_dir, DEFAULT_PRIO)) return 1; return 0; } static struct prio * alloc_prio (void) { struct prio *p; p = MALLOC(sizeof(struct prio)); if (p) { INIT_LIST_HEAD(&p->node); p->refcount = 1; } return p; } void free_prio (struct prio * p) { if (!p) return; p->refcount--; if (p->refcount) { condlog(3, "%s prioritizer refcount %d", p->name, p->refcount); return; } condlog(3, "unloading %s prioritizer", p->name); list_del(&p->node); if (p->handle) { if (dlclose(p->handle) != 0) { condlog(0, "Cannot unload prioritizer %s: %s", p->name, dlerror()); } } FREE(p); } void cleanup_prio(void) { struct prio * prio_loop; struct prio * prio_temp; list_for_each_entry_safe(prio_loop, prio_temp, &prioritizers, node) { free_prio(prio_loop); } } static struct prio * prio_lookup (char * name) { struct prio * p; if (!name || !strlen(name)) return NULL; list_for_each_entry(p, &prioritizers, node) { if (!strncmp(name, p->name, PRIO_NAME_LEN)) return p; } return NULL; } int prio_set_args (struct prio * p, char * args) { return snprintf(p->args, PRIO_ARGS_LEN, "%s", args); } struct prio * add_prio (char *multipath_dir, char * name) { char libname[LIB_PRIO_NAMELEN]; struct stat stbuf; struct prio * p; char *errstr; p = alloc_prio(); if (!p) return NULL; snprintf(p->name, PRIO_NAME_LEN, "%s", name); snprintf(libname, LIB_PRIO_NAMELEN, "%s/libprio%s.so", multipath_dir, name); if (stat(libname,&stbuf) < 0) { condlog(0,"Prioritizer '%s' not found in %s", name, multipath_dir); goto out; } condlog(3, "loading %s prioritizer", libname); p->handle = dlopen(libname, RTLD_NOW); if (!p->handle) { if ((errstr = dlerror()) != NULL) condlog(0, "A dynamic linking error occurred: (%s)", errstr); goto out; } p->getprio = (int (*)(struct path *, char *, unsigned int)) dlsym(p->handle, "getprio"); errstr = dlerror(); if (errstr != NULL) condlog(0, "A dynamic linking error occurred: (%s)", errstr); if (!p->getprio) goto out; list_add(&p->node, &prioritizers); return p; out: free_prio(p); return NULL; } int prio_getprio (struct prio * p, struct path * pp, unsigned int timeout) { return p->getprio(pp, p->args, timeout); } int prio_selected (struct prio * p) { if (!p) return 0; return (p->getprio) ? 1 : 0; } char * prio_name (struct prio * p) { return p->name; } char * prio_args (struct prio * p) { return p->args; } void prio_get (char *multipath_dir, struct prio * dst, char * name, char * args) { struct prio * src = NULL; if (!dst) return; if (name && strlen(name)) { src = prio_lookup(name); if (!src) src = add_prio(multipath_dir, name); } if (!src) { dst->getprio = NULL; return; } strncpy(dst->name, src->name, PRIO_NAME_LEN); if (args) strncpy(dst->args, args, PRIO_ARGS_LEN - 1); dst->getprio = src->getprio; dst->handle = NULL; src->refcount++; } void prio_put (struct prio * dst) { struct prio * src; if (!dst || !dst->getprio) return; src = prio_lookup(dst->name); memset(dst, 0x0, sizeof(struct prio)); free_prio(src); } multipath-tools-0.7.4/libmultipath/prio.h000066400000000000000000000032561320314174000205100ustar00rootroot00000000000000#ifndef _PRIO_H #define _PRIO_H /* * knowing about path struct gives flexibility to prioritizers */ #include "checkers.h" #include "vector.h" /* forward declaration to avoid circular dependency */ struct path; #include "list.h" #include "memory.h" #include "defaults.h" /* * Known prioritizers for use in hwtable.c */ #define PRIO_ALUA "alua" #define PRIO_CONST "const" #define PRIO_DATACORE "datacore" #define PRIO_EMC "emc" #define PRIO_HDS "hds" #define PRIO_HP_SW "hp_sw" #define PRIO_IET "iet" #define PRIO_ONTAP "ontap" #define PRIO_RANDOM "random" #define PRIO_RDAC "rdac" #define PRIO_WEIGHTED_PATH "weightedpath" #define PRIO_SYSFS "sysfs" #define PRIO_PATH_LATENCY "path_latency" /* * Value used to mark the fact prio was not defined */ #define PRIO_UNDEF -1 /* * strings lengths */ #define LIB_PRIO_NAMELEN 255 #define PRIO_NAME_LEN 16 #define PRIO_ARGS_LEN 255 struct prio { void *handle; int refcount; struct list_head node; char name[PRIO_NAME_LEN]; char args[PRIO_ARGS_LEN]; int (*getprio)(struct path *, char *, unsigned int); }; unsigned int get_prio_timeout(unsigned int checker_timeout, unsigned int default_timeout); int init_prio (char *); void cleanup_prio (void); struct prio * add_prio (char *, char *); int prio_getprio (struct prio *, struct path *, unsigned int); void prio_get (char *, struct prio *, char *, char *); void prio_put (struct prio *); int prio_selected (struct prio *); char * prio_name (struct prio *); char * prio_args (struct prio *); int prio_set_args (struct prio *, char *); /* The only function exported by prioritizer dynamic libraries (.so) */ int getprio(struct path *, char *, unsigned int); #endif /* _PRIO_H */ multipath-tools-0.7.4/libmultipath/prioritizers/000077500000000000000000000000001320314174000221255ustar00rootroot00000000000000multipath-tools-0.7.4/libmultipath/prioritizers/Makefile000066400000000000000000000016411320314174000235670ustar00rootroot00000000000000# # Copyright (C) 2007 Christophe Varoqui, # include ../../Makefile.inc CFLAGS += $(LIB_CFLAGS) -I.. # If you add or remove a prioritizer also update multipath/multipath.conf.5 LIBS = \ libprioalua.so \ libprioconst.so \ libpriodatacore.so \ libprioemc.so \ libpriohds.so \ libpriohp_sw.so \ libprioiet.so \ libprioontap.so \ libpriorandom.so \ libpriordac.so \ libprioweightedpath.so \ libpriopath_latency.so \ libpriosysfs.so all: $(LIBS) libprioalua.so: alua.o alua_rtpg.o $(CC) $(LDFLAGS) $(SHARED_FLAGS) -o $@ $^ libpriopath_latency.so: path_latency.o ../checkers/libsg.o $(CC) $(LDFLAGS) $(SHARED_FLAGS) -o $@ $^ -lm libprio%.so: %.o $(CC) $(LDFLAGS) $(SHARED_FLAGS) -o $@ $^ install: $(LIBS) $(INSTALL_PROGRAM) -m 755 libprio*.so $(DESTDIR)$(libdir) uninstall: for file in $(LIBS); do $(RM) $(DESTDIR)$(libdir)/$$file; done clean: $(RM) core *.a *.o *.gz *.so multipath-tools-0.7.4/libmultipath/prioritizers/alua.c000066400000000000000000000063031320314174000232150ustar00rootroot00000000000000/* * (C) Copyright IBM Corp. 2004, 2005 All Rights Reserved. * * main.c * * Tool to make use of a SCSI-feature called Asymmetric Logical Unit Access. * It determines the ALUA state of a device and prints a priority value to * stdout. * * Author(s): Jan Kunigk * S. Bader * * This file is released under the GPL. */ #include #include "debug.h" #include "prio.h" #include "structs.h" #include "alua.h" #define ALUA_PRIO_NOT_SUPPORTED 1 #define ALUA_PRIO_RTPG_FAILED 2 #define ALUA_PRIO_GETAAS_FAILED 3 #define ALUA_PRIO_TPGS_FAILED 4 #define ALUA_PRIO_NO_INFORMATION 5 static const char * aas_string[] = { [AAS_OPTIMIZED] = "active/optimized", [AAS_NON_OPTIMIZED] = "active/non-optimized", [AAS_STANDBY] = "standby", [AAS_UNAVAILABLE] = "unavailable", [AAS_LBA_DEPENDENT] = "logical block dependent", [AAS_RESERVED] = "ARRAY BUG: invalid TPGs state!", [AAS_OFFLINE] = "offline", [AAS_TRANSITIONING] = "transitioning between states", }; static const char *aas_print_string(int rc) { rc &= 0x7f; if (rc & 0x70) return aas_string[AAS_RESERVED]; rc &= 0x0f; if (rc > AAS_RESERVED && rc < AAS_OFFLINE) return aas_string[AAS_RESERVED]; else return aas_string[rc]; } int get_alua_info(struct path * pp, unsigned int timeout) { int rc; int tpg; tpg = get_target_port_group(pp, timeout); if (tpg < 0) { rc = get_target_port_group_support(pp->fd, timeout); if (rc < 0) return -ALUA_PRIO_TPGS_FAILED; if (rc == TPGS_NONE) return -ALUA_PRIO_NOT_SUPPORTED; return -ALUA_PRIO_RTPG_FAILED; } condlog(3, "%s: reported target port group is %i", pp->dev, tpg); rc = get_asymmetric_access_state(pp->fd, tpg, timeout); if (rc < 0) return -ALUA_PRIO_GETAAS_FAILED; condlog(3, "%s: aas = %02x [%s]%s", pp->dev, rc, aas_print_string(rc), (rc & 0x80) ? " [preferred]" : ""); return rc; } int get_exclusive_pref_arg(char *args) { char *ptr; if (args == NULL) return 0; ptr = strstr(args, "exclusive_pref_bit"); if (!ptr) return 0; if (ptr[18] != '\0' && ptr[18] != ' ' && ptr[18] != '\t') return 0; if (ptr != args && ptr[-1] != ' ' && ptr[-1] != '\t') return 0; return 1; } int getprio (struct path * pp, char * args, unsigned int timeout) { int rc; int aas; int priopath; int exclusive_pref; if (pp->fd < 0) return -ALUA_PRIO_NO_INFORMATION; exclusive_pref = get_exclusive_pref_arg(args); rc = get_alua_info(pp, timeout); if (rc >= 0) { aas = (rc & 0x0f); priopath = (rc & 0x80); switch(aas) { case AAS_OPTIMIZED: rc = 50; break; case AAS_NON_OPTIMIZED: rc = 10; break; case AAS_LBA_DEPENDENT: rc = 5; break; case AAS_STANDBY: rc = 1; break; default: rc = 0; } if (priopath && (aas != AAS_OPTIMIZED || exclusive_pref)) rc += 80; } else { switch(-rc) { case ALUA_PRIO_NOT_SUPPORTED: condlog(0, "%s: alua not supported", pp->dev); break; case ALUA_PRIO_RTPG_FAILED: condlog(0, "%s: couldn't get target port group", pp->dev); break; case ALUA_PRIO_GETAAS_FAILED: condlog(0, "%s: couldn't get asymmetric access state", pp->dev); break; case ALUA_PRIO_TPGS_FAILED: condlog(3, "%s: couldn't get supported alua states", pp->dev); break; } } return rc; } multipath-tools-0.7.4/libmultipath/prioritizers/alua.h000066400000000000000000000001731320314174000232210ustar00rootroot00000000000000#ifndef _ALUA_H #define _ALUA_H #include "alua_rtpg.h" #define PRIO_ALUA "alua" int prio_alua(struct path * pp); #endif multipath-tools-0.7.4/libmultipath/prioritizers/alua_rtpg.c000066400000000000000000000176271320314174000242640ustar00rootroot00000000000000/* * (C) Copyright IBM Corp. 2004, 2005 All Rights Reserved. * * rtpg.c * * Tool to make use of a SCSI-feature called Asymmetric Logical Unit Access. * It determines the ALUA state of a device and prints a priority value to * stdout. * * Author(s): Jan Kunigk * S. Bader * * This file is released under the GPL. */ #include #include #include #include #include #include #include #define __user #include #include "../structs.h" #include "../prio.h" #include "../discovery.h" #include "alua_rtpg.h" #define SENSE_BUFF_LEN 32 #define SGIO_TIMEOUT 60000 /* * Macro used to print debug messaged. */ #if DEBUG > 0 #define PRINT_DEBUG(f, a...) \ fprintf(stderr, "DEBUG: " f, ##a) #else #define PRINT_DEBUG(f, a...) #endif /* * Optionally print the commands sent and the data received a hex dump. */ #if DEBUG > 0 #if DEBUG_DUMPHEX > 0 #define PRINT_HEX(p, l) print_hex(p, l) void print_hex(unsigned char *p, unsigned long len) { int i; for(i = 0; i < len; i++) { if (i % 16 == 0) printf("%04x: ", i); printf("%02x%s", p[i], (((i + 1) % 16) == 0) ? "\n" : " "); } printf("\n"); } #else #define PRINT_HEX(p, l) #endif #else #define PRINT_HEX(p, l) #endif /* * Returns 0 if the SCSI command either was successful or if the an error was * recovered, otherwise 1. (definitions taken from sg_err.h) */ #define SCSI_CHECK_CONDITION 0x2 #define SCSI_COMMAND_TERMINATED 0x22 #define SG_ERR_DRIVER_SENSE 0x08 #define RECOVERED_ERROR 0x01 static int scsi_error(struct sg_io_hdr *hdr) { /* Treat SG_ERR here to get rid of sg_err.[ch] */ hdr->status &= 0x7e; if ( (hdr->status == 0) && (hdr->host_status == 0) && (hdr->driver_status == 0) ) { return 0; } if ( (hdr->status == SCSI_CHECK_CONDITION) || (hdr->status == SCSI_COMMAND_TERMINATED) || ((hdr->driver_status & 0xf) == SG_ERR_DRIVER_SENSE) ) { if (hdr->sbp && (hdr->sb_len_wr > 2)) { int sense_key; unsigned char * sense_buffer = hdr->sbp; if (sense_buffer[0] & 0x2) sense_key = sense_buffer[1] & 0xf; else sense_key = sense_buffer[2] & 0xf; if (sense_key == RECOVERED_ERROR) return 0; } } return 1; } /* * Helper function to setup and run a SCSI inquiry command. */ int do_inquiry(int fd, int evpd, unsigned int codepage, void *resp, int resplen, unsigned int timeout) { struct inquiry_command cmd; struct sg_io_hdr hdr; unsigned char sense[SENSE_BUFF_LEN]; memset(&cmd, 0, sizeof(cmd)); cmd.op = OPERATION_CODE_INQUIRY; if (evpd) { inquiry_command_set_evpd(&cmd); cmd.page = codepage; } set_uint16(cmd.length, resplen); PRINT_HEX((unsigned char *) &cmd, sizeof(cmd)); memset(&hdr, 0, sizeof(hdr)); hdr.interface_id = 'S'; hdr.cmdp = (unsigned char *) &cmd; hdr.cmd_len = sizeof(cmd); hdr.dxfer_direction = SG_DXFER_FROM_DEV; hdr.dxferp = resp; hdr.dxfer_len = resplen; hdr.sbp = sense; hdr.mx_sb_len = sizeof(sense); hdr.timeout = get_prio_timeout(timeout, SGIO_TIMEOUT); if (ioctl(fd, SG_IO, &hdr) < 0) { PRINT_DEBUG("do_inquiry: IOCTL failed!\n"); return -RTPG_INQUIRY_FAILED; } if (scsi_error(&hdr)) { PRINT_DEBUG("do_inquiry: SCSI error!\n"); return -RTPG_INQUIRY_FAILED; } PRINT_HEX((unsigned char *) resp, resplen); return 0; } /* * This function returns the support for target port groups by evaluating the * data returned by the standard inquiry command. */ int get_target_port_group_support(int fd, unsigned int timeout) { struct inquiry_data inq; int rc; memset((unsigned char *)&inq, 0, sizeof(inq)); rc = do_inquiry(fd, 0, 0x00, &inq, sizeof(inq), timeout); if (!rc) { rc = inquiry_data_get_tpgs(&inq); } return rc; } static int get_sysfs_pg83(struct path *pp, unsigned char *buff, int buflen) { struct udev_device *parent = pp->udev; while (parent) { const char *subsys = udev_device_get_subsystem(parent); if (subsys && !strncmp(subsys, "scsi", 4)) break; parent = udev_device_get_parent(parent); } if (!parent || sysfs_get_vpd(parent, 0x83, buff, buflen) <= 0) { PRINT_DEBUG("failed to read sysfs vpd pg83\n"); return -1; } return 0; } int get_target_port_group(struct path * pp, unsigned int timeout) { unsigned char *buf; struct vpd83_data * vpd83; struct vpd83_dscr * dscr; int rc; int buflen, scsi_buflen; buflen = 4096; buf = (unsigned char *)malloc(buflen); if (!buf) { PRINT_DEBUG("malloc failed: could not allocate" "%u bytes\n", buflen); return -RTPG_RTPG_FAILED; } memset(buf, 0, buflen); rc = get_sysfs_pg83(pp, buf, buflen); if (rc < 0) { rc = do_inquiry(pp->fd, 1, 0x83, buf, buflen, timeout); if (rc < 0) goto out; scsi_buflen = (buf[2] << 8 | buf[3]) + 4; /* Paranoia */ if (scsi_buflen >= USHRT_MAX) scsi_buflen = USHRT_MAX; if (buflen < scsi_buflen) { free(buf); buf = (unsigned char *)malloc(scsi_buflen); if (!buf) { PRINT_DEBUG("malloc failed: could not allocate" "%u bytes\n", scsi_buflen); return -RTPG_RTPG_FAILED; } buflen = scsi_buflen; memset(buf, 0, buflen); rc = do_inquiry(pp->fd, 1, 0x83, buf, buflen, timeout); if (rc < 0) goto out; } } vpd83 = (struct vpd83_data *) buf; rc = -RTPG_NO_TPG_IDENTIFIER; FOR_EACH_VPD83_DSCR(vpd83, dscr) { if (vpd83_dscr_istype(dscr, IDTYPE_TARGET_PORT_GROUP)) { struct vpd83_tpg_dscr *p; if (rc != -RTPG_NO_TPG_IDENTIFIER) { PRINT_DEBUG("get_target_port_group: more " "than one TPG identifier found!\n"); continue; } p = (struct vpd83_tpg_dscr *)dscr->data; rc = get_uint16(p->tpg); } } if (rc == -RTPG_NO_TPG_IDENTIFIER) { PRINT_DEBUG("get_target_port_group: " "no TPG identifier found!\n"); } out: free(buf); return rc; } int do_rtpg(int fd, void* resp, long resplen, unsigned int timeout) { struct rtpg_command cmd; struct sg_io_hdr hdr; unsigned char sense[SENSE_BUFF_LEN]; memset(&cmd, 0, sizeof(cmd)); cmd.op = OPERATION_CODE_RTPG; rtpg_command_set_service_action(&cmd); set_uint32(cmd.length, resplen); PRINT_HEX((unsigned char *) &cmd, sizeof(cmd)); memset(&hdr, 0, sizeof(hdr)); hdr.interface_id = 'S'; hdr.cmdp = (unsigned char *) &cmd; hdr.cmd_len = sizeof(cmd); hdr.dxfer_direction = SG_DXFER_FROM_DEV; hdr.dxferp = resp; hdr.dxfer_len = resplen; hdr.mx_sb_len = sizeof(sense); hdr.sbp = sense; hdr.timeout = get_prio_timeout(timeout, SGIO_TIMEOUT); if (ioctl(fd, SG_IO, &hdr) < 0) return -RTPG_RTPG_FAILED; if (scsi_error(&hdr)) { PRINT_DEBUG("do_rtpg: SCSI error!\n"); return -RTPG_RTPG_FAILED; } PRINT_HEX(resp, resplen); return 0; } int get_asymmetric_access_state(int fd, unsigned int tpg, unsigned int timeout) { unsigned char *buf; struct rtpg_data * tpgd; struct rtpg_tpg_dscr * dscr; int rc; int buflen; uint64_t scsi_buflen; buflen = 4096; buf = (unsigned char *)malloc(buflen); if (!buf) { PRINT_DEBUG ("malloc failed: could not allocate" "%u bytes\n", buflen); return -RTPG_RTPG_FAILED; } memset(buf, 0, buflen); rc = do_rtpg(fd, buf, buflen, timeout); if (rc < 0) goto out; scsi_buflen = (buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3]) + 4; if (scsi_buflen > UINT_MAX) scsi_buflen = UINT_MAX; if (buflen < scsi_buflen) { free(buf); buf = (unsigned char *)malloc(scsi_buflen); if (!buf) { PRINT_DEBUG ("malloc failed: could not allocate" "%u bytes\n", scsi_buflen); return -RTPG_RTPG_FAILED; } buflen = scsi_buflen; memset(buf, 0, buflen); rc = do_rtpg(fd, buf, buflen, timeout); if (rc < 0) goto out; } tpgd = (struct rtpg_data *) buf; rc = -RTPG_TPG_NOT_FOUND; RTPG_FOR_EACH_PORT_GROUP(tpgd, dscr) { if (get_uint16(dscr->tpg) == tpg) { if (rc != -RTPG_TPG_NOT_FOUND) { PRINT_DEBUG("get_asymmetric_access_state: " "more than one entry with same port " "group.\n"); } else { PRINT_DEBUG("pref=%i\n", dscr->b0); rc = rtpg_tpg_dscr_get_aas(dscr); } } } out: free(buf); return rc; } multipath-tools-0.7.4/libmultipath/prioritizers/alua_rtpg.h000066400000000000000000000014601320314174000242550ustar00rootroot00000000000000/* * (C) Copyright IBM Corp. 2004, 2005 All Rights Reserved. * * rtpg.h * * Tool to make use of a SCSI-feature called Asymmetric Logical Unit Access. * It determines the ALUA state of a device and prints a priority value to * stdout. * * Author(s): Jan Kunigk * S. Bader * * This file is released under the GPL. */ #ifndef __RTPG_H__ #define __RTPG_H__ #include "alua_spc3.h" #define RTPG_SUCCESS 0 #define RTPG_INQUIRY_FAILED 1 #define RTPG_NO_TPG_IDENTIFIER 2 #define RTPG_RTPG_FAILED 3 #define RTPG_TPG_NOT_FOUND 4 int get_target_port_group_support(int fd, unsigned int timeout); int get_target_port_group(struct path * pp, unsigned int timeout); int get_asymmetric_access_state(int fd, unsigned int tpg, unsigned int timeout); #endif /* __RTPG_H__ */ multipath-tools-0.7.4/libmultipath/prioritizers/alua_spc3.h000066400000000000000000000242051320314174000241530ustar00rootroot00000000000000/* * (C) Copyright IBM Corp. 2004, 2005 All Rights Reserved. * * spc3.h * * Tool to make use of a SCSI-feature called Asymmetric Logical Unit Access. * It determines the ALUA state of a device and prints a priority value to * stdout. * * Author(s): Jan Kunigk * S. Bader * * This file is released under the GPL. */ #ifndef __SPC3_H__ #define __SPC3_H__ /*============================================================================= * Some helper functions for getting and setting 16 and 32 bit values. *============================================================================= */ static inline unsigned short get_uint16(unsigned char *p) { return (p[0] << 8) + p[1]; } static inline void set_uint16(unsigned char *p, unsigned short v) { p[0] = (v >> 8) & 0xff; p[1] = v & 0xff; } static inline unsigned int get_uint32(unsigned char *p) { return (p[0] << 24) + (p[1] << 16) + (p[2] << 8) + p[3]; } static inline void set_uint32(unsigned char *p, unsigned int v) { p[0] = (v >> 24) & 0xff; p[1] = (v >> 16) & 0xff; p[2] = (v >> 8) & 0xff; p[3] = v & 0xff; } /*============================================================================= * Definitions to support the standard inquiry command as defined in SPC-3. * If the evpd (enable vital product data) bit is set the data that will be * returned is selected by the page field. This field must be 0 if the evpd * bit is not set. *============================================================================= */ #define OPERATION_CODE_INQUIRY 0x12 struct inquiry_command { unsigned char op; unsigned char b1; /* xxxxxx.. = reserved */ /* ......x. = obsolete */ /* .......x = evpd */ unsigned char page; unsigned char length[2]; unsigned char control; } __attribute__((packed)); static inline void inquiry_command_set_evpd(struct inquiry_command *ic) { ic->b1 |= 1; } /*----------------------------------------------------------------------------- * Data returned by the standard inquiry command. *----------------------------------------------------------------------------- * * Peripheral qualifier codes. */ #define PQ_CONNECTED 0x0 #define PQ_DISCONNECTED 0x1 #define PQ_UNSUPPORTED 0x3 /* Defined peripheral device types. */ #define PDT_DIRECT_ACCESS 0x00 #define PDT_SEQUENTIAL_ACCESS 0x01 #define PDT_PRINTER 0x02 #define PDT_PROCESSOR 0x03 #define PDT_WRITE_ONCE 0x04 #define PDT_CD_DVD 0x05 #define PDT_SCANNER 0x06 #define PDT_OPTICAL_MEMORY 0x07 #define PDT_MEDIUM_CHANGER 0x08 #define PDT_COMMUNICATIONS 0x09 #define PDT_STORAGE_ARRAY_CONTROLLER 0x0c #define PDT_ENCLOSURE_SERVICES 0x0d #define PDT_SIMPLIFIED_DIRECT_ACCESS 0x0e #define PDT_OPTICAL_CARD_READER_WRITER 0x0f #define PDT_BRIDGE_CONTROLLER 0x10 #define PDT_OBJECT_BASED 0x11 #define PDT_AUTOMATION_INTERFACE 0x12 #define PDT_LUN 0x1e #define PDT_UNKNOWN 0x1f /* Defined version codes. */ #define VERSION_NONE 0x00 #define VERSION_SPC 0x03 #define VERSION_SPC2 0x04 #define VERSION_SPC3 0x05 /* Defined TPGS field values. */ #define TPGS_UNDEF -1 #define TPGS_NONE 0x0 #define TPGS_IMPLICIT 0x1 #define TPGS_EXPLICIT 0x2 #define TPGS_BOTH 0x3 struct inquiry_data { unsigned char b0; /* xxx..... = peripheral_qualifier */ /* ...xxxxx = peripheral_device_type */ unsigned char b1; /* x....... = removable medium */ /* .xxxxxxx = reserverd */ unsigned char version; unsigned char b3; /* xx...... = obsolete */ /* ..x..... = normal aca supported */ /* ...x.... = hirarchichal lun supp. */ /* ....xxxx = response format */ /* 2 is spc-3 format */ unsigned char length; unsigned char b5; /* x....... = storage controller */ /* component supported */ /* .x...... = access controls coord. */ /* ..xx.... = target port group supp.*/ /* ....x... = third party copy supp. */ /* .....xx. = reserved */ /* .......x = protection info supp. */ unsigned char b6; /* x....... = bque */ /* .x...... = enclosure services sup.*/ /* ..x..... = vs1 */ /* ...x.... = multiport support */ /* ....x... = medium changer */ /* .....xx. = obsolete */ /* .......x = add16 */ unsigned char b7; /* xx...... = obsolete */ /* ..x..... = wbus16 */ /* ...x.... = sync */ /* ....x... = linked commands supp. */ /* .....x.. = obsolete */ /* ......x. = command queue support */ /* .......x = vs2 */ unsigned char vendor_identification[8]; unsigned char product_identification[16]; unsigned char product_revision[4]; unsigned char vendor_specific[20]; unsigned char b56; /* xxxx.... = reserved */ /* ....xx.. = clocking */ /* ......x. = qas */ /* .......x = ius */ unsigned char reserved4; unsigned char version_descriptor[8][2]; unsigned char reserved5[22]; unsigned char vendor_parameters[0]; } __attribute__((packed)); static inline int inquiry_data_get_tpgs(struct inquiry_data *id) { return (id->b5 >> 4) & 3; } /*----------------------------------------------------------------------------- * Inquiry data returned when requesting vital product data page 0x83. *----------------------------------------------------------------------------- */ #define CODESET_BINARY 0x1 #define CODESET_ACSII 0x2 #define CODESET_UTF8 0x3 #define ASSOCIATION_UNIT 0x0 #define ASSOCIATION_PORT 0x1 #define ASSOCIATION_DEVICE 0x2 #define IDTYPE_VENDOR_SPECIFIC 0x0 #define IDTYPE_T10_VENDOR_ID 0x1 #define IDTYPE_EUI64 0x2 #define IDTYPE_NAA 0x3 #define IDTYPE_RELATIVE_TPG_ID 0x4 #define IDTYPE_TARGET_PORT_GROUP 0x5 #define IDTYPE_LUN_GROUP 0x6 #define IDTYPE_MD5_LUN_ID 0x7 #define IDTYPE_SCSI_NAME_STRING 0x8 struct vpd83_tpg_dscr { unsigned char reserved1[2]; unsigned char tpg[2]; } __attribute__((packed)); struct vpd83_dscr { unsigned char b0; /* xxxx.... = protocol id */ /* ....xxxx = codeset */ unsigned char b1; /* x....... = protocol id valid */ /* .x...... = reserved */ /* ..xx.... = association */ /* ....xxxx = id type */ unsigned char reserved2; unsigned char length; /* size-4 */ unsigned char data[0]; } __attribute__((packed)); static inline int vpd83_dscr_istype(struct vpd83_dscr *d, unsigned char type) { return ((d->b1 & 7) == type); } struct vpd83_data { unsigned char b0; /* xxx..... = peripheral_qualifier */ /* ...xxxxx = peripheral_device_type */ unsigned char page_code; /* 0x83 */ unsigned char length[2]; /* size-4 */ struct vpd83_dscr data[0]; } __attribute__((packed)); /*----------------------------------------------------------------------------- * This macro should be used to walk through all identification descriptors * defined in the code page 0x83. * The argument p is a pointer to the code page 0x83 data and d is used to * point to the current descriptor. *----------------------------------------------------------------------------- */ #define FOR_EACH_VPD83_DSCR(p, d) \ for( \ d = p->data; \ (((char *) d) - ((char *) p)) < \ get_uint16(p->length); \ d = (struct vpd83_dscr *) \ ((char *) d + d->length + 4) \ ) /*============================================================================= * The following stuctures and macros are used to call the report target port * groups command defined in SPC-3. * This command is used to get information about the target port groups (which * states are supported, which ports belong to this group, and so on) and the * current state of each target port group. *============================================================================= */ #define OPERATION_CODE_RTPG 0xa3 #define SERVICE_ACTION_RTPG 0x0a struct rtpg_command { unsigned char op; /* 0xa3 */ unsigned char b1; /* xxx..... = reserved */ /* ...xxxxx = service action (0x0a) */ unsigned char reserved2[4]; unsigned char length[4]; unsigned char reserved3; unsigned char control; } __attribute__((packed)); static inline void rtpg_command_set_service_action(struct rtpg_command *cmd) { cmd->b1 = (cmd->b1 & 0xe0) | SERVICE_ACTION_RTPG; } struct rtpg_tp_dscr { unsigned char obsolete1[2]; /* The Relative Target Port Identifier of a target port. */ unsigned char rtpi[2]; } __attribute__((packed)); #define AAS_OPTIMIZED 0x0 #define AAS_NON_OPTIMIZED 0x1 #define AAS_STANDBY 0x2 #define AAS_UNAVAILABLE 0x3 #define AAS_LBA_DEPENDENT 0x4 #define AAS_RESERVED 0x5 #define AAS_OFFLINE 0xe #define AAS_TRANSITIONING 0xf #define TPG_STATUS_NONE 0x0 #define TPG_STATUS_SET 0x1 #define TPG_STATUS_IMPLICIT_CHANGE 0x2 struct rtpg_tpg_dscr { unsigned char b0; /* x....... = pref(ered) port */ /* .xxx.... = reserved */ /* ....xxxx = asymetric access state */ unsigned char b1; /* xxx..... = reserved */ /* ...x.... = LBA dependent support */ /* ....x... = unavailable support */ /* .....x.. = standby support */ /* ......x. = non-optimized support */ /* .......x = optimized support */ unsigned char tpg[2]; unsigned char reserved3; unsigned char status; unsigned char vendor_unique; unsigned char port_count; struct rtpg_tp_dscr data[0]; } __attribute__((packed)); static inline int rtpg_tpg_dscr_get_aas(struct rtpg_tpg_dscr *d) { return (d->b0 & 0x8f); } struct rtpg_data { unsigned char length[4]; /* size-4 */ struct rtpg_tpg_dscr data[0]; } __attribute__((packed)); #define RTPG_FOR_EACH_PORT_GROUP(p, g) \ for( \ g = &(p->data[0]); \ (((char *) g) - ((char *) p)) < get_uint32(p->length); \ g = (struct rtpg_tpg_dscr *) ( \ ((char *) g) + \ sizeof(struct rtpg_tpg_dscr) + \ g->port_count * sizeof(struct rtpg_tp_dscr) \ ) \ ) #endif /* __SPC3_H__ */ multipath-tools-0.7.4/libmultipath/prioritizers/const.c000066400000000000000000000001671320314174000234230ustar00rootroot00000000000000#include #include "prio.h" int getprio(struct path * pp, char * args, unsigned int timeout) { return 1; } multipath-tools-0.7.4/libmultipath/prioritizers/datacore.c000066400000000000000000000047751320314174000240700ustar00rootroot00000000000000/* * (C) 2010 Christophe Varoqui * (C) 2009 Dembach Goo Infromatik GmbH & Co KG * Manon Goo * * datacore.c * Version 0.9 * * This program was inspired by work from * Matthias Rudolph * * This work is made available on the basis of the * GPLv2 for detials see . * * Manon Goo 2009 * * */ #include #include #include #include "sg_include.h" #include "debug.h" #include "prio.h" #include "structs.h" #define INQ_REPLY_LEN 255 #define INQ_CMD_CODE 0x12 #define INQ_CMD_LEN 6 #define dc_log(prio, msg) condlog(prio, "%s: datacore prio: " msg, dev) int datacore_prio (const char *dev, int sg_fd, char * args) { int k; char sdsname[32]; unsigned char inqCmdBlk[INQ_CMD_LEN] = { INQ_CMD_CODE, 0, 0, 0, INQ_REPLY_LEN, 0 }; unsigned char inqBuff[INQ_REPLY_LEN]; unsigned char *inqBuffp = inqBuff; unsigned char sense_buffer[32]; sg_io_hdr_t io_hdr; int timeout = 2000; char preferredsds_buff[255] = ""; char * preferredsds = &preferredsds_buff[0]; if (!args) { dc_log(0, "need prio_args with preferredsds set"); return 0; } if (sscanf(args, "timeout=%i preferredsds=%s", &timeout, preferredsds) == 2) {} else if (sscanf(args, "preferredsds=%s timeout=%i", preferredsds, &timeout) == 2) {} else if (sscanf(args, "preferredsds=%s", preferredsds) == 1) {} else { dc_log(0, "unexpected prio_args format"); return 0; } // on error just return prio 0 if (strlen(preferredsds) <= 1) { dc_log(0, "prio args: preferredsds too short (1 character min)"); return 0; } if ((timeout < 500) || (timeout > 20000)) { dc_log(0, "prio args: timeout out of bounds [500:20000]"); return 0; } if ((ioctl(sg_fd, SG_GET_VERSION_NUM, &k) < 0) || (k < 30000)) return 0; memset (&io_hdr, 0, sizeof (sg_io_hdr_t)); io_hdr.interface_id = 'S'; io_hdr.cmd_len = sizeof (inqCmdBlk); io_hdr.mx_sb_len = sizeof (sense_buffer); io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; io_hdr.dxfer_len = INQ_REPLY_LEN; io_hdr.dxferp = inqBuff; io_hdr.cmdp = inqCmdBlk; io_hdr.sbp = sense_buffer; io_hdr.timeout = timeout; // on error just return prio 0 if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) return 0; if ((io_hdr.info & SG_INFO_OK_MASK) != SG_INFO_OK) return 0; snprintf(sdsname, sizeof(sdsname), "%.16s", inqBuffp + 112); if (strstr(sdsname , preferredsds)) return 1; return 0; } int getprio(struct path * pp, char * args, unsigned int timeout) { return datacore_prio(pp->dev, pp->fd, args); } multipath-tools-0.7.4/libmultipath/prioritizers/emc.c000066400000000000000000000045021320314174000230360ustar00rootroot00000000000000#include #include #include #include "sg_include.h" #include "debug.h" #include "prio.h" #include "structs.h" #define INQUIRY_CMD 0x12 #define INQUIRY_CMDLEN 6 #define pp_emc_log(prio, msg) condlog(prio, "%s: emc prio: " msg, dev) int emc_clariion_prio(const char *dev, int fd, unsigned int timeout) { unsigned char sense_buffer[128]; unsigned char sb[128]; unsigned char inqCmdBlk[INQUIRY_CMDLEN] = {INQUIRY_CMD, 1, 0xC0, 0, sizeof(sense_buffer), 0}; struct sg_io_hdr io_hdr; int ret = PRIO_UNDEF; memset(&io_hdr, 0, sizeof (struct sg_io_hdr)); memset(&sense_buffer, 0, 128); io_hdr.interface_id = 'S'; io_hdr.cmd_len = sizeof (inqCmdBlk); io_hdr.mx_sb_len = sizeof (sb); io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; io_hdr.dxfer_len = sizeof (sense_buffer); io_hdr.dxferp = sense_buffer; io_hdr.cmdp = inqCmdBlk; io_hdr.sbp = sb; io_hdr.timeout = get_prio_timeout(timeout, 60000); io_hdr.pack_id = 0; if (ioctl(fd, SG_IO, &io_hdr) < 0) { pp_emc_log(0, "sending query command failed"); goto out; } if (io_hdr.info & SG_INFO_OK_MASK) { pp_emc_log(0, "query command indicates error"); goto out; } if (/* Verify the code page - right page & revision */ sense_buffer[1] != 0xc0 || sense_buffer[9] != 0x00) { pp_emc_log(0, "path unit report page in unknown format"); goto out; } if ( /* Effective initiator type */ sense_buffer[27] != 0x03 /* * Failover mode should be set to 1 (PNR failover mode) * or 4 (ALUA failover mode). */ || (((sense_buffer[28] & 0x07) != 0x04) && ((sense_buffer[28] & 0x07) != 0x06)) /* Arraycommpath should be set to 1 */ || (sense_buffer[30] & 0x04) != 0x04) { pp_emc_log(0, "path not correctly configured for failover"); goto out; } if ( /* LUN operations should indicate normal operations */ sense_buffer[48] != 0x00) { pp_emc_log(0, "path not available for normal operations"); goto out; } /* LUN state: unbound, bound, or owned */ ret = sense_buffer[4]; /* Is the default owner equal to this path? */ /* Note this will switch to the default priority group, even if * it is not the currently active one. */ if (sense_buffer[5] == sense_buffer[8]) ret+=2; out: return(ret); } int getprio (struct path * pp, char * args, unsigned int timeout) { return emc_clariion_prio(pp->dev, pp->fd, timeout); } multipath-tools-0.7.4/libmultipath/prioritizers/hds.c000066400000000000000000000130741320314174000230540ustar00rootroot00000000000000/* * (C) Copyright HDS GmbH 2006. All Rights Reserved. * * pp_hds_modular.c * Version 2.00 * * Prioritizer for Device Mapper Multipath and HDS Storage * * Hitachis Modular Storage contains two controllers for redundancy. The * Storage internal LUN (LDEV) will normally allocated via two paths to the * server (one path per controller). For performance reasons should the server * access to a LDEV only via one controller. The other path to the other * controller is stand-by. It is also possible to allocate more as one path * for a LDEV per controller. Here is active/active access allowed. The other * paths via the other controller are stand-by. * * This prioritizer checks with inquiry command the represented LDEV and * Controller number and gives back a priority followed by this scheme: * * CONTROLLER ODD and LDEV ODD: PRIORITY 1 * CONTROLLER ODD and LDEV EVEN: PRIORITY 0 * CONTROLLER EVEN and LDEV ODD: PRIORITY 0 * CONTROLLER EVEN and LDEV EVEN: PRIORITY 1 * * In the storage you can define for each LDEV a owner controller. If the * server makes IOs via the other controller the storage will switch the * ownership automatically. In this case you can see in the storage that the * current controller is different from the default controller, but this is * absolutely no problem. * * With this prioritizer it is possible to establish a static load balancing. * Half of the LUNs are accessed via one HBA/storage controller and the other * half via the other HBA/storage controller. * * In cluster environmemnts (RAC) it also guarantees that all cluster nodes have * access to the LDEVs via the same controller. * * You can run the prioritizer manually in verbose mode: * # pp_hds_modular -v 8:224 * VENDOR: HITACHI * PRODUCT: DF600F-CM * SERIAL: 0x0105 * LDEV: 0x00C6 * CTRL: 1 * PORT: B * CTRL ODD, LDEV EVEN, PRIO 0 * * To compile this source please execute # cc pp_hds_modular.c -o /sbin/mpath_prio_hds_modular * * Changes 2006-07-16: * - Changed to forward declaration of functions * - The switch-statement was changed to a logical expression * - unlinking of the devpath now also occurs at the end of * hds_modular_prio to avoid old /tmp/.pp_balance.%u.%u.devnode * entries in /tmp-Directory * - The for-statements for passing variables where changed to * snprintf-commands in verbose mode * Changes 2006-08-10: * - Back to the old switch statements because the regular expression does * not work under RHEL4 U3 i386 * Changes 2007-06-27: * - switched from major:minor argument to device node argument * * This file is released under the GPL. * */ #include #include #include #include #include #include #include "sg_include.h" #include "debug.h" #include "prio.h" #include "structs.h" #define INQ_REPLY_LEN 255 #define INQ_CMD_CODE 0x12 #define INQ_CMD_LEN 6 #define pp_hds_log(prio, fmt, args...) \ condlog(prio, "%s: hds prio: " fmt, dev, ##args) int hds_modular_prio (const char *dev, int fd, unsigned int timeout) { int k; char vendor[9]; char product[32]; char serial[32]; char ldev[32]; char ctrl[32]; char port[32]; unsigned char inqCmdBlk[INQ_CMD_LEN] = { INQ_CMD_CODE, 0, 0, 0, INQ_REPLY_LEN, 0 }; unsigned char inqBuff[INQ_REPLY_LEN]; unsigned char *inqBuffp = inqBuff; unsigned char sense_buffer[32]; sg_io_hdr_t io_hdr; if ((ioctl (fd, SG_GET_VERSION_NUM, &k) < 0) || (k < 30000)) { pp_hds_log(0, "can't use SG ioctl interface"); return -1; } memset (&io_hdr, 0, sizeof (sg_io_hdr_t)); memset (inqBuff, 0, INQ_REPLY_LEN); io_hdr.interface_id = 'S'; io_hdr.cmd_len = sizeof (inqCmdBlk); io_hdr.mx_sb_len = sizeof (sense_buffer); io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; io_hdr.dxfer_len = INQ_REPLY_LEN; io_hdr.dxferp = inqBuff; io_hdr.cmdp = inqCmdBlk; io_hdr.sbp = sense_buffer; io_hdr.timeout = get_prio_timeout(timeout, 2000); /* TimeOut = 2 seconds */ if (ioctl (fd, SG_IO, &io_hdr) < 0) { pp_hds_log(0, "SG_IO error"); return -1; } if ((io_hdr.info & SG_INFO_OK_MASK) != SG_INFO_OK) { pp_hds_log(0, "SCSI error"); return -1; } snprintf (vendor, 9, "%.8s", inqBuffp + 8); snprintf (product, 17, "%.16s", inqBuffp + 16); snprintf (serial, 5, "%.4s", inqBuffp + 40); snprintf (ldev, 5, "%.4s", inqBuffp + 44); snprintf (ctrl, 2, "%.1s", inqBuffp + 49); snprintf (port, 2, "%.1s", inqBuffp + 50); pp_hds_log(4, "VENDOR: %s", vendor); pp_hds_log(4, "PRODUCT: %s", product); pp_hds_log(4, "SERIAL: 0x%s", serial); pp_hds_log(4, "LDEV: 0x%s", ldev); pp_hds_log(4, "CTRL: %s", ctrl); pp_hds_log(4, "PORT: %s", port); switch (ctrl[0]) { case '0': case '2': case '4': case '6': case '8': switch (ldev[3]) { case '0': case '2': case '4': case '6': case '8': case 'A': case 'C': case 'E': pp_hds_log(4, "CTRL EVEN, LDEV EVEN, PRIO 1"); return 1; break; case '1': case '3': case '5': case '7': case '9': case 'B': case 'D': case 'F': pp_hds_log(4, "CTRL EVEN, LDEV ODD, PRIO 0"); return 0; break; } break; case '1': case '3': case '5': case '7': case '9': switch (ldev[3]) { case '0': case '2': case '4': case '6': case '8': case 'A': case 'C': case 'E': pp_hds_log(4, "CTRL ODD, LDEV EVEN, PRIO 0"); return 0; break; case '1': case '3': case '5': case '7': case '9': case 'B': case 'D': case 'F': pp_hds_log(4, "CTRL ODD, LDEV ODD, PRIO 1"); return 1; break; } break; } return -1; } int getprio (struct path * pp, char * args, unsigned int timeout) { return hds_modular_prio(pp->dev, pp->fd, timeout); } multipath-tools-0.7.4/libmultipath/prioritizers/hp_sw.c000066400000000000000000000051211320314174000234100ustar00rootroot00000000000000/* * Path priority checker for HP active/standby controller * * Check the path state and sort them into groups. * There is actually a preferred path in the controller; * we should ask HP on how to retrieve that information. */ #include #include #include #include #include #include #include "sg_include.h" #include "debug.h" #include "prio.h" #include "structs.h" #define TUR_CMD_LEN 6 #define SCSI_CHECK_CONDITION 0x2 #define SCSI_COMMAND_TERMINATED 0x22 #define SG_ERR_DRIVER_SENSE 0x08 #define RECOVERED_ERROR 0x01 #define NOT_READY 0x02 #define UNIT_ATTENTION 0x06 #define HP_PATH_ACTIVE 0x04 #define HP_PATH_STANDBY 0x02 #define HP_PATH_FAILED 0x00 #define pp_hp_sw_log(prio, fmt, args...) \ condlog(prio, "%s: hp_sw prio: " fmt, dev, ##args) int hp_sw_prio(const char *dev, int fd, unsigned int timeout) { unsigned char turCmdBlk[TUR_CMD_LEN] = { 0x00, 0, 0, 0, 0, 0 }; unsigned char sb[128]; struct sg_io_hdr io_hdr; int ret = HP_PATH_FAILED; memset(&io_hdr, 0, sizeof (struct sg_io_hdr)); io_hdr.interface_id = 'S'; io_hdr.cmd_len = sizeof (turCmdBlk); io_hdr.mx_sb_len = sizeof (sb); io_hdr.dxfer_direction = SG_DXFER_NONE; io_hdr.cmdp = turCmdBlk; io_hdr.sbp = sb; io_hdr.timeout = get_prio_timeout(timeout, 60000); io_hdr.pack_id = 0; retry: if (ioctl(fd, SG_IO, &io_hdr) < 0) { pp_hp_sw_log(0, "sending tur command failed"); goto out; } io_hdr.status &= 0x7e; if ((0 == io_hdr.status) && (0 == io_hdr.host_status) && (0 == io_hdr.driver_status)) { /* Command completed normally, path is active */ ret = HP_PATH_ACTIVE; } if ((SCSI_CHECK_CONDITION == io_hdr.status) || (SCSI_COMMAND_TERMINATED == io_hdr.status) || (SG_ERR_DRIVER_SENSE == (0xf & io_hdr.driver_status))) { if (io_hdr.sbp && (io_hdr.sb_len_wr > 2)) { int sense_key, asc, asq; unsigned char * sense_buffer = io_hdr.sbp; if (sense_buffer[0] & 0x2) { sense_key = sense_buffer[1] & 0xf; asc = sense_buffer[2]; asq = sense_buffer[3]; } else { sense_key = sense_buffer[2] & 0xf; asc = sense_buffer[12]; asq = sense_buffer[13]; } if(RECOVERED_ERROR == sense_key) ret = HP_PATH_ACTIVE; if(NOT_READY == sense_key) { if (asc == 0x04 && asq == 0x02) { /* This is a standby path */ ret = HP_PATH_STANDBY; } } if(UNIT_ATTENTION == sense_key) { if (asc == 0x29) { /* Retry for device reset */ goto retry; } } } } out: return(ret); } int getprio (struct path * pp, char * args, unsigned int timeout) { return hp_sw_prio(pp->dev, pp->fd, timeout); } multipath-tools-0.7.4/libmultipath/prioritizers/iet.c000066400000000000000000000070251320314174000230560ustar00rootroot00000000000000#include #include #include #include #include #include #include #include "prio.h" #include "debug.h" #include #include "structs.h" // // This prioritizer suits iSCSI needs, makes it possible to prefer one path. // // (It's a bit of a misnomer since supports the client side [eg. open-iscsi] // instead of just "iet".) // // Usage: // prio "iet" // prio_args "preferredip=10.11.12.13" // // Uses /dev/disk/by-path to find the IP of the device. // Assigns prio 20 (high) to the preferred IP and prio 10 (low) to the rest. // // by Olivier Lambert // #define dc_log(prio, msg) condlog(prio, "%s: iet prio: " msg, dev) // // name: find_regex // @param string: string you want to search into // @param regex: the pattern used // @return result: string finded in string with regex, "none" if none char *find_regex(char * string, char * regex) { int err; regex_t preg; err = regcomp(&preg, regex, REG_EXTENDED); if (err == 0) { int match; size_t nmatch = 0; regmatch_t *pmatch = NULL; nmatch = preg.re_nsub; pmatch = malloc(sizeof(*pmatch) * nmatch); if (pmatch) { match = regexec(&preg, string, nmatch, pmatch, 0); regfree(&preg); if (match == 0) { char *result = NULL; int start = pmatch[0].rm_so; int end = pmatch[0].rm_eo; size_t size = end - start; result = malloc (sizeof(*result) * (size + 1)); if (result) { strncpy(result, &string[start], size); result[size] = '\0'; free(pmatch); return result; } } free(pmatch); } } return NULL; } // // name: inet_prio // @param // @return prio int iet_prio(const char *dev, char * args) { char preferredip_buff[255] = ""; char *preferredip = &preferredip_buff[0]; // Phase 1 : checks. If anyone fails, return prio 0. // check if args exists if (!args) { dc_log(0, "need prio_args with preferredip set"); return 0; } // check if args format is OK if (sscanf(args, "preferredip=%s", preferredip) ==1) {} else { dc_log(0, "unexpected prio_args format"); return 0; } // check if ip is not too short if (strlen(preferredip) <= 7) { dc_log(0, "prio args: preferredip too short"); return 0; } // Phase 2 : find device in /dev/disk/by-path to match device/ip DIR *dir_p; struct dirent *dir_entry_p; enum { BUFFERSIZE = 1024 }; char buffer[BUFFERSIZE]; char fullpath[BUFFERSIZE] = "/dev/disk/by-path/"; dir_p = opendir(fullpath); // loop to find device in /dev/disk/by-path while( NULL != (dir_entry_p = readdir(dir_p))) { if (dir_entry_p->d_name[0] != '.') { char path[BUFFERSIZE] = "/dev/disk/by-path/"; strcat(path,dir_entry_p->d_name); ssize_t nchars = readlink(path, buffer, sizeof(buffer)-1); if (nchars != -1) { char *device; buffer[nchars] = '\0'; device = find_regex(buffer,"(sd[a-z]+)"); // if device parsed is the right one if (device!=NULL && strncmp(device, dev, strlen(device)) == 0) { char *ip; ip = find_regex(dir_entry_p->d_name,"([0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3})"); // if prefferedip and ip fetched matches if (ip!=NULL && strncmp(ip, preferredip, strlen(ip)) == 0) { // high prio free(ip); free(device); closedir(dir_p); return 20; } free(ip); } free(device); } else { printf("error\n"); } } } // nothing found, low prio closedir(dir_p); return 10; } int getprio(struct path * pp, char * args, unsigned int timeout) { return iet_prio(pp->dev, args); } multipath-tools-0.7.4/libmultipath/prioritizers/ontap.c000066400000000000000000000141661320314174000234220ustar00rootroot00000000000000/* * Copyright 2005 Network Appliance, Inc., All Rights Reserved * Author: David Wysochanski available at davidw@netapp.com * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License v2 for more details. */ #include #include #include #include #include #include "sg_include.h" #include "debug.h" #include "prio.h" #include "structs.h" #define INQUIRY_CMD 0x12 #define INQUIRY_CMDLEN 6 #define DEFAULT_PRIOVAL 10 #define RESULTS_MAX 256 #define SG_TIMEOUT 60000 #define pp_ontap_log(prio, fmt, args...) \ condlog(prio, "%s: ontap prio: " fmt, dev, ##args) static void dump_cdb(unsigned char *cdb, int size) { int i; char buf[10*5+1]; char * p = &buf[0]; condlog(0, "- SCSI CDB: "); for (i=0; imasked_status, io_hdr->host_status, io_hdr->driver_status); if (io_hdr->sb_len_wr > 0) { condlog(0, "- SCSI sense data: "); for (i=0; isb_len_wr; i++) { p += snprintf(p, 128*(io_hdr->sb_len_wr-i), "0x%02x ", io_hdr->sbp[i]); } condlog(0, "%s", buf); } } /* * Returns: * -1: error, errno set * 0: success */ static int send_gva(const char *dev, int fd, unsigned char pg, unsigned char *results, int *results_size, unsigned int timeout) { unsigned char sb[128]; unsigned char cdb[10] = {0xc0, 0, 0x1, 0xa, 0x98, 0xa, pg, sizeof(sb), 0, 0}; struct sg_io_hdr io_hdr; int ret = -1; memset(&io_hdr, 0, sizeof (struct sg_io_hdr)); memset(results, 0, *results_size); io_hdr.interface_id = 'S'; io_hdr.cmd_len = sizeof (cdb); io_hdr.mx_sb_len = sizeof (sb); io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; io_hdr.dxfer_len = *results_size; io_hdr.dxferp = results; io_hdr.cmdp = cdb; io_hdr.sbp = sb; io_hdr.timeout = get_prio_timeout(timeout, SG_TIMEOUT); io_hdr.pack_id = 0; if (ioctl(fd, SG_IO, &io_hdr) < 0) { pp_ontap_log(0, "SG_IO ioctl failed, errno=%d", errno); dump_cdb(cdb, sizeof(cdb)); goto out; } if (io_hdr.info & SG_INFO_OK_MASK) { pp_ontap_log(0, "SCSI error"); dump_cdb(cdb, sizeof(cdb)); process_sg_error(&io_hdr); goto out; } if (results[4] != 0x0a || results[5] != 0x98 || results[6] != 0x0a ||results[7] != 0x01) { dump_cdb(cdb, sizeof(cdb)); pp_ontap_log(0, "GVA return wrong format "); pp_ontap_log(0, "results[4-7] = 0x%02x 0x%02x 0x%02x 0x%02x", results[4], results[5], results[6], results[7]); goto out; } ret = 0; out: return(ret); } /* * Returns: * -1: Unable to obtain proxy info * 0: Device _not_ proxy path * 1: Device _is_ proxy path */ static int get_proxy(const char *dev, int fd, unsigned int timeout) { unsigned char results[256]; unsigned char sb[128]; unsigned char cdb[INQUIRY_CMDLEN] = {INQUIRY_CMD, 1, 0xc1, 0, sizeof(sb), 0}; struct sg_io_hdr io_hdr; int ret = -1; memset(&results, 0, sizeof (results)); memset(&io_hdr, 0, sizeof (struct sg_io_hdr)); io_hdr.interface_id = 'S'; io_hdr.cmd_len = sizeof (cdb); io_hdr.mx_sb_len = sizeof (sb); io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; io_hdr.dxfer_len = sizeof (results); io_hdr.dxferp = results; io_hdr.cmdp = cdb; io_hdr.sbp = sb; io_hdr.timeout = get_prio_timeout(timeout, SG_TIMEOUT); io_hdr.pack_id = 0; if (ioctl(fd, SG_IO, &io_hdr) < 0) { pp_ontap_log(0, "ioctl sending inquiry command failed, " "errno=%d", errno); dump_cdb(cdb, sizeof(cdb)); goto out; } if (io_hdr.info & SG_INFO_OK_MASK) { pp_ontap_log(0, "SCSI error"); dump_cdb(cdb, sizeof(cdb)); process_sg_error(&io_hdr); goto out; } if (results[1] != 0xc1 || results[8] != 0x0a || results[9] != 0x98 || results[10] != 0x0a || results[11] != 0x0 || results[12] != 0xc1 || results[13] != 0x0) { pp_ontap_log(0,"proxy info page in unknown format - "); pp_ontap_log(0,"results[8-13]=0x%02x 0x%02x 0x%02x 0x%02x " "0x%02x 0x%02x", results[8], results[9], results[10], results[11], results[12], results[13]); dump_cdb(cdb, sizeof(cdb)); goto out; } ret = (results[19] & 0x02) >> 1; out: return(ret); } /* * Returns priority of device based on device info. * * 4: FCP non-proxy, FCP proxy unknown, or unable to determine protocol * 3: iSCSI HBA * 2: iSCSI software * 1: FCP proxy */ static int ontap_prio(const char *dev, int fd, unsigned int timeout) { unsigned char results[RESULTS_MAX]; int results_size=RESULTS_MAX; int rc; int is_proxy; int is_iscsi_software; int is_iscsi_hardware; int tot_len; is_iscsi_software = is_iscsi_hardware = is_proxy = 0; memset(&results, 0, sizeof (results)); rc = send_gva(dev, fd, 0x41, results, &results_size, timeout); if (rc >= 0) { tot_len = results[0] << 24 | results[1] << 16 | results[2] << 8 | results[3]; if (tot_len <= 8) { goto try_fcp_proxy; } if (results[8] != 0x41) { pp_ontap_log(0, "GVA page 0x41 error - " "results[8] = 0x%x", results[8]); goto try_fcp_proxy; } if ((strncmp((char *)&results[12], "ism_sw", 6) == 0) || (strncmp((char *)&results[12], "iswt", 4) == 0)) { is_iscsi_software = 1; goto prio_select; } else if (strncmp((char *)&results[12], "ism_sn", 6) == 0) { is_iscsi_hardware = 1; goto prio_select; } } else { return 0; } try_fcp_proxy: rc = get_proxy(dev, fd, timeout); if (rc >= 0) { is_proxy = rc; } prio_select: if (is_iscsi_hardware) { return 3; } else if (is_iscsi_software) { return 2; } else { if (is_proxy) { return 1; } else { /* Either non-proxy, or couldn't get proxy info */ return 4; } } } int getprio (struct path * pp, char * args, unsigned int timeout) { return ontap_prio(pp->dev, pp->fd, timeout); } multipath-tools-0.7.4/libmultipath/prioritizers/path_latency.c000066400000000000000000000215641320314174000247540ustar00rootroot00000000000000/* * (C) Copyright HUAWEI Technology Corp. 2017, All Rights Reserved. * * path_latency.c * * Prioritizer for device mapper multipath, where the corresponding priority * values of specific paths are provided by a latency algorithm. And the * latency algorithm is dependent on arguments("io_num" and "base_num"). * * The principle of the algorithm as follows: * 1. By sending a certain number "io_num" of read IOs to the current path * continuously, the IOs' average latency can be calculated. * 2. Max value and min value of average latency are constant. According to * the average latency of each path and the "base_num" of logarithmic * scale, the priority "rc" of each path can be provided. * * Author(s): Yang Feng * Revised: Guan Junxiong * * This file is released under the GPL version 2, or any later version. */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include "debug.h" #include "prio.h" #include "structs.h" #include "util.h" #define pp_pl_log(prio, fmt, args...) condlog(prio, "path_latency prio: " fmt, ##args) #define MAX_IO_NUM 200 #define MIN_IO_NUM 20 #define DEF_IO_NUM 100 #define MAX_BASE_NUM 10 #define MIN_BASE_NUM 1.01 #define DEF_BASE_NUM 1.5 #define MAX_AVG_LATENCY 100000000. /* Unit: us */ #define MIN_AVG_LATENCY 1. /* Unit: us */ #define DEFAULT_PRIORITY 0 #define USEC_PER_SEC 1000000LL #define NSEC_PER_USEC 1000LL #define DEF_BLK_SIZE 4096 static double lg_path_latency[MAX_IO_NUM]; static inline long long timeval_to_us(const struct timespec *tv) { return ((long long)tv->tv_sec * USEC_PER_SEC) + (tv->tv_nsec / NSEC_PER_USEC); } static int prepare_directio_read(int fd, int *blksz, char **pbuf, int *restore_flags) { unsigned long pgsize = getpagesize(); long flags; if (ioctl(fd, BLKBSZGET, blksz) < 0) { pp_pl_log(3,"catnnot get blocksize, set default"); *blksz = DEF_BLK_SIZE; } if (posix_memalign((void **)pbuf, pgsize, *blksz)) return -1; flags = fcntl(fd, F_GETFL); if (flags < 0) goto free_out; if (!(flags & O_DIRECT)) { flags |= O_DIRECT; if (fcntl(fd, F_SETFL, flags) < 0) goto free_out; *restore_flags = 1; } return 0; free_out: free(*pbuf); return -1; } static void cleanup_directio_read(int fd, char *buf, int restore_flags) { long flags; free(buf); if (!restore_flags) return; if ((flags = fcntl(fd, F_GETFL)) >= 0) { int ret __attribute__ ((unused)); flags &= ~O_DIRECT; /* No point in checking for errors */ ret = fcntl(fd, F_SETFL, flags); } } static int do_directio_read(int fd, unsigned int timeout, char *buf, int sz) { fd_set read_fds; struct timeval tm = { .tv_sec = timeout }; int ret; int num_read; if (lseek(fd, 0, SEEK_SET) == -1) return -1; FD_ZERO(&read_fds); FD_SET(fd, &read_fds); ret = select(fd+1, &read_fds, NULL, NULL, &tm); if (ret <= 0) return -1; num_read = read(fd, buf, sz); if (num_read != sz) return -1; return 0; } int check_args_valid(int io_num, double base_num) { if ((io_num < MIN_IO_NUM) || (io_num > MAX_IO_NUM)) { pp_pl_log(0, "args io_num is outside the valid range"); return 0; } if ((base_num < MIN_BASE_NUM) || (base_num > MAX_BASE_NUM)) { pp_pl_log(0, "args base_num is outside the valid range"); return 0; } return 1; } /* * In multipath.conf, args form: io_num=n base_num=m. For example, args are * "io_num=20 base_num=10", this function can get io_num value 20 and * base_num value 10. */ static int get_ionum_and_basenum(char *args, int *ionum, double *basenum) { char split_char[] = " \t"; char *arg, *temp; char *str, *str_inval; int i; int flag_io = 0, flag_base = 0; if ((args == NULL) || (ionum == NULL) || (basenum == NULL)) { pp_pl_log(0, "args string is NULL"); return 0; } arg = temp = STRDUP(args); if (!arg) return 0; for (i = 0; i < 2; i++) { str = get_next_string(&temp, split_char); if (!str) goto out; if (!strncmp(str, "io_num=", 7) && strlen(str) > 7) { *ionum = (int)strtoul(str + 7, &str_inval, 10); if (str == str_inval) goto out; flag_io = 1; } else if (!strncmp(str, "base_num=", 9) && strlen(str) > 9) { *basenum = strtod(str + 9, &str_inval); if (str == str_inval) goto out; flag_base = 1; } } if (!flag_io || !flag_base) goto out; if (check_args_valid(*ionum, *basenum) == 0) goto out; FREE(arg); return 1; out: FREE(arg); return 0; } double calc_standard_deviation(double *lg_path_latency, int size, double lg_avglatency) { int index; double sum = 0; for (index = 0; index < size; index++) { sum += (lg_path_latency[index] - lg_avglatency) * (lg_path_latency[index] - lg_avglatency); } sum /= (size - 1); return sqrt(sum); } /* * Do not scale the prioriy in a certain range such as [0, 1024] * because scaling will eliminate the effect of base_num. */ int calcPrio(double lg_avglatency, double lg_maxavglatency, double lg_minavglatency) { if (lg_avglatency <= lg_minavglatency) return lg_maxavglatency - lg_minavglatency; if (lg_avglatency >= lg_maxavglatency) return 0; return lg_maxavglatency - lg_avglatency; } int getprio(struct path *pp, char *args, unsigned int timeout) { int rc, temp; int index = 0; int io_num = 0; double base_num = 0; double lg_avglatency, lg_maxavglatency, lg_minavglatency; double standard_deviation; double lg_toldelay = 0; long long before, after; struct timespec tv; int blksize; char *buf; int restore_flags = 0; double lg_base; long long sum_latency = 0; long long arith_mean_lat; if (pp->fd < 0) return -1; if (get_ionum_and_basenum(args, &io_num, &base_num) == 0) { io_num = DEF_IO_NUM; base_num = DEF_BASE_NUM; pp_pl_log(0, "%s: fails to get path_latency args, set default:" "io_num=%d base_num=%.3lf", pp->dev, io_num, base_num); } memset(lg_path_latency, 0, sizeof(lg_path_latency)); lg_base = log(base_num); lg_maxavglatency = log(MAX_AVG_LATENCY) / lg_base; lg_minavglatency = log(MIN_AVG_LATENCY) / lg_base; prepare_directio_read(pp->fd, &blksize, &buf, &restore_flags); temp = io_num; while (temp-- > 0) { (void)clock_gettime(CLOCK_MONOTONIC, &tv); before = timeval_to_us(&tv); if (do_directio_read(pp->fd, timeout, buf, blksize)) { pp_pl_log(0, "%s: path down", pp->dev); cleanup_directio_read(pp->fd, buf, restore_flags); return -1; } (void)clock_gettime(CLOCK_MONOTONIC, &tv); after = timeval_to_us(&tv); /* * We assume that the latency complies with Log-normal * distribution. The logarithm of latency is in normal * distribution. */ lg_path_latency[index] = log(after - before) / lg_base; lg_toldelay += lg_path_latency[index++]; sum_latency += after - before; } cleanup_directio_read(pp->fd, buf, restore_flags); lg_avglatency = lg_toldelay / (long long)io_num; arith_mean_lat = sum_latency / (long long)io_num; pp_pl_log(4, "%s: arithmetic mean latency is (%lld us), geometric mean latency is (%lld us)", pp->dev, arith_mean_lat, (long long)pow(base_num, lg_avglatency)); if (lg_avglatency > lg_maxavglatency) { pp_pl_log(0, "%s: average latency (%lld us) is outside the thresold (%lld us)", pp->dev, (long long)pow(base_num, lg_avglatency), (long long)MAX_AVG_LATENCY); return DEFAULT_PRIORITY; } standard_deviation = calc_standard_deviation(lg_path_latency, index, lg_avglatency); /* * In calPrio(), we let prio y = f(x) = log(max, base) - log (x, base); * So if we want to let the priority of the latency outside 2 standard * deviations can be distinguished from the latency inside 2 standard * deviation, in others words at most 95% are the same and at least 5% * are different according interval estimation of normal distribution, * we should warn the user to set the base_num to be smaller if the * log(x_threshold, base) is small than 2 standard deviation. * x_threshold is derived from: * y + 1 = f(x) + 1 = f(x) + log(base, base), so x_threadshold = * base_num; Note that we only can compare the logarithm of x_threshold * with the standard deviation because the standard deviation is derived * from logarithm of latency. * * therefore , we recommend the base_num to meet the condition : * 1 <= 2 * standard_deviation */ pp_pl_log(5, "%s: standard deviation for logarithm of latency = %.6f", pp->dev, standard_deviation); if (standard_deviation <= 0.5) pp_pl_log(3, "%s: the base_num(%.3lf) is too big to distinguish different priority " "of two far-away latency. It is recommend to be set smaller", pp->dev, base_num); /* * If the standard deviation is too large , we should also warn the user */ if (standard_deviation > 4) pp_pl_log(3, "%s: the base_num(%.3lf) is too small to avoid noise disturbance " ".It is recommend to be set larger", pp->dev, base_num); rc = calcPrio(lg_avglatency, lg_maxavglatency, lg_minavglatency); return rc; } multipath-tools-0.7.4/libmultipath/prioritizers/random.c000066400000000000000000000004471320314174000235560ustar00rootroot00000000000000#include #include #include #include #include "prio.h" int getprio(struct path * pp, char * args, unsigned int timeout) { struct timeval tv; gettimeofday(&tv, NULL); srand((unsigned int)tv.tv_usec); return 1+(int) (10.0*rand()/(RAND_MAX+1.0)); } multipath-tools-0.7.4/libmultipath/prioritizers/rdac.c000066400000000000000000000044671320314174000232150ustar00rootroot00000000000000#include #include #include #include "sg_include.h" #include "debug.h" #include "prio.h" #include "structs.h" #define INQUIRY_CMD 0x12 #define INQUIRY_CMDLEN 6 #define pp_rdac_log(prio, msg) condlog(prio, "%s: rdac prio: " msg, dev) int rdac_prio(const char *dev, int fd, unsigned int timeout) { unsigned char sense_buffer[128]; unsigned char sb[128]; unsigned char inqCmdBlk[INQUIRY_CMDLEN] = {INQUIRY_CMD, 1, 0xC9, 0, sizeof(sense_buffer), 0}; struct sg_io_hdr io_hdr; int ret = 0; memset(&io_hdr, 0, sizeof (struct sg_io_hdr)); memset(sense_buffer, 0, 128); io_hdr.interface_id = 'S'; io_hdr.cmd_len = sizeof (inqCmdBlk); io_hdr.mx_sb_len = sizeof (sb); io_hdr.dxfer_direction = SG_DXFER_FROM_DEV; io_hdr.dxfer_len = sizeof (sense_buffer); io_hdr.dxferp = sense_buffer; io_hdr.cmdp = inqCmdBlk; io_hdr.sbp = sb; io_hdr.timeout = get_prio_timeout(timeout, 60000); io_hdr.pack_id = 0; if (ioctl(fd, SG_IO, &io_hdr) < 0) { pp_rdac_log(0, "sending inquiry command failed"); goto out; } if (io_hdr.info & SG_INFO_OK_MASK) { pp_rdac_log(0, "inquiry command indicates error"); goto out; } if (/* Verify the code page - right page & page identifier */ sense_buffer[1] != 0xc9 || sense_buffer[3] != 0x2c || sense_buffer[4] != 'v' || sense_buffer[5] != 'a' || sense_buffer[6] != 'c' ) { pp_rdac_log(0, "volume access control page in unknown format"); goto out; } if ( /* Current Volume Path Bit */ ( sense_buffer[8] & 0x01) == 0x01 ) { /* * This volume was owned by the controller receiving * the inquiry command. */ ret |= 0x02; } /* Volume Preferred Path Priority */ switch ( sense_buffer[9] & 0x0F ) { case 0x01: /* * Access to this volume is most preferred through * this path and other paths with this value. */ ret |= 0x04; break; case 0x02: /* * Access to this volume through this path is to be used * as a secondary path. Typically this path would be used * for fail-over situations. */ ret |= 0x01; break; default: /* Reserved values */ break; } /* For ioship mode set the bit 3 (00001000) */ if ((sense_buffer[8] >> 5) & 0x01) ret |= 0x08; out: return(ret); } int getprio (struct path * pp, char * args, unsigned int timeout) { return rdac_prio(pp->dev, pp->fd, timeout); } multipath-tools-0.7.4/libmultipath/prioritizers/sysfs.c000066400000000000000000000022711320314174000234420ustar00rootroot00000000000000/* * sysfs.c * * Copyright(c) 2016 Hannes Reinecke, SUSE Linux GmbH */ #include #include "structs.h" #include "discovery.h" #include "prio.h" static const struct { unsigned char value; char *name; } sysfs_access_state_map[] = { { 50, "active/optimized" }, { 10, "active/non-optimized" }, { 5, "lba-dependent" }, { 1, "standby" }, }; int get_exclusive_pref_arg(char *args) { char *ptr; if (args == NULL) return 0; ptr = strstr(args, "exclusive_pref_bit"); if (!ptr) return 0; if (ptr[18] != '\0' && ptr[18] != ' ' && ptr[18] != '\t') return 0; if (ptr != args && ptr[-1] != ' ' && ptr[-1] != '\t') return 0; return 1; } int getprio (struct path * pp, char * args, unsigned int timeout) { int prio = 0, rc, i; char buff[512]; int exclusive_pref; exclusive_pref = get_exclusive_pref_arg(args); rc = sysfs_get_asymmetric_access_state(pp, buff, 512); if (rc < 0) return PRIO_UNDEF; prio = 0; for (i = 0; i < 4; i++) { if (!strncmp(buff, sysfs_access_state_map[i].name, strlen(sysfs_access_state_map[i].name))) { prio = sysfs_access_state_map[i].value; break; } } if (rc > 0 && (prio != 50 || exclusive_pref)) prio += 80; return prio; } multipath-tools-0.7.4/libmultipath/prioritizers/weightedpath.c000066400000000000000000000065131320314174000247530ustar00rootroot00000000000000/* * * (C) Copyright 2008 Hewlett-Packard Development Company, L.P * * This file is released under the GPL */ /* * Prioritizer for device mapper multipath, where specific paths and the * corresponding priority values are provided as arguments. * * This prioritizer assigns the priority value provided in the configuration * file based on the comparison made between the specified paths and the path * instance for which this is called. * Paths can be specified as a regular expression of devname of the path or * as hbtl information of the path. * * Examples: * prio "weightedpath hbtl 1:.:.:. 2 4:.:.:. 4" * prio "weightedpath devname sda 10 sde 20" * * Returns zero as the default priority. */ #include #include #include "prio.h" #include "weightedpath.h" #include "config.h" #include "structs.h" #include "memory.h" #include "debug.h" #include #include "structs_vec.h" #include "print.h" #include "util.h" #define CHECK_LEN \ do { \ if ((p - str) >= (len - 1)) { \ condlog(0, "%s: %s - buffer size too small", pp->dev, pp->prio.name); \ return -1; \ } \ } while(0) static int build_serial_path(struct path *pp, char *str, int len) { char *p = str; p += snprint_path_serial(p, str + len - p, pp); CHECK_LEN; return 0; } static int build_wwn_path(struct path *pp, char *str, int len) { char *p = str; p += snprint_host_wwnn(p, str + len - p, pp); CHECK_LEN; p += snprintf(p, str + len - p, ":"); CHECK_LEN; p += snprint_host_wwpn(p, str + len - p, pp); CHECK_LEN; p += snprintf(p, str + len - p, ":"); CHECK_LEN; p += snprint_tgt_wwnn(p, str + len - p, pp); CHECK_LEN; p += snprintf(p, str + len - p, ":"); CHECK_LEN; p += snprint_tgt_wwpn(p, str + len - p, pp); CHECK_LEN; return 0; } /* main priority routine */ int prio_path_weight(struct path *pp, char *prio_args) { char path[FILE_NAME_SIZE]; char *arg; char *temp, *regex, *prio; char split_char[] = " \t"; int priority = DEFAULT_PRIORITY, path_found = 0; regex_t pathe; /* Return default priority if there is no argument */ if (!prio_args) return priority; arg = temp = STRDUP(prio_args); regex = get_next_string(&temp, split_char); /* Return default priority if the argument is not parseable */ if (!regex) { FREE(arg); return priority; } if (!strcmp(regex, HBTL)) { sprintf(path, "%d:%d:%d:%d", pp->sg_id.host_no, pp->sg_id.channel, pp->sg_id.scsi_id, pp->sg_id.lun); } else if (!strcmp(regex, DEV_NAME)) { strcpy(path, pp->dev); } else if (!strcmp(regex, SERIAL)) { if (build_serial_path(pp, path, FILE_NAME_SIZE) != 0) { FREE(arg); return priority; } } else if (!strcmp(regex, WWN)) { if (build_wwn_path(pp, path, FILE_NAME_SIZE) != 0) { FREE(arg); return priority; } } else { condlog(0, "%s: %s - Invalid arguments", pp->dev, pp->prio.name); FREE(arg); return priority; } while (!path_found) { if (!temp) break; if (!(regex = get_next_string(&temp, split_char))) break; if (!(prio = get_next_string(&temp, split_char))) break; if (!regcomp(&pathe, regex, REG_EXTENDED|REG_NOSUB)) { if (!regexec(&pathe, path, 0, NULL, 0)) { path_found = 1; priority = atoi(prio); } regfree(&pathe); } } FREE(arg); return priority; } int getprio(struct path *pp, char *args, unsigned int timeout) { return prio_path_weight(pp, args); } multipath-tools-0.7.4/libmultipath/prioritizers/weightedpath.h000066400000000000000000000004221320314174000247510ustar00rootroot00000000000000#ifndef _WEIGHTED_PATH_H #define _WEIGHTED_PATH_H #define PRIO_WEIGHTED_PATH "weightedpath" #define HBTL "hbtl" #define DEV_NAME "devname" #define SERIAL "serial" #define WWN "wwn" #define DEFAULT_PRIORITY 0 int prio_path_weight(struct path *pp, char *prio_args); #endif multipath-tools-0.7.4/libmultipath/prkey.c000066400000000000000000000066661320314174000206740ustar00rootroot00000000000000#include "structs.h" #include "file.h" #include "debug.h" #include "config.h" #include "util.h" #include "propsel.h" #include "prkey.h" #include #include #include #include #include #include #define PRKEY_READ 0 #define PRKEY_WRITE 1 static int do_prkey(int fd, char *wwid, char *keystr, int cmd) { char buf[4097]; char *ptr; off_t start = 0; int bytes; while (1) { if (lseek(fd, start, SEEK_SET) < 0) { condlog(0, "prkey file read lseek failed : %s", strerror(errno)); return 1; } bytes = read(fd, buf, 4096); if (bytes < 0) { if (errno == EINTR || errno == EAGAIN) continue; condlog(0, "failed to read from prkey file : %s", strerror(errno)); return 1; } if (!bytes) { ptr = NULL; break; } buf[bytes] = '\0'; ptr = strstr(buf, wwid); while (ptr) { if (ptr == buf || *(ptr - 1) != ' ' || *(ptr + strlen(wwid)) != '\n') ptr = strstr(ptr + strlen(wwid), wwid); else break; } if (ptr) { condlog(3, "found prkey for '%s'", wwid); ptr[strlen(wwid)] = '\0'; if (ptr - PRKEY_SIZE < buf || (ptr - PRKEY_SIZE != buf && *(ptr - PRKEY_SIZE - 1) != '\n')) { condlog(0, "malformed prkey file line for wwid: '%s'", ptr); return 1; } ptr = ptr - PRKEY_SIZE; break; } ptr = strrchr(buf, '\n'); if (ptr == NULL) { condlog(4, "couldn't file newline, assuming end of file"); break; } start = start + (ptr - buf) + 1; } if (cmd == PRKEY_READ) { if (!ptr || *ptr == '#') return 1; memcpy(keystr, ptr, PRKEY_SIZE - 1); keystr[PRKEY_SIZE - 1] = '\0'; return 0; } if (!ptr && !keystr) return 0; if (ptr) { if (lseek(fd, start + (ptr - buf), SEEK_SET) < 0) { condlog(0, "prkey write lseek failed : %s", strerror(errno)); return 1; } } if (!keystr) { if (safe_write(fd, "#", 1) < 0) { condlog(0, "failed to write to prkey file : %s", strerror(errno)); return 1; } return 0; } if (!ptr) { if (lseek(fd, 0, SEEK_END) < 0) { condlog(0, "prkey write lseek failed : %s", strerror(errno)); return 1; } } bytes = sprintf(buf, "%s %s\n", keystr, wwid); if (safe_write(fd, buf, bytes) < 0) { condlog(0, "failed to write to prkey file: %s", strerror(errno)); return 1; } return 0; } int get_prkey(struct config *conf, struct multipath *mpp, uint64_t *prkey) { int fd; int unused; int ret = 1; char keystr[PRKEY_SIZE]; if (!strlen(mpp->wwid)) goto out; fd = open_file(conf->prkeys_file, &unused, PRKEYS_FILE_HEADER); if (fd < 0) goto out; ret = do_prkey(fd, mpp->wwid, keystr, PRKEY_READ); if (ret) goto out_file; ret = !!parse_prkey(keystr, prkey); out_file: close(fd); out: return ret; } int set_prkey(struct config *conf, struct multipath *mpp, uint64_t prkey) { int fd; int can_write = 1; int ret = 1; char keystr[PRKEY_SIZE]; if (!strlen(mpp->wwid)) goto out; fd = open_file(conf->prkeys_file, &can_write, PRKEYS_FILE_HEADER); if (fd < 0) goto out; if (!can_write) { condlog(0, "cannot set prkey, prkeys file is read-only"); goto out_file; } if (prkey) { snprintf(keystr, PRKEY_SIZE, "0x%016" PRIx64, prkey); keystr[PRKEY_SIZE - 1] = '\0'; ret = do_prkey(fd, mpp->wwid, keystr, PRKEY_WRITE); } else ret = do_prkey(fd, mpp->wwid, NULL, PRKEY_WRITE); if (ret == 0) select_reservation_key(conf, mpp); if (get_be64(mpp->reservation_key) != prkey) ret = 1; out_file: close(fd); out: return ret; } multipath-tools-0.7.4/libmultipath/prkey.h000066400000000000000000000010371320314174000206640ustar00rootroot00000000000000#ifndef _PRKEY_H #define _PRKEY_H #include "structs.h" #include #define PRKEYS_FILE_HEADER \ "# Multipath persistent reservation keys, Version : 1.0\n" \ "# NOTE: this file is automatically maintained by the multipathd program.\n" \ "# You should not need to edit this file in normal circumstances.\n" \ "#\n" \ "# Format:\n" \ "# prkey wwid\n" \ "#\n" int set_prkey(struct config *conf, struct multipath *mpp, uint64_t prkey); int get_prkey(struct config *conf, struct multipath *mpp, uint64_t *prkey); #endif /* _PRKEY_H */ multipath-tools-0.7.4/libmultipath/propsel.c000066400000000000000000000576451320314174000212310ustar00rootroot00000000000000/* * Copyright (c) 2004, 2005 Christophe Varoqui * Copyright (c) 2005 Benjamin Marzinski, Redhat * Copyright (c) 2005 Kiyoshi Ueda, NEC */ #include #include "checkers.h" #include "memory.h" #include "vector.h" #include "structs.h" #include "config.h" #include "debug.h" #include "pgpolicies.h" #include "alias.h" #include "defaults.h" #include "devmapper.h" #include "prio.h" #include "discovery.h" #include "dict.h" #include "util.h" #include "prioritizers/alua_rtpg.h" #include "prkey.h" #include pgpolicyfn *pgpolicies[] = { NULL, one_path_per_group, one_group, group_by_serial, group_by_prio, group_by_node_name }; #define do_set(var, src, dest, msg) \ do { \ if (src && src->var) { \ dest = src->var; \ origin = msg; \ goto out; \ } \ } while(0) #define do_default(dest, value) \ do { \ dest = value; \ origin = "(setting: multipath internal)"; \ } while(0) #define mp_set_mpe(var) \ do_set(var, mp->mpe, mp->var, "(setting: multipath.conf multipaths section)") #define mp_set_hwe(var) \ do_set(var, mp->hwe, mp->var, "(setting: storage device configuration)") #define mp_set_ovr(var) \ do_set(var, conf->overrides, mp->var, "(setting: multipath.conf overrides section)") #define mp_set_conf(var) \ do_set(var, conf, mp->var, "(setting: multipath.conf defaults/devices section)") #define mp_set_default(var, value) \ do_default(mp->var, value) #define pp_set_mpe(var) \ do_set(var, mpe, pp->var, "(setting: multipath.conf multipaths section)") #define pp_set_hwe(var) \ do_set(var, pp->hwe, pp->var, "(setting: storage device configuration)") #define pp_set_conf(var) \ do_set(var, conf, pp->var, "(setting: multipath.conf defaults/devices section)") #define pp_set_ovr(var) \ do_set(var, conf->overrides, pp->var, "(setting: multipath.conf overrides section)") #define pp_set_default(var, value) \ do_default(pp->var, value) #define do_attr_set(var, src, shift, msg) \ do { \ if (src && (src->attribute_flags & (1 << shift))) { \ mp->attribute_flags |= (1 << shift); \ mp->var = src->var; \ origin = msg; \ goto out; \ } \ } while(0) #define set_attr_mpe(var, shift) \ do_attr_set(var, mp->mpe, shift, "(setting: multipath.conf multipaths section)") #define set_attr_conf(var, shift) \ do_attr_set(var, conf, shift, "(setting: multipath.conf defaults/devices section)") #define do_prkey_set(src, msg) \ do { \ if (src && src->prkey_source != PRKEY_SOURCE_NONE) { \ mp->prkey_source = src->prkey_source; \ mp->reservation_key = src->reservation_key; \ origin = msg; \ goto out; \ } \ } while (0) int select_mode(struct config *conf, struct multipath *mp) { char *origin; set_attr_mpe(mode, ATTR_MODE); set_attr_conf(mode, ATTR_MODE); mp->attribute_flags &= ~(1 << ATTR_MODE); return 0; out: condlog(3, "%s: mode = 0%o %s", mp->alias, mp->mode, origin); return 0; } int select_uid(struct config *conf, struct multipath *mp) { char *origin; set_attr_mpe(uid, ATTR_UID); set_attr_conf(uid, ATTR_UID); mp->attribute_flags &= ~(1 << ATTR_UID); return 0; out: condlog(3, "%s: uid = 0%o %s", mp->alias, mp->uid, origin); return 0; } int select_gid(struct config *conf, struct multipath *mp) { char *origin; set_attr_mpe(gid, ATTR_GID); set_attr_conf(gid, ATTR_GID); mp->attribute_flags &= ~(1 << ATTR_GID); return 0; out: condlog(3, "%s: gid = 0%o %s", mp->alias, mp->gid, origin); return 0; } /* * selectors : * traverse the configuration layers from most specific to most generic * stop at first explicit setting found */ int select_rr_weight(struct config *conf, struct multipath * mp) { char *origin, buff[13]; mp_set_mpe(rr_weight); mp_set_ovr(rr_weight); mp_set_hwe(rr_weight); mp_set_conf(rr_weight); mp_set_default(rr_weight, DEFAULT_RR_WEIGHT); out: print_rr_weight(buff, 13, &mp->rr_weight); condlog(3, "%s: rr_weight = %s %s", mp->alias, buff, origin); return 0; } int select_pgfailback(struct config *conf, struct multipath * mp) { char *origin, buff[13]; mp_set_mpe(pgfailback); mp_set_ovr(pgfailback); mp_set_hwe(pgfailback); mp_set_conf(pgfailback); mp_set_default(pgfailback, DEFAULT_FAILBACK); out: print_pgfailback(buff, 13, &mp->pgfailback); condlog(3, "%s: failback = %s %s", mp->alias, buff, origin); return 0; } int select_pgpolicy(struct config *conf, struct multipath * mp) { char *origin, buff[POLICY_NAME_SIZE]; if (conf->pgpolicy_flag > 0) { mp->pgpolicy = conf->pgpolicy_flag; origin = "(setting: multipath command line [-p] flag)"; goto out; } mp_set_mpe(pgpolicy); mp_set_ovr(pgpolicy); mp_set_hwe(pgpolicy); mp_set_conf(pgpolicy); mp_set_default(pgpolicy, DEFAULT_PGPOLICY); out: mp->pgpolicyfn = pgpolicies[mp->pgpolicy]; get_pgpolicy_name(buff, POLICY_NAME_SIZE, mp->pgpolicy); condlog(3, "%s: path_grouping_policy = %s %s", mp->alias, buff, origin); return 0; } int select_selector(struct config *conf, struct multipath * mp) { char *origin; mp_set_mpe(selector); mp_set_ovr(selector); mp_set_hwe(selector); mp_set_conf(selector); mp_set_default(selector, DEFAULT_SELECTOR); out: mp->selector = STRDUP(mp->selector); condlog(3, "%s: path_selector = \"%s\" %s", mp->alias, mp->selector, origin); return 0; } static void select_alias_prefix (struct config *conf, struct multipath * mp) { char *origin; mp_set_ovr(alias_prefix); mp_set_hwe(alias_prefix); mp_set_conf(alias_prefix); mp_set_default(alias_prefix, DEFAULT_ALIAS_PREFIX); out: condlog(3, "%s: alias_prefix = %s %s", mp->wwid, mp->alias_prefix, origin); } static int want_user_friendly_names(struct config *conf, struct multipath * mp) { char *origin; int user_friendly_names; do_set(user_friendly_names, mp->mpe, user_friendly_names, "(setting: multipath.conf multipaths section)"); do_set(user_friendly_names, conf->overrides, user_friendly_names, "(setting: multipath.conf overrides section)"); do_set(user_friendly_names, mp->hwe, user_friendly_names, "(setting: storage device configuration)"); do_set(user_friendly_names, conf, user_friendly_names, "(setting: multipath.conf defaults/devices section)"); do_default(user_friendly_names, DEFAULT_USER_FRIENDLY_NAMES); out: condlog(3, "%s: user_friendly_names = %s %s", mp->wwid, (user_friendly_names == USER_FRIENDLY_NAMES_ON)? "yes" : "no", origin); return (user_friendly_names == USER_FRIENDLY_NAMES_ON); } int select_alias(struct config *conf, struct multipath * mp) { char *origin = NULL; if (mp->mpe && mp->mpe->alias) { mp->alias = STRDUP(mp->mpe->alias); origin = "(setting: multipath.conf multipaths section)"; goto out; } mp->alias = NULL; if (!want_user_friendly_names(conf, mp)) goto out; select_alias_prefix(conf, mp); if (strlen(mp->alias_old) > 0) { mp->alias = use_existing_alias(mp->wwid, conf->bindings_file, mp->alias_old, mp->alias_prefix, conf->bindings_read_only); memset (mp->alias_old, 0, WWID_SIZE); origin = "(setting: using existing alias)"; } if (mp->alias == NULL) { mp->alias = get_user_friendly_alias(mp->wwid, conf->bindings_file, mp->alias_prefix, conf->bindings_read_only); origin = "(setting: user_friendly_name)"; } out: if (mp->alias == NULL) { mp->alias = STRDUP(mp->wwid); origin = "(setting: default to WWID)"; } if (mp->alias) condlog(3, "%s: alias = %s %s", mp->wwid, mp->alias, origin); return mp->alias ? 0 : 1; } void reconcile_features_with_options(const char *id, char **features, int* no_path_retry, int *retain_hwhandler) { static const char q_i_n_p[] = "queue_if_no_path"; static const char r_a_h_h[] = "retain_attached_hw_handler"; char buff[12]; if (*features == NULL) return; if (id == NULL) id = "UNKNOWN"; /* * We only use no_path_retry internally. The "queue_if_no_path" * device-mapper feature is derived from it when the map is loaded. * For consistency, "queue_if_no_path" is removed from the * internal libmultipath features string. * For backward compatibility we allow 'features "1 queue_if_no_path"'; * it's translated into "no_path_retry queue" here. */ if (strstr(*features, q_i_n_p)) { condlog(0, "%s: option 'features \"1 %s\"' is deprecated, " "please use 'no_path_retry queue' instead", id, q_i_n_p); if (*no_path_retry == NO_PATH_RETRY_UNDEF) { *no_path_retry = NO_PATH_RETRY_QUEUE; print_no_path_retry(buff, sizeof(buff), no_path_retry); condlog(3, "%s: no_path_retry = %s (inherited setting from feature '%s')", id, buff, q_i_n_p); }; /* Warn only if features string is overridden */ if (*no_path_retry != NO_PATH_RETRY_QUEUE) { print_no_path_retry(buff, sizeof(buff), no_path_retry); condlog(2, "%s: ignoring feature '%s' because no_path_retry is set to '%s'", id, q_i_n_p, buff); } remove_feature(features, q_i_n_p); } if (strstr(*features, r_a_h_h)) { condlog(0, "%s: option 'features \"1 %s\"' is deprecated", id, r_a_h_h); if (*retain_hwhandler == RETAIN_HWHANDLER_UNDEF) { condlog(3, "%s: %s = on (inherited setting from feature '%s')", id, r_a_h_h, r_a_h_h); *retain_hwhandler = RETAIN_HWHANDLER_ON; } else if (*retain_hwhandler == RETAIN_HWHANDLER_OFF) condlog(2, "%s: ignoring feature '%s' because %s is set to 'off'", id, r_a_h_h, r_a_h_h); remove_feature(features, r_a_h_h); } } int select_features(struct config *conf, struct multipath *mp) { char *origin; mp_set_mpe(features); mp_set_ovr(features); mp_set_hwe(features); mp_set_conf(features); mp_set_default(features, DEFAULT_FEATURES); out: mp->features = STRDUP(mp->features); reconcile_features_with_options(mp->alias, &mp->features, &mp->no_path_retry, &mp->retain_hwhandler); condlog(3, "%s: features = \"%s\" %s", mp->alias, mp->features, origin); return 0; } int select_hwhandler(struct config *conf, struct multipath *mp) { char *origin; mp_set_hwe(hwhandler); mp_set_conf(hwhandler); mp_set_default(hwhandler, DEFAULT_HWHANDLER); out: mp->hwhandler = STRDUP(mp->hwhandler); condlog(3, "%s: hardware_handler = \"%s\" %s", mp->alias, mp->hwhandler, origin); return 0; } /* * Current RDAC (NetApp E-Series) firmware relies * on periodic REPORT TARGET PORT GROUPS for * internal load balancing. * Using the sysfs priority checker defeats this purpose. * * Moreover, NetApp would also prefer the RDAC checker over ALUA. * (https://www.redhat.com/archives/dm-devel/2017-September/msg00326.html) */ static int check_rdac(struct path * pp) { int len; char buff[44]; len = get_vpd_sgio(pp->fd, 0xC9, buff, 44); if (len <= 0) return 0; return !(memcmp(buff + 4, "vac1", 4)); } int select_checker(struct config *conf, struct path *pp) { char *origin, *checker_name; struct checker * c = &pp->checker; if (pp->detect_checker == DETECT_CHECKER_ON) { origin = "(setting: storage device autodetected)"; if (check_rdac(pp)) { checker_name = RDAC; goto out; } else if (pp->tpgs > 0) { checker_name = TUR; goto out; } } do_set(checker_name, conf->overrides, checker_name, "(setting: multipath.conf overrides section)"); do_set(checker_name, pp->hwe, checker_name, "(setting: storage device configuration)"); do_set(checker_name, conf, checker_name, "(setting: multipath.conf defaults/devices section)"); do_default(checker_name, DEFAULT_CHECKER); out: checker_get(conf->multipath_dir, c, checker_name); condlog(3, "%s: path_checker = %s %s", pp->dev, c->name, origin); if (conf->checker_timeout) { c->timeout = conf->checker_timeout; condlog(3, "%s: checker timeout = %u s (setting: multipath.conf defaults/devices section)", pp->dev, c->timeout); } else if (sysfs_get_timeout(pp, &c->timeout) > 0) condlog(3, "%s: checker timeout = %u s (setting: kernel sysfs)", pp->dev, c->timeout); else { c->timeout = DEF_TIMEOUT; condlog(3, "%s: checker timeout = %u s (setting: multipath internal)", pp->dev, c->timeout); } return 0; } int select_getuid(struct config *conf, struct path *pp) { char *origin; pp->uid_attribute = parse_uid_attribute_by_attrs(conf->uid_attrs, pp->dev); if (pp->uid_attribute) { origin = "(setting: multipath.conf defaults section)"; goto out; } pp_set_ovr(getuid); pp_set_ovr(uid_attribute); pp_set_hwe(getuid); pp_set_hwe(uid_attribute); pp_set_conf(getuid); pp_set_conf(uid_attribute); pp_set_default(uid_attribute, DEFAULT_UID_ATTRIBUTE); out: if (pp->uid_attribute) condlog(3, "%s: uid_attribute = %s %s", pp->dev, pp->uid_attribute, origin); else if (pp->getuid) condlog(3, "%s: getuid = \"%s\" %s", pp->dev, pp->getuid, origin); return 0; } void detect_prio(struct config *conf, struct path * pp) { struct prio *p = &pp->prio; char buff[512]; char *default_prio = PRIO_ALUA; if (pp->tpgs <= 0) return; if (pp->tpgs == 2 && !check_rdac(pp)) { if (sysfs_get_asymmetric_access_state(pp, buff, 512) >= 0) default_prio = PRIO_SYSFS; } prio_get(conf->multipath_dir, p, default_prio, DEFAULT_PRIO_ARGS); } #define set_prio(dir, src, msg) \ do { \ if (src && src->prio_name) { \ prio_get(dir, p, src->prio_name, src->prio_args); \ origin = msg; \ goto out; \ } \ } while(0) int select_prio(struct config *conf, struct path *pp) { char *origin; struct mpentry * mpe; struct prio * p = &pp->prio; if (pp->detect_prio == DETECT_PRIO_ON) { detect_prio(conf, pp); if (prio_selected(p)) { origin = "(setting: storage device autodetected)"; goto out; } } mpe = find_mpe(conf->mptable, pp->wwid); set_prio(conf->multipath_dir, mpe, "(setting: multipath.conf multipaths section)"); set_prio(conf->multipath_dir, conf->overrides, "(setting: multipath.conf overrides section)"); set_prio(conf->multipath_dir, pp->hwe, "(setting: storage device configuration)"); set_prio(conf->multipath_dir, conf, "(setting: multipath.conf defaults/devices section)"); prio_get(conf->multipath_dir, p, DEFAULT_PRIO, DEFAULT_PRIO_ARGS); origin = "(setting: multipath internal)"; out: /* * fetch tpgs mode for alua, if its not already obtained */ if (!strncmp(prio_name(p), PRIO_ALUA, PRIO_NAME_LEN)) { int tpgs = 0; unsigned int timeout = conf->checker_timeout; if(!pp->tpgs && (tpgs = get_target_port_group_support(pp->fd, timeout)) >= 0) pp->tpgs = tpgs; } condlog(3, "%s: prio = %s %s", pp->dev, prio_name(p), origin); condlog(3, "%s: prio args = \"%s\" %s", pp->dev, prio_args(p), origin); return 0; } int select_no_path_retry(struct config *conf, struct multipath *mp) { char *origin = NULL; char buff[12]; if (mp->flush_on_last_del == FLUSH_IN_PROGRESS) { condlog(0, "%s: flush_on_last_del in progress", mp->alias); mp->no_path_retry = NO_PATH_RETRY_FAIL; return 0; } mp_set_mpe(no_path_retry); mp_set_ovr(no_path_retry); mp_set_hwe(no_path_retry); mp_set_conf(no_path_retry); out: print_no_path_retry(buff, 12, &mp->no_path_retry); if (origin) condlog(3, "%s: no_path_retry = %s %s", mp->alias, buff, origin); else condlog(3, "%s: no_path_retry = undef (setting: multipath internal)", mp->alias); return 0; } int select_minio_rq (struct config *conf, struct multipath * mp) { char *origin; do_set(minio_rq, mp->mpe, mp->minio, "(setting: multipath.conf multipaths section)"); do_set(minio_rq, conf->overrides, mp->minio, "(setting: multipath.conf overrides section)"); do_set(minio_rq, mp->hwe, mp->minio, "(setting: storage device configuration)"); do_set(minio_rq, conf, mp->minio, "(setting: multipath.conf defaults/devices section)"); do_default(mp->minio, DEFAULT_MINIO_RQ); out: condlog(3, "%s: minio = %i %s", mp->alias, mp->minio, origin); return 0; } int select_minio_bio (struct config *conf, struct multipath * mp) { char *origin; mp_set_mpe(minio); mp_set_ovr(minio); mp_set_hwe(minio); mp_set_conf(minio); mp_set_default(minio, DEFAULT_MINIO); out: condlog(3, "%s: minio = %i %s", mp->alias, mp->minio, origin); return 0; } int select_minio(struct config *conf, struct multipath *mp) { unsigned int minv_dmrq[3] = {1, 1, 0}; if (VERSION_GE(conf->version, minv_dmrq)) return select_minio_rq(conf, mp); else return select_minio_bio(conf, mp); } int select_fast_io_fail(struct config *conf, struct multipath *mp) { char *origin, buff[12]; mp_set_ovr(fast_io_fail); mp_set_hwe(fast_io_fail); mp_set_conf(fast_io_fail); mp_set_default(fast_io_fail, DEFAULT_FAST_IO_FAIL); out: print_fast_io_fail(buff, 12, &mp->fast_io_fail); condlog(3, "%s: fast_io_fail_tmo = %s %s", mp->alias, buff, origin); return 0; } int select_dev_loss(struct config *conf, struct multipath *mp) { char *origin, buff[12]; mp_set_ovr(dev_loss); mp_set_hwe(dev_loss); mp_set_conf(dev_loss); mp->dev_loss = 0; return 0; out: print_dev_loss(buff, 12, &mp->dev_loss); condlog(3, "%s: dev_loss_tmo = %s %s", mp->alias, buff, origin); return 0; } int select_flush_on_last_del(struct config *conf, struct multipath *mp) { char *origin; if (mp->flush_on_last_del == FLUSH_IN_PROGRESS) return 0; mp_set_mpe(flush_on_last_del); mp_set_ovr(flush_on_last_del); mp_set_hwe(flush_on_last_del); mp_set_conf(flush_on_last_del); mp_set_default(flush_on_last_del, DEFAULT_FLUSH); out: condlog(3, "%s: flush_on_last_del = %s %s", mp->alias, (mp->flush_on_last_del == FLUSH_ENABLED)? "yes" : "no", origin); return 0; } int select_reservation_key(struct config *conf, struct multipath *mp) { char *origin, buff[PRKEY_SIZE]; char *from_file = ""; uint64_t prkey = 0; do_prkey_set(mp->mpe, "(setting: multipath.conf multipaths section)"); do_prkey_set(conf, "(setting: multipath.conf defaults/devices section)"); put_be64(mp->reservation_key, 0); mp->prkey_source = PRKEY_SOURCE_NONE; return 0; out: if (mp->prkey_source == PRKEY_SOURCE_FILE) { from_file = " (from prkeys file)"; if (get_prkey(conf, mp, &prkey) != 0) put_be64(mp->reservation_key, 0); else put_be64(mp->reservation_key, prkey); } print_reservation_key(buff, PRKEY_SIZE, mp->reservation_key, mp->prkey_source); condlog(3, "%s: reservation_key = %s %s%s", mp->alias, buff, origin, from_file); return 0; } int select_retain_hwhandler(struct config *conf, struct multipath *mp) { char *origin; unsigned int minv_dm_retain[3] = {1, 5, 0}; if (!VERSION_GE(conf->version, minv_dm_retain)) { mp->retain_hwhandler = RETAIN_HWHANDLER_OFF; origin = "(setting: WARNING, requires kernel dm-mpath version >= 1.5.0)"; goto out; } if (get_linux_version_code() >= KERNEL_VERSION(4, 3, 0)) { mp->retain_hwhandler = RETAIN_HWHANDLER_ON; origin = "(setting: implied in kernel >= 4.3.0)"; goto out; } mp_set_ovr(retain_hwhandler); mp_set_hwe(retain_hwhandler); mp_set_conf(retain_hwhandler); mp_set_default(retain_hwhandler, DEFAULT_RETAIN_HWHANDLER); out: condlog(3, "%s: retain_attached_hw_handler = %s %s", mp->alias, (mp->retain_hwhandler == RETAIN_HWHANDLER_ON)? "yes" : "no", origin); return 0; } int select_detect_prio(struct config *conf, struct path *pp) { char *origin; pp_set_ovr(detect_prio); pp_set_hwe(detect_prio); pp_set_conf(detect_prio); pp_set_default(detect_prio, DEFAULT_DETECT_PRIO); out: condlog(3, "%s: detect_prio = %s %s", pp->dev, (pp->detect_prio == DETECT_PRIO_ON)? "yes" : "no", origin); return 0; } int select_detect_checker(struct config *conf, struct path *pp) { char *origin; pp_set_ovr(detect_checker); pp_set_hwe(detect_checker); pp_set_conf(detect_checker); pp_set_default(detect_checker, DEFAULT_DETECT_CHECKER); out: condlog(3, "%s: detect_checker = %s %s", pp->dev, (pp->detect_checker == DETECT_CHECKER_ON)? "yes" : "no", origin); return 0; } int select_deferred_remove(struct config *conf, struct multipath *mp) { char *origin; #ifndef LIBDM_API_DEFERRED mp->deferred_remove = DEFERRED_REMOVE_OFF; origin = "(setting: WARNING, not compiled with support)"; goto out; #endif if (mp->deferred_remove == DEFERRED_REMOVE_IN_PROGRESS) { condlog(3, "%s: deferred remove in progress", mp->alias); return 0; } mp_set_mpe(deferred_remove); mp_set_ovr(deferred_remove); mp_set_hwe(deferred_remove); mp_set_conf(deferred_remove); mp_set_default(deferred_remove, DEFAULT_DEFERRED_REMOVE); out: condlog(3, "%s: deferred_remove = %s %s", mp->alias, (mp->deferred_remove == DEFERRED_REMOVE_ON)? "yes" : "no", origin); return 0; } int select_delay_watch_checks(struct config *conf, struct multipath *mp) { char *origin, buff[12]; mp_set_mpe(delay_watch_checks); mp_set_ovr(delay_watch_checks); mp_set_hwe(delay_watch_checks); mp_set_conf(delay_watch_checks); mp_set_default(delay_watch_checks, DEFAULT_DELAY_CHECKS); out: print_off_int_undef(buff, 12, &mp->delay_watch_checks); condlog(3, "%s: delay_watch_checks = %s %s", mp->alias, buff, origin); return 0; } int select_delay_wait_checks(struct config *conf, struct multipath *mp) { char *origin, buff[12]; mp_set_mpe(delay_wait_checks); mp_set_ovr(delay_wait_checks); mp_set_hwe(delay_wait_checks); mp_set_conf(delay_wait_checks); mp_set_default(delay_wait_checks, DEFAULT_DELAY_CHECKS); out: print_off_int_undef(buff, 12, &mp->delay_wait_checks); condlog(3, "%s: delay_wait_checks = %s %s", mp->alias, buff, origin); return 0; } int select_marginal_path_err_sample_time(struct config *conf, struct multipath *mp) { char *origin, buff[12]; mp_set_mpe(marginal_path_err_sample_time); mp_set_ovr(marginal_path_err_sample_time); mp_set_hwe(marginal_path_err_sample_time); mp_set_conf(marginal_path_err_sample_time); mp_set_default(marginal_path_err_sample_time, DEFAULT_ERR_CHECKS); out: print_off_int_undef(buff, 12, &mp->marginal_path_err_sample_time); condlog(3, "%s: marginal_path_err_sample_time = %s %s", mp->alias, buff, origin); return 0; } int select_marginal_path_err_rate_threshold(struct config *conf, struct multipath *mp) { char *origin, buff[12]; mp_set_mpe(marginal_path_err_rate_threshold); mp_set_ovr(marginal_path_err_rate_threshold); mp_set_hwe(marginal_path_err_rate_threshold); mp_set_conf(marginal_path_err_rate_threshold); mp_set_default(marginal_path_err_rate_threshold, DEFAULT_ERR_CHECKS); out: print_off_int_undef(buff, 12, &mp->marginal_path_err_rate_threshold); condlog(3, "%s: marginal_path_err_rate_threshold = %s %s", mp->alias, buff, origin); return 0; } int select_marginal_path_err_recheck_gap_time(struct config *conf, struct multipath *mp) { char *origin, buff[12]; mp_set_mpe(marginal_path_err_recheck_gap_time); mp_set_ovr(marginal_path_err_recheck_gap_time); mp_set_hwe(marginal_path_err_recheck_gap_time); mp_set_conf(marginal_path_err_recheck_gap_time); mp_set_default(marginal_path_err_recheck_gap_time, DEFAULT_ERR_CHECKS); out: print_off_int_undef(buff, 12, &mp->marginal_path_err_recheck_gap_time); condlog(3, "%s: marginal_path_err_recheck_gap_time = %s %s", mp->alias, buff, origin); return 0; } int select_marginal_path_double_failed_time(struct config *conf, struct multipath *mp) { char *origin, buff[12]; mp_set_mpe(marginal_path_double_failed_time); mp_set_ovr(marginal_path_double_failed_time); mp_set_hwe(marginal_path_double_failed_time); mp_set_conf(marginal_path_double_failed_time); mp_set_default(marginal_path_double_failed_time, DEFAULT_ERR_CHECKS); out: print_off_int_undef(buff, 12, &mp->marginal_path_double_failed_time); condlog(3, "%s: marginal_path_double_failed_time = %s %s", mp->alias, buff, origin); return 0; } int select_skip_kpartx (struct config *conf, struct multipath * mp) { char *origin; mp_set_mpe(skip_kpartx); mp_set_ovr(skip_kpartx); mp_set_hwe(skip_kpartx); mp_set_conf(skip_kpartx); mp_set_default(skip_kpartx, DEFAULT_SKIP_KPARTX); out: condlog(3, "%s: skip_kpartx = %s %s", mp->alias, (mp->skip_kpartx == SKIP_KPARTX_ON)? "yes" : "no", origin); return 0; } int select_max_sectors_kb(struct config *conf, struct multipath * mp) { char *origin; mp_set_mpe(max_sectors_kb); mp_set_ovr(max_sectors_kb); mp_set_hwe(max_sectors_kb); mp_set_conf(max_sectors_kb); mp_set_default(max_sectors_kb, DEFAULT_MAX_SECTORS_KB); /* * In the default case, we will not modify max_sectors_kb in sysfs * (see sysfs_set_max_sectors_kb()). * Don't print a log message here to avoid user confusion. */ return 0; out: condlog(3, "%s: max_sectors_kb = %i %s", mp->alias, mp->max_sectors_kb, origin); return 0; } multipath-tools-0.7.4/libmultipath/propsel.h000066400000000000000000000043361320314174000212230ustar00rootroot00000000000000int select_rr_weight (struct config *conf, struct multipath * mp); int select_pgfailback (struct config *conf, struct multipath * mp); int select_pgpolicy (struct config *conf, struct multipath * mp); int select_selector (struct config *conf, struct multipath * mp); int select_alias (struct config *conf, struct multipath * mp); int select_features (struct config *conf, struct multipath * mp); int select_hwhandler (struct config *conf, struct multipath * mp); int select_checker(struct config *conf, struct path *pp); int select_getuid (struct config *conf, struct path * pp); int select_prio (struct config *conf, struct path * pp); int select_no_path_retry(struct config *conf, struct multipath *mp); int select_flush_on_last_del(struct config *conf, struct multipath *mp); int select_minio(struct config *conf, struct multipath *mp); int select_mode(struct config *conf, struct multipath *mp); int select_uid(struct config *conf, struct multipath *mp); int select_gid(struct config *conf, struct multipath *mp); int select_fast_io_fail(struct config *conf, struct multipath *mp); int select_dev_loss(struct config *conf, struct multipath *mp); int select_reservation_key(struct config *conf, struct multipath *mp); int select_retain_hwhandler (struct config *conf, struct multipath * mp); int select_detect_prio(struct config *conf, struct path * pp); int select_detect_checker(struct config *conf, struct path * pp); int select_deferred_remove(struct config *conf, struct multipath *mp); int select_delay_watch_checks (struct config *conf, struct multipath * mp); int select_delay_wait_checks (struct config *conf, struct multipath * mp); int select_skip_kpartx (struct config *conf, struct multipath * mp); int select_max_sectors_kb (struct config *conf, struct multipath * mp); int select_marginal_path_err_sample_time(struct config *conf, struct multipath *mp); int select_marginal_path_err_rate_threshold(struct config *conf, struct multipath *mp); int select_marginal_path_err_recheck_gap_time(struct config *conf, struct multipath *mp); int select_marginal_path_double_failed_time(struct config *conf, struct multipath *mp); void reconcile_features_with_options(const char *id, char **features, int* no_path_retry, int *retain_hwhandler); multipath-tools-0.7.4/libmultipath/sg_include.h000066400000000000000000000026341320314174000216520ustar00rootroot00000000000000#define __user #include #ifndef DID_OK #define DID_OK 0x00 /* NO error */ #define DID_NO_CONNECT 0x01 /* Couldn't connect before timeout period */ #define DID_BUS_BUSY 0x02 /* BUS stayed busy through time out period */ #define DID_TIME_OUT 0x03 /* TIMED OUT for other reason */ #define DID_BAD_TARGET 0x04 /* BAD target. */ #define DID_ABORT 0x05 /* Told to abort for some other reason */ #define DID_PARITY 0x06 /* Parity error */ #define DID_ERROR 0x07 /* Internal error */ #define DID_RESET 0x08 /* Reset by somebody. */ #define DID_BAD_INTR 0x09 /* Got an interrupt we weren't expecting. */ #define DID_PASSTHROUGH 0x0a /* Force command past mid-layer */ #define DID_SOFT_ERROR 0x0b /* The low level driver just wish a retry */ #define DID_IMM_RETRY 0x0c /* Retry without decrementing retry count */ #define DID_REQUEUE 0x0d /* Requeue command (no immediate retry) also * without decrementing the retry count */ #define DID_TRANSPORT_DISRUPTED 0x0e /* Transport error disrupted execution * and the driver blocked the port to * recover the link. Transport class will * retry or fail IO */ #define DID_TRANSPORT_FAILFAST 0x0f /* Transport class fastfailed the io */ #endif multipath-tools-0.7.4/libmultipath/structs.c000066400000000000000000000254541320314174000212450ustar00rootroot00000000000000/* * Copyright (c) 2004, 2005 Christophe Varoqui * Copyright (c) 2004 Stefan Bader, IBM */ #include #include #include #include #include "checkers.h" #include "memory.h" #include "vector.h" #include "util.h" #include "structs.h" #include "config.h" #include "debug.h" #include "structs_vec.h" #include "blacklist.h" #include "prio.h" #include "prioritizers/alua_spc3.h" struct adapter_group * alloc_adaptergroup(void) { struct adapter_group *agp; agp = (struct adapter_group *)MALLOC(sizeof(struct adapter_group)); if (!agp) return NULL; agp->host_groups = vector_alloc(); if (!agp->host_groups) { FREE(agp); agp = NULL; } return agp; } void free_adaptergroup(vector adapters) { int i; struct adapter_group *agp; vector_foreach_slot(adapters, agp, i) { free_hostgroup(agp->host_groups); FREE(agp); } vector_free(adapters); } void free_hostgroup(vector hostgroups) { int i; struct host_group *hgp; if (!hostgroups) return; vector_foreach_slot(hostgroups, hgp, i) { vector_free(hgp->paths); FREE(hgp); } vector_free(hostgroups); } struct host_group * alloc_hostgroup(void) { struct host_group *hgp; hgp = (struct host_group *)MALLOC(sizeof(struct host_group)); if (!hgp) return NULL; hgp->paths = vector_alloc(); if (!hgp->paths) { FREE(hgp); hgp = NULL; } return hgp; } struct path * alloc_path (void) { struct path * pp; pp = (struct path *)MALLOC(sizeof(struct path)); if (pp) { pp->sg_id.host_no = -1; pp->sg_id.channel = -1; pp->sg_id.scsi_id = -1; pp->sg_id.lun = -1; pp->sg_id.proto_id = SCSI_PROTOCOL_UNSPEC; pp->fd = -1; pp->tpgs = TPGS_UNDEF; pp->priority = PRIO_UNDEF; checker_clear(&pp->checker); } return pp; } void free_path (struct path * pp) { if (!pp) return; if (checker_selected(&pp->checker)) checker_put(&pp->checker); if (prio_selected(&pp->prio)) prio_put(&pp->prio); if (pp->fd >= 0) close(pp->fd); if (pp->udev) { udev_device_unref(pp->udev); pp->udev = NULL; } FREE(pp); } void free_pathvec (vector vec, enum free_path_mode free_paths) { int i; struct path * pp; if (!vec) return; if (free_paths == FREE_PATHS) vector_foreach_slot(vec, pp, i) free_path(pp); vector_free(vec); } struct pathgroup * alloc_pathgroup (void) { struct pathgroup * pgp; pgp = (struct pathgroup *)MALLOC(sizeof(struct pathgroup)); if (!pgp) return NULL; pgp->paths = vector_alloc(); if (!pgp->paths) { FREE(pgp); pgp = NULL; } return pgp; } void free_pathgroup (struct pathgroup * pgp, enum free_path_mode free_paths) { if (!pgp) return; free_pathvec(pgp->paths, free_paths); FREE(pgp); } void free_pgvec (vector pgvec, enum free_path_mode free_paths) { int i; struct pathgroup * pgp; if (!pgvec) return; vector_foreach_slot(pgvec, pgp, i) free_pathgroup(pgp, free_paths); vector_free(pgvec); } struct multipath * alloc_multipath (void) { struct multipath * mpp; mpp = (struct multipath *)MALLOC(sizeof(struct multipath)); if (mpp) { mpp->bestpg = 1; mpp->mpcontext = NULL; mpp->no_path_retry = NO_PATH_RETRY_UNDEF; mpp->fast_io_fail = MP_FAST_IO_FAIL_UNSET; } return mpp; } void free_multipath_attributes(struct multipath *mpp) { if (!mpp) return; if (mpp->selector) { FREE(mpp->selector); mpp->selector = NULL; } if (mpp->features) { FREE(mpp->features); mpp->features = NULL; } if (mpp->hwhandler) { FREE(mpp->hwhandler); mpp->hwhandler = NULL; } } void free_multipath (struct multipath * mpp, enum free_path_mode free_paths) { if (!mpp) return; free_multipath_attributes(mpp); if (mpp->alias) { FREE(mpp->alias); mpp->alias = NULL; } if (mpp->dmi) { FREE(mpp->dmi); mpp->dmi = NULL; } free_pathvec(mpp->paths, free_paths); free_pgvec(mpp->pg, free_paths); FREE_PTR(mpp->mpcontext); FREE(mpp); } void drop_multipath (vector mpvec, char * wwid, enum free_path_mode free_paths) { int i; struct multipath * mpp; if (!mpvec) return; vector_foreach_slot (mpvec, mpp, i) { if (!strncmp(mpp->wwid, wwid, WWID_SIZE)) { free_multipath(mpp, free_paths); vector_del_slot(mpvec, i); return; } } } void free_multipathvec (vector mpvec, enum free_path_mode free_paths) { int i; struct multipath * mpp; if (!mpvec) return; vector_foreach_slot (mpvec, mpp, i) free_multipath(mpp, free_paths); vector_free(mpvec); } int store_path (vector pathvec, struct path * pp) { int err = 0; if (!strlen(pp->dev_t)) { condlog(2, "%s: Empty device number", pp->dev); err++; } if (!strlen(pp->dev)) { condlog(2, "%s: Empty device name", pp->dev_t); err++; } if (err > 1) return 1; if (!vector_alloc_slot(pathvec)) return 1; vector_set_slot(pathvec, pp); return 0; } int store_pathgroup (vector pgvec, struct pathgroup * pgp) { if (!vector_alloc_slot(pgvec)) return 1; vector_set_slot(pgvec, pgp); return 0; } int store_hostgroup(vector hostgroupvec, struct host_group * hgp) { if (!vector_alloc_slot(hostgroupvec)) return 1; vector_set_slot(hostgroupvec, hgp); return 0; } int store_adaptergroup(vector adapters, struct adapter_group * agp) { if (!vector_alloc_slot(adapters)) return 1; vector_set_slot(adapters, agp); return 0; } struct multipath * find_mp_by_minor (vector mpvec, int minor) { int i; struct multipath * mpp; if (!mpvec) return NULL; vector_foreach_slot (mpvec, mpp, i) { if (!mpp->dmi) continue; if (mpp->dmi->minor == minor) return mpp; } return NULL; } struct multipath * find_mp_by_wwid (vector mpvec, char * wwid) { int i; struct multipath * mpp; if (!mpvec) return NULL; vector_foreach_slot (mpvec, mpp, i) if (!strncmp(mpp->wwid, wwid, WWID_SIZE)) return mpp; return NULL; } struct multipath * find_mp_by_alias (vector mpvec, char * alias) { int i; int len; struct multipath * mpp; if (!mpvec) return NULL; len = strlen(alias); if (!len) return NULL; vector_foreach_slot (mpvec, mpp, i) { if (strlen(mpp->alias) == len && !strncmp(mpp->alias, alias, len)) return mpp; } return NULL; } struct multipath * find_mp_by_str (vector mpvec, char * str) { int minor; if (sscanf(str, "dm-%d", &minor) == 1) return find_mp_by_minor(mpvec, minor); else return find_mp_by_alias(mpvec, str); } struct path * find_path_by_dev (vector pathvec, char * dev) { int i; struct path * pp; if (!pathvec) return NULL; vector_foreach_slot (pathvec, pp, i) if (!strcmp(pp->dev, dev)) return pp; condlog(4, "%s: dev not found in pathvec", dev); return NULL; } struct path * find_path_by_devt (vector pathvec, char * dev_t) { int i; struct path * pp; if (!pathvec) return NULL; vector_foreach_slot (pathvec, pp, i) if (!strcmp(pp->dev_t, dev_t)) return pp; condlog(4, "%s: dev_t not found in pathvec", dev_t); return NULL; } int pathcountgr(struct pathgroup *pgp, int state) { struct path *pp; int count = 0; int i; vector_foreach_slot (pgp->paths, pp, i) if ((pp->state == state) || (state == PATH_WILD)) count++; return count; } int pathcount(struct multipath *mpp, int state) { struct pathgroup *pgp; int count = 0; int i; if (mpp->pg) { vector_foreach_slot (mpp->pg, pgp, i) count += pathcountgr(pgp, state); } return count; } int pathcmp(struct pathgroup *pgp, struct pathgroup *cpgp) { int i, j; struct path *pp, *cpp; int pnum = 0, found = 0; vector_foreach_slot(pgp->paths, pp, i) { pnum++; vector_foreach_slot(cpgp->paths, cpp, j) { if ((long)pp == (long)cpp) { found++; break; } } } return pnum - found; } struct path * first_path (struct multipath * mpp) { struct pathgroup * pgp; if (!mpp->pg) return NULL; pgp = VECTOR_SLOT(mpp->pg, 0); return pgp?VECTOR_SLOT(pgp->paths, 0):NULL; } void setup_feature(struct multipath *mpp, char *feature) { if (!strncmp(feature, "queue_if_no_path", 16)) { if (mpp->no_path_retry <= NO_PATH_RETRY_UNDEF) mpp->no_path_retry = NO_PATH_RETRY_QUEUE; else condlog(1, "%s: ignoring feature queue_if_no_path because no_path_retry = %d", mpp->alias, mpp->no_path_retry); } else if (!strcmp(feature, "retain_attached_hw_handler")) { if (mpp->retain_hwhandler != RETAIN_HWHANDLER_OFF) mpp->retain_hwhandler = RETAIN_HWHANDLER_ON; else condlog(1, "%s: ignoring feature 'retain_attached_hw_handler'", mpp->alias); } } int add_feature(char **f, const char *n) { int c = 0, d, l; char *e, *t; if (!f) return 1; /* Nothing to do */ if (!n || *n == '0') return 0; if (strchr(n, ' ') != NULL) { condlog(0, "internal error: feature \"%s\" contains spaces", n); return 1; } /* default feature is null */ if(!*f) { l = asprintf(&t, "1 %s", n); if(l == -1) return 1; *f = t; return 0; } /* Check if feature is already present */ if (strstr(*f, n)) return 0; /* Get feature count */ c = strtoul(*f, &e, 10); if (*f == e || (*e != ' ' && *e != '\0')) { condlog(0, "parse error in feature string \"%s\"", *f); return 1; } /* Add 1 digit and 1 space */ l = strlen(e) + strlen(n) + 2; c++; /* Check if we need more digits for feature count */ for (d = c; d >= 10; d /= 10) l++; t = MALLOC(l + 1); if (!t) return 1; /* e: old feature string with leading space, or "" */ if (*e == ' ') while (*(e + 1) == ' ') e++; snprintf(t, l + 1, "%0d%s %s", c, e, n); FREE(*f); *f = t; return 0; } int remove_feature(char **f, const char *o) { int c = 0, d, l; char *e, *p, *n; const char *q; if (!f || !*f) return 1; /* Nothing to do */ if (!o || *o == '\0') return 0; /* Check if not present */ if (!strstr(*f, o)) return 0; /* Get feature count */ c = strtoul(*f, &e, 10); if (*f == e) /* parse error */ return 1; /* Normalize features */ while (*o == ' ') { o++; } /* Just spaces, return */ if (*o == '\0') return 0; q = o + strlen(o); while (*q == ' ') q--; d = (int)(q - o); /* Update feature count */ c--; q = o; while (q[0] != '\0') { if (q[0] == ' ' && q[1] != ' ' && q[1] != '\0') c--; q++; } /* Quick exit if all features have been removed */ if (c == 0) { n = MALLOC(2); if (!n) return 1; strcpy(n, "0"); goto out; } /* Search feature to be removed */ e = strstr(*f, o); if (!e) /* Not found, return */ return 0; /* Update feature count space */ l = strlen(*f) - d; n = MALLOC(l + 1); if (!n) return 1; /* Copy the feature count */ sprintf(n, "%0d", c); /* * Copy existing features up to the feature * about to be removed */ p = strchr(*f, ' '); if (!p) { /* Internal error, feature string inconsistent */ FREE(n); return 1; } while (*p == ' ') p++; p--; if (e != p) { do { e--; d++; } while (*e == ' '); e++; d--; strncat(n, p, (size_t)(e - p)); p += (size_t)(e - p); } /* Skip feature to be removed */ p += d; /* Copy remaining features */ if (strlen(p)) { while (*p == ' ') p++; if (strlen(p)) { p--; strcat(n, p); } } out: FREE(*f); *f = n; return 0; } multipath-tools-0.7.4/libmultipath/structs.h000066400000000000000000000202671320314174000212470ustar00rootroot00000000000000#ifndef _STRUCTS_H #define _STRUCTS_H #include #include #include "prio.h" #include "byteorder.h" #define WWID_SIZE 128 #define SERIAL_SIZE 65 #define NODE_NAME_SIZE 224 #define PATH_STR_SIZE 16 #define PARAMS_SIZE 4096 #define FILE_NAME_SIZE 256 #define CALLOUT_MAX_SIZE 256 #define BLK_DEV_SIZE 33 #define PATH_SIZE 512 #define NAME_SIZE 512 #define HOST_NAME_LEN 16 #define SLOT_NAME_SIZE 40 #define PRKEY_SIZE 19 #define SCSI_VENDOR_SIZE 9 #define SCSI_PRODUCT_SIZE 17 #define SCSI_REV_SIZE 5 #define SCSI_STATE_SIZE 19 #define NO_PATH_RETRY_UNDEF 0 #define NO_PATH_RETRY_FAIL -1 #define NO_PATH_RETRY_QUEUE -2 enum free_path_mode { KEEP_PATHS, FREE_PATHS }; enum rr_weight_mode { RR_WEIGHT_UNDEF, RR_WEIGHT_NONE, RR_WEIGHT_PRIO }; enum failback_mode { FAILBACK_UNDEF, FAILBACK_MANUAL, FAILBACK_IMMEDIATE, FAILBACK_FOLLOWOVER }; enum sysfs_buses { SYSFS_BUS_UNDEF, SYSFS_BUS_SCSI, SYSFS_BUS_IDE, SYSFS_BUS_CCW, SYSFS_BUS_CCISS, SYSFS_BUS_RBD, SYSFS_BUS_NVME, }; enum pathstates { PSTATE_UNDEF, PSTATE_FAILED, PSTATE_ACTIVE }; enum pgstates { PGSTATE_UNDEF, PGSTATE_ENABLED, PGSTATE_DISABLED, PGSTATE_ACTIVE }; enum yes_no_states { YN_NO, YN_YES, }; enum queue_without_daemon_states { QUE_NO_DAEMON_OFF = YN_NO, QUE_NO_DAEMON_ON = YN_YES, QUE_NO_DAEMON_FORCE, }; enum attribute_bits { ATTR_UID, ATTR_GID, ATTR_MODE, }; enum yes_no_undef_states { YNU_UNDEF, YNU_NO, YNU_YES, }; enum flush_states { FLUSH_UNDEF = YNU_UNDEF, FLUSH_DISABLED = YNU_NO, FLUSH_ENABLED = YNU_YES, FLUSH_IN_PROGRESS, }; enum log_checker_err_states { LOG_CHKR_ERR_ALWAYS, LOG_CHKR_ERR_ONCE, }; enum user_friendly_names_states { USER_FRIENDLY_NAMES_UNDEF = YNU_UNDEF, USER_FRIENDLY_NAMES_OFF = YNU_NO, USER_FRIENDLY_NAMES_ON = YNU_YES, }; enum retain_hwhandler_states { RETAIN_HWHANDLER_UNDEF = YNU_UNDEF, RETAIN_HWHANDLER_OFF = YNU_NO, RETAIN_HWHANDLER_ON = YNU_YES, }; enum detect_prio_states { DETECT_PRIO_UNDEF = YNU_UNDEF, DETECT_PRIO_OFF = YNU_NO, DETECT_PRIO_ON = YNU_YES, }; enum detect_checker_states { DETECT_CHECKER_UNDEF = YNU_UNDEF, DETECT_CHECKER_OFF = YNU_NO, DETECT_CHECKER_ON = YNU_YES, }; enum deferred_remove_states { DEFERRED_REMOVE_UNDEF = YNU_UNDEF, DEFERRED_REMOVE_OFF = YNU_NO, DEFERRED_REMOVE_ON = YNU_YES, DEFERRED_REMOVE_IN_PROGRESS, }; enum skip_kpartx_states { SKIP_KPARTX_UNDEF = YNU_UNDEF, SKIP_KPARTX_OFF = YNU_NO, SKIP_KPARTX_ON = YNU_YES, }; enum max_sectors_kb_states { MAX_SECTORS_KB_UNDEF = 0, MAX_SECTORS_KB_MIN = 4, /* can't be smaller than page size */ }; enum scsi_protocol { SCSI_PROTOCOL_FCP = 0, /* Fibre Channel */ SCSI_PROTOCOL_SPI = 1, /* parallel SCSI */ SCSI_PROTOCOL_SSA = 2, /* Serial Storage Architecture - Obsolete */ SCSI_PROTOCOL_SBP = 3, /* firewire */ SCSI_PROTOCOL_SRP = 4, /* Infiniband RDMA */ SCSI_PROTOCOL_ISCSI = 5, SCSI_PROTOCOL_SAS = 6, SCSI_PROTOCOL_ADT = 7, /* Media Changers */ SCSI_PROTOCOL_ATA = 8, SCSI_PROTOCOL_UNSPEC = 0xf, /* No specific protocol */ }; enum no_undef_states { NU_NO = -1, NU_UNDEF = 0, }; enum initialized_states { INIT_FAILED, INIT_MISSING_UDEV, INIT_REQUESTED_UDEV, INIT_OK, }; enum prkey_sources { PRKEY_SOURCE_NONE, PRKEY_SOURCE_CONF, PRKEY_SOURCE_FILE, }; struct sg_id { int host_no; int channel; int scsi_id; int lun; short h_cmd_per_lun; short d_queue_depth; enum scsi_protocol proto_id; int transport_id; }; # ifndef HDIO_GETGEO # define HDIO_GETGEO 0x0301 /* get device geometry */ struct hd_geometry { unsigned char heads; unsigned char sectors; unsigned short cylinders; unsigned long start; }; #endif struct path { char dev[FILE_NAME_SIZE]; char dev_t[BLK_DEV_SIZE]; struct udev_device *udev; struct sg_id sg_id; struct hd_geometry geom; char wwid[WWID_SIZE]; char vendor_id[SCSI_VENDOR_SIZE]; char product_id[SCSI_PRODUCT_SIZE]; char rev[SCSI_REV_SIZE]; char serial[SERIAL_SIZE]; char tgt_node_name[NODE_NAME_SIZE]; unsigned long long size; unsigned int checkint; unsigned int tick; int bus; int offline; int state; int dmstate; int chkrstate; int failcount; int priority; int pgindex; int detect_prio; int detect_checker; int watch_checks; int wait_checks; int tpgs; char * uid_attribute; char * getuid; struct prio prio; char * prio_args; struct checker checker; struct multipath * mpp; int fd; int initialized; int retriggers; int wwid_changed; time_t io_err_dis_reinstate_time; int io_err_disable_reinstate; int io_err_pathfail_cnt; int io_err_pathfail_starttime; /* configlet pointers */ struct hwentry * hwe; }; typedef int (pgpolicyfn) (struct multipath *); struct multipath { char wwid[WWID_SIZE]; char alias_old[WWID_SIZE]; int pgpolicy; pgpolicyfn *pgpolicyfn; int nextpg; int bestpg; int queuedio; int action; int wait_for_udev; int uev_wait_tick; int pgfailback; int failback_tick; int rr_weight; int nr_active; /* current available(= not known as failed) paths */ int no_path_retry; /* number of retries after all paths are down */ int retry_tick; /* remaining times for retries */ int minio; int flush_on_last_del; int attribute_flags; int fast_io_fail; int retain_hwhandler; int deferred_remove; int delay_watch_checks; int delay_wait_checks; int marginal_path_err_sample_time; int marginal_path_err_rate_threshold; int marginal_path_err_recheck_gap_time; int marginal_path_double_failed_time; int skip_kpartx; int max_sectors_kb; int force_readonly; int force_udev_reload; unsigned int dev_loss; uid_t uid; gid_t gid; mode_t mode; unsigned long long size; vector paths; vector pg; struct dm_info * dmi; /* configlet pointers */ char * alias; char * alias_prefix; char * selector; char * features; char * hwhandler; struct mpentry * mpe; struct hwentry * hwe; /* threads */ pthread_t waiter; /* stats */ unsigned int stat_switchgroup; unsigned int stat_path_failures; unsigned int stat_map_loads; unsigned int stat_total_queueing_time; unsigned int stat_queueing_timeouts; unsigned int stat_map_failures; /* checkers shared data */ void * mpcontext; /* persistent management data*/ int prkey_source; struct be64 reservation_key; unsigned char prflag; }; struct pathgroup { long id; int status; int priority; int enabled_paths; vector paths; char * selector; }; struct adapter_group { char adapter_name[SLOT_NAME_SIZE]; struct pathgroup *pgp; int num_hosts; vector host_groups; int next_host_index; }; struct host_group { int host_no; int num_paths; vector paths; }; struct path * alloc_path (void); struct pathgroup * alloc_pathgroup (void); struct multipath * alloc_multipath (void); void free_path (struct path *); void free_pathvec (vector vec, enum free_path_mode free_paths); void free_pathgroup (struct pathgroup * pgp, enum free_path_mode free_paths); void free_pgvec (vector pgvec, enum free_path_mode free_paths); void free_multipath (struct multipath *, enum free_path_mode free_paths); void free_multipath_attributes (struct multipath *); void drop_multipath (vector mpvec, char * wwid, enum free_path_mode free_paths); void free_multipathvec (vector mpvec, enum free_path_mode free_paths); struct adapter_group * alloc_adaptergroup(void); struct host_group * alloc_hostgroup(void); void free_adaptergroup(vector adapters); void free_hostgroup(vector hostgroups); int store_adaptergroup(vector adapters, struct adapter_group *agp); int store_hostgroup(vector hostgroupvec, struct host_group *hgp); int store_path (vector pathvec, struct path * pp); int store_pathgroup (vector pgvec, struct pathgroup * pgp); struct multipath * find_mp_by_alias (vector mp, char * alias); struct multipath * find_mp_by_wwid (vector mp, char * wwid); struct multipath * find_mp_by_str (vector mp, char * wwid); struct multipath * find_mp_by_minor (vector mp, int minor); struct path * find_path_by_devt (vector pathvec, char * devt); struct path * find_path_by_dev (vector pathvec, char * dev); struct path * first_path (struct multipath * mpp); int pathcountgr (struct pathgroup *, int); int pathcount (struct multipath *, int); int pathcmp (struct pathgroup *, struct pathgroup *); void setup_feature(struct multipath *, char *); int add_feature (char **, const char *); int remove_feature (char **, const char *); extern char sysfs_path[PATH_SIZE]; #endif /* _STRUCTS_H */ multipath-tools-0.7.4/libmultipath/structs_vec.c000066400000000000000000000323371320314174000221000ustar00rootroot00000000000000#include #include #include #include "checkers.h" #include "vector.h" #include "defaults.h" #include "debug.h" #include "config.h" #include "structs.h" #include "structs_vec.h" #include "sysfs.h" #include "waiter.h" #include "devmapper.h" #include "dmparser.h" #include "propsel.h" #include "discovery.h" #include "prio.h" /* * creates or updates mpp->paths reading mpp->pg */ int update_mpp_paths(struct multipath *mpp, vector pathvec) { struct pathgroup * pgp; struct path * pp; int i,j; if (!mpp || !mpp->pg) return 0; if (!mpp->paths && !(mpp->paths = vector_alloc())) return 1; vector_foreach_slot (mpp->pg, pgp, i) { vector_foreach_slot (pgp->paths, pp, j) { if (!find_path_by_devt(mpp->paths, pp->dev_t) && (find_path_by_devt(pathvec, pp->dev_t)) && store_path(mpp->paths, pp)) return 1; } } return 0; } int adopt_paths(vector pathvec, struct multipath *mpp) { int i, ret; struct path * pp; struct config *conf; if (!mpp) return 0; if (update_mpp_paths(mpp, pathvec)) return 1; vector_foreach_slot (pathvec, pp, i) { if (!strncmp(mpp->wwid, pp->wwid, WWID_SIZE)) { condlog(3, "%s: ownership set to %s", pp->dev, mpp->alias); pp->mpp = mpp; if (!mpp->paths && !(mpp->paths = vector_alloc())) return 1; if (!find_path_by_dev(mpp->paths, pp->dev) && store_path(mpp->paths, pp)) return 1; conf = get_multipath_config(); ret = pathinfo(pp, conf, DI_PRIO | DI_CHECKER); put_multipath_config(conf); if (ret) return 1; } } return 0; } void orphan_path(struct path *pp, const char *reason) { condlog(3, "%s: orphan path, %s", pp->dev, reason); pp->mpp = NULL; pp->dmstate = PSTATE_UNDEF; pp->uid_attribute = NULL; pp->getuid = NULL; prio_put(&pp->prio); checker_put(&pp->checker); if (pp->fd >= 0) close(pp->fd); pp->fd = -1; } void orphan_paths(vector pathvec, struct multipath *mpp) { int i; struct path * pp; vector_foreach_slot (pathvec, pp, i) { if (pp->mpp == mpp) { orphan_path(pp, "map flushed"); } } } static void set_multipath_wwid (struct multipath * mpp) { if (strlen(mpp->wwid)) return; dm_get_uuid(mpp->alias, mpp->wwid); } #define KEEP_WAITER 0 #define STOP_WAITER 1 #define PURGE_VEC 1 static void _remove_map (struct multipath * mpp, struct vectors * vecs, int stop_waiter, int purge_vec) { int i; condlog(4, "%s: remove multipath map", mpp->alias); /* * stop the DM event waiter thread */ if (stop_waiter) stop_waiter_thread(mpp, vecs); /* * clear references to this map */ orphan_paths(vecs->pathvec, mpp); if (purge_vec && (i = find_slot(vecs->mpvec, (void *)mpp)) != -1) vector_del_slot(vecs->mpvec, i); /* * final free */ free_multipath(mpp, KEEP_PATHS); } void remove_map(struct multipath *mpp, struct vectors *vecs, int purge_vec) { _remove_map(mpp, vecs, KEEP_WAITER, purge_vec); } void remove_map_and_stop_waiter(struct multipath *mpp, struct vectors *vecs, int purge_vec) { _remove_map(mpp, vecs, STOP_WAITER, purge_vec); } static void _remove_maps (struct vectors * vecs, int stop_waiter) { int i; struct multipath * mpp; if (!vecs) return; vector_foreach_slot (vecs->mpvec, mpp, i) { _remove_map(mpp, vecs, stop_waiter, 1); i--; } vector_free(vecs->mpvec); vecs->mpvec = NULL; } void remove_maps(struct vectors *vecs) { _remove_maps(vecs, KEEP_WAITER); } void remove_maps_and_stop_waiters(struct vectors *vecs) { _remove_maps(vecs, STOP_WAITER); } static struct hwentry * extract_hwe_from_path(struct multipath * mpp) { struct path * pp = NULL; int pg_num = -1, p_num = -1, i; struct pathgroup * pgp = NULL; condlog(3, "%s: searching paths for valid hwe", mpp->alias); if (mpp && mpp->pg) { vector_foreach_slot(mpp->pg, pgp, i) { if (pgp->status == PGSTATE_ACTIVE || pgp->status == PGSTATE_ENABLED) { pg_num = i; break; } } if (pg_num >= 0) pgp = VECTOR_SLOT(mpp->pg, pg_num); } if (pgp && pgp->paths) { vector_foreach_slot(pgp->paths, pp, i) { if (pp->dmstate == PSTATE_FAILED) continue; if (strlen(pp->vendor_id) > 0 && strlen(pp->product_id) > 0 && strlen(pp->rev) > 0) { p_num = i; break; } } if (p_num >= 0) pp = VECTOR_SLOT(pgp->paths, i); } if (pp) { if (!strlen(pp->vendor_id) || !strlen(pp->product_id) || !strlen(pp->rev)) { condlog(3, "%s: no device details available", pp->dev); return NULL; } condlog(3, "%s: vendor = %s", pp->dev, pp->vendor_id); condlog(3, "%s: product = %s", pp->dev, pp->product_id); condlog(3, "%s: rev = %s", pp->dev, pp->rev); if (!pp->hwe) { struct config *conf = get_multipath_config(); condlog(3, "searching hwtable"); pp->hwe = find_hwe(conf->hwtable, pp->vendor_id, pp->product_id, pp->rev); put_multipath_config(conf); } } return pp?pp->hwe:NULL; } static int update_multipath_table (struct multipath *mpp, vector pathvec, int is_daemon) { char params[PARAMS_SIZE] = {0}; if (!mpp) return 1; if (dm_get_map(mpp->alias, &mpp->size, params)) { condlog(3, "%s: cannot get map", mpp->alias); return 1; } if (disassemble_map(pathvec, params, mpp, is_daemon)) { condlog(3, "%s: cannot disassemble map", mpp->alias); return 1; } return 0; } static int update_multipath_status (struct multipath *mpp) { char status[PARAMS_SIZE] = {0}; if (!mpp) return 1; if (dm_get_status(mpp->alias, status)) { condlog(3, "%s: cannot get status", mpp->alias); return 1; } if (disassemble_status(status, mpp)) { condlog(3, "%s: cannot disassemble status", mpp->alias); return 1; } return 0; } void sync_paths(struct multipath *mpp, vector pathvec) { struct path *pp; struct pathgroup *pgp; int found, i, j; vector_foreach_slot (mpp->paths, pp, i) { found = 0; vector_foreach_slot(mpp->pg, pgp, j) { if (find_slot(pgp->paths, (void *)pp) != -1) { found = 1; break; } } if (!found) { condlog(3, "%s dropped path %s", mpp->alias, pp->dev); vector_del_slot(mpp->paths, i--); orphan_path(pp, "path removed externally"); } } update_mpp_paths(mpp, pathvec); vector_foreach_slot (mpp->paths, pp, i) pp->mpp = mpp; } int update_multipath_strings(struct multipath *mpp, vector pathvec, int is_daemon) { if (!mpp) return 1; update_mpp_paths(mpp, pathvec); condlog(4, "%s: %s", mpp->alias, __FUNCTION__); free_multipath_attributes(mpp); free_pgvec(mpp->pg, KEEP_PATHS); mpp->pg = NULL; if (update_multipath_table(mpp, pathvec, is_daemon)) return 1; sync_paths(mpp, pathvec); if (update_multipath_status(mpp)) return 1; return 0; } void set_no_path_retry(struct config *conf, struct multipath *mpp) { mpp->retry_tick = 0; mpp->nr_active = pathcount(mpp, PATH_UP) + pathcount(mpp, PATH_GHOST); select_no_path_retry(conf, mpp); switch (mpp->no_path_retry) { case NO_PATH_RETRY_UNDEF: break; case NO_PATH_RETRY_FAIL: dm_queue_if_no_path(mpp->alias, 0); break; case NO_PATH_RETRY_QUEUE: dm_queue_if_no_path(mpp->alias, 1); break; default: dm_queue_if_no_path(mpp->alias, 1); if (mpp->nr_active == 0) { struct config *conf = get_multipath_config(); /* Enter retry mode */ mpp->retry_tick = mpp->no_path_retry * conf->checkint; condlog(1, "%s: Entering recovery mode: max_retries=%d", mpp->alias, mpp->no_path_retry); put_multipath_config(conf); } break; } } int __setup_multipath(struct vectors *vecs, struct multipath *mpp, int reset, int is_daemon) { struct config *conf; if (dm_get_info(mpp->alias, &mpp->dmi)) { /* Error accessing table */ condlog(3, "%s: cannot access table", mpp->alias); goto out; } if (!dm_map_present(mpp->alias)) { /* Table has been removed */ condlog(3, "%s: table does not exist", mpp->alias); goto out; } if (update_multipath_strings(mpp, vecs->pathvec, is_daemon)) { condlog(0, "%s: failed to setup multipath", mpp->alias); goto out; } set_multipath_wwid(mpp); conf = get_multipath_config(); mpp->mpe = find_mpe(conf->mptable, mpp->wwid); put_multipath_config(conf); condlog(3, "%s: discover", mpp->alias); if (!mpp->hwe) mpp->hwe = extract_hwe_from_path(mpp); if (!mpp->hwe) { condlog(3, "%s: no hardware entry found, using defaults", mpp->alias); } if (reset) { conf = get_multipath_config(); select_rr_weight(conf, mpp); select_pgfailback(conf, mpp); set_no_path_retry(conf, mpp); select_flush_on_last_del(conf, mpp); put_multipath_config(conf); if (VECTOR_SIZE(mpp->paths) != 0) dm_cancel_deferred_remove(mpp); } return 0; out: remove_map(mpp, vecs, PURGE_VEC); return 1; } struct multipath *add_map_without_path (struct vectors *vecs, char *alias) { struct multipath * mpp = alloc_multipath(); if (!mpp) return NULL; if (!alias) { FREE(mpp); return NULL; } mpp->alias = STRDUP(alias); if (setup_multipath(vecs, mpp)) return NULL; /* mpp freed in setup_multipath */ if (adopt_paths(vecs->pathvec, mpp)) goto out; if (!vector_alloc_slot(vecs->mpvec)) goto out; vector_set_slot(vecs->mpvec, mpp); if (start_waiter_thread(mpp, vecs)) goto out; return mpp; out: remove_map(mpp, vecs, PURGE_VEC); return NULL; } static void find_existing_alias (struct multipath * mpp, struct vectors *vecs) { struct multipath * mp; int i; vector_foreach_slot (vecs->mpvec, mp, i) if (strncmp(mp->wwid, mpp->wwid, WWID_SIZE - 1) == 0) { strncpy(mpp->alias_old, mp->alias, WWID_SIZE - 1); return; } } struct multipath *add_map_with_path(struct vectors *vecs, struct path *pp, int add_vec) { struct multipath * mpp; struct config *conf = NULL; if (!strlen(pp->wwid)) return NULL; if (!(mpp = alloc_multipath())) return NULL; conf = get_multipath_config(); mpp->mpe = find_mpe(conf->mptable, pp->wwid); mpp->hwe = pp->hwe; put_multipath_config(conf); strcpy(mpp->wwid, pp->wwid); find_existing_alias(mpp, vecs); if (select_alias(conf, mpp)) goto out; mpp->size = pp->size; if (adopt_paths(vecs->pathvec, mpp)) goto out; if (add_vec) { if (!vector_alloc_slot(vecs->mpvec)) goto out; vector_set_slot(vecs->mpvec, mpp); } return mpp; out: remove_map(mpp, vecs, PURGE_VEC); return NULL; } int verify_paths(struct multipath *mpp, struct vectors *vecs) { struct path * pp; int count = 0; int i, j; if (!mpp) return 0; vector_foreach_slot (mpp->paths, pp, i) { /* * see if path is in sysfs */ if (sysfs_attr_get_value(pp->udev, "dev", pp->dev_t, BLK_DEV_SIZE) < 0) { if (pp->state != PATH_DOWN) { condlog(1, "%s: removing valid path %s in state %d", mpp->alias, pp->dev, pp->state); } else { condlog(3, "%s: failed to access path %s", mpp->alias, pp->dev); } count++; vector_del_slot(mpp->paths, i); i--; if ((j = find_slot(vecs->pathvec, (void *)pp)) != -1) vector_del_slot(vecs->pathvec, j); free_path(pp); } else { condlog(4, "%s: verified path %s dev_t %s", mpp->alias, pp->dev, pp->dev_t); } } return count; } int update_multipath (struct vectors *vecs, char *mapname, int reset) { struct multipath *mpp; struct pathgroup *pgp; struct path *pp; int i, j; mpp = find_mp_by_alias(vecs->mpvec, mapname); if (!mpp) { condlog(3, "%s: multipath map not found", mapname); return 2; } if (__setup_multipath(vecs, mpp, reset, 1)) return 1; /* mpp freed in setup_multipath */ /* * compare checkers states with DM states */ vector_foreach_slot (mpp->pg, pgp, i) { vector_foreach_slot (pgp->paths, pp, j) { if (pp->dmstate != PSTATE_FAILED) continue; if (pp->state != PATH_DOWN) { struct config *conf = get_multipath_config(); int oldstate = pp->state; condlog(2, "%s: mark as failed", pp->dev); mpp->stat_path_failures++; pp->state = PATH_DOWN; if (oldstate == PATH_UP || oldstate == PATH_GHOST) update_queue_mode_del_path(mpp); /* * if opportune, * schedule the next check earlier */ if (pp->tick > conf->checkint) pp->tick = conf->checkint; put_multipath_config(conf); } } } return 0; } /* * mpp->no_path_retry: * -2 (QUEUE) : queue_if_no_path enabled, never turned off * -1 (FAIL) : fail_if_no_path * 0 (UNDEF) : nothing * >0 : queue_if_no_path enabled, turned off after polling n times */ void update_queue_mode_del_path(struct multipath *mpp) { if (--mpp->nr_active == 0) { if (mpp->no_path_retry > 0) { struct config *conf = get_multipath_config(); /* * Enter retry mode. * meaning of +1: retry_tick may be decremented in * checkerloop before starting retry. */ mpp->stat_queueing_timeouts++; mpp->retry_tick = mpp->no_path_retry * conf->checkint + 1; condlog(1, "%s: Entering recovery mode: max_retries=%d", mpp->alias, mpp->no_path_retry); put_multipath_config(conf); } else if (mpp->no_path_retry != NO_PATH_RETRY_QUEUE) mpp->stat_map_failures++; } condlog(2, "%s: remaining active paths: %d", mpp->alias, mpp->nr_active); } void update_queue_mode_add_path(struct multipath *mpp) { if (mpp->nr_active++ == 0 && mpp->no_path_retry > 0) { /* come back to normal mode from retry mode */ mpp->retry_tick = 0; dm_queue_if_no_path(mpp->alias, 1); condlog(2, "%s: queue_if_no_path enabled", mpp->alias); condlog(1, "%s: Recovered to normal mode", mpp->alias); } condlog(2, "%s: remaining active paths: %d", mpp->alias, mpp->nr_active); } multipath-tools-0.7.4/libmultipath/structs_vec.h000066400000000000000000000027551320314174000221060ustar00rootroot00000000000000#ifndef _STRUCTS_VEC_H #define _STRUCTS_VEC_H #include "vector.h" #include "config.h" #include "lock.h" struct vectors { struct mutex_lock lock; /* defined in lock.h */ vector pathvec; vector mpvec; }; void set_no_path_retry(struct config *conf, struct multipath *mpp); int adopt_paths (vector pathvec, struct multipath * mpp); void orphan_paths (vector pathvec, struct multipath * mpp); void orphan_path (struct path * pp, const char *reason); int verify_paths(struct multipath * mpp, struct vectors * vecs); int update_mpp_paths(struct multipath * mpp, vector pathvec); int __setup_multipath (struct vectors * vecs, struct multipath * mpp, int reset, int is_daemon); #define setup_multipath(vecs, mpp) __setup_multipath(vecs, mpp, 1, 1) int update_multipath_strings (struct multipath *mpp, vector pathvec, int is_daemon); void remove_map (struct multipath * mpp, struct vectors * vecs, int purge_vec); void remove_map_and_stop_waiter (struct multipath * mpp, struct vectors * vecs, int purge_vec); void remove_maps (struct vectors * vecs); void remove_maps_and_stop_waiters (struct vectors * vecs); struct multipath * add_map_without_path (struct vectors * vecs, char * alias); struct multipath * add_map_with_path (struct vectors * vecs, struct path * pp, int add_vec); int update_multipath (struct vectors *vecs, char *mapname, int reset); void update_queue_mode_del_path(struct multipath *mpp); void update_queue_mode_add_path(struct multipath *mpp); #endif /* _STRUCTS_VEC_H */ multipath-tools-0.7.4/libmultipath/switchgroup.c000066400000000000000000000023721320314174000221060ustar00rootroot00000000000000/* * Copyright (c) 2005 Christophe Varoqui * Copyright (c) 2005 Edward Goggin, EMC */ #include "checkers.h" #include "vector.h" #include "structs.h" #include "switchgroup.h" void path_group_prio_update(struct pathgroup *pgp) { int i; int priority = 0; struct path * pp; pgp->enabled_paths = 0; if (!pgp->paths) { pgp->priority = 0; return; } vector_foreach_slot (pgp->paths, pp, i) { if (pp->state == PATH_UP || pp->state == PATH_GHOST) { priority += pp->priority; pgp->enabled_paths++; } } if (pgp->enabled_paths) pgp->priority = priority / pgp->enabled_paths; else pgp->priority = 0; } int select_path_group(struct multipath *mpp) { int i; int max_priority = 0; int bestpg = 1; int max_enabled_paths = 1; struct pathgroup * pgp; if (!mpp->pg) return 1; vector_foreach_slot (mpp->pg, pgp, i) { if (!pgp->paths) continue; path_group_prio_update(pgp); if (pgp->enabled_paths) { if (pgp->priority > max_priority) { max_priority = pgp->priority; max_enabled_paths = pgp->enabled_paths; bestpg = i + 1; } else if (pgp->priority == max_priority) { if (pgp->enabled_paths > max_enabled_paths) { max_enabled_paths = pgp->enabled_paths; bestpg = i + 1; } } } } return bestpg; } multipath-tools-0.7.4/libmultipath/switchgroup.h000066400000000000000000000001461320314174000221100ustar00rootroot00000000000000void path_group_prio_update (struct pathgroup * pgp); int select_path_group (struct multipath * mpp); multipath-tools-0.7.4/libmultipath/sysfs.c000066400000000000000000000154371320314174000207050ustar00rootroot00000000000000/* * Copyright (C) 2005-2006 Kay Sievers * * 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 version 2 of the License. * * 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, see . * */ #include #include #include #include #include #include #include #include #include #include #include #include "checkers.h" #include "vector.h" #include "structs.h" #include "sysfs.h" #include "list.h" #include "util.h" #include "debug.h" #include "devmapper.h" /* * When we modify an attribute value we cannot rely on libudev for now, * as libudev lacks the capability to update an attribute value. * So for modified attributes we need to implement our own function. */ ssize_t sysfs_attr_get_value(struct udev_device *dev, const char *attr_name, char * value, size_t value_len) { char devpath[PATH_SIZE]; struct stat statbuf; int fd; ssize_t size = -1; if (!dev || !attr_name || !value) return 0; snprintf(devpath, PATH_SIZE, "%s/%s", udev_device_get_syspath(dev), attr_name); condlog(4, "open '%s'", devpath); /* read attribute value */ fd = open(devpath, O_RDONLY); if (fd < 0) { condlog(4, "attribute '%s' can not be opened: %s", devpath, strerror(errno)); return -errno; } if (fstat(fd, &statbuf) < 0) { condlog(4, "stat '%s' failed: %s", devpath, strerror(errno)); close(fd); return -ENXIO; } /* skip directories */ if (S_ISDIR(statbuf.st_mode)) { condlog(4, "%s is a directory", devpath); close(fd); return -EISDIR; } /* skip non-writeable files */ if ((statbuf.st_mode & S_IRUSR) == 0) { condlog(4, "%s is not readable", devpath); close(fd); return -EPERM; } size = read(fd, value, value_len); if (size < 0) { condlog(4, "read from %s failed: %s", devpath, strerror(errno)); size = -errno; value[0] = '\0'; } else if (size == value_len) { value[size - 1] = '\0'; condlog(4, "overflow while reading from %s", devpath); size = 0; } else { value[size] = '\0'; size = strchop(value); } close(fd); return size; } ssize_t sysfs_bin_attr_get_value(struct udev_device *dev, const char *attr_name, unsigned char * value, size_t value_len) { char devpath[PATH_SIZE]; struct stat statbuf; int fd; ssize_t size = -1; if (!dev || !attr_name || !value) return 0; snprintf(devpath, PATH_SIZE, "%s/%s", udev_device_get_syspath(dev), attr_name); condlog(4, "open '%s'", devpath); /* read attribute value */ fd = open(devpath, O_RDONLY); if (fd < 0) { condlog(4, "attribute '%s' can not be opened: %s", devpath, strerror(errno)); return -errno; } if (fstat(fd, &statbuf) != 0) { condlog(4, "stat '%s' failed: %s", devpath, strerror(errno)); close(fd); return -ENXIO; } /* skip directories */ if (S_ISDIR(statbuf.st_mode)) { condlog(4, "%s is a directory", devpath); close(fd); return -EISDIR; } /* skip non-writeable files */ if ((statbuf.st_mode & S_IRUSR) == 0) { condlog(4, "%s is not readable", devpath); close(fd); return -EPERM; } size = read(fd, value, value_len); if (size < 0) { condlog(4, "read from %s failed: %s", devpath, strerror(errno)); size = -errno; } else if (size == value_len) { condlog(4, "overflow while reading from %s", devpath); size = 0; } close(fd); return size; } ssize_t sysfs_attr_set_value(struct udev_device *dev, const char *attr_name, const char * value, size_t value_len) { char devpath[PATH_SIZE]; struct stat statbuf; int fd; ssize_t size = -1; if (!dev || !attr_name || !value || !value_len) return 0; snprintf(devpath, PATH_SIZE, "%s/%s", udev_device_get_syspath(dev), attr_name); condlog(4, "open '%s'", devpath); /* write attribute value */ fd = open(devpath, O_WRONLY); if (fd < 0) { condlog(4, "attribute '%s' can not be opened: %s", devpath, strerror(errno)); return -errno; } if (fstat(fd, &statbuf) != 0) { condlog(4, "stat '%s' failed: %s", devpath, strerror(errno)); close(fd); return -errno; } /* skip directories */ if (S_ISDIR(statbuf.st_mode)) { condlog(4, "%s is a directory", devpath); close(fd); return -EISDIR; } /* skip non-writeable files */ if ((statbuf.st_mode & S_IWUSR) == 0) { condlog(4, "%s is not writeable", devpath); close(fd); return -EPERM; } size = write(fd, value, value_len); if (size < 0) { condlog(4, "write to %s failed: %s", devpath, strerror(errno)); size = -errno; } else if (size < value_len) { condlog(4, "tried to write %ld to %s. Wrote %ld", (long)value_len, devpath, (long)size); size = 0; } close(fd); return size; } int sysfs_get_size (struct path *pp, unsigned long long * size) { char attr[255]; int r; if (!pp->udev || !size) return 1; attr[0] = '\0'; if (sysfs_attr_get_value(pp->udev, "size", attr, 255) <= 0) { condlog(3, "%s: No size attribute in sysfs", pp->dev); return 1; } r = sscanf(attr, "%llu\n", size); if (r != 1) { condlog(3, "%s: Cannot parse size attribute", pp->dev); *size = 0; return 1; } return 0; } int sysfs_check_holders(char * check_devt, char * new_devt) { unsigned int major, new_minor, table_minor; char path[PATH_SIZE], check_dev[PATH_SIZE]; char * table_name; DIR *dirfd; struct dirent *holder; if (sscanf(new_devt,"%d:%d", &major, &new_minor) != 2) { condlog(1, "invalid device number %s", new_devt); return 0; } if (devt2devname(check_dev, PATH_SIZE, check_devt)) { condlog(1, "can't get devname for %s", check_devt); return 0; } condlog(3, "%s: checking holder", check_dev); snprintf(path, PATH_SIZE, "/sys/block/%s/holders", check_dev); dirfd = opendir(path); if (dirfd == NULL) { condlog(3, "%s: failed to open directory %s (%d)", check_dev, path, errno); return 0; } while ((holder = readdir(dirfd)) != NULL) { if ((strcmp(holder->d_name,".") == 0) || (strcmp(holder->d_name,"..") == 0)) continue; if (sscanf(holder->d_name, "dm-%d", &table_minor) != 1) { condlog(3, "%s: %s is not a dm-device", check_dev, holder->d_name); continue; } if (table_minor == new_minor) { condlog(3, "%s: holder already correct", check_dev); continue; } table_name = dm_mapname(major, table_minor); condlog(0, "%s: reassign table %s old %s new %s", check_dev, table_name, check_devt, new_devt); dm_reassign_table(table_name, check_devt, new_devt); FREE(table_name); } closedir(dirfd); return 0; } multipath-tools-0.7.4/libmultipath/sysfs.h000066400000000000000000000011071320314174000206770ustar00rootroot00000000000000/* * sysfs.h */ #ifndef _LIBMULTIPATH_SYSFS_H #define _LIBMULTIPATH_SYSFS_H ssize_t sysfs_attr_set_value(struct udev_device *dev, const char *attr_name, const char * value, size_t value_len); ssize_t sysfs_attr_get_value(struct udev_device *dev, const char *attr_name, char * value, size_t value_len); ssize_t sysfs_bin_attr_get_value(struct udev_device *dev, const char *attr_name, unsigned char * value, size_t value_len); int sysfs_get_size (struct path *pp, unsigned long long * size); int sysfs_check_holders(char * check_devt, char * new_devt); #endif multipath-tools-0.7.4/libmultipath/time-util.c000066400000000000000000000020261320314174000214350ustar00rootroot00000000000000#include #include #include #include "time-util.h" /* Initialize @cond as a condition variable that uses the monotonic clock */ void pthread_cond_init_mono(pthread_cond_t *cond) { pthread_condattr_t attr; int res; res = pthread_condattr_init(&attr); assert(res == 0); res = pthread_condattr_setclock(&attr, CLOCK_MONOTONIC); assert(res == 0); res = pthread_cond_init(cond, &attr); assert(res == 0); res = pthread_condattr_destroy(&attr); assert(res == 0); } /* Ensure that 0 <= ts->tv_nsec && ts->tv_nsec < 1000 * 1000 * 1000. */ void normalize_timespec(struct timespec *ts) { while (ts->tv_nsec < 0) { ts->tv_nsec += 1000UL * 1000 * 1000; ts->tv_sec--; } while (ts->tv_nsec >= 1000UL * 1000 * 1000) { ts->tv_nsec -= 1000UL * 1000 * 1000; ts->tv_sec++; } } /* Compute *res = *a - *b */ void timespecsub(const struct timespec *a, const struct timespec *b, struct timespec *res) { res->tv_sec = a->tv_sec - b->tv_sec; res->tv_nsec = a->tv_nsec - b->tv_nsec; normalize_timespec(res); } multipath-tools-0.7.4/libmultipath/time-util.h000066400000000000000000000004611320314174000214430ustar00rootroot00000000000000#ifndef _TIME_UTIL_H_ #define _TIME_UTIL_H_ #include struct timespec; void pthread_cond_init_mono(pthread_cond_t *cond); void normalize_timespec(struct timespec *ts); void timespecsub(const struct timespec *a, const struct timespec *b, struct timespec *res); #endif /* _TIME_UTIL_H_ */ multipath-tools-0.7.4/libmultipath/uevent.c000066400000000000000000000533611320314174000210420ustar00rootroot00000000000000/* * uevent.c - trigger upon netlink uevents from the kernel * * Only kernels from version 2.6.10* on provide the uevent netlink socket. * Until the libc-kernel-headers are updated, you need to compile with: * * gcc -I /lib/modules/`uname -r`/build/include -o uevent_listen uevent_listen.c * * Copyright (C) 2004 Kay Sievers * * 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 version 2 of the License. * * 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, see . * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "memory.h" #include "debug.h" #include "list.h" #include "uevent.h" #include "vector.h" #include "structs.h" #include "util.h" #include "config.h" #include "blacklist.h" #define MAX_ACCUMULATION_COUNT 2048 #define MAX_ACCUMULATION_TIME 30*1000 #define MIN_BURST_SPEED 10 typedef int (uev_trigger)(struct uevent *, void * trigger_data); LIST_HEAD(uevq); pthread_mutex_t uevq_lock = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_t *uevq_lockp = &uevq_lock; pthread_cond_t uev_cond = PTHREAD_COND_INITIALIZER; pthread_cond_t *uev_condp = &uev_cond; uev_trigger *my_uev_trigger; void * my_trigger_data; int servicing_uev; int is_uevent_busy(void) { int empty; pthread_mutex_lock(uevq_lockp); empty = list_empty(&uevq); pthread_mutex_unlock(uevq_lockp); return (!empty || servicing_uev); } struct uevent * alloc_uevent (void) { struct uevent *uev = MALLOC(sizeof(struct uevent)); if (uev) { INIT_LIST_HEAD(&uev->node); INIT_LIST_HEAD(&uev->merge_node); } return uev; } void uevq_cleanup(struct list_head *tmpq) { struct uevent *uev, *tmp; list_for_each_entry_safe(uev, tmp, tmpq, node) { list_del_init(&uev->node); if (uev->udev) udev_device_unref(uev->udev); FREE(uev); } } void uevent_get_wwid(struct uevent *uev) { int i; char *uid_attribute; struct config * conf; conf = get_multipath_config(); uid_attribute = parse_uid_attribute_by_attrs(conf->uid_attrs, uev->kernel); put_multipath_config(conf); if (!uid_attribute) return; for (i = 0; uev->envp[i] != NULL; i++) { if (!strncmp(uev->envp[i], uid_attribute, strlen(uid_attribute)) && strlen(uev->envp[i]) > strlen(uid_attribute) && uev->envp[i][strlen(uid_attribute)] == '=') { uev->wwid = uev->envp[i] + strlen(uid_attribute) + 1; break; } } free(uid_attribute); } bool uevent_need_merge(void) { struct config * conf; bool need_merge = false; conf = get_multipath_config(); if (conf->uid_attrs) need_merge = true; put_multipath_config(conf); return need_merge; } bool uevent_can_discard(struct uevent *uev) { struct config * conf; /* * do not filter dm devices by devnode */ if (!strncmp(uev->kernel, "dm-", 3)) return false; /* * filter paths devices by devnode */ conf = get_multipath_config(); if (filter_devnode(conf->blist_devnode, conf->elist_devnode, uev->kernel) > 0) { put_multipath_config(conf); return true; } put_multipath_config(conf); return false; } bool uevent_can_filter(struct uevent *earlier, struct uevent *later) { /* * filter earlier uvents if path has removed later. Eg: * "add path1 |chang path1 |add path2 |remove path1" * can filter as: * "add path2 |remove path1" * uevents "add path1" and "chang path1" are filtered out */ if (!strcmp(earlier->kernel, later->kernel) && !strcmp(later->action, "remove") && strncmp(later->kernel, "dm-", 3)) { return true; } /* * filter change uvents if add uevents exist. Eg: * "change path1| add path1 |add path2" * can filter as: * "add path1 |add path2" * uevent "chang path1" is filtered out */ if (!strcmp(earlier->kernel, later->kernel) && !strcmp(earlier->action, "change") && !strcmp(later->action, "add") && strncmp(later->kernel, "dm-", 3)) { return true; } return false; } bool merge_need_stop(struct uevent *earlier, struct uevent *later) { /* * dm uevent do not try to merge with left uevents */ if (!strncmp(later->kernel, "dm-", 3)) return true; /* * we can not make a jugement without wwid, * so it is sensible to stop merging */ if (!earlier->wwid || !later->wwid) return true; /* * uevents merging stoped * when we meet an opposite action uevent from the same LUN to AVOID * "add path1 |remove path1 |add path2 |remove path2 |add path3" * to merge as "remove path1, path2" and "add path1, path2, path3" * OR * "remove path1 |add path1 |remove path2 |add path2 |remove path3" * to merge as "add path1, path2" and "remove path1, path2, path3" * SO * when we meet a non-change uevent from the same LUN * with the same wwid and different action * it would be better to stop merging. */ if (!strcmp(earlier->wwid, later->wwid) && strcmp(earlier->action, later->action) && strcmp(earlier->action, "change") && strcmp(later->action, "change")) return true; return false; } bool uevent_can_merge(struct uevent *earlier, struct uevent *later) { /* merge paths uevents * whose wwids exsit and are same * and actions are same, * and actions are addition or deletion */ if (earlier->wwid && later->wwid && !strcmp(earlier->wwid, later->wwid) && !strcmp(earlier->action, later->action) && strncmp(earlier->action, "change", 6) && strncmp(earlier->kernel, "dm-", 3)) { return true; } return false; } void uevent_prepare(struct list_head *tmpq) { struct uevent *uev, *tmp; list_for_each_entry_reverse_safe(uev, tmp, tmpq, node) { if (uevent_can_discard(uev)) { list_del_init(&uev->node); if (uev->udev) udev_device_unref(uev->udev); FREE(uev); continue; } if (strncmp(uev->kernel, "dm-", 3) && uevent_need_merge()) uevent_get_wwid(uev); } } void uevent_filter(struct uevent *later, struct list_head *tmpq) { struct uevent *earlier, *tmp; list_for_some_entry_reverse_safe(earlier, tmp, &later->node, tmpq, node) { /* * filter unnessary earlier uevents * by the later uevent */ if (uevent_can_filter(earlier, later)) { condlog(2, "uevent: %s-%s has filtered by uevent: %s-%s", earlier->kernel, earlier->action, later->kernel, later->action); list_del_init(&earlier->node); if (earlier->udev) udev_device_unref(earlier->udev); FREE(earlier); } } } void uevent_merge(struct uevent *later, struct list_head *tmpq) { struct uevent *earlier, *tmp; list_for_some_entry_reverse_safe(earlier, tmp, &later->node, tmpq, node) { if (merge_need_stop(earlier, later)) break; /* * merge earlier uevents to the later uevent */ if (uevent_can_merge(earlier, later)) { condlog(2, "merged uevent: %s-%s-%s with uevent: %s-%s-%s", earlier->action, earlier->kernel, earlier->wwid, later->action, later->kernel, later->wwid); list_move(&earlier->node, &later->merge_node); } } } void merge_uevq(struct list_head *tmpq) { struct uevent *later; uevent_prepare(tmpq); list_for_each_entry_reverse(later, tmpq, node) { uevent_filter(later, tmpq); if(uevent_need_merge()) uevent_merge(later, tmpq); } } void service_uevq(struct list_head *tmpq) { struct uevent *uev, *tmp; list_for_each_entry_safe(uev, tmp, tmpq, node) { list_del_init(&uev->node); if (my_uev_trigger && my_uev_trigger(uev, my_trigger_data)) condlog(0, "uevent trigger error"); uevq_cleanup(&uev->merge_node); if (uev->udev) udev_device_unref(uev->udev); FREE(uev); } } static void uevent_cleanup(void *arg) { struct udev *udev = arg; condlog(3, "Releasing uevent_listen() resources"); udev_unref(udev); } static void monitor_cleanup(void *arg) { struct udev_monitor *monitor = arg; condlog(3, "Releasing uevent_monitor() resources"); udev_monitor_unref(monitor); } /* * Service the uevent queue. */ int uevent_dispatch(int (*uev_trigger)(struct uevent *, void * trigger_data), void * trigger_data) { my_uev_trigger = uev_trigger; my_trigger_data = trigger_data; mlockall(MCL_CURRENT | MCL_FUTURE); while (1) { LIST_HEAD(uevq_tmp); pthread_mutex_lock(uevq_lockp); servicing_uev = 0; /* * Condition signals are unreliable, * so make sure we only wait if we have to. */ if (list_empty(&uevq)) { pthread_cond_wait(uev_condp, uevq_lockp); } servicing_uev = 1; list_splice_init(&uevq, &uevq_tmp); pthread_mutex_unlock(uevq_lockp); if (!my_uev_trigger) break; merge_uevq(&uevq_tmp); service_uevq(&uevq_tmp); } condlog(3, "Terminating uev service queue"); uevq_cleanup(&uevq); return 0; } struct uevent *uevent_from_buffer(char *buf, ssize_t buflen) { struct uevent *uev; char *buffer; size_t bufpos; int i; char *pos; uev = alloc_uevent(); if (!uev) { condlog(1, "lost uevent, oom"); return NULL; } if ((size_t)buflen > sizeof(buf)-1) buflen = sizeof(buf)-1; /* * Copy the shared receive buffer contents to buffer private * to this uevent so we can immediately reuse the shared buffer. */ memcpy(uev->buffer, buf, HOTPLUG_BUFFER_SIZE + OBJECT_SIZE); buffer = uev->buffer; buffer[buflen] = '\0'; /* save start of payload */ bufpos = strlen(buffer) + 1; /* action string */ uev->action = buffer; pos = strchr(buffer, '@'); if (!pos) { condlog(3, "bad action string '%s'", buffer); FREE(uev); return NULL; } pos[0] = '\0'; /* sysfs path */ uev->devpath = &pos[1]; /* hotplug events have the environment attached - reconstruct envp[] */ for (i = 0; (bufpos < (size_t)buflen) && (i < HOTPLUG_NUM_ENVP-1); i++) { int keylen; char *key; key = &buffer[bufpos]; keylen = strlen(key); uev->envp[i] = key; /* Filter out sequence number */ if (strncmp(key, "SEQNUM=", 7) == 0) { char *eptr; uev->seqnum = strtoul(key + 7, &eptr, 10); if (eptr == key + 7) uev->seqnum = -1; } bufpos += keylen + 1; } uev->envp[i] = NULL; condlog(3, "uevent %ld '%s' from '%s'", uev->seqnum, uev->action, uev->devpath); uev->kernel = strrchr(uev->devpath, '/'); if (uev->kernel) uev->kernel++; /* print payload environment */ for (i = 0; uev->envp[i] != NULL; i++) condlog(5, "%s", uev->envp[i]); return uev; } int failback_listen(void) { int sock; struct sockaddr_nl snl; struct sockaddr_un sun; socklen_t addrlen; int retval; int rcvbufsz = 128*1024; int rcvsz = 0; int rcvszsz = sizeof(rcvsz); unsigned int *prcvszsz = (unsigned int *)&rcvszsz; const int feature_on = 1; /* * First check whether we have a udev socket */ memset(&sun, 0x00, sizeof(struct sockaddr_un)); sun.sun_family = AF_LOCAL; strcpy(&sun.sun_path[1], "/org/kernel/dm/multipath_event"); addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(sun.sun_path+1) + 1; sock = socket(AF_LOCAL, SOCK_DGRAM, 0); if (sock >= 0) { condlog(3, "reading events from udev socket."); /* the bind takes care of ensuring only one copy running */ retval = bind(sock, (struct sockaddr *) &sun, addrlen); if (retval < 0) { condlog(0, "bind failed, exit"); goto exit; } /* enable receiving of the sender credentials */ retval = setsockopt(sock, SOL_SOCKET, SO_PASSCRED, &feature_on, sizeof(feature_on)); if (retval < 0) { condlog(0, "failed to enable credential passing, exit"); goto exit; } } else { /* Fallback to read kernel netlink events */ memset(&snl, 0x00, sizeof(struct sockaddr_nl)); snl.nl_family = AF_NETLINK; snl.nl_pid = getpid(); snl.nl_groups = 0x01; sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT); if (sock == -1) { condlog(0, "error getting socket, exit"); return 1; } condlog(3, "reading events from kernel."); /* * try to avoid dropping uevents, even so, this is not a guarantee, * but it does help to change the netlink uevent socket's * receive buffer threshold from the default value of 106,496 to * the maximum value of 262,142. */ retval = setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &rcvbufsz, sizeof(rcvbufsz)); if (retval < 0) { condlog(0, "error setting receive buffer size for socket, exit"); exit(1); } retval = getsockopt(sock, SOL_SOCKET, SO_RCVBUF, &rcvsz, prcvszsz); if (retval < 0) { condlog(0, "error setting receive buffer size for socket, exit"); exit(1); } condlog(3, "receive buffer size for socket is %u.", rcvsz); /* enable receiving of the sender credentials */ if (setsockopt(sock, SOL_SOCKET, SO_PASSCRED, &feature_on, sizeof(feature_on)) < 0) { condlog(0, "error on enabling credential passing for socket"); exit(1); } retval = bind(sock, (struct sockaddr *) &snl, sizeof(struct sockaddr_nl)); if (retval < 0) { condlog(0, "bind failed, exit"); goto exit; } } while (1) { size_t bufpos; ssize_t buflen; struct uevent *uev; struct msghdr smsg; struct iovec iov; char cred_msg[CMSG_SPACE(sizeof(struct ucred))]; struct cmsghdr *cmsg; struct ucred *cred; static char buf[HOTPLUG_BUFFER_SIZE + OBJECT_SIZE]; memset(buf, 0x00, sizeof(buf)); iov.iov_base = &buf; iov.iov_len = sizeof(buf); memset (&smsg, 0x00, sizeof(struct msghdr)); smsg.msg_iov = &iov; smsg.msg_iovlen = 1; smsg.msg_control = cred_msg; smsg.msg_controllen = sizeof(cred_msg); buflen = recvmsg(sock, &smsg, 0); if (buflen < 0) { if (errno != EINTR) condlog(0, "error receiving message, errno %d", errno); continue; } cmsg = CMSG_FIRSTHDR(&smsg); if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) { condlog(3, "no sender credentials received, message ignored"); continue; } cred = (struct ucred *)CMSG_DATA(cmsg); if (cred->uid != 0) { condlog(3, "sender uid=%d, message ignored", cred->uid); continue; } /* skip header */ bufpos = strlen(buf) + 1; if (bufpos < sizeof("a@/d") || bufpos >= sizeof(buf)) { condlog(3, "invalid message length"); continue; } /* check message header */ if (strstr(buf, "@/") == NULL) { condlog(3, "unrecognized message header"); continue; } if ((size_t)buflen > sizeof(buf)-1) { condlog(2, "buffer overflow for received uevent"); buflen = sizeof(buf)-1; } uev = uevent_from_buffer(buf, buflen); if (!uev) continue; /* * Queue uevent and poke service pthread. */ pthread_mutex_lock(uevq_lockp); list_add_tail(&uev->node, &uevq); pthread_cond_signal(uev_condp); pthread_mutex_unlock(uevq_lockp); } exit: close(sock); return 1; } struct uevent *uevent_from_udev_device(struct udev_device *dev) { struct uevent *uev; int i = 0; char *pos, *end; struct udev_list_entry *list_entry; uev = alloc_uevent(); if (!uev) { udev_device_unref(dev); condlog(1, "lost uevent, oom"); return NULL; } pos = uev->buffer; end = pos + HOTPLUG_BUFFER_SIZE + OBJECT_SIZE - 1; udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(dev)) { const char *name, *value; int bytes; name = udev_list_entry_get_name(list_entry); if (!name) name = "(null)"; value = udev_list_entry_get_value(list_entry); if (!value) value = "(null)"; bytes = snprintf(pos, end - pos, "%s=%s", name, value); if (pos + bytes >= end) { condlog(2, "buffer overflow for uevent"); break; } uev->envp[i] = pos; pos += bytes; *pos = '\0'; pos++; if (strcmp(name, "DEVPATH") == 0) uev->devpath = uev->envp[i] + 8; if (strcmp(name, "ACTION") == 0) uev->action = uev->envp[i] + 7; i++; if (i == HOTPLUG_NUM_ENVP - 1) break; } uev->udev = dev; uev->envp[i] = NULL; condlog(3, "uevent '%s' from '%s'", uev->action, uev->devpath); uev->kernel = strrchr(uev->devpath, '/'); if (uev->kernel) uev->kernel++; /* print payload environment */ for (i = 0; uev->envp[i] != NULL; i++) condlog(5, "%s", uev->envp[i]); return uev; } bool uevent_burst(struct timeval *start_time, int events) { struct timeval diff_time, end_time; unsigned long speed; unsigned long eclipse_ms; if(events > MAX_ACCUMULATION_COUNT) { condlog(2, "burst got %u uevents, too much uevents, stopped", events); return false; } gettimeofday(&end_time, NULL); timersub(&end_time, start_time, &diff_time); eclipse_ms = diff_time.tv_sec * 1000 + diff_time.tv_usec / 1000; if (eclipse_ms == 0) return true; if (eclipse_ms > MAX_ACCUMULATION_TIME) { condlog(2, "burst continued %lu ms, too long time, stopped", eclipse_ms); return false; } speed = (events * 1000) / eclipse_ms; if (speed > MIN_BURST_SPEED) return true; return false; } int uevent_listen(struct udev *udev) { int err = 2; struct udev_monitor *monitor = NULL; int fd, socket_flags, events; struct timeval start_time; int need_failback = 1; int timeout = 30; LIST_HEAD(uevlisten_tmp); /* * Queue uevents for service by dedicated thread so that the uevent * listening thread does not block on multipathd locks (vecs->lock) * thereby not getting to empty the socket's receive buffer queue * often enough. */ if (!udev) { condlog(1, "no udev context"); return 1; } udev_ref(udev); pthread_cleanup_push(uevent_cleanup, udev); monitor = udev_monitor_new_from_netlink(udev, "udev"); if (!monitor) { condlog(2, "failed to create udev monitor"); goto out; } pthread_cleanup_push(monitor_cleanup, monitor); #ifdef LIBUDEV_API_RECVBUF if (udev_monitor_set_receive_buffer_size(monitor, 128 * 1024 * 1024)) condlog(2, "failed to increase buffer size"); #endif fd = udev_monitor_get_fd(monitor); if (fd < 0) { condlog(2, "failed to get monitor fd"); goto out; } socket_flags = fcntl(fd, F_GETFL); if (socket_flags < 0) { condlog(2, "failed to get monitor socket flags : %s", strerror(errno)); goto out; } if (fcntl(fd, F_SETFL, socket_flags & ~O_NONBLOCK) < 0) { condlog(2, "failed to set monitor socket flags : %s", strerror(errno)); goto out; } err = udev_monitor_filter_add_match_subsystem_devtype(monitor, "block", "disk"); if (err) condlog(2, "failed to create filter : %s", strerror(-err)); err = udev_monitor_enable_receiving(monitor); if (err) { condlog(2, "failed to enable receiving : %s", strerror(-err)); goto out; } events = 0; gettimeofday(&start_time, NULL); while (1) { struct uevent *uev; struct udev_device *dev; struct pollfd ev_poll; int poll_timeout; int fdcount; memset(&ev_poll, 0, sizeof(struct pollfd)); ev_poll.fd = fd; ev_poll.events = POLLIN; poll_timeout = timeout * 1000; errno = 0; fdcount = poll(&ev_poll, 1, poll_timeout); if (fdcount && ev_poll.revents & POLLIN) { timeout = uevent_burst(&start_time, events + 1) ? 1 : 0; dev = udev_monitor_receive_device(monitor); if (!dev) { condlog(0, "failed getting udev device"); continue; } uev = uevent_from_udev_device(dev); if (!uev) continue; list_add_tail(&uev->node, &uevlisten_tmp); events++; continue; } if (fdcount < 0) { if (errno == EINTR) continue; condlog(0, "error receiving " "uevent message: %m"); err = -errno; break; } if (!list_empty(&uevlisten_tmp)) { /* * Queue uevents and poke service pthread. */ condlog(3, "Forwarding %d uevents", events); pthread_mutex_lock(uevq_lockp); list_splice_tail_init(&uevlisten_tmp, &uevq); pthread_cond_signal(uev_condp); pthread_mutex_unlock(uevq_lockp); events = 0; } gettimeofday(&start_time, NULL); timeout = 30; } need_failback = 0; out: if (monitor) pthread_cleanup_pop(1); if (need_failback) err = failback_listen(); pthread_cleanup_pop(1); return err; } int uevent_get_major(struct uevent *uev) { char *p, *q; int i, major = -1; for (i = 0; uev->envp[i] != NULL; i++) { if (!strncmp(uev->envp[i], "MAJOR", 5) && strlen(uev->envp[i]) > 6) { p = uev->envp[i] + 6; major = strtoul(p, &q, 10); if (p == q) { condlog(2, "invalid major '%s'", p); major = -1; } break; } } return major; } int uevent_get_minor(struct uevent *uev) { char *p, *q; int i, minor = -1; for (i = 0; uev->envp[i] != NULL; i++) { if (!strncmp(uev->envp[i], "MINOR", 5) && strlen(uev->envp[i]) > 6) { p = uev->envp[i] + 6; minor = strtoul(p, &q, 10); if (p == q) { condlog(2, "invalid minor '%s'", p); minor = -1; } break; } } return minor; } int uevent_get_disk_ro(struct uevent *uev) { char *p, *q; int i, ro = -1; for (i = 0; uev->envp[i] != NULL; i++) { if (!strncmp(uev->envp[i], "DISK_RO", 7) && strlen(uev->envp[i]) > 8) { p = uev->envp[i] + 8; ro = strtoul(p, &q, 10); if (p == q) { condlog(2, "invalid read_only setting '%s'", p); ro = -1; } break; } } return ro; } char *uevent_get_dm_name(struct uevent *uev) { char *p = NULL; int i; for (i = 0; uev->envp[i] != NULL; i++) { if (!strncmp(uev->envp[i], "DM_NAME", 7) && strlen(uev->envp[i]) > 8) { p = MALLOC(strlen(uev->envp[i] + 8) + 1); strcpy(p, uev->envp[i] + 8); break; } } return p; } char *uevent_get_dm_path(struct uevent *uev) { char *p = NULL; int i; for (i = 0; uev->envp[i] != NULL; i++) { if (!strncmp(uev->envp[i], "DM_PATH", 7) && strlen(uev->envp[i]) > 8) { p = MALLOC(strlen(uev->envp[i] + 8) + 1); strcpy(p, uev->envp[i] + 8); break; } } return p; } char *uevent_get_dm_action(struct uevent *uev) { char *p = NULL; int i; for (i = 0; uev->envp[i] != NULL; i++) { if (!strncmp(uev->envp[i], "DM_ACTION", 9) && strlen(uev->envp[i]) > 10) { p = MALLOC(strlen(uev->envp[i] + 10) + 1); strcpy(p, uev->envp[i] + 10); break; } } return p; } multipath-tools-0.7.4/libmultipath/uevent.h000066400000000000000000000020211320314174000210320ustar00rootroot00000000000000#ifndef _UEVENT_H #define _UEVENT_H /* * buffer for environment variables, the kernel's size in * lib/kobject_uevent.c should fit in */ #define HOTPLUG_BUFFER_SIZE 2048 #define HOTPLUG_NUM_ENVP 32 #define OBJECT_SIZE 512 #ifndef NETLINK_KOBJECT_UEVENT #define NETLINK_KOBJECT_UEVENT 15 #endif struct udev; struct uevent { struct list_head node; struct list_head merge_node; struct udev_device *udev; char buffer[HOTPLUG_BUFFER_SIZE + OBJECT_SIZE]; char *devpath; char *action; char *kernel; char *wwid; unsigned long seqnum; char *envp[HOTPLUG_NUM_ENVP]; }; int is_uevent_busy(void); int uevent_listen(struct udev *udev); int uevent_dispatch(int (*store_uev)(struct uevent *, void * trigger_data), void * trigger_data); int uevent_get_major(struct uevent *uev); int uevent_get_minor(struct uevent *uev); int uevent_get_disk_ro(struct uevent *uev); char *uevent_get_dm_name(struct uevent *uev); char *uevent_get_dm_path(struct uevent *uev); char *uevent_get_dm_action(struct uevent *uev); #endif /* _UEVENT_H */ multipath-tools-0.7.4/libmultipath/util.c000066400000000000000000000212071320314174000205030ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #include #include #include #include "util.h" #include "debug.h" #include "memory.h" #include "checkers.h" #include "vector.h" #include "structs.h" #include "log.h" size_t strchop(char *str) { int i; for (i=strlen(str)-1; i >=0 && isspace(str[i]); --i) ; str[++i] = '\0'; return strlen(str); } int basenamecpy (const char * str1, char * str2, int str2len) { char *p; if (!str1 || !strlen(str1)) return 0; if (strlen(str1) >= str2len) return 0; if (!str2) return 0; p = (char *)str1 + (strlen(str1) - 1); while (*--p != '/' && p != str1) continue; if (p != str1) p++; strncpy(str2, p, str2len); str2[str2len - 1] = '\0'; return strchop(str2); } int filepresent (char * run) { struct stat buf; if(!stat(run, &buf)) return 1; return 0; } char *get_next_string(char **temp, char *split_char) { char *token = NULL; token = strsep(temp, split_char); while (token != NULL && !strcmp(token, "")) token = strsep(temp, split_char); return token; } int get_word (char * sentence, char ** word) { char * p; int len; int skip = 0; if (word) *word = NULL; while (*sentence == ' ') { sentence++; skip++; } if (*sentence == '\0') return 0; p = sentence; while (*p != ' ' && *p != '\0') p++; len = (int) (p - sentence); if (!word) return skip + len; *word = MALLOC(len + 1); if (!*word) { condlog(0, "get_word : oom"); return 0; } strncpy(*word, sentence, len); strchop(*word); condlog(4, "*word = %s, len = %i", *word, len); if (*p == '\0') return 0; return skip + len; } size_t strlcpy(char *dst, const char *src, size_t size) { size_t bytes = 0; char *q = dst; const char *p = src; char ch; while ((ch = *p++)) { if (bytes+1 < size) *q++ = ch; bytes++; } /* If size == 0 there is no space for a final null... */ if (size) *q = '\0'; return bytes; } size_t strlcat(char *dst, const char *src, size_t size) { size_t bytes = 0; char *q = dst; const char *p = src; char ch; while (bytes < size && *q) { q++; bytes++; } if (bytes == size) return (bytes + strlen(src)); while ((ch = *p++)) { if (bytes+1 < size) *q++ = ch; bytes++; } *q = '\0'; return bytes; } int devt2devname(char *devname, int devname_len, char *devt) { FILE *fd; unsigned int tmpmaj, tmpmin, major, minor; char dev[FILE_NAME_SIZE]; char block_path[PATH_SIZE]; struct stat statbuf; memset(block_path, 0, sizeof(block_path)); memset(dev, 0, sizeof(dev)); if (sscanf(devt, "%u:%u", &major, &minor) != 2) { condlog(0, "Invalid device number %s", devt); return 1; } if (devname_len > FILE_NAME_SIZE) devname_len = FILE_NAME_SIZE; if (stat("/sys/dev/block", &statbuf) == 0) { /* Newer kernels have /sys/dev/block */ sprintf(block_path,"/sys/dev/block/%u:%u", major, minor); if (lstat(block_path, &statbuf) == 0) { if (S_ISLNK(statbuf.st_mode) && readlink(block_path, dev, FILE_NAME_SIZE-1) > 0) { char *p = strrchr(dev, '/'); if (!p) { condlog(0, "No sysfs entry for %s", block_path); return 1; } p++; strncpy(devname, p, devname_len); return 0; } } goto skip_proc; } memset(block_path, 0, sizeof(block_path)); if (!(fd = fopen("/proc/partitions", "r"))) { condlog(0, "Cannot open /proc/partitions"); return 1; } while (!feof(fd)) { int r = fscanf(fd,"%u %u %*d %s",&tmpmaj, &tmpmin, dev); if (!r) { r = fscanf(fd,"%*s\n"); continue; } if (r != 3) continue; if ((major == tmpmaj) && (minor == tmpmin)) { if (snprintf(block_path, sizeof(block_path), "/sys/block/%s", dev) >= sizeof(block_path)) { condlog(0, "device name %s is too long", dev); fclose(fd); return 1; } break; } } fclose(fd); skip_proc: if (strncmp(block_path,"/sys/block", 10)) { condlog(3, "No device found for %u:%u", major, minor); return 1; } if (stat(block_path, &statbuf) < 0) { condlog(0, "No sysfs entry for %s", block_path); return 1; } if (S_ISDIR(statbuf.st_mode) == 0) { condlog(0, "sysfs entry %s is not a directory", block_path); return 1; } basenamecpy((const char *)block_path, devname, devname_len); return 0; } /* This function returns a pointer inside of the supplied pathname string. * If is_path_device is true, it may also modify the supplied string */ char *convert_dev(char *name, int is_path_device) { char *ptr; if (!name) return NULL; if (is_path_device) { ptr = strstr(name, "cciss/"); if (ptr) { ptr += 5; *ptr = '!'; } } if (!strncmp(name, "/dev/", 5) && strlen(name) > 5) ptr = name + 5; else ptr = name; return ptr; } dev_t parse_devt(const char *dev_t) { int maj, min; if (sscanf(dev_t,"%d:%d", &maj, &min) != 2) return 0; return makedev(maj, min); } char *parse_uid_attribute_by_attrs(char *uid_attrs, char *path_dev) { char *uid_attribute; char *uid_attr_record; char *dev; char *attr; char *tmp; int count; if(!uid_attrs || !path_dev) return NULL; count = get_word(uid_attrs, &uid_attr_record); while (uid_attr_record) { tmp = strrchr(uid_attr_record, ':'); if (!tmp) { free(uid_attr_record); if (!count) break; uid_attrs += count; count = get_word(uid_attrs, &uid_attr_record); continue; } dev = uid_attr_record; attr = tmp + 1; *tmp = '\0'; if(!strncmp(path_dev, dev, strlen(dev))) { uid_attribute = STRDUP(attr); free(uid_attr_record); return uid_attribute; } free(uid_attr_record); if (!count) break; uid_attrs += count; count = get_word(uid_attrs, &uid_attr_record); } return NULL; } void setup_thread_attr(pthread_attr_t *attr, size_t stacksize, int detached) { int ret; ret = pthread_attr_init(attr); assert(ret == 0); if (stacksize < PTHREAD_STACK_MIN) stacksize = PTHREAD_STACK_MIN; ret = pthread_attr_setstacksize(attr, stacksize); assert(ret == 0); if (detached) { ret = pthread_attr_setdetachstate(attr, PTHREAD_CREATE_DETACHED); assert(ret == 0); } } int systemd_service_enabled_in(const char *dev, const char *prefix) { char path[PATH_SIZE], file[PATH_SIZE], service[PATH_SIZE]; DIR *dirfd; struct dirent *d; int found = 0; snprintf(service, PATH_SIZE, "multipathd.service"); snprintf(path, PATH_SIZE, "%s/systemd/system", prefix); condlog(3, "%s: checking for %s in %s", dev, service, path); dirfd = opendir(path); if (dirfd == NULL) return 0; while ((d = readdir(dirfd)) != NULL) { char *p; struct stat stbuf; if ((strcmp(d->d_name,".") == 0) || (strcmp(d->d_name,"..") == 0)) continue; if (strlen(d->d_name) < 6) continue; p = d->d_name + strlen(d->d_name) - 6; if (strcmp(p, ".wants")) continue; snprintf(file, PATH_SIZE, "%s/%s/%s", path, d->d_name, service); if (stat(file, &stbuf) == 0) { condlog(3, "%s: found %s", dev, file); found++; break; } } closedir(dirfd); return found; } int systemd_service_enabled(const char *dev) { int found = 0; found = systemd_service_enabled_in(dev, "/etc"); if (!found) found = systemd_service_enabled_in(dev, "/usr/lib"); if (!found) found = systemd_service_enabled_in(dev, "/lib"); if (!found) found = systemd_service_enabled_in(dev, "/run"); return found; } static int _linux_version_code; static pthread_once_t _lvc_initialized = PTHREAD_ONCE_INIT; /* Returns current kernel version encoded as major*65536 + minor*256 + patch, * so, for example, to check if the kernel is greater than 2.2.11: * * if (get_linux_version_code() > KERNEL_VERSION(2,2,11)) { } * * Copyright (C) 1999-2004 by Erik Andersen * Code copied from busybox (GPLv2 or later) */ static void _set_linux_version_code(void) { struct utsname name; char *t; int i, r; uname(&name); /* never fails */ t = name.release; r = 0; for (i = 0; i < 3; i++) { t = strtok(t, "."); r = r * 256 + (t ? atoi(t) : 0); t = NULL; } _linux_version_code = r; } int get_linux_version_code(void) { pthread_once(&_lvc_initialized, _set_linux_version_code); return _linux_version_code; } int parse_prkey(char *ptr, uint64_t *prkey) { if (!ptr) return 1; if (*ptr == '0') ptr++; if (*ptr == 'x' || *ptr == 'X') ptr++; if (*ptr == '\0' || strlen(ptr) > 16) return 1; if (strlen(ptr) != strspn(ptr, "0123456789aAbBcCdDeEfF")) return 1; if (sscanf(ptr, "%" SCNx64 "", prkey) != 1) return 1; return 0; } int safe_write(int fd, const void *buf, size_t count) { while (count > 0) { ssize_t r = write(fd, buf, count); if (r < 0) { if (errno == EINTR) continue; return -errno; } count -= r; buf = (char *)buf + r; } return 0; } multipath-tools-0.7.4/libmultipath/util.h000066400000000000000000000022121320314174000205030ustar00rootroot00000000000000#ifndef _UTIL_H #define _UTIL_H #include #include size_t strchop(char *); int basenamecpy (const char * src, char * dst, int); int filepresent (char * run); char *get_next_string(char **temp, char *split_char); int get_word (char * sentence, char ** word); size_t strlcpy(char *dst, const char *src, size_t size); size_t strlcat(char *dst, const char *src, size_t size); int devt2devname (char *, int, char *); dev_t parse_devt(const char *dev_t); char *convert_dev(char *dev, int is_path_device); char *parse_uid_attribute_by_attrs(char *uid_attrs, char *path_dev); void setup_thread_attr(pthread_attr_t *attr, size_t stacksize, int detached); int systemd_service_enabled(const char *dev); int get_linux_version_code(void); int parse_prkey(char *ptr, uint64_t *prkey); int safe_write(int fd, const void *buf, size_t count); #define KERNEL_VERSION(maj, min, ptc) ((((maj) * 256) + (min)) * 256 + (ptc)) #define safe_sprintf(var, format, args...) \ snprintf(var, sizeof(var), format, ##args) >= sizeof(var) #define safe_snprintf(var, size, format, args...) \ snprintf(var, size, format, ##args) >= size #endif /* _UTIL_H */ multipath-tools-0.7.4/libmultipath/uxsock.c000066400000000000000000000054341320314174000210460ustar00rootroot00000000000000/* * Original author : tridge@samba.org, January 2002 * * Copyright (c) 2005 Christophe Varoqui * Copyright (c) 2005 Alasdair Kergon, Redhat */ #include #include #include #include #include #include #include #include #include #include #include #include #ifdef USE_SYSTEMD #include #endif #include "mpath_cmd.h" #include "memory.h" #include "uxsock.h" #include "debug.h" /* * Code is similar with mpath_recv_reply() with data size limitation * and debug-able malloc. * When limit == 0, it means no limit on data size, used for socket client * to receiving data from multipathd. */ static int _recv_packet(int fd, char **buf, unsigned int timeout, ssize_t limit); /* * create a unix domain socket and start listening on it * return a file descriptor open on the socket */ int ux_socket_listen(const char *name) { int fd, len; #ifdef USE_SYSTEMD int num; #endif struct sockaddr_un addr; #ifdef USE_SYSTEMD num = sd_listen_fds(0); if (num > 1) { condlog(3, "sd_listen_fds returned %d fds", num); return -1; } else if (num == 1) { fd = SD_LISTEN_FDS_START + 0; condlog(3, "using fd %d from sd_listen_fds", fd); return fd; } #endif fd = socket(AF_LOCAL, SOCK_STREAM, 0); if (fd == -1) { condlog(3, "Couldn't create ux_socket, error %d", errno); return -1; } memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_LOCAL; addr.sun_path[0] = '\0'; len = strlen(name) + 1 + sizeof(sa_family_t); strncpy(&addr.sun_path[1], name, len); if (bind(fd, (struct sockaddr *)&addr, len) == -1) { condlog(3, "Couldn't bind to ux_socket, error %d", errno); close(fd); return -1; } if (listen(fd, 10) == -1) { condlog(3, "Couldn't listen to ux_socket, error %d", errno); close(fd); return -1; } return fd; } /* * send a packet in length prefix format */ int send_packet(int fd, const char *buf) { if (mpath_send_cmd(fd, buf) < 0) return -errno; return 0; } static int _recv_packet(int fd, char **buf, unsigned int timeout, ssize_t limit) { int err = 0; ssize_t len = 0; *buf = NULL; len = mpath_recv_reply_len(fd, timeout); if (len == 0) return len; if (len < 0) return -errno; if ((limit > 0) && (len > limit)) return -EINVAL; (*buf) = MALLOC(len); if (!*buf) return -ENOMEM; err = mpath_recv_reply_data(fd, *buf, len, timeout); if (err != 0) { FREE(*buf); (*buf) = NULL; return -errno; } return err; } /* * receive a packet in length prefix format */ int recv_packet(int fd, char **buf, unsigned int timeout) { return _recv_packet(fd, buf, timeout, 0 /* no limit */); } int recv_packet_from_client(int fd, char **buf, unsigned int timeout) { return _recv_packet(fd, buf, timeout, _MAX_CMD_LEN); } multipath-tools-0.7.4/libmultipath/uxsock.h000066400000000000000000000007361320314174000210530ustar00rootroot00000000000000/* some prototypes */ int ux_socket_listen(const char *name); int send_packet(int fd, const char *buf); int recv_packet(int fd, char **buf, unsigned int timeout); #define _MAX_CMD_LEN 512 /* * Used for receiving socket command from untrusted socket client where data * size is restricted to 512(_MAX_CMD_LEN) at most. * Return -EINVAL if data length requested by client exceeded the _MAX_CMD_LEN. */ int recv_packet_from_client(int fd, char **buf, unsigned int timeout); multipath-tools-0.7.4/libmultipath/vector.c000066400000000000000000000065241320314174000210350ustar00rootroot00000000000000/* * Part: Vector structure manipulation. * * Version: $Id: vector.c,v 1.0.3 2003/05/11 02:28:03 acassen Exp $ * * Author: Alexandre Cassen, * * 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. * * 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. * * Copyright (c) 2002, 2003, 2004 Alexandre Cassen * Copyright (c) 2005 Christophe Varoqui */ #include "memory.h" #include #include "vector.h" /* * Initialize vector struct. * allocated 'size' slot elements then return vector. */ vector vector_alloc(void) { vector v = (vector) MALLOC(sizeof (struct _vector)); return v; } /* allocated one slot */ void * vector_alloc_slot(vector v) { void *new_slot = NULL; if (!v) return NULL; v->allocated += VECTOR_DEFAULT_SIZE; if (v->slot) new_slot = REALLOC(v->slot, sizeof (void *) * v->allocated); else new_slot = (void *) MALLOC(sizeof (void *) * v->allocated); if (!new_slot) v->allocated -= VECTOR_DEFAULT_SIZE; else v->slot = new_slot; return v->slot; } int vector_move_up(vector v, int src, int dest) { void *value; int i; if (dest == src) return 0; if (dest > src || src >= v->allocated) return -1; value = v->slot[src]; for (i = src - 1; i >= dest; i--) v->slot[i + 1] = v->slot[i]; v->slot[dest] = value; return 0; } void * vector_insert_slot(vector v, int slot, void *value) { int i; if (!vector_alloc_slot(v)) return NULL; for (i = VECTOR_SIZE(v) - 2; i >= slot; i--) v->slot[i + 1] = v->slot[i]; v->slot[slot] = value; return v->slot[slot]; } int find_slot(vector v, void * addr) { int i; if (!v) return -1; for (i = 0; i < VECTOR_SIZE(v); i++) if (v->slot[i] == addr) return i; return -1; } void vector_del_slot(vector v, int slot) { int i; if (!v || !v->allocated || slot < 0 || slot > VECTOR_SIZE(v)) return; for (i = slot + 1; i < VECTOR_SIZE(v); i++) v->slot[i-1] = v->slot[i]; v->allocated -= VECTOR_DEFAULT_SIZE; if (v->allocated <= 0) { FREE(v->slot); v->slot = NULL; v->allocated = 0; } else { void *new_slot; new_slot = REALLOC(v->slot, sizeof (void *) * v->allocated); if (!new_slot) v->allocated += VECTOR_DEFAULT_SIZE; else v->slot = new_slot; } } void vector_repack(vector v) { int i; if (!v || !v->allocated) return; for (i = 0; i < VECTOR_SIZE(v); i++) if (i > 0 && v->slot[i] == NULL) vector_del_slot(v, i--); } /* Free memory vector allocation */ void vector_free(vector v) { if (!v) return; if (v->slot) FREE(v->slot); v->allocated = 0; v->slot = NULL; FREE(v); } void free_strvec(vector strvec) { int i; char *str; if (!strvec) return; vector_foreach_slot (strvec, str, i) if (str) FREE(str); vector_free(strvec); } /* Set a vector slot value */ void vector_set_slot(vector v, void *value) { unsigned int i; if (!v) return; i = VECTOR_SIZE(v) - 1; v->slot[i] = value; } multipath-tools-0.7.4/libmultipath/vector.h000066400000000000000000000044021320314174000210330ustar00rootroot00000000000000/* * Soft: Keepalived is a failover program for the LVS project * . It monitor & manipulate * a loadbalanced server pool using multi-layer checks. * * Part: vector.c include file. * * Version: $Id: vector.h,v 1.0.3 2003/05/11 02:28:03 acassen Exp $ * * Author: Alexandre Cassen, * * 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. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */ #ifndef _VECTOR_H #define _VECTOR_H /* vector definition */ struct _vector { int allocated; void **slot; }; typedef struct _vector *vector; #define VECTOR_DEFAULT_SIZE 1 #define VECTOR_SIZE(V) ((V) ? ((V)->allocated) / VECTOR_DEFAULT_SIZE : 0) #define VECTOR_SLOT(V,E) (((V) && (E) < VECTOR_SIZE(V)) ? (V)->slot[(E)] : NULL) #define VECTOR_LAST_SLOT(V) (((V) && VECTOR_SIZE(V) > 0) ? (V)->slot[(VECTOR_SIZE(V) - 1)] : NULL) #define vector_foreach_slot(v,p,i) \ for (i = 0; (v) && i < VECTOR_SIZE(v) && ((p) = (v)->slot[i]); i++) #define vector_foreach_slot_after(v,p,i) \ for (; (v) && i < VECTOR_SIZE(v) && ((p) = (v)->slot[i]); i++) #define vector_foreach_slot_backwards(v,p,i) \ for (i = VECTOR_SIZE(v); i > 0 && ((p) = (v)->slot[i-1]); i--) /* Prototypes */ extern vector vector_alloc(void); extern void *vector_alloc_slot(vector v); extern void vector_free(vector v); extern void free_strvec(vector strvec); extern void vector_set_slot(vector v, void *value); extern void vector_del_slot(vector v, int slot); extern void *vector_insert_slot(vector v, int slot, void *value); int find_slot(vector v, void * addr); extern void vector_repack(vector v); extern void vector_dump(vector v); extern void dump_strvec(vector strvec); extern int vector_move_up(vector v, int src, int dest); #endif multipath-tools-0.7.4/libmultipath/version.h000066400000000000000000000023621320314174000212210ustar00rootroot00000000000000/* * Soft: multipath device mapper target autoconfig * * Version: $Id: main.h,v 0.0.1 2003/09/18 15:13:38 cvaroqui Exp $ * * Author: Christophe Varoqui * * 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. * * 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. * * Copyright (c) 2006 Christophe Varoqui */ #ifndef _VERSION_H #define _VERSION_H #define VERSION_CODE 0x000704 #define DATE_CODE 0x0b0f11 #define PROG "multipath-tools" #define MULTIPATH_VERSION(version) \ (version >> 16) & 0xFF, \ (version >> 8) & 0xFF, \ version & 0xFF #define VERSION_STRING PROG" v%d.%d.%d (%.2d/%.2d, 20%.2d)\n", \ MULTIPATH_VERSION(VERSION_CODE), \ MULTIPATH_VERSION(DATE_CODE) #endif /* _VERSION_H */ multipath-tools-0.7.4/libmultipath/waiter.c000066400000000000000000000107651320314174000210300ustar00rootroot00000000000000/* * Copyright (c) 2004, 2005 Christophe Varoqui * Copyright (c) 2005 Kiyoshi Ueda, NEC * Copyright (c) 2005 Benjamin Marzinski, Redhat * Copyright (c) 2005 Edward Goggin, EMC */ #include #include #include #include #include #include #include "vector.h" #include "memory.h" #include "checkers.h" #include "config.h" #include "structs.h" #include "structs_vec.h" #include "devmapper.h" #include "debug.h" #include "lock.h" #include "waiter.h" pthread_attr_t waiter_attr; static struct event_thread *alloc_waiter (void) { struct event_thread *wp; wp = (struct event_thread *)MALLOC(sizeof(struct event_thread)); memset(wp, 0, sizeof(struct event_thread)); return wp; } static void free_waiter (void *data) { struct event_thread *wp = (struct event_thread *)data; if (wp->dmt) dm_task_destroy(wp->dmt); rcu_unregister_thread(); FREE(wp); } void stop_waiter_thread (struct multipath *mpp, struct vectors *vecs) { pthread_t thread; if (mpp->waiter == (pthread_t)0) { condlog(3, "%s: event checker thread already stopped", mpp->alias); return; } condlog(2, "%s: stop event checker thread (%lu)", mpp->alias, mpp->waiter); thread = mpp->waiter; mpp->waiter = (pthread_t)0; pthread_cancel(thread); pthread_kill(thread, SIGUSR2); } /* * returns the reschedule delay * negative means *stop* */ static int waiteventloop (struct event_thread *waiter) { sigset_t set, oldset; int event_nr; int r; if (!waiter->event_nr) waiter->event_nr = dm_geteventnr(waiter->mapname); if (!(waiter->dmt = libmp_dm_task_create(DM_DEVICE_WAITEVENT))) { condlog(0, "%s: devmap event #%i dm_task_create error", waiter->mapname, waiter->event_nr); return 1; } if (!dm_task_set_name(waiter->dmt, waiter->mapname)) { condlog(0, "%s: devmap event #%i dm_task_set_name error", waiter->mapname, waiter->event_nr); dm_task_destroy(waiter->dmt); waiter->dmt = NULL; return 1; } if (waiter->event_nr && !dm_task_set_event_nr(waiter->dmt, waiter->event_nr)) { condlog(0, "%s: devmap event #%i dm_task_set_event_nr error", waiter->mapname, waiter->event_nr); dm_task_destroy(waiter->dmt); waiter->dmt = NULL; return 1; } dm_task_no_open_count(waiter->dmt); /* wait */ sigemptyset(&set); sigaddset(&set, SIGUSR2); pthread_sigmask(SIG_UNBLOCK, &set, &oldset); pthread_testcancel(); r = dm_task_run(waiter->dmt); pthread_testcancel(); pthread_sigmask(SIG_SETMASK, &oldset, NULL); dm_task_destroy(waiter->dmt); waiter->dmt = NULL; if (!r) /* wait interrupted by signal */ return -1; waiter->event_nr++; /* * upon event ... */ while (1) { condlog(3, "%s: devmap event #%i", waiter->mapname, waiter->event_nr); /* * event might be : * * 1) a table reload, which means our mpp structure is * obsolete : refresh it through update_multipath() * 2) a path failed by DM : mark as such through * update_multipath() * 3) map has gone away : stop the thread. * 4) a path reinstate : nothing to do * 5) a switch group : nothing to do */ pthread_cleanup_push(cleanup_lock, &waiter->vecs->lock); lock(&waiter->vecs->lock); pthread_testcancel(); r = update_multipath(waiter->vecs, waiter->mapname, 1); lock_cleanup_pop(waiter->vecs->lock); if (r) { condlog(2, "%s: event checker exit", waiter->mapname); return -1; /* stop the thread */ } event_nr = dm_geteventnr(waiter->mapname); if (waiter->event_nr == event_nr) return 1; /* upon problem reschedule 1s later */ waiter->event_nr = event_nr; } return -1; /* never reach there */ } static void *waitevent (void *et) { int r; struct event_thread *waiter; mlockall(MCL_CURRENT | MCL_FUTURE); waiter = (struct event_thread *)et; pthread_cleanup_push(free_waiter, et); rcu_register_thread(); while (1) { r = waiteventloop(waiter); if (r < 0) break; sleep(r); } pthread_cleanup_pop(1); return NULL; } int start_waiter_thread (struct multipath *mpp, struct vectors *vecs) { struct event_thread *wp; if (!mpp) return 0; wp = alloc_waiter(); if (!wp) goto out; strncpy(wp->mapname, mpp->alias, WWID_SIZE - 1); wp->vecs = vecs; if (pthread_create(&wp->thread, &waiter_attr, waitevent, wp)) { condlog(0, "%s: cannot create event checker", wp->mapname); goto out1; } mpp->waiter = wp->thread; condlog(2, "%s: event checker started", wp->mapname); return 0; out1: free_waiter(wp); mpp->waiter = (pthread_t)0; out: condlog(0, "failed to start waiter thread"); return 1; } multipath-tools-0.7.4/libmultipath/waiter.h000066400000000000000000000005621320314174000210270ustar00rootroot00000000000000#ifndef _WAITER_H #define _WAITER_H extern pthread_attr_t waiter_attr; struct event_thread { struct dm_task *dmt; pthread_t thread; int event_nr; char mapname[WWID_SIZE]; struct vectors *vecs; }; void stop_waiter_thread (struct multipath *mpp, struct vectors *vecs); int start_waiter_thread (struct multipath *mpp, struct vectors *vecs); #endif /* _WAITER_H */ multipath-tools-0.7.4/libmultipath/wwids.c000066400000000000000000000152331320314174000206650ustar00rootroot00000000000000#include #include #include #include #include #include #include #include "checkers.h" #include "vector.h" #include "structs.h" #include "debug.h" #include "uxsock.h" #include "file.h" #include "wwids.h" #include "defaults.h" #include "config.h" /* * Copyright (c) 2010 Benjamin Marzinski, Redhat */ static int lookup_wwid(FILE *f, char *wwid) { int c; char buf[LINE_MAX]; int count; while ((c = fgetc(f)) != EOF){ if (c != '/') { if (fgets(buf, LINE_MAX, f) == NULL) return 0; else continue; } count = 0; while ((c = fgetc(f)) != '/') { if (c == EOF) return 0; if (count >= WWID_SIZE - 1) goto next; if (wwid[count] == '\0') goto next; if (c != wwid[count++]) goto next; } if (wwid[count] == '\0') return 1; next: if (fgets(buf, LINE_MAX, f) == NULL) return 0; } return 0; } static int write_out_wwid(int fd, char *wwid) { int ret; off_t offset; char buf[WWID_SIZE + 3]; ret = snprintf(buf, WWID_SIZE + 3, "/%s/\n", wwid); if (ret >= (WWID_SIZE + 3) || ret < 0){ condlog(0, "can't format wwid for writing (%d) : %s", ret, strerror(errno)); return -1; } offset = lseek(fd, 0, SEEK_END); if (offset < 0) { condlog(0, "can't seek to the end of wwids file : %s", strerror(errno)); return -1; } if (write(fd, buf, strlen(buf)) != strlen(buf)) { condlog(0, "cannot write wwid to wwids file : %s", strerror(errno)); if (ftruncate(fd, offset)) condlog(0, "cannot truncate failed wwid write : %s", strerror(errno)); return -1; } return 1; } int replace_wwids(vector mp) { int i, fd, can_write; struct multipath * mpp; size_t len; int ret = -1; struct config *conf; conf = get_multipath_config(); fd = open_file(conf->wwids_file, &can_write, WWIDS_FILE_HEADER); put_multipath_config(conf); if (fd < 0) goto out; if (!can_write) { condlog(0, "cannot replace wwids. wwids file is read-only"); goto out_file; } if (ftruncate(fd, 0) < 0) { condlog(0, "cannot truncate wwids file : %s", strerror(errno)); goto out_file; } if (lseek(fd, 0, SEEK_SET) < 0) { condlog(0, "cannot seek to the start of the file : %s", strerror(errno)); goto out_file; } len = strlen(WWIDS_FILE_HEADER); if (write(fd, WWIDS_FILE_HEADER, len) != len) { condlog(0, "Can't write wwid file header : %s", strerror(errno)); /* cleanup partially written header */ if (ftruncate(fd, 0) < 0) condlog(0, "Cannot truncate header : %s", strerror(errno)); goto out_file; } if (!mp || !mp->allocated) { ret = 0; goto out_file; } vector_foreach_slot(mp, mpp, i) { if (write_out_wwid(fd, mpp->wwid) < 0) goto out_file; } ret = 0; out_file: close(fd); out: return ret; } int do_remove_wwid(int fd, char *str) { char buf[4097]; char *ptr; off_t start = 0; int bytes; while (1) { if (lseek(fd, start, SEEK_SET) < 0) { condlog(0, "wwid file read lseek failed : %s", strerror(errno)); return -1; } bytes = read(fd, buf, 4096); if (bytes < 0) { if (errno == EINTR || errno == EAGAIN) continue; condlog(0, "failed to read from wwids file : %s", strerror(errno)); return -1; } if (!bytes) /* didn't find wwid to remove */ return 1; buf[bytes] = '\0'; ptr = strstr(buf, str); if (ptr != NULL) { condlog(3, "found '%s'", str); if (lseek(fd, start + (ptr - buf), SEEK_SET) < 0) { condlog(0, "write lseek failed : %s", strerror(errno)); return -1; } while (1) { if (write(fd, "#", 1) < 0) { if (errno == EINTR || errno == EAGAIN) continue; condlog(0, "failed to write to wwids file : %s", strerror(errno)); return -1; } return 0; } } ptr = strrchr(buf, '\n'); if (ptr == NULL) { /* shouldn't happen, assume it is EOF */ condlog(4, "couldn't find newline, assuming end of file"); return 1; } start = start + (ptr - buf) + 1; } } int remove_wwid(char *wwid) { int fd, len, can_write; char *str; int ret = -1; struct config *conf; len = strlen(wwid) + 4; /* two slashes the newline and a zero byte */ str = malloc(len); if (str == NULL) { condlog(0, "can't allocate memory to remove wwid : %s", strerror(errno)); return -1; } if (snprintf(str, len, "/%s/\n", wwid) >= len) { condlog(0, "string overflow trying to remove wwid"); goto out; } condlog(3, "removing line '%s' from wwids file", str); conf = get_multipath_config(); fd = open_file(conf->wwids_file, &can_write, WWIDS_FILE_HEADER); put_multipath_config(conf); if (fd < 0) goto out; if (!can_write) { condlog(0, "cannot remove wwid. wwids file is read-only"); goto out_file; } ret = do_remove_wwid(fd, str); out_file: close(fd); out: free(str); return ret; } int check_wwids_file(char *wwid, int write_wwid) { int fd, can_write, found, ret; FILE *f; struct config *conf; conf = get_multipath_config(); fd = open_file(conf->wwids_file, &can_write, WWIDS_FILE_HEADER); put_multipath_config(conf); if (fd < 0) return -1; f = fdopen(fd, "r"); if (!f) { condlog(0,"can't fdopen wwids file : %s", strerror(errno)); close(fd); return -1; } found = lookup_wwid(f, wwid); if (found) { ret = 0; goto out; } if (!write_wwid) { ret = -1; goto out; } if (!can_write) { condlog(0, "wwids file is read-only. Can't write wwid"); ret = -1; goto out; } if (fflush(f) != 0) { condlog(0, "cannot fflush wwids file stream : %s", strerror(errno)); ret = -1; goto out; } ret = write_out_wwid(fd, wwid); out: fclose(f); return ret; } int should_multipath(struct path *pp1, vector pathvec) { int i, ignore_new_devs; struct path *pp2; struct config *conf; conf = get_multipath_config(); ignore_new_devs = conf->ignore_new_devs; if (!conf->find_multipaths && !ignore_new_devs) { put_multipath_config(conf); return 1; } put_multipath_config(conf); condlog(4, "checking if %s should be multipathed", pp1->dev); if (!ignore_new_devs) { vector_foreach_slot(pathvec, pp2, i) { if (pp1->dev == pp2->dev) continue; if (strncmp(pp1->wwid, pp2->wwid, WWID_SIZE) == 0) { condlog(3, "found multiple paths with wwid %s, " "multipathing %s", pp1->wwid, pp1->dev); return 1; } } } if (check_wwids_file(pp1->wwid, 0) < 0) { condlog(3, "wwid %s not in wwids file, skipping %s", pp1->wwid, pp1->dev); return 0; } condlog(3, "found wwid %s in wwids file, multipathing %s", pp1->wwid, pp1->dev); return 1; } int remember_wwid(char *wwid) { int ret = check_wwids_file(wwid, 1); if (ret < 0){ condlog(3, "failed writing wwid %s to wwids file", wwid); return -1; } if (ret == 1) condlog(3, "wrote wwid %s to wwids file", wwid); else condlog(4, "wwid %s already in wwids file", wwid); return 0; } multipath-tools-0.7.4/libmultipath/wwids.h000066400000000000000000000010551320314174000206670ustar00rootroot00000000000000/* * Copyright (c) 2010 Benjamin Marzinski, Redhat */ #ifndef _WWIDS_H #define _WWIDS_H #define WWIDS_FILE_HEADER \ "# Multipath wwids, Version : 1.0\n" \ "# NOTE: This file is automatically maintained by multipath and multipathd.\n" \ "# You should not need to edit this file in normal circumstances.\n" \ "#\n" \ "# Valid WWIDs:\n" int should_multipath(struct path *pp, vector pathvec); int remember_wwid(char *wwid); int check_wwids_file(char *wwid, int write_wwid); int remove_wwid(char *wwid); int replace_wwids(vector mp); #endif /* _WWIDS_H */ multipath-tools-0.7.4/mpathpersist/000077500000000000000000000000001320314174000174055ustar00rootroot00000000000000multipath-tools-0.7.4/mpathpersist/Makefile000066400000000000000000000013571320314174000210530ustar00rootroot00000000000000include ../Makefile.inc CFLAGS += $(BIN_CFLAGS) -I$(multipathdir) -I$(mpathpersistdir) LDFLAGS += $(BIN_LDFLAGS) LIBDEPS += -L$(mpathpersistdir) -lmpathpersist -L$(multipathdir) -lmultipath \ -L$(mpathcmddir) -lmpathcmd -lpthread -ldevmapper -ludev EXEC = mpathpersist OBJS = main.o all: $(EXEC) $(EXEC): $(OBJS) $(CC) $(OBJS) -o $(EXEC) $(LDFLAGS) $(CFLAGS) $(LIBDEPS) $(GZIP) $(EXEC).8 > $(EXEC).8.gz install: $(INSTALL_PROGRAM) -d $(DESTDIR)$(bindir) $(INSTALL_PROGRAM) -m 755 $(EXEC) $(DESTDIR)$(bindir)/ $(INSTALL_PROGRAM) -d $(DESTDIR)$(man8dir) $(INSTALL_PROGRAM) -m 644 $(EXEC).8.gz $(DESTDIR)$(man8dir) clean: $(RM) core *.o $(EXEC) *.gz uninstall: $(RM) $(DESTDIR)$(bindir)/$(EXEC) $(RM) $(DESTDIR)$(man8dir)/$(EXEC).8.gz multipath-tools-0.7.4/mpathpersist/main.c000066400000000000000000000530221320314174000204770ustar00rootroot00000000000000#include #include #include #include #include #include "checkers.h" #include "vector.h" #include "config.h" #include "structs.h" #include #include #include "mpath_persist.h" #include "main.h" #include #include #include static const char * pr_type_strs[] = { "obsolete [0]", "Write Exclusive", "obsolete [2]", "Exclusive Access", "obsolete [4]", "Write Exclusive, registrants only", "Exclusive Access, registrants only", "Write Exclusive, all registrants", "Exclusive Access, all registrants", "obsolete [9]", "obsolete [0xa]", "obsolete [0xb]", "obsolete [0xc]", "obsolete [0xd]", "obsolete [0xe]", "obsolete [0xf]", }; int get_transportids_length(unsigned char * transportid_arr, int max_transportid, int num_transportids); void mpath_print_buf_readcap(struct prin_resp *pr_buff); void mpath_print_buf_readfullstat(struct prin_resp *pr_buff); void mpath_print_buf_readresv(struct prin_resp *pr_buff); void mpath_print_buf_readkeys(struct prin_resp *pr_buff); void dumpHex(const char* str, int len, int no_ascii); void * mpath_alloc_prin_response(int prin_sa); void mpath_print_transport_id(struct prin_fulldescr *fdesc); int construct_transportid(const char * inp, struct transportid transid[], int num_transportids); int logsink; struct config *multipath_conf; struct config *get_multipath_config(void) { return multipath_conf; } void put_multipath_config(struct config *conf) { /* Noop for now */ } void rcu_register_thread_memb(void) {} void rcu_unregister_thread_memb(void) {} struct udev *udev; int main (int argc, char * argv[]) { int fd, c, res; const char *device_name = NULL; int num_prin_sa = 0; int num_prout_sa = 0; int num_prout_param = 0; int prin_flag = 0; int prout_flag = 0; int ret = 0; int hex = 0; uint64_t param_sark = 0; unsigned int prout_type = 0; int param_alltgpt = 0; int param_aptpl = 0; uint64_t param_rk = 0; unsigned int param_rtp = 0; int num_transportids = 0; struct transportid transportids[MPATH_MX_TIDS]; int prout = 1; int prin = 1; int prin_sa = -1; int prout_sa = -1; int verbose = 0; int loglevel = 0; int noisy = 0; int num_transport =0; void *resp = NULL; struct transportid * tmp; struct config *conf; if (optind == argc) { fprintf (stderr, "No parameter used\n"); usage (); exit (1); } if (getuid () != 0) { fprintf (stderr, "need to be root\n"); exit (1); } udev = udev_new(); conf = mpath_lib_init(); if(!conf) { udev_unref(udev); exit(1); } memset(transportids, 0, MPATH_MX_TIDS * sizeof(struct transportid)); multipath_conf = conf; while (1) { int option_index = 0; c = getopt_long (argc, argv, "v:Cd:hHioZK:S:PAT:skrGILcRX:l:", long_options, &option_index); if (c == -1) break; switch (c) { case 'v': if (1 != sscanf (optarg, "%d", &loglevel)) { fprintf (stderr, "bad argument to '--verbose'\n"); return MPATH_PR_SYNTAX_ERROR; } break; case 'C': prout_sa = MPATH_PROUT_CLEAR_SA; ++num_prout_sa; break; case 'd': device_name = optarg; break; case 'h': usage (); return 0; case 'H': hex=1; break; case 'i': prin_flag = 1; break; case 'o': prout_flag = 1; break; case 'Z': param_aptpl = 1; ++num_prout_param; break; case 'K': if (1 != sscanf (optarg, "%" SCNx64 "", ¶m_rk)) { fprintf (stderr, "bad argument to '--param-rk'\n"); return MPATH_PR_SYNTAX_ERROR; } ++num_prout_param; break; case 'S': if (1 != sscanf (optarg, "%" SCNx64 "", ¶m_sark)) { fprintf (stderr, "bad argument to '--param-sark'\n"); return MPATH_PR_SYNTAX_ERROR; } ++num_prout_param; break; case 'P': prout_sa = MPATH_PROUT_PREE_SA; ++num_prout_sa; break; case 'A': prout_sa = MPATH_PROUT_PREE_AB_SA; ++num_prout_sa; break; case 'T': if (1 != sscanf (optarg, "%x", &prout_type)) { fprintf (stderr, "bad argument to '--prout-type'\n"); return MPATH_PR_SYNTAX_ERROR; } ++num_prout_param; break; case 's': prin_sa = MPATH_PRIN_RFSTAT_SA; ++num_prin_sa; break; case 'k': prin_sa = MPATH_PRIN_RKEY_SA; ++num_prin_sa; break; case 'r': prin_sa = MPATH_PRIN_RRES_SA; ++num_prin_sa; break; case 'G': prout_sa = MPATH_PROUT_REG_SA; ++num_prout_sa; break; case 'I': prout_sa = MPATH_PROUT_REG_IGN_SA; ++num_prout_sa; break; case 'L': prout_sa = MPATH_PROUT_REL_SA; ++num_prout_sa; break; case 'c': prin_sa = MPATH_PRIN_RCAP_SA; ++num_prin_sa; break; case 'R': prout_sa = MPATH_PROUT_RES_SA; ++num_prout_sa; break; case 'X': if (0 != construct_transportid(optarg, transportids, num_transport)) { fprintf(stderr, "bad argument to '--transport-id'\n"); return MPATH_PR_SYNTAX_ERROR; } ++num_transport; break; case 'l': if (1 != sscanf(optarg, "%u", &mpath_mx_alloc_len)) { fprintf(stderr, "bad argument to '--alloc-length'\n"); return MPATH_PR_SYNTAX_ERROR; } else if (MPATH_MAX_PARAM_LEN < mpath_mx_alloc_len) { fprintf(stderr, "'--alloc-length' argument exceeds maximum" " limit(%d)\n", MPATH_MAX_PARAM_LEN); return MPATH_PR_SYNTAX_ERROR; } break; default: fprintf(stderr, "unrecognised switch " "code 0x%x ??\n", c); usage (); ret = MPATH_PR_SYNTAX_ERROR; goto out; } } if (optind < argc) { if (NULL == device_name) { device_name = argv[optind]; ++optind; } if (optind < argc) { for (; optind < argc; ++optind) fprintf (stderr, "Unexpected extra argument: %s\n", argv[optind]); usage (); ret = MPATH_PR_SYNTAX_ERROR; goto out; } } /* set verbosity */ noisy = (loglevel >= 3) ? 1 : hex; verbose = (loglevel >= 3)? 3: loglevel; if ((prout_flag + prin_flag) == 0) { fprintf (stderr, "choose either '--in' or '--out' \n"); usage (); ret = MPATH_PR_SYNTAX_ERROR; goto out; } if ((prout_flag + prin_flag) > 1) { fprintf (stderr, "choose either '--in' or '--out' \n"); usage (); ret = MPATH_PR_SYNTAX_ERROR; goto out; } else if (prout_flag) { /* syntax check on PROUT arguments */ prin = 0; if ((1 != num_prout_sa) || (0 != num_prin_sa)) { fprintf (stderr, " For Persistent Reserve Out only one " "appropriate\n service action must be " "chosen \n"); ret = MPATH_PR_SYNTAX_ERROR; goto out; } } else if (prin_flag) { /* syntax check on PRIN arguments */ prout = 0; if (num_prout_sa > 0) { fprintf (stderr, " When a service action for Persistent " "Reserve Out is chosen the\n" " '--out' option must be given \n"); ret = MPATH_PR_SYNTAX_ERROR; goto out; } if (0 == num_prin_sa) { fprintf (stderr, " No service action given for Persistent Reserve IN\n"); usage(); ret = MPATH_PR_SYNTAX_ERROR; } else if (num_prin_sa > 1) { fprintf (stderr, " Too many service actions given; choose " "one only\n"); usage(); ret = MPATH_PR_SYNTAX_ERROR; } } else { usage (); ret = MPATH_PR_SYNTAX_ERROR; goto out; } if ((param_rtp) && (MPATH_PROUT_REG_MOV_SA != prout_sa)) { fprintf (stderr, " --relative-target-port" " only useful with --register-move\n"); usage (); ret = MPATH_PR_SYNTAX_ERROR; goto out; } if (((MPATH_PROUT_RES_SA == prout_sa) || (MPATH_PROUT_REL_SA == prout_sa) || (MPATH_PROUT_PREE_SA == prout_sa) || (MPATH_PROUT_PREE_AB_SA == prout_sa)) && (0 == prout_type)) { fprintf(stderr, "Warning: --prout-type probably needs to be " "given\n"); } if ((verbose > 2) && num_transportids) { fprintf (stderr, "number of tranport-ids decoded from " "command line : %d\n", num_transportids); } if (device_name == NULL) { fprintf (stderr, "No device name given \n"); usage (); ret = MPATH_PR_SYNTAX_ERROR; goto out; } /* open device */ if ((fd = open (device_name, O_WRONLY)) < 0) { fprintf (stderr, "%s: error opening file (rw) fd=%d\n", device_name, fd); ret = MPATH_PR_FILE_ERROR; goto out; } if (prin) { resp = mpath_alloc_prin_response(prin_sa); if (!resp) { fprintf (stderr, "failed to allocate PRIN response buffer\n"); ret = MPATH_PR_OTHER; goto out; } ret = mpath_persistent_reserve_in (fd, prin_sa, resp, noisy, verbose); if (ret != MPATH_PR_SUCCESS ) { fprintf (stderr, "Persistent Reserve IN command failed\n"); goto out; } switch(prin_sa) { case MPATH_PRIN_RKEY_SA: mpath_print_buf_readkeys(resp); break; case MPATH_PRIN_RRES_SA: mpath_print_buf_readresv(resp); break; case MPATH_PRIN_RCAP_SA: mpath_print_buf_readcap(resp); break; case MPATH_PRIN_RFSTAT_SA: mpath_print_buf_readfullstat(resp); break; } free(resp); } else if (prout) { int j; struct prout_param_descriptor *paramp; paramp= malloc(sizeof(struct prout_param_descriptor) + (sizeof(struct transportid *)*(MPATH_MX_TIDS ))); memset(paramp, 0, sizeof(struct prout_param_descriptor) + (sizeof(struct transportid *)*(MPATH_MX_TIDS))); for (j = 7; j >= 0; --j) { paramp->key[j] = (param_rk & 0xff); param_rk >>= 8; } for (j = 7; j >= 0; --j) { paramp->sa_key[j] = (param_sark & 0xff); param_sark >>= 8; } if (param_alltgpt) paramp->sa_flags |= 0x4; if (param_aptpl) paramp->sa_flags |= 0x1; if (num_transport) { paramp->sa_flags |= MPATH_F_SPEC_I_PT_MASK; paramp->num_transportid = num_transport; for (j = 0 ; j < num_transport; j++) { paramp->trnptid_list[j] = (struct transportid *)malloc(sizeof(struct transportid)); memcpy(paramp->trnptid_list[j], &transportids[j],sizeof(struct transportid)); } } /* PROUT commands other than 'register and move' */ ret = mpath_persistent_reserve_out (fd, prout_sa, 0, prout_type, paramp, noisy, verbose); for (j = 0 ; j < num_transport; j++) { tmp = paramp->trnptid_list[j]; free(tmp); } free(paramp); } if (ret != MPATH_PR_SUCCESS) { switch(ret) { case MPATH_PR_SENSE_UNIT_ATTENTION: printf("persistent reserve out: scsi status: Unit Attention\n"); break; case MPATH_PR_RESERV_CONFLICT: printf("persistent reserve out: scsi status: Reservation Conflict\n"); break; } printf("PR out: command failed\n"); } res = close (fd); if (res < 0) { mpath_lib_exit(conf); udev_unref(udev); return MPATH_PR_FILE_ERROR; } out : mpath_lib_exit(conf); udev_unref(udev); return (ret >= 0) ? ret : MPATH_PR_OTHER; } int get_transportids_length(unsigned char * transportid_arr, int max_transportid, int num_transportids) { int compact_len = 0; unsigned char * ucp = transportid_arr; int k, off, protocol_id, len; for (k = 0, off = 0; ((k < num_transportids) && (k < max_transportid)); ++k, off += MPATH_MX_TID_LEN) { protocol_id = ucp[off] & 0xf; if (5 == protocol_id) { len = (ucp[off + 2] << 8) + ucp[off + 3] + 4; if (len < 24) len = 24; if (off > compact_len) memmove(ucp + compact_len, ucp + off, len); compact_len += len; } else { if (off > compact_len) memmove(ucp + compact_len, ucp + off, 24); compact_len += 24; } } return compact_len; } void mpath_print_buf_readkeys( struct prin_resp *pr_buff) { int i,j,k, num; unsigned char *keyp; uint64_t prkey; printf(" PR generation=0x%x, ", pr_buff->prin_descriptor.prin_readkeys.prgeneration); num = pr_buff->prin_descriptor.prin_readkeys.additional_length / 8; if (0 == num) { printf(" 0 registered reservation key.\n"); return; } else if (1 == num) printf(" 1 registered reservation key follows:\n"); else printf(" %d registered reservation keys follow:\n", num); keyp = (unsigned char *)&pr_buff->prin_descriptor.prin_readkeys.key_list[0]; for (i = 0; i < num ; i++) { prkey = 0; for (j = 0; j < 8; ++j) { if (j > 0) prkey <<= 8; prkey |= keyp[j]; } printf(" 0x%" PRIx64 "\n", prkey); k=8*i+j; keyp = (unsigned char *)&pr_buff->prin_descriptor.prin_readkeys.key_list[k]; } } void mpath_print_buf_readresv( struct prin_resp *pr_buff) { int j, num, scope=0, type=0; unsigned char *keyp; uint64_t prkey; num = pr_buff->prin_descriptor.prin_readresv.additional_length / 8; if (0 == num) { printf(" PR generation=0x%x, there is NO reservation held \n", pr_buff->prin_descriptor.prin_readresv.prgeneration); return ; } else printf(" PR generation=0x%x, Reservation follows:\n", pr_buff->prin_descriptor.prin_readresv.prgeneration); keyp = (unsigned char *)&pr_buff->prin_descriptor.prin_readkeys.key_list[0]; prkey = 0; for (j = 0; j < 8; ++j) { if (j > 0) prkey <<= 8; prkey |= keyp[j]; } printf(" Key = 0x%" PRIx64 "\n", prkey); scope = (pr_buff->prin_descriptor.prin_readresv.scope_type >> 4) & 0x0f; type = pr_buff->prin_descriptor.prin_readresv.scope_type & 0x0f; if (scope == 0) printf(" scope = LU_SCOPE, type = %s", pr_type_strs[type]); else printf(" scope = %d, type = %s", scope, pr_type_strs[type]); printf("\n"); } void mpath_print_buf_readcap( struct prin_resp *pr_buff) { if ( pr_buff->prin_descriptor.prin_readcap.length <= 2 ) { fprintf(stderr, "Unexpected response for PRIN Report " "Capabilities\n"); return; //MALFORMED; } printf("Report capabilities response:\n"); printf(" Compatible Reservation Handling(CRH): %d\n", !!(pr_buff->prin_descriptor.prin_readcap.flags[0] & 0x10)); printf(" Specify Initiator Ports Capable(SIP_C): %d\n",!!(pr_buff->prin_descriptor.prin_readcap.flags[0] & 0x8)); printf(" All Target Ports Capable(ATP_C): %d\n",!!(pr_buff->prin_descriptor.prin_readcap.flags[0] & 0x4 )); printf(" Persist Through Power Loss Capable(PTPL_C): %d\n",!!(pr_buff->prin_descriptor.prin_readcap.flags[0])); printf(" Type Mask Valid(TMV): %d\n", !!(pr_buff->prin_descriptor.prin_readcap.flags[1] & 0x80)); printf(" Allow Commands: %d\n", !!(( pr_buff->prin_descriptor.prin_readcap.flags[1] >> 4) & 0x7)); printf(" Persist Through Power Loss Active(PTPL_A): %d\n", !!(pr_buff->prin_descriptor.prin_readcap.flags[1] & 0x1)); if(pr_buff->prin_descriptor.prin_readcap.flags[1] & 0x80) { printf(" Support indicated in Type mask:\n"); printf(" %s: %d\n", pr_type_strs[7], pr_buff->prin_descriptor.prin_readcap.pr_type_mask & 0x80); printf(" %s: %d\n", pr_type_strs[6], pr_buff->prin_descriptor.prin_readcap.pr_type_mask & 0x40); printf(" %s: %d\n", pr_type_strs[5], pr_buff->prin_descriptor.prin_readcap.pr_type_mask & 0x20); printf(" %s: %d\n", pr_type_strs[3], pr_buff->prin_descriptor.prin_readcap.pr_type_mask & 0x8); printf(" %s: %d\n", pr_type_strs[1], pr_buff->prin_descriptor.prin_readcap.pr_type_mask & 0x2); printf(" %s: %d\n", pr_type_strs[8], pr_buff->prin_descriptor.prin_readcap.pr_type_mask & 0x100); } } void mpath_print_buf_readfullstat( struct prin_resp *pr_buff) { int i,j, num; uint64_t prkey; uint16_t rel_pt_addr; unsigned char * keyp; num = pr_buff->prin_descriptor.prin_readfd.number_of_descriptor; if (0 == num) { printf(" PR generation=0x%x \n", pr_buff->prin_descriptor.prin_readfd.prgeneration); return ; } else printf(" PR generation=0x%x \n", pr_buff->prin_descriptor.prin_readfd.prgeneration); for (i = 0 ; i < num; i++) { keyp = (unsigned char *)&pr_buff->prin_descriptor.prin_readfd.descriptors[i]->key; prkey = 0; for (j = 0; j < 8; ++j) { if (j > 0) prkey <<= 8; prkey |= *keyp; ++keyp; } printf(" Key = 0x%" PRIx64 "\n", prkey); if (pr_buff->prin_descriptor.prin_readfd.descriptors[i]->flag & 0x02) printf(" All target ports bit set\n"); else { printf(" All target ports bit clear\n"); rel_pt_addr = pr_buff->prin_descriptor.prin_readfd.descriptors[i]->rtpi; printf(" Relative port address: 0x%x\n", rel_pt_addr); } if (pr_buff->prin_descriptor.prin_readfd.descriptors[i]->flag & 0x1) { printf(" << Reservation holder >>\n"); j = ((pr_buff->prin_descriptor.prin_readfd.descriptors[i]->scope_type >> 4) & 0xf); if (0 == j) printf(" scope: LU_SCOPE, "); else printf(" scope: %d ", j); j = (pr_buff->prin_descriptor.prin_readfd.descriptors[i]->scope_type & 0xf); printf(" type: %s\n", pr_type_strs[j]); } else printf(" not reservation holder\n"); mpath_print_transport_id(pr_buff->prin_descriptor.prin_readfd.descriptors[i]); } } static void usage(void) { fprintf(stderr, "Usage: mpathpersist [OPTIONS] [DEVICE]\n" " Options:\n" " --verbose|-v level verbosity level\n" " 0 Critical messages\n" " 1 Error messages\n" " 2 Warning messages\n" " 3 Informational messages\n" " 4 Informational messages with trace enabled\n" " --clear|-C PR Out: Clear\n" " --device=DEVICE|-d DEVICE query or change DEVICE\n" " --help|-h output this usage message\n" " --hex|-H output response in hex\n" " --in|-i request PR In command \n" " --out|-o request PR Out command\n" " --param-aptpl|-Z PR Out parameter 'APTPL'\n" " --read-keys|-k PR In: Read Keys\n" " --param-sark=SARK|-S SARK PR Out parameter service " "action\n" " reservation key (SARK is in " "hex)\n" " --preempt|-P PR Out: Preempt\n" " --preempt-abort|-A PR Out: Preempt and Abort\n" " --prout-type=TYPE|-T TYPE PR Out command type\n" " --read-full-status|-s PR In: Read Full Status\n" " --read-keys|-k PR In: Read Keys\n" " --read-reservation|-r PR In: Read Reservation\n" " --register|-G PR Out: Register\n" " --register-ignore|-I PR Out: Register and Ignore\n" " --release|-L PR Out: Release\n" " --report-capabilities|-c PR In: Report Capabilities\n" " --reserve|-R PR Out: Reserve\n" " --transport-id=TIDS|-X TIDS TransportIDs can be mentioned \n" " in several forms\n" " Examples:\n" " mpathpersist --out --register --param-sark=123abc --prout-type=5 /dev/mapper/mpath9\n" " mpathpersist -i -k /dev/mapper/mpath9\n" ); } void mpath_print_transport_id(struct prin_fulldescr *fdesc) { switch (fdesc->trnptid.protocol_id) { case MPATH_PROTOCOL_ID_FC: printf(" FCP-2 "); if (0 != fdesc->trnptid.format_code) printf(" [Unexpected format code: %d]\n", fdesc->trnptid.format_code); dumpHex((const char *)fdesc->trnptid.n_port_name, 8, 0); break; case MPATH_PROTOCOL_ID_ISCSI: printf(" iSCSI "); if (0 == fdesc->trnptid.format_code) { printf("name: %.*s\n", (int)sizeof(fdesc->trnptid.iscsi_name), fdesc->trnptid.iscsi_name); }else if (1 == fdesc->trnptid.format_code){ printf("world wide unique port id: %.*s\n", (int)sizeof(fdesc->trnptid.iscsi_name), fdesc->trnptid.iscsi_name); }else { printf(" [Unexpected format code: %d]\n", fdesc->trnptid.format_code); dumpHex((const char *)fdesc->trnptid.iscsi_name, (int)sizeof(fdesc->trnptid.iscsi_name), 0); } break; case MPATH_PROTOCOL_ID_SAS: printf(" SAS "); if (0 != fdesc->trnptid.format_code) printf(" [Unexpected format code: %d]\n", fdesc->trnptid.format_code); dumpHex((const char *)fdesc->trnptid.sas_address, 8, 0); break; default: return; } } int construct_transportid(const char * lcp, struct transportid transid[], int num_transportids) { int k = 0; int j, n, b, c, len, alen; const char * ecp; const char * isip; if ((0 == memcmp("fcp,", lcp, 4)) || (0 == memcmp("FCP,", lcp, 4))) { lcp += 4; k = strspn(lcp, "0123456789aAbBcCdDeEfF"); len = strlen(lcp); if (len != k) { fprintf(stderr, "badly formed symbolic FCP TransportID: %s\n", lcp); return 1; } transid[num_transportids].format_code = MPATH_PROTOCOL_ID_FC; transid[num_transportids].protocol_id = MPATH_WWUI_DEVICE_NAME; for (k = 0, j = 0, b = 0; k < 16; ++k) { c = lcp[k]; if (isdigit(c)) n = c - 0x30; else if (isupper(c)) n = c - 0x37; else n = c - 0x57; if (k & 1) { transid[num_transportids].n_port_name[j] = b | n; ++j; } else b = n << 4; } goto my_cont_b; } if ((0 == memcmp("sas,", lcp, 4)) || (0 == memcmp("SAS,", lcp, 4))) { lcp += 4; k = strspn(lcp, "0123456789aAbBcCdDeEfF"); len =strlen(lcp); if (len != k) { fprintf(stderr, "badly formed symbolic SAS TransportID: %s\n", lcp); return 1; } transid[num_transportids].format_code = MPATH_PROTOCOL_ID_SAS; transid[num_transportids].protocol_id = MPATH_WWUI_DEVICE_NAME; memcpy(&transid[num_transportids].sas_address, lcp, 8); goto my_cont_b; } if (0 == memcmp("iqn.", lcp, 4)) { ecp = strpbrk(lcp, " \t"); isip = strstr(lcp, ",i,0x"); if (ecp && (isip > ecp)) isip = NULL; len = ecp ? (ecp - lcp) : (int)strlen(lcp); transid[num_transportids].format_code = (isip ? MPATH_WWUI_PORT_IDENTIFIER:MPATH_WWUI_DEVICE_NAME); transid[num_transportids].protocol_id = MPATH_PROTOCOL_ID_ISCSI; alen = len + 1; /* at least one trailing null */ if (alen < 20) alen = 20; else if (0 != (alen % 4)) alen = ((alen / 4) + 1) * 4; if (alen > 241) { /* sam5r02.pdf A.2 (Annex) */ fprintf(stderr, "iSCSI name too long, alen=%d\n", alen); return 0; } transid[num_transportids].iscsi_name[1] = alen & 0xff; memcpy(&transid[num_transportids].iscsi_name[2], lcp, len); goto my_cont_b; } my_cont_b: if (k >= MPATH_MAX_PARAM_LEN) { fprintf(stderr, "build_transportid: array length exceeded\n"); return 1; } return 0; } multipath-tools-0.7.4/mpathpersist/main.h000066400000000000000000000014161320314174000205040ustar00rootroot00000000000000static struct option long_options[] = { {"verbose", 1, NULL, 'v'}, {"clear", 0, NULL, 'C'}, {"device", 1, NULL, 'd'}, {"help", 0, NULL, 'h'}, {"hex", 0, NULL, 'H'}, {"in", 0, NULL, 'i'}, {"out", 0, NULL, 'o'}, {"param-aptpl", 0, NULL, 'Z'}, {"param-rk", 1, NULL, 'K'}, {"param-sark", 1, NULL, 'S'}, {"preempt", 0, NULL, 'P'}, {"preempt-abort", 0, NULL, 'A'}, {"prout-type", 1, NULL, 'T'}, {"read-full-status", 0, NULL, 's'}, {"read-keys", 0, NULL, 'k'}, {"read-reservation", 0, NULL, 'r'}, {"register", 0, NULL, 'G'}, {"register-ignore", 0, NULL, 'I'}, {"release", 0, NULL, 'L'}, {"report-capabilities", 0, NULL, 'c'}, {"reserve", 0, NULL, 'R'}, {"transport-id", 1, NULL, 'X'}, {"alloc-length", 1, NULL, 'l'}, {NULL, 0, NULL, 0} }; static void usage(void); multipath-tools-0.7.4/mpathpersist/mpathpersist.8000066400000000000000000000111511320314174000222200ustar00rootroot00000000000000.\" ---------------------------------------------------------------------------- .\" Update the date below if you make any significant change. .\" Make sure there are no errors with: .\" groff -z -wall -b -e -t mpathpersist/mpathpersist.8 .\" .\" ---------------------------------------------------------------------------- . .TH MPATHPERSIST 8 2016-10-30 "Linux" . . .\" ---------------------------------------------------------------------------- .SH NAME .\" ---------------------------------------------------------------------------- . mpathpersist \- Manages SCSI persistent reservations on dm multipath devices. . . .\" ---------------------------------------------------------------------------- .SH SYNOPSIS .\" ---------------------------------------------------------------------------- . .B mpathpersist .RB [\| OPTIONS \|] .I device . . .\" ---------------------------------------------------------------------------- .SH DESCRIPTION .\" ---------------------------------------------------------------------------- . This utility is used to manage SCSI persistent reservations on Device Mapper Multipath devices. To be able to use this functionality, the \fIreservation_key\fR attribute must be defined in the \fI/etc/multipath.conf\fR file. Otherwise the \fBmultipathd\fR daemon will not check for persistent reservation for newly discovered paths or reinstated paths. . .LP \fBmpathpersist\fR supports the same command-line options as the \fBsg_persist\fR utility. . Consult the \fBsg_persist (8)\fR manual page for an in-depth discussion of the various options. . .\" ---------------------------------------------------------------------------- .SH OPTIONS .\" ---------------------------------------------------------------------------- . .TP .BI \-verbose|\-v " level" Verbosity: .RS .TP 5 .I 0 Critical messages. .TP .I 1 Error messages. .TP .I 2 Warning messages. .TP .I 3 Informational messages. .TP .I 4 Informational messages with trace enabled. .RE . .TP .BI \--device=\fIDEVICE\fB|\-d " DEVICE" Query or change DEVICE. . .TP .B \--help|\-h Output this usage message. . .TP .B \--hex|\-H Output response in hex. . .TP .B \--in|\-i Request PR In command. . .TP .B \--out|\-o Request PR Out command. . .TP .B \--param-aptpl|\-Z PR Out parameter 'APTPL'. . .TP .B \--read-keys|\-k PR In: Read Keys. . .TP .BI \--param-rk=\fIRK\fB|\-K " RK" PR Out parameter reservation key (RK is in hex, up to 8 bytes). . .TP .BI \--param-sark=\fISARK\fB|\-S " SARK" PR Out parameter service action reservation key (SARK is in hex). . .TP .B \--preempt|\-P PR Out: Preempt. . .TP .B \--clear|\-C PR Out: Clear registrations. . .TP .B \--preempt-abort|\-A PR Out: Preempt and Abort. . .TP .BI \--prout-type=\fITYPE\fB|\-T " TYPE" PR Out command type. . .TP .B \--read-full-status|\-s PR In: Read Full Status. . .TP .B \--read-keys|\-k PR In: Read Keys. . .TP .B \--read-reservation|\-r PR In: Read Reservation. . .TP .B \--register|\-G PR Out: Register. . .TP .B \--register-ignore|\-I PR Out: Register and Ignore. . .TP .B \--release|\-L PR Out: Release. . .TP .B \--report-capabilities|\-c PR In: Report Capabilities. . .TP .B \--reserve|\-R PR Out: Reserve. . .TP .BI \--transport-id=\fITIDS\fB|\-X " TIDS" TransportIDs can be mentioned in several forms. . .TP .BI \--alloc-length=\fILEN\fB|\-l " LEN" PR In: maximum allocation length. LEN is a decimal number between 0 and 8192. . . .\" ---------------------------------------------------------------------------- .SH EXAMPLE .\" ---------------------------------------------------------------------------- . .TP Register the Service Action Reservation Key for the /dev/mapper/mpath9 device: \fBmpathpersist --out --register --param-sark=\fI123abc \fB--prout-type=\fI5 /dev/mapper/mpath9\fR .TP Read the Service Action Reservation Key for the /dev/mapper/mpath9 device: \fBmpathpersist -i -k \fI/dev/mapper/mpath9\fR .TP Reserve the Service Action Reservation Key for the /dev/mapper/mpath9 device: \fBmpathpersist --out --reserve --param-sark=\fI123abc \fB--prout-type=\fI8 \fB-d \fI/dev/mapper/mpath9\fR .TP Read the reservation status of the /dev/mapper/mpath9 device: \fBmpathpersist -i -s -d \fI/dev/mapper/mpath9\fR . . .\" ---------------------------------------------------------------------------- .SH "SEE ALSO" .\" ---------------------------------------------------------------------------- . .BR multipath (8), .BR multipathd (8), .BR sg_persist (8). . . .\" ---------------------------------------------------------------------------- .SH AUTHORS .\" ---------------------------------------------------------------------------- . \fImultipath-tools\fR was developed by Christophe Varoqui and others. .\" EOF multipath-tools-0.7.4/multipath/000077500000000000000000000000001320314174000166715ustar00rootroot00000000000000multipath-tools-0.7.4/multipath/11-dm-mpath.rules000066400000000000000000000103461320314174000216770ustar00rootroot00000000000000ACTION!="add|change", GOTO="mpath_end" ENV{DM_UDEV_RULES_VSN}!="?*", GOTO="mpath_end" ENV{DM_UUID}!="mpath-?*", GOTO="mpath_end" IMPORT{db}="DM_DISABLE_OTHER_RULES_FLAG_OLD" IMPORT{db}="MPATH_DEVICE_READY" # If this uevent didn't come from dm, don't try to update the # device state ENV{DM_COOKIE}!="?*", ENV{DM_ACTION}!="PATH_*", IMPORT{db}="DM_UDEV_DISABLE_OTHER_RULES_FLAG", IMPORT{db}="DM_NOSCAN", GOTO="scan_import" ENV{.MPATH_DEVICE_READY_OLD}="$env{MPATH_DEVICE_READY}" # multipath sets DM_SUBSYSTEM_UDEV_FLAG2 when it reloads a # table with no active devices. If this happens, mark the # device not ready ENV{DM_SUBSYSTEM_UDEV_FLAG2}=="1", ENV{MPATH_DEVICE_READY}="0",\ GOTO="mpath_action" # If the last path has failed mark the device not ready # Note that DM_NR_VALID_PATHS is only set for PATH_FAILED|PATH_REINSTATED # events. # This may not be reliable, as events aren't necessarily received in order. ENV{DM_NR_VALID_PATHS}=="0", ENV{MPATH_DEVICE_READY}="0", GOTO="mpath_action" ENV{MPATH_SBIN_PATH}="/sbin" TEST!="$env{MPATH_SBIN_PATH}/multipath", ENV{MPATH_SBIN_PATH}="/usr/sbin" # Check the map state directly with multipath -U. # This doesn't attempt I/O on the device. PROGRAM=="$env{MPATH_SBIN_PATH}/multipath -U %k", GOTO="paths_ok" ENV{MPATH_DEVICE_READY}="0", GOTO="mpath_action" LABEL="paths_ok" # Don't mark a device ready on a PATH_FAILED event. even if # DM_NR_VALID_PATHS is greater than 0. Just keep the existing # value ENV{DM_ACTION}=="PATH_FAILED", GOTO="mpath_action" # This event is either a PATH_REINSTATED or a table reload where # there are active paths. Mark the device ready ENV{MPATH_DEVICE_READY}="1" LABEL="mpath_action" # DM_SUBSYSTEM_UDEV_FLAG0 is the "RELOAD" flag for multipath subsystem. # Drop the DM_ACTIVATION flag here as mpath reloads tables if any of its # paths are lost/recovered. For any stack above the mpath device, this is not # something that should be reacted upon since it would be useless extra work. # It's exactly mpath's job to provide *seamless* device access to any of the # paths that are available underneath. ENV{DM_SUBSYSTEM_UDEV_FLAG0}=="1", \ ENV{DM_ACTIVATION}="0", ENV{MPATH_UNCHANGED}="1" # For path failed or reinstated events, unset DM_ACTIVATION. # This is similar to the DM_SUBSYSTEM_UDEV_FLAG0 case above. ENV{DM_ACTION}=="PATH_FAILED|PATH_REINSTATED", \ ENV{DM_ACTIVATION}="0", ENV{MPATH_UNCHANGED}="1" # Do not initiate scanning if no path is available, # otherwise there would be a hang or IO error on access. # We'd like to avoid this, especially within udev processing. ENV{MPATH_DEVICE_READY}=="0", ENV{DM_NOSCAN}="1" # Also skip all foreign rules if no path is available. # Remember the original value of DM_DISABLE_OTHER_RULES_FLAG # and restore it back once we have at least one path available. ENV{MPATH_DEVICE_READY}=="0", ENV{.MPATH_DEVICE_READY_OLD}=="1",\ ENV{DM_DISABLE_OTHER_RULES_FLAG_OLD}=="",\ ENV{DM_DISABLE_OTHER_RULES_FLAG_OLD}="$env{DM_UDEV_DISABLE_OTHER_RULES_FLAG}" ENV{MPATH_DEVICE_READY}=="0", ENV{DM_UDEV_DISABLE_OTHER_RULES_FLAG}="1" ENV{MPATH_DEVICE_READY}!="0", ENV{.MPATH_DEVICE_READY_OLD}=="0",\ ENV{DM_UDEV_DISABLE_OTHER_RULES_FLAG}="$env{DM_DISABLE_OTHER_RULES_FLAG_OLD}",\ ENV{DM_DISABLE_OTHER_RULES_FLAG_OLD}="",\ ENV{DM_ACTIVATION}="1" # The code to check multipath state ends here. We need to set # properties and symlinks regardless whether the map is usable or # not. If symlinks get lost, systemd may auto-unmount file systems. LABEL="scan_import" ENV{DM_NOSCAN}!="1", GOTO="import_end" IMPORT{db}="ID_FS_TYPE" IMPORT{db}="ID_FS_USAGE" IMPORT{db}="ID_FS_UUID" IMPORT{db}="ID_FS_UUID_ENC" IMPORT{db}="ID_FS_LABEL" IMPORT{db}="ID_FS_LABEL_ENC" IMPORT{db}="ID_FS_VERSION" LABEL="import_end" # Multipath maps should take precedence over their members. ENV{DM_UDEV_LOW_PRIORITY_FLAG}!="1", OPTIONS+="link_priority=50" # Set some additional symlinks that typically exist for mpath # path members, too, and should be overridden. # kpartx_id is very robust, it works for suspended maps and maps # with 0 dependencies. It sets DM_TYPE, DM_PART, DM_WWN TEST=="/usr/lib/udev/kpartx_id", \ IMPORT{program}=="kpartx_id %M %m $env{DM_UUID}" ENV{DM_TYPE}=="?*", SYMLINK+="disk/by-id/$env{DM_TYPE}-$env{DM_NAME}" ENV{DM_WWN}=="?*", SYMLINK+="disk/by-id/wwn-$env{DM_WWN}" LABEL="mpath_end" multipath-tools-0.7.4/multipath/Makefile000066400000000000000000000024371320314174000203370ustar00rootroot00000000000000# # Copyright (C) 2003 Christophe Varoqui, # include ../Makefile.inc CFLAGS += $(BIN_CFLAGS) -I$(multipathdir) -I$(mpathcmddir) LDFLAGS += $(BIN_LDFLAGS) LIBDEPS += -L$(multipathdir) -lmultipath -L$(mpathcmddir) -lmpathcmd \ -lpthread -ldevmapper -ldl -ludev EXEC = multipath OBJS = main.o all: $(EXEC) $(EXEC): $(OBJS) $(CC) $(CFLAGS) $(OBJS) -o $(EXEC) $(LDFLAGS) $(LIBDEPS) $(GZIP) $(EXEC).8 > $(EXEC).8.gz $(GZIP) $(EXEC).conf.5 > $(EXEC).conf.5.gz install: $(INSTALL_PROGRAM) -d $(DESTDIR)$(bindir) $(INSTALL_PROGRAM) -m 755 $(EXEC) $(DESTDIR)$(bindir)/ $(INSTALL_PROGRAM) -d $(DESTDIR)$(udevrulesdir) $(INSTALL_PROGRAM) -m 644 11-dm-mpath.rules $(DESTDIR)$(udevrulesdir) $(INSTALL_PROGRAM) -m 644 $(EXEC).rules $(DESTDIR)$(libudevdir)/rules.d/56-multipath.rules $(INSTALL_PROGRAM) -d $(DESTDIR)$(man8dir) $(INSTALL_PROGRAM) -m 644 $(EXEC).8.gz $(DESTDIR)$(man8dir) $(INSTALL_PROGRAM) -d $(DESTDIR)$(man5dir) $(INSTALL_PROGRAM) -m 644 $(EXEC).conf.5.gz $(DESTDIR)$(man5dir) uninstall: $(RM) $(DESTDIR)$(bindir)/$(EXEC) $(RM) $(DESTDIR)$(udevrulesdir)/11-dm-mpath.rules $(RM) $(DESTDIR)$(libudevdir)/rules.d/56-multipath.rules $(RM) $(DESTDIR)$(man8dir)/$(EXEC).8.gz $(RM) $(DESTDIR)$(man5dir)/$(EXEC).conf.5.gz clean: $(RM) core *.o $(EXEC) *.gz multipath-tools-0.7.4/multipath/main.c000066400000000000000000000472411320314174000177710ustar00rootroot00000000000000/* * Soft: multipath device mapper target autoconfig * * Version: $Id: main.h,v 0.0.1 2003/09/18 15:13:38 cvaroqui Exp $ * * Author: Christophe Varoqui * * 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. * * 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. * * Copyright (c) 2003, 2004, 2005 Christophe Varoqui * Copyright (c) 2005 Benjamin Marzinski, Redhat * Copyright (c) 2005 Kiyoshi Ueda, NEC * Copyright (c) 2005 Patrick Caulfield, Redhat * Copyright (c) 2005 Edward Goggin, EMC */ #include #include #include #include #include #include #include #include "checkers.h" #include "prio.h" #include "vector.h" #include "memory.h" #include #include "devmapper.h" #include "util.h" #include "defaults.h" #include "config.h" #include "structs.h" #include "structs_vec.h" #include "dmparser.h" #include "sysfs.h" #include "blacklist.h" #include "discovery.h" #include "debug.h" #include "switchgroup.h" #include "print.h" #include "alias.h" #include "configure.h" #include "pgpolicies.h" #include "version.h" #include #include #include #include "wwids.h" #include "uxsock.h" #include "mpath_cmd.h" int logsink; struct udev *udev; struct config *multipath_conf; struct config *get_multipath_config(void) { return multipath_conf; } void put_multipath_config(struct config *conf) { /* Noop for now */ } void rcu_register_thread_memb(void) {} void rcu_unregister_thread_memb(void) {} static int filter_pathvec (vector pathvec, char * refwwid) { int i; struct path * pp; if (!refwwid || !strlen(refwwid)) return 0; vector_foreach_slot (pathvec, pp, i) { if (strncmp(pp->wwid, refwwid, WWID_SIZE) != 0) { condlog(3, "skip path %s : out of scope", pp->dev); free_path(pp); vector_del_slot(pathvec, i); i--; } } return 0; } static void usage (char * progname) { fprintf (stderr, VERSION_STRING); fprintf (stderr, "Usage:\n"); fprintf (stderr, " %s [-a|-c|-w|-W] [-d] [-r] [-i] [-v lvl] [-p pol] [-b fil] [-q] [dev]\n", progname); fprintf (stderr, " %s -l|-ll|-f [-v lvl] [-b fil] [-R num] [dev]\n", progname); fprintf (stderr, " %s -F [-v lvl] [-R num]\n", progname); fprintf (stderr, " %s -t\n", progname); fprintf (stderr, " %s -h\n", progname); fprintf (stderr, "\n" "Where:\n" " -h print this usage text\n" " -l show multipath topology (sysfs and DM info)\n" " -ll show multipath topology (maximum info)\n" " -f flush a multipath device map\n" " -F flush all multipath device maps\n" " -a add a device wwid to the wwids file\n" " -c check if a device should be a path in a multipath device\n" " -C check if a multipath device has usable paths\n" " -q allow queue_if_no_path when multipathd is not running\n" " -d dry run, do not create or update devmaps\n" " -t display the currently used multipathd configuration\n" " -r force devmap reload\n" " -i ignore wwids file\n" " -B treat the bindings file as read only\n" " -b fil bindings file location\n" " -w remove a device from the wwids file\n" " -W reset the wwids file include only the current devices\n" " -p pol force all maps to specified path grouping policy :\n" " . failover one path per priority group\n" " . multibus all paths in one priority group\n" " . group_by_serial one priority group per serial\n" " . group_by_prio one priority group per priority lvl\n" " . group_by_node_name one priority group per target node\n" " -v lvl verbosity level\n" " . 0 no output\n" " . 1 print created devmap names only\n" " . 2 default verbosity\n" " . 3 print debug information\n" " -R num number of times to retry removes of in-use devices\n" " dev action limited to:\n" " . multipath named 'dev' (ex: mpath0) or\n" " . multipath whose wwid is 'dev' (ex: 60051..)\n" " . multipath including the path named 'dev' (ex: /dev/sda)\n" " . multipath including the path with maj:min 'dev' (ex: 8:0)\n" ); } static int update_paths (struct multipath * mpp) { int i, j; struct pathgroup * pgp; struct path * pp; struct config *conf; if (!mpp->pg) return 0; vector_foreach_slot (mpp->pg, pgp, i) { if (!pgp->paths) continue; vector_foreach_slot (pgp->paths, pp, j) { if (!strlen(pp->dev)) { if (devt2devname(pp->dev, FILE_NAME_SIZE, pp->dev_t)) { /* * path is not in sysfs anymore */ pp->chkrstate = pp->state = PATH_DOWN; continue; } pp->mpp = mpp; conf = get_multipath_config(); if (pathinfo(pp, conf, DI_ALL)) pp->state = PATH_UNCHECKED; put_multipath_config(conf); continue; } pp->mpp = mpp; if (pp->state == PATH_UNCHECKED || pp->state == PATH_WILD) { conf = get_multipath_config(); if (pathinfo(pp, conf, DI_CHECKER)) pp->state = PATH_UNCHECKED; put_multipath_config(conf); } if (pp->priority == PRIO_UNDEF) { conf = get_multipath_config(); if (pathinfo(pp, conf, DI_PRIO)) pp->priority = PRIO_UNDEF; put_multipath_config(conf); } } } return 0; } static int get_dm_mpvec (enum mpath_cmds cmd, vector curmp, vector pathvec, char * refwwid) { int i; struct multipath * mpp; char params[PARAMS_SIZE], status[PARAMS_SIZE]; if (dm_get_maps(curmp)) return 1; vector_foreach_slot (curmp, mpp, i) { /* * discard out of scope maps */ if (refwwid && strlen(refwwid) && strncmp(mpp->wwid, refwwid, WWID_SIZE)) { condlog(3, "skip map %s: out of scope", mpp->alias); free_multipath(mpp, KEEP_PATHS); vector_del_slot(curmp, i); i--; continue; } if (cmd == CMD_VALID_PATH) continue; dm_get_map(mpp->alias, &mpp->size, params); condlog(3, "params = %s", params); dm_get_status(mpp->alias, status); condlog(3, "status = %s", status); disassemble_map(pathvec, params, mpp, 0); /* * disassemble_map() can add new paths to pathvec. * If not in "fast list mode", we need to fetch information * about them */ if (cmd != CMD_LIST_SHORT) update_paths(mpp); if (cmd == CMD_LIST_LONG) mpp->bestpg = select_path_group(mpp); disassemble_status(status, mpp); if (cmd == CMD_LIST_SHORT || cmd == CMD_LIST_LONG) { struct config *conf = get_multipath_config(); print_multipath_topology(mpp, conf->verbosity); put_multipath_config(conf); } if (cmd == CMD_CREATE) reinstate_paths(mpp); } return 0; } static int check_usable_paths(struct config *conf, const char *devpath, enum devtypes dev_type) { struct udev_device *ud = NULL; struct multipath *mpp = NULL; struct pathgroup *pg; struct path *pp; char *mapname; vector pathvec = NULL; char params[PARAMS_SIZE], status[PARAMS_SIZE]; dev_t devt; int r = 1, i, j; ud = get_udev_device(devpath, dev_type); if (ud == NULL) return r; devt = udev_device_get_devnum(ud); if (!dm_is_dm_major(major(devt))) { condlog(1, "%s is not a dm device", devpath); goto out; } mapname = dm_mapname(major(devt), minor(devt)); if (mapname == NULL) { condlog(1, "dm device not found: %s", devpath); goto out; } if (!dm_is_mpath(mapname)) { condlog(1, "%s is not a multipath map", devpath); goto free; } /* pathvec is needed for disassemble_map */ pathvec = vector_alloc(); if (pathvec == NULL) goto free; mpp = dm_get_multipath(mapname); if (mpp == NULL) goto free; dm_get_map(mpp->alias, &mpp->size, params); dm_get_status(mpp->alias, status); disassemble_map(pathvec, params, mpp, 0); disassemble_status(status, mpp); vector_foreach_slot (mpp->pg, pg, i) { vector_foreach_slot (pg->paths, pp, j) { pp->udev = get_udev_device(pp->dev_t, DEV_DEVT); if (pp->udev == NULL) continue; if (pathinfo(pp, conf, DI_SYSFS|DI_NOIO) != PATHINFO_OK) continue; if (pp->state == PATH_UP && pp->dmstate == PSTATE_ACTIVE) { condlog(3, "%s: path %s is usable", devpath, pp->dev); r = 0; goto found; } } } found: condlog(2, "%s:%s usable paths found", devpath, r == 0 ? "" : " no"); free: FREE(mapname); free_multipath(mpp, FREE_PATHS); vector_free(pathvec); out: udev_device_unref(ud); return r; } /* * Return value: * -1: Retry * 0: Success * 1: Failure */ static int configure (struct config *conf, enum mpath_cmds cmd, enum devtypes dev_type, char *devpath) { vector curmp = NULL; vector pathvec = NULL; struct vectors vecs; int r = 1; int di_flag = 0; char * refwwid = NULL; char * dev = NULL; /* * allocate core vectors to store paths and multipaths */ curmp = vector_alloc(); pathvec = vector_alloc(); if (!curmp || !pathvec) { condlog(0, "can not allocate memory"); goto out; } vecs.pathvec = pathvec; vecs.mpvec = curmp; dev = convert_dev(devpath, (dev_type == DEV_DEVNODE)); /* * if we have a blacklisted device parameter, exit early */ if (dev && (dev_type == DEV_DEVNODE || dev_type == DEV_UEVENT) && cmd != CMD_REMOVE_WWID && (filter_devnode(conf->blist_devnode, conf->elist_devnode, dev) > 0)) { if (cmd == CMD_VALID_PATH) printf("%s is not a valid multipath device path\n", devpath); goto out; } /* * scope limiting must be translated into a wwid * failing the translation is fatal (by policy) */ if (devpath) { int failed = get_refwwid(cmd, devpath, dev_type, pathvec, &refwwid); if (!refwwid) { condlog(4, "%s: failed to get wwid", devpath); if (failed == 2 && cmd == CMD_VALID_PATH) printf("%s is not a valid multipath device path\n", devpath); else condlog(3, "scope is null"); goto out; } if (cmd == CMD_REMOVE_WWID) { r = remove_wwid(refwwid); if (r == 0) printf("wwid '%s' removed\n", refwwid); else if (r == 1) { printf("wwid '%s' not in wwids file\n", refwwid); r = 0; } goto out; } if (cmd == CMD_ADD_WWID) { r = remember_wwid(refwwid); if (r == 0) printf("wwid '%s' added\n", refwwid); else printf("failed adding '%s' to wwids file\n", refwwid); goto out; } condlog(3, "scope limited to %s", refwwid); /* If you are ignoring the wwids file and find_multipaths is * set, you need to actually check if there are two available * paths to determine if this path should be multipathed. To * do this, we put off the check until after discovering all * the paths */ if (cmd == CMD_VALID_PATH && (!conf->find_multipaths || !conf->ignore_wwids)) { if (conf->ignore_wwids || check_wwids_file(refwwid, 0) == 0) r = 0; printf("%s %s a valid multipath device path\n", devpath, r == 0 ? "is" : "is not"); goto out; } } /* * get a path list */ if (devpath) di_flag = DI_WWID; if (cmd == CMD_LIST_LONG) /* extended path info '-ll' */ di_flag |= DI_SYSFS | DI_CHECKER | DI_SERIAL; else if (cmd == CMD_LIST_SHORT) /* minimum path info '-l' */ di_flag |= DI_SYSFS; else /* maximum info */ di_flag = DI_ALL; if (path_discovery(pathvec, di_flag) < 0) goto out; if (conf->verbosity > 2) print_all_paths(pathvec, 1); get_path_layout(pathvec, 0); if (get_dm_mpvec(cmd, curmp, pathvec, refwwid)) goto out; filter_pathvec(pathvec, refwwid); if (cmd == CMD_VALID_PATH) { /* This only happens if find_multipaths and * ignore_wwids is set. * If there is currently a multipath device matching * the refwwid, or there is more than one path matching * the refwwid, then the path is valid */ if (VECTOR_SIZE(curmp) != 0 || VECTOR_SIZE(pathvec) > 1) r = 0; printf("%s %s a valid multipath device path\n", devpath, r == 0 ? "is" : "is not"); goto out; } if (cmd != CMD_CREATE && cmd != CMD_DRY_RUN) { r = 0; goto out; } /* * core logic entry point */ r = coalesce_paths(&vecs, NULL, refwwid, conf->force_reload, cmd); out: if (refwwid) FREE(refwwid); free_multipathvec(curmp, KEEP_PATHS); free_pathvec(pathvec, FREE_PATHS); return r; } static int dump_config (struct config *conf) { char * c, * tmp = NULL; char * reply; unsigned int maxlen = 256; int again = 1; reply = MALLOC(maxlen); while (again) { if (!reply) { if (tmp) free(tmp); return 1; } c = tmp = reply; c += snprint_defaults(conf, c, reply + maxlen - c); again = ((c - reply) == maxlen); if (again) { reply = REALLOC(reply, maxlen *= 2); continue; } c += snprint_blacklist(conf, c, reply + maxlen - c); again = ((c - reply) == maxlen); if (again) { reply = REALLOC(reply, maxlen *= 2); continue; } c += snprint_blacklist_except(conf, c, reply + maxlen - c); again = ((c - reply) == maxlen); if (again) { reply = REALLOC(reply, maxlen *= 2); continue; } c += snprint_hwtable(conf, c, reply + maxlen - c, conf->hwtable); again = ((c - reply) == maxlen); if (again) { reply = REALLOC(reply, maxlen *= 2); continue; } c += snprint_overrides(conf, c, reply + maxlen - c, conf->overrides); again = ((c - reply) == maxlen); if (again) { reply = REALLOC(reply, maxlen *= 2); continue; } if (VECTOR_SIZE(conf->mptable) > 0) { c += snprint_mptable(conf, c, reply + maxlen - c, conf->mptable); again = ((c - reply) == maxlen); if (again) reply = REALLOC(reply, maxlen *= 2); } } printf("%s", reply); FREE(reply); return 0; } static int get_dev_type(char *dev) { struct stat buf; int i; if (stat(dev, &buf) == 0 && S_ISBLK(buf.st_mode)) { if (dm_is_dm_major(major(buf.st_rdev))) return DEV_DEVMAP; return DEV_DEVNODE; } else if (sscanf(dev, "%d:%d", &i, &i) == 2) return DEV_DEVT; else if (valid_alias(dev)) return DEV_DEVMAP; return DEV_NONE; } int main (int argc, char *argv[]) { int arg; extern char *optarg; extern int optind; int r = 1; enum mpath_cmds cmd = CMD_CREATE; enum devtypes dev_type = DEV_NONE; char *dev = NULL; struct config *conf; int retries = -1; udev = udev_new(); logsink = 0; conf = load_config(DEFAULT_CONFIGFILE); if (!conf) exit(1); multipath_conf = conf; conf->retrigger_tries = 0; while ((arg = getopt(argc, argv, ":adcChl::FfM:v:p:b:BrR:itquUwW")) != EOF ) { switch(arg) { case 1: printf("optarg : %s\n",optarg); break; case 'v': if (sizeof(optarg) > sizeof(char *) || !isdigit(optarg[0])) { usage (argv[0]); exit(1); } conf->verbosity = atoi(optarg); break; case 'b': conf->bindings_file = strdup(optarg); break; case 'B': conf->bindings_read_only = 1; break; case 'q': conf->allow_queueing = 1; break; case 'c': cmd = CMD_VALID_PATH; break; case 'C': cmd = CMD_USABLE_PATHS; break; case 'd': if (cmd == CMD_CREATE) cmd = CMD_DRY_RUN; break; case 'f': conf->remove = FLUSH_ONE; break; case 'F': conf->remove = FLUSH_ALL; break; case 'l': if (optarg && !strncmp(optarg, "l", 1)) cmd = CMD_LIST_LONG; else cmd = CMD_LIST_SHORT; break; case 'M': #if _DEBUG_ debug = atoi(optarg); #endif break; case 'p': conf->pgpolicy_flag = get_pgpolicy_id(optarg); if (conf->pgpolicy_flag == IOPOLICY_UNDEF) { printf("'%s' is not a valid policy\n", optarg); usage(argv[0]); exit(1); } break; case 'r': conf->force_reload = FORCE_RELOAD_YES; break; case 'i': conf->ignore_wwids = 1; break; case 't': r = dump_config(conf); goto out_free_config; case 'h': usage(argv[0]); exit(0); case 'u': cmd = CMD_VALID_PATH; dev_type = DEV_UEVENT; break; case 'U': cmd = CMD_USABLE_PATHS; dev_type = DEV_UEVENT; break; case 'w': cmd = CMD_REMOVE_WWID; break; case 'W': cmd = CMD_RESET_WWIDS; break; case 'a': cmd = CMD_ADD_WWID; break; case 'R': retries = atoi(optarg); break; case ':': fprintf(stderr, "Missing option argument\n"); usage(argv[0]); exit(1); case '?': fprintf(stderr, "Unknown switch: %s\n", optarg); usage(argv[0]); exit(1); default: usage(argv[0]); exit(1); } } /* * FIXME: new device detection with find_multipaths currently * doesn't work reliably. */ if (cmd == CMD_VALID_PATH && conf->find_multipaths && conf->ignore_wwids) { condlog(2, "ignoring -i flag because find_multipath is set in multipath.conf"); conf->ignore_wwids = 0; } if (getuid() != 0) { fprintf(stderr, "need to be root\n"); exit(1); } if (optind < argc) { dev = MALLOC(FILE_NAME_SIZE); if (!dev) goto out; strncpy(dev, argv[optind], FILE_NAME_SIZE); if (dev_type != DEV_UEVENT) dev_type = get_dev_type(dev); if (dev_type == DEV_NONE) { condlog(0, "'%s' is not a valid argument\n", dev); goto out; } } if (dev_type == DEV_UEVENT) { openlog("multipath", 0, LOG_DAEMON); setlogmask(LOG_UPTO(conf->verbosity + 3)); logsink = 1; } if (conf->max_fds) { struct rlimit fd_limit; fd_limit.rlim_cur = conf->max_fds; fd_limit.rlim_max = conf->max_fds; if (setrlimit(RLIMIT_NOFILE, &fd_limit) < 0) condlog(0, "can't set open fds limit to %d : %s", conf->max_fds, strerror(errno)); } libmp_udev_set_sync_support(1); if (init_checkers(conf->multipath_dir)) { condlog(0, "failed to initialize checkers"); goto out; } if (init_prio(conf->multipath_dir)) { condlog(0, "failed to initialize prioritizers"); goto out; } if (cmd == CMD_USABLE_PATHS) { r = check_usable_paths(conf, dev, dev_type); goto out; } if (cmd == CMD_VALID_PATH && (!dev || dev_type == DEV_DEVMAP)) { condlog(0, "the -c option requires a path to check"); goto out; } if (cmd == CMD_VALID_PATH && dev_type == DEV_UEVENT) { int fd; fd = mpath_connect(); if (fd == -1) { condlog(3, "%s: daemon is not running", dev); if (!systemd_service_enabled(dev)) { printf("%s is not a valid " "multipath device path\n", dev); goto out; } } else mpath_disconnect(fd); } if (cmd == CMD_REMOVE_WWID && !dev) { condlog(0, "the -w option requires a device"); goto out; } if (cmd == CMD_RESET_WWIDS) { struct multipath * mpp; int i; vector curmp; curmp = vector_alloc(); if (!curmp) { condlog(0, "can't allocate memory for mp list"); goto out; } if (dm_get_maps(curmp) == 0) r = replace_wwids(curmp); if (r == 0) printf("successfully reset wwids\n"); vector_foreach_slot_backwards(curmp, mpp, i) { vector_del_slot(curmp, i); free_multipath(mpp, KEEP_PATHS); } vector_free(curmp); goto out; } if (retries < 0) retries = conf->remove_retries; if (conf->remove == FLUSH_ONE) { if (dev_type == DEV_DEVMAP) { r = dm_suspend_and_flush_map(dev, retries); } else condlog(0, "must provide a map name to remove"); goto out; } else if (conf->remove == FLUSH_ALL) { r = dm_flush_maps(retries); goto out; } while ((r = configure(conf, cmd, dev_type, dev)) < 0) condlog(3, "restart multipath configuration process"); out: dm_lib_release(); dm_lib_exit(); cleanup_prio(); cleanup_checkers(); if (dev_type == DEV_UEVENT) closelog(); out_free_config: /* * Freeing config must be done after dm_lib_exit(), because * the logging function (dm_write_log()), which is called there, * references the config. */ free_config(conf); conf = NULL; udev_unref(udev); if (dev) FREE(dev); #ifdef _DEBUG_ dbg_free_final(NULL); #endif return r; } multipath-tools-0.7.4/multipath/multipath.8000066400000000000000000000117551320314174000210020ustar00rootroot00000000000000.\" ---------------------------------------------------------------------------- .\" Update the date below if you make any significant change. .\" Make sure there are no errors with: .\" groff -z -wall -b -e -t multipath/multipath.8 .\" .\" ---------------------------------------------------------------------------- . .TH MULTIPATH 8 2016-10-26 "Linux" . . .\" ---------------------------------------------------------------------------- .SH NAME .\" ---------------------------------------------------------------------------- . multipath \- Device mapper target autoconfig. . . .\" ---------------------------------------------------------------------------- .SH SYNOPSIS .\" ---------------------------------------------------------------------------- . .B multipath .RB [\| \-v\ \c .IR verbosity \|] .RB [\| \-b\ \c .IR bindings_file \|] .RB [\| \-d \|] .RB [\| \-h | \-l | \-ll | \-f | \-t | \-F | \-B | \-c | \-C | \-q | \-r | \-i | \-a | \-u | \-U | \-w | \-W \|] .RB [\| \-p\ \c .IR failover | multibus | group_by_serial | group_by_prio | group_by_node_name \|] .RB [\| \-R\ \c .IR retries \|] .RB [\| device \|] . . .\" ---------------------------------------------------------------------------- .SH DESCRIPTION .\" ---------------------------------------------------------------------------- . .B multipath is used to detect and coalesce multiple paths to devices, for fail-over or performance reasons. . . .\" ---------------------------------------------------------------------------- .SH OPTIONS .\" ---------------------------------------------------------------------------- . .TP .BI \-v " level" Verbosity, print all paths and multipaths: .RS 1.2i .TP 1.2i .I 0 No output. .TP .I 1 Print the created or updated multipath names only, for use to feed other tools like kpartx. .TP .I 2 + Print all info: detected paths, coalesced paths (ie multipaths) and device maps. .RE . .TP .B \-h Print usage text. . .TP .B \-d Dry run, do not create or update devmaps. . .TP .B \-l Show the current multipath topology from information fetched in sysfs and the device mapper. . .TP .B \-ll Show the current multipath topology from all available information (sysfs, the device mapper, path checkers ...). . .TP .B \-f Flush a multipath device map specified as parameter, if unused. . .TP .B \-F Flush all unused multipath device maps. . .TP .B \-t Display the currently used multipathd configuration. . .TP .B \-r Force devmap reload. . .TP .B \-i Ignore WWIDs file when processing devices. . .TP .B \-B Treat the bindings file as read only. . .TP .BI \-b " bindings_file" Set user_friendly_names bindings file location. The default is \fI/etc/multipath/bindings\fR. . .TP .B \-c Check if a block device should be a path in a multipath device. . .TP .B \-C Check if a multipath device has usable paths. This can be used to test whether or not I/O on this device is likely to succeed. The command itself doesn't attempt to do I/O on the device. . .TP .B \-q Allow device tables with \fIqueue_if_no_path\fR when multipathd is not running. . .TP .B \-a Add the WWID for the specified device to the WWIDs file. . .TP .B \-u Check if the device specified in the program environment should be a path in a multipath device. . .TP .B \-U Check if the device specified in the program environment is a multipath device with usable paths. See \fB-C\fB. . .TP .B \-w Remove the WWID for the specified device from the WWIDs file. . .TP .B \-W Reset the WWIDs file to only include the current multipath devices. . .TP .BI \-p " policy" Force new maps to use the specified policy: .RS 1.2i .TP 1.2i .I failover One path per priority group. .TP .I multibus All paths in one priority group. .TP .I group_by_serial One priority group per serial number. .TP .I group_by_prio One priority group per priority value. Priorities are determined by callout programs specified as a global, per-controller or per-multipath option in the configuration file. .TP .I group_by_node_name One priority group per target node name. Target node names are fetched in \fI/sys/class/fc_transport/target*/node_name\fR. .TP .RE Existing maps are not modified. . .TP .BI \-R " retries" Number of times to retry flushing multipath devices that are in-use. The default is \fI0\fR. . .TP .BI device Update only the devmap specified by .IR device , which is either: .RS 1.2i .IP \[bu] A devmap name. .IP \[bu] A path associated with the desired devmap; the path may be in one of the following formats: .RS 1.2i .IP \[bu] .B /dev/sdX .IP \[bu] .B major:minor . . .\" ---------------------------------------------------------------------------- .SH "SEE ALSO" .\" ---------------------------------------------------------------------------- . .BR multipathd (8), .BR multipath.conf (5), .BR kpartx (8), .BR udev (8), .BR dmsetup (8), .BR hotplug (8). . . .\" ---------------------------------------------------------------------------- .SH AUTHORS .\" ---------------------------------------------------------------------------- . \fImultipath-tools\fR was developed by Christophe Varoqui and others. .\" EOF multipath-tools-0.7.4/multipath/multipath.conf.5000066400000000000000000001133751320314174000217240ustar00rootroot00000000000000.\" ---------------------------------------------------------------------------- .\" Update the date below if you make any significant change. .\" Make sure there are no errors with: .\" groff -z -wall -b -e -t multipath/multipath.conf.5 .\" man --warnings -E UTF-8 -l -Tutf8 -Z multipath/multipath.conf.5 >/dev/null .\" .\" TODO: Look for XXX and ??? .\" .\" ---------------------------------------------------------------------------- . .TH MULTIPATH.CONF 5 2017-08-18 Linux . . .\" ---------------------------------------------------------------------------- .SH NAME .\" ---------------------------------------------------------------------------- . multipath.conf \- multipath daemon configuration file. . . .\" ---------------------------------------------------------------------------- .SH DESCRIPTION .\" ---------------------------------------------------------------------------- . .B "/etc/multipath.conf" is the configuration file for the multipath daemon. It is used to overwrite the built-in configuration table of \fBmultipathd\fP. Any line whose first non-white-space character is a '#' is considered a comment line. Empty lines are ignored. .PP Currently used multipathd configuration can be displayed with the \fBmultipath -t\fR or \fBmultipathd show config\fR command. . . .\" ---------------------------------------------------------------------------- .SH SYNTAX .\" ---------------------------------------------------------------------------- . The configuration file contains entries of the form: .RS .nf .ft B .sp
{ .RS .ft B .I "..." .ft B { .RS .ft B .I "..." .RE .ft B } .RE .ft B } .ft R .fi .RE .LP Each \fIsection\fP contains one or more attributes or subsections. The recognized keywords for attributes or subsections depend on the section in which they occur. .LP . . The following \fIsection\fP keywords are recognized: .TP 17 .B defaults This section defines default values for attributes which are used whenever no values are given in the appropriate device or multipath sections. .TP .B blacklist This section defines which devices should be excluded from the multipath topology discovery. .TP .B blacklist_exceptions This section defines which devices should be included in the multipath topology discovery, despite being listed in the \fIblacklist\fR section. .TP .B multipaths This section defines the multipath topologies. They are indexed by a \fIWorld Wide Identifier\fR(WWID). For details on the WWID generation see section \fIWWID generation\fR below. .TP .B devices This section defines the device-specific settings. .TP .B overrides This section defines values for attributes that should override the device-specific settings for all devices. .RE .LP . . .\" ---------------------------------------------------------------------------- .SH "defaults section" .\" ---------------------------------------------------------------------------- . The \fIdefaults\fR section recognizes the following keywords: . . .TP 17 .B verbosity Default verbosity. Higher values increase the verbosity level. Valid levels are between 0 and 6. .RS .TP The default is: \fB2\fR .RE . . .TP .B polling_interval Interval between two path checks in seconds. For properly functioning paths, the interval between checks will gradually increase to \fImax_polling_interval\fR. This value will be overridden by the \fIWatchdogSec\fR setting in the multipathd.service definition if systemd is used. .RS .TP The default is: \fB5\fR .RE . . .TP .B max_polling_interval Maximal interval between two path checks in seconds. .RS .TP The default is: \fB4 * polling_interval\fR .RE . . .TP .B reassign_maps Enable reassigning of device-mapper maps. With this option multipathd will remap existing device-mapper maps to always point to multipath device, not the underlying block devices. Possible values are \fIyes\fR and \fIno\fR. .RS .TP The default is: \fBno\fR .RE . . .TP .B multipath_dir Directory where the dynamic shared objects are stored. Defined at compile time, commonly \fI/lib64/multipath/\fR or \fI/lib/multipath/\fR. .RS .TP The default is: \fB\fR .RE . . .TP .B path_selector The default path selector algorithm to use; they are offered by the kernel multipath target. There are three selector algorithms: .RS .TP 12 .I "round-robin 0" Loop through every path in the path group, sending the same amount of I/O to each. Some aspects of behavior can be controlled with the attributes: \fIrr_min_io\fR, \fIrr_min_io_rq\fR and \fIrr_weight\fR. .TP .I "queue-length 0" (Since 2.6.31 kernel) Choose the path for the next bunch of I/O based on the amount of outstanding I/O to the path. .TP .I "service-time 0" (Since 2.6.31 kernel) Choose the path for the next bunch of I/O based on the amount of outstanding I/O to the path and its relative throughput. .TP The default is: \fBservice-time 0\fR .RE . . .TP .B path_grouping_policy The default path grouping policy to apply to unspecified multipaths. Possible values are: .RS .TP 12 .I failover One path per priority group. .TP .I multibus All paths in one priority group. .TP .I group_by_serial One priority group per serial number. .TP .I group_by_prio One priority group per priority value. Priorities are determined by callout programs specified as a global, per-controller or per-multipath option in the configuration file. .TP .I group_by_node_name One priority group per target node name. Target node names are fetched in \fI/sys/class/fc_transport/target*/node_name\fR. .TP The default is: \fBfailover\fR .RE . . .TP .B uid_attrs The udev attribute providing a unique path identifier for corresponding type of path devices. If this field is configured and matched with type of device, it would override any other methods providing for device unique identifier in config file, and it would activate merging uevents according to the identifier to promote effiecncy in processing uevents. It has no default value, if you want to identify path by udev attribute and to activate merging uevents for SCSI and DASD devices, you can set its value as: \fIuid_attrs "sd:ID_SERIAL dasd:ID_UID"\fR. .RS .TP The default is: \fB\fR .RE . . .TP .B uid_attribute The udev attribute providing a unique path identifier. .RS .TP The default is: for SCSI devices \fBID_SERIAL\fR .TP The default is: for DASD devices \fBID_UID\fR .TP The default is: for NVME devices \fBID_WWN\fR .RE . . .TP .B getuid_callout (Superseded by \fIuid_attribute\fR) The default program and args to callout to obtain a unique path identifier. Should be specified with an absolute path. .RS .TP The default is: \fB\fR .RE . . .TP .B prio The name of the path priority routine. The specified routine should return a numeric value specifying the relative priority of this path. Higher number have a higher priority. \fI"none"\fR is a valid value. Currently the following path priority routines are implemented: .RS .TP 12 .I const Return a constant priority of \fI1\fR. .TP .I sysfs Use the sysfs attributes \fIaccess_state\fR and \fIpreferred_path\fR to generate the path priority. This prioritizer accepts the optional prio_arg \fIexclusive_pref_bit\fR. .TP .I emc (Hardware-dependent) Generate the path priority for DGC class arrays as CLARiiON CX/AX and EMC VNX and Unity families. .TP .I alua (Hardware-dependent) Generate the path priority based on the SCSI-3 ALUA settings. This prioritizer accepts the optional prio_arg \fIexclusive_pref_bit\fR. .TP .I ontap (Hardware-dependent) Generate the path priority for NetApp ONTAP class and OEM arrays as IBM NSeries. .TP .I rdac (Hardware-dependent) Generate the path priority for LSI/Engenio/NetApp RDAC class as NetApp SANtricity E/EF Series, and OEM arrays from IBM DELL SGI STK and SUN. .TP .I hp_sw (Hardware-dependent) Generate the path priority for HP/COMPAQ/DEC HSG80 and MSA/HSV arrays with Active/Standby mode exclusively. .TP .I hds (Hardware-dependent) Generate the path priority for Hitachi AMS 2000 and HUS 100 families of arrays. .TP .I random Generate a random priority between 1 and 10. .TP .I weightedpath Generate the path priority based on the regular expression and the priority provided as argument. Requires prio_args keyword. .TP .I path_latency Generate the path priority based on a latency algorithm. Requires prio_args keyword. .TP .I datacore (Hardware-dependent) Generate the path priority for some DataCore storage arrays. Requires prio_args keyword. .TP .I iet (iSCSI only) Generate path priority for iSCSI targets based on IP address. Requires prio_args keyword. .PP The default depends on the \fBdetect_prio\fR setting: If \fBdetect_prio\fR is \fByes\fR (default), the default priority algorithm is \fBsysfs\fR (except for NetAPP E-Series, where it is \fBalua\fR). If \fBdetect_prio\fR is \fBno\fR, the default priority algorithm is \fBconst\fR. .RE . . .TP .B prio_args Arguments to pass to to the prio function. This only applies to certain prioritizers: .RS .TP 12 .I weighted Needs a value of the form \fI" ..."\fR .RS .TP 8 .I hbtl Regex can be of SCSI H:B:T:L format. For example: 1:0:.:. , *:0:0:. .TP .I devname Regex can be of device name format. For example: sda , sd.e .TP .I serial Regex can be of serial number format. For example: .*J1FR.*324 . The serial can be looked up through sysfs or by running multipathd show paths format "%z". For example: 0395J1FR904324 .TP .I wwn Regex can be of the form \fI"host_wwnn:host_wwpn:target_wwnn:target_wwpn"\fR these values can be looked up through sysfs or by running \fImultipathd show paths format "%N:%R:%n:%r"\fR. For example: 0x200100e08ba0aea0:0x210100e08ba0aea0:.*:.* , .*:.*:iqn.2009-10.com.redhat.msp.lab.ask-06:.* .RE .TP 12 .I path_latency Needs a value of the form "io_num=\fI<20>\fR base_num=\fI<10>\fR" .RS .TP 8 .I io_num The number of read IOs sent to the current path continuously, used to calculate the average path latency. Valid Values: Integer, [2, 200]. .TP .I base_num The base number value of logarithmic scale, used to partition different priority ranks. Valid Values: Integer, [2, 10]. And Max average latency value is 100s, min average latency value is 1us. For example: If base_num=10, the paths will be grouped in priority groups with path latency <=1us, (1us, 10us], (10us, 100us], (100us, 1ms], (1ms, 10ms], (10ms, 100ms], (100ms, 1s], (1s, 10s], (10s, 100s], >100s. .RE .TP 12 .I alua If \fIexclusive_pref_bit\fR is set, paths with the \fIpreferred path\fR bit set will always be in their own path group. .TP .I sysfs If \fIexclusive_pref_bit\fR is set, paths with the \fIpreferred path\fR bit set will always be in their own path group. .TP .I datacore .RS .TP 8 .I preferredsds (Mandatory) The preferred "SDS name". .TP .I timeout (Optional) The timeout for the INQUIRY, in ms. .RE .TP 12 .I iet .RS .TP 8 .I preferredip=... (Mandatory) Th preferred IP address, in dotted decimal notation, for iSCSI targets. .RE .TP The default is: \fB\fR .RE . . .TP .B features Specify any device-mapper features to be used. Syntax is \fInum list\fR where \fInum\fR is the number, between 0 and 8, of features in \fIlist\fR. Possible values for the feature list are: .RS .TP 12 .I queue_if_no_path (Deprecated, superseded by \fIno_path_retry\fR) Queue I/O if no path is active. Identical to the \fIno_path_retry\fR with \fIqueue\fR value. If both this feature and \fIno_path_retry\fR are set, the latter value takes precedence. See KNOWN ISSUES. .TP .I pg_init_retries (Since kernel 2.6.24) Number of times to retry pg_init, it must be between 1 and 50. .TP .I pg_init_delay_msecs (Since kernel 2.6.38) Number of msecs before pg_init retry, it must be between 0 and 60000. .TP .I queue_mode (Since kernel 4.8) Select the the queueing mode per multipath device. can be \fIbio\fR, \fIrq\fR or \fImq\fR, which corresponds to bio-based, request-based, and block-multiqueue (blk-mq) request-based, respectively. The default depends on the kernel parameter \fBdm_mod.use_blk_mq\fR. It is \fImq\fR if the latter is set, and \fIrq\fR otherwise. .TP The default is: \fB\fR .RE . . .TP .B path_checker The default method used to determine the paths state. Possible values are: .RS .TP 12 .I readsector0 (Deprecated) Read the first sector of the device. This checker is being deprecated, please use \fItur\fR instead. .TP .I tur Issue a \fITEST UNIT READY\fR command to the device. .TP .I emc_clariion (Hardware-dependent) Query the DGC/EMC specific EVPD page 0xC0 to determine the path state for CLARiiON CX/AX and EMC VNX and Unity arrays families. .TP .I hp_sw (Hardware-dependent) Check the path state for HP/COMPAQ/DEC HSG80 and MSA/HSV arrays with Active/Standby mode exclusively. .TP .I rdac (Hardware-dependent) Check the path state for LSI/Engenio/NetApp RDAC class as NetApp SANtricity E/EF Series, and OEM arrays from IBM DELL SGI STK and SUN. .TP .I directio (Deprecated) Read the first sector with direct I/O. This checker is being deprecated, it could cause spurious path failures under high load. Please use \fItur\fR instead. .TP .I cciss_tur (Hardware-dependent) Check the path state for HP/COMPAQ Smart Array(CCISS) controllers. .TP .I none Do not check the device, fallback to use the values retrieved from sysfs .TP .I rbd Check if the path is in the Ceph blacklist and remap the path if it is. .TP The default is: \fBtur\fR .RE . . .TP .B alias_prefix The \fIuser_friendly_names\fR prefix. .RS .TP The default is: \fBmpath\fR .RE . . .TP .B failback Tell multipathd how to manage path group failback. To select \fIimmediate\fR or a \fIvalue\fR, it's mandatory that the device has support for a working prioritizer. .RS .TP 12 .I immediate Immediately failback to the highest priority pathgroup that contains active paths. .TP .I manual Do not perform automatic failback. .TP .I followover Used to deal with multiple computers accessing the same Active/Passive storage devices. Only perform automatic failback when the first path of a pathgroup becomes active. This keeps a cluster node from automatically failing back when another node requested the failover. .TP .I values > 0 Deferred failback (time to defer in seconds). .TP The default is: \fBmanual\fR .RE . . .TP .B rr_min_io Number of I/O requests to route to a path before switching to the next in the same path group. This is only for \fIBlock I/O\fR(BIO) based multipath and only apply to \fIround-robin\fR path_selector. .RS .TP The default is: \fB1000\fR .RE . . .TP .B rr_min_io_rq Number of I/O requests to route to a path before switching to the next in the same path group. This is only for \fIRequest\fR based multipath and only apply to \fIround-robin\fR path_selector. .RS .TP The default is: \fB1\fR .RE . . .TP .B max_fds Specify the maximum number of file descriptors that can be opened by multipath and multipathd. This is equivalent to ulimit \-n. A value of \fImax\fR will set this to the system limit from \fI/proc/sys/fs/nr_open\fR. If this is not set, the maximum number of open fds is taken from the calling process. It is usually 1024. To be safe, this should be set to the maximum number of paths plus 32, if that number is greated than 1024. .RS .TP The default is: \fBmax\fR .RE . . .TP .B rr_weight If set to \fIpriorities\fR the multipath configurator will assign path weights as "path prio * rr_min_io". Possible values are .I priorities or .I uniform . Only apply to \fIround-robin\fR path_selector. .RS .TP The default is: \fBuniform\fR .RE . . .TP .B no_path_retry Specify what to do when all paths are down. Possible values are: .RS .TP 12 .I value > 0 Number of retries until disable I/O queueing. .TP .I fail For immediate failure (no I/O queueing). .TP .I queue For never stop I/O queueing, similar to \fIqueue_if_no_path\fR. See KNOWN ISSUES. .TP The default is: \fBfail\fR .RE . . .TP .B queue_without_daemon If set to .I no , when multipathd stops, queueing will be turned off for all devices. This is useful for devices that set no_path_retry. If a machine is shut down while all paths to a device are down, it is possible to hang waiting for I/O to return from the device after multipathd has been stopped. Without multipathd running, access to the paths cannot be restored, and the kernel cannot be told to stop queueing I/O. Setting queue_without_daemon to .I no , avoids this problem. .RS .TP The default is: \fBno\fR .RE . . .TP .B checker_timeout Specify the timeout to use for path checkers and prioritizers that issue SCSI commands with an explicit timeout, in seconds. .RS .TP The default is: in \fB/sys/block/sd/device/timeout\fR .RE . . .TP .B flush_on_last_del If set to .I yes , multipathd will disable queueing when the last path to a device has been deleted. .RS .TP The default is: \fBno\fR .RE . . .TP .B user_friendly_names If set to .I yes , using the bindings file \fI/etc/multipath/bindings\fR to assign a persistent and unique alias to the multipath, in the form of mpath. If set to .I no use the WWID as the alias. In either case this be will be overridden by any specific aliases in the \fImultipaths\fR section. .RS .TP The default is: \fBno\fR .RE . . .TP .B fast_io_fail_tmo Specify the number of seconds the SCSI layer will wait after a problem has been detected on a FC remote port before failing I/O to devices on that remote port. This should be smaller than dev_loss_tmo. Setting this to .I off will disable the timeout. .RS .TP The default is: in \fB5\fR .RE . . .TP .B dev_loss_tmo Specify the number of seconds the SCSI layer will wait after a problem has been detected on a FC remote port before removing it from the system. This can be set to "infinity" which sets it to the max value of 2147483647 seconds, or 68 years. It will be automatically adjusted to the overall retry interval \fIno_path_retry\fR * \fIpolling_interval\fR if a number of retries is given with \fIno_path_retry\fR and the overall retry interval is longer than the specified \fIdev_loss_tmo\fR value. The Linux kernel will cap this value to \fI600\fR if \fIfast_io_fail_tmo\fR is not set. See KNOWN ISSUES. .RS .TP The default is: \fB600\fR .RE . . .TP .B bindings_file The full pathname of the binding file to be used when the user_friendly_names option is set. .RS .TP The default is: \fB/etc/multipath/bindings\fR .RE . . .TP .B wwids_file The full pathname of the WWIDs file, which is used by multipath to keep track of the WWIDs for LUNs it has created multipath devices on in the past. .RS .TP The default is: \fB/etc/multipath/wwids\fR .RE . . .TP .B prkeys_file The full pathname of the prkeys file, which is used by multipathd to keep track of the persistent reservation key used for a specific WWID, when \fIreservation_key\fR is set to \fBfile\fR. .RS .TP The default is \fB/etc/multipath/prkeys\fR .RE . . .TP .B log_checker_err If set to .I once , multipathd logs the first path checker error at logging level 2. Any later errors are logged at level 3 until the device is restored. If set to .I always , multipathd always logs the path checker error at logging level 2. .RS .TP The default is: \fBalways\fR .RE . . .TP .B reservation_key This is the service action reservation key used by mpathpersist. It must be set for all multipath devices using persistent reservations, and it must be the same as the RESERVATION KEY field of the PERSISTENT RESERVE OUT parameter list which contains an 8-byte value provided by the application client to the device server to identify the I_T nexus. .RS .PP Alternatively, this can be set to \fBfile\fR, which will store the RESERVATION KEY registered by mpathpersist in the \fIprkeys_file\fR. multipathd will then use this key to register additional paths as they appear. When the registration is removed, the RESERVATION KEY is removed from the \fIprkeys_file\fR. .TP The default is: \fB\fR .RE . . .TP .B retain_attached_hw_handler (Obsolete for kernels >= 4.3) If set to .I yes and the SCSI layer has already attached a hardware_handler to the device, multipath will not force the device to use the hardware_handler specified by mutipath.conf. If the SCSI layer has not attached a hardware handler, multipath will continue to use its configured hardware handler. .RS .PP The default is: \fByes\fR .PP \fBImportant Note:\fR Linux kernel 4.3 or newer always behaves as if \fB"retain_attached_hw_handler yes"\fR was set. .RE . . .TP .B detect_prio If set to .I yes , multipath will try to detect if the device supports SCSI-3 ALUA. If so, the device will automatically use the \fIsysfs\fR prioritizer if the required sysf attributes \fIaccess_state\fR and \fIpreferred_path\fR are supported, or the \fIalua\fR prioritizer if not. If set to .I no , the prioritizer will be selected as usual. .RS .TP The default is: \fByes\fR .RE . . .TP .B detect_checker if set to .I yes , multipath will try to detect if the device supports SCSI-3 ALUA. If so, the device will automatically use the \fItur\fR checker. If set to .I no , the checker will be selected as usual. .RS .TP The default is: \fByes\fR .RE . . .TP .B force_sync If set to .I yes , multipathd will call the path checkers in sync mode only. This means that only one checker will run at a time. This is useful in the case where many multipathd checkers running in parallel causes significant CPU pressure. .RS .TP The default is: \fBno\fR .RE . . .TP .B strict_timing If set to .I yes , multipathd will start a new path checker loop after exactly one second, so that each path check will occur at exactly \fIpolling_interval\fR seconds. On busy systems path checks might take longer than one second; here the missing ticks will be accounted for on the next round. A warning will be printed if path checks take longer than \fIpolling_interval\fR seconds. .RS .TP The default is: \fBno\fR .RE . . .TP .B deferred_remove If set to .I yes , multipathd will do a deferred remove instead of a regular remove when the last path device has been deleted. This means that if the multipath device is still in use, it will be freed when the last user closes it. If path is added to the multipath device before the last user closes it, the deferred remove will be canceled. .RS .TP The default is: \fBno\fR .RE . . .TP .B partition_delimiter If this value is not set, when multipath renames a device, it will act just like the kpartx default does, only adding a \fI"p"\fR to names ending in a number. If this parameter is set, multipath will act like kpartx does with the \fI-p\fR option is used, and always add delimiter. .RS .TP The default is: \fB\fR .RE . . .TP .B config_dir If set to anything other than "", multipath will search this directory alphabetically for file ending in ".conf" and it will read configuration information from them, just as if it was in \fI/etc/multipath.conf\fR. config_dir must either be "" or a fully qualified directory name. .RS .TP The default is: \fB/etc/multipath/conf.d/\fR .RE . . .TP .B marginal_path_double_failed_time One of the four parameters of supporting path check based on accounting IO error such as intermittent error. When a path failed event occurs twice in \fImarginal_path_double_failed_time\fR seconds due to an IO error and all the other three parameters are set, multipathd will fail the path and enqueue this path into a queue of which members are sent a couple of continuous direct reading asynchronous IOs at a fixed sample rate of 10HZ to start IO error accounting process. .RS .TP The default is: \fBno\fR .RE . . .TP .B marginal_path_err_sample_time One of the four parameters of supporting path check based on accounting IO error such as intermittent error. If it is set to a value no less than 120, when a path fail event occurs twice in \fImarginal_path_double_failed_time\fR second due to an IO error, multipathd will fail the path and enqueue this path into a queue of which members are sent a couple of continuous direct reading asynchronous IOs at a fixed sample rate of 10HZ to start the IO accounting process for the path will last for \fImarginal_path_err_sample_time\fR. If the rate of IO error on a particular path is greater than the \fImarginal_path_err_rate_threshold\fR, then the path will not reinstate for \fImarginal_path_err_rate_threshold\fR seconds unless there is only one active path. After \fImarginal_path_err_recheck_gap_time\fR expires, the path will be requeueed for rechecking. If checking result is good enough, the path will be reinstated. .RS .TP The default is: \fBno\fR .RE . . .TP .B marginal_path_err_rate_threshold The error rate threshold as a permillage (1/1000). One of the four parameters of supporting path check based on accounting IO error such as intermittent error. Refer to \fImarginal_path_err_sample_time\fR. If the rate of IO errors on a particular path is greater than this parameter, then the path will not reinstate for \fImarginal_path_err_rate_threshold\fR seconds unless there is only one active path. .RS .TP The default is: \fBno\fR .RE . . .TP .B marginal_path_err_recheck_gap_time One of the four parameters of supporting path check based on accounting IO error such as intermittent error. Refer to \fImarginal_path_err_sample_time\fR. If this parameter is set to a positive value, the failed path of which the IO error rate is larger than \fImarginal_path_err_rate_threshold\fR will be kept in failed state for \fImarginal_path_err_recheck_gap_time\fR seconds. When \fImarginal_path_err_recheck_gap_time\fR seconds expires, the path will be requeueed for checking. If checking result is good enough, the path will be reinstated, or else it will keep failed. .RS .TP The default is: \fBno\fR .RE . . .TP .B delay_watch_checks If set to a value greater than 0, multipathd will watch paths that have recently become valid for this many checks. If they fail again while they are being watched, when they next become valid, they will not be used until they have stayed up for \fIdelay_wait_checks\fR checks. .RS .TP The default is: \fBno\fR .RE . . .TP .B delay_wait_checks If set to a value greater than 0, when a device that has recently come back online fails again within \fIdelay_watch_checks\fR checks, the next time it comes back online, it will marked and delayed, and not used until it has passed \fIdelay_wait_checks\fR checks. .RS .TP The default is: \fBno\fR .RE . . .TP .B find_multipaths If set to .I yes , instead of trying to create a multipath device for every non-blacklisted path, multipath will only create a device if one of three condidions are met. .I 1 There are at least two non-blacklisted paths with the same WWID, .I 2 the user manually forces the creation, by specifying a device with the multipath command, or .I 3 a path has the same WWID as a multipath device that was previously created while find_multipaths was set (even if that multipath device doesn't currently exist). Whenever a multipath device is created with find_multipaths set, multipath will remember the WWID of the device, so that it will automatically create the device again, as soon as it sees a path with that WWID. This should allow most users to have multipath automatically choose the correct paths to make into multipath devices, without having to edit the blacklist. .RS .TP The default is: \fBno\fR .RE . . .TP .B uxsock_timeout CLI receive timeout in milliseconds. For larger systems CLI commands might timeout before the multipathd lock is released and the CLI command can be processed. This will result in errors like "timeout receiving packet" to be returned from CLI commands. In these cases it is recommended to increase the CLI timeout to avoid those issues. .RS .TP The default is: \fB1000\fR .RE . . .TP .B retrigger_tries Sets the number of times multipathd will try to retrigger a uevent to get the WWID. .RS .TP The default is: \fB3\fR .RE . . .TP .B retrigger_delay Sets the amount of time, in seconds, to wait between retriggers. .RS .TP The default is: \fB10\fR .RE . . .TP .B missing_uev_wait_timeout Controls how many seconds multipathd will wait, after a new multipath device is created, to receive a change event from udev for the device, before automatically enabling device reloads. Usually multipathd will delay reloads on a device until it receives a change uevent from the initial table load. .RS .TP The default is: \fB30\fR .RE . . .TP .B skip_kpartx If set to .I yes , kpartx will not automatically create partitions on the device. .RS .TP The default is: \fBno\fR .RE . . .TP .B disable_changed_wwids If set to \fIyes\fR, multipathd will check the path wwid on change events, and if it has changed from the wwid of the multipath device, multipathd will disable access to the path until the wwid changes back. .RS .TP The default is: \fBno\fR .RE . . .TP .B remove_retries This sets how may times multipath will retry removing a device that is in-use. Between each attempt, multipath will sleep 1 second. .RS .TP The default is: \fB0\fR .RE . . .TP .B max_sectors_kb Sets the max_sectors_kb device parameter on all path devices and the multipath device to the specified value. .RS .TP The default is: \fB\fR .RE . . .\" ---------------------------------------------------------------------------- .SH "blacklist section" .\" ---------------------------------------------------------------------------- . The \fIblacklist\fR section is used to exclude specific device from inclusion in the multipath topology. It is most commonly used to exclude local disks or LUNs for the array controller. .LP . . The following keywords are recognized: .TP 17 .B devnode Regular expression of the device nodes to be excluded. .RS .TP The default is: \fB^(ram|raw|loop|fd|md|dm-|sr|scd|st|dcssblk)[0-9]\fR and \fB^(td|hd|vd)[a-z]\fR .RE .TP .B wwid The \fIWorld Wide Identification\fR of a device. .TP .B property Regular expression of the udev property to be excluded. .TP .B device Subsection for the device description. This subsection recognizes the .B vendor and .B product keywords. For a full description of these keywords please see the \fIdevices\fR section description. . . .\" ---------------------------------------------------------------------------- .SH "blacklist_exceptions section" .\" ---------------------------------------------------------------------------- . The \fIblacklist_exceptions\fR section is used to revert the actions of the \fIblacklist\fR section. For example to include specific device in the multipath topology. This allows one to selectively include devices which would normally be excluded via the \fIblacklist\fR section. .LP . . The following keywords are recognized: .TP 17 .B devnode Regular expression of the device nodes to be whitelisted. .TP .B wwid The \fIWorld Wide Identification\fR of a device. .TP .B property Regular expression of the udev property to be whitelisted. .RS .TP The default is: \fB(SCSI_IDENT_|ID_WWN)\fR .RE .TP .B device Subsection for the device description. This subsection recognizes the .B vendor and .B product keywords. For a full description of these keywords please see the \fIdevices\fR section description. .LP The \fIproperty\fR blacklist and whitelist handling is different from the usual handling in the sense that the whitelist \fIhas\fR to be set, otherwise the device will be blacklisted. In these cases the message \fIblacklisted, udev property missing\fR will be displayed. . . .\" ---------------------------------------------------------------------------- .SH "multipaths section" .\" ---------------------------------------------------------------------------- . The only recognized attribute for the \fImultipaths\fR section is the \fImultipath\fR subsection. .LP . . The \fImultipath\fR subsection recognizes the following attributes: .TP 17 .B wwid (Mandatory) Index of the container. .TP .B alias Symbolic name for the multipath map. .LP . . The following attributes are optional; if not set the default values are taken from the \fIdefaults\fR or \fIdevices\fR section: .sp 1 .PD .1v .RS .TP 18 .B path_grouping_policy .TP .B path_selector .TP .B prio .TP .B prio_args .TP .B failback .TP .B rr_weight .TP .B no_path_retry .TP .B rr_min_io .TP .B rr_min_io_rq .TP .B flush_on_last_del .TP .B features .TP .B reservation_key .TP .B user_friendly_names .TP .B deferred_remove .TP .B marginal_path_err_sample_time .TP .B marginal_path_err_rate_threshold .TP .B marginal_path_err_recheck_gap_time .TP .B marginal_path_double_failed_time .TP .B delay_watch_checks .TP .B delay_wait_checks .TP .B skip_kpartx .TP .B max_sectors_kb .RE .PD .LP . . .\" ---------------------------------------------------------------------------- .SH "devices section" .\" ---------------------------------------------------------------------------- . The only recognized attribute for the \fIdevices\fR section is the \fIdevice\fR subsection. .LP . . The \fIdevice\fR subsection recognizes the following attributes: .TP vendor, product, revision and product_blacklist are POSIX Extended regex. .TP 17 .B vendor (Mandatory) Vendor identifier. .TP .B product (Mandatory) Product identifier. .TP .B revision Revision identfier. .TP .B product_blacklist Product strings to blacklist for this vendor. .TP .B alias_prefix The user_friendly_names prefix to use for this device type, instead of the default "mpath". .TP .B hardware_handler The hardware handler to use for this device type. The following hardware handler are implemented: .RS .TP 12 .I 1 emc (Hardware-dependent) Hardware handler for DGC class arrays as CLARiiON CX/AX and EMC VNX and Unity families. .TP .I 1 rdac (Hardware-dependent) Hardware handler for LSI/Engenio/NetApp RDAC class as NetApp SANtricity E/EF Series, and OEM arrays from IBM DELL SGI STK and SUN. .TP .I 1 hp_sw (Hardware-dependent) Hardware handler for HP/COMPAQ/DEC HSG80 and MSA/HSV arrays with Active/Standby mode exclusively. .TP .I 1 alua (Hardware-dependent) Hardware handler for SCSI-3 ALUA compatible arrays. .PP The default is: \fB\fR .PP \fBImportant Note:\fR Linux kernels 4.3 and newer automatically attach a device handler to known devices (which includes all devices supporting SCSI-3 ALUA) and disallow changing the handler afterwards. Setting \fBhardware_handler\fR for such devices on these kernels has no effect. .RE . . .LP The following attributes are optional; if not set the default values are taken from the \fIdefaults\fR section: .sp 1 .PD .1v .RS .TP 18 .B path_grouping_policy .TP .B uid_attribute .TP .B path_selector .TP .B path_checker .TP .B prio .TP .B prio_args .TP .B features .TP .B failback .TP .B rr_weight .TP .B no_path_retry .TP .B rr_min_io .TP .B rr_min_io_rq .TP .B fast_io_fail_tmo .TP .B dev_loss_tmo .TP .B flush_on_last_del .TP .B retain_attached_hw_handler .TP .B detect_prio .TP .B detect_checker .TP .B deferred_remove .TP .B marginal_path_err_sample_time .TP .B marginal_path_err_rate_threshold .TP .B marginal_path_err_recheck_gap_time .TP .B marginal_path_double_failed_time .TP .B delay_watch_checks .TP .B delay_wait_checks .TP .B skip_kpartx .TP .B max_sectors_kb .RE .PD .LP . . .\" ---------------------------------------------------------------------------- .SH "overrides section" .\" ---------------------------------------------------------------------------- . The overrides section recognizes the following optional attributes; if not set the values are taken from the \fIdevices\fR or \fIdefaults\fR sections: .sp 1 .PD .1v .RS .TP 18 .B path_grouping_policy .TP .B uid_attribute .TP .B getuid_callout .TP .B path_selector .TP .B path_checker .TP .B alias_prefix .TP .B features .TP .B prio .TP .B prio_args .TP .B failback .TP .B rr_weight .TP .B no_path_retry .TP .B rr_min_io .TP .B rr_min_io_rq .TP .B flush_on_last_del .TP .B fast_io_fail_tmo .TP .B dev_loss_tmo .TP .B user_friendly_names .TP .B retain_attached_hw_handler .TP .B detect_prio .TP .B detect_checker .TP .B deferred_remove .TP .B marginal_path_err_sample_time .TP .B marginal_path_err_rate_threshold .TP .B marginal_path_err_recheck_gap_time .TP .B marginal_path_double_failed_time .TP .B delay_watch_checks .TP .B delay_wait_checks .TP .B skip_kpartx .RE .PD .LP . . .\" ---------------------------------------------------------------------------- .SH "WWID generation" .\" ---------------------------------------------------------------------------- . Multipath uses a \fIWorld Wide Identification\fR (WWID) to determine which paths belong to the same device. Each path presenting the same WWID is assumed to point to the same device. .LP The WWID is generated by three methods (in the order of preference): .TP 17 .B getuid_callout Use the specified external program; cf \fIgetuid_callout\fR above. Care should be taken when using this method; the external program needs to be loaded from disk for execution, which might lead to deadlock situations in an all-paths-down scenario. .TP .B uid_attribute Use the value of the specified udev attribute; cf \fIuid_attribute\fR above. This method is preferred to \fIgetuid_callout\fR as multipath does not need to call any external programs here. However, under certain circumstances udev might not be able to generate the requested variable. .TP .B vpd_pg83 If none of the \fIgetuid_callout\fR or \fIuid_attribute\fR parameters are present multipath will try to use the sysfs attribute \fIvpd_pg83\fR to generate the WWID. . . .\" ---------------------------------------------------------------------------- .SH "KNOWN ISSUES" .\" ---------------------------------------------------------------------------- . The usage of \fIqueue_if_no_path\fR option can lead to \fID state\fR processes being hung and not killable in situations where all the paths to the LUN go offline. It is advisable to use the \fIno_path_retry\fR option instead. .P The use of \fIqueue_if_no_path\fR or \fIno_path_retry\fR might lead to a deadlock if the \fIdev_loss_tmo\fR setting results in a device being removed while I/O is still queued. The multipath daemon will update the \fIdev_loss_tmo\fR setting accordingly to avoid this deadlock. Hence if both values are specified the order of precedence is \fIno_path_retry, queue_if_no_path, dev_loss_tmo\fR. . . .\" ---------------------------------------------------------------------------- .SH "SEE ALSO" .\" ---------------------------------------------------------------------------- . .BR udev (8), .BR dmsetup (8), .BR multipath (8), .BR multipathd (8). . . .\" ---------------------------------------------------------------------------- .SH AUTHORS .\" ---------------------------------------------------------------------------- . \fImultipath-tools\fR was developed by Christophe Varoqui, and others. .\" EOF multipath-tools-0.7.4/multipath/multipath.rules000066400000000000000000000015061320314174000217560ustar00rootroot00000000000000# Set DM_MULTIPATH_DEVICE_PATH if the device should be handled by multipath SUBSYSTEM!="block", GOTO="end_mpath" ACTION!="add|change", GOTO="end_mpath" KERNEL!="sd*|dasd*", GOTO="end_mpath" IMPORT{cmdline}="nompath" ENV{nompath}=="?*", GOTO="end_mpath" IMPORT{cmdline}="multipath" ENV{multipath}=="off", GOTO="end_mpath" ENV{DEVTYPE}!="partition", GOTO="test_dev" IMPORT{parent}="DM_MULTIPATH_DEVICE_PATH" ENV{DM_MULTIPATH_DEVICE_PATH}=="1", ENV{ID_FS_TYPE}="none", \ ENV{SYSTEMD_READY}="0" GOTO="end_mpath" LABEL="test_dev" ENV{MPATH_SBIN_PATH}="/sbin" TEST!="$env{MPATH_SBIN_PATH}/multipath", ENV{MPATH_SBIN_PATH}="/usr/sbin" ENV{DM_MULTIPATH_DEVICE_PATH}!="1", \ PROGRAM=="$env{MPATH_SBIN_PATH}/multipath -u %k", \ ENV{DM_MULTIPATH_DEVICE_PATH}="1", ENV{ID_FS_TYPE}="mpath_member", \ ENV{SYSTEMD_READY}="0" LABEL="end_mpath" multipath-tools-0.7.4/multipathd/000077500000000000000000000000001320314174000170355ustar00rootroot00000000000000multipath-tools-0.7.4/multipathd/Makefile000066400000000000000000000026001320314174000204730ustar00rootroot00000000000000include ../Makefile.inc # # debugging stuff # #CFLAGS += -DLCKDBG #CFLAGS += -D_DEBUG_ #CFLAGS += -DLOGDBG CFLAGS += $(BIN_CFLAGS) -I$(multipathdir) -I$(mpathpersistdir) \ -I$(mpathcmddir) -I$(thirdpartydir) LDFLAGS += $(BIN_LDFLAGS) LIBDEPS += -L$(multipathdir) -lmultipath -L$(mpathpersistdir) -lmpathpersist \ -L$(mpathcmddir) -lmpathcmd -ludev -ldl -lurcu -lpthread \ -ldevmapper -lreadline ifdef SYSTEMD CFLAGS += -DUSE_SYSTEMD=$(SYSTEMD) ifeq ($(shell test $(SYSTEMD) -gt 209 && echo 1), 1) LIBDEPS += -lsystemd else LIBDEPS += -lsystemd-daemon endif endif OBJS = main.o pidfile.o uxlsnr.o uxclnt.o cli.o cli_handlers.o EXEC = multipathd all : $(EXEC) $(EXEC): $(OBJS) $(CC) $(CFLAGS) $(OBJS) $(LDFLAGS) -o $(EXEC) $(LIBDEPS) $(GZIP) $(EXEC).8 > $(EXEC).8.gz install: $(INSTALL_PROGRAM) -d $(DESTDIR)$(bindir) $(INSTALL_PROGRAM) -m 755 $(EXEC) $(DESTDIR)$(bindir) ifdef SYSTEMD $(INSTALL_PROGRAM) -d $(DESTDIR)$(unitdir) $(INSTALL_PROGRAM) -m 644 $(EXEC).service $(DESTDIR)$(unitdir) $(INSTALL_PROGRAM) -m 644 $(EXEC).socket $(DESTDIR)$(unitdir) endif $(INSTALL_PROGRAM) -d $(DESTDIR)$(man8dir) $(INSTALL_PROGRAM) -m 644 $(EXEC).8.gz $(DESTDIR)$(man8dir) uninstall: $(RM) $(DESTDIR)$(bindir)/$(EXEC) $(RM) $(DESTDIR)$(man8dir)/$(EXEC).8.gz $(RM) $(DESTDIR)$(unitdir)/$(EXEC).service $(RM) $(DESTDIR)$(unitdir)/$(EXEC).socket clean: $(RM) core *.o $(EXEC) *.gz multipath-tools-0.7.4/multipathd/cli.c000066400000000000000000000327321320314174000177570ustar00rootroot00000000000000/* * Copyright (c) 2005 Christophe Varoqui */ #include #include #include #include "memory.h" #include "vector.h" #include "structs.h" #include "structs_vec.h" #include "parser.h" #include "util.h" #include "version.h" #include #include "cli.h" static vector keys; static vector handlers; static struct key * alloc_key (void) { return (struct key *)MALLOC(sizeof(struct key)); } static struct handler * alloc_handler (void) { return (struct handler *)MALLOC(sizeof(struct handler)); } static int add_key (vector vec, char * str, uint64_t code, int has_param) { struct key * kw; kw = alloc_key(); if (!kw) return 1; kw->code = code; kw->has_param = has_param; kw->str = STRDUP(str); if (!kw->str) goto out; if (!vector_alloc_slot(vec)) goto out1; vector_set_slot(vec, kw); return 0; out1: FREE(kw->str); out: FREE(kw); return 1; } int add_handler (uint64_t fp, int (*fn)(void *, char **, int *, void *)) { struct handler * h; h = alloc_handler(); if (!h) return 1; if (!vector_alloc_slot(handlers)) { FREE(h); return 1; } vector_set_slot(handlers, h); h->fingerprint = fp; h->fn = fn; return 0; } static struct handler * find_handler (uint64_t fp) { int i; struct handler *h; vector_foreach_slot (handlers, h, i) if (h->fingerprint == fp) return h; return NULL; } int set_handler_callback (uint64_t fp, int (*fn)(void *, char **, int *, void *)) { struct handler * h = find_handler(fp); if (!h) return 1; h->fn = fn; h->locked = 1; return 0; } int set_unlocked_handler_callback (uint64_t fp,int (*fn)(void *, char **, int *, void *)) { struct handler * h = find_handler(fp); if (!h) return 1; h->fn = fn; h->locked = 0; return 0; } static void free_key (struct key * kw) { if (kw->str) FREE(kw->str); if (kw->param) FREE(kw->param); FREE(kw); } void free_keys (vector vec) { int i; struct key * kw; vector_foreach_slot (vec, kw, i) free_key(kw); vector_free(vec); } void free_handlers (void) { int i; struct handler * h; vector_foreach_slot (handlers, h, i) FREE(h); vector_free(handlers); handlers = NULL; } int load_keys (void) { int r = 0; keys = vector_alloc(); if (!keys) return 1; r += add_key(keys, "list", LIST, 0); r += add_key(keys, "show", LIST, 0); r += add_key(keys, "add", ADD, 0); r += add_key(keys, "remove", DEL, 0); r += add_key(keys, "del", DEL, 0); r += add_key(keys, "switch", SWITCH, 0); r += add_key(keys, "switchgroup", SWITCH, 0); r += add_key(keys, "suspend", SUSPEND, 0); r += add_key(keys, "resume", RESUME, 0); r += add_key(keys, "reinstate", REINSTATE, 0); r += add_key(keys, "fail", FAIL, 0); r += add_key(keys, "resize", RESIZE, 0); r += add_key(keys, "reset", RESET, 0); r += add_key(keys, "reload", RELOAD, 0); r += add_key(keys, "forcequeueing", FORCEQ, 0); r += add_key(keys, "disablequeueing", DISABLEQ, 0); r += add_key(keys, "restorequeueing", RESTOREQ, 0); r += add_key(keys, "paths", PATHS, 0); r += add_key(keys, "maps", MAPS, 0); r += add_key(keys, "multipaths", MAPS, 0); r += add_key(keys, "path", PATH, 1); r += add_key(keys, "map", MAP, 1); r += add_key(keys, "multipath", MAP, 1); r += add_key(keys, "group", GROUP, 1); r += add_key(keys, "reconfigure", RECONFIGURE, 0); r += add_key(keys, "daemon", DAEMON, 0); r += add_key(keys, "status", STATUS, 0); r += add_key(keys, "stats", STATS, 0); r += add_key(keys, "topology", TOPOLOGY, 0); r += add_key(keys, "config", CONFIG, 0); r += add_key(keys, "blacklist", BLACKLIST, 0); r += add_key(keys, "devices", DEVICES, 0); r += add_key(keys, "raw", RAW, 0); r += add_key(keys, "wildcards", WILDCARDS, 0); r += add_key(keys, "quit", QUIT, 0); r += add_key(keys, "exit", QUIT, 0); r += add_key(keys, "shutdown", SHUTDOWN, 0); r += add_key(keys, "getprstatus", GETPRSTATUS, 0); r += add_key(keys, "setprstatus", SETPRSTATUS, 0); r += add_key(keys, "unsetprstatus", UNSETPRSTATUS, 0); r += add_key(keys, "format", FMT, 1); r += add_key(keys, "json", JSON, 0); r += add_key(keys, "getprkey", GETPRKEY, 0); r += add_key(keys, "setprkey", SETPRKEY, 0); r += add_key(keys, "unsetprkey", UNSETPRKEY, 0); r += add_key(keys, "key", KEY, 1); if (r) { free_keys(keys); keys = NULL; return 1; } return 0; } static struct key * find_key (const char * str) { int i; int len, klen; struct key * kw = NULL; struct key * foundkw = NULL; len = strlen(str); vector_foreach_slot (keys, kw, i) { if (strncmp(kw->str, str, len)) continue; klen = strlen(kw->str); if (len == klen) return kw; /* exact match */ if (len < klen) { if (!foundkw) foundkw = kw; /* shortcut match */ else return NULL; /* ambiguous word */ } } return foundkw; } /* * get_cmdvec * * returns: * ENOMEM: not enough memory to allocate command * EAGAIN: command not found * EINVAL: argument missing for command */ static int get_cmdvec (char * cmd, vector *v) { int i; int r = 0; int get_param = 0; char * buff; struct key * kw = NULL; struct key * cmdkw = NULL; vector cmdvec, strvec; strvec = alloc_strvec(cmd); if (!strvec) return ENOMEM; cmdvec = vector_alloc(); if (!cmdvec) { free_strvec(strvec); return ENOMEM; } vector_foreach_slot(strvec, buff, i) { if (*buff == '"') continue; if (get_param) { get_param = 0; cmdkw->param = strdup(buff); continue; } kw = find_key(buff); if (!kw) { r = EAGAIN; goto out; } cmdkw = alloc_key(); if (!cmdkw) { r = ENOMEM; goto out; } if (!vector_alloc_slot(cmdvec)) { FREE(cmdkw); r = ENOMEM; goto out; } vector_set_slot(cmdvec, cmdkw); cmdkw->code = kw->code; cmdkw->has_param = kw->has_param; if (kw->has_param) get_param = 1; } if (get_param) { r = EINVAL; goto out; } *v = cmdvec; free_strvec(strvec); return 0; out: free_strvec(strvec); free_keys(cmdvec); return r; } static uint64_t fingerprint(vector vec) { int i; uint64_t fp = 0; struct key * kw; if (!vec) return 0; vector_foreach_slot(vec, kw, i) fp += kw->code; return fp; } int alloc_handlers (void) { handlers = vector_alloc(); if (!handlers) return 1; return 0; } static int genhelp_sprint_aliases (char * reply, int maxlen, vector keys, struct key * refkw) { int i, len = 0; struct key * kw; vector_foreach_slot (keys, kw, i) { if (kw->code == refkw->code && kw != refkw) { len += snprintf(reply + len, maxlen - len, "|%s", kw->str); if (len >= maxlen) return len; } } return len; } static int do_genhelp(char *reply, int maxlen, const char *cmd, int error) { int len = 0; int i, j; uint64_t fp; struct handler * h; struct key * kw; switch(error) { case ENOMEM: len += snprintf(reply + len, maxlen - len, "%s: Not enough memory\n", cmd); break; case EAGAIN: len += snprintf(reply + len, maxlen - len, "%s: not found\n", cmd); break; case EINVAL: len += snprintf(reply + len, maxlen - len, "%s: Missing argument\n", cmd); break; } if (len >= maxlen) goto out; len += snprintf(reply + len, maxlen - len, VERSION_STRING); if (len >= maxlen) goto out; len += snprintf(reply + len, maxlen - len, "CLI commands reference:\n"); if (len >= maxlen) goto out; vector_foreach_slot (handlers, h, i) { fp = h->fingerprint; vector_foreach_slot (keys, kw, j) { if ((kw->code & fp)) { fp -= kw->code; len += snprintf(reply + len , maxlen - len, " %s", kw->str); if (len >= maxlen) goto out; len += genhelp_sprint_aliases(reply + len, maxlen - len, keys, kw); if (len >= maxlen) goto out; if (kw->has_param) { len += snprintf(reply + len, maxlen - len, " $%s", kw->str); if (len >= maxlen) goto out; } } } len += snprintf(reply + len, maxlen - len, "\n"); if (len >= maxlen) goto out; } out: return len; } static char * genhelp_handler (const char *cmd, int error) { char * reply; char * p = NULL; int maxlen = INITIAL_REPLY_LEN; int again = 1; reply = MALLOC(maxlen); while (again) { if (!reply) return NULL; p = reply; p += do_genhelp(reply, maxlen, cmd, error); again = ((p - reply) >= maxlen); REALLOC_REPLY(reply, again, maxlen); } return reply; } int parse_cmd (char * cmd, char ** reply, int * len, void * data, int timeout ) { int r; struct handler * h; vector cmdvec = NULL; struct timespec tmo; r = get_cmdvec(cmd, &cmdvec); if (r) { *reply = genhelp_handler(cmd, r); *len = strlen(*reply) + 1; return 0; } h = find_handler(fingerprint(cmdvec)); if (!h || !h->fn) { *reply = genhelp_handler(cmd, EINVAL); *len = strlen(*reply) + 1; free_keys(cmdvec); return 0; } /* * execute handler */ if (clock_gettime(CLOCK_MONOTONIC, &tmo) == 0) { tmo.tv_sec += timeout; } else { tmo.tv_sec = 0; } if (h->locked) { int locked = 0; struct vectors * vecs = (struct vectors *)data; pthread_cleanup_push(cleanup_lock, &vecs->lock); if (tmo.tv_sec) { r = timedlock(&vecs->lock, &tmo); } else { lock(&vecs->lock); r = 0; } if (r == 0) { locked = 1; pthread_testcancel(); r = h->fn(cmdvec, reply, len, data); } pthread_cleanup_pop(locked); } else r = h->fn(cmdvec, reply, len, data); free_keys(cmdvec); return r; } char * get_keyparam (vector v, uint64_t code) { struct key * kw; int i; vector_foreach_slot(v, kw, i) if (kw->code == code) return kw->param; return NULL; } int cli_init (void) { if (load_keys()) return 1; if (alloc_handlers()) return 1; add_handler(LIST+PATHS, NULL); add_handler(LIST+PATHS+FMT, NULL); add_handler(LIST+PATHS+RAW+FMT, NULL); add_handler(LIST+PATH, NULL); add_handler(LIST+STATUS, NULL); add_handler(LIST+DAEMON, NULL); add_handler(LIST+MAPS, NULL); add_handler(LIST+MAPS+STATUS, NULL); add_handler(LIST+MAPS+STATS, NULL); add_handler(LIST+MAPS+FMT, NULL); add_handler(LIST+MAPS+RAW+FMT, NULL); add_handler(LIST+MAPS+TOPOLOGY, NULL); add_handler(LIST+MAPS+JSON, NULL); add_handler(LIST+TOPOLOGY, NULL); add_handler(LIST+MAP+TOPOLOGY, NULL); add_handler(LIST+MAP+JSON, NULL); add_handler(LIST+MAP+FMT, NULL); add_handler(LIST+MAP+RAW+FMT, NULL); add_handler(LIST+CONFIG, NULL); add_handler(LIST+BLACKLIST, NULL); add_handler(LIST+DEVICES, NULL); add_handler(LIST+WILDCARDS, NULL); add_handler(RESET+MAPS+STATS, NULL); add_handler(RESET+MAP+STATS, NULL); add_handler(ADD+PATH, NULL); add_handler(DEL+PATH, NULL); add_handler(ADD+MAP, NULL); add_handler(DEL+MAP, NULL); add_handler(SWITCH+MAP+GROUP, NULL); add_handler(RECONFIGURE, NULL); add_handler(SUSPEND+MAP, NULL); add_handler(RESUME+MAP, NULL); add_handler(RESIZE+MAP, NULL); add_handler(RESET+MAP, NULL); add_handler(RELOAD+MAP, NULL); add_handler(DISABLEQ+MAP, NULL); add_handler(RESTOREQ+MAP, NULL); add_handler(DISABLEQ+MAPS, NULL); add_handler(RESTOREQ+MAPS, NULL); add_handler(REINSTATE+PATH, NULL); add_handler(FAIL+PATH, NULL); add_handler(QUIT, NULL); add_handler(SHUTDOWN, NULL); add_handler(GETPRSTATUS+MAP, NULL); add_handler(SETPRSTATUS+MAP, NULL); add_handler(UNSETPRSTATUS+MAP, NULL); add_handler(GETPRKEY+MAP, NULL); add_handler(SETPRKEY+MAP+KEY, NULL); add_handler(UNSETPRKEY+MAP, NULL); add_handler(FORCEQ+DAEMON, NULL); add_handler(RESTOREQ+DAEMON, NULL); return 0; } void cli_exit(void) { free_handlers(); free_keys(keys); keys = NULL; } static int key_match_fingerprint (struct key * kw, uint64_t fp) { if (!fp) return 0; return ((fp & kw->code) == kw->code); } /* * This is the readline completion handler */ char * key_generator (const char * str, int state) { static int index, len, has_param; static uint64_t rlfp; struct key * kw; int i; struct handler *h; vector v = NULL; if (!state) { index = 0; has_param = 0; rlfp = 0; len = strlen(str); int r = get_cmdvec(rl_line_buffer, &v); /* * If a word completion is in progess, we don't want * to take an exact keyword match in the fingerprint. * For ex "show map[tab]" would validate "map" and discard * "maps" as a valid candidate. */ if (v && len) vector_del_slot(v, VECTOR_SIZE(v) - 1); /* * Clean up the mess if we dropped the last slot of a 1-slot * vector */ if (v && !VECTOR_SIZE(v)) { vector_free(v); v = NULL; } /* * If last keyword takes a param, don't even try to guess */ if (r == EINVAL) { has_param = 1; return (strdup("(value)")); } /* * Compute a command fingerprint to find out possible completions. * Once done, the vector is useless. Free it. */ if (v) { rlfp = fingerprint(v); free_keys(v); } } /* * No more completions for parameter placeholder. * Brave souls might try to add parameter completion by walking paths and * multipaths vectors. */ if (has_param) return ((char *)NULL); /* * Loop through keywords for completion candidates */ vector_foreach_slot_after (keys, kw, index) { if (!strncmp(kw->str, str, len)) { /* * Discard keywords already in the command line */ if (key_match_fingerprint(kw, rlfp)) { struct key * curkw = find_key(str); if (!curkw || (curkw != kw)) continue; } /* * Discard keywords making syntax errors. * * nfp is the candidate fingerprint we try to * validate against all known command fingerprints. */ uint64_t nfp = rlfp | kw->code; vector_foreach_slot(handlers, h, i) { if (!rlfp || ((h->fingerprint & nfp) == nfp)) { /* * At least one full command is * possible with this keyword : * Consider it validated */ index++; return (strdup(kw->str)); } } } } /* * No more candidates */ return ((char *)NULL); } multipath-tools-0.7.4/multipathd/cli.h000066400000000000000000000055011320314174000177560ustar00rootroot00000000000000#include enum { __LIST, __ADD, __DEL, __SWITCH, __SUSPEND, __RESUME, __REINSTATE, __FAIL, __RESIZE, __RESET, __RELOAD, __FORCEQ, __DISABLEQ, __RESTOREQ, __PATHS, __MAPS, __PATH, __MAP, __GROUP, __RECONFIGURE, __DAEMON, __STATUS, __STATS, __TOPOLOGY, __CONFIG, __BLACKLIST, __DEVICES, __RAW, __WILDCARDS, __QUIT, __SHUTDOWN, __GETPRSTATUS, __SETPRSTATUS, __UNSETPRSTATUS, __FMT, __JSON, __GETPRKEY, __SETPRKEY, __UNSETPRKEY, __KEY, }; #define LIST (1 << __LIST) #define ADD (1 << __ADD) #define DEL (1 << __DEL) #define SWITCH (1 << __SWITCH) #define SUSPEND (1 << __SUSPEND) #define RESUME (1 << __RESUME) #define REINSTATE (1 << __REINSTATE) #define FAIL (1 << __FAIL) #define RESIZE (1 << __RESIZE) #define RESET (1 << __RESET) #define RELOAD (1 << __RELOAD) #define FORCEQ (1 << __FORCEQ) #define DISABLEQ (1 << __DISABLEQ) #define RESTOREQ (1 << __RESTOREQ) #define PATHS (1 << __PATHS) #define MAPS (1 << __MAPS) #define PATH (1 << __PATH) #define MAP (1 << __MAP) #define GROUP (1 << __GROUP) #define RECONFIGURE (1 << __RECONFIGURE) #define DAEMON (1 << __DAEMON) #define STATUS (1 << __STATUS) #define STATS (1 << __STATS) #define TOPOLOGY (1 << __TOPOLOGY) #define CONFIG (1 << __CONFIG) #define BLACKLIST (1 << __BLACKLIST) #define DEVICES (1 << __DEVICES) #define RAW (1 << __RAW) #define COUNT (1 << __COUNT) #define WILDCARDS (1 << __WILDCARDS) #define QUIT (1 << __QUIT) #define SHUTDOWN (1 << __SHUTDOWN) #define GETPRSTATUS (1ULL << __GETPRSTATUS) #define SETPRSTATUS (1ULL << __SETPRSTATUS) #define UNSETPRSTATUS (1ULL << __UNSETPRSTATUS) #define FMT (1ULL << __FMT) #define JSON (1ULL << __JSON) #define GETPRKEY (1ULL << __GETPRKEY) #define SETPRKEY (1ULL << __SETPRKEY) #define UNSETPRKEY (1ULL << __UNSETPRKEY) #define KEY (1ULL << __KEY) #define INITIAL_REPLY_LEN 1200 #define REALLOC_REPLY(r, a, m) \ do { \ if ((a)) { \ char *tmp = (r); \ (r) = REALLOC((r), (m) * 2); \ if ((r)) { \ memset((r) + (m), 0, (m)); \ (m) *= 2; \ } \ else \ free(tmp); \ } \ } while (0) struct key { char * str; char * param; uint64_t code; int has_param; }; struct handler { uint64_t fingerprint; int locked; int (*fn)(void *, char **, int *, void *); }; int alloc_handlers (void); int add_handler (uint64_t fp, int (*fn)(void *, char **, int *, void *)); int set_handler_callback (uint64_t fp, int (*fn)(void *, char **, int *, void *)); int set_unlocked_handler_callback (uint64_t fp, int (*fn)(void *, char **, int *, void *)); int parse_cmd (char * cmd, char ** reply, int * len, void *, int); int load_keys (void); char * get_keyparam (vector v, uint64_t code); void free_keys (vector vec); void free_handlers (void); int cli_init (void); void cli_exit(void); char * key_generator (const char * str, int state); multipath-tools-0.7.4/multipathd/cli_handlers.c000066400000000000000000000755341320314174000216460ustar00rootroot00000000000000/* * Copyright (c) 2005 Christophe Varoqui */ #define _GNU_SOURCE #include "checkers.h" #include "memory.h" #include "vector.h" #include "structs.h" #include "structs_vec.h" #include #include "devmapper.h" #include "discovery.h" #include "config.h" #include "configure.h" #include "blacklist.h" #include "debug.h" #include "print.h" #include "sysfs.h" #include #include #include "util.h" #include "prkey.h" #include "main.h" #include "cli.h" #include "uevent.h" int show_paths (char ** r, int * len, struct vectors * vecs, char * style, int pretty) { int i; struct path * pp; char * c; char * reply; unsigned int maxlen = INITIAL_REPLY_LEN; int again = 1; get_path_layout(vecs->pathvec, 1); reply = MALLOC(maxlen); while (again) { if (!reply) return 1; c = reply; if (pretty && VECTOR_SIZE(vecs->pathvec) > 0) c += snprint_path_header(c, reply + maxlen - c, style); vector_foreach_slot(vecs->pathvec, pp, i) c += snprint_path(c, reply + maxlen - c, style, pp, pretty); again = ((c - reply) == (maxlen - 1)); REALLOC_REPLY(reply, again, maxlen); } *r = reply; *len = (int)(c - reply + 1); return 0; } int show_path (char ** r, int * len, struct vectors * vecs, struct path *pp, char * style) { char * c; char * reply; unsigned int maxlen = INITIAL_REPLY_LEN; int again = 1; get_path_layout(vecs->pathvec, 1); reply = MALLOC(maxlen); while (again) { if (!reply) return 1; c = reply; c += snprint_path(c, reply + maxlen - c, style, pp, 0); again = ((c - reply) == (maxlen - 1)); REALLOC_REPLY(reply, again, maxlen); } *r = reply; *len = (int)(c - reply + 1); return 0; } int show_map_topology (char ** r, int * len, struct multipath * mpp, struct vectors * vecs) { char * c; char * reply; unsigned int maxlen = INITIAL_REPLY_LEN; int again = 1; if (update_multipath(vecs, mpp->alias, 0)) return 1; reply = MALLOC(maxlen); while (again) { if (!reply) return 1; c = reply; c += snprint_multipath_topology(c, reply + maxlen - c, mpp, 2); again = ((c - reply) == (maxlen - 1)); REALLOC_REPLY(reply, again, maxlen); } *r = reply; *len = (int)(c - reply + 1); return 0; } int show_maps_topology (char ** r, int * len, struct vectors * vecs) { int i; struct multipath * mpp; char * c; char * reply; unsigned int maxlen = INITIAL_REPLY_LEN; int again = 1; get_path_layout(vecs->pathvec, 0); reply = MALLOC(maxlen); while (again) { if (!reply) return 1; c = reply; vector_foreach_slot(vecs->mpvec, mpp, i) { if (update_multipath(vecs, mpp->alias, 0)) { i--; continue; } c += snprint_multipath_topology(c, reply + maxlen - c, mpp, 2); } again = ((c - reply) == (maxlen - 1)); REALLOC_REPLY(reply, again, maxlen); } *r = reply; *len = (int)(c - reply + 1); return 0; } int show_maps_json (char ** r, int * len, struct vectors * vecs) { int i; struct multipath * mpp; char * c; char * reply; unsigned int maxlen = INITIAL_REPLY_LEN; int again = 1; if (VECTOR_SIZE(vecs->mpvec) > 0) maxlen *= PRINT_JSON_MULTIPLIER * VECTOR_SIZE(vecs->mpvec); vector_foreach_slot(vecs->mpvec, mpp, i) { if (update_multipath(vecs, mpp->alias, 0)) { return 1; } } reply = MALLOC(maxlen); while (again) { if (!reply) return 1; c = reply; c += snprint_multipath_topology_json(c, maxlen, vecs); again = ((c - reply) == maxlen); REALLOC_REPLY(reply, again, maxlen); } *r = reply; *len = (int)(c - reply); return 0; } int show_map_json (char ** r, int * len, struct multipath * mpp, struct vectors * vecs) { char * c; char * reply; unsigned int maxlen = INITIAL_REPLY_LEN; int again = 1; if (update_multipath(vecs, mpp->alias, 0)) return 1; reply = MALLOC(maxlen); while (again) { if (!reply) return 1; c = reply; c += snprint_multipath_map_json(c, maxlen, mpp, 1); again = ((c - reply) == maxlen); REALLOC_REPLY(reply, again, maxlen); } *r = reply; *len = (int)(c - reply); return 0; } int show_config (char ** r, int * len) { char * c; char * reply; unsigned int maxlen = INITIAL_REPLY_LEN; int again = 1; struct config *conf; c = reply = MALLOC(maxlen); conf = get_multipath_config(); while (again) { if (!reply) { put_multipath_config(conf); return 1; } c = reply; c += snprint_defaults(conf, c, reply + maxlen - c); again = ((c - reply) == maxlen); REALLOC_REPLY(reply, again, maxlen); if (again) continue; c += snprint_blacklist(conf, c, reply + maxlen - c); again = ((c - reply) == maxlen); REALLOC_REPLY(reply, again, maxlen); if (again) continue; c += snprint_blacklist_except(conf, c, reply + maxlen - c); again = ((c - reply) == maxlen); REALLOC_REPLY(reply, again, maxlen); if (again) continue; c += snprint_hwtable(conf, c, reply + maxlen - c, conf->hwtable); again = ((c - reply) == maxlen); REALLOC_REPLY(reply, again, maxlen); if (again) continue; c += snprint_overrides(conf, c, reply + maxlen - c, conf->overrides); again = ((c - reply) == maxlen); REALLOC_REPLY(reply, again, maxlen); if (again) continue; if (VECTOR_SIZE(conf->mptable) > 0) { c += snprint_mptable(conf, c, reply + maxlen - c, conf->mptable); again = ((c - reply) == maxlen); REALLOC_REPLY(reply, again, maxlen); } } put_multipath_config(conf); *r = reply; *len = (int)(c - reply + 1); return 0; } void reset_stats(struct multipath * mpp) { mpp->stat_switchgroup = 0; mpp->stat_path_failures = 0; mpp->stat_map_loads = 0; mpp->stat_total_queueing_time = 0; mpp->stat_queueing_timeouts = 0; mpp->stat_map_failures = 0; } int cli_list_config (void * v, char ** reply, int * len, void * data) { condlog(3, "list config (operator)"); return show_config(reply, len); } int cli_list_paths (void * v, char ** reply, int * len, void * data) { struct vectors * vecs = (struct vectors *)data; condlog(3, "list paths (operator)"); return show_paths(reply, len, vecs, PRINT_PATH_CHECKER, 1); } int cli_list_paths_fmt (void * v, char ** reply, int * len, void * data) { struct vectors * vecs = (struct vectors *)data; char * fmt = get_keyparam(v, FMT); condlog(3, "list paths (operator)"); return show_paths(reply, len, vecs, fmt, 1); } int cli_list_paths_raw (void * v, char ** reply, int * len, void * data) { struct vectors * vecs = (struct vectors *)data; char * fmt = get_keyparam(v, FMT); condlog(3, "list paths (operator)"); return show_paths(reply, len, vecs, fmt, 0); } int cli_list_path (void * v, char ** reply, int * len, void * data) { struct vectors * vecs = (struct vectors *)data; char * param = get_keyparam(v, PATH); struct path *pp; param = convert_dev(param, 1); condlog(3, "%s: list path (operator)", param); pp = find_path_by_dev(vecs->pathvec, param); return show_path(reply, len, vecs, pp, "%o"); } int cli_list_map_topology (void * v, char ** reply, int * len, void * data) { struct multipath * mpp; struct vectors * vecs = (struct vectors *)data; char * param = get_keyparam(v, MAP); param = convert_dev(param, 0); get_path_layout(vecs->pathvec, 0); mpp = find_mp_by_str(vecs->mpvec, param); if (!mpp) return 1; condlog(3, "list multipath %s (operator)", param); return show_map_topology(reply, len, mpp, vecs); } int cli_list_maps_topology (void * v, char ** reply, int * len, void * data) { struct vectors * vecs = (struct vectors *)data; condlog(3, "list multipaths (operator)"); return show_maps_topology(reply, len, vecs); } int cli_list_map_json (void * v, char ** reply, int * len, void * data) { struct multipath * mpp; struct vectors * vecs = (struct vectors *)data; char * param = get_keyparam(v, MAP); param = convert_dev(param, 0); get_path_layout(vecs->pathvec, 0); mpp = find_mp_by_str(vecs->mpvec, param); if (!mpp) return 1; condlog(3, "list multipath json %s (operator)", param); return show_map_json(reply, len, mpp, vecs); } int cli_list_maps_json (void * v, char ** reply, int * len, void * data) { struct vectors * vecs = (struct vectors *)data; condlog(3, "list multipaths json (operator)"); return show_maps_json(reply, len, vecs); } int cli_list_wildcards (void * v, char ** reply, int * len, void * data) { char * c; *reply = MALLOC(INITIAL_REPLY_LEN); if (!*reply) return 1; c = *reply; c += snprint_wildcards(c, INITIAL_REPLY_LEN); *len = INITIAL_REPLY_LEN; return 0; } int show_status (char ** r, int *len, struct vectors * vecs) { char * c; char * reply; unsigned int maxlen = INITIAL_REPLY_LEN; reply = MALLOC(maxlen); if (!reply) return 1; c = reply; c += snprint_status(c, reply + maxlen - c, vecs); *r = reply; *len = (int)(c - reply + 1); return 0; } int show_daemon (char ** r, int *len) { char * c; char * reply; unsigned int maxlen = INITIAL_REPLY_LEN; reply = MALLOC(maxlen); if (!reply) return 1; c = reply; c += snprintf(c, INITIAL_REPLY_LEN, "pid %d %s\n", daemon_pid, daemon_status()); *r = reply; *len = (int)(c - reply + 1); return 0; } int show_map (char ** r, int *len, struct multipath * mpp, char * style, int pretty) { char * c; char * reply; unsigned int maxlen = INITIAL_REPLY_LEN; int again = 1; reply = MALLOC(maxlen); while (again) { if (!reply) return 1; c = reply; c += snprint_multipath(c, reply + maxlen - c, style, mpp, pretty); again = ((c - reply) == (maxlen - 1)); REALLOC_REPLY(reply, again, maxlen); } *r = reply; *len = (int)(c - reply + 1); return 0; } int show_maps (char ** r, int *len, struct vectors * vecs, char * style, int pretty) { int i; struct multipath * mpp; char * c; char * reply; unsigned int maxlen = INITIAL_REPLY_LEN; int again = 1; get_multipath_layout(vecs->mpvec, 1); reply = MALLOC(maxlen); while (again) { if (!reply) return 1; c = reply; if (pretty && VECTOR_SIZE(vecs->mpvec) > 0) c += snprint_multipath_header(c, reply + maxlen - c, style); vector_foreach_slot(vecs->mpvec, mpp, i) { if (update_multipath(vecs, mpp->alias, 0)) { i--; continue; } c += snprint_multipath(c, reply + maxlen - c, style, mpp, pretty); } again = ((c - reply) == (maxlen - 1)); REALLOC_REPLY(reply, again, maxlen); } *r = reply; *len = (int)(c - reply + 1); return 0; } int cli_list_maps_fmt (void * v, char ** reply, int * len, void * data) { struct vectors * vecs = (struct vectors *)data; char * fmt = get_keyparam(v, FMT); condlog(3, "list maps (operator)"); return show_maps(reply, len, vecs, fmt, 1); } int cli_list_maps_raw (void * v, char ** reply, int * len, void * data) { struct vectors * vecs = (struct vectors *)data; char * fmt = get_keyparam(v, FMT); condlog(3, "list maps (operator)"); return show_maps(reply, len, vecs, fmt, 0); } int cli_list_map_fmt (void * v, char ** reply, int * len, void * data) { struct multipath * mpp; struct vectors * vecs = (struct vectors *)data; char * param = get_keyparam(v, MAP); char * fmt = get_keyparam(v, FMT); param = convert_dev(param, 0); get_path_layout(vecs->pathvec, 0); get_multipath_layout(vecs->mpvec, 1); mpp = find_mp_by_str(vecs->mpvec, param); if (!mpp) return 1; condlog(3, "list map %s fmt %s (operator)", param, fmt); return show_map(reply, len, mpp, fmt, 1); } int cli_list_map_raw (void * v, char ** reply, int * len, void * data) { struct multipath * mpp; struct vectors * vecs = (struct vectors *)data; char * param = get_keyparam(v, MAP); char * fmt = get_keyparam(v, FMT); param = convert_dev(param, 0); get_path_layout(vecs->pathvec, 0); get_multipath_layout(vecs->mpvec, 1); mpp = find_mp_by_str(vecs->mpvec, param); if (!mpp) return 1; condlog(3, "list map %s fmt %s (operator)", param, fmt); return show_map(reply, len, mpp, fmt, 0); } int cli_list_maps (void * v, char ** reply, int * len, void * data) { struct vectors * vecs = (struct vectors *)data; condlog(3, "list maps (operator)"); return show_maps(reply, len, vecs, PRINT_MAP_NAMES, 1); } int cli_list_status (void * v, char ** reply, int * len, void * data) { struct vectors * vecs = (struct vectors *)data; condlog(3, "list status (operator)"); return show_status(reply, len, vecs); } int cli_list_maps_status (void * v, char ** reply, int * len, void * data) { struct vectors * vecs = (struct vectors *)data; condlog(3, "list maps status (operator)"); return show_maps(reply, len, vecs, PRINT_MAP_STATUS, 1); } int cli_list_maps_stats (void * v, char ** reply, int * len, void * data) { struct vectors * vecs = (struct vectors *)data; condlog(3, "list maps stats (operator)"); return show_maps(reply, len, vecs, PRINT_MAP_STATS, 1); } int cli_list_daemon (void * v, char ** reply, int * len, void * data) { condlog(3, "list daemon (operator)"); return show_daemon(reply, len); } int cli_reset_maps_stats (void * v, char ** reply, int * len, void * data) { struct vectors * vecs = (struct vectors *)data; int i; struct multipath * mpp; condlog(3, "reset multipaths stats (operator)"); vector_foreach_slot(vecs->mpvec, mpp, i) { reset_stats(mpp); } return 0; } int cli_reset_map_stats (void * v, char ** reply, int * len, void * data) { struct vectors * vecs = (struct vectors *)data; struct multipath * mpp; char * param = get_keyparam(v, MAP); param = convert_dev(param, 0); mpp = find_mp_by_str(vecs->mpvec, param); if (!mpp) return 1; condlog(3, "reset multipath %s stats (operator)", param); reset_stats(mpp); return 0; } int cli_add_path (void * v, char ** reply, int * len, void * data) { struct vectors * vecs = (struct vectors *)data; char * param = get_keyparam(v, PATH); struct path *pp; int r; struct config *conf; param = convert_dev(param, 1); condlog(2, "%s: add path (operator)", param); conf = get_multipath_config(); if (filter_devnode(conf->blist_devnode, conf->elist_devnode, param) > 0) { put_multipath_config(conf); goto blacklisted; } pp = find_path_by_dev(vecs->pathvec, param); if (pp) { condlog(2, "%s: path already in pathvec", param); if (pp->mpp) { put_multipath_config(conf); return 0; } } else { struct udev_device *udevice; udevice = udev_device_new_from_subsystem_sysname(udev, "block", param); r = store_pathinfo(vecs->pathvec, conf, udevice, DI_ALL, &pp); udev_device_unref(udevice); if (!pp) { put_multipath_config(conf); if (r == 2) goto blacklisted; condlog(0, "%s: failed to store path info", param); return 1; } pp->checkint = conf->checkint; } put_multipath_config(conf); return ev_add_path(pp, vecs, 1); blacklisted: *reply = strdup("blacklisted\n"); *len = strlen(*reply) + 1; condlog(2, "%s: path blacklisted", param); return 0; } int cli_del_path (void * v, char ** reply, int * len, void * data) { struct vectors * vecs = (struct vectors *)data; char * param = get_keyparam(v, PATH); struct path *pp; param = convert_dev(param, 1); condlog(2, "%s: remove path (operator)", param); pp = find_path_by_dev(vecs->pathvec, param); if (!pp) { condlog(0, "%s: path already removed", param); return 1; } return ev_remove_path(pp, vecs, 1); } int cli_add_map (void * v, char ** reply, int * len, void * data) { struct vectors * vecs = (struct vectors *)data; char * param = get_keyparam(v, MAP); int major, minor; char dev_path[PATH_SIZE]; char *refwwid, *alias = NULL; int rc, count = 0; struct config *conf; param = convert_dev(param, 0); condlog(2, "%s: add map (operator)", param); conf = get_multipath_config(); if (filter_wwid(conf->blist_wwid, conf->elist_wwid, param, NULL) > 0) { put_multipath_config(conf); *reply = strdup("blacklisted\n"); *len = strlen(*reply) + 1; condlog(2, "%s: map blacklisted", param); return 1; } put_multipath_config(conf); do { if (dm_get_major_minor(param, &major, &minor) < 0) condlog(2, "%s: not a device mapper table", param); else { sprintf(dev_path, "dm-%d", minor); alias = dm_mapname(major, minor); } /*if there is no mapname found, we first create the device*/ if (!alias && !count) { condlog(2, "%s: mapname not found for %d:%d", param, major, minor); rc = get_refwwid(CMD_NONE, param, DEV_DEVMAP, vecs->pathvec, &refwwid); if (refwwid) { if (coalesce_paths(vecs, NULL, refwwid, FORCE_RELOAD_NONE, CMD_NONE)) condlog(2, "%s: coalesce_paths failed", param); dm_lib_release(); FREE(refwwid); } } /*we attempt to create device only once*/ count++; } while (!alias && (count < 2)); if (!alias) { condlog(2, "%s: add map failed", param); return 1; } rc = ev_add_map(dev_path, alias, vecs); FREE(alias); return rc; } int cli_del_map (void * v, char ** reply, int * len, void * data) { struct vectors * vecs = (struct vectors *)data; char * param = get_keyparam(v, MAP); int major, minor; char *alias; int rc; param = convert_dev(param, 0); condlog(2, "%s: remove map (operator)", param); if (dm_get_major_minor(param, &major, &minor) < 0) { condlog(2, "%s: not a device mapper table", param); return 1; } alias = dm_mapname(major, minor); if (!alias) { condlog(2, "%s: mapname not found for %d:%d", param, major, minor); return 1; } rc = ev_remove_map(param, alias, minor, vecs); FREE(alias); return rc; } int cli_reload(void *v, char **reply, int *len, void *data) { struct vectors * vecs = (struct vectors *)data; char * mapname = get_keyparam(v, MAP); struct multipath *mpp; int minor; mapname = convert_dev(mapname, 0); condlog(2, "%s: reload map (operator)", mapname); if (sscanf(mapname, "dm-%d", &minor) == 1) mpp = find_mp_by_minor(vecs->mpvec, minor); else mpp = find_mp_by_alias(vecs->mpvec, mapname); if (!mpp) { condlog(0, "%s: invalid map name. cannot reload", mapname); return 1; } if (mpp->wait_for_udev) { condlog(2, "%s: device not fully created, failing reload", mpp->alias); return 1; } return reload_map(vecs, mpp, 0, 1); } int resize_map(struct multipath *mpp, unsigned long long size, struct vectors * vecs) { char params[PARAMS_SIZE] = {0}; unsigned long long orig_size = mpp->size; mpp->size = size; update_mpp_paths(mpp, vecs->pathvec); setup_map(mpp, params, PARAMS_SIZE); mpp->action = ACT_RESIZE; mpp->force_udev_reload = 1; if (domap(mpp, params, 1) <= 0) { condlog(0, "%s: failed to resize map : %s", mpp->alias, strerror(errno)); mpp->size = orig_size; return 1; } return 0; } int cli_resize(void *v, char **reply, int *len, void *data) { struct vectors * vecs = (struct vectors *)data; char * mapname = get_keyparam(v, MAP); struct multipath *mpp; int minor; unsigned long long size; struct pathgroup *pgp; struct path *pp; mapname = convert_dev(mapname, 0); condlog(2, "%s: resize map (operator)", mapname); if (sscanf(mapname, "dm-%d", &minor) == 1) mpp = find_mp_by_minor(vecs->mpvec, minor); else mpp = find_mp_by_alias(vecs->mpvec, mapname); if (!mpp) { condlog(0, "%s: invalid map name. cannot resize", mapname); return 1; } if (mpp->wait_for_udev) { condlog(2, "%s: device not fully created, failing resize", mpp->alias); return 1; } pgp = VECTOR_SLOT(mpp->pg, 0); if (!pgp){ condlog(0, "%s: couldn't get path group. cannot resize", mapname); return 1; } pp = VECTOR_SLOT(pgp->paths, 0); if (!pp){ condlog(0, "%s: couldn't get path. cannot resize", mapname); return 1; } if (!pp->udev || sysfs_get_size(pp, &size)) { condlog(0, "%s: couldn't get size for sysfs. cannot resize", mapname); return 1; } if (size == mpp->size) { condlog(0, "%s: map is still the same size (%llu)", mapname, mpp->size); return 0; } condlog(3, "%s old size is %llu, new size is %llu", mapname, mpp->size, size); if (resize_map(mpp, size, vecs) != 0) return 1; dm_lib_release(); if (setup_multipath(vecs, mpp) != 0) return 1; sync_map_state(mpp); return 0; } int cli_force_no_daemon_q(void * v, char ** reply, int * len, void * data) { struct config *conf = get_multipath_config(); condlog(2, "force queue_without_daemon (operator)"); if (conf->queue_without_daemon == QUE_NO_DAEMON_OFF) conf->queue_without_daemon = QUE_NO_DAEMON_FORCE; put_multipath_config(conf); return 0; } int cli_restore_no_daemon_q(void * v, char ** reply, int * len, void * data) { struct config *conf = get_multipath_config(); condlog(2, "restore queue_without_daemon (operator)"); if (conf->queue_without_daemon == QUE_NO_DAEMON_FORCE) conf->queue_without_daemon = QUE_NO_DAEMON_OFF; put_multipath_config(conf); return 0; } int cli_restore_queueing(void *v, char **reply, int *len, void *data) { struct vectors * vecs = (struct vectors *)data; char * mapname = get_keyparam(v, MAP); struct multipath *mpp; int minor; mapname = convert_dev(mapname, 0); condlog(2, "%s: restore map queueing (operator)", mapname); if (sscanf(mapname, "dm-%d", &minor) == 1) mpp = find_mp_by_minor(vecs->mpvec, minor); else mpp = find_mp_by_alias(vecs->mpvec, mapname); if (!mpp) { condlog(0, "%s: invalid map name, cannot restore queueing", mapname); return 1; } if (mpp->no_path_retry != NO_PATH_RETRY_UNDEF && mpp->no_path_retry != NO_PATH_RETRY_FAIL) { dm_queue_if_no_path(mpp->alias, 1); if (mpp->nr_active > 0) mpp->retry_tick = 0; else { struct config *conf = get_multipath_config(); mpp->retry_tick = mpp->no_path_retry * conf->checkint; put_multipath_config(conf); } } return 0; } int cli_restore_all_queueing(void *v, char **reply, int *len, void *data) { struct vectors * vecs = (struct vectors *)data; struct multipath *mpp; int i; condlog(2, "restore queueing (operator)"); vector_foreach_slot(vecs->mpvec, mpp, i) { if (mpp->no_path_retry != NO_PATH_RETRY_UNDEF && mpp->no_path_retry != NO_PATH_RETRY_FAIL) { dm_queue_if_no_path(mpp->alias, 1); if (mpp->nr_active > 0) mpp->retry_tick = 0; else { struct config *conf = get_multipath_config(); mpp->retry_tick = mpp->no_path_retry * conf->checkint; put_multipath_config(conf); } } } return 0; } int cli_disable_queueing(void *v, char **reply, int *len, void *data) { struct vectors * vecs = (struct vectors *)data; char * mapname = get_keyparam(v, MAP); struct multipath *mpp; int minor; mapname = convert_dev(mapname, 0); condlog(2, "%s: disable map queueing (operator)", mapname); if (sscanf(mapname, "dm-%d", &minor) == 1) mpp = find_mp_by_minor(vecs->mpvec, minor); else mpp = find_mp_by_alias(vecs->mpvec, mapname); if (!mpp) { condlog(0, "%s: invalid map name, cannot disable queueing", mapname); return 1; } if (mpp->nr_active == 0) mpp->stat_map_failures++; mpp->retry_tick = -1; dm_queue_if_no_path(mpp->alias, 0); return 0; } int cli_disable_all_queueing(void *v, char **reply, int *len, void *data) { struct vectors * vecs = (struct vectors *)data; struct multipath *mpp; int i; condlog(2, "disable queueing (operator)"); vector_foreach_slot(vecs->mpvec, mpp, i) { if (mpp->nr_active == 0) mpp->stat_map_failures++; mpp->retry_tick = -1; dm_queue_if_no_path(mpp->alias, 0); } return 0; } int cli_switch_group(void * v, char ** reply, int * len, void * data) { char * mapname = get_keyparam(v, MAP); int groupnum = atoi(get_keyparam(v, GROUP)); mapname = convert_dev(mapname, 0); condlog(2, "%s: switch to path group #%i (operator)", mapname, groupnum); return dm_switchgroup(mapname, groupnum); } int cli_reconfigure(void * v, char ** reply, int * len, void * data) { condlog(2, "reconfigure (operator)"); if (set_config_state(DAEMON_CONFIGURE) == ETIMEDOUT) { condlog(2, "timeout starting reconfiguration"); return 1; } return 0; } int cli_suspend(void * v, char ** reply, int * len, void * data) { struct vectors * vecs = (struct vectors *)data; char * param = get_keyparam(v, MAP); int r; struct multipath * mpp; param = convert_dev(param, 0); mpp = find_mp_by_alias(vecs->mpvec, param); if (!mpp) return 1; if (mpp->wait_for_udev) { condlog(2, "%s: device not fully created, failing suspend", mpp->alias); return 1; } r = dm_simplecmd_noflush(DM_DEVICE_SUSPEND, param, 0); condlog(2, "%s: suspend (operator)", param); if (!r) /* error */ return 1; dm_get_info(param, &mpp->dmi); return 0; } int cli_resume(void * v, char ** reply, int * len, void * data) { struct vectors * vecs = (struct vectors *)data; char * param = get_keyparam(v, MAP); int r; struct multipath * mpp; uint16_t udev_flags; param = convert_dev(param, 0); mpp = find_mp_by_alias(vecs->mpvec, param); if (!mpp) return 1; udev_flags = (mpp->skip_kpartx)? MPATH_UDEV_NO_KPARTX_FLAG : 0; if (mpp->wait_for_udev) { condlog(2, "%s: device not fully created, failing resume", mpp->alias); return 1; } r = dm_simplecmd_noflush(DM_DEVICE_RESUME, param, udev_flags); condlog(2, "%s: resume (operator)", param); if (!r) /* error */ return 1; dm_get_info(param, &mpp->dmi); return 0; } int cli_reinstate(void * v, char ** reply, int * len, void * data) { struct vectors * vecs = (struct vectors *)data; char * param = get_keyparam(v, PATH); struct path * pp; param = convert_dev(param, 1); pp = find_path_by_dev(vecs->pathvec, param); if (!pp) pp = find_path_by_devt(vecs->pathvec, param); if (!pp || !pp->mpp || !pp->mpp->alias) return 1; condlog(2, "%s: reinstate path %s (operator)", pp->mpp->alias, pp->dev_t); checker_enable(&pp->checker); return dm_reinstate_path(pp->mpp->alias, pp->dev_t); } int cli_reassign (void * v, char ** reply, int * len, void * data) { struct vectors * vecs = (struct vectors *)data; char * param = get_keyparam(v, MAP); struct multipath *mpp; param = convert_dev(param, 0); mpp = find_mp_by_alias(vecs->mpvec, param); if (!mpp) return 1; if (mpp->wait_for_udev) { condlog(2, "%s: device not fully created, failing reassign", mpp->alias); return 1; } condlog(3, "%s: reset devices (operator)", param); dm_reassign(param); return 0; } int cli_fail(void * v, char ** reply, int * len, void * data) { struct vectors * vecs = (struct vectors *)data; char * param = get_keyparam(v, PATH); struct path * pp; int r; param = convert_dev(param, 1); pp = find_path_by_dev(vecs->pathvec, param); if (!pp) pp = find_path_by_devt(vecs->pathvec, param); if (!pp || !pp->mpp || !pp->mpp->alias) return 1; condlog(2, "%s: fail path %s (operator)", pp->mpp->alias, pp->dev_t); r = dm_fail_path(pp->mpp->alias, pp->dev_t); /* * Suspend path checking to avoid auto-reinstating the path */ if (!r) checker_disable(&pp->checker); return r; } int show_blacklist (char ** r, int * len) { char *c = NULL; char *reply = NULL; unsigned int maxlen = INITIAL_REPLY_LEN; int again = 1; struct config *conf = get_multipath_config(); reply = MALLOC(maxlen); while (again) { if (!reply) { put_multipath_config(conf); return 1; } c = reply; c += snprint_blacklist_report(conf, c, maxlen); again = ((c - reply) == maxlen); REALLOC_REPLY(reply, again, maxlen); } *r = reply; *len = (int)(c - reply + 1); put_multipath_config(conf); return 0; } int cli_list_blacklist (void * v, char ** reply, int * len, void * data) { condlog(3, "list blacklist (operator)"); return show_blacklist(reply, len); } int show_devices (char ** r, int * len, struct vectors *vecs) { char *c = NULL; char *reply = NULL; unsigned int maxlen = INITIAL_REPLY_LEN; int again = 1; struct config *conf = get_multipath_config(); reply = MALLOC(maxlen); while (again) { if (!reply) { put_multipath_config(conf); return 1; } c = reply; c += snprint_devices(conf, c, maxlen, vecs); again = ((c - reply) == maxlen); REALLOC_REPLY(reply, again, maxlen); } *r = reply; *len = (int)(c - reply + 1); put_multipath_config(conf); return 0; } int cli_list_devices (void * v, char ** reply, int * len, void * data) { struct vectors * vecs = (struct vectors *)data; condlog(3, "list devices (operator)"); return show_devices(reply, len, vecs); } int cli_quit (void * v, char ** reply, int * len, void * data) { return 0; } int cli_shutdown (void * v, char ** reply, int * len, void * data) { condlog(3, "shutdown (operator)"); exit_daemon(); return 0; } int cli_getprstatus (void * v, char ** reply, int * len, void * data) { struct multipath * mpp; struct vectors * vecs = (struct vectors *)data; char * param = get_keyparam(v, MAP); param = convert_dev(param, 0); get_path_layout(vecs->pathvec, 0); mpp = find_mp_by_str(vecs->mpvec, param); if (!mpp) return 1; condlog(3, "%s: prflag = %u", param, (unsigned int)mpp->prflag); *len = asprintf(reply, "%d", mpp->prflag); if (*len < 0) return 1; condlog(3, "%s: reply = %s", param, *reply); return 0; } int cli_setprstatus(void * v, char ** reply, int * len, void * data) { struct multipath * mpp; struct vectors * vecs = (struct vectors *)data; char * param = get_keyparam(v, MAP); param = convert_dev(param, 0); get_path_layout(vecs->pathvec, 0); mpp = find_mp_by_str(vecs->mpvec, param); if (!mpp) return 1; if (!mpp->prflag) { mpp->prflag = 1; condlog(2, "%s: prflag set", param); } return 0; } int cli_unsetprstatus(void * v, char ** reply, int * len, void * data) { struct multipath * mpp; struct vectors * vecs = (struct vectors *)data; char * param = get_keyparam(v, MAP); param = convert_dev(param, 0); get_path_layout(vecs->pathvec, 0); mpp = find_mp_by_str(vecs->mpvec, param); if (!mpp) return 1; if (mpp->prflag) { mpp->prflag = 0; condlog(2, "%s: prflag unset", param); } return 0; } int cli_getprkey(void * v, char ** reply, int * len, void * data) { struct multipath * mpp; struct vectors * vecs = (struct vectors *)data; char *mapname = get_keyparam(v, MAP); mapname = convert_dev(mapname, 0); condlog(3, "%s: get persistent reservation key (operator)", mapname); mpp = find_mp_by_str(vecs->mpvec, mapname); if (!mpp) return 1; *reply = malloc(20); if (!get_be64(mpp->reservation_key)) { sprintf(*reply, "none\n"); *len = strlen(*reply) + 1; return 0; } snprintf(*reply, 20, "0x%" PRIx64 "\n", get_be64(mpp->reservation_key)); (*reply)[19] = '\0'; *len = strlen(*reply) + 1; return 0; } int cli_unsetprkey(void * v, char ** reply, int * len, void * data) { struct multipath * mpp; struct vectors * vecs = (struct vectors *)data; char *mapname = get_keyparam(v, MAP); int ret; struct config *conf; mapname = convert_dev(mapname, 0); condlog(3, "%s: unset persistent reservation key (operator)", mapname); mpp = find_mp_by_str(vecs->mpvec, mapname); if (!mpp) return 1; conf = get_multipath_config(); ret = set_prkey(conf, mpp, 0); put_multipath_config(conf); return ret; } int cli_setprkey(void * v, char ** reply, int * len, void * data) { struct multipath * mpp; struct vectors * vecs = (struct vectors *)data; char *mapname = get_keyparam(v, MAP); char *keyparam = get_keyparam(v, KEY); uint64_t prkey; int ret; struct config *conf; mapname = convert_dev(mapname, 0); condlog(3, "%s: set persistent reservation key (operator)", mapname); mpp = find_mp_by_str(vecs->mpvec, mapname); if (!mpp) return 1; if (parse_prkey(keyparam, &prkey) != 0) { condlog(0, "%s: invalid prkey : '%s'", mapname, keyparam); return 1; } conf = get_multipath_config(); ret = set_prkey(conf, mpp, prkey); put_multipath_config(conf); return ret; } multipath-tools-0.7.4/multipathd/cli_handlers.h000066400000000000000000000067511320314174000216460ustar00rootroot00000000000000int cli_list_paths (void * v, char ** reply, int * len, void * data); int cli_list_paths_fmt (void * v, char ** reply, int * len, void * data); int cli_list_paths_raw (void * v, char ** reply, int * len, void * data); int cli_list_path (void * v, char ** reply, int * len, void * data); int cli_list_status (void * v, char ** reply, int * len, void * data); int cli_list_daemon (void * v, char ** reply, int * len, void * data); int cli_list_maps (void * v, char ** reply, int * len, void * data); int cli_list_maps_fmt (void * v, char ** reply, int * len, void * data); int cli_list_maps_raw (void * v, char ** reply, int * len, void * data); int cli_list_map_fmt (void * v, char ** reply, int * len, void * data); int cli_list_map_raw (void * v, char ** reply, int * len, void * data); int cli_list_maps_status (void * v, char ** reply, int * len, void * data); int cli_list_maps_stats (void * v, char ** reply, int * len, void * data); int cli_list_map_topology (void * v, char ** reply, int * len, void * data); int cli_list_maps_topology (void * v, char ** reply, int * len, void * data); int cli_list_map_json (void * v, char ** reply, int * len, void * data); int cli_list_maps_json (void * v, char ** reply, int * len, void * data); int cli_list_config (void * v, char ** reply, int * len, void * data); int cli_list_blacklist (void * v, char ** reply, int * len, void * data); int cli_list_devices (void * v, char ** reply, int * len, void * data); int cli_list_wildcards (void * v, char ** reply, int * len, void * data); int cli_reset_maps_stats (void * v, char ** reply, int * len, void * data); int cli_reset_map_stats (void * v, char ** reply, int * len, void * data); int cli_add_path (void * v, char ** reply, int * len, void * data); int cli_del_path (void * v, char ** reply, int * len, void * data); int cli_add_map (void * v, char ** reply, int * len, void * data); int cli_del_map (void * v, char ** reply, int * len, void * data); int cli_switch_group(void * v, char ** reply, int * len, void * data); int cli_reconfigure(void * v, char ** reply, int * len, void * data); int cli_resize(void * v, char ** reply, int * len, void * data); int cli_reload(void * v, char ** reply, int * len, void * data); int cli_disable_queueing(void * v, char ** reply, int * len, void * data); int cli_disable_all_queueing(void * v, char ** reply, int * len, void * data); int cli_restore_queueing(void * v, char ** reply, int * len, void * data); int cli_restore_all_queueing(void * v, char ** reply, int * len, void * data); int cli_suspend(void * v, char ** reply, int * len, void * data); int cli_resume(void * v, char ** reply, int * len, void * data); int cli_reinstate(void * v, char ** reply, int * len, void * data); int cli_fail(void * v, char ** reply, int * len, void * data); int cli_force_no_daemon_q(void * v, char ** reply, int * len, void * data); int cli_restore_no_daemon_q(void * v, char ** reply, int * len, void * data); int cli_quit(void * v, char ** reply, int * len, void * data); int cli_shutdown(void * v, char ** reply, int * len, void * data); int cli_reassign (void * v, char ** reply, int * len, void * data); int cli_getprstatus(void * v, char ** reply, int * len, void * data); int cli_setprstatus(void * v, char ** reply, int * len, void * data); int cli_unsetprstatus(void * v, char ** reply, int * len, void * data); int cli_getprkey(void * v, char ** reply, int * len, void * data); int cli_setprkey(void * v, char ** reply, int * len, void * data); int cli_unsetprkey(void * v, char ** reply, int * len, void * data); multipath-tools-0.7.4/multipathd/main.c000066400000000000000000002022331320314174000201270ustar00rootroot00000000000000/* * Copyright (c) 2004, 2005 Christophe Varoqui * Copyright (c) 2005 Kiyoshi Ueda, NEC * Copyright (c) 2005 Benjamin Marzinski, Redhat * Copyright (c) 2005 Edward Goggin, EMC */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef USE_SYSTEMD #include #endif #include #include #include /* * libmultipath */ #include "time-util.h" /* * libcheckers */ #include "checkers.h" #ifdef USE_SYSTEMD static int use_watchdog; #endif int uxsock_timeout; /* * libmultipath */ #include "parser.h" #include "vector.h" #include "memory.h" #include "config.h" #include "util.h" #include "hwtable.h" #include "defaults.h" #include "structs.h" #include "blacklist.h" #include "structs_vec.h" #include "dmparser.h" #include "devmapper.h" #include "sysfs.h" #include "dict.h" #include "discovery.h" #include "debug.h" #include "propsel.h" #include "uevent.h" #include "switchgroup.h" #include "print.h" #include "configure.h" #include "prio.h" #include "wwids.h" #include "pgpolicies.h" #include "uevent.h" #include "log.h" #include "mpath_cmd.h" #include "mpath_persist.h" #include "prioritizers/alua_rtpg.h" #include "main.h" #include "pidfile.h" #include "uxlsnr.h" #include "uxclnt.h" #include "cli.h" #include "cli_handlers.h" #include "lock.h" #include "waiter.h" #include "io_err_stat.h" #include "wwids.h" #include "../third-party/valgrind/drd.h" #define FILE_NAME_SIZE 256 #define CMDSIZE 160 #define LOG_MSG(a, b) \ do { \ if (pp->offline) \ condlog(a, "%s: %s - path offline", pp->mpp->alias, pp->dev); \ else if (strlen(b)) \ condlog(a, "%s: %s - %s", pp->mpp->alias, pp->dev, b); \ } while(0) struct mpath_event_param { char * devname; struct multipath *mpp; }; int logsink; int verbosity; int bindings_read_only; int ignore_new_devs; enum daemon_status running_state = DAEMON_INIT; pid_t daemon_pid; pthread_mutex_t config_lock = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t config_cond; /* * global copy of vecs for use in sig handlers */ struct vectors * gvecs; struct udev * udev; struct config *multipath_conf; /* Local variables */ static volatile sig_atomic_t exit_sig; static volatile sig_atomic_t reconfig_sig; static volatile sig_atomic_t log_reset_sig; const char * daemon_status(void) { switch (running_state) { case DAEMON_INIT: return "init"; case DAEMON_START: return "startup"; case DAEMON_CONFIGURE: return "configure"; case DAEMON_IDLE: return "idle"; case DAEMON_RUNNING: return "running"; case DAEMON_SHUTDOWN: return "shutdown"; } return NULL; } /* * I love you too, systemd ... */ const char * sd_notify_status(void) { switch (running_state) { case DAEMON_INIT: return "STATUS=init"; case DAEMON_START: return "STATUS=startup"; case DAEMON_CONFIGURE: return "STATUS=configure"; case DAEMON_IDLE: case DAEMON_RUNNING: return "STATUS=up"; case DAEMON_SHUTDOWN: return "STATUS=shutdown"; } return NULL; } #ifdef USE_SYSTEMD static void do_sd_notify(enum daemon_status old_state) { /* * Checkerloop switches back and forth between idle and running state. * No need to tell systemd each time. * These notifications cause a lot of overhead on dbus. */ if ((running_state == DAEMON_IDLE || running_state == DAEMON_RUNNING) && (old_state == DAEMON_IDLE || old_state == DAEMON_RUNNING)) return; sd_notify(0, sd_notify_status()); } #endif static void config_cleanup(void *arg) { pthread_mutex_unlock(&config_lock); } void post_config_state(enum daemon_status state) { pthread_mutex_lock(&config_lock); if (state != running_state) { enum daemon_status old_state = running_state; running_state = state; pthread_cond_broadcast(&config_cond); #ifdef USE_SYSTEMD do_sd_notify(old_state); #endif } pthread_mutex_unlock(&config_lock); } int set_config_state(enum daemon_status state) { int rc = 0; pthread_cleanup_push(config_cleanup, NULL); pthread_mutex_lock(&config_lock); if (running_state != state) { enum daemon_status old_state = running_state; if (running_state != DAEMON_IDLE) { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); ts.tv_sec += 1; rc = pthread_cond_timedwait(&config_cond, &config_lock, &ts); } if (!rc) { running_state = state; pthread_cond_broadcast(&config_cond); #ifdef USE_SYSTEMD do_sd_notify(old_state); #endif } } pthread_cleanup_pop(1); return rc; } struct config *get_multipath_config(void) { rcu_read_lock(); return rcu_dereference(multipath_conf); } void put_multipath_config(struct config *conf) { rcu_read_unlock(); } static int need_switch_pathgroup (struct multipath * mpp, int refresh) { struct pathgroup * pgp; struct path * pp; unsigned int i, j; struct config *conf; if (!mpp || mpp->pgfailback == -FAILBACK_MANUAL) return 0; /* * Refresh path priority values */ if (refresh) { vector_foreach_slot (mpp->pg, pgp, i) { vector_foreach_slot (pgp->paths, pp, j) { conf = get_multipath_config(); pathinfo(pp, conf, DI_PRIO); put_multipath_config(conf); } } } if (!mpp->pg || VECTOR_SIZE(mpp->paths) == 0) return 0; mpp->bestpg = select_path_group(mpp); if (mpp->bestpg != mpp->nextpg) return 1; return 0; } static void switch_pathgroup (struct multipath * mpp) { mpp->stat_switchgroup++; dm_switchgroup(mpp->alias, mpp->bestpg); condlog(2, "%s: switch to path group #%i", mpp->alias, mpp->bestpg); } static int coalesce_maps(struct vectors *vecs, vector nmpv) { struct multipath * ompp; vector ompv = vecs->mpvec; unsigned int i, reassign_maps; struct config *conf; conf = get_multipath_config(); reassign_maps = conf->reassign_maps; put_multipath_config(conf); vector_foreach_slot (ompv, ompp, i) { condlog(3, "%s: coalesce map", ompp->alias); if (!find_mp_by_wwid(nmpv, ompp->wwid)) { /* * remove all current maps not allowed by the * current configuration */ if (dm_flush_map(ompp->alias)) { condlog(0, "%s: unable to flush devmap", ompp->alias); /* * may be just because the device is open */ if (setup_multipath(vecs, ompp) != 0) { i--; continue; } if (!vector_alloc_slot(nmpv)) return 1; vector_set_slot(nmpv, ompp); vector_del_slot(ompv, i); i--; } else { dm_lib_release(); condlog(2, "%s devmap removed", ompp->alias); } } else if (reassign_maps) { condlog(3, "%s: Reassign existing device-mapper" " devices", ompp->alias); dm_reassign(ompp->alias); } } return 0; } void sync_map_state(struct multipath *mpp) { struct pathgroup *pgp; struct path *pp; unsigned int i, j; if (!mpp->pg) return; vector_foreach_slot (mpp->pg, pgp, i){ vector_foreach_slot (pgp->paths, pp, j){ if (pp->state == PATH_UNCHECKED || pp->state == PATH_WILD || pp->state == PATH_DELAYED) continue; if ((pp->dmstate == PSTATE_FAILED || pp->dmstate == PSTATE_UNDEF) && (pp->state == PATH_UP || pp->state == PATH_GHOST)) dm_reinstate_path(mpp->alias, pp->dev_t); else if ((pp->dmstate == PSTATE_ACTIVE || pp->dmstate == PSTATE_UNDEF) && (pp->state == PATH_DOWN || pp->state == PATH_SHAKY)) dm_fail_path(mpp->alias, pp->dev_t); } } } static void sync_maps_state(vector mpvec) { unsigned int i; struct multipath *mpp; vector_foreach_slot (mpvec, mpp, i) sync_map_state(mpp); } static int flush_map(struct multipath * mpp, struct vectors * vecs, int nopaths) { int r; if (nopaths) r = dm_flush_map_nopaths(mpp->alias, mpp->deferred_remove); else r = dm_flush_map(mpp->alias); /* * clear references to this map before flushing so we can ignore * the spurious uevent we may generate with the dm_flush_map call below */ if (r) { /* * May not really be an error -- if the map was already flushed * from the device mapper by dmsetup(8) for instance. */ if (r == 1) condlog(0, "%s: can't flush", mpp->alias); else { condlog(2, "%s: devmap deferred remove", mpp->alias); mpp->deferred_remove = DEFERRED_REMOVE_IN_PROGRESS; } return r; } else { dm_lib_release(); condlog(2, "%s: map flushed", mpp->alias); } orphan_paths(vecs->pathvec, mpp); remove_map_and_stop_waiter(mpp, vecs, 1); return 0; } int update_map (struct multipath *mpp, struct vectors *vecs) { int retries = 3; char params[PARAMS_SIZE] = {0}; retry: condlog(4, "%s: updating new map", mpp->alias); if (adopt_paths(vecs->pathvec, mpp)) { condlog(0, "%s: failed to adopt paths for new map update", mpp->alias); retries = -1; goto fail; } verify_paths(mpp, vecs); mpp->flush_on_last_del = FLUSH_UNDEF; mpp->action = ACT_RELOAD; if (setup_map(mpp, params, PARAMS_SIZE)) { condlog(0, "%s: failed to setup new map in update", mpp->alias); retries = -1; goto fail; } if (domap(mpp, params, 1) <= 0 && retries-- > 0) { condlog(0, "%s: map_udate sleep", mpp->alias); sleep(1); goto retry; } dm_lib_release(); fail: if (setup_multipath(vecs, mpp)) return 1; sync_map_state(mpp); if (retries < 0) condlog(0, "%s: failed reload in new map update", mpp->alias); return 0; } static int uev_add_map (struct uevent * uev, struct vectors * vecs) { char *alias; int major = -1, minor = -1, rc; condlog(3, "%s: add map (uevent)", uev->kernel); alias = uevent_get_dm_name(uev); if (!alias) { condlog(3, "%s: No DM_NAME in uevent", uev->kernel); major = uevent_get_major(uev); minor = uevent_get_minor(uev); alias = dm_mapname(major, minor); if (!alias) { condlog(2, "%s: mapname not found for %d:%d", uev->kernel, major, minor); return 1; } } pthread_cleanup_push(cleanup_lock, &vecs->lock); lock(&vecs->lock); pthread_testcancel(); rc = ev_add_map(uev->kernel, alias, vecs); lock_cleanup_pop(vecs->lock); FREE(alias); return rc; } int ev_add_map (char * dev, char * alias, struct vectors * vecs) { char * refwwid; struct multipath * mpp; int map_present; int r = 1, delayed_reconfig, reassign_maps; struct config *conf; map_present = dm_map_present(alias); if (map_present && !dm_is_mpath(alias)) { condlog(4, "%s: not a multipath map", alias); return 0; } mpp = find_mp_by_alias(vecs->mpvec, alias); if (mpp) { if (mpp->wait_for_udev > 1) { condlog(2, "%s: performing delayed actions", mpp->alias); if (update_map(mpp, vecs)) /* setup multipathd removed the map */ return 1; } conf = get_multipath_config(); delayed_reconfig = conf->delayed_reconfig; reassign_maps = conf->reassign_maps; put_multipath_config(conf); if (mpp->wait_for_udev) { mpp->wait_for_udev = 0; if (delayed_reconfig && !need_to_delay_reconfig(vecs)) { condlog(2, "reconfigure (delayed)"); set_config_state(DAEMON_CONFIGURE); return 0; } } /* * Not really an error -- we generate our own uevent * if we create a multipath mapped device as a result * of uev_add_path */ if (reassign_maps) { condlog(3, "%s: Reassign existing device-mapper devices", alias); dm_reassign(alias); } return 0; } condlog(2, "%s: adding map", alias); /* * now we can register the map */ if (map_present) { if ((mpp = add_map_without_path(vecs, alias))) { sync_map_state(mpp); condlog(2, "%s: devmap %s registered", alias, dev); return 0; } else { condlog(2, "%s: uev_add_map failed", dev); return 1; } } r = get_refwwid(CMD_NONE, dev, DEV_DEVMAP, vecs->pathvec, &refwwid); if (refwwid) { r = coalesce_paths(vecs, NULL, refwwid, FORCE_RELOAD_NONE, CMD_NONE); dm_lib_release(); } if (!r) condlog(2, "%s: devmap %s added", alias, dev); else if (r == 2) condlog(2, "%s: uev_add_map %s blacklisted", alias, dev); else condlog(0, "%s: uev_add_map %s failed", alias, dev); FREE(refwwid); return r; } static int uev_remove_map (struct uevent * uev, struct vectors * vecs) { char *alias; int minor; struct multipath *mpp; condlog(2, "%s: remove map (uevent)", uev->kernel); alias = uevent_get_dm_name(uev); if (!alias) { condlog(3, "%s: No DM_NAME in uevent, ignoring", uev->kernel); return 0; } minor = uevent_get_minor(uev); pthread_cleanup_push(cleanup_lock, &vecs->lock); lock(&vecs->lock); pthread_testcancel(); mpp = find_mp_by_minor(vecs->mpvec, minor); if (!mpp) { condlog(2, "%s: devmap not registered, can't remove", uev->kernel); goto out; } if (strcmp(mpp->alias, alias)) { condlog(2, "%s: minor number mismatch (map %d, event %d)", mpp->alias, mpp->dmi->minor, minor); goto out; } orphan_paths(vecs->pathvec, mpp); remove_map_and_stop_waiter(mpp, vecs, 1); out: lock_cleanup_pop(vecs->lock); FREE(alias); return 0; } /* Called from CLI handler */ int ev_remove_map (char * devname, char * alias, int minor, struct vectors * vecs) { struct multipath * mpp; mpp = find_mp_by_minor(vecs->mpvec, minor); if (!mpp) { condlog(2, "%s: devmap not registered, can't remove", devname); return 1; } if (strcmp(mpp->alias, alias)) { condlog(2, "%s: minor number mismatch (map %d, event %d)", mpp->alias, mpp->dmi->minor, minor); return 1; } return flush_map(mpp, vecs, 0); } static int uev_add_path (struct uevent *uev, struct vectors * vecs, int need_do_map) { struct path *pp; int ret = 0, i; struct config *conf; condlog(2, "%s: add path (uevent)", uev->kernel); if (strstr(uev->kernel, "..") != NULL) { /* * Don't allow relative device names in the pathvec */ condlog(0, "%s: path name is invalid", uev->kernel); return 1; } pthread_cleanup_push(cleanup_lock, &vecs->lock); lock(&vecs->lock); pthread_testcancel(); pp = find_path_by_dev(vecs->pathvec, uev->kernel); if (pp) { int r; condlog(0, "%s: spurious uevent, path already in pathvec", uev->kernel); if (!pp->mpp && !strlen(pp->wwid)) { condlog(3, "%s: reinitialize path", uev->kernel); udev_device_unref(pp->udev); pp->udev = udev_device_ref(uev->udev); conf = get_multipath_config(); r = pathinfo(pp, conf, DI_ALL | DI_BLACKLIST); put_multipath_config(conf); if (r == PATHINFO_OK) ret = ev_add_path(pp, vecs, need_do_map); else if (r == PATHINFO_SKIPPED) { condlog(3, "%s: remove blacklisted path", uev->kernel); i = find_slot(vecs->pathvec, (void *)pp); if (i != -1) vector_del_slot(vecs->pathvec, i); free_path(pp); } else { condlog(0, "%s: failed to reinitialize path", uev->kernel); ret = 1; } } } lock_cleanup_pop(vecs->lock); if (pp) return ret; /* * get path vital state */ conf = get_multipath_config(); ret = alloc_path_with_pathinfo(conf, uev->udev, uev->wwid, DI_ALL, &pp); put_multipath_config(conf); if (!pp) { if (ret == PATHINFO_SKIPPED) return 0; condlog(3, "%s: failed to get path info", uev->kernel); return 1; } pthread_cleanup_push(cleanup_lock, &vecs->lock); lock(&vecs->lock); pthread_testcancel(); ret = store_path(vecs->pathvec, pp); if (!ret) { conf = get_multipath_config(); pp->checkint = conf->checkint; put_multipath_config(conf); ret = ev_add_path(pp, vecs, need_do_map); } else { condlog(0, "%s: failed to store path info, " "dropping event", uev->kernel); free_path(pp); ret = 1; } lock_cleanup_pop(vecs->lock); return ret; } /* * returns: * 0: added * 1: error */ int ev_add_path (struct path * pp, struct vectors * vecs, int need_do_map) { struct multipath * mpp; char params[PARAMS_SIZE] = {0}; int retries = 3; int start_waiter = 0; int ret; /* * need path UID to go any further */ if (strlen(pp->wwid) == 0) { condlog(0, "%s: failed to get path uid", pp->dev); goto fail; /* leave path added to pathvec */ } mpp = find_mp_by_wwid(vecs->mpvec, pp->wwid); if (mpp && mpp->wait_for_udev && (pathcount(mpp, PATH_UP) > 0 || (pathcount(mpp, PATH_GHOST) > 0 && pp->tpgs != TPGS_IMPLICIT))) { /* if wait_for_udev is set and valid paths exist */ condlog(2, "%s: delaying path addition until %s is fully initialized", pp->dev, mpp->alias); mpp->wait_for_udev = 2; orphan_path(pp, "waiting for create to complete"); return 0; } pp->mpp = mpp; rescan: if (mpp) { if (pp->size && mpp->size != pp->size) { condlog(0, "%s: failed to add new path %s, " "device size mismatch", mpp->alias, pp->dev); int i = find_slot(vecs->pathvec, (void *)pp); if (i != -1) vector_del_slot(vecs->pathvec, i); free_path(pp); return 1; } condlog(4,"%s: adopting all paths for path %s", mpp->alias, pp->dev); if (adopt_paths(vecs->pathvec, mpp)) goto fail; /* leave path added to pathvec */ verify_paths(mpp, vecs); mpp->flush_on_last_del = FLUSH_UNDEF; mpp->action = ACT_RELOAD; } else { if (!should_multipath(pp, vecs->pathvec)) { orphan_path(pp, "only one path"); return 0; } condlog(4,"%s: creating new map", pp->dev); if ((mpp = add_map_with_path(vecs, pp, 1))) { mpp->action = ACT_CREATE; /* * We don't depend on ACT_CREATE, as domap will * set it to ACT_NOTHING when complete. */ start_waiter = 1; } if (!start_waiter) goto fail; /* leave path added to pathvec */ } /* persistent reservation check*/ mpath_pr_event_handle(pp); if (!need_do_map) return 0; if (!dm_map_present(mpp->alias)) { mpp->action = ACT_CREATE; start_waiter = 1; } /* * push the map to the device-mapper */ if (setup_map(mpp, params, PARAMS_SIZE)) { condlog(0, "%s: failed to setup map for addition of new " "path %s", mpp->alias, pp->dev); goto fail_map; } /* * reload the map for the multipath mapped device */ retry: ret = domap(mpp, params, 1); if (ret <= 0) { if (ret < 0 && retries-- > 0) { condlog(0, "%s: retry domap for addition of new " "path %s", mpp->alias, pp->dev); sleep(1); goto retry; } condlog(0, "%s: failed in domap for addition of new " "path %s", mpp->alias, pp->dev); /* * deal with asynchronous uevents :(( */ if (mpp->action == ACT_RELOAD && retries-- > 0) { condlog(0, "%s: ev_add_path sleep", mpp->alias); sleep(1); update_mpp_paths(mpp, vecs->pathvec); goto rescan; } else if (mpp->action == ACT_RELOAD) condlog(0, "%s: giving up reload", mpp->alias); else goto fail_map; } dm_lib_release(); /* * update our state from kernel regardless of create or reload */ if (setup_multipath(vecs, mpp)) goto fail; /* if setup_multipath fails, it removes the map */ sync_map_state(mpp); if ((mpp->action == ACT_CREATE || (mpp->action == ACT_NOTHING && start_waiter && !mpp->waiter)) && start_waiter_thread(mpp, vecs)) goto fail_map; if (retries >= 0) { condlog(2, "%s [%s]: path added to devmap %s", pp->dev, pp->dev_t, mpp->alias); return 0; } else goto fail; fail_map: remove_map(mpp, vecs, 1); fail: orphan_path(pp, "failed to add path"); return 1; } static int uev_remove_path (struct uevent *uev, struct vectors * vecs, int need_do_map) { struct path *pp; int ret; condlog(2, "%s: remove path (uevent)", uev->kernel); pthread_cleanup_push(cleanup_lock, &vecs->lock); lock(&vecs->lock); pthread_testcancel(); pp = find_path_by_dev(vecs->pathvec, uev->kernel); if (pp) ret = ev_remove_path(pp, vecs, need_do_map); lock_cleanup_pop(vecs->lock); if (!pp) { /* Not an error; path might have been purged earlier */ condlog(0, "%s: path already removed", uev->kernel); return 0; } return ret; } int ev_remove_path (struct path *pp, struct vectors * vecs, int need_do_map) { struct multipath * mpp; int i, retval = 0; char params[PARAMS_SIZE] = {0}; /* * avoid referring to the map of an orphaned path */ if ((mpp = pp->mpp)) { /* * transform the mp->pg vector of vectors of paths * into a mp->params string to feed the device-mapper */ if (update_mpp_paths(mpp, vecs->pathvec)) { condlog(0, "%s: failed to update paths", mpp->alias); goto fail; } if ((i = find_slot(mpp->paths, (void *)pp)) != -1) vector_del_slot(mpp->paths, i); /* * remove the map IFF removing the last path */ if (VECTOR_SIZE(mpp->paths) == 0) { char alias[WWID_SIZE]; /* * flush_map will fail if the device is open */ strncpy(alias, mpp->alias, WWID_SIZE); if (mpp->flush_on_last_del == FLUSH_ENABLED) { condlog(2, "%s Last path deleted, disabling queueing", mpp->alias); mpp->retry_tick = 0; mpp->no_path_retry = NO_PATH_RETRY_FAIL; mpp->flush_on_last_del = FLUSH_IN_PROGRESS; mpp->stat_map_failures++; dm_queue_if_no_path(mpp->alias, 0); } if (!flush_map(mpp, vecs, 1)) { condlog(2, "%s: removed map after" " removing all paths", alias); retval = 0; goto out; } /* * Not an error, continue */ } if (setup_map(mpp, params, PARAMS_SIZE)) { condlog(0, "%s: failed to setup map for" " removal of path %s", mpp->alias, pp->dev); goto fail; } if (mpp->wait_for_udev) { mpp->wait_for_udev = 2; goto out; } if (!need_do_map) goto out; /* * reload the map */ mpp->action = ACT_RELOAD; if (domap(mpp, params, 1) <= 0) { condlog(0, "%s: failed in domap for " "removal of path %s", mpp->alias, pp->dev); retval = 1; } else { /* * update our state from kernel */ if (setup_multipath(vecs, mpp)) return 1; sync_map_state(mpp); condlog(2, "%s [%s]: path removed from map %s", pp->dev, pp->dev_t, mpp->alias); } } out: if ((i = find_slot(vecs->pathvec, (void *)pp)) != -1) vector_del_slot(vecs->pathvec, i); free_path(pp); return retval; fail: remove_map_and_stop_waiter(mpp, vecs, 1); return 1; } static int uev_update_path (struct uevent *uev, struct vectors * vecs) { int ro, retval = 0; struct path * pp; struct config *conf; int disable_changed_wwids; int needs_reinit = 0; conf = get_multipath_config(); disable_changed_wwids = conf->disable_changed_wwids; put_multipath_config(conf); ro = uevent_get_disk_ro(uev); pthread_cleanup_push(cleanup_lock, &vecs->lock); lock(&vecs->lock); pthread_testcancel(); pp = find_path_by_dev(vecs->pathvec, uev->kernel); if (pp) { struct multipath *mpp = pp->mpp; if (disable_changed_wwids && (strlen(pp->wwid) || pp->wwid_changed)) { char wwid[WWID_SIZE]; strcpy(wwid, pp->wwid); get_uid(pp, pp->state, uev->udev); if (strcmp(wwid, pp->wwid) != 0) { condlog(0, "%s: path wwid changed from '%s' to '%s'. disallowing", uev->kernel, wwid, pp->wwid); strcpy(pp->wwid, wwid); if (!pp->wwid_changed) { pp->wwid_changed = 1; pp->tick = 1; if (pp->mpp) dm_fail_path(pp->mpp->alias, pp->dev_t); } goto out; } else pp->wwid_changed = 0; } if (pp->initialized == INIT_REQUESTED_UDEV) needs_reinit = 1; else if (mpp && ro >= 0) { condlog(2, "%s: update path write_protect to '%d' (uevent)", uev->kernel, ro); if (mpp->wait_for_udev) mpp->wait_for_udev = 2; else { if (ro == 1) pp->mpp->force_readonly = 1; retval = reload_map(vecs, mpp, 0, 1); pp->mpp->force_readonly = 0; condlog(2, "%s: map %s reloaded (retval %d)", uev->kernel, mpp->alias, retval); } } } out: lock_cleanup_pop(vecs->lock); if (!pp) { /* If the path is blacklisted, print a debug/non-default verbosity message. */ if (uev->udev) { int flag = DI_SYSFS | DI_WWID; conf = get_multipath_config(); retval = alloc_path_with_pathinfo(conf, uev->udev, uev->wwid, flag, NULL); put_multipath_config(conf); if (retval == PATHINFO_SKIPPED) { condlog(3, "%s: spurious uevent, path is blacklisted", uev->kernel); return 0; } } condlog(0, "%s: spurious uevent, path not found", uev->kernel); } if (needs_reinit) retval = uev_add_path(uev, vecs, 1); return retval; } static int uev_pathfail_check(struct uevent *uev, struct vectors *vecs) { char *action = NULL, *devt = NULL; struct path *pp; int r; action = uevent_get_dm_action(uev); if (!action) return 1; if (strncmp(action, "PATH_FAILED", 11)) goto out; devt = uevent_get_dm_path(uev); if (!devt) { condlog(3, "%s: No DM_PATH in uevent", uev->kernel); goto out; } pthread_cleanup_push(cleanup_lock, &vecs->lock); lock(&vecs->lock); pthread_testcancel(); pp = find_path_by_devt(vecs->pathvec, devt); r = io_err_stat_handle_pathfail(pp); lock_cleanup_pop(vecs->lock); if (r) condlog(3, "io_err_stat: %s: cannot handle pathfail uevent", pp->dev); FREE(devt); FREE(action); return 0; out: FREE(action); return 1; } static int map_discovery (struct vectors * vecs) { struct multipath * mpp; unsigned int i; if (dm_get_maps(vecs->mpvec)) return 1; vector_foreach_slot (vecs->mpvec, mpp, i) if (setup_multipath(vecs, mpp)) i--; return 0; } int uxsock_trigger (char * str, char ** reply, int * len, bool is_root, void * trigger_data) { struct vectors * vecs; int r; *reply = NULL; *len = 0; vecs = (struct vectors *)trigger_data; if ((str != NULL) && (is_root == false) && (strncmp(str, "list", strlen("list")) != 0) && (strncmp(str, "show", strlen("show")) != 0)) { *reply = STRDUP("permission deny: need to be root"); if (*reply) *len = strlen(*reply) + 1; return 1; } r = parse_cmd(str, reply, len, vecs, uxsock_timeout / 1000); if (r > 0) { if (r == ETIMEDOUT) *reply = STRDUP("timeout\n"); else *reply = STRDUP("fail\n"); if (*reply) *len = strlen(*reply) + 1; r = 1; } else if (!r && *len == 0) { *reply = STRDUP("ok\n"); if (*reply) *len = strlen(*reply) + 1; r = 0; } /* else if (r < 0) leave *reply alone */ return r; } int uev_trigger (struct uevent * uev, void * trigger_data) { int r = 0; struct vectors * vecs; struct uevent *merge_uev, *tmp; vecs = (struct vectors *)trigger_data; pthread_cleanup_push(config_cleanup, NULL); pthread_mutex_lock(&config_lock); if (running_state != DAEMON_IDLE && running_state != DAEMON_RUNNING) pthread_cond_wait(&config_cond, &config_lock); pthread_cleanup_pop(1); if (running_state == DAEMON_SHUTDOWN) return 0; /* * device map event * Add events are ignored here as the tables * are not fully initialised then. */ if (!strncmp(uev->kernel, "dm-", 3)) { if (!strncmp(uev->action, "change", 6)) { r = uev_add_map(uev, vecs); /* * the kernel-side dm-mpath issues a PATH_FAILED event * when it encounters a path IO error. It is reason- * able be the entry of path IO error accounting pro- * cess. */ uev_pathfail_check(uev, vecs); goto out; } if (!strncmp(uev->action, "remove", 6)) { r = uev_remove_map(uev, vecs); goto out; } goto out; } /* * path add/remove/change event, add/remove maybe merged */ list_for_each_entry_safe(merge_uev, tmp, &uev->merge_node, node) { if (!strncmp(merge_uev->action, "add", 3)) r += uev_add_path(merge_uev, vecs, 0); if (!strncmp(merge_uev->action, "remove", 6)) r += uev_remove_path(merge_uev, vecs, 0); } if (!strncmp(uev->action, "add", 3)) r += uev_add_path(uev, vecs, 1); if (!strncmp(uev->action, "remove", 6)) r += uev_remove_path(uev, vecs, 1); if (!strncmp(uev->action, "change", 6)) r += uev_update_path(uev, vecs); out: return r; } static void rcu_unregister(void *param) { rcu_unregister_thread(); } static void * ueventloop (void * ap) { struct udev *udev = ap; pthread_cleanup_push(rcu_unregister, NULL); rcu_register_thread(); if (uevent_listen(udev)) condlog(0, "error starting uevent listener"); pthread_cleanup_pop(1); return NULL; } static void * uevqloop (void * ap) { pthread_cleanup_push(rcu_unregister, NULL); rcu_register_thread(); if (uevent_dispatch(&uev_trigger, ap)) condlog(0, "error starting uevent dispatcher"); pthread_cleanup_pop(1); return NULL; } static void * uxlsnrloop (void * ap) { if (cli_init()) { condlog(1, "Failed to init uxsock listener"); return NULL; } pthread_cleanup_push(rcu_unregister, NULL); rcu_register_thread(); set_handler_callback(LIST+PATHS, cli_list_paths); set_handler_callback(LIST+PATHS+FMT, cli_list_paths_fmt); set_handler_callback(LIST+PATHS+RAW+FMT, cli_list_paths_raw); set_handler_callback(LIST+PATH, cli_list_path); set_handler_callback(LIST+MAPS, cli_list_maps); set_unlocked_handler_callback(LIST+STATUS, cli_list_status); set_unlocked_handler_callback(LIST+DAEMON, cli_list_daemon); set_handler_callback(LIST+MAPS+STATUS, cli_list_maps_status); set_handler_callback(LIST+MAPS+STATS, cli_list_maps_stats); set_handler_callback(LIST+MAPS+FMT, cli_list_maps_fmt); set_handler_callback(LIST+MAPS+RAW+FMT, cli_list_maps_raw); set_handler_callback(LIST+MAPS+TOPOLOGY, cli_list_maps_topology); set_handler_callback(LIST+TOPOLOGY, cli_list_maps_topology); set_handler_callback(LIST+MAPS+JSON, cli_list_maps_json); set_handler_callback(LIST+MAP+TOPOLOGY, cli_list_map_topology); set_handler_callback(LIST+MAP+FMT, cli_list_map_fmt); set_handler_callback(LIST+MAP+RAW+FMT, cli_list_map_fmt); set_handler_callback(LIST+MAP+JSON, cli_list_map_json); set_handler_callback(LIST+CONFIG, cli_list_config); set_handler_callback(LIST+BLACKLIST, cli_list_blacklist); set_handler_callback(LIST+DEVICES, cli_list_devices); set_handler_callback(LIST+WILDCARDS, cli_list_wildcards); set_handler_callback(RESET+MAPS+STATS, cli_reset_maps_stats); set_handler_callback(RESET+MAP+STATS, cli_reset_map_stats); set_handler_callback(ADD+PATH, cli_add_path); set_handler_callback(DEL+PATH, cli_del_path); set_handler_callback(ADD+MAP, cli_add_map); set_handler_callback(DEL+MAP, cli_del_map); set_handler_callback(SWITCH+MAP+GROUP, cli_switch_group); set_unlocked_handler_callback(RECONFIGURE, cli_reconfigure); set_handler_callback(SUSPEND+MAP, cli_suspend); set_handler_callback(RESUME+MAP, cli_resume); set_handler_callback(RESIZE+MAP, cli_resize); set_handler_callback(RELOAD+MAP, cli_reload); set_handler_callback(RESET+MAP, cli_reassign); set_handler_callback(REINSTATE+PATH, cli_reinstate); set_handler_callback(FAIL+PATH, cli_fail); set_handler_callback(DISABLEQ+MAP, cli_disable_queueing); set_handler_callback(RESTOREQ+MAP, cli_restore_queueing); set_handler_callback(DISABLEQ+MAPS, cli_disable_all_queueing); set_handler_callback(RESTOREQ+MAPS, cli_restore_all_queueing); set_unlocked_handler_callback(QUIT, cli_quit); set_unlocked_handler_callback(SHUTDOWN, cli_shutdown); set_handler_callback(GETPRSTATUS+MAP, cli_getprstatus); set_handler_callback(SETPRSTATUS+MAP, cli_setprstatus); set_handler_callback(UNSETPRSTATUS+MAP, cli_unsetprstatus); set_handler_callback(FORCEQ+DAEMON, cli_force_no_daemon_q); set_handler_callback(RESTOREQ+DAEMON, cli_restore_no_daemon_q); set_handler_callback(GETPRKEY+MAP, cli_getprkey); set_handler_callback(SETPRKEY+MAP+KEY, cli_setprkey); set_handler_callback(UNSETPRKEY+MAP, cli_unsetprkey); umask(077); uxsock_listen(&uxsock_trigger, ap); pthread_cleanup_pop(1); return NULL; } void exit_daemon (void) { post_config_state(DAEMON_SHUTDOWN); } static void fail_path (struct path * pp, int del_active) { if (!pp->mpp) return; condlog(2, "checker failed path %s in map %s", pp->dev_t, pp->mpp->alias); dm_fail_path(pp->mpp->alias, pp->dev_t); if (del_active) update_queue_mode_del_path(pp->mpp); } /* * caller must have locked the path list before calling that function */ static int reinstate_path (struct path * pp, int add_active) { int ret = 0; if (!pp->mpp) return 0; if (dm_reinstate_path(pp->mpp->alias, pp->dev_t)) { condlog(0, "%s: reinstate failed", pp->dev_t); ret = 1; } else { condlog(2, "%s: reinstated", pp->dev_t); if (add_active) update_queue_mode_add_path(pp->mpp); } return ret; } static void enable_group(struct path * pp) { struct pathgroup * pgp; /* * if path is added through uev_add_path, pgindex can be unset. * next update_strings() will set it, upon map reload event. * * we can safely return here, because upon map reload, all * PG will be enabled. */ if (!pp->mpp->pg || !pp->pgindex) return; pgp = VECTOR_SLOT(pp->mpp->pg, pp->pgindex - 1); if (pgp->status == PGSTATE_DISABLED) { condlog(2, "%s: enable group #%i", pp->mpp->alias, pp->pgindex); dm_enablegroup(pp->mpp->alias, pp->pgindex); } } static void mpvec_garbage_collector (struct vectors * vecs) { struct multipath * mpp; unsigned int i; if (!vecs->mpvec) return; vector_foreach_slot (vecs->mpvec, mpp, i) { if (mpp && mpp->alias && !dm_map_present(mpp->alias)) { condlog(2, "%s: remove dead map", mpp->alias); remove_map_and_stop_waiter(mpp, vecs, 1); i--; } } } /* This is called after a path has started working again. It the multipath * device for this path uses the followover failback type, and this is the * best pathgroup, and this is the first path in the pathgroup to come back * up, then switch to this pathgroup */ static int followover_should_failback(struct path * pp) { struct pathgroup * pgp; struct path *pp1; int i; if (pp->mpp->pgfailback != -FAILBACK_FOLLOWOVER || !pp->mpp->pg || !pp->pgindex || pp->pgindex != pp->mpp->bestpg) return 0; pgp = VECTOR_SLOT(pp->mpp->pg, pp->pgindex - 1); vector_foreach_slot(pgp->paths, pp1, i) { if (pp1 == pp) continue; if (pp1->chkrstate != PATH_DOWN && pp1->chkrstate != PATH_SHAKY) return 0; } return 1; } static void missing_uev_wait_tick(struct vectors *vecs) { struct multipath * mpp; unsigned int i; int timed_out = 0, delayed_reconfig; struct config *conf; vector_foreach_slot (vecs->mpvec, mpp, i) { if (mpp->wait_for_udev && --mpp->uev_wait_tick <= 0) { timed_out = 1; condlog(0, "%s: timeout waiting on creation uevent. enabling reloads", mpp->alias); if (mpp->wait_for_udev > 1 && update_map(mpp, vecs)) { /* update_map removed map */ i--; continue; } mpp->wait_for_udev = 0; } } conf = get_multipath_config(); delayed_reconfig = conf->delayed_reconfig; put_multipath_config(conf); if (timed_out && delayed_reconfig && !need_to_delay_reconfig(vecs)) { condlog(2, "reconfigure (delayed)"); set_config_state(DAEMON_CONFIGURE); } } static void defered_failback_tick (vector mpvec) { struct multipath * mpp; unsigned int i; vector_foreach_slot (mpvec, mpp, i) { /* * defered failback getting sooner */ if (mpp->pgfailback > 0 && mpp->failback_tick > 0) { mpp->failback_tick--; if (!mpp->failback_tick && need_switch_pathgroup(mpp, 1)) switch_pathgroup(mpp); } } } static void retry_count_tick(vector mpvec) { struct multipath *mpp; unsigned int i; vector_foreach_slot (mpvec, mpp, i) { if (mpp->retry_tick > 0) { mpp->stat_total_queueing_time++; condlog(4, "%s: Retrying.. No active path", mpp->alias); if(--mpp->retry_tick == 0) { mpp->stat_map_failures++; dm_queue_if_no_path(mpp->alias, 0); condlog(2, "%s: Disable queueing", mpp->alias); } } } } int update_prio(struct path *pp, int refresh_all) { int oldpriority; struct path *pp1; struct pathgroup * pgp; int i, j, changed = 0; struct config *conf; if (refresh_all) { vector_foreach_slot (pp->mpp->pg, pgp, i) { vector_foreach_slot (pgp->paths, pp1, j) { oldpriority = pp1->priority; conf = get_multipath_config(); pathinfo(pp1, conf, DI_PRIO); put_multipath_config(conf); if (pp1->priority != oldpriority) changed = 1; } } return changed; } oldpriority = pp->priority; conf = get_multipath_config(); if (pp->state != PATH_DOWN) pathinfo(pp, conf, DI_PRIO); put_multipath_config(conf); if (pp->priority == oldpriority) return 0; return 1; } int update_path_groups(struct multipath *mpp, struct vectors *vecs, int refresh) { if (reload_map(vecs, mpp, refresh, 1)) return 1; dm_lib_release(); if (setup_multipath(vecs, mpp) != 0) return 1; sync_map_state(mpp); return 0; } void repair_path(struct path * pp) { if (pp->state != PATH_DOWN) return; checker_repair(&pp->checker); LOG_MSG(1, checker_message(&pp->checker)); } /* * Returns '1' if the path has been checked, '-1' if it was blacklisted * and '0' otherwise */ int check_path (struct vectors * vecs, struct path * pp, int ticks) { int newstate; int new_path_up = 0; int chkr_new_path_up = 0; int add_active; int disable_reinstate = 0; int oldchkrstate = pp->chkrstate; int retrigger_tries, checkint; struct config *conf; int ret; if ((pp->initialized == INIT_OK || pp->initialized == INIT_REQUESTED_UDEV) && !pp->mpp) return 0; if (pp->tick) pp->tick -= (pp->tick > ticks) ? ticks : pp->tick; if (pp->tick) return 0; /* don't check this path yet */ conf = get_multipath_config(); retrigger_tries = conf->retrigger_tries; checkint = conf->checkint; put_multipath_config(conf); if (!pp->mpp && pp->initialized == INIT_MISSING_UDEV && pp->retriggers < retrigger_tries) { condlog(2, "%s: triggering change event to reinitialize", pp->dev); pp->initialized = INIT_REQUESTED_UDEV; pp->retriggers++; sysfs_attr_set_value(pp->udev, "uevent", "change", strlen("change")); return 0; } /* * provision a next check soonest, * in case we exit abnormaly from here */ pp->tick = checkint; newstate = path_offline(pp); /* * Wait for uevent for removed paths; * some LLDDs like zfcp keep paths unavailable * without sending uevents. */ if (newstate == PATH_REMOVED) newstate = PATH_DOWN; if (newstate == PATH_UP) { conf = get_multipath_config(); newstate = get_state(pp, conf, 1, newstate); put_multipath_config(conf); } else checker_clear_message(&pp->checker); if (pp->wwid_changed) { condlog(2, "%s: path wwid has changed. Refusing to use", pp->dev); newstate = PATH_DOWN; } if (newstate == PATH_WILD || newstate == PATH_UNCHECKED) { condlog(2, "%s: unusable path", pp->dev); conf = get_multipath_config(); pathinfo(pp, conf, 0); put_multipath_config(conf); return 1; } if (!pp->mpp) { if (!strlen(pp->wwid) && pp->initialized != INIT_MISSING_UDEV && (newstate == PATH_UP || newstate == PATH_GHOST)) { condlog(2, "%s: add missing path", pp->dev); conf = get_multipath_config(); ret = pathinfo(pp, conf, DI_ALL | DI_BLACKLIST); if (ret == PATHINFO_OK) { ev_add_path(pp, vecs, 1); pp->tick = 1; } else if (ret == PATHINFO_SKIPPED) { put_multipath_config(conf); return -1; } put_multipath_config(conf); } return 0; } /* * Async IO in flight. Keep the previous path state * and reschedule as soon as possible */ if (newstate == PATH_PENDING) { pp->tick = 1; return 0; } /* * Synchronize with kernel state */ if (update_multipath_strings(pp->mpp, vecs->pathvec, 1)) { condlog(1, "%s: Could not synchronize with kernel state", pp->dev); pp->dmstate = PSTATE_UNDEF; } /* if update_multipath_strings orphaned the path, quit early */ if (!pp->mpp) return 0; if (pp->io_err_disable_reinstate && hit_io_err_recheck_time(pp)) { pp->state = PATH_SHAKY; /* * to reschedule as soon as possible,so that this path can * be recoverd in time */ pp->tick = 1; return 1; } if ((newstate == PATH_UP || newstate == PATH_GHOST) && pp->wait_checks > 0) { if (pp->mpp->nr_active > 0) { pp->state = PATH_DELAYED; pp->wait_checks--; return 1; } else pp->wait_checks = 0; } /* * don't reinstate failed path, if its in stand-by * and if target supports only implicit tpgs mode. * this will prevent unnecessary i/o by dm on stand-by * paths if there are no other active paths in map. */ disable_reinstate = (newstate == PATH_GHOST && pp->mpp->nr_active == 0 && pp->tpgs == TPGS_IMPLICIT) ? 1 : 0; pp->chkrstate = newstate; if (newstate != pp->state) { int oldstate = pp->state; pp->state = newstate; LOG_MSG(1, checker_message(&pp->checker)); /* * upon state change, reset the checkint * to the shortest delay */ conf = get_multipath_config(); pp->checkint = conf->checkint; put_multipath_config(conf); if (newstate != PATH_UP && newstate != PATH_GHOST) { /* * proactively fail path in the DM */ if (oldstate == PATH_UP || oldstate == PATH_GHOST) { fail_path(pp, 1); if (pp->mpp->delay_wait_checks > 0 && pp->watch_checks > 0) { pp->wait_checks = pp->mpp->delay_wait_checks; pp->watch_checks = 0; } }else fail_path(pp, 0); /* * cancel scheduled failback */ pp->mpp->failback_tick = 0; pp->mpp->stat_path_failures++; repair_path(pp); return 1; } if(newstate == PATH_UP || newstate == PATH_GHOST){ if ( pp->mpp && pp->mpp->prflag ){ /* * Check Persistent Reservation. */ condlog(2, "%s: checking persistent reservation " "registration", pp->dev); mpath_pr_event_handle(pp); } } /* * reinstate this path */ if (oldstate != PATH_UP && oldstate != PATH_GHOST) { if (pp->mpp->delay_watch_checks > 0) pp->watch_checks = pp->mpp->delay_watch_checks; add_active = 1; } else { if (pp->watch_checks > 0) pp->watch_checks--; add_active = 0; } if (!disable_reinstate && reinstate_path(pp, add_active)) { condlog(3, "%s: reload map", pp->dev); ev_add_path(pp, vecs, 1); pp->tick = 1; return 0; } new_path_up = 1; if (oldchkrstate != PATH_UP && oldchkrstate != PATH_GHOST) chkr_new_path_up = 1; /* * if at least one path is up in a group, and * the group is disabled, re-enable it */ if (newstate == PATH_UP) enable_group(pp); } else if (newstate == PATH_UP || newstate == PATH_GHOST) { if ((pp->dmstate == PSTATE_FAILED || pp->dmstate == PSTATE_UNDEF) && !disable_reinstate) { /* Clear IO errors */ if (reinstate_path(pp, 0)) { condlog(3, "%s: reload map", pp->dev); ev_add_path(pp, vecs, 1); pp->tick = 1; return 0; } } else { unsigned int max_checkint; LOG_MSG(4, checker_message(&pp->checker)); conf = get_multipath_config(); max_checkint = conf->max_checkint; put_multipath_config(conf); if (pp->checkint != max_checkint) { /* * double the next check delay. * max at conf->max_checkint */ if (pp->checkint < (max_checkint / 2)) pp->checkint = 2 * pp->checkint; else pp->checkint = max_checkint; condlog(4, "%s: delay next check %is", pp->dev_t, pp->checkint); } if (pp->watch_checks > 0) pp->watch_checks--; pp->tick = pp->checkint; } } else if (newstate == PATH_DOWN) { int log_checker_err; conf = get_multipath_config(); log_checker_err = conf->log_checker_err; put_multipath_config(conf); if (log_checker_err == LOG_CHKR_ERR_ONCE) LOG_MSG(3, checker_message(&pp->checker)); else LOG_MSG(2, checker_message(&pp->checker)); } pp->state = newstate; repair_path(pp); if (pp->mpp->wait_for_udev) return 1; /* * path prio refreshing */ condlog(4, "path prio refresh"); if (update_prio(pp, new_path_up) && (pp->mpp->pgpolicyfn == (pgpolicyfn *)group_by_prio) && pp->mpp->pgfailback == -FAILBACK_IMMEDIATE) update_path_groups(pp->mpp, vecs, !new_path_up); else if (need_switch_pathgroup(pp->mpp, 0)) { if (pp->mpp->pgfailback > 0 && (new_path_up || pp->mpp->failback_tick <= 0)) pp->mpp->failback_tick = pp->mpp->pgfailback + 1; else if (pp->mpp->pgfailback == -FAILBACK_IMMEDIATE || (chkr_new_path_up && followover_should_failback(pp))) switch_pathgroup(pp->mpp); } return 1; } static void init_path_check_interval(struct vectors *vecs) { struct config *conf; struct path *pp; unsigned int i; vector_foreach_slot (vecs->pathvec, pp, i) { conf = get_multipath_config(); pp->checkint = conf->checkint; put_multipath_config(conf); } } static void * checkerloop (void *ap) { struct vectors *vecs; struct path *pp; int count = 0; unsigned int i; struct itimerval timer_tick_it; struct timespec last_time; struct config *conf; pthread_cleanup_push(rcu_unregister, NULL); rcu_register_thread(); mlockall(MCL_CURRENT | MCL_FUTURE); vecs = (struct vectors *)ap; condlog(2, "path checkers start up"); /* Tweak start time for initial path check */ if (clock_gettime(CLOCK_MONOTONIC, &last_time) != 0) last_time.tv_sec = 0; else last_time.tv_sec -= 1; while (1) { struct timespec diff_time, start_time, end_time; int num_paths = 0, ticks = 0, signo, strict_timing, rc = 0; sigset_t mask; if (clock_gettime(CLOCK_MONOTONIC, &start_time) != 0) start_time.tv_sec = 0; if (start_time.tv_sec && last_time.tv_sec) { timespecsub(&start_time, &last_time, &diff_time); condlog(4, "tick (%lu.%06lu secs)", diff_time.tv_sec, diff_time.tv_nsec / 1000); last_time = start_time; ticks = diff_time.tv_sec; } else { ticks = 1; condlog(4, "tick (%d ticks)", ticks); } #ifdef USE_SYSTEMD if (use_watchdog) sd_notify(0, "WATCHDOG=1"); #endif rc = set_config_state(DAEMON_RUNNING); if (rc == ETIMEDOUT) { condlog(4, "timeout waiting for DAEMON_IDLE"); continue; } pthread_cleanup_push(cleanup_lock, &vecs->lock); lock(&vecs->lock); pthread_testcancel(); vector_foreach_slot (vecs->pathvec, pp, i) { rc = check_path(vecs, pp, ticks); if (rc < 0) { vector_del_slot(vecs->pathvec, i); free_path(pp); i--; } else num_paths += rc; } lock_cleanup_pop(vecs->lock); pthread_cleanup_push(cleanup_lock, &vecs->lock); lock(&vecs->lock); pthread_testcancel(); defered_failback_tick(vecs->mpvec); retry_count_tick(vecs->mpvec); missing_uev_wait_tick(vecs); lock_cleanup_pop(vecs->lock); if (count) count--; else { pthread_cleanup_push(cleanup_lock, &vecs->lock); lock(&vecs->lock); pthread_testcancel(); condlog(4, "map garbage collection"); mpvec_garbage_collector(vecs); count = MAPGCINT; lock_cleanup_pop(vecs->lock); } diff_time.tv_nsec = 0; if (start_time.tv_sec && clock_gettime(CLOCK_MONOTONIC, &end_time) == 0) { timespecsub(&end_time, &start_time, &diff_time); if (num_paths) { unsigned int max_checkint; condlog(3, "checked %d path%s in %lu.%06lu secs", num_paths, num_paths > 1 ? "s" : "", diff_time.tv_sec, diff_time.tv_nsec / 1000); conf = get_multipath_config(); max_checkint = conf->max_checkint; put_multipath_config(conf); if (diff_time.tv_sec > max_checkint) condlog(1, "path checkers took longer " "than %lu seconds, consider " "increasing max_polling_interval", diff_time.tv_sec); } } post_config_state(DAEMON_IDLE); conf = get_multipath_config(); strict_timing = conf->strict_timing; put_multipath_config(conf); if (!strict_timing) sleep(1); else { timer_tick_it.it_interval.tv_sec = 0; timer_tick_it.it_interval.tv_usec = 0; if (diff_time.tv_nsec) { timer_tick_it.it_value.tv_sec = 0; timer_tick_it.it_value.tv_usec = 1000UL * 1000 * 1000 - diff_time.tv_nsec; } else { timer_tick_it.it_value.tv_sec = 1; timer_tick_it.it_value.tv_usec = 0; } setitimer(ITIMER_REAL, &timer_tick_it, NULL); sigemptyset(&mask); sigaddset(&mask, SIGALRM); condlog(3, "waiting for %lu.%06lu secs", timer_tick_it.it_value.tv_sec, timer_tick_it.it_value.tv_usec); if (sigwait(&mask, &signo) != 0) { condlog(3, "sigwait failed with error %d", errno); conf = get_multipath_config(); conf->strict_timing = 0; put_multipath_config(conf); break; } } } pthread_cleanup_pop(1); return NULL; } int configure (struct vectors * vecs, int start_waiters) { struct multipath * mpp; struct path * pp; vector mpvec; int i, ret; struct config *conf; static int force_reload = FORCE_RELOAD_WEAK; if (!vecs->pathvec && !(vecs->pathvec = vector_alloc())) { condlog(0, "couldn't allocate path vec in configure"); return 1; } if (!vecs->mpvec && !(vecs->mpvec = vector_alloc())) { condlog(0, "couldn't allocate multipath vec in configure"); return 1; } if (!(mpvec = vector_alloc())) { condlog(0, "couldn't allocate new maps vec in configure"); return 1; } /* * probe for current path (from sysfs) and map (from dm) sets */ ret = path_discovery(vecs->pathvec, DI_ALL); if (ret < 0) { condlog(0, "configure failed at path discovery"); return 1; } vector_foreach_slot (vecs->pathvec, pp, i){ conf = get_multipath_config(); if (filter_path(conf, pp) > 0){ vector_del_slot(vecs->pathvec, i); free_path(pp); i--; } else pp->checkint = conf->checkint; put_multipath_config(conf); } if (map_discovery(vecs)) { condlog(0, "configure failed at map discovery"); return 1; } /* * create new set of maps & push changed ones into dm * In the first call, use FORCE_RELOAD_WEAK to avoid making * superfluous ACT_RELOAD ioctls. Later calls are done * with FORCE_RELOAD_YES. */ ret = coalesce_paths(vecs, mpvec, NULL, force_reload, CMD_NONE); if (force_reload == FORCE_RELOAD_WEAK) force_reload = FORCE_RELOAD_YES; if (ret) { condlog(0, "configure failed while coalescing paths"); return 1; } /* * may need to remove some maps which are no longer relevant * e.g., due to blacklist changes in conf file */ if (coalesce_maps(vecs, mpvec)) { condlog(0, "configure failed while coalescing maps"); return 1; } dm_lib_release(); sync_maps_state(mpvec); vector_foreach_slot(mpvec, mpp, i){ remember_wwid(mpp->wwid); update_map_pr(mpp); } /* * purge dm of old maps */ remove_maps(vecs); /* * save new set of maps formed by considering current path state */ vector_free(vecs->mpvec); vecs->mpvec = mpvec; /* * start dm event waiter threads for these new maps */ vector_foreach_slot(vecs->mpvec, mpp, i) { if (setup_multipath(vecs, mpp)) { i--; continue; } if (start_waiters) { if (start_waiter_thread(mpp, vecs)) { remove_map(mpp, vecs, 1); i--; } } } return 0; } int need_to_delay_reconfig(struct vectors * vecs) { struct multipath *mpp; int i; if (!VECTOR_SIZE(vecs->mpvec)) return 0; vector_foreach_slot(vecs->mpvec, mpp, i) { if (mpp->wait_for_udev) return 1; } return 0; } void rcu_free_config(struct rcu_head *head) { struct config *conf = container_of(head, struct config, rcu); free_config(conf); } int reconfigure (struct vectors * vecs) { struct config * old, *conf; conf = load_config(DEFAULT_CONFIGFILE); if (!conf) return 1; /* * free old map and path vectors ... they use old conf state */ if (VECTOR_SIZE(vecs->mpvec)) remove_maps_and_stop_waiters(vecs); free_pathvec(vecs->pathvec, FREE_PATHS); vecs->pathvec = NULL; /* Re-read any timezone changes */ tzset(); dm_drv_version(conf->version, TGT_MPATH); if (verbosity) conf->verbosity = verbosity; if (bindings_read_only) conf->bindings_read_only = bindings_read_only; if (conf->find_multipaths) { condlog(2, "find_multipaths is set: -n is implied"); ignore_new_devs = 1; } if (ignore_new_devs) conf->ignore_new_devs = ignore_new_devs; uxsock_timeout = conf->uxsock_timeout; old = rcu_dereference(multipath_conf); rcu_assign_pointer(multipath_conf, conf); call_rcu(&old->rcu, rcu_free_config); configure(vecs, 1); return 0; } static struct vectors * init_vecs (void) { struct vectors * vecs; vecs = (struct vectors *)MALLOC(sizeof(struct vectors)); if (!vecs) return NULL; pthread_mutex_init(&vecs->lock.mutex, NULL); return vecs; } static void * signal_set(int signo, void (*func) (int)) { int r; struct sigaction sig; struct sigaction osig; sig.sa_handler = func; sigemptyset(&sig.sa_mask); sig.sa_flags = 0; r = sigaction(signo, &sig, &osig); if (r < 0) return (SIG_ERR); else return (osig.sa_handler); } void handle_signals(void) { if (exit_sig) { condlog(2, "exit (signal)"); exit_daemon(); } if (reconfig_sig) { condlog(2, "reconfigure (signal)"); set_config_state(DAEMON_CONFIGURE); } if (log_reset_sig) { condlog(2, "reset log (signal)"); pthread_mutex_lock(&logq_lock); log_reset("multipathd"); pthread_mutex_unlock(&logq_lock); } exit_sig = 0; reconfig_sig = 0; log_reset_sig = 0; } static void sighup (int sig) { reconfig_sig = 1; } static void sigend (int sig) { exit_sig = 1; } static void sigusr1 (int sig) { log_reset_sig = 1; } static void sigusr2 (int sig) { condlog(3, "SIGUSR2 received"); } static void signal_init(void) { sigset_t set; sigemptyset(&set); sigaddset(&set, SIGUSR2); pthread_sigmask(SIG_SETMASK, &set, NULL); signal_set(SIGHUP, sighup); signal_set(SIGUSR1, sigusr1); signal_set(SIGUSR2, sigusr2); signal_set(SIGINT, sigend); signal_set(SIGTERM, sigend); signal_set(SIGPIPE, sigend); } static void setscheduler (void) { int res; static struct sched_param sched_param = { .sched_priority = 99 }; res = sched_setscheduler (0, SCHED_RR, &sched_param); if (res == -1) condlog(LOG_WARNING, "Could not set SCHED_RR at priority 99"); return; } static void set_oom_adj (void) { #ifdef OOM_SCORE_ADJ_MIN int retry = 1; char *file = "/proc/self/oom_score_adj"; int score = OOM_SCORE_ADJ_MIN; #else int retry = 0; char *file = "/proc/self/oom_adj"; int score = OOM_ADJUST_MIN; #endif FILE *fp; struct stat st; char *envp; envp = getenv("OOMScoreAdjust"); if (envp) { condlog(3, "Using systemd provided OOMScoreAdjust"); return; } do { if (stat(file, &st) == 0){ fp = fopen(file, "w"); if (!fp) { condlog(0, "couldn't fopen %s : %s", file, strerror(errno)); return; } fprintf(fp, "%i", score); fclose(fp); return; } if (errno != ENOENT) { condlog(0, "couldn't stat %s : %s", file, strerror(errno)); return; } #ifdef OOM_ADJUST_MIN file = "/proc/self/oom_adj"; score = OOM_ADJUST_MIN; #else retry = 0; #endif } while (retry--); condlog(0, "couldn't adjust oom score"); } static int child (void * param) { pthread_t check_thr, uevent_thr, uxlsnr_thr, uevq_thr; pthread_attr_t log_attr, misc_attr, uevent_attr; struct vectors * vecs; struct multipath * mpp; int i; #ifdef USE_SYSTEMD unsigned long checkint; int startup_done = 0; #endif int rc; int pid_fd = -1; struct config *conf; char *envp; mlockall(MCL_CURRENT | MCL_FUTURE); signal_init(); rcu_init(); setup_thread_attr(&misc_attr, 64 * 1024, 0); setup_thread_attr(&uevent_attr, DEFAULT_UEVENT_STACKSIZE * 1024, 0); setup_thread_attr(&waiter_attr, 32 * 1024, 1); setup_thread_attr(&io_err_stat_attr, 32 * 1024, 1); if (logsink == 1) { setup_thread_attr(&log_attr, 64 * 1024, 0); log_thread_start(&log_attr); pthread_attr_destroy(&log_attr); } pid_fd = pidfile_create(DEFAULT_PIDFILE, daemon_pid); if (pid_fd < 0) { condlog(1, "failed to create pidfile"); if (logsink == 1) log_thread_stop(); exit(1); } post_config_state(DAEMON_START); condlog(2, "--------start up--------"); condlog(2, "read " DEFAULT_CONFIGFILE); conf = load_config(DEFAULT_CONFIGFILE); if (!conf) goto failed; if (verbosity) conf->verbosity = verbosity; if (bindings_read_only) conf->bindings_read_only = bindings_read_only; if (ignore_new_devs) conf->ignore_new_devs = ignore_new_devs; uxsock_timeout = conf->uxsock_timeout; rcu_assign_pointer(multipath_conf, conf); if (init_checkers(conf->multipath_dir)) { condlog(0, "failed to initialize checkers"); goto failed; } if (init_prio(conf->multipath_dir)) { condlog(0, "failed to initialize prioritizers"); goto failed; } setlogmask(LOG_UPTO(conf->verbosity + 3)); envp = getenv("LimitNOFILE"); if (envp) { condlog(2,"Using systemd provided open fds limit of %s", envp); } else if (conf->max_fds) { struct rlimit fd_limit; if (getrlimit(RLIMIT_NOFILE, &fd_limit) < 0) { condlog(0, "can't get open fds limit: %s", strerror(errno)); fd_limit.rlim_cur = 0; fd_limit.rlim_max = 0; } if (fd_limit.rlim_cur < conf->max_fds) { fd_limit.rlim_cur = conf->max_fds; if (fd_limit.rlim_max < conf->max_fds) fd_limit.rlim_max = conf->max_fds; if (setrlimit(RLIMIT_NOFILE, &fd_limit) < 0) { condlog(0, "can't set open fds limit to " "%lu/%lu : %s", fd_limit.rlim_cur, fd_limit.rlim_max, strerror(errno)); } else { condlog(3, "set open fds limit to %lu/%lu", fd_limit.rlim_cur, fd_limit.rlim_max); } } } vecs = gvecs = init_vecs(); if (!vecs) goto failed; setscheduler(); set_oom_adj(); #ifdef USE_SYSTEMD envp = getenv("WATCHDOG_USEC"); if (envp && sscanf(envp, "%lu", &checkint) == 1) { /* Value is in microseconds */ conf->max_checkint = checkint / 1000000; /* Rescale checkint */ if (conf->checkint > conf->max_checkint) conf->checkint = conf->max_checkint; else conf->checkint = conf->max_checkint / 4; condlog(3, "enabling watchdog, interval %d max %d", conf->checkint, conf->max_checkint); use_watchdog = conf->checkint; } #endif /* * Startup done, invalidate configuration */ conf = NULL; /* * Signal start of configuration */ post_config_state(DAEMON_CONFIGURE); init_path_check_interval(vecs); /* * Start uevent listener early to catch events */ if ((rc = pthread_create(&uevent_thr, &uevent_attr, ueventloop, udev))) { condlog(0, "failed to create uevent thread: %d", rc); goto failed; } pthread_attr_destroy(&uevent_attr); if ((rc = pthread_create(&uxlsnr_thr, &misc_attr, uxlsnrloop, vecs))) { condlog(0, "failed to create cli listener: %d", rc); goto failed; } /* * start threads */ rc = start_io_err_stat_thread(vecs); if (rc) goto failed; if ((rc = pthread_create(&check_thr, &misc_attr, checkerloop, vecs))) { condlog(0,"failed to create checker loop thread: %d", rc); goto failed; } if ((rc = pthread_create(&uevq_thr, &misc_attr, uevqloop, vecs))) { condlog(0, "failed to create uevent dispatcher: %d", rc); goto failed; } pthread_attr_destroy(&misc_attr); while (running_state != DAEMON_SHUTDOWN) { pthread_cleanup_push(config_cleanup, NULL); pthread_mutex_lock(&config_lock); if (running_state != DAEMON_CONFIGURE && running_state != DAEMON_SHUTDOWN) { pthread_cond_wait(&config_cond, &config_lock); } pthread_cleanup_pop(1); if (running_state == DAEMON_CONFIGURE) { pthread_cleanup_push(cleanup_lock, &vecs->lock); lock(&vecs->lock); pthread_testcancel(); if (!need_to_delay_reconfig(vecs)) { reconfigure(vecs); } else { conf = get_multipath_config(); conf->delayed_reconfig = 1; put_multipath_config(conf); } lock_cleanup_pop(vecs->lock); post_config_state(DAEMON_IDLE); #ifdef USE_SYSTEMD if (!startup_done) { sd_notify(0, "READY=1"); startup_done = 1; } #endif } } lock(&vecs->lock); conf = get_multipath_config(); if (conf->queue_without_daemon == QUE_NO_DAEMON_OFF) vector_foreach_slot(vecs->mpvec, mpp, i) dm_queue_if_no_path(mpp->alias, 0); put_multipath_config(conf); remove_maps_and_stop_waiters(vecs); unlock(&vecs->lock); stop_io_err_stat_thread(); pthread_cancel(check_thr); pthread_cancel(uevent_thr); pthread_cancel(uxlsnr_thr); pthread_cancel(uevq_thr); pthread_join(check_thr, NULL); pthread_join(uevent_thr, NULL); pthread_join(uxlsnr_thr, NULL); pthread_join(uevq_thr, NULL); lock(&vecs->lock); free_pathvec(vecs->pathvec, FREE_PATHS); vecs->pathvec = NULL; unlock(&vecs->lock); pthread_mutex_destroy(&vecs->lock.mutex); FREE(vecs); vecs = NULL; cleanup_checkers(); cleanup_prio(); dm_lib_release(); dm_lib_exit(); /* We're done here */ condlog(3, "unlink pidfile"); unlink(DEFAULT_PIDFILE); condlog(2, "--------shut down-------"); if (logsink == 1) log_thread_stop(); /* * Freeing config must be done after condlog() and dm_lib_exit(), * because logging functions like dlog() and dm_write_log() * reference the config. */ conf = rcu_dereference(multipath_conf); rcu_assign_pointer(multipath_conf, NULL); call_rcu(&conf->rcu, rcu_free_config); udev_unref(udev); udev = NULL; pthread_attr_destroy(&waiter_attr); pthread_attr_destroy(&io_err_stat_attr); #ifdef _DEBUG_ dbg_free_final(NULL); #endif #ifdef USE_SYSTEMD sd_notify(0, "ERRNO=0"); #endif exit(0); failed: #ifdef USE_SYSTEMD sd_notify(0, "ERRNO=1"); #endif if (pid_fd >= 0) close(pid_fd); exit(1); } static int daemonize(void) { int pid; int dev_null_fd; if( (pid = fork()) < 0){ fprintf(stderr, "Failed first fork : %s\n", strerror(errno)); return -1; } else if (pid != 0) return pid; setsid(); if ( (pid = fork()) < 0) fprintf(stderr, "Failed second fork : %s\n", strerror(errno)); else if (pid != 0) _exit(0); if (chdir("/") < 0) fprintf(stderr, "cannot chdir to '/', continuing\n"); dev_null_fd = open("/dev/null", O_RDWR); if (dev_null_fd < 0){ fprintf(stderr, "cannot open /dev/null for input & output : %s\n", strerror(errno)); _exit(0); } close(STDIN_FILENO); if (dup(dev_null_fd) < 0) { fprintf(stderr, "cannot dup /dev/null to stdin : %s\n", strerror(errno)); _exit(0); } close(STDOUT_FILENO); if (dup(dev_null_fd) < 0) { fprintf(stderr, "cannot dup /dev/null to stdout : %s\n", strerror(errno)); _exit(0); } close(STDERR_FILENO); if (dup(dev_null_fd) < 0) { fprintf(stderr, "cannot dup /dev/null to stderr : %s\n", strerror(errno)); _exit(0); } close(dev_null_fd); daemon_pid = getpid(); return 0; } int main (int argc, char *argv[]) { extern char *optarg; extern int optind; int arg; int err; int foreground = 0; struct config *conf; ANNOTATE_BENIGN_RACE_SIZED(&multipath_conf, sizeof(multipath_conf), "Manipulated through RCU"); ANNOTATE_BENIGN_RACE_SIZED(&running_state, sizeof(running_state), "Suppress complaints about unprotected running_state reads"); ANNOTATE_BENIGN_RACE_SIZED(&uxsock_timeout, sizeof(uxsock_timeout), "Suppress complaints about this scalar variable"); logsink = 1; if (getuid() != 0) { fprintf(stderr, "need to be root\n"); exit(1); } /* make sure we don't lock any path */ if (chdir("/") < 0) fprintf(stderr, "can't chdir to root directory : %s\n", strerror(errno)); umask(umask(077) | 022); pthread_cond_init_mono(&config_cond); udev = udev_new(); libmp_udev_set_sync_support(0); while ((arg = getopt(argc, argv, ":dsv:k::Bn")) != EOF ) { switch(arg) { case 'd': foreground = 1; if (logsink > 0) logsink = 0; //debug=1; /* ### comment me out ### */ break; case 'v': if (sizeof(optarg) > sizeof(char *) || !isdigit(optarg[0])) exit(1); verbosity = atoi(optarg); break; case 's': logsink = -1; break; case 'k': conf = load_config(DEFAULT_CONFIGFILE); if (!conf) exit(1); if (verbosity) conf->verbosity = verbosity; uxsock_timeout = conf->uxsock_timeout; uxclnt(optarg, uxsock_timeout + 100); free_config(conf); exit(0); case 'B': bindings_read_only = 1; break; case 'n': ignore_new_devs = 1; break; default: fprintf(stderr, "Invalid argument '-%c'\n", optopt); exit(1); } } if (optind < argc) { char cmd[CMDSIZE]; char * s = cmd; char * c = s; conf = load_config(DEFAULT_CONFIGFILE); if (!conf) exit(1); if (verbosity) conf->verbosity = verbosity; uxsock_timeout = conf->uxsock_timeout; memset(cmd, 0x0, CMDSIZE); while (optind < argc) { if (strchr(argv[optind], ' ')) c += snprintf(c, s + CMDSIZE - c, "\"%s\" ", argv[optind]); else c += snprintf(c, s + CMDSIZE - c, "%s ", argv[optind]); optind++; } c += snprintf(c, s + CMDSIZE - c, "\n"); uxclnt(s, uxsock_timeout + 100); free_config(conf); exit(0); } if (foreground) { if (!isatty(fileno(stdout))) setbuf(stdout, NULL); err = 0; daemon_pid = getpid(); } else err = daemonize(); if (err < 0) /* error */ exit(1); else if (err > 0) /* parent dies */ exit(0); else /* child lives */ return (child(NULL)); } void * mpath_pr_event_handler_fn (void * pathp ) { struct multipath * mpp; int i, ret, isFound; struct path * pp = (struct path *)pathp; struct prout_param_descriptor *param; struct prin_resp *resp; mpp = pp->mpp; resp = mpath_alloc_prin_response(MPATH_PRIN_RKEY_SA); if (!resp){ condlog(0,"%s Alloc failed for prin response", pp->dev); return NULL; } ret = prin_do_scsi_ioctl(pp->dev, MPATH_PRIN_RKEY_SA, resp, 0); if (ret != MPATH_PR_SUCCESS ) { condlog(0,"%s : pr in read keys service action failed. Error=%d", pp->dev, ret); goto out; } condlog(3, " event pr=%d addlen=%d",resp->prin_descriptor.prin_readkeys.prgeneration, resp->prin_descriptor.prin_readkeys.additional_length ); if (resp->prin_descriptor.prin_readkeys.additional_length == 0 ) { condlog(1, "%s: No key found. Device may not be registered.", pp->dev); ret = MPATH_PR_SUCCESS; goto out; } condlog(2, "Multipath reservation_key: 0x%" PRIx64 " ", get_be64(mpp->reservation_key)); isFound =0; for (i = 0; i < resp->prin_descriptor.prin_readkeys.additional_length/8; i++ ) { condlog(2, "PR IN READKEYS[%d] reservation key:",i); dumpHex((char *)&resp->prin_descriptor.prin_readkeys.key_list[i*8], 8 , -1); if (!memcmp(&mpp->reservation_key, &resp->prin_descriptor.prin_readkeys.key_list[i*8], 8)) { condlog(2, "%s: pr key found in prin readkeys response", mpp->alias); isFound =1; break; } } if (!isFound) { condlog(0, "%s: Either device not registered or ", pp->dev); condlog(0, "host is not authorised for registration. Skip path"); ret = MPATH_PR_OTHER; goto out; } param= malloc(sizeof(struct prout_param_descriptor)); memset(param, 0 , sizeof(struct prout_param_descriptor)); memcpy(param->sa_key, &mpp->reservation_key, 8); param->num_transportid = 0; condlog(3, "device %s:%s", pp->dev, pp->mpp->wwid); ret = prout_do_scsi_ioctl(pp->dev, MPATH_PROUT_REG_IGN_SA, 0, 0, param, 0); if (ret != MPATH_PR_SUCCESS ) { condlog(0,"%s: Reservation registration failed. Error: %d", pp->dev, ret); } mpp->prflag = 1; free(param); out: free(resp); return NULL; } int mpath_pr_event_handle(struct path *pp) { pthread_t thread; int rc; pthread_attr_t attr; struct multipath * mpp; mpp = pp->mpp; if (get_be64(mpp->reservation_key)) return -1; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); rc = pthread_create(&thread, NULL , mpath_pr_event_handler_fn, pp); if (rc) { condlog(0, "%s: ERROR; return code from pthread_create() is %d", pp->dev, rc); return -1; } pthread_attr_destroy(&attr); rc = pthread_join(thread, NULL); return 0; } multipath-tools-0.7.4/multipathd/main.h000066400000000000000000000024151320314174000201340ustar00rootroot00000000000000#ifndef MAIN_H #define MAIN_H #define MAPGCINT 5 enum daemon_status { DAEMON_INIT, DAEMON_START, DAEMON_CONFIGURE, DAEMON_IDLE, DAEMON_RUNNING, DAEMON_SHUTDOWN, }; struct prout_param_descriptor; struct prin_resp; extern pid_t daemon_pid; extern int uxsock_timeout; void exit_daemon(void); const char * daemon_status(void); int need_to_delay_reconfig (struct vectors *); int reconfigure (struct vectors *); int ev_add_path (struct path *, struct vectors *, int); int ev_remove_path (struct path *, struct vectors *, int); int ev_add_map (char *, char *, struct vectors *); int ev_remove_map (char *, char *, int, struct vectors *); void sync_map_state (struct multipath *); int set_config_state(enum daemon_status); void * mpath_alloc_prin_response(int prin_sa); int prin_do_scsi_ioctl(char *, int rq_servact, struct prin_resp * resp, int noisy); void dumpHex(const char * , int len, int no_ascii); int prout_do_scsi_ioctl(char * , int rq_servact, int rq_scope, unsigned int rq_type, struct prout_param_descriptor *param, int noisy); int mpath_pr_event_handle(struct path *pp); void * mpath_pr_event_handler_fn (void * ); int update_map_pr(struct multipath *mpp); void * mpath_pr_event_handler_fn (void * pathp ); void handle_signals(void); #endif /* MAIN_H */ multipath-tools-0.7.4/multipathd/multipathd.8000066400000000000000000000227601320314174000213100ustar00rootroot00000000000000.\" ---------------------------------------------------------------------------- .\" Update the date below if you make any significant change. .\" Make sure there are no errors with: .\" groff -z -wall -b -e -t multipathd/multipathd.8 .\" .\" ---------------------------------------------------------------------------- . .TH MULTIPATHD 8 2016-10-27 Linux . . .\" ---------------------------------------------------------------------------- .SH NAME .\" ---------------------------------------------------------------------------- . multipathd \- Multipath daemon. . . .\" ---------------------------------------------------------------------------- .SH SYNOPSIS .\" ---------------------------------------------------------------------------- . .B multipathd .RB [\| \-d | \-k \|] .RB [\| \-s \|] .RB [\| \-v\ \c .IR verbosity \|] .RB [\| \-B \|] .RB [\| \-n \|] . . .\" ---------------------------------------------------------------------------- .SH DESCRIPTION .\" ---------------------------------------------------------------------------- . The \fBmultipathd\fR daemon is in charge of checking for failed paths. When this happens, it will reconfigure the multipath map the path belongs to, so that this map regains its maximum performance and redundancy. This daemon executes the external \fBmultipath\fR tool when events occur. In turn, the multipath tool signals the multipathd daemon when it is done with devmap reconfiguration, so that it can refresh its failed path list. . . .\" ---------------------------------------------------------------------------- .SH OPTIONS .\" ---------------------------------------------------------------------------- . .TP .B \-d Foreground Mode. Don't daemonize, and print all messages to stdout and stderr. . .TP .B \-s Suppress timestamps. Do not prefix logging messages with a timestamp. . .TP .BI \-v " level" Verbosity level. Print additional information while running multipathd. A level of 0 means only print errors. A level of 3 or greater prints debugging information as well. . .TP .B \-B Read-only bindings file. multipathd will not write to the \fIuser_friendly_names\fR bindings file. If a \fIuser_friendly_name\fR doesn't already exist for a device, it will use its WWID as its alias. . .TP .B \-k multipathd will enter interactive mode. From this mode, the available commands can be viewed by entering '\fIhelp\fR'. When you are finished entering commands, press \fBCTRL-D\fR to quit. . .TP .B \-n Ignore new devices. multipathd will not create a multipath device unless the WWID for the device is already listed in the WWIDs file. . . .\" ---------------------------------------------------------------------------- .SH COMMANDS .\" ---------------------------------------------------------------------------- . .TP The following commands can be used in interactive mode: . .TP .B list|show paths Show the paths that multipathd is monitoring, and their state. . .TP .B list|show paths format $format Show the paths that multipathd is monitoring, using a format string with path format wildcards. . .TP .B list|show maps|multipaths Show the multipath devices that the multipathd is monitoring. . .TP .B list|show maps|multipaths format $format Show the status of all multipath devices that the multipathd is monitoring, using a format string with multipath format wildcards. . .TP .B list|show maps|multipaths status Show the status of all multipath devices that the multipathd is monitoring. . .TP .B list|show maps|multipaths stats Show some statistics of all multipath devices that the multipathd is monitoring. . .TP .B list|show maps|multipaths topology Show the current multipath topology. Same as '\fImultipath \-ll\fR'. . .TP .B list|show topology Show the current multipath topology. Same as '\fImultipath \-ll\fR'. . .TP .B list|show map|multipath $map topology Show topology of a single multipath device specified by $map, for example 36005076303ffc56200000000000010aa. This map could be obtained from '\fIlist maps\fR'. . .TP .B list|show wildcards Show the format wildcards used in interactive commands taking $format. . .TP .B list|show config Show the currently used configuration, derived from default values and values specified within the configuration file \fI/etc/multipath.conf\fR. . .TP .B list|show blacklist Show the currently used blacklist rules, derived from default values and values specified within the configuration file \fI/etc/multipath.conf\fR. . .TP .B list|show devices Show all available block devices by name including the information if they are blacklisted or not. . .TP .B list|show status Show the number of path checkers in each possible state, the number of monitored paths, and whether multipathd is currently handling a uevent. . .TP .B list|show daemon Show the current state of the multipathd daemon. . .TP .B add path $path Add a path to the list of monitored paths. $path is as listed in /sys/block (e.g. sda). . .TP .B remove|del path $path Stop monitoring a path. $path is as listed in /sys/block (e.g. sda). . .TP .B add map|multipath $map Add a multipath device to the list of monitored devices. $map can either be a device-mapper device as listed in /sys/block (e.g. dm-0) or it can be the alias for the multipath device (e.g. mpath1) or the uid of the multipath device (e.g. 36005076303ffc56200000000000010aa). . .TP .B remove|del map|multipath $map Stop monitoring a multipath device. . .TP .B resize map|multipath $map Resizes map $map to the given size. . .TP .B switch|switchgroup map|multipath $map group $group Force a multipath device to switch to a specific path group. $group is the path group index, starting with 1. . .TP .B reconfigure Reconfigures the multipaths. This should be triggered automatically after anyi hotplug event. . .TP .B suspend map|multipath $map Sets map $map into suspend state. . .TP .B resume map|multipath $map Resumes map $map from suspend state. . .TP .B reset map|multipath $map Reassign existing device-mapper table(s) use use the multipath device, instead of its path devices. . .TP .B reload map|multipath $map Reload a multipath device. . .TP .B fail path $path Sets path $path into failed state. . .TP .B reinstate path $path Resumes path $path from failed state. . .TP .B disablequeueing maps|multipaths Disable queueing on all multipath devices. . .TP .B restorequeueing maps|multipaths Restore queueing on all multipath devices. . .TP .B disablequeueing map|multipath $map Disable queuing on multipathed map $map. . .TP .B restorequeueing map|multipath $map Restore queuing on multipahted map $map. . .TP .B forcequeueing daemon Forces multipathd into queue_without_daemon mode, so that no_path_retry queueing will not be disabled when the daemon stops. . .TP .B restorequeueing daemon Restores configured queue_without_daemon mode. . .TP .B map|multipath $map setprstatus Enable persistent reservation management on $map. . .TP .B map|multipath $map unsetprstatus Disable persistent reservation management on $map. . .TP .B map|multipath $map getprstatus Get the current persistent reservation management status of $map. . .TP .B map|multipath $map getprkey Get the current persistent reservation key associated with $map. . .TP .B map|multipath $map setprkey key $key Set the persistent reservation key associated with $map to $key in the \fIprkeys_file\fR. This key will only be used by multipathd if \fIreservation_key\fR is set to \fBfile\fR in \fI/etc/multipath.conf\fR. . .TP .B map|multipath $map unsetprkey Remove the persistent reservation key associated with $map from the \fIprkeys_file\fR. This will only unset the key used by multipathd if \fIreservation_key\fR is set to \fBfile\fR in \fI/etc/multipath.conf\fR. . .TP .B quit|exit End interactive session. . .TP .B shutdown Stop multipathd. . . .\" ---------------------------------------------------------------------------- .SH "SYSTEMD INTEGRATION" .\" ---------------------------------------------------------------------------- . When compiled with systemd support two systemd service files are installed, \fImultipathd.service\fR and \fImultipathd.socket\fR The \fImultipathd.socket\fR service instructs systemd to intercept the CLI command socket, so that any call to the CLI interface will start-up the daemon if required. The \fImultipathd.service\fR file carries the definitions for controlling the multipath daemon. The daemon itself uses the \fBsd_notify\fR(3) interface to communicate with systemd. The following unit keywords are recognized: . .TP .B WatchdogSec= Enables the internal watchdog from systemd. multipath will send a notification via \fBsd_notify\fR(3) to systemd to reset the watchdog. If specified the \fIpolling_interval\fR and \fImax_polling_interval\fR settings will be overridden by the watchdog settings. Please note that systemd prior to version 207 has issues which prevent the systemd-provided watchdog from working correctly. So the watchdog is not enabled per default, but has to be enabled manually by updating the \fImultipathd.service\fR file. . .TP .B OOMScoreAdjust= Overrides the internal OOM adjust mechanism. . .TP .B LimitNOFILE= Overrides the \fImax_fds\fR configuration setting. . . .\" ---------------------------------------------------------------------------- .SH "SEE ALSO" .\" ---------------------------------------------------------------------------- . .BR multipath (8), .BR kpartx (8), .BR sd_notify (3), .BR system.service (5). . . .\" ---------------------------------------------------------------------------- .SH AUTHORS .\" ---------------------------------------------------------------------------- . \fImultipath-tools\fR was developed by Christophe Varoqui and others. .\" EOF multipath-tools-0.7.4/multipathd/multipathd.service000066400000000000000000000013511320314174000225720ustar00rootroot00000000000000[Unit] Description=Device-Mapper Multipath Device Controller Wants=systemd-udev-trigger.service systemd-udev-settle.service Before=iscsi.service iscsid.service lvm2-lvmetad.service lvm2-activation-early.service Before=local-fs-pre.target blk-availability.service After=multipathd.socket systemd-udev-trigger.service systemd-udev-settle.service DefaultDependencies=no Conflicts=shutdown.target ConditionKernelCommandLine=!nompath ConditionKernelCommandLine=!multipath=off [Service] Type=notify NotifyAccess=main LimitCORE=infinity ExecStartPre=-/sbin/modprobe -a scsi_dh_alua scsi_dh_emc scsi_dh_rdac dm-multipath ExecStart=/sbin/multipathd -d -s ExecReload=/sbin/multipathd reconfigure [Install] WantedBy=sysinit.target Also=multipathd.socket multipath-tools-0.7.4/multipathd/multipathd.socket000066400000000000000000000002271320314174000224230ustar00rootroot00000000000000[Unit] Description=multipathd control socket DefaultDependencies=no Before=sockets.target [Socket] ListenStream=@/org/kernel/linux/storage/multipathd multipath-tools-0.7.4/multipathd/pidfile.c000066400000000000000000000033431320314174000206200ustar00rootroot00000000000000#include /* for pid_t */ #include /* for open */ #include /* for EACCESS and EAGAIN */ #include /* for snprintf() */ #include /* for memset() */ #include /* for ftruncate() */ #include /* for fcntl() */ #include "debug.h" #include "pidfile.h" int pidfile_create(const char *pidFile, pid_t pid) { char buf[20]; struct flock lock; int fd, value; if((fd = open(pidFile, O_WRONLY | O_CREAT, (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH))) < 0) { condlog(0, "Cannot open pidfile [%s], error was [%s]", pidFile, strerror(errno)); return -errno; } lock.l_type = F_WRLCK; lock.l_start = 0; lock.l_whence = SEEK_SET; lock.l_len = 0; if (fcntl(fd, F_SETLK, &lock) < 0) { if (errno != EACCES && errno != EAGAIN) condlog(0, "Cannot lock pidfile [%s], error was [%s]", pidFile, strerror(errno)); else condlog(0, "process is already running"); goto fail; } if (ftruncate(fd, 0) < 0) { condlog(0, "Cannot truncate pidfile [%s], error was [%s]", pidFile, strerror(errno)); goto fail; } memset(buf, 0, sizeof(buf)); snprintf(buf, sizeof(buf)-1, "%u", pid); if (write(fd, buf, strlen(buf)) != strlen(buf)) { condlog(0, "Cannot write pid to pidfile [%s], error was [%s]", pidFile, strerror(errno)); goto fail; } if ((value = fcntl(fd, F_GETFD, 0)) < 0) { condlog(0, "Cannot get close-on-exec flag from pidfile [%s], " "error was [%s]", pidFile, strerror(errno)); goto fail; } value |= FD_CLOEXEC; if (fcntl(fd, F_SETFD, value) < 0) { condlog(0, "Cannot set close-on-exec flag from pidfile [%s], " "error was [%s]", pidFile, strerror(errno)); goto fail; } return fd; fail: close(fd); return -errno; } multipath-tools-0.7.4/multipathd/pidfile.h000066400000000000000000000000641320314174000206220ustar00rootroot00000000000000int pidfile_create(const char *pidFile, pid_t pid); multipath-tools-0.7.4/multipathd/uxclnt.c000066400000000000000000000046571320314174000205320ustar00rootroot00000000000000/* * Original author : tridge@samba.org, January 2002 * * Copyright (c) 2005 Christophe Varoqui * Copyright (c) 2005 Benjamin Marzinski, Redhat */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mpath_cmd.h" #include "uxsock.h" #include "memory.h" #include "defaults.h" #include "vector.h" #include "cli.h" static void print_reply(char *s) { if (!s) return; if (isatty(1)) { printf("%s", s); return; } /* strip ANSI color markers */ while (*s != '\0') { if ((*s == 0x1b) && (*(s+1) == '[')) while ((*s++ != 'm') && (*s != '\0')) {}; putchar(*s++); } } static int need_quit(char *str, size_t len) { char *ptr, *start; size_t trimed_len = len; for (ptr = str; trimed_len && isspace(*ptr); trimed_len--, ptr++) ; start = ptr; for (ptr = str + len - 1; trimed_len && isspace(*ptr); trimed_len--, ptr--) ; if ((trimed_len == 4 && !strncmp(start, "exit", 4)) || (trimed_len == 4 && !strncmp(start, "quit", 4))) return 1; return 0; } /* * process the client */ static void process(int fd, unsigned int timeout) { char *line; char *reply; int ret; cli_init(); rl_readline_name = "multipathd"; rl_completion_entry_function = key_generator; while ((line = readline("multipathd> "))) { size_t llen = strlen(line); if (!llen) { free(line); continue; } if (need_quit(line, llen)) break; if (send_packet(fd, line) != 0) break; ret = recv_packet(fd, &reply, timeout); if (ret != 0) break; print_reply(reply); if (line && *line) add_history(line); free(line); FREE(reply); } } static void process_req(int fd, char * inbuf, unsigned int timeout) { char *reply; int ret; if (send_packet(fd, inbuf) != 0) { printf("cannot send packet\n"); return; } ret = recv_packet(fd, &reply, timeout); if (ret < 0) { if (ret == -ETIMEDOUT) printf("timeout receiving packet\n"); else printf("error %d receiving packet\n", ret); } else { printf("%s", reply); FREE(reply); } } /* * entry point */ int uxclnt(char * inbuf, unsigned int timeout) { int fd; fd = mpath_connect(); if (fd == -1) exit(1); if (inbuf) process_req(fd, inbuf, timeout); else process(fd, timeout); mpath_disconnect(fd); return 0; } multipath-tools-0.7.4/multipathd/uxclnt.h000066400000000000000000000000601320314174000205170ustar00rootroot00000000000000int uxclnt(char * inbuf, unsigned int timeout); multipath-tools-0.7.4/multipathd/uxlsnr.c000066400000000000000000000142251320314174000205400ustar00rootroot00000000000000/* * Original author : tridge@samba.org, January 2002 * * Copyright (c) 2005 Christophe Varoqui * Copyright (c) 2005 Benjamin Marzinski, Redhat */ /* * A simple domain socket listener */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "checkers.h" #include "memory.h" #include "debug.h" #include "vector.h" #include "structs.h" #include "structs_vec.h" #include "uxsock.h" #include "defaults.h" #include "config.h" #include "mpath_cmd.h" #include "time-util.h" #include "main.h" #include "cli.h" #include "uxlsnr.h" struct timespec sleep_time = {5, 0}; struct client { struct list_head node; int fd; }; #define MIN_POLLS 1023 LIST_HEAD(clients); pthread_mutex_t client_lock = PTHREAD_MUTEX_INITIALIZER; struct pollfd *polls; static bool _socket_client_is_root(int fd); static bool _socket_client_is_root(int fd) { socklen_t len = 0; struct ucred uc; len = sizeof(struct ucred); if ((fd >= 0) && (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &uc, &len) == 0) && (uc.uid == 0)) return true; /* Treat error as not root client */ return false; } /* * handle a new client joining */ static void new_client(int ux_sock) { struct client *c; struct sockaddr addr; socklen_t len = sizeof(addr); int fd; fd = accept(ux_sock, &addr, &len); if (fd == -1) return; c = (struct client *)MALLOC(sizeof(*c)); if (!c) { close(fd); return; } memset(c, 0, sizeof(*c)); INIT_LIST_HEAD(&c->node); c->fd = fd; /* put it in our linked list */ pthread_mutex_lock(&client_lock); list_add_tail(&c->node, &clients); pthread_mutex_unlock(&client_lock); } /* * kill off a dead client */ static void dead_client(struct client *c) { pthread_mutex_lock(&client_lock); list_del_init(&c->node); pthread_mutex_unlock(&client_lock); close(c->fd); c->fd = -1; FREE(c); } void free_polls (void) { if (polls) FREE(polls); } void check_timeout(struct timespec start_time, char *inbuf, unsigned int timeout) { struct timespec diff_time, end_time; if (start_time.tv_sec && clock_gettime(CLOCK_MONOTONIC, &end_time) == 0) { unsigned long msecs; timespecsub(&end_time, &start_time, &diff_time); msecs = diff_time.tv_sec * 1000 + diff_time.tv_nsec / (1000 * 1000); if (msecs > timeout) condlog(2, "cli cmd '%s' timeout reached " "after %lu.%06lu secs", inbuf, diff_time.tv_sec, diff_time.tv_nsec / 1000); } } void uxsock_cleanup(void *arg) { cli_exit(); free_polls(); } /* * entry point */ void * uxsock_listen(uxsock_trigger_fn uxsock_trigger, void * trigger_data) { int ux_sock; int rlen; char *inbuf; char *reply; sigset_t mask; int old_clients = MIN_POLLS; ux_sock = ux_socket_listen(DEFAULT_SOCKET); if (ux_sock == -1) { condlog(1, "could not create uxsock: %d", errno); return NULL; } pthread_cleanup_push(uxsock_cleanup, NULL); condlog(3, "uxsock: startup listener"); polls = (struct pollfd *)MALLOC((MIN_POLLS + 1) * sizeof(struct pollfd)); if (!polls) { condlog(0, "uxsock: failed to allocate poll fds"); return NULL; } sigemptyset(&mask); sigaddset(&mask, SIGINT); sigaddset(&mask, SIGTERM); sigaddset(&mask, SIGHUP); sigaddset(&mask, SIGUSR1); while (1) { struct client *c, *tmp; int i, poll_count, num_clients; /* setup for a poll */ pthread_mutex_lock(&client_lock); num_clients = 0; list_for_each_entry(c, &clients, node) { num_clients++; } if (num_clients != old_clients) { struct pollfd *new; if (num_clients <= MIN_POLLS && old_clients > MIN_POLLS) { new = REALLOC(polls, (1 + MIN_POLLS) * sizeof(struct pollfd)); } else if (num_clients <= MIN_POLLS && old_clients <= MIN_POLLS) { new = polls; } else { new = REALLOC(polls, (1+num_clients) * sizeof(struct pollfd)); } if (!new) { pthread_mutex_unlock(&client_lock); condlog(0, "%s: failed to realloc %d poll fds", "uxsock", 1 + num_clients); sched_yield(); continue; } old_clients = num_clients; polls = new; } polls[0].fd = ux_sock; polls[0].events = POLLIN; /* setup the clients */ i = 1; list_for_each_entry(c, &clients, node) { polls[i].fd = c->fd; polls[i].events = POLLIN; i++; } pthread_mutex_unlock(&client_lock); /* most of our life is spent in this call */ poll_count = ppoll(polls, i, &sleep_time, &mask); if (poll_count == -1) { if (errno == EINTR) { handle_signals(); continue; } /* something went badly wrong! */ condlog(0, "uxsock: poll failed with %d", errno); break; } if (poll_count == 0) { handle_signals(); continue; } /* see if a client wants to speak to us */ for (i = 1; i < num_clients + 1; i++) { if (polls[i].revents & POLLIN) { struct timespec start_time; c = NULL; pthread_mutex_lock(&client_lock); list_for_each_entry(tmp, &clients, node) { if (tmp->fd == polls[i].fd) { c = tmp; break; } } pthread_mutex_unlock(&client_lock); if (!c) { condlog(4, "cli%d: new fd %d", i, polls[i].fd); continue; } if (clock_gettime(CLOCK_MONOTONIC, &start_time) != 0) start_time.tv_sec = 0; if (recv_packet_from_client(c->fd, &inbuf, uxsock_timeout) != 0) { dead_client(c); continue; } if (!inbuf) { condlog(4, "recv_packet_from_client " "get null request"); continue; } condlog(4, "cli[%d]: Got request [%s]", i, inbuf); uxsock_trigger(inbuf, &reply, &rlen, _socket_client_is_root(c->fd), trigger_data); if (reply) { if (send_packet(c->fd, reply) != 0) { dead_client(c); } else { condlog(4, "cli[%d]: " "Reply [%d bytes]", i, rlen); } FREE(reply); reply = NULL; } check_timeout(start_time, inbuf, uxsock_timeout); FREE(inbuf); } } /* see if we got a new client */ if (polls[0].revents & POLLIN) { new_client(ux_sock); } } pthread_cleanup_pop(1); close(ux_sock); return NULL; } multipath-tools-0.7.4/multipathd/uxlsnr.h000066400000000000000000000003371320314174000205440ustar00rootroot00000000000000#ifndef _UXLSNR_H #define _UXLSNR_H #include typedef int (uxsock_trigger_fn)(char *, char **, int *, bool, void *); void * uxsock_listen(uxsock_trigger_fn uxsock_trigger, void * trigger_data); #endif multipath-tools-0.7.4/third-party/000077500000000000000000000000001320314174000171315ustar00rootroot00000000000000multipath-tools-0.7.4/third-party/valgrind/000077500000000000000000000000001320314174000207375ustar00rootroot00000000000000multipath-tools-0.7.4/third-party/valgrind/drd.h000066400000000000000000000547061320314174000216750ustar00rootroot00000000000000/* ---------------------------------------------------------------- Notice that the following BSD-style license applies to this one file (drd.h) only. The rest of Valgrind is licensed under the terms of the GNU General Public License, version 2, unless otherwise indicated. See the COPYING file in the source distribution for details. ---------------------------------------------------------------- This file is part of DRD, a Valgrind tool for verification of multithreaded programs. Copyright (C) 2006-2017 Bart Van Assche . All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 3. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 4. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------- Notice that the above BSD-style license applies to this one file (drd.h) only. The entire rest of Valgrind is licensed under the terms of the GNU General Public License, version 2. See the COPYING file in the source distribution for details. ---------------------------------------------------------------- */ #ifndef __VALGRIND_DRD_H #define __VALGRIND_DRD_H #include "valgrind.h" /** Obtain the thread ID assigned by Valgrind's core. */ #define DRD_GET_VALGRIND_THREADID \ (unsigned)VALGRIND_DO_CLIENT_REQUEST_EXPR(0, \ VG_USERREQ__DRD_GET_VALGRIND_THREAD_ID, \ 0, 0, 0, 0, 0) /** Obtain the thread ID assigned by DRD. */ #define DRD_GET_DRD_THREADID \ (unsigned)VALGRIND_DO_CLIENT_REQUEST_EXPR(0, \ VG_USERREQ__DRD_GET_DRD_THREAD_ID, \ 0, 0, 0, 0, 0) /** Tell DRD not to complain about data races for the specified variable. */ #define DRD_IGNORE_VAR(x) ANNOTATE_BENIGN_RACE_SIZED(&(x), sizeof(x), "") /** Tell DRD to no longer ignore data races for the specified variable. */ #define DRD_STOP_IGNORING_VAR(x) \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__DRD_FINISH_SUPPRESSION, \ &(x), sizeof(x), 0, 0, 0) /** * Tell DRD to trace all memory accesses for the specified variable * until the memory that was allocated for the variable is freed. */ #define DRD_TRACE_VAR(x) \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__DRD_START_TRACE_ADDR, \ &(x), sizeof(x), 0, 0, 0) /** * Tell DRD to stop tracing memory accesses for the specified variable. */ #define DRD_STOP_TRACING_VAR(x) \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__DRD_STOP_TRACE_ADDR, \ &(x), sizeof(x), 0, 0, 0) /** * @defgroup RaceDetectionAnnotations Data race detection annotations. * * @see See also the source file producer-consumer. */ #define ANNOTATE_PCQ_CREATE(pcq) do { } while(0) /** Tell DRD that a FIFO queue has been destroyed. */ #define ANNOTATE_PCQ_DESTROY(pcq) do { } while(0) /** * Tell DRD that an element has been added to the FIFO queue at address pcq. */ #define ANNOTATE_PCQ_PUT(pcq) do { } while(0) /** * Tell DRD that an element has been removed from the FIFO queue at address pcq, * and that DRD should insert a happens-before relationship between the memory * accesses that occurred before the corresponding ANNOTATE_PCQ_PUT(pcq) * annotation and the memory accesses after this annotation. Correspondence * between PUT and GET annotations happens in FIFO order. Since locking * of the queue is needed anyway to add elements to or to remove elements from * the queue, for DRD all four FIFO annotations are defined as no-ops. */ #define ANNOTATE_PCQ_GET(pcq) do { } while(0) /** * Tell DRD that data races at the specified address are expected and must not * be reported. */ #define ANNOTATE_BENIGN_RACE(addr, descr) \ ANNOTATE_BENIGN_RACE_SIZED(addr, sizeof(*addr), descr) /* Same as ANNOTATE_BENIGN_RACE(addr, descr), but applies to the memory range [addr, addr + size). */ #define ANNOTATE_BENIGN_RACE_SIZED(addr, size, descr) \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__DRD_START_SUPPRESSION, \ addr, size, 0, 0, 0) /** Tell DRD to ignore all reads performed by the current thread. */ #define ANNOTATE_IGNORE_READS_BEGIN() \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__DRD_RECORD_LOADS, \ 0, 0, 0, 0, 0); /** Tell DRD to no longer ignore the reads performed by the current thread. */ #define ANNOTATE_IGNORE_READS_END() \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__DRD_RECORD_LOADS, \ 1, 0, 0, 0, 0); /** Tell DRD to ignore all writes performed by the current thread. */ #define ANNOTATE_IGNORE_WRITES_BEGIN() \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__DRD_RECORD_STORES, \ 0, 0, 0, 0, 0) /** Tell DRD to no longer ignore the writes performed by the current thread. */ #define ANNOTATE_IGNORE_WRITES_END() \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__DRD_RECORD_STORES, \ 1, 0, 0, 0, 0) /** Tell DRD to ignore all memory accesses performed by the current thread. */ #define ANNOTATE_IGNORE_READS_AND_WRITES_BEGIN() \ do { ANNOTATE_IGNORE_READS_BEGIN(); ANNOTATE_IGNORE_WRITES_BEGIN(); } while(0) /** * Tell DRD to no longer ignore the memory accesses performed by the current * thread. */ #define ANNOTATE_IGNORE_READS_AND_WRITES_END() \ do { ANNOTATE_IGNORE_READS_END(); ANNOTATE_IGNORE_WRITES_END(); } while(0) /** * Tell DRD that size bytes starting at addr has been allocated by a custom * memory allocator. */ #define ANNOTATE_NEW_MEMORY(addr, size) \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__DRD_CLEAN_MEMORY, \ addr, size, 0, 0, 0) /** Ask DRD to report every access to the specified address. */ #define ANNOTATE_TRACE_MEMORY(addr) DRD_TRACE_VAR(*(char*)(addr)) /** * Tell DRD to assign the specified name to the current thread. This name will * be used in error messages printed by DRD. */ #define ANNOTATE_THREAD_NAME(name) \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__DRD_SET_THREAD_NAME, \ name, 0, 0, 0, 0) /*@}*/ /* !! ABIWARNING !! ABIWARNING !! ABIWARNING !! ABIWARNING !! This enum comprises an ABI exported by Valgrind to programs which use client requests. DO NOT CHANGE THE ORDER OF THESE ENTRIES, NOR DELETE ANY -- add new ones at the end. */ enum { /* Ask the DRD tool to discard all information about memory accesses */ /* and client objects for the specified range. This client request is */ /* binary compatible with the similarly named Helgrind client request. */ VG_USERREQ__DRD_CLEAN_MEMORY = VG_USERREQ_TOOL_BASE('H','G'), /* args: Addr, SizeT. */ /* Ask the DRD tool the thread ID assigned by Valgrind. */ VG_USERREQ__DRD_GET_VALGRIND_THREAD_ID = VG_USERREQ_TOOL_BASE('D','R'), /* args: none. */ /* Ask the DRD tool the thread ID assigned by DRD. */ VG_USERREQ__DRD_GET_DRD_THREAD_ID, /* args: none. */ /* To tell the DRD tool to suppress data race detection on the */ /* specified address range. */ VG_USERREQ__DRD_START_SUPPRESSION, /* args: start address, size in bytes */ /* To tell the DRD tool no longer to suppress data race detection on */ /* the specified address range. */ VG_USERREQ__DRD_FINISH_SUPPRESSION, /* args: start address, size in bytes */ /* To ask the DRD tool to trace all accesses to the specified range. */ VG_USERREQ__DRD_START_TRACE_ADDR, /* args: Addr, SizeT. */ /* To ask the DRD tool to stop tracing accesses to the specified range. */ VG_USERREQ__DRD_STOP_TRACE_ADDR, /* args: Addr, SizeT. */ /* Tell DRD whether or not to record memory loads in the calling thread. */ VG_USERREQ__DRD_RECORD_LOADS, /* args: Bool. */ /* Tell DRD whether or not to record memory stores in the calling thread. */ VG_USERREQ__DRD_RECORD_STORES, /* args: Bool. */ /* Set the name of the thread that performs this client request. */ VG_USERREQ__DRD_SET_THREAD_NAME, /* args: null-terminated character string. */ /* Tell DRD that a DRD annotation has not yet been implemented. */ VG_USERREQ__DRD_ANNOTATION_UNIMP, /* args: char*. */ /* Tell DRD that a user-defined semaphore synchronization object * is about to be created. */ VG_USERREQ__DRD_ANNOTATE_SEM_INIT_PRE, /* args: Addr, UInt value. */ /* Tell DRD that a user-defined semaphore synchronization object * has been destroyed. */ VG_USERREQ__DRD_ANNOTATE_SEM_DESTROY_POST, /* args: Addr. */ /* Tell DRD that a user-defined semaphore synchronization * object is going to be acquired (semaphore wait). */ VG_USERREQ__DRD_ANNOTATE_SEM_WAIT_PRE, /* args: Addr. */ /* Tell DRD that a user-defined semaphore synchronization * object has been acquired (semaphore wait). */ VG_USERREQ__DRD_ANNOTATE_SEM_WAIT_POST, /* args: Addr. */ /* Tell DRD that a user-defined semaphore synchronization * object is about to be released (semaphore post). */ VG_USERREQ__DRD_ANNOTATE_SEM_POST_PRE, /* args: Addr. */ /* Tell DRD to ignore the inter-thread ordering introduced by a mutex. */ VG_USERREQ__DRD_IGNORE_MUTEX_ORDERING, /* args: Addr. */ /* Tell DRD that a user-defined reader-writer synchronization object * has been created. */ VG_USERREQ__DRD_ANNOTATE_RWLOCK_CREATE = VG_USERREQ_TOOL_BASE('H','G') + 256 + 14, /* args: Addr. */ /* Tell DRD that a user-defined reader-writer synchronization object * is about to be destroyed. */ VG_USERREQ__DRD_ANNOTATE_RWLOCK_DESTROY = VG_USERREQ_TOOL_BASE('H','G') + 256 + 15, /* args: Addr. */ /* Tell DRD that a lock on a user-defined reader-writer synchronization * object has been acquired. */ VG_USERREQ__DRD_ANNOTATE_RWLOCK_ACQUIRED = VG_USERREQ_TOOL_BASE('H','G') + 256 + 17, /* args: Addr, Int is_rw. */ /* Tell DRD that a lock on a user-defined reader-writer synchronization * object is about to be released. */ VG_USERREQ__DRD_ANNOTATE_RWLOCK_RELEASED = VG_USERREQ_TOOL_BASE('H','G') + 256 + 18, /* args: Addr, Int is_rw. */ /* Tell DRD that a Helgrind annotation has not yet been implemented. */ VG_USERREQ__HELGRIND_ANNOTATION_UNIMP = VG_USERREQ_TOOL_BASE('H','G') + 256 + 32, /* args: char*. */ /* Tell DRD to insert a happens-before annotation. */ VG_USERREQ__DRD_ANNOTATE_HAPPENS_BEFORE = VG_USERREQ_TOOL_BASE('H','G') + 256 + 33, /* args: Addr. */ /* Tell DRD to insert a happens-after annotation. */ VG_USERREQ__DRD_ANNOTATE_HAPPENS_AFTER = VG_USERREQ_TOOL_BASE('H','G') + 256 + 34, /* args: Addr. */ }; /** * @addtogroup RaceDetectionAnnotations */ /*@{*/ #ifdef __cplusplus /* ANNOTATE_UNPROTECTED_READ is the preferred way to annotate racy reads. Instead of doing ANNOTATE_IGNORE_READS_BEGIN(); ... = x; ANNOTATE_IGNORE_READS_END(); one can use ... = ANNOTATE_UNPROTECTED_READ(x); */ template inline T ANNOTATE_UNPROTECTED_READ(const volatile T& x) { ANNOTATE_IGNORE_READS_BEGIN(); const T result = x; ANNOTATE_IGNORE_READS_END(); return result; } /* Apply ANNOTATE_BENIGN_RACE_SIZED to a static variable. */ #define ANNOTATE_BENIGN_RACE_STATIC(static_var, description) \ namespace { \ static class static_var##_annotator \ { \ public: \ static_var##_annotator() \ { \ ANNOTATE_BENIGN_RACE_SIZED(&static_var, sizeof(static_var), \ #static_var ": " description); \ } \ } the_##static_var##_annotator; \ } #endif /*@}*/ #endif /* __VALGRIND_DRD_H */ multipath-tools-0.7.4/third-party/valgrind/valgrind.h000066400000000000000000013751341320314174000227340ustar00rootroot00000000000000/* -*- c -*- ---------------------------------------------------------------- Notice that the following BSD-style license applies to this one file (valgrind.h) only. The rest of Valgrind is licensed under the terms of the GNU General Public License, version 2, unless otherwise indicated. See the COPYING file in the source distribution for details. ---------------------------------------------------------------- This file is part of Valgrind, a dynamic binary instrumentation framework. Copyright (C) 2000-2017 Julian Seward. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 3. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 4. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------- Notice that the above BSD-style license applies to this one file (valgrind.h) only. The entire rest of Valgrind is licensed under the terms of the GNU General Public License, version 2. See the COPYING file in the source distribution for details. ---------------------------------------------------------------- */ /* This file is for inclusion into client (your!) code. You can use these macros to manipulate and query Valgrind's execution inside your own programs. The resulting executables will still run without Valgrind, just a little bit more slowly than they otherwise would, but otherwise unchanged. When not running on valgrind, each client request consumes very few (eg. 7) instructions, so the resulting performance loss is negligible unless you plan to execute client requests millions of times per second. Nevertheless, if that is still a problem, you can compile with the NVALGRIND symbol defined (gcc -DNVALGRIND) so that client requests are not even compiled in. */ #ifndef __VALGRIND_H #define __VALGRIND_H /* ------------------------------------------------------------------ */ /* VERSION NUMBER OF VALGRIND */ /* ------------------------------------------------------------------ */ /* Specify Valgrind's version number, so that user code can conditionally compile based on our version number. Note that these were introduced at version 3.6 and so do not exist in version 3.5 or earlier. The recommended way to use them to check for "version X.Y or later" is (eg) #if defined(__VALGRIND_MAJOR__) && defined(__VALGRIND_MINOR__) \ && (__VALGRIND_MAJOR__ > 3 \ || (__VALGRIND_MAJOR__ == 3 && __VALGRIND_MINOR__ >= 6)) */ #define __VALGRIND_MAJOR__ 3 #define __VALGRIND_MINOR__ 13 #include /* Nb: this file might be included in a file compiled with -ansi. So we can't use C++ style "//" comments nor the "asm" keyword (instead use "__asm__"). */ /* Derive some tags indicating what the target platform is. Note that in this file we're using the compiler's CPP symbols for identifying architectures, which are different to the ones we use within the rest of Valgrind. Note, __powerpc__ is active for both 32 and 64-bit PPC, whereas __powerpc64__ is only active for the latter (on Linux, that is). Misc note: how to find out what's predefined in gcc by default: gcc -Wp,-dM somefile.c */ #undef PLAT_x86_darwin #undef PLAT_amd64_darwin #undef PLAT_x86_win32 #undef PLAT_amd64_win64 #undef PLAT_x86_linux #undef PLAT_amd64_linux #undef PLAT_ppc32_linux #undef PLAT_ppc64be_linux #undef PLAT_ppc64le_linux #undef PLAT_arm_linux #undef PLAT_arm64_linux #undef PLAT_s390x_linux #undef PLAT_mips32_linux #undef PLAT_mips64_linux #undef PLAT_x86_solaris #undef PLAT_amd64_solaris #if defined(__APPLE__) && defined(__i386__) # define PLAT_x86_darwin 1 #elif defined(__APPLE__) && defined(__x86_64__) # define PLAT_amd64_darwin 1 #elif (defined(__MINGW32__) && !defined(__MINGW64__)) \ || defined(__CYGWIN32__) \ || (defined(_WIN32) && defined(_M_IX86)) # define PLAT_x86_win32 1 #elif defined(__MINGW64__) \ || (defined(_WIN64) && defined(_M_X64)) # define PLAT_amd64_win64 1 #elif defined(__linux__) && defined(__i386__) # define PLAT_x86_linux 1 #elif defined(__linux__) && defined(__x86_64__) && !defined(__ILP32__) # define PLAT_amd64_linux 1 #elif defined(__linux__) && defined(__powerpc__) && !defined(__powerpc64__) # define PLAT_ppc32_linux 1 #elif defined(__linux__) && defined(__powerpc__) && defined(__powerpc64__) && _CALL_ELF != 2 /* Big Endian uses ELF version 1 */ # define PLAT_ppc64be_linux 1 #elif defined(__linux__) && defined(__powerpc__) && defined(__powerpc64__) && _CALL_ELF == 2 /* Little Endian uses ELF version 2 */ # define PLAT_ppc64le_linux 1 #elif defined(__linux__) && defined(__arm__) && !defined(__aarch64__) # define PLAT_arm_linux 1 #elif defined(__linux__) && defined(__aarch64__) && !defined(__arm__) # define PLAT_arm64_linux 1 #elif defined(__linux__) && defined(__s390__) && defined(__s390x__) # define PLAT_s390x_linux 1 #elif defined(__linux__) && defined(__mips__) && (__mips==64) # define PLAT_mips64_linux 1 #elif defined(__linux__) && defined(__mips__) && (__mips!=64) # define PLAT_mips32_linux 1 #elif defined(__sun) && defined(__i386__) # define PLAT_x86_solaris 1 #elif defined(__sun) && defined(__x86_64__) # define PLAT_amd64_solaris 1 #else /* If we're not compiling for our target platform, don't generate any inline asms. */ # if !defined(NVALGRIND) # define NVALGRIND 1 # endif #endif /* ------------------------------------------------------------------ */ /* ARCHITECTURE SPECIFICS for SPECIAL INSTRUCTIONS. There is nothing */ /* in here of use to end-users -- skip to the next section. */ /* ------------------------------------------------------------------ */ /* * VALGRIND_DO_CLIENT_REQUEST(): a statement that invokes a Valgrind client * request. Accepts both pointers and integers as arguments. * * VALGRIND_DO_CLIENT_REQUEST_STMT(): a statement that invokes a Valgrind * client request that does not return a value. * VALGRIND_DO_CLIENT_REQUEST_EXPR(): a C expression that invokes a Valgrind * client request and whose value equals the client request result. Accepts * both pointers and integers as arguments. Note that such calls are not * necessarily pure functions -- they may have side effects. */ #define VALGRIND_DO_CLIENT_REQUEST(_zzq_rlval, _zzq_default, \ _zzq_request, _zzq_arg1, _zzq_arg2, \ _zzq_arg3, _zzq_arg4, _zzq_arg5) \ do { (_zzq_rlval) = VALGRIND_DO_CLIENT_REQUEST_EXPR((_zzq_default), \ (_zzq_request), (_zzq_arg1), (_zzq_arg2), \ (_zzq_arg3), (_zzq_arg4), (_zzq_arg5)); } while (0) #define VALGRIND_DO_CLIENT_REQUEST_STMT(_zzq_request, _zzq_arg1, \ _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ do { (void) VALGRIND_DO_CLIENT_REQUEST_EXPR(0, \ (_zzq_request), (_zzq_arg1), (_zzq_arg2), \ (_zzq_arg3), (_zzq_arg4), (_zzq_arg5)); } while (0) #if defined(NVALGRIND) /* Define NVALGRIND to completely remove the Valgrind magic sequence from the compiled code (analogous to NDEBUG's effects on assert()) */ #define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ _zzq_default, _zzq_request, \ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ (_zzq_default) #else /* ! NVALGRIND */ /* The following defines the magic code sequences which the JITter spots and handles magically. Don't look too closely at them as they will rot your brain. The assembly code sequences for all architectures is in this one file. This is because this file must be stand-alone, and we don't want to have multiple files. For VALGRIND_DO_CLIENT_REQUEST, we must ensure that the default value gets put in the return slot, so that everything works when this is executed not under Valgrind. Args are passed in a memory block, and so there's no intrinsic limit to the number that could be passed, but it's currently five. The macro args are: _zzq_rlval result lvalue _zzq_default default value (result returned when running on real CPU) _zzq_request request code _zzq_arg1..5 request params The other two macros are used to support function wrapping, and are a lot simpler. VALGRIND_GET_NR_CONTEXT returns the value of the guest's NRADDR pseudo-register and whatever other information is needed to safely run the call original from the wrapper: on ppc64-linux, the R2 value at the divert point is also needed. This information is abstracted into a user-visible type, OrigFn. VALGRIND_CALL_NOREDIR_* behaves the same as the following on the guest, but guarantees that the branch instruction will not be redirected: x86: call *%eax, amd64: call *%rax, ppc32/ppc64: branch-and-link-to-r11. VALGRIND_CALL_NOREDIR is just text, not a complete inline asm, since it needs to be combined with more magic inline asm stuff to be useful. */ /* ----------------- x86-{linux,darwin,solaris} ---------------- */ #if defined(PLAT_x86_linux) || defined(PLAT_x86_darwin) \ || (defined(PLAT_x86_win32) && defined(__GNUC__)) \ || defined(PLAT_x86_solaris) typedef struct { unsigned int nraddr; /* where's the code? */ } OrigFn; #define __SPECIAL_INSTRUCTION_PREAMBLE \ "roll $3, %%edi ; roll $13, %%edi\n\t" \ "roll $29, %%edi ; roll $19, %%edi\n\t" #define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ _zzq_default, _zzq_request, \ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ __extension__ \ ({volatile unsigned int _zzq_args[6]; \ volatile unsigned int _zzq_result; \ _zzq_args[0] = (unsigned int)(_zzq_request); \ _zzq_args[1] = (unsigned int)(_zzq_arg1); \ _zzq_args[2] = (unsigned int)(_zzq_arg2); \ _zzq_args[3] = (unsigned int)(_zzq_arg3); \ _zzq_args[4] = (unsigned int)(_zzq_arg4); \ _zzq_args[5] = (unsigned int)(_zzq_arg5); \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ /* %EDX = client_request ( %EAX ) */ \ "xchgl %%ebx,%%ebx" \ : "=d" (_zzq_result) \ : "a" (&_zzq_args[0]), "0" (_zzq_default) \ : "cc", "memory" \ ); \ _zzq_result; \ }) #define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ volatile unsigned int __addr; \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ /* %EAX = guest_NRADDR */ \ "xchgl %%ecx,%%ecx" \ : "=a" (__addr) \ : \ : "cc", "memory" \ ); \ _zzq_orig->nraddr = __addr; \ } #define VALGRIND_CALL_NOREDIR_EAX \ __SPECIAL_INSTRUCTION_PREAMBLE \ /* call-noredir *%EAX */ \ "xchgl %%edx,%%edx\n\t" #define VALGRIND_VEX_INJECT_IR() \ do { \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ "xchgl %%edi,%%edi\n\t" \ : : : "cc", "memory" \ ); \ } while (0) #endif /* PLAT_x86_linux || PLAT_x86_darwin || (PLAT_x86_win32 && __GNUC__) || PLAT_x86_solaris */ /* ------------------------- x86-Win32 ------------------------- */ #if defined(PLAT_x86_win32) && !defined(__GNUC__) typedef struct { unsigned int nraddr; /* where's the code? */ } OrigFn; #if defined(_MSC_VER) #define __SPECIAL_INSTRUCTION_PREAMBLE \ __asm rol edi, 3 __asm rol edi, 13 \ __asm rol edi, 29 __asm rol edi, 19 #define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ _zzq_default, _zzq_request, \ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ valgrind_do_client_request_expr((uintptr_t)(_zzq_default), \ (uintptr_t)(_zzq_request), (uintptr_t)(_zzq_arg1), \ (uintptr_t)(_zzq_arg2), (uintptr_t)(_zzq_arg3), \ (uintptr_t)(_zzq_arg4), (uintptr_t)(_zzq_arg5)) static __inline uintptr_t valgrind_do_client_request_expr(uintptr_t _zzq_default, uintptr_t _zzq_request, uintptr_t _zzq_arg1, uintptr_t _zzq_arg2, uintptr_t _zzq_arg3, uintptr_t _zzq_arg4, uintptr_t _zzq_arg5) { volatile uintptr_t _zzq_args[6]; volatile unsigned int _zzq_result; _zzq_args[0] = (uintptr_t)(_zzq_request); _zzq_args[1] = (uintptr_t)(_zzq_arg1); _zzq_args[2] = (uintptr_t)(_zzq_arg2); _zzq_args[3] = (uintptr_t)(_zzq_arg3); _zzq_args[4] = (uintptr_t)(_zzq_arg4); _zzq_args[5] = (uintptr_t)(_zzq_arg5); __asm { __asm lea eax, _zzq_args __asm mov edx, _zzq_default __SPECIAL_INSTRUCTION_PREAMBLE /* %EDX = client_request ( %EAX ) */ __asm xchg ebx,ebx __asm mov _zzq_result, edx } return _zzq_result; } #define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ volatile unsigned int __addr; \ __asm { __SPECIAL_INSTRUCTION_PREAMBLE \ /* %EAX = guest_NRADDR */ \ __asm xchg ecx,ecx \ __asm mov __addr, eax \ } \ _zzq_orig->nraddr = __addr; \ } #define VALGRIND_CALL_NOREDIR_EAX ERROR #define VALGRIND_VEX_INJECT_IR() \ do { \ __asm { __SPECIAL_INSTRUCTION_PREAMBLE \ __asm xchg edi,edi \ } \ } while (0) #else #error Unsupported compiler. #endif #endif /* PLAT_x86_win32 */ /* ----------------- amd64-{linux,darwin,solaris} --------------- */ #if defined(PLAT_amd64_linux) || defined(PLAT_amd64_darwin) \ || defined(PLAT_amd64_solaris) \ || (defined(PLAT_amd64_win64) && defined(__GNUC__)) typedef struct { unsigned long int nraddr; /* where's the code? */ } OrigFn; #define __SPECIAL_INSTRUCTION_PREAMBLE \ "rolq $3, %%rdi ; rolq $13, %%rdi\n\t" \ "rolq $61, %%rdi ; rolq $51, %%rdi\n\t" #define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ _zzq_default, _zzq_request, \ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ __extension__ \ ({ volatile unsigned long int _zzq_args[6]; \ volatile unsigned long int _zzq_result; \ _zzq_args[0] = (unsigned long int)(_zzq_request); \ _zzq_args[1] = (unsigned long int)(_zzq_arg1); \ _zzq_args[2] = (unsigned long int)(_zzq_arg2); \ _zzq_args[3] = (unsigned long int)(_zzq_arg3); \ _zzq_args[4] = (unsigned long int)(_zzq_arg4); \ _zzq_args[5] = (unsigned long int)(_zzq_arg5); \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ /* %RDX = client_request ( %RAX ) */ \ "xchgq %%rbx,%%rbx" \ : "=d" (_zzq_result) \ : "a" (&_zzq_args[0]), "0" (_zzq_default) \ : "cc", "memory" \ ); \ _zzq_result; \ }) #define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ volatile unsigned long int __addr; \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ /* %RAX = guest_NRADDR */ \ "xchgq %%rcx,%%rcx" \ : "=a" (__addr) \ : \ : "cc", "memory" \ ); \ _zzq_orig->nraddr = __addr; \ } #define VALGRIND_CALL_NOREDIR_RAX \ __SPECIAL_INSTRUCTION_PREAMBLE \ /* call-noredir *%RAX */ \ "xchgq %%rdx,%%rdx\n\t" #define VALGRIND_VEX_INJECT_IR() \ do { \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ "xchgq %%rdi,%%rdi\n\t" \ : : : "cc", "memory" \ ); \ } while (0) #endif /* PLAT_amd64_linux || PLAT_amd64_darwin || PLAT_amd64_solaris */ /* ------------------------- amd64-Win64 ------------------------- */ #if defined(PLAT_amd64_win64) && !defined(__GNUC__) #error Unsupported compiler. #endif /* PLAT_amd64_win64 */ /* ------------------------ ppc32-linux ------------------------ */ #if defined(PLAT_ppc32_linux) typedef struct { unsigned int nraddr; /* where's the code? */ } OrigFn; #define __SPECIAL_INSTRUCTION_PREAMBLE \ "rlwinm 0,0,3,0,31 ; rlwinm 0,0,13,0,31\n\t" \ "rlwinm 0,0,29,0,31 ; rlwinm 0,0,19,0,31\n\t" #define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ _zzq_default, _zzq_request, \ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ \ __extension__ \ ({ unsigned int _zzq_args[6]; \ unsigned int _zzq_result; \ unsigned int* _zzq_ptr; \ _zzq_args[0] = (unsigned int)(_zzq_request); \ _zzq_args[1] = (unsigned int)(_zzq_arg1); \ _zzq_args[2] = (unsigned int)(_zzq_arg2); \ _zzq_args[3] = (unsigned int)(_zzq_arg3); \ _zzq_args[4] = (unsigned int)(_zzq_arg4); \ _zzq_args[5] = (unsigned int)(_zzq_arg5); \ _zzq_ptr = _zzq_args; \ __asm__ volatile("mr 3,%1\n\t" /*default*/ \ "mr 4,%2\n\t" /*ptr*/ \ __SPECIAL_INSTRUCTION_PREAMBLE \ /* %R3 = client_request ( %R4 ) */ \ "or 1,1,1\n\t" \ "mr %0,3" /*result*/ \ : "=b" (_zzq_result) \ : "b" (_zzq_default), "b" (_zzq_ptr) \ : "cc", "memory", "r3", "r4"); \ _zzq_result; \ }) #define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ unsigned int __addr; \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ /* %R3 = guest_NRADDR */ \ "or 2,2,2\n\t" \ "mr %0,3" \ : "=b" (__addr) \ : \ : "cc", "memory", "r3" \ ); \ _zzq_orig->nraddr = __addr; \ } #define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ __SPECIAL_INSTRUCTION_PREAMBLE \ /* branch-and-link-to-noredir *%R11 */ \ "or 3,3,3\n\t" #define VALGRIND_VEX_INJECT_IR() \ do { \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ "or 5,5,5\n\t" \ ); \ } while (0) #endif /* PLAT_ppc32_linux */ /* ------------------------ ppc64-linux ------------------------ */ #if defined(PLAT_ppc64be_linux) typedef struct { unsigned long int nraddr; /* where's the code? */ unsigned long int r2; /* what tocptr do we need? */ } OrigFn; #define __SPECIAL_INSTRUCTION_PREAMBLE \ "rotldi 0,0,3 ; rotldi 0,0,13\n\t" \ "rotldi 0,0,61 ; rotldi 0,0,51\n\t" #define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ _zzq_default, _zzq_request, \ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ \ __extension__ \ ({ unsigned long int _zzq_args[6]; \ unsigned long int _zzq_result; \ unsigned long int* _zzq_ptr; \ _zzq_args[0] = (unsigned long int)(_zzq_request); \ _zzq_args[1] = (unsigned long int)(_zzq_arg1); \ _zzq_args[2] = (unsigned long int)(_zzq_arg2); \ _zzq_args[3] = (unsigned long int)(_zzq_arg3); \ _zzq_args[4] = (unsigned long int)(_zzq_arg4); \ _zzq_args[5] = (unsigned long int)(_zzq_arg5); \ _zzq_ptr = _zzq_args; \ __asm__ volatile("mr 3,%1\n\t" /*default*/ \ "mr 4,%2\n\t" /*ptr*/ \ __SPECIAL_INSTRUCTION_PREAMBLE \ /* %R3 = client_request ( %R4 ) */ \ "or 1,1,1\n\t" \ "mr %0,3" /*result*/ \ : "=b" (_zzq_result) \ : "b" (_zzq_default), "b" (_zzq_ptr) \ : "cc", "memory", "r3", "r4"); \ _zzq_result; \ }) #define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ unsigned long int __addr; \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ /* %R3 = guest_NRADDR */ \ "or 2,2,2\n\t" \ "mr %0,3" \ : "=b" (__addr) \ : \ : "cc", "memory", "r3" \ ); \ _zzq_orig->nraddr = __addr; \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ /* %R3 = guest_NRADDR_GPR2 */ \ "or 4,4,4\n\t" \ "mr %0,3" \ : "=b" (__addr) \ : \ : "cc", "memory", "r3" \ ); \ _zzq_orig->r2 = __addr; \ } #define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ __SPECIAL_INSTRUCTION_PREAMBLE \ /* branch-and-link-to-noredir *%R11 */ \ "or 3,3,3\n\t" #define VALGRIND_VEX_INJECT_IR() \ do { \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ "or 5,5,5\n\t" \ ); \ } while (0) #endif /* PLAT_ppc64be_linux */ #if defined(PLAT_ppc64le_linux) typedef struct { unsigned long int nraddr; /* where's the code? */ unsigned long int r2; /* what tocptr do we need? */ } OrigFn; #define __SPECIAL_INSTRUCTION_PREAMBLE \ "rotldi 0,0,3 ; rotldi 0,0,13\n\t" \ "rotldi 0,0,61 ; rotldi 0,0,51\n\t" #define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ _zzq_default, _zzq_request, \ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ \ __extension__ \ ({ unsigned long int _zzq_args[6]; \ unsigned long int _zzq_result; \ unsigned long int* _zzq_ptr; \ _zzq_args[0] = (unsigned long int)(_zzq_request); \ _zzq_args[1] = (unsigned long int)(_zzq_arg1); \ _zzq_args[2] = (unsigned long int)(_zzq_arg2); \ _zzq_args[3] = (unsigned long int)(_zzq_arg3); \ _zzq_args[4] = (unsigned long int)(_zzq_arg4); \ _zzq_args[5] = (unsigned long int)(_zzq_arg5); \ _zzq_ptr = _zzq_args; \ __asm__ volatile("mr 3,%1\n\t" /*default*/ \ "mr 4,%2\n\t" /*ptr*/ \ __SPECIAL_INSTRUCTION_PREAMBLE \ /* %R3 = client_request ( %R4 ) */ \ "or 1,1,1\n\t" \ "mr %0,3" /*result*/ \ : "=b" (_zzq_result) \ : "b" (_zzq_default), "b" (_zzq_ptr) \ : "cc", "memory", "r3", "r4"); \ _zzq_result; \ }) #define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ unsigned long int __addr; \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ /* %R3 = guest_NRADDR */ \ "or 2,2,2\n\t" \ "mr %0,3" \ : "=b" (__addr) \ : \ : "cc", "memory", "r3" \ ); \ _zzq_orig->nraddr = __addr; \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ /* %R3 = guest_NRADDR_GPR2 */ \ "or 4,4,4\n\t" \ "mr %0,3" \ : "=b" (__addr) \ : \ : "cc", "memory", "r3" \ ); \ _zzq_orig->r2 = __addr; \ } #define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ __SPECIAL_INSTRUCTION_PREAMBLE \ /* branch-and-link-to-noredir *%R12 */ \ "or 3,3,3\n\t" #define VALGRIND_VEX_INJECT_IR() \ do { \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ "or 5,5,5\n\t" \ ); \ } while (0) #endif /* PLAT_ppc64le_linux */ /* ------------------------- arm-linux ------------------------- */ #if defined(PLAT_arm_linux) typedef struct { unsigned int nraddr; /* where's the code? */ } OrigFn; #define __SPECIAL_INSTRUCTION_PREAMBLE \ "mov r12, r12, ror #3 ; mov r12, r12, ror #13 \n\t" \ "mov r12, r12, ror #29 ; mov r12, r12, ror #19 \n\t" #define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ _zzq_default, _zzq_request, \ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ \ __extension__ \ ({volatile unsigned int _zzq_args[6]; \ volatile unsigned int _zzq_result; \ _zzq_args[0] = (unsigned int)(_zzq_request); \ _zzq_args[1] = (unsigned int)(_zzq_arg1); \ _zzq_args[2] = (unsigned int)(_zzq_arg2); \ _zzq_args[3] = (unsigned int)(_zzq_arg3); \ _zzq_args[4] = (unsigned int)(_zzq_arg4); \ _zzq_args[5] = (unsigned int)(_zzq_arg5); \ __asm__ volatile("mov r3, %1\n\t" /*default*/ \ "mov r4, %2\n\t" /*ptr*/ \ __SPECIAL_INSTRUCTION_PREAMBLE \ /* R3 = client_request ( R4 ) */ \ "orr r10, r10, r10\n\t" \ "mov %0, r3" /*result*/ \ : "=r" (_zzq_result) \ : "r" (_zzq_default), "r" (&_zzq_args[0]) \ : "cc","memory", "r3", "r4"); \ _zzq_result; \ }) #define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ unsigned int __addr; \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ /* R3 = guest_NRADDR */ \ "orr r11, r11, r11\n\t" \ "mov %0, r3" \ : "=r" (__addr) \ : \ : "cc", "memory", "r3" \ ); \ _zzq_orig->nraddr = __addr; \ } #define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ __SPECIAL_INSTRUCTION_PREAMBLE \ /* branch-and-link-to-noredir *%R4 */ \ "orr r12, r12, r12\n\t" #define VALGRIND_VEX_INJECT_IR() \ do { \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ "orr r9, r9, r9\n\t" \ : : : "cc", "memory" \ ); \ } while (0) #endif /* PLAT_arm_linux */ /* ------------------------ arm64-linux ------------------------- */ #if defined(PLAT_arm64_linux) typedef struct { unsigned long int nraddr; /* where's the code? */ } OrigFn; #define __SPECIAL_INSTRUCTION_PREAMBLE \ "ror x12, x12, #3 ; ror x12, x12, #13 \n\t" \ "ror x12, x12, #51 ; ror x12, x12, #61 \n\t" #define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ _zzq_default, _zzq_request, \ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ \ __extension__ \ ({volatile unsigned long int _zzq_args[6]; \ volatile unsigned long int _zzq_result; \ _zzq_args[0] = (unsigned long int)(_zzq_request); \ _zzq_args[1] = (unsigned long int)(_zzq_arg1); \ _zzq_args[2] = (unsigned long int)(_zzq_arg2); \ _zzq_args[3] = (unsigned long int)(_zzq_arg3); \ _zzq_args[4] = (unsigned long int)(_zzq_arg4); \ _zzq_args[5] = (unsigned long int)(_zzq_arg5); \ __asm__ volatile("mov x3, %1\n\t" /*default*/ \ "mov x4, %2\n\t" /*ptr*/ \ __SPECIAL_INSTRUCTION_PREAMBLE \ /* X3 = client_request ( X4 ) */ \ "orr x10, x10, x10\n\t" \ "mov %0, x3" /*result*/ \ : "=r" (_zzq_result) \ : "r" ((unsigned long int)(_zzq_default)), \ "r" (&_zzq_args[0]) \ : "cc","memory", "x3", "x4"); \ _zzq_result; \ }) #define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ unsigned long int __addr; \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ /* X3 = guest_NRADDR */ \ "orr x11, x11, x11\n\t" \ "mov %0, x3" \ : "=r" (__addr) \ : \ : "cc", "memory", "x3" \ ); \ _zzq_orig->nraddr = __addr; \ } #define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ __SPECIAL_INSTRUCTION_PREAMBLE \ /* branch-and-link-to-noredir X8 */ \ "orr x12, x12, x12\n\t" #define VALGRIND_VEX_INJECT_IR() \ do { \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ "orr x9, x9, x9\n\t" \ : : : "cc", "memory" \ ); \ } while (0) #endif /* PLAT_arm64_linux */ /* ------------------------ s390x-linux ------------------------ */ #if defined(PLAT_s390x_linux) typedef struct { unsigned long int nraddr; /* where's the code? */ } OrigFn; /* __SPECIAL_INSTRUCTION_PREAMBLE will be used to identify Valgrind specific * code. This detection is implemented in platform specific toIR.c * (e.g. VEX/priv/guest_s390_decoder.c). */ #define __SPECIAL_INSTRUCTION_PREAMBLE \ "lr 15,15\n\t" \ "lr 1,1\n\t" \ "lr 2,2\n\t" \ "lr 3,3\n\t" #define __CLIENT_REQUEST_CODE "lr 2,2\n\t" #define __GET_NR_CONTEXT_CODE "lr 3,3\n\t" #define __CALL_NO_REDIR_CODE "lr 4,4\n\t" #define __VEX_INJECT_IR_CODE "lr 5,5\n\t" #define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ _zzq_default, _zzq_request, \ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ __extension__ \ ({volatile unsigned long int _zzq_args[6]; \ volatile unsigned long int _zzq_result; \ _zzq_args[0] = (unsigned long int)(_zzq_request); \ _zzq_args[1] = (unsigned long int)(_zzq_arg1); \ _zzq_args[2] = (unsigned long int)(_zzq_arg2); \ _zzq_args[3] = (unsigned long int)(_zzq_arg3); \ _zzq_args[4] = (unsigned long int)(_zzq_arg4); \ _zzq_args[5] = (unsigned long int)(_zzq_arg5); \ __asm__ volatile(/* r2 = args */ \ "lgr 2,%1\n\t" \ /* r3 = default */ \ "lgr 3,%2\n\t" \ __SPECIAL_INSTRUCTION_PREAMBLE \ __CLIENT_REQUEST_CODE \ /* results = r3 */ \ "lgr %0, 3\n\t" \ : "=d" (_zzq_result) \ : "a" (&_zzq_args[0]), "0" (_zzq_default) \ : "cc", "2", "3", "memory" \ ); \ _zzq_result; \ }) #define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ volatile unsigned long int __addr; \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ __GET_NR_CONTEXT_CODE \ "lgr %0, 3\n\t" \ : "=a" (__addr) \ : \ : "cc", "3", "memory" \ ); \ _zzq_orig->nraddr = __addr; \ } #define VALGRIND_CALL_NOREDIR_R1 \ __SPECIAL_INSTRUCTION_PREAMBLE \ __CALL_NO_REDIR_CODE #define VALGRIND_VEX_INJECT_IR() \ do { \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ __VEX_INJECT_IR_CODE); \ } while (0) #endif /* PLAT_s390x_linux */ /* ------------------------- mips32-linux ---------------- */ #if defined(PLAT_mips32_linux) typedef struct { unsigned int nraddr; /* where's the code? */ } OrigFn; /* .word 0x342 * .word 0x742 * .word 0xC2 * .word 0x4C2*/ #define __SPECIAL_INSTRUCTION_PREAMBLE \ "srl $0, $0, 13\n\t" \ "srl $0, $0, 29\n\t" \ "srl $0, $0, 3\n\t" \ "srl $0, $0, 19\n\t" #define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ _zzq_default, _zzq_request, \ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ __extension__ \ ({ volatile unsigned int _zzq_args[6]; \ volatile unsigned int _zzq_result; \ _zzq_args[0] = (unsigned int)(_zzq_request); \ _zzq_args[1] = (unsigned int)(_zzq_arg1); \ _zzq_args[2] = (unsigned int)(_zzq_arg2); \ _zzq_args[3] = (unsigned int)(_zzq_arg3); \ _zzq_args[4] = (unsigned int)(_zzq_arg4); \ _zzq_args[5] = (unsigned int)(_zzq_arg5); \ __asm__ volatile("move $11, %1\n\t" /*default*/ \ "move $12, %2\n\t" /*ptr*/ \ __SPECIAL_INSTRUCTION_PREAMBLE \ /* T3 = client_request ( T4 ) */ \ "or $13, $13, $13\n\t" \ "move %0, $11\n\t" /*result*/ \ : "=r" (_zzq_result) \ : "r" (_zzq_default), "r" (&_zzq_args[0]) \ : "$11", "$12", "memory"); \ _zzq_result; \ }) #define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ volatile unsigned int __addr; \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ /* %t9 = guest_NRADDR */ \ "or $14, $14, $14\n\t" \ "move %0, $11" /*result*/ \ : "=r" (__addr) \ : \ : "$11" \ ); \ _zzq_orig->nraddr = __addr; \ } #define VALGRIND_CALL_NOREDIR_T9 \ __SPECIAL_INSTRUCTION_PREAMBLE \ /* call-noredir *%t9 */ \ "or $15, $15, $15\n\t" #define VALGRIND_VEX_INJECT_IR() \ do { \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ "or $11, $11, $11\n\t" \ ); \ } while (0) #endif /* PLAT_mips32_linux */ /* ------------------------- mips64-linux ---------------- */ #if defined(PLAT_mips64_linux) typedef struct { unsigned long nraddr; /* where's the code? */ } OrigFn; /* dsll $0,$0, 3 * dsll $0,$0, 13 * dsll $0,$0, 29 * dsll $0,$0, 19*/ #define __SPECIAL_INSTRUCTION_PREAMBLE \ "dsll $0,$0, 3 ; dsll $0,$0,13\n\t" \ "dsll $0,$0,29 ; dsll $0,$0,19\n\t" #define VALGRIND_DO_CLIENT_REQUEST_EXPR( \ _zzq_default, _zzq_request, \ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ __extension__ \ ({ volatile unsigned long int _zzq_args[6]; \ volatile unsigned long int _zzq_result; \ _zzq_args[0] = (unsigned long int)(_zzq_request); \ _zzq_args[1] = (unsigned long int)(_zzq_arg1); \ _zzq_args[2] = (unsigned long int)(_zzq_arg2); \ _zzq_args[3] = (unsigned long int)(_zzq_arg3); \ _zzq_args[4] = (unsigned long int)(_zzq_arg4); \ _zzq_args[5] = (unsigned long int)(_zzq_arg5); \ __asm__ volatile("move $11, %1\n\t" /*default*/ \ "move $12, %2\n\t" /*ptr*/ \ __SPECIAL_INSTRUCTION_PREAMBLE \ /* $11 = client_request ( $12 ) */ \ "or $13, $13, $13\n\t" \ "move %0, $11\n\t" /*result*/ \ : "=r" (_zzq_result) \ : "r" (_zzq_default), "r" (&_zzq_args[0]) \ : "$11", "$12", "memory"); \ _zzq_result; \ }) #define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ volatile unsigned long int __addr; \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ /* $11 = guest_NRADDR */ \ "or $14, $14, $14\n\t" \ "move %0, $11" /*result*/ \ : "=r" (__addr) \ : \ : "$11"); \ _zzq_orig->nraddr = __addr; \ } #define VALGRIND_CALL_NOREDIR_T9 \ __SPECIAL_INSTRUCTION_PREAMBLE \ /* call-noredir $25 */ \ "or $15, $15, $15\n\t" #define VALGRIND_VEX_INJECT_IR() \ do { \ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ "or $11, $11, $11\n\t" \ ); \ } while (0) #endif /* PLAT_mips64_linux */ /* Insert assembly code for other platforms here... */ #endif /* NVALGRIND */ /* ------------------------------------------------------------------ */ /* PLATFORM SPECIFICS for FUNCTION WRAPPING. This is all very */ /* ugly. It's the least-worst tradeoff I can think of. */ /* ------------------------------------------------------------------ */ /* This section defines magic (a.k.a appalling-hack) macros for doing guaranteed-no-redirection macros, so as to get from function wrappers to the functions they are wrapping. The whole point is to construct standard call sequences, but to do the call itself with a special no-redirect call pseudo-instruction that the JIT understands and handles specially. This section is long and repetitious, and I can't see a way to make it shorter. The naming scheme is as follows: CALL_FN_{W,v}_{v,W,WW,WWW,WWWW,5W,6W,7W,etc} 'W' stands for "word" and 'v' for "void". Hence there are different macros for calling arity 0, 1, 2, 3, 4, etc, functions, and for each, the possibility of returning a word-typed result, or no result. */ /* Use these to write the name of your wrapper. NOTE: duplicates VG_WRAP_FUNCTION_Z{U,Z} in pub_tool_redir.h. NOTE also: inserts the default behaviour equivalance class tag "0000" into the name. See pub_tool_redir.h for details -- normally you don't need to think about this, though. */ /* Use an extra level of macroisation so as to ensure the soname/fnname args are fully macro-expanded before pasting them together. */ #define VG_CONCAT4(_aa,_bb,_cc,_dd) _aa##_bb##_cc##_dd #define I_WRAP_SONAME_FNNAME_ZU(soname,fnname) \ VG_CONCAT4(_vgw00000ZU_,soname,_,fnname) #define I_WRAP_SONAME_FNNAME_ZZ(soname,fnname) \ VG_CONCAT4(_vgw00000ZZ_,soname,_,fnname) /* Use this macro from within a wrapper function to collect the context (address and possibly other info) of the original function. Once you have that you can then use it in one of the CALL_FN_ macros. The type of the argument _lval is OrigFn. */ #define VALGRIND_GET_ORIG_FN(_lval) VALGRIND_GET_NR_CONTEXT(_lval) /* Also provide end-user facilities for function replacement, rather than wrapping. A replacement function differs from a wrapper in that it has no way to get hold of the original function being called, and hence no way to call onwards to it. In a replacement function, VALGRIND_GET_ORIG_FN always returns zero. */ #define I_REPLACE_SONAME_FNNAME_ZU(soname,fnname) \ VG_CONCAT4(_vgr00000ZU_,soname,_,fnname) #define I_REPLACE_SONAME_FNNAME_ZZ(soname,fnname) \ VG_CONCAT4(_vgr00000ZZ_,soname,_,fnname) /* Derivatives of the main macros below, for calling functions returning void. */ #define CALL_FN_v_v(fnptr) \ do { volatile unsigned long _junk; \ CALL_FN_W_v(_junk,fnptr); } while (0) #define CALL_FN_v_W(fnptr, arg1) \ do { volatile unsigned long _junk; \ CALL_FN_W_W(_junk,fnptr,arg1); } while (0) #define CALL_FN_v_WW(fnptr, arg1,arg2) \ do { volatile unsigned long _junk; \ CALL_FN_W_WW(_junk,fnptr,arg1,arg2); } while (0) #define CALL_FN_v_WWW(fnptr, arg1,arg2,arg3) \ do { volatile unsigned long _junk; \ CALL_FN_W_WWW(_junk,fnptr,arg1,arg2,arg3); } while (0) #define CALL_FN_v_WWWW(fnptr, arg1,arg2,arg3,arg4) \ do { volatile unsigned long _junk; \ CALL_FN_W_WWWW(_junk,fnptr,arg1,arg2,arg3,arg4); } while (0) #define CALL_FN_v_5W(fnptr, arg1,arg2,arg3,arg4,arg5) \ do { volatile unsigned long _junk; \ CALL_FN_W_5W(_junk,fnptr,arg1,arg2,arg3,arg4,arg5); } while (0) #define CALL_FN_v_6W(fnptr, arg1,arg2,arg3,arg4,arg5,arg6) \ do { volatile unsigned long _junk; \ CALL_FN_W_6W(_junk,fnptr,arg1,arg2,arg3,arg4,arg5,arg6); } while (0) #define CALL_FN_v_7W(fnptr, arg1,arg2,arg3,arg4,arg5,arg6,arg7) \ do { volatile unsigned long _junk; \ CALL_FN_W_7W(_junk,fnptr,arg1,arg2,arg3,arg4,arg5,arg6,arg7); } while (0) /* ----------------- x86-{linux,darwin,solaris} ---------------- */ #if defined(PLAT_x86_linux) || defined(PLAT_x86_darwin) \ || defined(PLAT_x86_solaris) /* These regs are trashed by the hidden call. No need to mention eax as gcc can already see that, plus causes gcc to bomb. */ #define __CALLER_SAVED_REGS /*"eax"*/ "ecx", "edx" /* Macros to save and align the stack before making a function call and restore it afterwards as gcc may not keep the stack pointer aligned if it doesn't realise calls are being made to other functions. */ #define VALGRIND_ALIGN_STACK \ "movl %%esp,%%edi\n\t" \ "andl $0xfffffff0,%%esp\n\t" #define VALGRIND_RESTORE_STACK \ "movl %%edi,%%esp\n\t" /* These CALL_FN_ macros assume that on x86-linux, sizeof(unsigned long) == 4. */ #define CALL_FN_W_v(lval, orig) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[1]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "movl (%%eax), %%eax\n\t" /* target->%eax */ \ VALGRIND_CALL_NOREDIR_EAX \ VALGRIND_RESTORE_STACK \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_W(lval, orig, arg1) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[2]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "subl $12, %%esp\n\t" \ "pushl 4(%%eax)\n\t" \ "movl (%%eax), %%eax\n\t" /* target->%eax */ \ VALGRIND_CALL_NOREDIR_EAX \ VALGRIND_RESTORE_STACK \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WW(lval, orig, arg1,arg2) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "subl $8, %%esp\n\t" \ "pushl 8(%%eax)\n\t" \ "pushl 4(%%eax)\n\t" \ "movl (%%eax), %%eax\n\t" /* target->%eax */ \ VALGRIND_CALL_NOREDIR_EAX \ VALGRIND_RESTORE_STACK \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[4]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "subl $4, %%esp\n\t" \ "pushl 12(%%eax)\n\t" \ "pushl 8(%%eax)\n\t" \ "pushl 4(%%eax)\n\t" \ "movl (%%eax), %%eax\n\t" /* target->%eax */ \ VALGRIND_CALL_NOREDIR_EAX \ VALGRIND_RESTORE_STACK \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[5]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "pushl 16(%%eax)\n\t" \ "pushl 12(%%eax)\n\t" \ "pushl 8(%%eax)\n\t" \ "pushl 4(%%eax)\n\t" \ "movl (%%eax), %%eax\n\t" /* target->%eax */ \ VALGRIND_CALL_NOREDIR_EAX \ VALGRIND_RESTORE_STACK \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[6]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "subl $12, %%esp\n\t" \ "pushl 20(%%eax)\n\t" \ "pushl 16(%%eax)\n\t" \ "pushl 12(%%eax)\n\t" \ "pushl 8(%%eax)\n\t" \ "pushl 4(%%eax)\n\t" \ "movl (%%eax), %%eax\n\t" /* target->%eax */ \ VALGRIND_CALL_NOREDIR_EAX \ VALGRIND_RESTORE_STACK \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[7]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "subl $8, %%esp\n\t" \ "pushl 24(%%eax)\n\t" \ "pushl 20(%%eax)\n\t" \ "pushl 16(%%eax)\n\t" \ "pushl 12(%%eax)\n\t" \ "pushl 8(%%eax)\n\t" \ "pushl 4(%%eax)\n\t" \ "movl (%%eax), %%eax\n\t" /* target->%eax */ \ VALGRIND_CALL_NOREDIR_EAX \ VALGRIND_RESTORE_STACK \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[8]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "subl $4, %%esp\n\t" \ "pushl 28(%%eax)\n\t" \ "pushl 24(%%eax)\n\t" \ "pushl 20(%%eax)\n\t" \ "pushl 16(%%eax)\n\t" \ "pushl 12(%%eax)\n\t" \ "pushl 8(%%eax)\n\t" \ "pushl 4(%%eax)\n\t" \ "movl (%%eax), %%eax\n\t" /* target->%eax */ \ VALGRIND_CALL_NOREDIR_EAX \ VALGRIND_RESTORE_STACK \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[9]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "pushl 32(%%eax)\n\t" \ "pushl 28(%%eax)\n\t" \ "pushl 24(%%eax)\n\t" \ "pushl 20(%%eax)\n\t" \ "pushl 16(%%eax)\n\t" \ "pushl 12(%%eax)\n\t" \ "pushl 8(%%eax)\n\t" \ "pushl 4(%%eax)\n\t" \ "movl (%%eax), %%eax\n\t" /* target->%eax */ \ VALGRIND_CALL_NOREDIR_EAX \ VALGRIND_RESTORE_STACK \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[10]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "subl $12, %%esp\n\t" \ "pushl 36(%%eax)\n\t" \ "pushl 32(%%eax)\n\t" \ "pushl 28(%%eax)\n\t" \ "pushl 24(%%eax)\n\t" \ "pushl 20(%%eax)\n\t" \ "pushl 16(%%eax)\n\t" \ "pushl 12(%%eax)\n\t" \ "pushl 8(%%eax)\n\t" \ "pushl 4(%%eax)\n\t" \ "movl (%%eax), %%eax\n\t" /* target->%eax */ \ VALGRIND_CALL_NOREDIR_EAX \ VALGRIND_RESTORE_STACK \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9,arg10) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[11]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ _argvec[10] = (unsigned long)(arg10); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "subl $8, %%esp\n\t" \ "pushl 40(%%eax)\n\t" \ "pushl 36(%%eax)\n\t" \ "pushl 32(%%eax)\n\t" \ "pushl 28(%%eax)\n\t" \ "pushl 24(%%eax)\n\t" \ "pushl 20(%%eax)\n\t" \ "pushl 16(%%eax)\n\t" \ "pushl 12(%%eax)\n\t" \ "pushl 8(%%eax)\n\t" \ "pushl 4(%%eax)\n\t" \ "movl (%%eax), %%eax\n\t" /* target->%eax */ \ VALGRIND_CALL_NOREDIR_EAX \ VALGRIND_RESTORE_STACK \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ arg6,arg7,arg8,arg9,arg10, \ arg11) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[12]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ _argvec[10] = (unsigned long)(arg10); \ _argvec[11] = (unsigned long)(arg11); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "subl $4, %%esp\n\t" \ "pushl 44(%%eax)\n\t" \ "pushl 40(%%eax)\n\t" \ "pushl 36(%%eax)\n\t" \ "pushl 32(%%eax)\n\t" \ "pushl 28(%%eax)\n\t" \ "pushl 24(%%eax)\n\t" \ "pushl 20(%%eax)\n\t" \ "pushl 16(%%eax)\n\t" \ "pushl 12(%%eax)\n\t" \ "pushl 8(%%eax)\n\t" \ "pushl 4(%%eax)\n\t" \ "movl (%%eax), %%eax\n\t" /* target->%eax */ \ VALGRIND_CALL_NOREDIR_EAX \ VALGRIND_RESTORE_STACK \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ arg6,arg7,arg8,arg9,arg10, \ arg11,arg12) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[13]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ _argvec[10] = (unsigned long)(arg10); \ _argvec[11] = (unsigned long)(arg11); \ _argvec[12] = (unsigned long)(arg12); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "pushl 48(%%eax)\n\t" \ "pushl 44(%%eax)\n\t" \ "pushl 40(%%eax)\n\t" \ "pushl 36(%%eax)\n\t" \ "pushl 32(%%eax)\n\t" \ "pushl 28(%%eax)\n\t" \ "pushl 24(%%eax)\n\t" \ "pushl 20(%%eax)\n\t" \ "pushl 16(%%eax)\n\t" \ "pushl 12(%%eax)\n\t" \ "pushl 8(%%eax)\n\t" \ "pushl 4(%%eax)\n\t" \ "movl (%%eax), %%eax\n\t" /* target->%eax */ \ VALGRIND_CALL_NOREDIR_EAX \ VALGRIND_RESTORE_STACK \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #endif /* PLAT_x86_linux || PLAT_x86_darwin || PLAT_x86_solaris */ /* ---------------- amd64-{linux,darwin,solaris} --------------- */ #if defined(PLAT_amd64_linux) || defined(PLAT_amd64_darwin) \ || defined(PLAT_amd64_solaris) /* ARGREGS: rdi rsi rdx rcx r8 r9 (the rest on stack in R-to-L order) */ /* These regs are trashed by the hidden call. */ #define __CALLER_SAVED_REGS /*"rax",*/ "rcx", "rdx", "rsi", \ "rdi", "r8", "r9", "r10", "r11" /* This is all pretty complex. It's so as to make stack unwinding work reliably. See bug 243270. The basic problem is the sub and add of 128 of %rsp in all of the following macros. If gcc believes the CFA is in %rsp, then unwinding may fail, because what's at the CFA is not what gcc "expected" when it constructs the CFIs for the places where the macros are instantiated. But we can't just add a CFI annotation to increase the CFA offset by 128, to match the sub of 128 from %rsp, because we don't know whether gcc has chosen %rsp as the CFA at that point, or whether it has chosen some other register (eg, %rbp). In the latter case, adding a CFI annotation to change the CFA offset is simply wrong. So the solution is to get hold of the CFA using __builtin_dwarf_cfa(), put it in a known register, and add a CFI annotation to say what the register is. We choose %rbp for this (perhaps perversely), because: (1) %rbp is already subject to unwinding. If a new register was chosen then the unwinder would have to unwind it in all stack traces, which is expensive, and (2) %rbp is already subject to precise exception updates in the JIT. If a new register was chosen, we'd have to have precise exceptions for it too, which reduces performance of the generated code. However .. one extra complication. We can't just whack the result of __builtin_dwarf_cfa() into %rbp and then add %rbp to the list of trashed registers at the end of the inline assembly fragments; gcc won't allow %rbp to appear in that list. Hence instead we need to stash %rbp in %r15 for the duration of the asm, and say that %r15 is trashed instead. gcc seems happy to go with that. Oh .. and this all needs to be conditionalised so that it is unchanged from before this commit, when compiled with older gccs that don't support __builtin_dwarf_cfa. Furthermore, since this header file is freestanding, it has to be independent of config.h, and so the following conditionalisation cannot depend on configure time checks. Although it's not clear from 'defined(__GNUC__) && defined(__GCC_HAVE_DWARF2_CFI_ASM)', this expression excludes Darwin. .cfi directives in Darwin assembly appear to be completely different and I haven't investigated how they work. For even more entertainment value, note we have to use the completely undocumented __builtin_dwarf_cfa(), which appears to really compute the CFA, whereas __builtin_frame_address(0) claims to but actually doesn't. See https://bugs.kde.org/show_bug.cgi?id=243270#c47 */ #if defined(__GNUC__) && defined(__GCC_HAVE_DWARF2_CFI_ASM) # define __FRAME_POINTER \ ,"r"(__builtin_dwarf_cfa()) # define VALGRIND_CFI_PROLOGUE \ "movq %%rbp, %%r15\n\t" \ "movq %2, %%rbp\n\t" \ ".cfi_remember_state\n\t" \ ".cfi_def_cfa rbp, 0\n\t" # define VALGRIND_CFI_EPILOGUE \ "movq %%r15, %%rbp\n\t" \ ".cfi_restore_state\n\t" #else # define __FRAME_POINTER # define VALGRIND_CFI_PROLOGUE # define VALGRIND_CFI_EPILOGUE #endif /* Macros to save and align the stack before making a function call and restore it afterwards as gcc may not keep the stack pointer aligned if it doesn't realise calls are being made to other functions. */ #define VALGRIND_ALIGN_STACK \ "movq %%rsp,%%r14\n\t" \ "andq $0xfffffffffffffff0,%%rsp\n\t" #define VALGRIND_RESTORE_STACK \ "movq %%r14,%%rsp\n\t" /* These CALL_FN_ macros assume that on amd64-linux, sizeof(unsigned long) == 8. */ /* NB 9 Sept 07. There is a nasty kludge here in all these CALL_FN_ macros. In order not to trash the stack redzone, we need to drop %rsp by 128 before the hidden call, and restore afterwards. The nastyness is that it is only by luck that the stack still appears to be unwindable during the hidden call - since then the behaviour of any routine using this macro does not match what the CFI data says. Sigh. Why is this important? Imagine that a wrapper has a stack allocated local, and passes to the hidden call, a pointer to it. Because gcc does not know about the hidden call, it may allocate that local in the redzone. Unfortunately the hidden call may then trash it before it comes to use it. So we must step clear of the redzone, for the duration of the hidden call, to make it safe. Probably the same problem afflicts the other redzone-style ABIs too (ppc64-linux); but for those, the stack is self describing (none of this CFI nonsense) so at least messing with the stack pointer doesn't give a danger of non-unwindable stack. */ #define CALL_FN_W_v(lval, orig) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[1]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ __asm__ volatile( \ VALGRIND_CFI_PROLOGUE \ VALGRIND_ALIGN_STACK \ "subq $128,%%rsp\n\t" \ "movq (%%rax), %%rax\n\t" /* target->%rax */ \ VALGRIND_CALL_NOREDIR_RAX \ VALGRIND_RESTORE_STACK \ VALGRIND_CFI_EPILOGUE \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_W(lval, orig, arg1) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[2]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ __asm__ volatile( \ VALGRIND_CFI_PROLOGUE \ VALGRIND_ALIGN_STACK \ "subq $128,%%rsp\n\t" \ "movq 8(%%rax), %%rdi\n\t" \ "movq (%%rax), %%rax\n\t" /* target->%rax */ \ VALGRIND_CALL_NOREDIR_RAX \ VALGRIND_RESTORE_STACK \ VALGRIND_CFI_EPILOGUE \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WW(lval, orig, arg1,arg2) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ __asm__ volatile( \ VALGRIND_CFI_PROLOGUE \ VALGRIND_ALIGN_STACK \ "subq $128,%%rsp\n\t" \ "movq 16(%%rax), %%rsi\n\t" \ "movq 8(%%rax), %%rdi\n\t" \ "movq (%%rax), %%rax\n\t" /* target->%rax */ \ VALGRIND_CALL_NOREDIR_RAX \ VALGRIND_RESTORE_STACK \ VALGRIND_CFI_EPILOGUE \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[4]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ __asm__ volatile( \ VALGRIND_CFI_PROLOGUE \ VALGRIND_ALIGN_STACK \ "subq $128,%%rsp\n\t" \ "movq 24(%%rax), %%rdx\n\t" \ "movq 16(%%rax), %%rsi\n\t" \ "movq 8(%%rax), %%rdi\n\t" \ "movq (%%rax), %%rax\n\t" /* target->%rax */ \ VALGRIND_CALL_NOREDIR_RAX \ VALGRIND_RESTORE_STACK \ VALGRIND_CFI_EPILOGUE \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[5]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ __asm__ volatile( \ VALGRIND_CFI_PROLOGUE \ VALGRIND_ALIGN_STACK \ "subq $128,%%rsp\n\t" \ "movq 32(%%rax), %%rcx\n\t" \ "movq 24(%%rax), %%rdx\n\t" \ "movq 16(%%rax), %%rsi\n\t" \ "movq 8(%%rax), %%rdi\n\t" \ "movq (%%rax), %%rax\n\t" /* target->%rax */ \ VALGRIND_CALL_NOREDIR_RAX \ VALGRIND_RESTORE_STACK \ VALGRIND_CFI_EPILOGUE \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[6]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ __asm__ volatile( \ VALGRIND_CFI_PROLOGUE \ VALGRIND_ALIGN_STACK \ "subq $128,%%rsp\n\t" \ "movq 40(%%rax), %%r8\n\t" \ "movq 32(%%rax), %%rcx\n\t" \ "movq 24(%%rax), %%rdx\n\t" \ "movq 16(%%rax), %%rsi\n\t" \ "movq 8(%%rax), %%rdi\n\t" \ "movq (%%rax), %%rax\n\t" /* target->%rax */ \ VALGRIND_CALL_NOREDIR_RAX \ VALGRIND_RESTORE_STACK \ VALGRIND_CFI_EPILOGUE \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[7]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ __asm__ volatile( \ VALGRIND_CFI_PROLOGUE \ VALGRIND_ALIGN_STACK \ "subq $128,%%rsp\n\t" \ "movq 48(%%rax), %%r9\n\t" \ "movq 40(%%rax), %%r8\n\t" \ "movq 32(%%rax), %%rcx\n\t" \ "movq 24(%%rax), %%rdx\n\t" \ "movq 16(%%rax), %%rsi\n\t" \ "movq 8(%%rax), %%rdi\n\t" \ "movq (%%rax), %%rax\n\t" /* target->%rax */ \ VALGRIND_CALL_NOREDIR_RAX \ VALGRIND_RESTORE_STACK \ VALGRIND_CFI_EPILOGUE \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[8]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ __asm__ volatile( \ VALGRIND_CFI_PROLOGUE \ VALGRIND_ALIGN_STACK \ "subq $136,%%rsp\n\t" \ "pushq 56(%%rax)\n\t" \ "movq 48(%%rax), %%r9\n\t" \ "movq 40(%%rax), %%r8\n\t" \ "movq 32(%%rax), %%rcx\n\t" \ "movq 24(%%rax), %%rdx\n\t" \ "movq 16(%%rax), %%rsi\n\t" \ "movq 8(%%rax), %%rdi\n\t" \ "movq (%%rax), %%rax\n\t" /* target->%rax */ \ VALGRIND_CALL_NOREDIR_RAX \ VALGRIND_RESTORE_STACK \ VALGRIND_CFI_EPILOGUE \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[9]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ __asm__ volatile( \ VALGRIND_CFI_PROLOGUE \ VALGRIND_ALIGN_STACK \ "subq $128,%%rsp\n\t" \ "pushq 64(%%rax)\n\t" \ "pushq 56(%%rax)\n\t" \ "movq 48(%%rax), %%r9\n\t" \ "movq 40(%%rax), %%r8\n\t" \ "movq 32(%%rax), %%rcx\n\t" \ "movq 24(%%rax), %%rdx\n\t" \ "movq 16(%%rax), %%rsi\n\t" \ "movq 8(%%rax), %%rdi\n\t" \ "movq (%%rax), %%rax\n\t" /* target->%rax */ \ VALGRIND_CALL_NOREDIR_RAX \ VALGRIND_RESTORE_STACK \ VALGRIND_CFI_EPILOGUE \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[10]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ __asm__ volatile( \ VALGRIND_CFI_PROLOGUE \ VALGRIND_ALIGN_STACK \ "subq $136,%%rsp\n\t" \ "pushq 72(%%rax)\n\t" \ "pushq 64(%%rax)\n\t" \ "pushq 56(%%rax)\n\t" \ "movq 48(%%rax), %%r9\n\t" \ "movq 40(%%rax), %%r8\n\t" \ "movq 32(%%rax), %%rcx\n\t" \ "movq 24(%%rax), %%rdx\n\t" \ "movq 16(%%rax), %%rsi\n\t" \ "movq 8(%%rax), %%rdi\n\t" \ "movq (%%rax), %%rax\n\t" /* target->%rax */ \ VALGRIND_CALL_NOREDIR_RAX \ VALGRIND_RESTORE_STACK \ VALGRIND_CFI_EPILOGUE \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9,arg10) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[11]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ _argvec[10] = (unsigned long)(arg10); \ __asm__ volatile( \ VALGRIND_CFI_PROLOGUE \ VALGRIND_ALIGN_STACK \ "subq $128,%%rsp\n\t" \ "pushq 80(%%rax)\n\t" \ "pushq 72(%%rax)\n\t" \ "pushq 64(%%rax)\n\t" \ "pushq 56(%%rax)\n\t" \ "movq 48(%%rax), %%r9\n\t" \ "movq 40(%%rax), %%r8\n\t" \ "movq 32(%%rax), %%rcx\n\t" \ "movq 24(%%rax), %%rdx\n\t" \ "movq 16(%%rax), %%rsi\n\t" \ "movq 8(%%rax), %%rdi\n\t" \ "movq (%%rax), %%rax\n\t" /* target->%rax */ \ VALGRIND_CALL_NOREDIR_RAX \ VALGRIND_RESTORE_STACK \ VALGRIND_CFI_EPILOGUE \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9,arg10,arg11) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[12]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ _argvec[10] = (unsigned long)(arg10); \ _argvec[11] = (unsigned long)(arg11); \ __asm__ volatile( \ VALGRIND_CFI_PROLOGUE \ VALGRIND_ALIGN_STACK \ "subq $136,%%rsp\n\t" \ "pushq 88(%%rax)\n\t" \ "pushq 80(%%rax)\n\t" \ "pushq 72(%%rax)\n\t" \ "pushq 64(%%rax)\n\t" \ "pushq 56(%%rax)\n\t" \ "movq 48(%%rax), %%r9\n\t" \ "movq 40(%%rax), %%r8\n\t" \ "movq 32(%%rax), %%rcx\n\t" \ "movq 24(%%rax), %%rdx\n\t" \ "movq 16(%%rax), %%rsi\n\t" \ "movq 8(%%rax), %%rdi\n\t" \ "movq (%%rax), %%rax\n\t" /* target->%rax */ \ VALGRIND_CALL_NOREDIR_RAX \ VALGRIND_RESTORE_STACK \ VALGRIND_CFI_EPILOGUE \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9,arg10,arg11,arg12) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[13]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ _argvec[10] = (unsigned long)(arg10); \ _argvec[11] = (unsigned long)(arg11); \ _argvec[12] = (unsigned long)(arg12); \ __asm__ volatile( \ VALGRIND_CFI_PROLOGUE \ VALGRIND_ALIGN_STACK \ "subq $128,%%rsp\n\t" \ "pushq 96(%%rax)\n\t" \ "pushq 88(%%rax)\n\t" \ "pushq 80(%%rax)\n\t" \ "pushq 72(%%rax)\n\t" \ "pushq 64(%%rax)\n\t" \ "pushq 56(%%rax)\n\t" \ "movq 48(%%rax), %%r9\n\t" \ "movq 40(%%rax), %%r8\n\t" \ "movq 32(%%rax), %%rcx\n\t" \ "movq 24(%%rax), %%rdx\n\t" \ "movq 16(%%rax), %%rsi\n\t" \ "movq 8(%%rax), %%rdi\n\t" \ "movq (%%rax), %%rax\n\t" /* target->%rax */ \ VALGRIND_CALL_NOREDIR_RAX \ VALGRIND_RESTORE_STACK \ VALGRIND_CFI_EPILOGUE \ : /*out*/ "=a" (_res) \ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #endif /* PLAT_amd64_linux || PLAT_amd64_darwin || PLAT_amd64_solaris */ /* ------------------------ ppc32-linux ------------------------ */ #if defined(PLAT_ppc32_linux) /* This is useful for finding out about the on-stack stuff: extern int f9 ( int,int,int,int,int,int,int,int,int ); extern int f10 ( int,int,int,int,int,int,int,int,int,int ); extern int f11 ( int,int,int,int,int,int,int,int,int,int,int ); extern int f12 ( int,int,int,int,int,int,int,int,int,int,int,int ); int g9 ( void ) { return f9(11,22,33,44,55,66,77,88,99); } int g10 ( void ) { return f10(11,22,33,44,55,66,77,88,99,110); } int g11 ( void ) { return f11(11,22,33,44,55,66,77,88,99,110,121); } int g12 ( void ) { return f12(11,22,33,44,55,66,77,88,99,110,121,132); } */ /* ARGREGS: r3 r4 r5 r6 r7 r8 r9 r10 (the rest on stack somewhere) */ /* These regs are trashed by the hidden call. */ #define __CALLER_SAVED_REGS \ "lr", "ctr", "xer", \ "cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7", \ "r0", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", \ "r11", "r12", "r13" /* Macros to save and align the stack before making a function call and restore it afterwards as gcc may not keep the stack pointer aligned if it doesn't realise calls are being made to other functions. */ #define VALGRIND_ALIGN_STACK \ "mr 28,1\n\t" \ "rlwinm 1,1,0,0,27\n\t" #define VALGRIND_RESTORE_STACK \ "mr 1,28\n\t" /* These CALL_FN_ macros assume that on ppc32-linux, sizeof(unsigned long) == 4. */ #define CALL_FN_W_v(lval, orig) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[1]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 11,%1\n\t" \ "lwz 11,0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ VALGRIND_RESTORE_STACK \ "mr %0,3" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_W(lval, orig, arg1) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[2]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 11,%1\n\t" \ "lwz 3,4(11)\n\t" /* arg1->r3 */ \ "lwz 11,0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ VALGRIND_RESTORE_STACK \ "mr %0,3" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WW(lval, orig, arg1,arg2) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ _argvec[2] = (unsigned long)arg2; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 11,%1\n\t" \ "lwz 3,4(11)\n\t" /* arg1->r3 */ \ "lwz 4,8(11)\n\t" \ "lwz 11,0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ VALGRIND_RESTORE_STACK \ "mr %0,3" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[4]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ _argvec[2] = (unsigned long)arg2; \ _argvec[3] = (unsigned long)arg3; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 11,%1\n\t" \ "lwz 3,4(11)\n\t" /* arg1->r3 */ \ "lwz 4,8(11)\n\t" \ "lwz 5,12(11)\n\t" \ "lwz 11,0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ VALGRIND_RESTORE_STACK \ "mr %0,3" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[5]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ _argvec[2] = (unsigned long)arg2; \ _argvec[3] = (unsigned long)arg3; \ _argvec[4] = (unsigned long)arg4; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 11,%1\n\t" \ "lwz 3,4(11)\n\t" /* arg1->r3 */ \ "lwz 4,8(11)\n\t" \ "lwz 5,12(11)\n\t" \ "lwz 6,16(11)\n\t" /* arg4->r6 */ \ "lwz 11,0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ VALGRIND_RESTORE_STACK \ "mr %0,3" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[6]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ _argvec[2] = (unsigned long)arg2; \ _argvec[3] = (unsigned long)arg3; \ _argvec[4] = (unsigned long)arg4; \ _argvec[5] = (unsigned long)arg5; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 11,%1\n\t" \ "lwz 3,4(11)\n\t" /* arg1->r3 */ \ "lwz 4,8(11)\n\t" \ "lwz 5,12(11)\n\t" \ "lwz 6,16(11)\n\t" /* arg4->r6 */ \ "lwz 7,20(11)\n\t" \ "lwz 11,0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ VALGRIND_RESTORE_STACK \ "mr %0,3" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[7]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ _argvec[2] = (unsigned long)arg2; \ _argvec[3] = (unsigned long)arg3; \ _argvec[4] = (unsigned long)arg4; \ _argvec[5] = (unsigned long)arg5; \ _argvec[6] = (unsigned long)arg6; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 11,%1\n\t" \ "lwz 3,4(11)\n\t" /* arg1->r3 */ \ "lwz 4,8(11)\n\t" \ "lwz 5,12(11)\n\t" \ "lwz 6,16(11)\n\t" /* arg4->r6 */ \ "lwz 7,20(11)\n\t" \ "lwz 8,24(11)\n\t" \ "lwz 11,0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ VALGRIND_RESTORE_STACK \ "mr %0,3" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[8]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ _argvec[2] = (unsigned long)arg2; \ _argvec[3] = (unsigned long)arg3; \ _argvec[4] = (unsigned long)arg4; \ _argvec[5] = (unsigned long)arg5; \ _argvec[6] = (unsigned long)arg6; \ _argvec[7] = (unsigned long)arg7; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 11,%1\n\t" \ "lwz 3,4(11)\n\t" /* arg1->r3 */ \ "lwz 4,8(11)\n\t" \ "lwz 5,12(11)\n\t" \ "lwz 6,16(11)\n\t" /* arg4->r6 */ \ "lwz 7,20(11)\n\t" \ "lwz 8,24(11)\n\t" \ "lwz 9,28(11)\n\t" \ "lwz 11,0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ VALGRIND_RESTORE_STACK \ "mr %0,3" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[9]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ _argvec[2] = (unsigned long)arg2; \ _argvec[3] = (unsigned long)arg3; \ _argvec[4] = (unsigned long)arg4; \ _argvec[5] = (unsigned long)arg5; \ _argvec[6] = (unsigned long)arg6; \ _argvec[7] = (unsigned long)arg7; \ _argvec[8] = (unsigned long)arg8; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 11,%1\n\t" \ "lwz 3,4(11)\n\t" /* arg1->r3 */ \ "lwz 4,8(11)\n\t" \ "lwz 5,12(11)\n\t" \ "lwz 6,16(11)\n\t" /* arg4->r6 */ \ "lwz 7,20(11)\n\t" \ "lwz 8,24(11)\n\t" \ "lwz 9,28(11)\n\t" \ "lwz 10,32(11)\n\t" /* arg8->r10 */ \ "lwz 11,0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ VALGRIND_RESTORE_STACK \ "mr %0,3" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[10]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ _argvec[2] = (unsigned long)arg2; \ _argvec[3] = (unsigned long)arg3; \ _argvec[4] = (unsigned long)arg4; \ _argvec[5] = (unsigned long)arg5; \ _argvec[6] = (unsigned long)arg6; \ _argvec[7] = (unsigned long)arg7; \ _argvec[8] = (unsigned long)arg8; \ _argvec[9] = (unsigned long)arg9; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 11,%1\n\t" \ "addi 1,1,-16\n\t" \ /* arg9 */ \ "lwz 3,36(11)\n\t" \ "stw 3,8(1)\n\t" \ /* args1-8 */ \ "lwz 3,4(11)\n\t" /* arg1->r3 */ \ "lwz 4,8(11)\n\t" \ "lwz 5,12(11)\n\t" \ "lwz 6,16(11)\n\t" /* arg4->r6 */ \ "lwz 7,20(11)\n\t" \ "lwz 8,24(11)\n\t" \ "lwz 9,28(11)\n\t" \ "lwz 10,32(11)\n\t" /* arg8->r10 */ \ "lwz 11,0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ VALGRIND_RESTORE_STACK \ "mr %0,3" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9,arg10) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[11]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ _argvec[2] = (unsigned long)arg2; \ _argvec[3] = (unsigned long)arg3; \ _argvec[4] = (unsigned long)arg4; \ _argvec[5] = (unsigned long)arg5; \ _argvec[6] = (unsigned long)arg6; \ _argvec[7] = (unsigned long)arg7; \ _argvec[8] = (unsigned long)arg8; \ _argvec[9] = (unsigned long)arg9; \ _argvec[10] = (unsigned long)arg10; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 11,%1\n\t" \ "addi 1,1,-16\n\t" \ /* arg10 */ \ "lwz 3,40(11)\n\t" \ "stw 3,12(1)\n\t" \ /* arg9 */ \ "lwz 3,36(11)\n\t" \ "stw 3,8(1)\n\t" \ /* args1-8 */ \ "lwz 3,4(11)\n\t" /* arg1->r3 */ \ "lwz 4,8(11)\n\t" \ "lwz 5,12(11)\n\t" \ "lwz 6,16(11)\n\t" /* arg4->r6 */ \ "lwz 7,20(11)\n\t" \ "lwz 8,24(11)\n\t" \ "lwz 9,28(11)\n\t" \ "lwz 10,32(11)\n\t" /* arg8->r10 */ \ "lwz 11,0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ VALGRIND_RESTORE_STACK \ "mr %0,3" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9,arg10,arg11) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[12]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ _argvec[2] = (unsigned long)arg2; \ _argvec[3] = (unsigned long)arg3; \ _argvec[4] = (unsigned long)arg4; \ _argvec[5] = (unsigned long)arg5; \ _argvec[6] = (unsigned long)arg6; \ _argvec[7] = (unsigned long)arg7; \ _argvec[8] = (unsigned long)arg8; \ _argvec[9] = (unsigned long)arg9; \ _argvec[10] = (unsigned long)arg10; \ _argvec[11] = (unsigned long)arg11; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 11,%1\n\t" \ "addi 1,1,-32\n\t" \ /* arg11 */ \ "lwz 3,44(11)\n\t" \ "stw 3,16(1)\n\t" \ /* arg10 */ \ "lwz 3,40(11)\n\t" \ "stw 3,12(1)\n\t" \ /* arg9 */ \ "lwz 3,36(11)\n\t" \ "stw 3,8(1)\n\t" \ /* args1-8 */ \ "lwz 3,4(11)\n\t" /* arg1->r3 */ \ "lwz 4,8(11)\n\t" \ "lwz 5,12(11)\n\t" \ "lwz 6,16(11)\n\t" /* arg4->r6 */ \ "lwz 7,20(11)\n\t" \ "lwz 8,24(11)\n\t" \ "lwz 9,28(11)\n\t" \ "lwz 10,32(11)\n\t" /* arg8->r10 */ \ "lwz 11,0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ VALGRIND_RESTORE_STACK \ "mr %0,3" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9,arg10,arg11,arg12) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[13]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ _argvec[2] = (unsigned long)arg2; \ _argvec[3] = (unsigned long)arg3; \ _argvec[4] = (unsigned long)arg4; \ _argvec[5] = (unsigned long)arg5; \ _argvec[6] = (unsigned long)arg6; \ _argvec[7] = (unsigned long)arg7; \ _argvec[8] = (unsigned long)arg8; \ _argvec[9] = (unsigned long)arg9; \ _argvec[10] = (unsigned long)arg10; \ _argvec[11] = (unsigned long)arg11; \ _argvec[12] = (unsigned long)arg12; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 11,%1\n\t" \ "addi 1,1,-32\n\t" \ /* arg12 */ \ "lwz 3,48(11)\n\t" \ "stw 3,20(1)\n\t" \ /* arg11 */ \ "lwz 3,44(11)\n\t" \ "stw 3,16(1)\n\t" \ /* arg10 */ \ "lwz 3,40(11)\n\t" \ "stw 3,12(1)\n\t" \ /* arg9 */ \ "lwz 3,36(11)\n\t" \ "stw 3,8(1)\n\t" \ /* args1-8 */ \ "lwz 3,4(11)\n\t" /* arg1->r3 */ \ "lwz 4,8(11)\n\t" \ "lwz 5,12(11)\n\t" \ "lwz 6,16(11)\n\t" /* arg4->r6 */ \ "lwz 7,20(11)\n\t" \ "lwz 8,24(11)\n\t" \ "lwz 9,28(11)\n\t" \ "lwz 10,32(11)\n\t" /* arg8->r10 */ \ "lwz 11,0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ VALGRIND_RESTORE_STACK \ "mr %0,3" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #endif /* PLAT_ppc32_linux */ /* ------------------------ ppc64-linux ------------------------ */ #if defined(PLAT_ppc64be_linux) /* ARGREGS: r3 r4 r5 r6 r7 r8 r9 r10 (the rest on stack somewhere) */ /* These regs are trashed by the hidden call. */ #define __CALLER_SAVED_REGS \ "lr", "ctr", "xer", \ "cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7", \ "r0", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", \ "r11", "r12", "r13" /* Macros to save and align the stack before making a function call and restore it afterwards as gcc may not keep the stack pointer aligned if it doesn't realise calls are being made to other functions. */ #define VALGRIND_ALIGN_STACK \ "mr 28,1\n\t" \ "rldicr 1,1,0,59\n\t" #define VALGRIND_RESTORE_STACK \ "mr 1,28\n\t" /* These CALL_FN_ macros assume that on ppc64-linux, sizeof(unsigned long) == 8. */ #define CALL_FN_W_v(lval, orig) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+0]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 11,%1\n\t" \ "std 2,-16(11)\n\t" /* save tocptr */ \ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ "ld 11, 0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr 11,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(11)\n\t" /* restore tocptr */ \ VALGRIND_RESTORE_STACK \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_W(lval, orig, arg1) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+1]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 11,%1\n\t" \ "std 2,-16(11)\n\t" /* save tocptr */ \ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ "ld 3, 8(11)\n\t" /* arg1->r3 */ \ "ld 11, 0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr 11,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(11)\n\t" /* restore tocptr */ \ VALGRIND_RESTORE_STACK \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WW(lval, orig, arg1,arg2) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+2]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 11,%1\n\t" \ "std 2,-16(11)\n\t" /* save tocptr */ \ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ "ld 3, 8(11)\n\t" /* arg1->r3 */ \ "ld 4, 16(11)\n\t" /* arg2->r4 */ \ "ld 11, 0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr 11,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(11)\n\t" /* restore tocptr */ \ VALGRIND_RESTORE_STACK \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+3]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 11,%1\n\t" \ "std 2,-16(11)\n\t" /* save tocptr */ \ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ "ld 3, 8(11)\n\t" /* arg1->r3 */ \ "ld 4, 16(11)\n\t" /* arg2->r4 */ \ "ld 5, 24(11)\n\t" /* arg3->r5 */ \ "ld 11, 0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr 11,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(11)\n\t" /* restore tocptr */ \ VALGRIND_RESTORE_STACK \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+4]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 11,%1\n\t" \ "std 2,-16(11)\n\t" /* save tocptr */ \ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ "ld 3, 8(11)\n\t" /* arg1->r3 */ \ "ld 4, 16(11)\n\t" /* arg2->r4 */ \ "ld 5, 24(11)\n\t" /* arg3->r5 */ \ "ld 6, 32(11)\n\t" /* arg4->r6 */ \ "ld 11, 0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr 11,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(11)\n\t" /* restore tocptr */ \ VALGRIND_RESTORE_STACK \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+5]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ _argvec[2+5] = (unsigned long)arg5; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 11,%1\n\t" \ "std 2,-16(11)\n\t" /* save tocptr */ \ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ "ld 3, 8(11)\n\t" /* arg1->r3 */ \ "ld 4, 16(11)\n\t" /* arg2->r4 */ \ "ld 5, 24(11)\n\t" /* arg3->r5 */ \ "ld 6, 32(11)\n\t" /* arg4->r6 */ \ "ld 7, 40(11)\n\t" /* arg5->r7 */ \ "ld 11, 0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr 11,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(11)\n\t" /* restore tocptr */ \ VALGRIND_RESTORE_STACK \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+6]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ _argvec[2+5] = (unsigned long)arg5; \ _argvec[2+6] = (unsigned long)arg6; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 11,%1\n\t" \ "std 2,-16(11)\n\t" /* save tocptr */ \ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ "ld 3, 8(11)\n\t" /* arg1->r3 */ \ "ld 4, 16(11)\n\t" /* arg2->r4 */ \ "ld 5, 24(11)\n\t" /* arg3->r5 */ \ "ld 6, 32(11)\n\t" /* arg4->r6 */ \ "ld 7, 40(11)\n\t" /* arg5->r7 */ \ "ld 8, 48(11)\n\t" /* arg6->r8 */ \ "ld 11, 0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr 11,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(11)\n\t" /* restore tocptr */ \ VALGRIND_RESTORE_STACK \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+7]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ _argvec[2+5] = (unsigned long)arg5; \ _argvec[2+6] = (unsigned long)arg6; \ _argvec[2+7] = (unsigned long)arg7; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 11,%1\n\t" \ "std 2,-16(11)\n\t" /* save tocptr */ \ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ "ld 3, 8(11)\n\t" /* arg1->r3 */ \ "ld 4, 16(11)\n\t" /* arg2->r4 */ \ "ld 5, 24(11)\n\t" /* arg3->r5 */ \ "ld 6, 32(11)\n\t" /* arg4->r6 */ \ "ld 7, 40(11)\n\t" /* arg5->r7 */ \ "ld 8, 48(11)\n\t" /* arg6->r8 */ \ "ld 9, 56(11)\n\t" /* arg7->r9 */ \ "ld 11, 0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr 11,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(11)\n\t" /* restore tocptr */ \ VALGRIND_RESTORE_STACK \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+8]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ _argvec[2+5] = (unsigned long)arg5; \ _argvec[2+6] = (unsigned long)arg6; \ _argvec[2+7] = (unsigned long)arg7; \ _argvec[2+8] = (unsigned long)arg8; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 11,%1\n\t" \ "std 2,-16(11)\n\t" /* save tocptr */ \ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ "ld 3, 8(11)\n\t" /* arg1->r3 */ \ "ld 4, 16(11)\n\t" /* arg2->r4 */ \ "ld 5, 24(11)\n\t" /* arg3->r5 */ \ "ld 6, 32(11)\n\t" /* arg4->r6 */ \ "ld 7, 40(11)\n\t" /* arg5->r7 */ \ "ld 8, 48(11)\n\t" /* arg6->r8 */ \ "ld 9, 56(11)\n\t" /* arg7->r9 */ \ "ld 10, 64(11)\n\t" /* arg8->r10 */ \ "ld 11, 0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr 11,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(11)\n\t" /* restore tocptr */ \ VALGRIND_RESTORE_STACK \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+9]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ _argvec[2+5] = (unsigned long)arg5; \ _argvec[2+6] = (unsigned long)arg6; \ _argvec[2+7] = (unsigned long)arg7; \ _argvec[2+8] = (unsigned long)arg8; \ _argvec[2+9] = (unsigned long)arg9; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 11,%1\n\t" \ "std 2,-16(11)\n\t" /* save tocptr */ \ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ "addi 1,1,-128\n\t" /* expand stack frame */ \ /* arg9 */ \ "ld 3,72(11)\n\t" \ "std 3,112(1)\n\t" \ /* args1-8 */ \ "ld 3, 8(11)\n\t" /* arg1->r3 */ \ "ld 4, 16(11)\n\t" /* arg2->r4 */ \ "ld 5, 24(11)\n\t" /* arg3->r5 */ \ "ld 6, 32(11)\n\t" /* arg4->r6 */ \ "ld 7, 40(11)\n\t" /* arg5->r7 */ \ "ld 8, 48(11)\n\t" /* arg6->r8 */ \ "ld 9, 56(11)\n\t" /* arg7->r9 */ \ "ld 10, 64(11)\n\t" /* arg8->r10 */ \ "ld 11, 0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr 11,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(11)\n\t" /* restore tocptr */ \ VALGRIND_RESTORE_STACK \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9,arg10) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+10]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ _argvec[2+5] = (unsigned long)arg5; \ _argvec[2+6] = (unsigned long)arg6; \ _argvec[2+7] = (unsigned long)arg7; \ _argvec[2+8] = (unsigned long)arg8; \ _argvec[2+9] = (unsigned long)arg9; \ _argvec[2+10] = (unsigned long)arg10; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 11,%1\n\t" \ "std 2,-16(11)\n\t" /* save tocptr */ \ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ "addi 1,1,-128\n\t" /* expand stack frame */ \ /* arg10 */ \ "ld 3,80(11)\n\t" \ "std 3,120(1)\n\t" \ /* arg9 */ \ "ld 3,72(11)\n\t" \ "std 3,112(1)\n\t" \ /* args1-8 */ \ "ld 3, 8(11)\n\t" /* arg1->r3 */ \ "ld 4, 16(11)\n\t" /* arg2->r4 */ \ "ld 5, 24(11)\n\t" /* arg3->r5 */ \ "ld 6, 32(11)\n\t" /* arg4->r6 */ \ "ld 7, 40(11)\n\t" /* arg5->r7 */ \ "ld 8, 48(11)\n\t" /* arg6->r8 */ \ "ld 9, 56(11)\n\t" /* arg7->r9 */ \ "ld 10, 64(11)\n\t" /* arg8->r10 */ \ "ld 11, 0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr 11,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(11)\n\t" /* restore tocptr */ \ VALGRIND_RESTORE_STACK \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9,arg10,arg11) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+11]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ _argvec[2+5] = (unsigned long)arg5; \ _argvec[2+6] = (unsigned long)arg6; \ _argvec[2+7] = (unsigned long)arg7; \ _argvec[2+8] = (unsigned long)arg8; \ _argvec[2+9] = (unsigned long)arg9; \ _argvec[2+10] = (unsigned long)arg10; \ _argvec[2+11] = (unsigned long)arg11; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 11,%1\n\t" \ "std 2,-16(11)\n\t" /* save tocptr */ \ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ "addi 1,1,-144\n\t" /* expand stack frame */ \ /* arg11 */ \ "ld 3,88(11)\n\t" \ "std 3,128(1)\n\t" \ /* arg10 */ \ "ld 3,80(11)\n\t" \ "std 3,120(1)\n\t" \ /* arg9 */ \ "ld 3,72(11)\n\t" \ "std 3,112(1)\n\t" \ /* args1-8 */ \ "ld 3, 8(11)\n\t" /* arg1->r3 */ \ "ld 4, 16(11)\n\t" /* arg2->r4 */ \ "ld 5, 24(11)\n\t" /* arg3->r5 */ \ "ld 6, 32(11)\n\t" /* arg4->r6 */ \ "ld 7, 40(11)\n\t" /* arg5->r7 */ \ "ld 8, 48(11)\n\t" /* arg6->r8 */ \ "ld 9, 56(11)\n\t" /* arg7->r9 */ \ "ld 10, 64(11)\n\t" /* arg8->r10 */ \ "ld 11, 0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr 11,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(11)\n\t" /* restore tocptr */ \ VALGRIND_RESTORE_STACK \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9,arg10,arg11,arg12) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+12]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ _argvec[2+5] = (unsigned long)arg5; \ _argvec[2+6] = (unsigned long)arg6; \ _argvec[2+7] = (unsigned long)arg7; \ _argvec[2+8] = (unsigned long)arg8; \ _argvec[2+9] = (unsigned long)arg9; \ _argvec[2+10] = (unsigned long)arg10; \ _argvec[2+11] = (unsigned long)arg11; \ _argvec[2+12] = (unsigned long)arg12; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 11,%1\n\t" \ "std 2,-16(11)\n\t" /* save tocptr */ \ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \ "addi 1,1,-144\n\t" /* expand stack frame */ \ /* arg12 */ \ "ld 3,96(11)\n\t" \ "std 3,136(1)\n\t" \ /* arg11 */ \ "ld 3,88(11)\n\t" \ "std 3,128(1)\n\t" \ /* arg10 */ \ "ld 3,80(11)\n\t" \ "std 3,120(1)\n\t" \ /* arg9 */ \ "ld 3,72(11)\n\t" \ "std 3,112(1)\n\t" \ /* args1-8 */ \ "ld 3, 8(11)\n\t" /* arg1->r3 */ \ "ld 4, 16(11)\n\t" /* arg2->r4 */ \ "ld 5, 24(11)\n\t" /* arg3->r5 */ \ "ld 6, 32(11)\n\t" /* arg4->r6 */ \ "ld 7, 40(11)\n\t" /* arg5->r7 */ \ "ld 8, 48(11)\n\t" /* arg6->r8 */ \ "ld 9, 56(11)\n\t" /* arg7->r9 */ \ "ld 10, 64(11)\n\t" /* arg8->r10 */ \ "ld 11, 0(11)\n\t" /* target->r11 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \ "mr 11,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(11)\n\t" /* restore tocptr */ \ VALGRIND_RESTORE_STACK \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #endif /* PLAT_ppc64be_linux */ /* ------------------------- ppc64le-linux ----------------------- */ #if defined(PLAT_ppc64le_linux) /* ARGREGS: r3 r4 r5 r6 r7 r8 r9 r10 (the rest on stack somewhere) */ /* These regs are trashed by the hidden call. */ #define __CALLER_SAVED_REGS \ "lr", "ctr", "xer", \ "cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7", \ "r0", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", \ "r11", "r12", "r13" /* Macros to save and align the stack before making a function call and restore it afterwards as gcc may not keep the stack pointer aligned if it doesn't realise calls are being made to other functions. */ #define VALGRIND_ALIGN_STACK \ "mr 28,1\n\t" \ "rldicr 1,1,0,59\n\t" #define VALGRIND_RESTORE_STACK \ "mr 1,28\n\t" /* These CALL_FN_ macros assume that on ppc64-linux, sizeof(unsigned long) == 8. */ #define CALL_FN_W_v(lval, orig) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+0]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 12,%1\n\t" \ "std 2,-16(12)\n\t" /* save tocptr */ \ "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ "ld 12, 0(12)\n\t" /* target->r12 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ "mr 12,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(12)\n\t" /* restore tocptr */ \ VALGRIND_RESTORE_STACK \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_W(lval, orig, arg1) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+1]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 12,%1\n\t" \ "std 2,-16(12)\n\t" /* save tocptr */ \ "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ "ld 3, 8(12)\n\t" /* arg1->r3 */ \ "ld 12, 0(12)\n\t" /* target->r12 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ "mr 12,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(12)\n\t" /* restore tocptr */ \ VALGRIND_RESTORE_STACK \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WW(lval, orig, arg1,arg2) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+2]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 12,%1\n\t" \ "std 2,-16(12)\n\t" /* save tocptr */ \ "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ "ld 3, 8(12)\n\t" /* arg1->r3 */ \ "ld 4, 16(12)\n\t" /* arg2->r4 */ \ "ld 12, 0(12)\n\t" /* target->r12 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ "mr 12,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(12)\n\t" /* restore tocptr */ \ VALGRIND_RESTORE_STACK \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+3]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 12,%1\n\t" \ "std 2,-16(12)\n\t" /* save tocptr */ \ "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ "ld 3, 8(12)\n\t" /* arg1->r3 */ \ "ld 4, 16(12)\n\t" /* arg2->r4 */ \ "ld 5, 24(12)\n\t" /* arg3->r5 */ \ "ld 12, 0(12)\n\t" /* target->r12 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ "mr 12,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(12)\n\t" /* restore tocptr */ \ VALGRIND_RESTORE_STACK \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+4]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 12,%1\n\t" \ "std 2,-16(12)\n\t" /* save tocptr */ \ "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ "ld 3, 8(12)\n\t" /* arg1->r3 */ \ "ld 4, 16(12)\n\t" /* arg2->r4 */ \ "ld 5, 24(12)\n\t" /* arg3->r5 */ \ "ld 6, 32(12)\n\t" /* arg4->r6 */ \ "ld 12, 0(12)\n\t" /* target->r12 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ "mr 12,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(12)\n\t" /* restore tocptr */ \ VALGRIND_RESTORE_STACK \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+5]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ _argvec[2+5] = (unsigned long)arg5; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 12,%1\n\t" \ "std 2,-16(12)\n\t" /* save tocptr */ \ "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ "ld 3, 8(12)\n\t" /* arg1->r3 */ \ "ld 4, 16(12)\n\t" /* arg2->r4 */ \ "ld 5, 24(12)\n\t" /* arg3->r5 */ \ "ld 6, 32(12)\n\t" /* arg4->r6 */ \ "ld 7, 40(12)\n\t" /* arg5->r7 */ \ "ld 12, 0(12)\n\t" /* target->r12 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ "mr 12,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(12)\n\t" /* restore tocptr */ \ VALGRIND_RESTORE_STACK \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+6]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ _argvec[2+5] = (unsigned long)arg5; \ _argvec[2+6] = (unsigned long)arg6; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 12,%1\n\t" \ "std 2,-16(12)\n\t" /* save tocptr */ \ "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ "ld 3, 8(12)\n\t" /* arg1->r3 */ \ "ld 4, 16(12)\n\t" /* arg2->r4 */ \ "ld 5, 24(12)\n\t" /* arg3->r5 */ \ "ld 6, 32(12)\n\t" /* arg4->r6 */ \ "ld 7, 40(12)\n\t" /* arg5->r7 */ \ "ld 8, 48(12)\n\t" /* arg6->r8 */ \ "ld 12, 0(12)\n\t" /* target->r12 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ "mr 12,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(12)\n\t" /* restore tocptr */ \ VALGRIND_RESTORE_STACK \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+7]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ _argvec[2+5] = (unsigned long)arg5; \ _argvec[2+6] = (unsigned long)arg6; \ _argvec[2+7] = (unsigned long)arg7; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 12,%1\n\t" \ "std 2,-16(12)\n\t" /* save tocptr */ \ "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ "ld 3, 8(12)\n\t" /* arg1->r3 */ \ "ld 4, 16(12)\n\t" /* arg2->r4 */ \ "ld 5, 24(12)\n\t" /* arg3->r5 */ \ "ld 6, 32(12)\n\t" /* arg4->r6 */ \ "ld 7, 40(12)\n\t" /* arg5->r7 */ \ "ld 8, 48(12)\n\t" /* arg6->r8 */ \ "ld 9, 56(12)\n\t" /* arg7->r9 */ \ "ld 12, 0(12)\n\t" /* target->r12 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ "mr 12,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(12)\n\t" /* restore tocptr */ \ VALGRIND_RESTORE_STACK \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+8]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ _argvec[2+5] = (unsigned long)arg5; \ _argvec[2+6] = (unsigned long)arg6; \ _argvec[2+7] = (unsigned long)arg7; \ _argvec[2+8] = (unsigned long)arg8; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 12,%1\n\t" \ "std 2,-16(12)\n\t" /* save tocptr */ \ "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ "ld 3, 8(12)\n\t" /* arg1->r3 */ \ "ld 4, 16(12)\n\t" /* arg2->r4 */ \ "ld 5, 24(12)\n\t" /* arg3->r5 */ \ "ld 6, 32(12)\n\t" /* arg4->r6 */ \ "ld 7, 40(12)\n\t" /* arg5->r7 */ \ "ld 8, 48(12)\n\t" /* arg6->r8 */ \ "ld 9, 56(12)\n\t" /* arg7->r9 */ \ "ld 10, 64(12)\n\t" /* arg8->r10 */ \ "ld 12, 0(12)\n\t" /* target->r12 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ "mr 12,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(12)\n\t" /* restore tocptr */ \ VALGRIND_RESTORE_STACK \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+9]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ _argvec[2+5] = (unsigned long)arg5; \ _argvec[2+6] = (unsigned long)arg6; \ _argvec[2+7] = (unsigned long)arg7; \ _argvec[2+8] = (unsigned long)arg8; \ _argvec[2+9] = (unsigned long)arg9; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 12,%1\n\t" \ "std 2,-16(12)\n\t" /* save tocptr */ \ "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ "addi 1,1,-128\n\t" /* expand stack frame */ \ /* arg9 */ \ "ld 3,72(12)\n\t" \ "std 3,96(1)\n\t" \ /* args1-8 */ \ "ld 3, 8(12)\n\t" /* arg1->r3 */ \ "ld 4, 16(12)\n\t" /* arg2->r4 */ \ "ld 5, 24(12)\n\t" /* arg3->r5 */ \ "ld 6, 32(12)\n\t" /* arg4->r6 */ \ "ld 7, 40(12)\n\t" /* arg5->r7 */ \ "ld 8, 48(12)\n\t" /* arg6->r8 */ \ "ld 9, 56(12)\n\t" /* arg7->r9 */ \ "ld 10, 64(12)\n\t" /* arg8->r10 */ \ "ld 12, 0(12)\n\t" /* target->r12 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ "mr 12,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(12)\n\t" /* restore tocptr */ \ VALGRIND_RESTORE_STACK \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9,arg10) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+10]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ _argvec[2+5] = (unsigned long)arg5; \ _argvec[2+6] = (unsigned long)arg6; \ _argvec[2+7] = (unsigned long)arg7; \ _argvec[2+8] = (unsigned long)arg8; \ _argvec[2+9] = (unsigned long)arg9; \ _argvec[2+10] = (unsigned long)arg10; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 12,%1\n\t" \ "std 2,-16(12)\n\t" /* save tocptr */ \ "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ "addi 1,1,-128\n\t" /* expand stack frame */ \ /* arg10 */ \ "ld 3,80(12)\n\t" \ "std 3,104(1)\n\t" \ /* arg9 */ \ "ld 3,72(12)\n\t" \ "std 3,96(1)\n\t" \ /* args1-8 */ \ "ld 3, 8(12)\n\t" /* arg1->r3 */ \ "ld 4, 16(12)\n\t" /* arg2->r4 */ \ "ld 5, 24(12)\n\t" /* arg3->r5 */ \ "ld 6, 32(12)\n\t" /* arg4->r6 */ \ "ld 7, 40(12)\n\t" /* arg5->r7 */ \ "ld 8, 48(12)\n\t" /* arg6->r8 */ \ "ld 9, 56(12)\n\t" /* arg7->r9 */ \ "ld 10, 64(12)\n\t" /* arg8->r10 */ \ "ld 12, 0(12)\n\t" /* target->r12 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ "mr 12,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(12)\n\t" /* restore tocptr */ \ VALGRIND_RESTORE_STACK \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9,arg10,arg11) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+11]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ _argvec[2+5] = (unsigned long)arg5; \ _argvec[2+6] = (unsigned long)arg6; \ _argvec[2+7] = (unsigned long)arg7; \ _argvec[2+8] = (unsigned long)arg8; \ _argvec[2+9] = (unsigned long)arg9; \ _argvec[2+10] = (unsigned long)arg10; \ _argvec[2+11] = (unsigned long)arg11; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 12,%1\n\t" \ "std 2,-16(12)\n\t" /* save tocptr */ \ "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ "addi 1,1,-144\n\t" /* expand stack frame */ \ /* arg11 */ \ "ld 3,88(12)\n\t" \ "std 3,112(1)\n\t" \ /* arg10 */ \ "ld 3,80(12)\n\t" \ "std 3,104(1)\n\t" \ /* arg9 */ \ "ld 3,72(12)\n\t" \ "std 3,96(1)\n\t" \ /* args1-8 */ \ "ld 3, 8(12)\n\t" /* arg1->r3 */ \ "ld 4, 16(12)\n\t" /* arg2->r4 */ \ "ld 5, 24(12)\n\t" /* arg3->r5 */ \ "ld 6, 32(12)\n\t" /* arg4->r6 */ \ "ld 7, 40(12)\n\t" /* arg5->r7 */ \ "ld 8, 48(12)\n\t" /* arg6->r8 */ \ "ld 9, 56(12)\n\t" /* arg7->r9 */ \ "ld 10, 64(12)\n\t" /* arg8->r10 */ \ "ld 12, 0(12)\n\t" /* target->r12 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ "mr 12,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(12)\n\t" /* restore tocptr */ \ VALGRIND_RESTORE_STACK \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9,arg10,arg11,arg12) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3+12]; \ volatile unsigned long _res; \ /* _argvec[0] holds current r2 across the call */ \ _argvec[1] = (unsigned long)_orig.r2; \ _argvec[2] = (unsigned long)_orig.nraddr; \ _argvec[2+1] = (unsigned long)arg1; \ _argvec[2+2] = (unsigned long)arg2; \ _argvec[2+3] = (unsigned long)arg3; \ _argvec[2+4] = (unsigned long)arg4; \ _argvec[2+5] = (unsigned long)arg5; \ _argvec[2+6] = (unsigned long)arg6; \ _argvec[2+7] = (unsigned long)arg7; \ _argvec[2+8] = (unsigned long)arg8; \ _argvec[2+9] = (unsigned long)arg9; \ _argvec[2+10] = (unsigned long)arg10; \ _argvec[2+11] = (unsigned long)arg11; \ _argvec[2+12] = (unsigned long)arg12; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "mr 12,%1\n\t" \ "std 2,-16(12)\n\t" /* save tocptr */ \ "ld 2,-8(12)\n\t" /* use nraddr's tocptr */ \ "addi 1,1,-144\n\t" /* expand stack frame */ \ /* arg12 */ \ "ld 3,96(12)\n\t" \ "std 3,120(1)\n\t" \ /* arg11 */ \ "ld 3,88(12)\n\t" \ "std 3,112(1)\n\t" \ /* arg10 */ \ "ld 3,80(12)\n\t" \ "std 3,104(1)\n\t" \ /* arg9 */ \ "ld 3,72(12)\n\t" \ "std 3,96(1)\n\t" \ /* args1-8 */ \ "ld 3, 8(12)\n\t" /* arg1->r3 */ \ "ld 4, 16(12)\n\t" /* arg2->r4 */ \ "ld 5, 24(12)\n\t" /* arg3->r5 */ \ "ld 6, 32(12)\n\t" /* arg4->r6 */ \ "ld 7, 40(12)\n\t" /* arg5->r7 */ \ "ld 8, 48(12)\n\t" /* arg6->r8 */ \ "ld 9, 56(12)\n\t" /* arg7->r9 */ \ "ld 10, 64(12)\n\t" /* arg8->r10 */ \ "ld 12, 0(12)\n\t" /* target->r12 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R12 \ "mr 12,%1\n\t" \ "mr %0,3\n\t" \ "ld 2,-16(12)\n\t" /* restore tocptr */ \ VALGRIND_RESTORE_STACK \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[2]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #endif /* PLAT_ppc64le_linux */ /* ------------------------- arm-linux ------------------------- */ #if defined(PLAT_arm_linux) /* These regs are trashed by the hidden call. */ #define __CALLER_SAVED_REGS "r0", "r1", "r2", "r3","r4", "r12", "r14" /* Macros to save and align the stack before making a function call and restore it afterwards as gcc may not keep the stack pointer aligned if it doesn't realise calls are being made to other functions. */ /* This is a bit tricky. We store the original stack pointer in r10 as it is callee-saves. gcc doesn't allow the use of r11 for some reason. Also, we can't directly "bic" the stack pointer in thumb mode since r13 isn't an allowed register number in that context. So use r4 as a temporary, since that is about to get trashed anyway, just after each use of this macro. Side effect is we need to be very careful about any future changes, since VALGRIND_ALIGN_STACK simply assumes r4 is usable. */ #define VALGRIND_ALIGN_STACK \ "mov r10, sp\n\t" \ "mov r4, sp\n\t" \ "bic r4, r4, #7\n\t" \ "mov sp, r4\n\t" #define VALGRIND_RESTORE_STACK \ "mov sp, r10\n\t" /* These CALL_FN_ macros assume that on arm-linux, sizeof(unsigned long) == 4. */ #define CALL_FN_W_v(lval, orig) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[1]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "ldr r4, [%1] \n\t" /* target->r4 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ VALGRIND_RESTORE_STACK \ "mov %0, r0\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_W(lval, orig, arg1) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[2]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "ldr r0, [%1, #4] \n\t" \ "ldr r4, [%1] \n\t" /* target->r4 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ VALGRIND_RESTORE_STACK \ "mov %0, r0\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WW(lval, orig, arg1,arg2) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "ldr r0, [%1, #4] \n\t" \ "ldr r1, [%1, #8] \n\t" \ "ldr r4, [%1] \n\t" /* target->r4 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ VALGRIND_RESTORE_STACK \ "mov %0, r0\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[4]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "ldr r0, [%1, #4] \n\t" \ "ldr r1, [%1, #8] \n\t" \ "ldr r2, [%1, #12] \n\t" \ "ldr r4, [%1] \n\t" /* target->r4 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ VALGRIND_RESTORE_STACK \ "mov %0, r0\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[5]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "ldr r0, [%1, #4] \n\t" \ "ldr r1, [%1, #8] \n\t" \ "ldr r2, [%1, #12] \n\t" \ "ldr r3, [%1, #16] \n\t" \ "ldr r4, [%1] \n\t" /* target->r4 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ VALGRIND_RESTORE_STACK \ "mov %0, r0" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[6]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "sub sp, sp, #4 \n\t" \ "ldr r0, [%1, #20] \n\t" \ "push {r0} \n\t" \ "ldr r0, [%1, #4] \n\t" \ "ldr r1, [%1, #8] \n\t" \ "ldr r2, [%1, #12] \n\t" \ "ldr r3, [%1, #16] \n\t" \ "ldr r4, [%1] \n\t" /* target->r4 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ VALGRIND_RESTORE_STACK \ "mov %0, r0" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[7]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "ldr r0, [%1, #20] \n\t" \ "ldr r1, [%1, #24] \n\t" \ "push {r0, r1} \n\t" \ "ldr r0, [%1, #4] \n\t" \ "ldr r1, [%1, #8] \n\t" \ "ldr r2, [%1, #12] \n\t" \ "ldr r3, [%1, #16] \n\t" \ "ldr r4, [%1] \n\t" /* target->r4 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ VALGRIND_RESTORE_STACK \ "mov %0, r0" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[8]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "sub sp, sp, #4 \n\t" \ "ldr r0, [%1, #20] \n\t" \ "ldr r1, [%1, #24] \n\t" \ "ldr r2, [%1, #28] \n\t" \ "push {r0, r1, r2} \n\t" \ "ldr r0, [%1, #4] \n\t" \ "ldr r1, [%1, #8] \n\t" \ "ldr r2, [%1, #12] \n\t" \ "ldr r3, [%1, #16] \n\t" \ "ldr r4, [%1] \n\t" /* target->r4 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ VALGRIND_RESTORE_STACK \ "mov %0, r0" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[9]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "ldr r0, [%1, #20] \n\t" \ "ldr r1, [%1, #24] \n\t" \ "ldr r2, [%1, #28] \n\t" \ "ldr r3, [%1, #32] \n\t" \ "push {r0, r1, r2, r3} \n\t" \ "ldr r0, [%1, #4] \n\t" \ "ldr r1, [%1, #8] \n\t" \ "ldr r2, [%1, #12] \n\t" \ "ldr r3, [%1, #16] \n\t" \ "ldr r4, [%1] \n\t" /* target->r4 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ VALGRIND_RESTORE_STACK \ "mov %0, r0" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[10]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "sub sp, sp, #4 \n\t" \ "ldr r0, [%1, #20] \n\t" \ "ldr r1, [%1, #24] \n\t" \ "ldr r2, [%1, #28] \n\t" \ "ldr r3, [%1, #32] \n\t" \ "ldr r4, [%1, #36] \n\t" \ "push {r0, r1, r2, r3, r4} \n\t" \ "ldr r0, [%1, #4] \n\t" \ "ldr r1, [%1, #8] \n\t" \ "ldr r2, [%1, #12] \n\t" \ "ldr r3, [%1, #16] \n\t" \ "ldr r4, [%1] \n\t" /* target->r4 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ VALGRIND_RESTORE_STACK \ "mov %0, r0" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9,arg10) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[11]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ _argvec[10] = (unsigned long)(arg10); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "ldr r0, [%1, #40] \n\t" \ "push {r0} \n\t" \ "ldr r0, [%1, #20] \n\t" \ "ldr r1, [%1, #24] \n\t" \ "ldr r2, [%1, #28] \n\t" \ "ldr r3, [%1, #32] \n\t" \ "ldr r4, [%1, #36] \n\t" \ "push {r0, r1, r2, r3, r4} \n\t" \ "ldr r0, [%1, #4] \n\t" \ "ldr r1, [%1, #8] \n\t" \ "ldr r2, [%1, #12] \n\t" \ "ldr r3, [%1, #16] \n\t" \ "ldr r4, [%1] \n\t" /* target->r4 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ VALGRIND_RESTORE_STACK \ "mov %0, r0" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ arg6,arg7,arg8,arg9,arg10, \ arg11) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[12]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ _argvec[10] = (unsigned long)(arg10); \ _argvec[11] = (unsigned long)(arg11); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "sub sp, sp, #4 \n\t" \ "ldr r0, [%1, #40] \n\t" \ "ldr r1, [%1, #44] \n\t" \ "push {r0, r1} \n\t" \ "ldr r0, [%1, #20] \n\t" \ "ldr r1, [%1, #24] \n\t" \ "ldr r2, [%1, #28] \n\t" \ "ldr r3, [%1, #32] \n\t" \ "ldr r4, [%1, #36] \n\t" \ "push {r0, r1, r2, r3, r4} \n\t" \ "ldr r0, [%1, #4] \n\t" \ "ldr r1, [%1, #8] \n\t" \ "ldr r2, [%1, #12] \n\t" \ "ldr r3, [%1, #16] \n\t" \ "ldr r4, [%1] \n\t" /* target->r4 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ VALGRIND_RESTORE_STACK \ "mov %0, r0" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ arg6,arg7,arg8,arg9,arg10, \ arg11,arg12) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[13]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ _argvec[10] = (unsigned long)(arg10); \ _argvec[11] = (unsigned long)(arg11); \ _argvec[12] = (unsigned long)(arg12); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "ldr r0, [%1, #40] \n\t" \ "ldr r1, [%1, #44] \n\t" \ "ldr r2, [%1, #48] \n\t" \ "push {r0, r1, r2} \n\t" \ "ldr r0, [%1, #20] \n\t" \ "ldr r1, [%1, #24] \n\t" \ "ldr r2, [%1, #28] \n\t" \ "ldr r3, [%1, #32] \n\t" \ "ldr r4, [%1, #36] \n\t" \ "push {r0, r1, r2, r3, r4} \n\t" \ "ldr r0, [%1, #4] \n\t" \ "ldr r1, [%1, #8] \n\t" \ "ldr r2, [%1, #12] \n\t" \ "ldr r3, [%1, #16] \n\t" \ "ldr r4, [%1] \n\t" /* target->r4 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \ VALGRIND_RESTORE_STACK \ "mov %0, r0" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #endif /* PLAT_arm_linux */ /* ------------------------ arm64-linux ------------------------ */ #if defined(PLAT_arm64_linux) /* These regs are trashed by the hidden call. */ #define __CALLER_SAVED_REGS \ "x0", "x1", "x2", "x3","x4", "x5", "x6", "x7", "x8", "x9", \ "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", \ "x18", "x19", "x20", "x30", \ "v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7", "v8", "v9", \ "v10", "v11", "v12", "v13", "v14", "v15", "v16", "v17", \ "v18", "v19", "v20", "v21", "v22", "v23", "v24", "v25", \ "v26", "v27", "v28", "v29", "v30", "v31" /* x21 is callee-saved, so we can use it to save and restore SP around the hidden call. */ #define VALGRIND_ALIGN_STACK \ "mov x21, sp\n\t" \ "bic sp, x21, #15\n\t" #define VALGRIND_RESTORE_STACK \ "mov sp, x21\n\t" /* These CALL_FN_ macros assume that on arm64-linux, sizeof(unsigned long) == 8. */ #define CALL_FN_W_v(lval, orig) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[1]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "ldr x8, [%1] \n\t" /* target->x8 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ VALGRIND_RESTORE_STACK \ "mov %0, x0\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_W(lval, orig, arg1) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[2]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "ldr x0, [%1, #8] \n\t" \ "ldr x8, [%1] \n\t" /* target->x8 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ VALGRIND_RESTORE_STACK \ "mov %0, x0\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WW(lval, orig, arg1,arg2) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "ldr x0, [%1, #8] \n\t" \ "ldr x1, [%1, #16] \n\t" \ "ldr x8, [%1] \n\t" /* target->x8 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ VALGRIND_RESTORE_STACK \ "mov %0, x0\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[4]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "ldr x0, [%1, #8] \n\t" \ "ldr x1, [%1, #16] \n\t" \ "ldr x2, [%1, #24] \n\t" \ "ldr x8, [%1] \n\t" /* target->x8 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ VALGRIND_RESTORE_STACK \ "mov %0, x0\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[5]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "ldr x0, [%1, #8] \n\t" \ "ldr x1, [%1, #16] \n\t" \ "ldr x2, [%1, #24] \n\t" \ "ldr x3, [%1, #32] \n\t" \ "ldr x8, [%1] \n\t" /* target->x8 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ VALGRIND_RESTORE_STACK \ "mov %0, x0" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[6]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "ldr x0, [%1, #8] \n\t" \ "ldr x1, [%1, #16] \n\t" \ "ldr x2, [%1, #24] \n\t" \ "ldr x3, [%1, #32] \n\t" \ "ldr x4, [%1, #40] \n\t" \ "ldr x8, [%1] \n\t" /* target->x8 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ VALGRIND_RESTORE_STACK \ "mov %0, x0" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[7]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "ldr x0, [%1, #8] \n\t" \ "ldr x1, [%1, #16] \n\t" \ "ldr x2, [%1, #24] \n\t" \ "ldr x3, [%1, #32] \n\t" \ "ldr x4, [%1, #40] \n\t" \ "ldr x5, [%1, #48] \n\t" \ "ldr x8, [%1] \n\t" /* target->x8 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ VALGRIND_RESTORE_STACK \ "mov %0, x0" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[8]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "ldr x0, [%1, #8] \n\t" \ "ldr x1, [%1, #16] \n\t" \ "ldr x2, [%1, #24] \n\t" \ "ldr x3, [%1, #32] \n\t" \ "ldr x4, [%1, #40] \n\t" \ "ldr x5, [%1, #48] \n\t" \ "ldr x6, [%1, #56] \n\t" \ "ldr x8, [%1] \n\t" /* target->x8 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ VALGRIND_RESTORE_STACK \ "mov %0, x0" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[9]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "ldr x0, [%1, #8] \n\t" \ "ldr x1, [%1, #16] \n\t" \ "ldr x2, [%1, #24] \n\t" \ "ldr x3, [%1, #32] \n\t" \ "ldr x4, [%1, #40] \n\t" \ "ldr x5, [%1, #48] \n\t" \ "ldr x6, [%1, #56] \n\t" \ "ldr x7, [%1, #64] \n\t" \ "ldr x8, [%1] \n\t" /* target->x8 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ VALGRIND_RESTORE_STACK \ "mov %0, x0" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[10]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "sub sp, sp, #0x20 \n\t" \ "ldr x0, [%1, #8] \n\t" \ "ldr x1, [%1, #16] \n\t" \ "ldr x2, [%1, #24] \n\t" \ "ldr x3, [%1, #32] \n\t" \ "ldr x4, [%1, #40] \n\t" \ "ldr x5, [%1, #48] \n\t" \ "ldr x6, [%1, #56] \n\t" \ "ldr x7, [%1, #64] \n\t" \ "ldr x8, [%1, #72] \n\t" \ "str x8, [sp, #0] \n\t" \ "ldr x8, [%1] \n\t" /* target->x8 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ VALGRIND_RESTORE_STACK \ "mov %0, x0" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9,arg10) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[11]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ _argvec[10] = (unsigned long)(arg10); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "sub sp, sp, #0x20 \n\t" \ "ldr x0, [%1, #8] \n\t" \ "ldr x1, [%1, #16] \n\t" \ "ldr x2, [%1, #24] \n\t" \ "ldr x3, [%1, #32] \n\t" \ "ldr x4, [%1, #40] \n\t" \ "ldr x5, [%1, #48] \n\t" \ "ldr x6, [%1, #56] \n\t" \ "ldr x7, [%1, #64] \n\t" \ "ldr x8, [%1, #72] \n\t" \ "str x8, [sp, #0] \n\t" \ "ldr x8, [%1, #80] \n\t" \ "str x8, [sp, #8] \n\t" \ "ldr x8, [%1] \n\t" /* target->x8 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ VALGRIND_RESTORE_STACK \ "mov %0, x0" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9,arg10,arg11) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[12]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ _argvec[10] = (unsigned long)(arg10); \ _argvec[11] = (unsigned long)(arg11); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "sub sp, sp, #0x30 \n\t" \ "ldr x0, [%1, #8] \n\t" \ "ldr x1, [%1, #16] \n\t" \ "ldr x2, [%1, #24] \n\t" \ "ldr x3, [%1, #32] \n\t" \ "ldr x4, [%1, #40] \n\t" \ "ldr x5, [%1, #48] \n\t" \ "ldr x6, [%1, #56] \n\t" \ "ldr x7, [%1, #64] \n\t" \ "ldr x8, [%1, #72] \n\t" \ "str x8, [sp, #0] \n\t" \ "ldr x8, [%1, #80] \n\t" \ "str x8, [sp, #8] \n\t" \ "ldr x8, [%1, #88] \n\t" \ "str x8, [sp, #16] \n\t" \ "ldr x8, [%1] \n\t" /* target->x8 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ VALGRIND_RESTORE_STACK \ "mov %0, x0" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9,arg10,arg11, \ arg12) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[13]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ _argvec[10] = (unsigned long)(arg10); \ _argvec[11] = (unsigned long)(arg11); \ _argvec[12] = (unsigned long)(arg12); \ __asm__ volatile( \ VALGRIND_ALIGN_STACK \ "sub sp, sp, #0x30 \n\t" \ "ldr x0, [%1, #8] \n\t" \ "ldr x1, [%1, #16] \n\t" \ "ldr x2, [%1, #24] \n\t" \ "ldr x3, [%1, #32] \n\t" \ "ldr x4, [%1, #40] \n\t" \ "ldr x5, [%1, #48] \n\t" \ "ldr x6, [%1, #56] \n\t" \ "ldr x7, [%1, #64] \n\t" \ "ldr x8, [%1, #72] \n\t" \ "str x8, [sp, #0] \n\t" \ "ldr x8, [%1, #80] \n\t" \ "str x8, [sp, #8] \n\t" \ "ldr x8, [%1, #88] \n\t" \ "str x8, [sp, #16] \n\t" \ "ldr x8, [%1, #96] \n\t" \ "str x8, [sp, #24] \n\t" \ "ldr x8, [%1] \n\t" /* target->x8 */ \ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_X8 \ VALGRIND_RESTORE_STACK \ "mov %0, x0" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "x21" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #endif /* PLAT_arm64_linux */ /* ------------------------- s390x-linux ------------------------- */ #if defined(PLAT_s390x_linux) /* Similar workaround as amd64 (see above), but we use r11 as frame pointer and save the old r11 in r7. r11 might be used for argvec, therefore we copy argvec in r1 since r1 is clobbered after the call anyway. */ #if defined(__GNUC__) && defined(__GCC_HAVE_DWARF2_CFI_ASM) # define __FRAME_POINTER \ ,"d"(__builtin_dwarf_cfa()) # define VALGRIND_CFI_PROLOGUE \ ".cfi_remember_state\n\t" \ "lgr 1,%1\n\t" /* copy the argvec pointer in r1 */ \ "lgr 7,11\n\t" \ "lgr 11,%2\n\t" \ ".cfi_def_cfa r11, 0\n\t" # define VALGRIND_CFI_EPILOGUE \ "lgr 11, 7\n\t" \ ".cfi_restore_state\n\t" #else # define __FRAME_POINTER # define VALGRIND_CFI_PROLOGUE \ "lgr 1,%1\n\t" # define VALGRIND_CFI_EPILOGUE #endif /* Nb: On s390 the stack pointer is properly aligned *at all times* according to the s390 GCC maintainer. (The ABI specification is not precise in this regard.) Therefore, VALGRIND_ALIGN_STACK and VALGRIND_RESTORE_STACK are not defined here. */ /* These regs are trashed by the hidden call. Note that we overwrite r14 in s390_irgen_noredir (VEX/priv/guest_s390_irgen.c) to give the function a proper return address. All others are ABI defined call clobbers. */ #define __CALLER_SAVED_REGS "0","1","2","3","4","5","14", \ "f0","f1","f2","f3","f4","f5","f6","f7" /* Nb: Although r11 is modified in the asm snippets below (inside VALGRIND_CFI_PROLOGUE) it is not listed in the clobber section, for two reasons: (1) r11 is restored in VALGRIND_CFI_EPILOGUE, so effectively it is not modified (2) GCC will complain that r11 cannot appear inside a clobber section, when compiled with -O -fno-omit-frame-pointer */ #define CALL_FN_W_v(lval, orig) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[1]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ __asm__ volatile( \ VALGRIND_CFI_PROLOGUE \ "aghi 15,-160\n\t" \ "lg 1, 0(1)\n\t" /* target->r1 */ \ VALGRIND_CALL_NOREDIR_R1 \ "lgr %0, 2\n\t" \ "aghi 15,160\n\t" \ VALGRIND_CFI_EPILOGUE \ : /*out*/ "=d" (_res) \ : /*in*/ "d" (&_argvec[0]) __FRAME_POINTER \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"7" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) /* The call abi has the arguments in r2-r6 and stack */ #define CALL_FN_W_W(lval, orig, arg1) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[2]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ __asm__ volatile( \ VALGRIND_CFI_PROLOGUE \ "aghi 15,-160\n\t" \ "lg 2, 8(1)\n\t" \ "lg 1, 0(1)\n\t" \ VALGRIND_CALL_NOREDIR_R1 \ "lgr %0, 2\n\t" \ "aghi 15,160\n\t" \ VALGRIND_CFI_EPILOGUE \ : /*out*/ "=d" (_res) \ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"7" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WW(lval, orig, arg1, arg2) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ _argvec[2] = (unsigned long)arg2; \ __asm__ volatile( \ VALGRIND_CFI_PROLOGUE \ "aghi 15,-160\n\t" \ "lg 2, 8(1)\n\t" \ "lg 3,16(1)\n\t" \ "lg 1, 0(1)\n\t" \ VALGRIND_CALL_NOREDIR_R1 \ "lgr %0, 2\n\t" \ "aghi 15,160\n\t" \ VALGRIND_CFI_EPILOGUE \ : /*out*/ "=d" (_res) \ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"7" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WWW(lval, orig, arg1, arg2, arg3) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[4]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ _argvec[2] = (unsigned long)arg2; \ _argvec[3] = (unsigned long)arg3; \ __asm__ volatile( \ VALGRIND_CFI_PROLOGUE \ "aghi 15,-160\n\t" \ "lg 2, 8(1)\n\t" \ "lg 3,16(1)\n\t" \ "lg 4,24(1)\n\t" \ "lg 1, 0(1)\n\t" \ VALGRIND_CALL_NOREDIR_R1 \ "lgr %0, 2\n\t" \ "aghi 15,160\n\t" \ VALGRIND_CFI_EPILOGUE \ : /*out*/ "=d" (_res) \ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"7" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WWWW(lval, orig, arg1, arg2, arg3, arg4) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[5]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ _argvec[2] = (unsigned long)arg2; \ _argvec[3] = (unsigned long)arg3; \ _argvec[4] = (unsigned long)arg4; \ __asm__ volatile( \ VALGRIND_CFI_PROLOGUE \ "aghi 15,-160\n\t" \ "lg 2, 8(1)\n\t" \ "lg 3,16(1)\n\t" \ "lg 4,24(1)\n\t" \ "lg 5,32(1)\n\t" \ "lg 1, 0(1)\n\t" \ VALGRIND_CALL_NOREDIR_R1 \ "lgr %0, 2\n\t" \ "aghi 15,160\n\t" \ VALGRIND_CFI_EPILOGUE \ : /*out*/ "=d" (_res) \ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"7" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_5W(lval, orig, arg1, arg2, arg3, arg4, arg5) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[6]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ _argvec[2] = (unsigned long)arg2; \ _argvec[3] = (unsigned long)arg3; \ _argvec[4] = (unsigned long)arg4; \ _argvec[5] = (unsigned long)arg5; \ __asm__ volatile( \ VALGRIND_CFI_PROLOGUE \ "aghi 15,-160\n\t" \ "lg 2, 8(1)\n\t" \ "lg 3,16(1)\n\t" \ "lg 4,24(1)\n\t" \ "lg 5,32(1)\n\t" \ "lg 6,40(1)\n\t" \ "lg 1, 0(1)\n\t" \ VALGRIND_CALL_NOREDIR_R1 \ "lgr %0, 2\n\t" \ "aghi 15,160\n\t" \ VALGRIND_CFI_EPILOGUE \ : /*out*/ "=d" (_res) \ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_6W(lval, orig, arg1, arg2, arg3, arg4, arg5, \ arg6) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[7]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ _argvec[2] = (unsigned long)arg2; \ _argvec[3] = (unsigned long)arg3; \ _argvec[4] = (unsigned long)arg4; \ _argvec[5] = (unsigned long)arg5; \ _argvec[6] = (unsigned long)arg6; \ __asm__ volatile( \ VALGRIND_CFI_PROLOGUE \ "aghi 15,-168\n\t" \ "lg 2, 8(1)\n\t" \ "lg 3,16(1)\n\t" \ "lg 4,24(1)\n\t" \ "lg 5,32(1)\n\t" \ "lg 6,40(1)\n\t" \ "mvc 160(8,15), 48(1)\n\t" \ "lg 1, 0(1)\n\t" \ VALGRIND_CALL_NOREDIR_R1 \ "lgr %0, 2\n\t" \ "aghi 15,168\n\t" \ VALGRIND_CFI_EPILOGUE \ : /*out*/ "=d" (_res) \ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_7W(lval, orig, arg1, arg2, arg3, arg4, arg5, \ arg6, arg7) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[8]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ _argvec[2] = (unsigned long)arg2; \ _argvec[3] = (unsigned long)arg3; \ _argvec[4] = (unsigned long)arg4; \ _argvec[5] = (unsigned long)arg5; \ _argvec[6] = (unsigned long)arg6; \ _argvec[7] = (unsigned long)arg7; \ __asm__ volatile( \ VALGRIND_CFI_PROLOGUE \ "aghi 15,-176\n\t" \ "lg 2, 8(1)\n\t" \ "lg 3,16(1)\n\t" \ "lg 4,24(1)\n\t" \ "lg 5,32(1)\n\t" \ "lg 6,40(1)\n\t" \ "mvc 160(8,15), 48(1)\n\t" \ "mvc 168(8,15), 56(1)\n\t" \ "lg 1, 0(1)\n\t" \ VALGRIND_CALL_NOREDIR_R1 \ "lgr %0, 2\n\t" \ "aghi 15,176\n\t" \ VALGRIND_CFI_EPILOGUE \ : /*out*/ "=d" (_res) \ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_8W(lval, orig, arg1, arg2, arg3, arg4, arg5, \ arg6, arg7 ,arg8) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[9]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ _argvec[2] = (unsigned long)arg2; \ _argvec[3] = (unsigned long)arg3; \ _argvec[4] = (unsigned long)arg4; \ _argvec[5] = (unsigned long)arg5; \ _argvec[6] = (unsigned long)arg6; \ _argvec[7] = (unsigned long)arg7; \ _argvec[8] = (unsigned long)arg8; \ __asm__ volatile( \ VALGRIND_CFI_PROLOGUE \ "aghi 15,-184\n\t" \ "lg 2, 8(1)\n\t" \ "lg 3,16(1)\n\t" \ "lg 4,24(1)\n\t" \ "lg 5,32(1)\n\t" \ "lg 6,40(1)\n\t" \ "mvc 160(8,15), 48(1)\n\t" \ "mvc 168(8,15), 56(1)\n\t" \ "mvc 176(8,15), 64(1)\n\t" \ "lg 1, 0(1)\n\t" \ VALGRIND_CALL_NOREDIR_R1 \ "lgr %0, 2\n\t" \ "aghi 15,184\n\t" \ VALGRIND_CFI_EPILOGUE \ : /*out*/ "=d" (_res) \ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_9W(lval, orig, arg1, arg2, arg3, arg4, arg5, \ arg6, arg7 ,arg8, arg9) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[10]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ _argvec[2] = (unsigned long)arg2; \ _argvec[3] = (unsigned long)arg3; \ _argvec[4] = (unsigned long)arg4; \ _argvec[5] = (unsigned long)arg5; \ _argvec[6] = (unsigned long)arg6; \ _argvec[7] = (unsigned long)arg7; \ _argvec[8] = (unsigned long)arg8; \ _argvec[9] = (unsigned long)arg9; \ __asm__ volatile( \ VALGRIND_CFI_PROLOGUE \ "aghi 15,-192\n\t" \ "lg 2, 8(1)\n\t" \ "lg 3,16(1)\n\t" \ "lg 4,24(1)\n\t" \ "lg 5,32(1)\n\t" \ "lg 6,40(1)\n\t" \ "mvc 160(8,15), 48(1)\n\t" \ "mvc 168(8,15), 56(1)\n\t" \ "mvc 176(8,15), 64(1)\n\t" \ "mvc 184(8,15), 72(1)\n\t" \ "lg 1, 0(1)\n\t" \ VALGRIND_CALL_NOREDIR_R1 \ "lgr %0, 2\n\t" \ "aghi 15,192\n\t" \ VALGRIND_CFI_EPILOGUE \ : /*out*/ "=d" (_res) \ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_10W(lval, orig, arg1, arg2, arg3, arg4, arg5, \ arg6, arg7 ,arg8, arg9, arg10) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[11]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ _argvec[2] = (unsigned long)arg2; \ _argvec[3] = (unsigned long)arg3; \ _argvec[4] = (unsigned long)arg4; \ _argvec[5] = (unsigned long)arg5; \ _argvec[6] = (unsigned long)arg6; \ _argvec[7] = (unsigned long)arg7; \ _argvec[8] = (unsigned long)arg8; \ _argvec[9] = (unsigned long)arg9; \ _argvec[10] = (unsigned long)arg10; \ __asm__ volatile( \ VALGRIND_CFI_PROLOGUE \ "aghi 15,-200\n\t" \ "lg 2, 8(1)\n\t" \ "lg 3,16(1)\n\t" \ "lg 4,24(1)\n\t" \ "lg 5,32(1)\n\t" \ "lg 6,40(1)\n\t" \ "mvc 160(8,15), 48(1)\n\t" \ "mvc 168(8,15), 56(1)\n\t" \ "mvc 176(8,15), 64(1)\n\t" \ "mvc 184(8,15), 72(1)\n\t" \ "mvc 192(8,15), 80(1)\n\t" \ "lg 1, 0(1)\n\t" \ VALGRIND_CALL_NOREDIR_R1 \ "lgr %0, 2\n\t" \ "aghi 15,200\n\t" \ VALGRIND_CFI_EPILOGUE \ : /*out*/ "=d" (_res) \ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_11W(lval, orig, arg1, arg2, arg3, arg4, arg5, \ arg6, arg7 ,arg8, arg9, arg10, arg11) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[12]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ _argvec[2] = (unsigned long)arg2; \ _argvec[3] = (unsigned long)arg3; \ _argvec[4] = (unsigned long)arg4; \ _argvec[5] = (unsigned long)arg5; \ _argvec[6] = (unsigned long)arg6; \ _argvec[7] = (unsigned long)arg7; \ _argvec[8] = (unsigned long)arg8; \ _argvec[9] = (unsigned long)arg9; \ _argvec[10] = (unsigned long)arg10; \ _argvec[11] = (unsigned long)arg11; \ __asm__ volatile( \ VALGRIND_CFI_PROLOGUE \ "aghi 15,-208\n\t" \ "lg 2, 8(1)\n\t" \ "lg 3,16(1)\n\t" \ "lg 4,24(1)\n\t" \ "lg 5,32(1)\n\t" \ "lg 6,40(1)\n\t" \ "mvc 160(8,15), 48(1)\n\t" \ "mvc 168(8,15), 56(1)\n\t" \ "mvc 176(8,15), 64(1)\n\t" \ "mvc 184(8,15), 72(1)\n\t" \ "mvc 192(8,15), 80(1)\n\t" \ "mvc 200(8,15), 88(1)\n\t" \ "lg 1, 0(1)\n\t" \ VALGRIND_CALL_NOREDIR_R1 \ "lgr %0, 2\n\t" \ "aghi 15,208\n\t" \ VALGRIND_CFI_EPILOGUE \ : /*out*/ "=d" (_res) \ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_12W(lval, orig, arg1, arg2, arg3, arg4, arg5, \ arg6, arg7 ,arg8, arg9, arg10, arg11, arg12)\ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[13]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)arg1; \ _argvec[2] = (unsigned long)arg2; \ _argvec[3] = (unsigned long)arg3; \ _argvec[4] = (unsigned long)arg4; \ _argvec[5] = (unsigned long)arg5; \ _argvec[6] = (unsigned long)arg6; \ _argvec[7] = (unsigned long)arg7; \ _argvec[8] = (unsigned long)arg8; \ _argvec[9] = (unsigned long)arg9; \ _argvec[10] = (unsigned long)arg10; \ _argvec[11] = (unsigned long)arg11; \ _argvec[12] = (unsigned long)arg12; \ __asm__ volatile( \ VALGRIND_CFI_PROLOGUE \ "aghi 15,-216\n\t" \ "lg 2, 8(1)\n\t" \ "lg 3,16(1)\n\t" \ "lg 4,24(1)\n\t" \ "lg 5,32(1)\n\t" \ "lg 6,40(1)\n\t" \ "mvc 160(8,15), 48(1)\n\t" \ "mvc 168(8,15), 56(1)\n\t" \ "mvc 176(8,15), 64(1)\n\t" \ "mvc 184(8,15), 72(1)\n\t" \ "mvc 192(8,15), 80(1)\n\t" \ "mvc 200(8,15), 88(1)\n\t" \ "mvc 208(8,15), 96(1)\n\t" \ "lg 1, 0(1)\n\t" \ VALGRIND_CALL_NOREDIR_R1 \ "lgr %0, 2\n\t" \ "aghi 15,216\n\t" \ VALGRIND_CFI_EPILOGUE \ : /*out*/ "=d" (_res) \ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #endif /* PLAT_s390x_linux */ /* ------------------------- mips32-linux ----------------------- */ #if defined(PLAT_mips32_linux) /* These regs are trashed by the hidden call. */ #define __CALLER_SAVED_REGS "$2", "$3", "$4", "$5", "$6", \ "$7", "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", "$24", \ "$25", "$31" /* These CALL_FN_ macros assume that on mips-linux, sizeof(unsigned long) == 4. */ #define CALL_FN_W_v(lval, orig) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[1]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ __asm__ volatile( \ "subu $29, $29, 8 \n\t" \ "sw $28, 0($29) \n\t" \ "sw $31, 4($29) \n\t" \ "subu $29, $29, 16 \n\t" \ "lw $25, 0(%1) \n\t" /* target->t9 */ \ VALGRIND_CALL_NOREDIR_T9 \ "addu $29, $29, 16\n\t" \ "lw $28, 0($29) \n\t" \ "lw $31, 4($29) \n\t" \ "addu $29, $29, 8 \n\t" \ "move %0, $2\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_W(lval, orig, arg1) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[2]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ __asm__ volatile( \ "subu $29, $29, 8 \n\t" \ "sw $28, 0($29) \n\t" \ "sw $31, 4($29) \n\t" \ "subu $29, $29, 16 \n\t" \ "lw $4, 4(%1) \n\t" /* arg1*/ \ "lw $25, 0(%1) \n\t" /* target->t9 */ \ VALGRIND_CALL_NOREDIR_T9 \ "addu $29, $29, 16 \n\t" \ "lw $28, 0($29) \n\t" \ "lw $31, 4($29) \n\t" \ "addu $29, $29, 8 \n\t" \ "move %0, $2\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WW(lval, orig, arg1,arg2) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ __asm__ volatile( \ "subu $29, $29, 8 \n\t" \ "sw $28, 0($29) \n\t" \ "sw $31, 4($29) \n\t" \ "subu $29, $29, 16 \n\t" \ "lw $4, 4(%1) \n\t" \ "lw $5, 8(%1) \n\t" \ "lw $25, 0(%1) \n\t" /* target->t9 */ \ VALGRIND_CALL_NOREDIR_T9 \ "addu $29, $29, 16 \n\t" \ "lw $28, 0($29) \n\t" \ "lw $31, 4($29) \n\t" \ "addu $29, $29, 8 \n\t" \ "move %0, $2\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[4]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ __asm__ volatile( \ "subu $29, $29, 8 \n\t" \ "sw $28, 0($29) \n\t" \ "sw $31, 4($29) \n\t" \ "subu $29, $29, 16 \n\t" \ "lw $4, 4(%1) \n\t" \ "lw $5, 8(%1) \n\t" \ "lw $6, 12(%1) \n\t" \ "lw $25, 0(%1) \n\t" /* target->t9 */ \ VALGRIND_CALL_NOREDIR_T9 \ "addu $29, $29, 16 \n\t" \ "lw $28, 0($29) \n\t" \ "lw $31, 4($29) \n\t" \ "addu $29, $29, 8 \n\t" \ "move %0, $2\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[5]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ __asm__ volatile( \ "subu $29, $29, 8 \n\t" \ "sw $28, 0($29) \n\t" \ "sw $31, 4($29) \n\t" \ "subu $29, $29, 16 \n\t" \ "lw $4, 4(%1) \n\t" \ "lw $5, 8(%1) \n\t" \ "lw $6, 12(%1) \n\t" \ "lw $7, 16(%1) \n\t" \ "lw $25, 0(%1) \n\t" /* target->t9 */ \ VALGRIND_CALL_NOREDIR_T9 \ "addu $29, $29, 16 \n\t" \ "lw $28, 0($29) \n\t" \ "lw $31, 4($29) \n\t" \ "addu $29, $29, 8 \n\t" \ "move %0, $2\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[6]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ __asm__ volatile( \ "subu $29, $29, 8 \n\t" \ "sw $28, 0($29) \n\t" \ "sw $31, 4($29) \n\t" \ "lw $4, 20(%1) \n\t" \ "subu $29, $29, 24\n\t" \ "sw $4, 16($29) \n\t" \ "lw $4, 4(%1) \n\t" \ "lw $5, 8(%1) \n\t" \ "lw $6, 12(%1) \n\t" \ "lw $7, 16(%1) \n\t" \ "lw $25, 0(%1) \n\t" /* target->t9 */ \ VALGRIND_CALL_NOREDIR_T9 \ "addu $29, $29, 24 \n\t" \ "lw $28, 0($29) \n\t" \ "lw $31, 4($29) \n\t" \ "addu $29, $29, 8 \n\t" \ "move %0, $2\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[7]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ __asm__ volatile( \ "subu $29, $29, 8 \n\t" \ "sw $28, 0($29) \n\t" \ "sw $31, 4($29) \n\t" \ "lw $4, 20(%1) \n\t" \ "subu $29, $29, 32\n\t" \ "sw $4, 16($29) \n\t" \ "lw $4, 24(%1) \n\t" \ "nop\n\t" \ "sw $4, 20($29) \n\t" \ "lw $4, 4(%1) \n\t" \ "lw $5, 8(%1) \n\t" \ "lw $6, 12(%1) \n\t" \ "lw $7, 16(%1) \n\t" \ "lw $25, 0(%1) \n\t" /* target->t9 */ \ VALGRIND_CALL_NOREDIR_T9 \ "addu $29, $29, 32 \n\t" \ "lw $28, 0($29) \n\t" \ "lw $31, 4($29) \n\t" \ "addu $29, $29, 8 \n\t" \ "move %0, $2\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[8]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ __asm__ volatile( \ "subu $29, $29, 8 \n\t" \ "sw $28, 0($29) \n\t" \ "sw $31, 4($29) \n\t" \ "lw $4, 20(%1) \n\t" \ "subu $29, $29, 32\n\t" \ "sw $4, 16($29) \n\t" \ "lw $4, 24(%1) \n\t" \ "sw $4, 20($29) \n\t" \ "lw $4, 28(%1) \n\t" \ "sw $4, 24($29) \n\t" \ "lw $4, 4(%1) \n\t" \ "lw $5, 8(%1) \n\t" \ "lw $6, 12(%1) \n\t" \ "lw $7, 16(%1) \n\t" \ "lw $25, 0(%1) \n\t" /* target->t9 */ \ VALGRIND_CALL_NOREDIR_T9 \ "addu $29, $29, 32 \n\t" \ "lw $28, 0($29) \n\t" \ "lw $31, 4($29) \n\t" \ "addu $29, $29, 8 \n\t" \ "move %0, $2\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[9]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ __asm__ volatile( \ "subu $29, $29, 8 \n\t" \ "sw $28, 0($29) \n\t" \ "sw $31, 4($29) \n\t" \ "lw $4, 20(%1) \n\t" \ "subu $29, $29, 40\n\t" \ "sw $4, 16($29) \n\t" \ "lw $4, 24(%1) \n\t" \ "sw $4, 20($29) \n\t" \ "lw $4, 28(%1) \n\t" \ "sw $4, 24($29) \n\t" \ "lw $4, 32(%1) \n\t" \ "sw $4, 28($29) \n\t" \ "lw $4, 4(%1) \n\t" \ "lw $5, 8(%1) \n\t" \ "lw $6, 12(%1) \n\t" \ "lw $7, 16(%1) \n\t" \ "lw $25, 0(%1) \n\t" /* target->t9 */ \ VALGRIND_CALL_NOREDIR_T9 \ "addu $29, $29, 40 \n\t" \ "lw $28, 0($29) \n\t" \ "lw $31, 4($29) \n\t" \ "addu $29, $29, 8 \n\t" \ "move %0, $2\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[10]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ __asm__ volatile( \ "subu $29, $29, 8 \n\t" \ "sw $28, 0($29) \n\t" \ "sw $31, 4($29) \n\t" \ "lw $4, 20(%1) \n\t" \ "subu $29, $29, 40\n\t" \ "sw $4, 16($29) \n\t" \ "lw $4, 24(%1) \n\t" \ "sw $4, 20($29) \n\t" \ "lw $4, 28(%1) \n\t" \ "sw $4, 24($29) \n\t" \ "lw $4, 32(%1) \n\t" \ "sw $4, 28($29) \n\t" \ "lw $4, 36(%1) \n\t" \ "sw $4, 32($29) \n\t" \ "lw $4, 4(%1) \n\t" \ "lw $5, 8(%1) \n\t" \ "lw $6, 12(%1) \n\t" \ "lw $7, 16(%1) \n\t" \ "lw $25, 0(%1) \n\t" /* target->t9 */ \ VALGRIND_CALL_NOREDIR_T9 \ "addu $29, $29, 40 \n\t" \ "lw $28, 0($29) \n\t" \ "lw $31, 4($29) \n\t" \ "addu $29, $29, 8 \n\t" \ "move %0, $2\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9,arg10) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[11]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ _argvec[10] = (unsigned long)(arg10); \ __asm__ volatile( \ "subu $29, $29, 8 \n\t" \ "sw $28, 0($29) \n\t" \ "sw $31, 4($29) \n\t" \ "lw $4, 20(%1) \n\t" \ "subu $29, $29, 48\n\t" \ "sw $4, 16($29) \n\t" \ "lw $4, 24(%1) \n\t" \ "sw $4, 20($29) \n\t" \ "lw $4, 28(%1) \n\t" \ "sw $4, 24($29) \n\t" \ "lw $4, 32(%1) \n\t" \ "sw $4, 28($29) \n\t" \ "lw $4, 36(%1) \n\t" \ "sw $4, 32($29) \n\t" \ "lw $4, 40(%1) \n\t" \ "sw $4, 36($29) \n\t" \ "lw $4, 4(%1) \n\t" \ "lw $5, 8(%1) \n\t" \ "lw $6, 12(%1) \n\t" \ "lw $7, 16(%1) \n\t" \ "lw $25, 0(%1) \n\t" /* target->t9 */ \ VALGRIND_CALL_NOREDIR_T9 \ "addu $29, $29, 48 \n\t" \ "lw $28, 0($29) \n\t" \ "lw $31, 4($29) \n\t" \ "addu $29, $29, 8 \n\t" \ "move %0, $2\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ arg6,arg7,arg8,arg9,arg10, \ arg11) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[12]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ _argvec[10] = (unsigned long)(arg10); \ _argvec[11] = (unsigned long)(arg11); \ __asm__ volatile( \ "subu $29, $29, 8 \n\t" \ "sw $28, 0($29) \n\t" \ "sw $31, 4($29) \n\t" \ "lw $4, 20(%1) \n\t" \ "subu $29, $29, 48\n\t" \ "sw $4, 16($29) \n\t" \ "lw $4, 24(%1) \n\t" \ "sw $4, 20($29) \n\t" \ "lw $4, 28(%1) \n\t" \ "sw $4, 24($29) \n\t" \ "lw $4, 32(%1) \n\t" \ "sw $4, 28($29) \n\t" \ "lw $4, 36(%1) \n\t" \ "sw $4, 32($29) \n\t" \ "lw $4, 40(%1) \n\t" \ "sw $4, 36($29) \n\t" \ "lw $4, 44(%1) \n\t" \ "sw $4, 40($29) \n\t" \ "lw $4, 4(%1) \n\t" \ "lw $5, 8(%1) \n\t" \ "lw $6, 12(%1) \n\t" \ "lw $7, 16(%1) \n\t" \ "lw $25, 0(%1) \n\t" /* target->t9 */ \ VALGRIND_CALL_NOREDIR_T9 \ "addu $29, $29, 48 \n\t" \ "lw $28, 0($29) \n\t" \ "lw $31, 4($29) \n\t" \ "addu $29, $29, 8 \n\t" \ "move %0, $2\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ arg6,arg7,arg8,arg9,arg10, \ arg11,arg12) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[13]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ _argvec[10] = (unsigned long)(arg10); \ _argvec[11] = (unsigned long)(arg11); \ _argvec[12] = (unsigned long)(arg12); \ __asm__ volatile( \ "subu $29, $29, 8 \n\t" \ "sw $28, 0($29) \n\t" \ "sw $31, 4($29) \n\t" \ "lw $4, 20(%1) \n\t" \ "subu $29, $29, 56\n\t" \ "sw $4, 16($29) \n\t" \ "lw $4, 24(%1) \n\t" \ "sw $4, 20($29) \n\t" \ "lw $4, 28(%1) \n\t" \ "sw $4, 24($29) \n\t" \ "lw $4, 32(%1) \n\t" \ "sw $4, 28($29) \n\t" \ "lw $4, 36(%1) \n\t" \ "sw $4, 32($29) \n\t" \ "lw $4, 40(%1) \n\t" \ "sw $4, 36($29) \n\t" \ "lw $4, 44(%1) \n\t" \ "sw $4, 40($29) \n\t" \ "lw $4, 48(%1) \n\t" \ "sw $4, 44($29) \n\t" \ "lw $4, 4(%1) \n\t" \ "lw $5, 8(%1) \n\t" \ "lw $6, 12(%1) \n\t" \ "lw $7, 16(%1) \n\t" \ "lw $25, 0(%1) \n\t" /* target->t9 */ \ VALGRIND_CALL_NOREDIR_T9 \ "addu $29, $29, 56 \n\t" \ "lw $28, 0($29) \n\t" \ "lw $31, 4($29) \n\t" \ "addu $29, $29, 8 \n\t" \ "move %0, $2\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #endif /* PLAT_mips32_linux */ /* ------------------------- mips64-linux ------------------------- */ #if defined(PLAT_mips64_linux) /* These regs are trashed by the hidden call. */ #define __CALLER_SAVED_REGS "$2", "$3", "$4", "$5", "$6", \ "$7", "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", "$24", \ "$25", "$31" /* These CALL_FN_ macros assume that on mips-linux, sizeof(unsigned long) == 4. */ #define CALL_FN_W_v(lval, orig) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[1]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ __asm__ volatile( \ "ld $25, 0(%1)\n\t" /* target->t9 */ \ VALGRIND_CALL_NOREDIR_T9 \ "move %0, $2\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "0" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_W(lval, orig, arg1) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[2]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ __asm__ volatile( \ "ld $4, 8(%1)\n\t" /* arg1*/ \ "ld $25, 0(%1)\n\t" /* target->t9 */ \ VALGRIND_CALL_NOREDIR_T9 \ "move %0, $2\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WW(lval, orig, arg1,arg2) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[3]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ __asm__ volatile( \ "ld $4, 8(%1)\n\t" \ "ld $5, 16(%1)\n\t" \ "ld $25, 0(%1)\n\t" /* target->t9 */ \ VALGRIND_CALL_NOREDIR_T9 \ "move %0, $2\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[4]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ __asm__ volatile( \ "ld $4, 8(%1)\n\t" \ "ld $5, 16(%1)\n\t" \ "ld $6, 24(%1)\n\t" \ "ld $25, 0(%1)\n\t" /* target->t9 */ \ VALGRIND_CALL_NOREDIR_T9 \ "move %0, $2\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[5]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ __asm__ volatile( \ "ld $4, 8(%1)\n\t" \ "ld $5, 16(%1)\n\t" \ "ld $6, 24(%1)\n\t" \ "ld $7, 32(%1)\n\t" \ "ld $25, 0(%1)\n\t" /* target->t9 */ \ VALGRIND_CALL_NOREDIR_T9 \ "move %0, $2\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[6]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ __asm__ volatile( \ "ld $4, 8(%1)\n\t" \ "ld $5, 16(%1)\n\t" \ "ld $6, 24(%1)\n\t" \ "ld $7, 32(%1)\n\t" \ "ld $8, 40(%1)\n\t" \ "ld $25, 0(%1)\n\t" /* target->t9 */ \ VALGRIND_CALL_NOREDIR_T9 \ "move %0, $2\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[7]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ __asm__ volatile( \ "ld $4, 8(%1)\n\t" \ "ld $5, 16(%1)\n\t" \ "ld $6, 24(%1)\n\t" \ "ld $7, 32(%1)\n\t" \ "ld $8, 40(%1)\n\t" \ "ld $9, 48(%1)\n\t" \ "ld $25, 0(%1)\n\t" /* target->t9 */ \ VALGRIND_CALL_NOREDIR_T9 \ "move %0, $2\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[8]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ __asm__ volatile( \ "ld $4, 8(%1)\n\t" \ "ld $5, 16(%1)\n\t" \ "ld $6, 24(%1)\n\t" \ "ld $7, 32(%1)\n\t" \ "ld $8, 40(%1)\n\t" \ "ld $9, 48(%1)\n\t" \ "ld $10, 56(%1)\n\t" \ "ld $25, 0(%1) \n\t" /* target->t9 */ \ VALGRIND_CALL_NOREDIR_T9 \ "move %0, $2\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[9]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ __asm__ volatile( \ "ld $4, 8(%1)\n\t" \ "ld $5, 16(%1)\n\t" \ "ld $6, 24(%1)\n\t" \ "ld $7, 32(%1)\n\t" \ "ld $8, 40(%1)\n\t" \ "ld $9, 48(%1)\n\t" \ "ld $10, 56(%1)\n\t" \ "ld $11, 64(%1)\n\t" \ "ld $25, 0(%1) \n\t" /* target->t9 */ \ VALGRIND_CALL_NOREDIR_T9 \ "move %0, $2\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[10]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ __asm__ volatile( \ "dsubu $29, $29, 8\n\t" \ "ld $4, 72(%1)\n\t" \ "sd $4, 0($29)\n\t" \ "ld $4, 8(%1)\n\t" \ "ld $5, 16(%1)\n\t" \ "ld $6, 24(%1)\n\t" \ "ld $7, 32(%1)\n\t" \ "ld $8, 40(%1)\n\t" \ "ld $9, 48(%1)\n\t" \ "ld $10, 56(%1)\n\t" \ "ld $11, 64(%1)\n\t" \ "ld $25, 0(%1)\n\t" /* target->t9 */ \ VALGRIND_CALL_NOREDIR_T9 \ "daddu $29, $29, 8\n\t" \ "move %0, $2\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \ arg7,arg8,arg9,arg10) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[11]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ _argvec[10] = (unsigned long)(arg10); \ __asm__ volatile( \ "dsubu $29, $29, 16\n\t" \ "ld $4, 72(%1)\n\t" \ "sd $4, 0($29)\n\t" \ "ld $4, 80(%1)\n\t" \ "sd $4, 8($29)\n\t" \ "ld $4, 8(%1)\n\t" \ "ld $5, 16(%1)\n\t" \ "ld $6, 24(%1)\n\t" \ "ld $7, 32(%1)\n\t" \ "ld $8, 40(%1)\n\t" \ "ld $9, 48(%1)\n\t" \ "ld $10, 56(%1)\n\t" \ "ld $11, 64(%1)\n\t" \ "ld $25, 0(%1)\n\t" /* target->t9 */ \ VALGRIND_CALL_NOREDIR_T9 \ "daddu $29, $29, 16\n\t" \ "move %0, $2\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ arg6,arg7,arg8,arg9,arg10, \ arg11) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[12]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ _argvec[10] = (unsigned long)(arg10); \ _argvec[11] = (unsigned long)(arg11); \ __asm__ volatile( \ "dsubu $29, $29, 24\n\t" \ "ld $4, 72(%1)\n\t" \ "sd $4, 0($29)\n\t" \ "ld $4, 80(%1)\n\t" \ "sd $4, 8($29)\n\t" \ "ld $4, 88(%1)\n\t" \ "sd $4, 16($29)\n\t" \ "ld $4, 8(%1)\n\t" \ "ld $5, 16(%1)\n\t" \ "ld $6, 24(%1)\n\t" \ "ld $7, 32(%1)\n\t" \ "ld $8, 40(%1)\n\t" \ "ld $9, 48(%1)\n\t" \ "ld $10, 56(%1)\n\t" \ "ld $11, 64(%1)\n\t" \ "ld $25, 0(%1)\n\t" /* target->t9 */ \ VALGRIND_CALL_NOREDIR_T9 \ "daddu $29, $29, 24\n\t" \ "move %0, $2\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5, \ arg6,arg7,arg8,arg9,arg10, \ arg11,arg12) \ do { \ volatile OrigFn _orig = (orig); \ volatile unsigned long _argvec[13]; \ volatile unsigned long _res; \ _argvec[0] = (unsigned long)_orig.nraddr; \ _argvec[1] = (unsigned long)(arg1); \ _argvec[2] = (unsigned long)(arg2); \ _argvec[3] = (unsigned long)(arg3); \ _argvec[4] = (unsigned long)(arg4); \ _argvec[5] = (unsigned long)(arg5); \ _argvec[6] = (unsigned long)(arg6); \ _argvec[7] = (unsigned long)(arg7); \ _argvec[8] = (unsigned long)(arg8); \ _argvec[9] = (unsigned long)(arg9); \ _argvec[10] = (unsigned long)(arg10); \ _argvec[11] = (unsigned long)(arg11); \ _argvec[12] = (unsigned long)(arg12); \ __asm__ volatile( \ "dsubu $29, $29, 32\n\t" \ "ld $4, 72(%1)\n\t" \ "sd $4, 0($29)\n\t" \ "ld $4, 80(%1)\n\t" \ "sd $4, 8($29)\n\t" \ "ld $4, 88(%1)\n\t" \ "sd $4, 16($29)\n\t" \ "ld $4, 96(%1)\n\t" \ "sd $4, 24($29)\n\t" \ "ld $4, 8(%1)\n\t" \ "ld $5, 16(%1)\n\t" \ "ld $6, 24(%1)\n\t" \ "ld $7, 32(%1)\n\t" \ "ld $8, 40(%1)\n\t" \ "ld $9, 48(%1)\n\t" \ "ld $10, 56(%1)\n\t" \ "ld $11, 64(%1)\n\t" \ "ld $25, 0(%1)\n\t" /* target->t9 */ \ VALGRIND_CALL_NOREDIR_T9 \ "daddu $29, $29, 32\n\t" \ "move %0, $2\n" \ : /*out*/ "=r" (_res) \ : /*in*/ "r" (&_argvec[0]) \ : /*trash*/ "memory", __CALLER_SAVED_REGS \ ); \ lval = (__typeof__(lval)) _res; \ } while (0) #endif /* PLAT_mips64_linux */ /* ------------------------------------------------------------------ */ /* ARCHITECTURE INDEPENDENT MACROS for CLIENT REQUESTS. */ /* */ /* ------------------------------------------------------------------ */ /* Some request codes. There are many more of these, but most are not exposed to end-user view. These are the public ones, all of the form 0x1000 + small_number. Core ones are in the range 0x00000000--0x0000ffff. The non-public ones start at 0x2000. */ /* These macros are used by tools -- they must be public, but don't embed them into other programs. */ #define VG_USERREQ_TOOL_BASE(a,b) \ ((unsigned int)(((a)&0xff) << 24 | ((b)&0xff) << 16)) #define VG_IS_TOOL_USERREQ(a, b, v) \ (VG_USERREQ_TOOL_BASE(a,b) == ((v) & 0xffff0000)) /* !! ABIWARNING !! ABIWARNING !! ABIWARNING !! ABIWARNING !! This enum comprises an ABI exported by Valgrind to programs which use client requests. DO NOT CHANGE THE NUMERIC VALUES OF THESE ENTRIES, NOR DELETE ANY -- add new ones at the end of the most relevant group. */ typedef enum { VG_USERREQ__RUNNING_ON_VALGRIND = 0x1001, VG_USERREQ__DISCARD_TRANSLATIONS = 0x1002, /* These allow any function to be called from the simulated CPU but run on the real CPU. Nb: the first arg passed to the function is always the ThreadId of the running thread! So CLIENT_CALL0 actually requires a 1 arg function, etc. */ VG_USERREQ__CLIENT_CALL0 = 0x1101, VG_USERREQ__CLIENT_CALL1 = 0x1102, VG_USERREQ__CLIENT_CALL2 = 0x1103, VG_USERREQ__CLIENT_CALL3 = 0x1104, /* Can be useful in regression testing suites -- eg. can send Valgrind's output to /dev/null and still count errors. */ VG_USERREQ__COUNT_ERRORS = 0x1201, /* Allows the client program and/or gdbserver to execute a monitor command. */ VG_USERREQ__GDB_MONITOR_COMMAND = 0x1202, /* These are useful and can be interpreted by any tool that tracks malloc() et al, by using vg_replace_malloc.c. */ VG_USERREQ__MALLOCLIKE_BLOCK = 0x1301, VG_USERREQ__RESIZEINPLACE_BLOCK = 0x130b, VG_USERREQ__FREELIKE_BLOCK = 0x1302, /* Memory pool support. */ VG_USERREQ__CREATE_MEMPOOL = 0x1303, VG_USERREQ__DESTROY_MEMPOOL = 0x1304, VG_USERREQ__MEMPOOL_ALLOC = 0x1305, VG_USERREQ__MEMPOOL_FREE = 0x1306, VG_USERREQ__MEMPOOL_TRIM = 0x1307, VG_USERREQ__MOVE_MEMPOOL = 0x1308, VG_USERREQ__MEMPOOL_CHANGE = 0x1309, VG_USERREQ__MEMPOOL_EXISTS = 0x130a, /* Allow printfs to valgrind log. */ /* The first two pass the va_list argument by value, which assumes it is the same size as or smaller than a UWord, which generally isn't the case. Hence are deprecated. The second two pass the vargs by reference and so are immune to this problem. */ /* both :: char* fmt, va_list vargs (DEPRECATED) */ VG_USERREQ__PRINTF = 0x1401, VG_USERREQ__PRINTF_BACKTRACE = 0x1402, /* both :: char* fmt, va_list* vargs */ VG_USERREQ__PRINTF_VALIST_BY_REF = 0x1403, VG_USERREQ__PRINTF_BACKTRACE_VALIST_BY_REF = 0x1404, /* Stack support. */ VG_USERREQ__STACK_REGISTER = 0x1501, VG_USERREQ__STACK_DEREGISTER = 0x1502, VG_USERREQ__STACK_CHANGE = 0x1503, /* Wine support */ VG_USERREQ__LOAD_PDB_DEBUGINFO = 0x1601, /* Querying of debug info. */ VG_USERREQ__MAP_IP_TO_SRCLOC = 0x1701, /* Disable/enable error reporting level. Takes a single Word arg which is the delta to this thread's error disablement indicator. Hence 1 disables or further disables errors, and -1 moves back towards enablement. Other values are not allowed. */ VG_USERREQ__CHANGE_ERR_DISABLEMENT = 0x1801, /* Some requests used for Valgrind internal, such as self-test or self-hosting. */ /* Initialise IR injection */ VG_USERREQ__VEX_INIT_FOR_IRI = 0x1901, /* Used by Inner Valgrind to inform Outer Valgrind where to find the list of inner guest threads */ VG_USERREQ__INNER_THREADS = 0x1902 } Vg_ClientRequest; #if !defined(__GNUC__) # define __extension__ /* */ #endif /* Returns the number of Valgrinds this code is running under. That is, 0 if running natively, 1 if running under Valgrind, 2 if running under Valgrind which is running under another Valgrind, etc. */ #define RUNNING_ON_VALGRIND \ (unsigned)VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* if not */, \ VG_USERREQ__RUNNING_ON_VALGRIND, \ 0, 0, 0, 0, 0) \ /* Discard translation of code in the range [_qzz_addr .. _qzz_addr + _qzz_len - 1]. Useful if you are debugging a JITter or some such, since it provides a way to make sure valgrind will retranslate the invalidated area. Returns no value. */ #define VALGRIND_DISCARD_TRANSLATIONS(_qzz_addr,_qzz_len) \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__DISCARD_TRANSLATIONS, \ _qzz_addr, _qzz_len, 0, 0, 0) #define VALGRIND_INNER_THREADS(_qzz_addr) \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__INNER_THREADS, \ _qzz_addr, 0, 0, 0, 0) /* These requests are for getting Valgrind itself to print something. Possibly with a backtrace. This is a really ugly hack. The return value is the number of characters printed, excluding the "**** " part at the start and the backtrace (if present). */ #if defined(__GNUC__) || defined(__INTEL_COMPILER) && !defined(_MSC_VER) /* Modern GCC will optimize the static routine out if unused, and unused attribute will shut down warnings about it. */ static int VALGRIND_PRINTF(const char *format, ...) __attribute__((format(__printf__, 1, 2), __unused__)); #endif static int #if defined(_MSC_VER) __inline #endif VALGRIND_PRINTF(const char *format, ...) { #if defined(NVALGRIND) (void)format; return 0; #else /* NVALGRIND */ #if defined(_MSC_VER) || defined(__MINGW64__) uintptr_t _qzz_res; #else unsigned long _qzz_res; #endif va_list vargs; va_start(vargs, format); #if defined(_MSC_VER) || defined(__MINGW64__) _qzz_res = VALGRIND_DO_CLIENT_REQUEST_EXPR(0, VG_USERREQ__PRINTF_VALIST_BY_REF, (uintptr_t)format, (uintptr_t)&vargs, 0, 0, 0); #else _qzz_res = VALGRIND_DO_CLIENT_REQUEST_EXPR(0, VG_USERREQ__PRINTF_VALIST_BY_REF, (unsigned long)format, (unsigned long)&vargs, 0, 0, 0); #endif va_end(vargs); return (int)_qzz_res; #endif /* NVALGRIND */ } #if defined(__GNUC__) || defined(__INTEL_COMPILER) && !defined(_MSC_VER) static int VALGRIND_PRINTF_BACKTRACE(const char *format, ...) __attribute__((format(__printf__, 1, 2), __unused__)); #endif static int #if defined(_MSC_VER) __inline #endif VALGRIND_PRINTF_BACKTRACE(const char *format, ...) { #if defined(NVALGRIND) (void)format; return 0; #else /* NVALGRIND */ #if defined(_MSC_VER) || defined(__MINGW64__) uintptr_t _qzz_res; #else unsigned long _qzz_res; #endif va_list vargs; va_start(vargs, format); #if defined(_MSC_VER) || defined(__MINGW64__) _qzz_res = VALGRIND_DO_CLIENT_REQUEST_EXPR(0, VG_USERREQ__PRINTF_BACKTRACE_VALIST_BY_REF, (uintptr_t)format, (uintptr_t)&vargs, 0, 0, 0); #else _qzz_res = VALGRIND_DO_CLIENT_REQUEST_EXPR(0, VG_USERREQ__PRINTF_BACKTRACE_VALIST_BY_REF, (unsigned long)format, (unsigned long)&vargs, 0, 0, 0); #endif va_end(vargs); return (int)_qzz_res; #endif /* NVALGRIND */ } /* These requests allow control to move from the simulated CPU to the real CPU, calling an arbitrary function. Note that the current ThreadId is inserted as the first argument. So this call: VALGRIND_NON_SIMD_CALL2(f, arg1, arg2) requires f to have this signature: Word f(Word tid, Word arg1, Word arg2) where "Word" is a word-sized type. Note that these client requests are not entirely reliable. For example, if you call a function with them that subsequently calls printf(), there's a high chance Valgrind will crash. Generally, your prospects of these working are made higher if the called function does not refer to any global variables, and does not refer to any libc or other functions (printf et al). Any kind of entanglement with libc or dynamic linking is likely to have a bad outcome, for tricky reasons which we've grappled with a lot in the past. */ #define VALGRIND_NON_SIMD_CALL0(_qyy_fn) \ VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \ VG_USERREQ__CLIENT_CALL0, \ _qyy_fn, \ 0, 0, 0, 0) #define VALGRIND_NON_SIMD_CALL1(_qyy_fn, _qyy_arg1) \ VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \ VG_USERREQ__CLIENT_CALL1, \ _qyy_fn, \ _qyy_arg1, 0, 0, 0) #define VALGRIND_NON_SIMD_CALL2(_qyy_fn, _qyy_arg1, _qyy_arg2) \ VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \ VG_USERREQ__CLIENT_CALL2, \ _qyy_fn, \ _qyy_arg1, _qyy_arg2, 0, 0) #define VALGRIND_NON_SIMD_CALL3(_qyy_fn, _qyy_arg1, _qyy_arg2, _qyy_arg3) \ VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \ VG_USERREQ__CLIENT_CALL3, \ _qyy_fn, \ _qyy_arg1, _qyy_arg2, \ _qyy_arg3, 0) /* Counts the number of errors that have been recorded by a tool. Nb: the tool must record the errors with VG_(maybe_record_error)() or VG_(unique_error)() for them to be counted. */ #define VALGRIND_COUNT_ERRORS \ (unsigned)VALGRIND_DO_CLIENT_REQUEST_EXPR( \ 0 /* default return */, \ VG_USERREQ__COUNT_ERRORS, \ 0, 0, 0, 0, 0) /* Several Valgrind tools (Memcheck, Massif, Helgrind, DRD) rely on knowing when heap blocks are allocated in order to give accurate results. This happens automatically for the standard allocator functions such as malloc(), calloc(), realloc(), memalign(), new, new[], free(), delete, delete[], etc. But if your program uses a custom allocator, this doesn't automatically happen, and Valgrind will not do as well. For example, if you allocate superblocks with mmap() and then allocates chunks of the superblocks, all Valgrind's observations will be at the mmap() level and it won't know that the chunks should be considered separate entities. In Memcheck's case, that means you probably won't get heap block overrun detection (because there won't be redzones marked as unaddressable) and you definitely won't get any leak detection. The following client requests allow a custom allocator to be annotated so that it can be handled accurately by Valgrind. VALGRIND_MALLOCLIKE_BLOCK marks a region of memory as having been allocated by a malloc()-like function. For Memcheck (an illustrative case), this does two things: - It records that the block has been allocated. This means any addresses within the block mentioned in error messages will be identified as belonging to the block. It also means that if the block isn't freed it will be detected by the leak checker. - It marks the block as being addressable and undefined (if 'is_zeroed' is not set), or addressable and defined (if 'is_zeroed' is set). This controls how accesses to the block by the program are handled. 'addr' is the start of the usable block (ie. after any redzone), 'sizeB' is its size. 'rzB' is the redzone size if the allocator can apply redzones -- these are blocks of padding at the start and end of each block. Adding redzones is recommended as it makes it much more likely Valgrind will spot block overruns. `is_zeroed' indicates if the memory is zeroed (or filled with another predictable value), as is the case for calloc(). VALGRIND_MALLOCLIKE_BLOCK should be put immediately after the point where a heap block -- that will be used by the client program -- is allocated. It's best to put it at the outermost level of the allocator if possible; for example, if you have a function my_alloc() which calls internal_alloc(), and the client request is put inside internal_alloc(), stack traces relating to the heap block will contain entries for both my_alloc() and internal_alloc(), which is probably not what you want. For Memcheck users: if you use VALGRIND_MALLOCLIKE_BLOCK to carve out custom blocks from within a heap block, B, that has been allocated with malloc/calloc/new/etc, then block B will be *ignored* during leak-checking -- the custom blocks will take precedence. VALGRIND_FREELIKE_BLOCK is the partner to VALGRIND_MALLOCLIKE_BLOCK. For Memcheck, it does two things: - It records that the block has been deallocated. This assumes that the block was annotated as having been allocated via VALGRIND_MALLOCLIKE_BLOCK. Otherwise, an error will be issued. - It marks the block as being unaddressable. VALGRIND_FREELIKE_BLOCK should be put immediately after the point where a heap block is deallocated. VALGRIND_RESIZEINPLACE_BLOCK informs a tool about reallocation. For Memcheck, it does four things: - It records that the size of a block has been changed. This assumes that the block was annotated as having been allocated via VALGRIND_MALLOCLIKE_BLOCK. Otherwise, an error will be issued. - If the block shrunk, it marks the freed memory as being unaddressable. - If the block grew, it marks the new area as undefined and defines a red zone past the end of the new block. - The V-bits of the overlap between the old and the new block are preserved. VALGRIND_RESIZEINPLACE_BLOCK should be put after allocation of the new block and before deallocation of the old block. In many cases, these three client requests will not be enough to get your allocator working well with Memcheck. More specifically, if your allocator writes to freed blocks in any way then a VALGRIND_MAKE_MEM_UNDEFINED call will be necessary to mark the memory as addressable just before the zeroing occurs, otherwise you'll get a lot of invalid write errors. For example, you'll need to do this if your allocator recycles freed blocks, but it zeroes them before handing them back out (via VALGRIND_MALLOCLIKE_BLOCK). Alternatively, if your allocator reuses freed blocks for allocator-internal data structures, VALGRIND_MAKE_MEM_UNDEFINED calls will also be necessary. Really, what's happening is a blurring of the lines between the client program and the allocator... after VALGRIND_FREELIKE_BLOCK is called, the memory should be considered unaddressable to the client program, but the allocator knows more than the rest of the client program and so may be able to safely access it. Extra client requests are necessary for Valgrind to understand the distinction between the allocator and the rest of the program. Ignored if addr == 0. */ #define VALGRIND_MALLOCLIKE_BLOCK(addr, sizeB, rzB, is_zeroed) \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__MALLOCLIKE_BLOCK, \ addr, sizeB, rzB, is_zeroed, 0) /* See the comment for VALGRIND_MALLOCLIKE_BLOCK for details. Ignored if addr == 0. */ #define VALGRIND_RESIZEINPLACE_BLOCK(addr, oldSizeB, newSizeB, rzB) \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__RESIZEINPLACE_BLOCK, \ addr, oldSizeB, newSizeB, rzB, 0) /* See the comment for VALGRIND_MALLOCLIKE_BLOCK for details. Ignored if addr == 0. */ #define VALGRIND_FREELIKE_BLOCK(addr, rzB) \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__FREELIKE_BLOCK, \ addr, rzB, 0, 0, 0) /* Create a memory pool. */ #define VALGRIND_CREATE_MEMPOOL(pool, rzB, is_zeroed) \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__CREATE_MEMPOOL, \ pool, rzB, is_zeroed, 0, 0) /* Create a memory pool with some flags specifying extended behaviour. When flags is zero, the behaviour is identical to VALGRIND_CREATE_MEMPOOL. The flag VALGRIND_MEMPOOL_METAPOOL specifies that the pieces of memory associated with the pool using VALGRIND_MEMPOOL_ALLOC will be used by the application as superblocks to dole out MALLOC_LIKE blocks using VALGRIND_MALLOCLIKE_BLOCK. In other words, a meta pool is a "2 levels" pool : first level is the blocks described by VALGRIND_MEMPOOL_ALLOC. The second level blocks are described using VALGRIND_MALLOCLIKE_BLOCK. Note that the association between the pool and the second level blocks is implicit : second level blocks will be located inside first level blocks. It is necessary to use the VALGRIND_MEMPOOL_METAPOOL flag for such 2 levels pools, as otherwise valgrind will detect overlapping memory blocks, and will abort execution (e.g. during leak search). Such a meta pool can also be marked as an 'auto free' pool using the flag VALGRIND_MEMPOOL_AUTO_FREE, which must be OR-ed together with the VALGRIND_MEMPOOL_METAPOOL. For an 'auto free' pool, VALGRIND_MEMPOOL_FREE will automatically free the second level blocks that are contained inside the first level block freed with VALGRIND_MEMPOOL_FREE. In other words, calling VALGRIND_MEMPOOL_FREE will cause implicit calls to VALGRIND_FREELIKE_BLOCK for all the second level blocks included in the first level block. Note: it is an error to use the VALGRIND_MEMPOOL_AUTO_FREE flag without the VALGRIND_MEMPOOL_METAPOOL flag. */ #define VALGRIND_MEMPOOL_AUTO_FREE 1 #define VALGRIND_MEMPOOL_METAPOOL 2 #define VALGRIND_CREATE_MEMPOOL_EXT(pool, rzB, is_zeroed, flags) \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__CREATE_MEMPOOL, \ pool, rzB, is_zeroed, flags, 0) /* Destroy a memory pool. */ #define VALGRIND_DESTROY_MEMPOOL(pool) \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__DESTROY_MEMPOOL, \ pool, 0, 0, 0, 0) /* Associate a piece of memory with a memory pool. */ #define VALGRIND_MEMPOOL_ALLOC(pool, addr, size) \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__MEMPOOL_ALLOC, \ pool, addr, size, 0, 0) /* Disassociate a piece of memory from a memory pool. */ #define VALGRIND_MEMPOOL_FREE(pool, addr) \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__MEMPOOL_FREE, \ pool, addr, 0, 0, 0) /* Disassociate any pieces outside a particular range. */ #define VALGRIND_MEMPOOL_TRIM(pool, addr, size) \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__MEMPOOL_TRIM, \ pool, addr, size, 0, 0) /* Resize and/or move a piece associated with a memory pool. */ #define VALGRIND_MOVE_MEMPOOL(poolA, poolB) \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__MOVE_MEMPOOL, \ poolA, poolB, 0, 0, 0) /* Resize and/or move a piece associated with a memory pool. */ #define VALGRIND_MEMPOOL_CHANGE(pool, addrA, addrB, size) \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__MEMPOOL_CHANGE, \ pool, addrA, addrB, size, 0) /* Return 1 if a mempool exists, else 0. */ #define VALGRIND_MEMPOOL_EXISTS(pool) \ (unsigned)VALGRIND_DO_CLIENT_REQUEST_EXPR(0, \ VG_USERREQ__MEMPOOL_EXISTS, \ pool, 0, 0, 0, 0) /* Mark a piece of memory as being a stack. Returns a stack id. start is the lowest addressable stack byte, end is the highest addressable stack byte. */ #define VALGRIND_STACK_REGISTER(start, end) \ (unsigned)VALGRIND_DO_CLIENT_REQUEST_EXPR(0, \ VG_USERREQ__STACK_REGISTER, \ start, end, 0, 0, 0) /* Unmark the piece of memory associated with a stack id as being a stack. */ #define VALGRIND_STACK_DEREGISTER(id) \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__STACK_DEREGISTER, \ id, 0, 0, 0, 0) /* Change the start and end address of the stack id. start is the new lowest addressable stack byte, end is the new highest addressable stack byte. */ #define VALGRIND_STACK_CHANGE(id, start, end) \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__STACK_CHANGE, \ id, start, end, 0, 0) /* Load PDB debug info for Wine PE image_map. */ #define VALGRIND_LOAD_PDB_DEBUGINFO(fd, ptr, total_size, delta) \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__LOAD_PDB_DEBUGINFO, \ fd, ptr, total_size, delta, 0) /* Map a code address to a source file name and line number. buf64 must point to a 64-byte buffer in the caller's address space. The result will be dumped in there and is guaranteed to be zero terminated. If no info is found, the first byte is set to zero. */ #define VALGRIND_MAP_IP_TO_SRCLOC(addr, buf64) \ (unsigned)VALGRIND_DO_CLIENT_REQUEST_EXPR(0, \ VG_USERREQ__MAP_IP_TO_SRCLOC, \ addr, buf64, 0, 0, 0) /* Disable error reporting for this thread. Behaves in a stack like way, so you can safely call this multiple times provided that VALGRIND_ENABLE_ERROR_REPORTING is called the same number of times to re-enable reporting. The first call of this macro disables reporting. Subsequent calls have no effect except to increase the number of VALGRIND_ENABLE_ERROR_REPORTING calls needed to re-enable reporting. Child threads do not inherit this setting from their parents -- they are always created with reporting enabled. */ #define VALGRIND_DISABLE_ERROR_REPORTING \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__CHANGE_ERR_DISABLEMENT, \ 1, 0, 0, 0, 0) /* Re-enable error reporting, as per comments on VALGRIND_DISABLE_ERROR_REPORTING. */ #define VALGRIND_ENABLE_ERROR_REPORTING \ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__CHANGE_ERR_DISABLEMENT, \ -1, 0, 0, 0, 0) /* Execute a monitor command from the client program. If a connection is opened with GDB, the output will be sent according to the output mode set for vgdb. If no connection is opened, output will go to the log output. Returns 1 if command not recognised, 0 otherwise. */ #define VALGRIND_MONITOR_COMMAND(command) \ VALGRIND_DO_CLIENT_REQUEST_EXPR(0, VG_USERREQ__GDB_MONITOR_COMMAND, \ command, 0, 0, 0, 0) #undef PLAT_x86_darwin #undef PLAT_amd64_darwin #undef PLAT_x86_win32 #undef PLAT_amd64_win64 #undef PLAT_x86_linux #undef PLAT_amd64_linux #undef PLAT_ppc32_linux #undef PLAT_ppc64be_linux #undef PLAT_ppc64le_linux #undef PLAT_arm_linux #undef PLAT_s390x_linux #undef PLAT_mips32_linux #undef PLAT_mips64_linux #undef PLAT_x86_solaris #undef PLAT_amd64_solaris #endif /* __VALGRIND_H */