diff --git a/Makefile b/Makefile index f891158..7fedfa1 100644 --- a/Makefile +++ b/Makefile @@ -6,6 +6,7 @@ MK_PATH ?= $(PWD) util := $(MK_PATH)/osd-util tgt := $(MK_PATH)/tgt target := $(MK_PATH)/osd-target +initiator := $(MK_PATH)/osd-initiator CHECKPATCH ?= ~/dev/git/pub/scsi-misc/scripts/checkpatch.pl checkpatch_2_kdev = checkpatch-2-kdev @@ -13,8 +14,15 @@ checkpatch_2_kdev = checkpatch-2-kdev # comment out any below to remove from compelation all: target all: stgt +all: target_test +# FreeBSD does not build initiator yet +ifneq ($(FreeBSD_Make),1) +all: initiator +all: initiator_tests +all: initiator_python +endif -clean: stgt_clean target_clean util_clean +clean: stgt_clean initiator_clean target_clean util_clean .PHONY: util util_clean util: @@ -27,8 +35,27 @@ util_clean: target: util $(MAKE) -C $(target) +target_test: target + $(MAKE) -C $(target)/tests + target_clean: $(MAKE) -C $(target) clean + $(MAKE) -C $(target)/tests clean + +.PHONY: initiator initiator_tests initiator_python initiator_clean +initiator: util + $(MAKE) -C $(initiator) + +initiator_tests: initiator + $(MAKE) -C $(initiator)/tests + +initiator_python: initiator + $(MAKE) -C $(initiator)/python + +initiator_clean: + $(MAKE) -C $(initiator)/python clean + $(MAKE) -C $(initiator)/tests clean + $(MAKE) -C $(initiator) clean OTGTD = otgtd ifeq ($(PANASAS_OSD),1) @@ -59,7 +86,7 @@ dist: # ctags generation for all projects ctags: - ctags -R $(util) $(target) $(tgt) /usr/include + ctags -R $(util) $(target) $(initiator) $(tgt) /usr/include osd_checkpatch: git show | $(CHECKPATCH) - | $(checkpatch_2_kdev) $(PWD) diff --git a/osd-initiator/COPYING b/osd-initiator/COPYING new file mode 100644 index 0000000..d511905 --- /dev/null +++ b/osd-initiator/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/osd-initiator/Makefile b/osd-initiator/Makefile new file mode 100644 index 0000000..50273f5 --- /dev/null +++ b/osd-initiator/Makefile @@ -0,0 +1,58 @@ +# +# OSD initiator library +# + +SRC := device.c sync.c drivelist.c command.c sense.c +INC := device.h sync.h drivelist.h command.h sense.h +OBJ := $(SRC:.c=.o) +LIB := libosdinit.a + +-include ../Makedefs + +CC := gcc +CPP_M := -MM +LD := $(CC) +COPTS := $(OPT) -fPIC +CWARN := -Wall -W -Wpointer-arith -Wwrite-strings -Wcast-align -Wcast-qual \ + -Wbad-function-cast -Wundef -Wmissing-prototypes -Wstrict-prototypes \ + -Wmissing-declarations -Wnested-externs +CFLAGS := $(COPTS) $(CWARN) -I.. +#CFLAGS += -Dsg_io_v4=sg_io_v4_v2622 +CFLAGS += -Dsg_io_v4=sg_io_v4_v2624 + +all:: $(LIB) $(OBJ) + +$(LIB): $(OBJ) + ar cr $@ $^ + +%.o: %.c + $(CC) $(CFLAGS) -c $< -o $@ + +ifeq (,$(filter clean distclean dist,$(MAKECMDGOALS))) +-include .depend +endif +all:: .depend +.depend: $(SRC) $(INC) Makefile + @$(CC) $(CPP_M) $(CFLAGS) $(SRC) > .depend + +clean: + rm -f $(LIB) $(OBJ) + rm -f .depend + +tags: FORCE + ctags $(SRC) $(INC) + +FORCE:; + +# distribution tarball +.PHONY: dist +MV := osd-initiator +MVD := $(MV)-$(shell date +%Y%m%d) +dist: + rm -rf $(MV) $(MVD) + mkdir $(MVD) + svn list -R | grep -v '/$$' | rsync --files-from=/dev/stdin ./ $(MVD)/ + ln -s $(MVD) $(MV) + tar cf - $(MV) $(MVD) | bzip2 -9c > $(MVD).tar.bz2 + rm -rf $(MV) $(MVD) + diff --git a/osd-initiator/README b/osd-initiator/README new file mode 100644 index 0000000..ba0dea4 --- /dev/null +++ b/osd-initiator/README @@ -0,0 +1,300 @@ +Documentation for OSC OSD Initiator library. + +Copyright (C) 2007 Dennis Dalessandro (dennis@osc.edu) +Copyright (C) 2007 Ananth Devulapalli (ananth@osc.edu) +Copyright (C) 2007 Pete Wyckoff (pw@osc.edu) + +Using +----- +This code relies on the kernel to ship the SCSI commands to an OSD. It +formmats CDBs then submits them to the BSG kernel interface. As such, +there are some number of kernel patches necessary to make this work, +because current v2.6.24-rc6 does not quite yet support bidirectional +SCSI transfers or variable length SCSI commands. See the linux-scsi +mailing list for progress on this. + +There are a few other missing features in current kernels. You can +grab the branch "osd" from + + git://git.osc.edu/linux.git + +or browse at + + http://git.osc.edu/?p=linux.git;a=summary + +to see these four patches: + +bsg-iovec-u64 + + The big one is the need for iovec support in bsg. Attribute lists + are much easier to manage if it is possible to submit a list of + address,length pairs to BSG to use as SCSI data buffers. Otherwise + it is necessary to do lots of memory copying to generate contiguous + buffers. + +bsg-accept-varlen-commands + + Allow non-root to submit OSD commands to devices through BSG. + Access to a bsg interface is still governed through file system + permissions on the character device. + +bsg-return-sam-device-status-code + + The return status code from bsg is half of what it should be. I.e. + instead of returning 2 for SAM_STAT_CHECK_CONDITION, it returns 1. + This fixes that. + +block-allow-no-queue-dma-alignment + + Even though iscsi TCP advertises itself as not needing queue + alignment, the current block interface rounds this zero up to + 512. That forces bounce buffers for any userspace tranfers + that are not aligned to 512 on both ends. + + +Build a kernel with these patches, type "make" to build the initiator +code (which relies on osd-util). Go into tests/ or python/ and type +make to build test codes, in C and Python respectively. Use iscsiadm to +attach an OSD device. Most of the codes will operate on the first +device of TYPE_OSD that they find. You'll have to read through them +all to see what they do. + +Udev +---- +User processes that wish to write BSG must use /dev/bsg/. It is +convenient not to have to do all this as root. On fedora-ish systems, +you can add a udev rule to set the perms on BSG character devices +automatically. Add a file /etc/udev/rules.d/20-bsg.rules (or some +other number, as long as it is in front of the default bsg rule in +50-udev-default.rules), with a single line: + + SUBSYSTEM="bsg", NAME="bsg/%k", MODE="0666" + +Contents +-------- +There are five major files here, each of which has a separate header +file that can be included from user programs: + + - drivelist + - device + - command + - sense + - sync + + +Drivelist +--------- +These two functions are used to get and free a list of available OSD +drives: + +int osd_get_drive_list(struct osd_drive_description **drives, int *num_drives); +void osd_free_drive_list(struct osd_drive_description *drives, int num_drives); + +It works by querying the SCSI interface. Data is allocated and returned +into the given pointer, so another call is used to free the data when +done with it. In the struct that is returned are two fields: +targetname, and chardev. Pass the chardev to open() to open a device. + + +Device +------ +Given an fd to an open device, this set of routines is used to submit +commands and wait for responses. + + +Command +------- +This is the element that encapsulates all information about an OSD +command, including attributes, in- and out-data, and sense results. +The various calls can build a command, which can then be given to one +of the device functions to submit. After completion, output fields +in the command structure can be inspected. + +To start, the user allocates a struct osd_command, perhaps on the +stack, and passes a pointer to it to one of the 29 osd_command_set_* +calls. These each clear the command and set up fields appropriately +for the SCSI command. + +Next, if the command requires data, like a write, set this in the +command. For a single buffer, + + command->outdata = buf; + command->outlen = len; + +Make sure to keep the buffer around until the command completes, i.e. +the data is not copied. For a vector of data, you must build an iovec +and assign that to outdata. + + struct bsg_iovec vec[2] = { + { .iov_base = (uintptr_t) buf1, .iov_len = len1 }, + { .iov_base = (uintptr_t) buf2, .iov_len = len2 }, + }; + command->outdata = vec; + command->outlen = len1 + len2; + command->iov_outlen = 2; + +If the command is expected to return data, you must set up a buffer +for the maximum size response. For a single buffer, + + command->indata = buf; + command->inlen_alloc = len; + +(Apply optional command attributes as described below.) + +Now, run the command using the device interface: + + osd_submit_command(fd, command); + +And wait for it to complete + + osd_wait_this_response(fd, command); + +After the command completes, check command->status to make sure it +executed properly. Also look at command->inlen to see the actual +size of data returned. For example, reading an object will only return +the number of bytes in the object regardless of how much you ask for. + + +Command attributes +------------------ +These are optional, and can be added to any command. There are four +different types of attributes that instruct the target to do something +along with the command: + + ATTR_GET - return a particular attribute + ATTR_SET - set a particular attribute to a given value + ATTR_GET_PAGE - return a pre-defined page structure + ATTR_GET_MULTI - only for multi-object CREATE command, does a + ATTR_GET for each of the newly created objects, essentially + +Subject to various constraints, a mixture of GET and SET attributes +can be added on the same command. + +After initializing a command and setting up the output and input data +pointers, it is possible to build a list of attributes. This must +be done after the data pointers as those will be modified. Only one +call to build attributes can be done per command. For example, + + const char data[] = "Some data."; + struct attribute_list attr_proto[] = { + { .type = ATTR_GET, .page = 0x82, .number = 1, .len = 8 }, + { .type = ATTR_GET, .page = 0x10993, .number = 12, .len = 20 }, + { .type = ATTR_SET, .page = 0x10076, .number = 1, + .val = data, .len = sizeof(data) }, + }; + osd_command_attr_build(command, attr_proto, 3); + +For PUT attributes (only), provide the input data. The attribute structure +and the output data contents are copied as part of the build command, so +they can be placed on the stack and forgotten immediately after the +command. Internally a single allocation holds the various structures +required by the OSD and must be freed by a call to osd_command_attr_free. + +Now submit the command to the device and wait for it to finish as above. + +Before the data or the GET attributes can be accessed, a call to +resolve the attributes must be made to locate the output values. This +is only required if there are any ATTR_GET* attributes to process: + + osd_command_attr_resolve(command); + +Now to look at a particular value, you can use the pointers that +were placed into the attribute structure. Do not expect to use the +original attribute_list you passed in to build, but look at the one +that was created for you in command->attr: + + uint64_t logical_len = ntohl(command->attr[0].val); + int str_len = command->attr[1].outlen; + char *s = Malloc(str_len + 1); + memcpy(s, command->attr[1].val); + s[str_len] = '\0'; + +Don't forget to do ntohll() on things that look like integers. You can +also check attr[1].outlen to see if the actual returned length is +what was expected. + +Now is also now safe to look at the data in command->indata. (If you +only had ATTR_SET, command->indata is valid without calling attr_resolve.) + +Finally, it is necessary to free the internal command state for +attributes: + + osd_command_attr_free(command); + +One twist to the attribute story. If you do an OSD CREATE command +and ask for more than one object, you must use ATTR_GET_MULTI to +request returned attributes. The attr->val you get back, then, is not +just a single value, but a pointer to a struct attribute_get_multi_results +that contains lists of val, outlen, and the owning oid. That is, there +is one returned value for each new object created. + + +Command attributes, get all +--------------------------- +Specifying a number of 0xffffffff in a attribute request tells the +device to return all user-defined attributes in the specified page. +If the page is also 0xffffffff, return all user-defined attributes in +all pages. + +This is messy, because we may not know how many attributes there are +in advance, hence the normal attribute code described above will not +work cleanly. While it is possible to mix non-all and all attribute +get requests, this interface does not allow that, hoping that it is +not a common thing to do. The tradeoff is avoiding much complexity +in the normal attribute code. + +This new interface, just to get all attributes of a specified page, +looks like: + + osd_command_attr_all_build(command, page); + +It can be applied to any command, and builds a request for the largest +possible attribute return size (64 kB), with number 0xffffffff. Note +that the user does not pass in an attribute structure. + +Resolving the results after the command finishes happens similarly with: + + osd_command_attr_all_resolve(command); + +Then you can see how many actually were returned by looking at +command->numattr, and loop through the attrs as follows: + + for (j=0; jnumattrs; j++) { + printf("attr %d page %lx number %lx len %x\n", j, + command->attr[j].page, command->attr[j].number, + command->attr[j].outlen); + /* look at val in command->attr[j].val */ + } + +And to clean up, a special call must be used: + + osd_command_attr_all_free(command); + +but be sure to complete accessing all the returned attribute values +first, because they will disappear at free time. + + +Sense +----- +One single call returns a descriptive string for an error. If, after +executing a command, the command->status field is 2 (CHECK_CONDITION), +call: + + s = osd_show_sense(command->sense, command->sense_len); + +then print s as an error, and free it when done. + + +Sync +---- +This collection of synchronous routines is very handy for testing and +for most programs. These functions are mostly analogous to the ones +in command.h, but they setup a command, run it, check the results, and +return data all in one step. For example + + create_partition(fd, 0); + +creates a new partitition. Internally it makes three calls to command.h +with lots of error checking. + +# vim: set tw=72 : diff --git a/osd-initiator/command.c b/osd-initiator/command.c new file mode 100644 index 0000000..dd4729c --- /dev/null +++ b/osd-initiator/command.c @@ -0,0 +1,1496 @@ +/* + * Commands and attributes. + * + * Copyright (C) 2007 OSD Team + * + * 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 + +/* turn this on automatically with configure some day */ +#if 0 +#include +#else +#define VALGRIND_MAKE_MEM_DEFINED(p, len) +#endif + +/* #define BSG_BACK_TO_SG_IOVEC */ + +#ifdef BSG_BACK_TO_SG_IOVEC +#include /* sg_iovec */ +#endif + +#include "osd-util/osd-util.h" +#include "command.h" + + + +static void varlen_cdb_init(struct osd_command *command, uint16_t action) +{ + memset(command, 0, sizeof(*command)); + command->cdb_len = OSD_CDB_SIZE; + command->cdb[0] = VARLEN_CDB; + /* we do not support ACA or LINK in control byte cdb[1], leave as 0 */ + command->cdb[7] = OSD_CDB_SIZE - 8; + command->cdb[8] = (action & 0xff00U) >> 8; + command->cdb[9] = (action & 0x00ffU); + /* default data distribution type is contiguous (0) */ + command->cdb[11] = 3 << 4; /* default to list, but empty list lens */ + /* Update timestamps based on action 5.2.8 */ + command->cdb[12] = 0x7f; /* timestamps not updated */ +} + +/* + * Non-varlen commands. + */ +int osd_command_set_test_unit_ready(struct osd_command *command) +{ + memset(command, 0, sizeof(*command)); + command->cdb_len = 6; + return 0; +} + +int osd_command_set_inquiry(struct osd_command *command, uint8_t page_code, + uint8_t outlen) +{ + memset(command, 0, sizeof(*command)); + command->cdb_len = 6; + command->cdb[0] = INQUIRY; + command->cdb[4] = outlen; + if (page_code) { + command->cdb[1] = 1; + command->cdb[2] = page_code; + } + return 0; +} + +/* + * These functions take a cdb of the appropriate size (200 bytes), + * and the arguments needed for the particular command. They marshall + * the arguments but do not submit the command. + */ +int osd_command_set_append(struct osd_command *command, uint64_t pid, + uint64_t oid, uint64_t len) +{ + varlen_cdb_init(command, OSD_APPEND); + set_htonll(&command->cdb[16], pid); + set_htonll(&command->cdb[24], oid); + set_htonll(&command->cdb[32], len); + return 0; +} + +int osd_command_set_clear(struct osd_command *command, uint64_t pid, + uint64_t oid, uint64_t len, uint64_t offset) +{ + varlen_cdb_init(command, OSD_CLEAR); + set_htonll(&command->cdb[16], pid); + set_htonll(&command->cdb[24], oid); + set_htonll(&command->cdb[32], len); + set_htonll(&command->cdb[40], offset); + return 0; +} + +int osd_command_set_create(struct osd_command *command, uint64_t pid, + uint64_t requested_oid, uint16_t num_user_objects) +{ + varlen_cdb_init(command, OSD_CREATE); + set_htonll(&command->cdb[16], pid); + set_htonll(&command->cdb[24], requested_oid); + set_htons(&command->cdb[32], num_user_objects); + return 0; +} + + +int osd_command_set_create_and_write(struct osd_command *command, uint64_t pid, + uint64_t requested_oid, uint64_t len, + uint64_t offset) +{ + varlen_cdb_init(command, OSD_CREATE_AND_WRITE); + set_htonll(&command->cdb[16], pid); + set_htonll(&command->cdb[24], requested_oid); + set_htonll(&command->cdb[32], len); + set_htonll(&command->cdb[40], offset); + return 0; +} + + +int osd_command_set_create_collection(struct osd_command *command, + uint64_t pid, uint64_t requested_cid) +{ + varlen_cdb_init(command, OSD_CREATE_COLLECTION); + set_htonll(&command->cdb[16], pid); + set_htonll(&command->cdb[24], requested_cid); + return 0; +} + +int osd_command_set_create_partition(struct osd_command *command, + uint64_t requested_pid) +{ + varlen_cdb_init(command, OSD_CREATE_PARTITION); + set_htonll(&command->cdb[16], requested_pid); + return 0; +} + + +int osd_command_set_flush(struct osd_command *command, uint64_t pid, uint64_t len, + uint64_t offset, uint64_t oid, int flush_scope) +{ + varlen_cdb_init(command, OSD_FLUSH); + set_htonll(&command->cdb[16], pid); + set_htonll(&command->cdb[24], oid); + set_htonll(&command->cdb[32], len); + set_htonll(&command->cdb[40], offset); + command->cdb[10] = (command->cdb[10] & ~0x3) | flush_scope; + return 0; +} + + +int osd_command_set_flush_collection(struct osd_command *command, uint64_t pid, + uint64_t cid, int flush_scope) +{ + varlen_cdb_init(command, OSD_FLUSH_COLLECTION); + set_htonll(&command->cdb[16], pid); + set_htonll(&command->cdb[24], cid); + command->cdb[10] = (command->cdb[10] & ~0x3) | flush_scope; + return 0; +} + + +int osd_command_set_flush_osd(struct osd_command *command, int flush_scope) +{ + varlen_cdb_init(command, OSD_FLUSH_OSD); + command->cdb[10] = (command->cdb[10] & ~0x3) | flush_scope; + return 0; +} + + +int osd_command_set_flush_partition(struct osd_command *command, uint64_t pid, + int flush_scope) +{ + varlen_cdb_init(command, OSD_FLUSH_PARTITION); + set_htonll(&command->cdb[16], pid); + command->cdb[10] = (command->cdb[10] & ~0x3) | flush_scope; + return 0; +} + +/* + * Destroy the db and start over again. + */ +int osd_command_set_format_osd(struct osd_command *command, uint64_t capacity) +{ + varlen_cdb_init(command, OSD_FORMAT_OSD); + set_htonll(&command->cdb[32], capacity); + return 0; +} + + +int osd_command_set_get_attributes(struct osd_command *command, uint64_t pid, + uint64_t oid) +{ + varlen_cdb_init(command, OSD_GET_ATTRIBUTES); + set_htonll(&command->cdb[16], pid); + set_htonll(&command->cdb[24], oid); + return 0; +} + + +int osd_command_set_get_member_attributes(struct osd_command *command, + uint64_t pid, uint64_t cid) +{ + varlen_cdb_init(command, OSD_GET_MEMBER_ATTRIBUTES); + set_htonll(&command->cdb[16], pid); + set_htonll(&command->cdb[24], cid); + return 0; +} + + +int osd_command_set_list(struct osd_command *command, uint64_t pid, + uint32_t list_id, uint64_t alloc_len, + uint64_t initial_oid, int list_attr) +{ + varlen_cdb_init(command, OSD_LIST); + if (list_attr) + command->cdb[11] |= 0x40; + set_htonll(&command->cdb[16], pid); + set_htonll(&command->cdb[32], alloc_len); + set_htonll(&command->cdb[40], initial_oid); + set_htonl(&command->cdb[48], list_id); + return 0; +} + +int osd_command_set_list_collection(struct osd_command *command, uint64_t pid, + uint64_t cid, uint32_t list_id, + uint64_t alloc_len, uint64_t initial_oid, + int list_attr) +{ + varlen_cdb_init(command, OSD_LIST_COLLECTION); + if (list_attr) + command->cdb[11] |= 0x40; + set_htonll(&command->cdb[16], pid); + set_htonll(&command->cdb[24], cid); + set_htonll(&command->cdb[32], alloc_len); + set_htonll(&command->cdb[40], initial_oid); + set_htonl(&command->cdb[48], list_id); + return 0; +} + +int osd_command_set_perform_scsi_command( + struct osd_command *command __attribute__((unused))) +{ + osd_error("%s: unimplemented", __func__); + return 1; +} + +int osd_command_set_perform_task_mgmt_func( + struct osd_command *command __attribute__((unused))) +{ + osd_error("%s: unimplemented", __func__); + return 1; +} + +int osd_command_set_punch(struct osd_command *command, uint64_t pid, + uint64_t oid, uint64_t len, uint64_t offset) +{ + varlen_cdb_init(command, OSD_PUNCH); + set_htonll(&command->cdb[16], pid); + set_htonll(&command->cdb[24], oid); + set_htonll(&command->cdb[32], len); + set_htonll(&command->cdb[40], offset); + return 0; +} + +int osd_command_set_query(struct osd_command *command, uint64_t pid, + uint64_t cid, uint32_t query_len, uint64_t alloc_len) +{ + varlen_cdb_init(command, OSD_QUERY); + set_htonll(&command->cdb[16], pid); + set_htonll(&command->cdb[24], cid); + set_htonl(&command->cdb[48], query_len); + set_htonll(&command->cdb[32], alloc_len); + return 0; +} + +int osd_command_set_read(struct osd_command *command, uint64_t pid, + uint64_t oid, uint64_t len, uint64_t offset) +{ + varlen_cdb_init(command, OSD_READ); + set_htonll(&command->cdb[16], pid); + set_htonll(&command->cdb[24], oid); + set_htonll(&command->cdb[32], len); + set_htonll(&command->cdb[40], offset); + return 0; +} + +int osd_command_set_read_map(struct osd_command*command, uint64_t pid, uint64_t oid, + uint64_t alloc_len, uint64_t offset, uint8_t map_type) +{ + varlen_cdb_init(command, OSD_READ_MAP); + set_htonll(&command->cdb[16], pid); + set_htonll(&command->cdb[24], oid); + set_htonll(&command->cdb[32], alloc_len); + set_htonll(&command->cdb[40], offset); + set_htons(&command->cdb[48], map_type); + return 0; +} + + +int osd_command_set_remove(struct osd_command *command, uint64_t pid, + uint64_t oid) +{ + varlen_cdb_init(command, OSD_REMOVE); + set_htonll(&command->cdb[16], pid); + set_htonll(&command->cdb[24], oid); + return 0; +} + + +int osd_command_set_remove_collection(struct osd_command *command, + uint64_t pid, uint64_t cid, int force) +{ + varlen_cdb_init(command, OSD_REMOVE_COLLECTION); + if (force) + command->cdb[11] |= 1; + set_htonll(&command->cdb[16], pid); + set_htonll(&command->cdb[24], cid); + return 0; +} + + +int osd_command_set_remove_member_objects(struct osd_command *command, + uint64_t pid, uint64_t cid) +{ + varlen_cdb_init(command, OSD_REMOVE_MEMBER_OBJECTS); + set_htonll(&command->cdb[16], pid); + set_htonll(&command->cdb[24], cid); + return 0; +} + + +int osd_command_set_remove_partition(struct osd_command *command, uint64_t pid) +{ + varlen_cdb_init(command, OSD_REMOVE_PARTITION); + set_htonll(&command->cdb[16], pid); + return 0; +} + + +int osd_command_set_set_attributes(struct osd_command *command, uint64_t pid, + uint64_t oid) +{ + varlen_cdb_init(command, OSD_SET_ATTRIBUTES); + set_htonll(&command->cdb[16], pid); + set_htonll(&command->cdb[24], oid); + return 0; +} + + +int osd_command_set_set_key(struct osd_command *command, int key_to_set, + uint64_t pid, uint64_t key, const uint8_t seed[20]) +{ + varlen_cdb_init(command, OSD_SET_KEY); + command->cdb[11] = (command->cdb[11] & ~0x3) | key_to_set; + set_htonll(&command->cdb[16], pid); + set_htonll(&command->cdb[24], key); + memcpy(&command->cdb[32], seed, 20); + return 0; +} + + +int osd_command_set_set_master_key(struct osd_command *command, int dh_step, + uint64_t key, uint32_t param_len, + uint32_t alloc_len) +{ + varlen_cdb_init(command, OSD_SET_KEY); + command->cdb[11] = (command->cdb[11] & ~0x3) | dh_step; + set_htonll(&command->cdb[24], key); + set_htonl(&command->cdb[32], param_len); + set_htonl(&command->cdb[36], alloc_len); + return 0; +} + + +int osd_command_set_set_member_attributes(struct osd_command *command, + uint64_t pid, uint64_t cid) +{ + varlen_cdb_init(command, OSD_SET_MEMBER_ATTRIBUTES); + set_htonll(&command->cdb[16], pid); + set_htonll(&command->cdb[24], cid); + return 0; +} + + +int osd_command_set_write(struct osd_command *command, uint64_t pid, + uint64_t oid, uint64_t len, uint64_t offset) +{ + varlen_cdb_init(command, OSD_WRITE); + set_htonll(&command->cdb[16], pid); + set_htonll(&command->cdb[24], oid); + set_htonll(&command->cdb[32], len); + set_htonll(&command->cdb[40], offset); + return 0; +} + +int osd_command_set_cas(struct osd_command *command, uint64_t pid, + uint64_t oid, uint64_t len, uint64_t offset) +{ + varlen_cdb_init(command, OSD_CAS); + set_htonll(&command->cdb[16], pid); + set_htonll(&command->cdb[24], oid); + set_htonll(&command->cdb[32], len); + set_htonll(&command->cdb[40], offset); + return 0; +} + +int osd_command_set_fa(struct osd_command *command, uint64_t pid, + uint64_t oid, uint64_t len, uint64_t offset) +{ + varlen_cdb_init(command, OSD_FA); + set_htonll(&command->cdb[16], pid); + set_htonll(&command->cdb[24], oid); + set_htonll(&command->cdb[32], len); + set_htonll(&command->cdb[40], offset); + return 0; +} + +int osd_command_set_gen_cas(struct osd_command *command, uint64_t pid, + uint64_t oid) +{ + varlen_cdb_init(command, OSD_GEN_CAS); + set_htonll(&command->cdb[16], pid); + set_htonll(&command->cdb[24], oid); + return 0; +} + +int osd_command_set_cond_setattr(struct osd_command *command, uint64_t pid, + uint64_t oid) +{ + varlen_cdb_init(command, OSD_COND_SETATTR); + set_htonll(&command->cdb[16], pid); + set_htonll(&command->cdb[24], oid); + return 0; +} + +void osd_command_set_ddt(struct osd_command *command, uint8_t ddt_type) +{ + /* + * Using last 2 bits of option byte, just like flush_scope, see 5.2.5 + */ + command->cdb[10] = (command->cdb[10] & ~0x3) | ddt_type; +} + + +uint8_t osd_command_get_ddt(struct osd_command *command) +{ + return command->cdb[10] & 0x3; +} + +/* + * Header for internal use across attr_build to attr_resolve. Keeps + * the original iov structures. + */ +struct attr_malloc_header { + const void *orig_outdata; + size_t orig_outlen; + int orig_iov_outlen; + void *orig_indata; + size_t orig_inlen_alloc; + int orig_iov_inlen; + uint8_t *retrieved_buf; + uint64_t start_retrieved; + /* for convenience of resolve, points into the big buffer */ + struct attribute_get_multi_results *mr; +}; + +/* + * After setting the command (i.e. building the CDB), and connecting + * the indata and outdata, this function tacks on a list of attributes. + * To do this it will possibly create a new buffer for output data + * containing the request, and it will possibly create a new buffer for + * input data with the GET responses. And it will build an iovec to + * hold the command data as well as the attribute data. + * + * After the command completes, call the resolve function to fill in + * the results and free the temp buffers. + */ +int osd_command_attr_build(struct osd_command *command, + const struct attribute_list *attr, int numattr) +{ + int use_getpage, use_set_one_attr; + int numget, numgetpage, numgetmulti, numset, numresult; + uint32_t getsize, getpagesize, getmultisize, setsize, resultsize; + uint32_t getmulti_num_objects = 0; + struct attr_malloc_header *header; + uint8_t *p, *extra_out_buf, *extra_in_buf; +#ifdef BSG_BACK_TO_SG_IOVEC + struct sg_iovec *iov; +#else + struct bsg_iovec *iov; +#endif + int i; + int neediov; + int setattr_index; + uint64_t extra_out, extra_in; + uint32_t header_space, attr_space, iov_space, getmulti_result_space; + + /* + * These are values, in units of bytes (not offsets), in the + * out data. + */ + uint64_t end_outdata; + uint64_t size_pad_outdata; + uint64_t start_getlist; + uint64_t size_getlist; + uint64_t size_pad_setlist; + uint64_t start_setlist; + uint64_t size_setlist; + + /* + * Ditto, in data. + */ + uint64_t end_indata; + uint64_t size_pad_indata; + uint64_t start_retrieved; + uint64_t size_retrieved; + + /* + * These are used so that we know the setlist and retrieved + * areas start on 8-byte boundaries. Since padding after + * outdata or indata can be arbitrary, alloc a bit extra to + * keep the pointers nice. + */ + uint64_t size_pad_outdata_alloc; + uint64_t size_pad_indata_alloc; + uint64_t extra_out_alloc, extra_in_alloc; + + if (numattr == 0) + return 0; + + /* + * Figure out what operations to do. That will lead to constraints + * on the choice of getpage/setone versus getlist/setlist format. + */ + numget = numgetpage = numgetmulti = numset = numresult = 0; + getsize = getpagesize = getmultisize = setsize = resultsize = 0; + setattr_index = -1; + for (i=0; i 1) { + osd_error("%s: only one ATTR_GET_PAGE at a time", + __func__); + return -EINVAL; + } + if (numget) { + osd_error("%s: no ATTR_GET allowed with ATTR_GET_PAGE", + __func__); + return -EINVAL; + } + if (numgetmulti) { + osd_error( + "%s: no ATTR_GETMULTI allowed with ATTR_GET_PAGE", + __func__); + return -EINVAL; + } + if (numset > 1) { + osd_error("%s: ATTR_GET_PAGE limits ATTR_SET to one", + __func__); + return -EINVAL; + } + if (numresult) { + /* Only CAS uses ATTR_RESULT, which needs list fmt */ + osd_error("%s: ATTR_RESULT not allowed with " + "ATTR_GET_PAGE", __func__); + return -EINVAL; + } + use_getpage = 1; + + }else { + if (numgetmulti) { + uint16_t action = (command->cdb[8] << 8) + | command->cdb[9]; + if (numget) { + osd_error( + "%s: no ATTR_GET allowed with ATTR_GET_MULTI", + __func__); + return -EINVAL; + } + if (action != OSD_CREATE) { + osd_error( + "%s: ATTR_GET_MULTI must only be used with CREATE", + __func__); + return -EINVAL; + } + getmulti_num_objects = get_ntohs(&command->cdb[36]); + } + use_getpage = 0; + if (numget == 0 && numgetmulti == 0 && numset == 1) { + /* + * For a single set and service action != + * OSD_SET_MEMBER_ATTRIBUTES *, prefer simpler page + * format, * except if page-to-set is zero, which is + * a noop in * this format. + */ + uint8_t *cdb = command->cdb; + uint16_t action = ((cdb[8] << 8) | cdb[9]); + if (attr[setattr_index].page != 0 && + (action != OSD_SET_MEMBER_ATTRIBUTES)) { + use_getpage = 1; + } + } + if (attr[0].len <= 18 && numset == 1 && numattr == 1) + use_set_one_attr = 1; + } + + /* + * Outdata: + * padding between real outdata and setlist + * setlist (including values) + * padding between setlist and getlist + * getlist + */ + end_outdata = command->outlen; + + size_pad_outdata = 0; + size_pad_outdata_alloc = 0; /* so setlist starts on 8-byte boundary */ + start_setlist = 0; + size_setlist = 0; + if (numset) { + start_setlist = next_offset(end_outdata); + size_pad_outdata = start_setlist - end_outdata; + size_pad_outdata_alloc = roundup8(size_pad_outdata); + if (use_getpage) + /* no list format around the single value */ + size_setlist = attr[setattr_index].len; + else + /* add list header; setsize already rounded */ + size_setlist = 8 + setsize; + } + + size_pad_setlist = 0; + start_getlist = 0; + size_getlist = 0; + if (numget || numgetmulti) { + uint64_t prev = start_setlist + size_setlist; + if (prev == 0) + prev = end_outdata; + start_getlist = next_offset(prev); + size_pad_setlist = start_getlist - prev; + if (size_setlist == 0) + size_pad_outdata_alloc = roundup8(size_pad_setlist); + size_getlist = 8 + 8 * (numget + numgetmulti); + } + + extra_out = size_pad_outdata + size_setlist + + size_pad_setlist + size_getlist; + extra_out_alloc = size_pad_outdata_alloc + size_setlist + + size_pad_setlist + size_getlist; + + /* + * Indata: + * padding between real indata and retrieved results area + * retrieved results area + */ + end_indata = command->inlen_alloc; + start_retrieved = 0; + size_pad_indata = 0; + size_pad_indata_alloc = 0; + size_retrieved = 0; + if (numget || numgetpage || numgetmulti || numresult) { + start_retrieved = next_offset(end_indata); + size_pad_indata = start_retrieved - end_indata; + size_pad_indata_alloc = roundup8(size_pad_indata); + if (numget) + size_retrieved = 8 + getsize; /* already rounded */ + if (numgetpage) + size_retrieved = getpagesize; /* no rounding */ + if (numgetmulti) + size_retrieved = 8 + getmultisize * + getmulti_num_objects; + if (numresult) { + if (!size_retrieved) + size_retrieved += 8; + size_retrieved += resultsize; + } + } + + extra_in = size_pad_indata + size_retrieved; + extra_in_alloc = size_pad_indata_alloc + size_retrieved; + + /* + * Each ATTR_GET_MULTI will return an array of values, one for + * each object. Allocate space for that now, for use by + * attr_resolve(); + */ + getmulti_result_space = 0; + if (numgetmulti) { + struct attribute_get_multi_results *mr; + int one_space = sizeof(*mr) + + getmulti_num_objects + * (sizeof(*mr->oid) + sizeof(*mr->val) + + sizeof(*mr->outlen)); + getmulti_result_space = roundup8(numgetmulti * one_space); + } + + /* + * If we are building out or indata items, and user already + * had some data there, build replacement iovecs to hold the + * new list. + */ + neediov = 0; + if (extra_out && command->outdata) { + if (command->iov_outlen == 0) + neediov += 2; + else + neediov += command->iov_outlen + 1; + } + if (extra_in && command->indata) { + if (command->iov_inlen == 0) + neediov += 2; + else + neediov += command->iov_inlen + 1; + } + + /* + * Allocate space for the following things, in order: + * header struct of original iov/data, etc. + * attr array + * get_multi results + * out iovec + * in iovec + * outgoing getlist area + * outgoing setlist area + * incoming retrieved area + */ + header_space = roundup8(sizeof(*header)); + attr_space = roundup8(numattr * sizeof(*attr)); + iov_space = roundup8(neediov * sizeof(*iov)); + p = Malloc(header_space + + attr_space + + getmulti_result_space + + iov_space + + extra_out_alloc + + extra_in_alloc); + if (!p) + return 1; + + /* to free it later */ + command->attr_malloc = p; + + /* header holds original indata, iniovec, etc. */ + header = (void *) p; + p += header_space; + + /* copy user's passed-in attribute structure for use by resolve */ + command->attr = (void *) p; + memcpy(command->attr, attr, numattr * sizeof(*attr)); + command->numattr = numattr; + p += attr_space; + + /* for ATTR_GET_MULTI results */ + header->mr = (void *) p; + p += getmulti_result_space; + + /* space for replacement out and in iovecs */ + iov = (void *) p; + p += iov_space; + + /* extra out; including pad, setlist, pad, getlist */ + extra_out_buf = p; + p += extra_out_alloc; + + /* extra in; including pad, retrieved area */ + extra_in_buf = p; + + /* + * Initialize get multi result space. + */ + if (getmulti_result_space) { + struct attribute_get_multi_results *mr = header->mr; + uint8_t *q = (void *) &mr[numgetmulti]; + for (i=0; iretrieved_buf = extra_in_buf + size_pad_indata_alloc; + header->start_retrieved = start_retrieved; + + /* + * Update the CDB bits. + */ + if (use_set_one_attr) { + /* set one attribute */ + command->cdb[11] = (command->cdb[11] & ~(3 << 4)) | (1 << 4); + set_htonl(&command->cdb[52], attr[0].page); + set_htonl(&command->cdb[56], attr[0].number); + set_htons(&command->cdb[60], attr[0].len); + memcpy(&command->cdb[62], attr[0].val, attr[0].len); + } + + else if (use_getpage) { + /* page format */ + command->cdb[11] = (command->cdb[11] & ~(3 << 4)) | (2 << 4); + for (i=0; icdb[52], attr[i].page); + if (attr[i].type == ATTR_SET) { + set_htonl(&command->cdb[64], attr[i].page); + set_htonl(&command->cdb[68], attr[i].number); + } + } + set_htonl(&command->cdb[56], size_retrieved); + set_htonoffset(&command->cdb[60], start_retrieved); + set_htonl(&command->cdb[72], size_setlist); + set_htonoffset(&command->cdb[76], start_setlist); + } else { + /* list format, cdb[11] is this as default */ + set_htonl(&command->cdb[52], size_getlist); + set_htonoffset(&command->cdb[56], start_getlist); + set_htonl(&command->cdb[60], size_retrieved); + set_htonoffset(&command->cdb[64], start_retrieved); + set_htonl(&command->cdb[68], size_setlist); + set_htonoffset(&command->cdb[72], start_setlist); + } + + /* + * Adjust the iovecs. These use the real sizes, not the _alloc + * sizes. + */ + header->orig_outdata = command->outdata; + header->orig_outlen = command->outlen; + header->orig_iov_outlen = command->iov_outlen; + if (extra_out) { + /* ignore the 0..7 extra bytes so that data lands nicely */ + p = extra_out_buf + (extra_out_alloc - extra_out); + if (command->outdata) { + command->outdata = iov; + if (command->iov_outlen == 0) { + iov->iov_base = + (uintptr_t) header->orig_outdata; + iov->iov_len = command->outlen; + ++iov; + command->iov_outlen = 2; + } else { + memcpy(iov, header->orig_outdata, + command->iov_outlen * sizeof(*iov)); + iov += command->iov_outlen; + ++command->iov_outlen; + } + iov->iov_base = (uintptr_t) p; + iov->iov_len = extra_out; + ++iov; + } else { + command->outdata = p; + } + command->outlen += extra_out; + } + + header->orig_indata = command->indata; + header->orig_inlen_alloc = command->inlen_alloc; + header->orig_iov_inlen = command->iov_inlen; + if (extra_in) { + p = extra_in_buf + (extra_in_alloc - extra_in); + if (command->indata) { + command->indata = iov; + if (command->iov_inlen == 0) { + iov->iov_base = (uintptr_t) header->orig_indata; + iov->iov_len = command->inlen_alloc; + ++iov; + command->iov_inlen = 2; + } else { + memcpy(iov, header->orig_indata, + command->iov_inlen * sizeof(*iov)); + iov += command->iov_inlen; + ++command->iov_inlen; + } + iov->iov_base = (uintptr_t) p; + iov->iov_len = extra_in; + ++iov; + } else { + command->indata = p; + } + command->inlen_alloc += extra_in; + } + + return 0; +} + +/* + * Must be called once after a command involving attributes. Figures out + * where GET output data landed and sets .val pointers and .outlen + * appropriately. Returns 0 if all okay. + */ +int osd_command_attr_resolve(struct osd_command *command) +{ + struct attr_malloc_header *header = command->attr_malloc; + uint8_t *p; + uint64_t len; + uint32_t list_len; + int numget, numgetmulti, i; + int ret = -EINVAL; + struct attribute_list *attr = command->attr; + int numattr = command->numattr; + + if (command->inlen < header->start_retrieved) + goto unwind; + + p = header->retrieved_buf; + len = command->inlen - header->start_retrieved; + VALGRIND_MAKE_MEM_DEFINED(p, len); + + /* + * If getpage, just copy it in and done. Assumes checking was + * done properly at the start. Also error out the outlens for + * the non-getpage case to come later. + */ + numget = 0; + numgetmulti = 0; + for (i=0; i attr[i].len) + attr[i].outlen = attr[i].len; + attr[i].val = p; + ret = 0; + goto unwind; + } + if (attr[i].type == ATTR_GET) { + attr[i].outlen = 0xffff; + ++numget; + } + if (attr[i].type == ATTR_GET_MULTI) { + attr[i].val = &header->mr[numgetmulti]; + ++numgetmulti; + } + } + + /* + * No ATTR_GET to process, not an error. + */ + if (numget == 0 && numgetmulti == 0) { + ret = 0; + goto unwind; + } + + /* + * Read off the header first. + */ + if (len < 8) + goto unwind; + + if ((p[0] & 0xf) == 0x9) { + if (numgetmulti) { + osd_error("%s: got list type 9, expecting multi", + __func__); + goto unwind; + } + } else if ((p[0] & 0xf) == 0xf) { + if (!numgetmulti) { + osd_error("%s: got list type f, not expecting multi", + __func__); + goto unwind; + } + } else { + osd_error("%s: expecting list type 9 or f, got 0x%x", + __func__, p[0] & 0xf); + goto unwind; + } + + list_len = get_ntohl(&p[4]) + 8; + if (list_len > len) { + osd_error("%s: target says it returned %u, but really %llu", + __func__, list_len, llu(len)); + goto unwind; + } + len = list_len; + p += 8; + len -= 8; + + /* + * Process retrieved entries, figuring out where they go. + */ + for (;;) { + uint64_t oid = 0; + uint32_t page, number; + uint16_t item_len, pad; + uint16_t avail_len; + + if (len < 10u + 8 * !!numgetmulti) + break; + if (numgetmulti) { + oid = get_ntohll(&p[0]); + p += 8; + } + page = get_ntohl(&p[0]); + number = get_ntohl(&p[4]); + item_len = get_ntohs(&p[8]); + p += 10; + len -= 10; + + for (i=0; i len) + avail_len = len; + + /* + * This can happen for a list of gets since the target does + * not know the requested size of each one, just return what + * we can. + */ + if (avail_len > attr[i].len) + avail_len = attr[i].len; + + /* set it, even if len is zero */ + if (attr[i].type == ATTR_GET_MULTI) { + struct attribute_get_multi_results *mr + = attr[i].val; + + mr->oid[mr->numoid] = oid; + mr->val[mr->numoid] = avail_len ? p : NULL; + mr->outlen[mr->numoid] = avail_len; + ++mr->numoid; + } else { + attr[i].val = avail_len ? p : NULL; + attr[i].outlen = avail_len; + } + + if (item_len == 0xffff) + item_len = 0; + + pad = roundup8(10 + item_len) - (10 + item_len); + if (item_len + pad >= len) + break; + + p += item_len + pad; + len -= item_len + pad; + } + ret = 0; + +unwind: + command->outdata = header->orig_outdata; + command->outlen = header->orig_outlen; + command->iov_outlen = header->orig_iov_outlen; + command->indata = header->orig_indata; + command->inlen_alloc = header->orig_inlen_alloc; + command->iov_inlen = header->orig_iov_inlen; + + /* This is tricky. We modified the inlen_alloc so that we + * could reserve extra space for the retrieved attributes. + * But if it was a short read, there is no way for the user + * to find that out without looking at the returned status, + * since the pad bytes papered over the short read. Instead + * of fixing it up here, or in wait_response, we just put + * inlen back so it is no bigger than what was asked for. But + * this is no guarantee that any of those bytes are valid + * read data! + */ + if (command->inlen > command->inlen_alloc) + command->inlen = command->inlen_alloc; + return ret; +} + +void osd_command_attr_free(struct osd_command *command) +{ + free(command->attr_malloc); +} + +/* + * All attributes can be retrieved if asking for number 0xffffffff + * of any page. Further, if the page is 0xffffffff, a list of all user-defined + * attributes on all pages will be returned. + * + * Could try to integrate this with the rest of the attr handlers, but it + * would be very messy. The number of returned entries is not known a priori. + * Signaling that there are more items available than for which space was + * requested is difficult too. Instead we just have a specialized set of + * all-attribute handlers, hoping that the need to combine this sort of + * attribute request with others will not happen. + */ +int osd_command_attr_all_build(struct osd_command *command, uint32_t page) +{ + struct attr_malloc_header *header; + uint8_t *p, *extra_out_buf, *extra_in_buf; + + /* + * In some of the later OSD specs, max space for a returned list + * is a 32-bit field. Get a big chunk of memory for this, because + * if we can't list all the items, there is no way to start the list + * again from the middle. + */ + int len = 512 << 10; /* 512k is the most BSG will let us do */ + + /* + * Cannot use normal attr build as it caps the lengths of items at + * 16 bits. We're asking for a whole bunch of items, each of which + * will be shorter than 16 bits, but together will exceed that. + */ + + /* not build to handle other in or out data */ + if (command->inlen_alloc || command->outlen) + return 1; + + p = Malloc(roundup8(sizeof(*header)) + 16 + len); + if (!p) + return 1; + + /* to free it later */ + command->attr_malloc = p; + + header = (void *) p; + p += roundup8(sizeof(*header)); + + extra_out_buf = p; + p += 16; + extra_in_buf = p; + + /* + * Build the getlist. + */ + p = extra_out_buf; + p[0] = 0x1; + p[1] = p[2] = p[3] = 0; + set_htonl(&p[4], 8); + set_htonl(&p[8], page); + set_htonl(&p[12], 0xffffffff); + + p = extra_in_buf; + header->retrieved_buf = p; + + /* + * Fill the CDB bits. List format. + */ + set_htonl(&command->cdb[52], 16); /* size getlist */ + set_htonl(&command->cdb[60], len); /* size retrieved */ + + command->outdata = extra_out_buf; + command->outlen = 16; + + command->indata = extra_in_buf; + command->inlen_alloc = len; + + return 0; +} + +/* + * Should have a bunch of "9 format" retrieved values. Allocate room + * for a new attr structure, return that. + */ +int osd_command_attr_all_resolve(struct osd_command *command) +{ + struct attr_malloc_header *header = command->attr_malloc; + uint8_t *p; + uint64_t len; + uint32_t list_len; + int ret = -EINVAL; + + p = header->retrieved_buf; + len = command->inlen; + VALGRIND_MAKE_MEM_DEFINED(p, len); + + /* + * Read off the header first. + */ + if (len < 8) + goto unwind; + + if ((p[0] & 0xf) != 0x9) { + osd_error("%s: expecting list type 9, got 0x%x", + __func__, p[0] & 0xf); + goto unwind; + } + + list_len = get_ntohl(&p[4]) + 8; + if (list_len > len) { + osd_error("%s: target says it returned %u, but really %llu", + __func__, list_len, llu(len)); + goto unwind; + } + len = list_len; + p += 8; + len -= 8; + + /* + * First figure out how many entries were returned. + */ + command->numattr = 0; + for (;;) { + uint16_t item_len, pad; + + if (len < 16u) /* 10 header plus val plus roundup */ + break; + item_len = get_ntohs(&p[8]); + p += 10; + len -= 10; + + if (item_len == 0xffff) { + osd_error("%s: target returned item_len -1", __func__); + goto unwind; + } + + ++command->numattr; + + pad = roundup8(10 + item_len) - (10 + item_len); + if (item_len + pad >= len) + break; + + p += item_len + pad; + len -= item_len + pad; + } + + /* + * Allocate space for them. Okay if none. + */ + ret = 0; + if (command->numattr == 0) + goto unwind; + + command->attr = Malloc(command->numattr * sizeof(*command->attr)); + + /* + * Now read it all, pointing their vals into the one big buf. + */ + p = header->retrieved_buf + 8; + len = list_len - 8; + command->numattr = 0; + for (;;) { + uint32_t page, number; + uint16_t item_len, pad; + uint16_t avail_len; + + if (len < 16u) + break; + page = get_ntohl(&p[0]); + number = get_ntohl(&p[4]); + item_len = get_ntohs(&p[8]); + p += 10; + len -= 10; + avail_len = item_len; + + /* + * Ran off the end of the allocated buffer? Just return + * the full entries and complain to caller. + */ + if (avail_len > len) { + avail_len = len; + ret = -E2BIG; + break; + } + + command->attr[command->numattr].val = p; + command->attr[command->numattr].page = page; + command->attr[command->numattr].number = number; + command->attr[command->numattr].outlen = avail_len; + ++command->numattr; + + pad = roundup8(10 + item_len) - (10 + item_len); + if (item_len + pad >= len) + break; + + p += item_len + pad; + len -= item_len + pad; + } + +unwind: + command->outdata = header->orig_outdata; + command->outlen = header->orig_outlen; + command->iov_outlen = header->orig_iov_outlen; + command->indata = header->orig_indata; + command->inlen_alloc = header->orig_inlen_alloc; + command->iov_inlen = header->orig_iov_inlen; + if (command->inlen > command->inlen_alloc) + command->inlen = command->inlen_alloc; + return ret; +} + +void osd_command_attr_all_free(struct osd_command *command) +{ + /* we hid the command->attr that points into attr_malloc, + * instead building our own at resolve time */ + if (command->numattr) + free(command->attr); + osd_command_attr_free(command); +} + +int osd_command_list_resolve(struct osd_command *command) +{ + uint8_t *p = command->indata; + uint64_t addl_len = get_ntohll(&p[0]); + uint64_t cont_oid = get_ntohll(&p[8]); + uint32_t lid = get_ntohl(&p[16]); + int num_results = (addl_len-24)/8; + uint64_t list[num_results]; + int i, listoid, list_attr; + + listoid = (p[23] & 0x40); + char title[3]; + (listoid ? strcpy(title, "OID") : strcpy(title, "PID")); + + if (p[23] & 0x08) + list_attr = 1; + else if (p[23] & 0x04) + list_attr = 0; + + /* + * For now, output the list elements. Later, work this into + * the normal attr_resolve above. Users have to specify what + * attributes they want to get, so we know what to expect and + * can fill in the results. Just LIST has some different formats + * not handled by the current (messy) attr_resolve. + */ + if (lid || cont_oid) + osd_debug("LID: %u CONTINUE_OID: %llu", lid, llu(cont_oid)); + + if (list_attr) { + /* List or store get/set attributes results in the + * command struct somehow, + * using a new method or perhaps attr_resolve? + * + * Also, will need to change how the data is extracted + * based on the value of list_attr, since each obj_descriptor + * isn't 8 bytes apart anymore if list_attr=1 (it depends + * on the number of attributes in the attr list + */ + } + + for (i=0; i < num_results; i++) { + list[i] = get_ntohll(&p[24+8*i]); + osd_debug("%s: %llu", title, llu(list[i])); + + } + + return 1; +} + +int osd_command_list_collection_resolve(struct osd_command *command) +{ + uint8_t *p = command->indata; + uint64_t addl_len = get_ntohll(&p[0]); + uint64_t cont_oid = get_ntohll(&p[8]); + uint32_t lid = get_ntohl(&p[16]); + int num_results = (addl_len-24)/8; + uint64_t list[num_results]; + int i, listoid, list_attr; + + listoid = (p[23] & 0x80); + char title[3]; + (listoid ? strcpy(title, "OID") : strcpy(title, "CID")); + + if (p[23] & 0x08) + list_attr = 1; + else if (p[23] & 0x04) + list_attr = 0; + + /* + * For now, output the list elements. Later, work this into + * the normal attr_resolve above. Users have to specify what + * attributes they want to get, so we know what to expect and + * can fill in the results. Just LIST has some different formats + * not handled by the current (messy) attr_resolve. + */ + if (lid || cont_oid) + osd_debug("LID: %u CONTINUE_OID: %llu", lid, llu(cont_oid)); + + if (list_attr) { + /* List or store get/set attributes results in the + * command struct somehow, + * using a new method or perhaps attr_resolve? + * + * Also, will need to change how the data is extracted + * based on the value of list_attr, since each obj_descriptor + * isn't 8 bytes apart anymore if list_attr=1 (it depends + * on the number of attributes in the attr list + */ + } + + for (i=0; i < num_results; i++) { + list[i] = get_ntohll(&p[24+8*i]); + osd_debug("%s: %llu", title, llu(list[i])); + } + + return 1; +} diff --git a/osd-initiator/command.h b/osd-initiator/command.h new file mode 100644 index 0000000..296a115 --- /dev/null +++ b/osd-initiator/command.h @@ -0,0 +1,184 @@ +/* + * Commands and attributes. + * + * Copyright (C) 2007 OSD Team + * + * 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 . + */ +#ifndef _COMMAND_H +#define _COMMAND_H + +#include +#include "osd-util/osd-defs.h" + +/* Data Structure Definition */ + +struct attribute_list { + enum { + ATTR_GET, + ATTR_GET_PAGE, + ATTR_GET_MULTI, + ATTR_SET, + ATTR_RESULT, + } type; + uint32_t page; + uint32_t number; + void *val; + uint16_t len; + uint16_t outlen; /* 0 -> empty or not exist, 0xffff -> overflow */ +}; + +/* + * In the case of ATTR_GET_MULTI, each returned attr->val will point to + * one of these. Look inside it to find the values for each oid. + */ +struct attribute_get_multi_results { + int numoid; + union { + uint64_t *oid; + uint64_t *cid; + uint64_t *pid; + }; + const void **val; /* arrays for each oid: val, outlen */ + uint16_t *outlen; +}; + +/* + * This is copied from a kernel header to avoid including it. + */ +struct bsg_iovec { + uint64_t iov_base; + uint32_t iov_len; + uint32_t __pad1; +}; + +/* + * All information needed to submit a command to the kernel module. + * [i] = provided by caller to submit_osd() + * [o] = return from library to caller + */ +struct osd_command { + uint8_t cdb[OSD_CDB_SIZE]; /* [i] maximum length CDB */ + int cdb_len; /* [i] actual len of valid bytes */ + const void *outdata; /* [i] data, goes out to target */ + size_t outlen; /* [i] length */ + int iov_outlen; /* [i] if non-zero, data are iovecs */ + void *indata; /* [o] results, returned from target */ + size_t inlen_alloc; /* [i] alloc size for results */ + size_t inlen; /* [o] actual size returned */ + int iov_inlen; /* [i] */ + uint8_t status; /* [o] scsi status */ + uint8_t sense[OSD_MAX_SENSE]; /* [o] sense errors */ + int sense_len; /* [o] number of bytes in sense */ + struct attribute_list *attr; /* [o] after attr_resolve() */ + int numattr; /* [o] */ + void *attr_malloc; /* [x] internal use only */ +}; + + +/* Set up the CDB */ +int osd_command_set_test_unit_ready(struct osd_command *command); +int osd_command_set_inquiry(struct osd_command *command, uint8_t page_code, + uint8_t outlen); +int osd_command_set_append(struct osd_command *command, uint64_t pid, + uint64_t oid, uint64_t len); +int osd_command_set_clear(struct osd_command *command, uint64_t pid, + uint64_t oid, uint64_t len, uint64_t offset); +int osd_command_set_create(struct osd_command *command, uint64_t pid, + uint64_t requested_oid, uint16_t num_user_objects); +int osd_command_set_create_and_write(struct osd_command *command, uint64_t pid, + uint64_t requested_oid, uint64_t len, + uint64_t offset); +int osd_command_set_create_collection(struct osd_command *command, + uint64_t pid, uint64_t requested_cid); +int osd_command_set_create_partition(struct osd_command *command, + uint64_t requested_pid); +int osd_command_set_flush(struct osd_command *command, uint64_t pid, uint64_t len, + uint64_t offset, uint64_t oid, int flush_scope); +int osd_command_set_flush_collection(struct osd_command *command, uint64_t pid, + uint64_t cid, int flush_scope); +int osd_command_set_flush_osd(struct osd_command *command, int flush_scope); +int osd_command_set_flush_partition(struct osd_command *command, uint64_t pid, + int flush_scope); +int osd_command_set_format_osd(struct osd_command *command, uint64_t capacity); +int osd_command_set_get_attributes(struct osd_command *command, uint64_t pid, + uint64_t oid); +int osd_command_set_get_member_attributes(struct osd_command *command, + uint64_t pid, uint64_t cid); +int osd_command_set_list(struct osd_command *command, uint64_t pid, + uint32_t list_id, uint64_t alloc_len, + uint64_t initial_oid, int list_attr); +int osd_command_set_list_collection(struct osd_command *command, uint64_t pid, + uint64_t cid, uint32_t list_id, + uint64_t alloc_len, + uint64_t initial_oid, + int list_attr); +int osd_command_set_perform_scsi_command(struct osd_command *command); +int osd_command_set_perform_task_mgmt_func(struct osd_command *command); +int osd_command_set_punch(struct osd_command *command, uint64_t pid, + uint64_t oid, uint64_t len, uint64_t offset); +int osd_command_set_query(struct osd_command *command, uint64_t pid, + uint64_t cid, uint32_t query_len, uint64_t alloc_len); +int osd_command_set_read(struct osd_command *command, uint64_t pid, + uint64_t oid, uint64_t len, uint64_t offset); +int osd_command_set_read_map(struct osd_command*command, uint64_t pid, uint64_t oid, + uint64_t alloc_len, uint64_t offset, uint8_t map_type); +int osd_command_set_remove(struct osd_command *command, uint64_t pid, + uint64_t oid); +int osd_command_set_remove_collection(struct osd_command *command, + uint64_t pid, uint64_t cid, int force); +int osd_command_set_remove_member_objects(struct osd_command *command, + uint64_t pid, uint64_t cid); +int osd_command_set_remove_partition(struct osd_command *command, + uint64_t pid); +int osd_command_set_set_attributes(struct osd_command *command, uint64_t pid, + uint64_t oid); +int osd_command_set_set_key(struct osd_command *command, int key_to_set, + uint64_t pid, uint64_t key, const uint8_t seed[20]); +int osd_command_set_set_master_key(struct osd_command *command, int dh_step, + uint64_t key, uint32_t param_len, + uint32_t alloc_len); +int osd_command_set_set_member_attributes(struct osd_command *command, + uint64_t pid, uint64_t cid); +int osd_command_set_write(struct osd_command *command, uint64_t pid, + uint64_t oid, uint64_t len, uint64_t offset); +void osd_command_set_ddt(struct osd_command *command, uint8_t ddt_type); +uint8_t osd_command_get_ddt(struct osd_command *command); +/* + * Extensions, not yet in T10 or SNIA OSD spec. + */ +int osd_command_set_cas(struct osd_command *command, uint64_t pid, + uint64_t oid, uint64_t len, uint64_t offset); +int osd_command_set_fa(struct osd_command *command, uint64_t pid, + uint64_t oid, uint64_t len, uint64_t offset); +int osd_command_set_gen_cas(struct osd_command *command, uint64_t pid, + uint64_t oid); +int osd_command_set_cond_setattr(struct osd_command *command, uint64_t pid, + uint64_t oid); + +/* Attributes */ +int osd_command_attr_build(struct osd_command *command, + const struct attribute_list *const attrs, int num); +int osd_command_attr_resolve(struct osd_command *command); +void osd_command_attr_free(struct osd_command *command); + +/* Get all attributes */ +int osd_command_attr_all_build(struct osd_command *command, uint32_t page); +int osd_command_attr_all_resolve(struct osd_command *command); +void osd_command_attr_all_free(struct osd_command *command); + +/* Lists */ +int osd_command_list_resolve(struct osd_command *command); +int osd_command_list_collection_resolve(struct osd_command *command); + +#endif diff --git a/osd-initiator/device.c b/osd-initiator/device.c new file mode 100644 index 0000000..c21b734 --- /dev/null +++ b/osd-initiator/device.c @@ -0,0 +1,134 @@ +/* + * Use the BSG interface in the kernel to submit SCSI commands and retrieve + * responses. + * + * Copyright (C) 2007 OSD Team + * + * 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 "osd-util/osd-util.h" +#include "osd-util/bsg.h" +#include "command.h" +#include "device.h" + +int osd_submit_command(int fd, struct osd_command *command) +{ + int ret; + struct sg_io_v4 sg; + + memset(&sg, 0, sizeof(sg)); + sg.guard = 'Q'; + sg.request_len = command->cdb_len; + sg.request = (uint64_t) (uintptr_t) command->cdb; + sg.max_response_len = sizeof(command->sense); + sg.response = (uint64_t) (uintptr_t) command->sense; + + if (command->outlen) { + sg.dout_xfer_len = command->outlen; + sg.dout_xferp = (uint64_t) (uintptr_t) command->outdata; + sg.dout_iovec_count = command->iov_outlen; + } + if (command->inlen_alloc) { + sg.din_xfer_len = command->inlen_alloc; + sg.din_xferp = (uint64_t) (uintptr_t) command->indata; + sg.din_iovec_count = command->iov_inlen; + } + + /* + * Allow 30 sec for entire command. Some can be + * slow, especially with debugging messages on. + */ + sg.timeout = 30000; + sg.usr_ptr = (uint64_t) (uintptr_t) command; + ret = write(fd, &sg, sizeof(sg)); + if (ret < 0) { + osd_error_errno("%s: write", __func__); + return -errno; + } + if (ret != sizeof(sg)) { + osd_error("%s: short write, %d not %zu", __func__, ret, + sizeof(sg)); + return -EIO; + } + return 0; +} + +int osd_wait_response(int fd, struct osd_command **out_command) +{ + struct sg_io_v4 sg; + struct osd_command *command; + int ret; + + ret = read(fd, &sg, sizeof(sg)); + if (ret < 0) { + osd_error_errno("%s: read", __func__); + return -errno; + } + if (ret != sizeof(sg)) { + osd_error("%s: short read, %d not %zu", __func__, ret, + sizeof(sg)); + return -EPIPE; + } + + command = (void *)(uintptr_t) sg.usr_ptr; + if (command->inlen_alloc) + command->inlen = command->inlen_alloc - sg.din_resid; + command->status = sg.device_status; + command->sense_len = sg.response_len; + + *out_command = command; + + return 0; +} + +/* + * osd_wait_response, plus verify that the command retrieved was the + * one we expected, or error. + */ +int osd_wait_this_response(int fd, struct osd_command *command) +{ + int ret; + struct osd_command *cmp; + + ret = osd_wait_response(fd, &cmp); + if (ret == 0) { + if (cmp != command) { + osd_error("%s: wrong command returned", __func__); + ret = -EIO; + } + } + return ret; +} + +int osd_submit_and_wait(int fd, struct osd_command *command) +{ + int ret; + + ret = osd_submit_command(fd, command); + if (ret) { + osd_error("%s: submit failed", __func__); + return ret; + } + + ret = osd_wait_this_response(fd, command); + if (ret) { + osd_error("%s: wait_response failed", __func__); + return ret; + } + return 0; +} diff --git a/osd-initiator/device.h b/osd-initiator/device.h new file mode 100644 index 0000000..0b04587 --- /dev/null +++ b/osd-initiator/device.h @@ -0,0 +1,28 @@ +/* + * Device interaction. + * + * Copyright (C) 2007 OSD Team + * + * 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 . + */ +#ifndef _DEVICE_H +#define _DEVICE_H + +struct osd_command; + +int osd_submit_command(int fd, struct osd_command *command); +int osd_wait_response(int fd, struct osd_command **command); +int osd_wait_this_response(int fd, struct osd_command *command); +int osd_submit_and_wait(int fd, struct osd_command *command); + +#endif /* _DEVICE_H */ diff --git a/osd-initiator/drivelist.c b/osd-initiator/drivelist.c new file mode 100644 index 0000000..f1813f8 --- /dev/null +++ b/osd-initiator/drivelist.c @@ -0,0 +1,172 @@ +/* + * Drive list. + * + * Copyright (C) 2007 OSD Team + * + * 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 "osd-util/osd-util.h" +#include "command.h" +#include "device.h" +#include "drivelist.h" + +/* + * Ask for page 0x80 to get serial number. Returns a new string if it + * succeeds. + */ +static char *osd_get_drive_serial(int fd) +{ + struct osd_command command; + char *s, *p, buf[80]; + int ret; + unsigned int len; + + memset(buf, 0, sizeof(buf)); + osd_command_set_inquiry(&command, 0x80, sizeof(buf)); + command.cdb[2] = 0x80; + command.indata = buf; + command.inlen_alloc = sizeof(buf); + + ret = osd_submit_and_wait(fd, &command); + if (ret) { + osd_error_xerrno(ret, "%s: osd_submit_and_wait failed", + __func__); + return NULL; + } + if (command.inlen < 4) + return NULL; + len = buf[3]; + if (len == 0) + return NULL; + if (len < command.inlen - 4) + len = command.inlen - 4; + /* trim spaces left and right */ + p = &buf[4]; + while (len > 0 && *p == ' ') { + ++p; + --len; + } + if (len == 0) + return NULL; + while (len > 0 && p[len-1] == ' ') + --len; + if (len == 0) + return NULL; + s = malloc(len + 1); + if (!s) + return NULL; + memcpy(s, p, len); + s[len] = '\0'; + + return s; +} + +int osd_get_drive_list(struct osd_drive_description **drives, int *num_drives) +{ + struct osd_drive_description *ret = NULL; + struct dirent *entry; + int count, fd, type; + char buf[512]; + char *serial; + DIR *toplevel; + + /* + * Walk through /dev/bsg to find available devices. Could look + * through /sys/class/bsg, but would have to create each device + * by hand in /tmp somewhere to use it, or figure out the mapping + * in /dev anyway. + */ + count = 0; + toplevel = opendir("/dev/bsg"); + if (!toplevel) + goto out; + + /* First, get the count */ + while ((entry = readdir(toplevel))) + ++count; + + /* subtract 2 for . and .. */ + count -= 2; + + if (count <= 0) + goto out; + + ret = malloc(count * sizeof(*ret)); + memset(ret, 0, count * sizeof(*ret)); + rewinddir(toplevel); + count = 0; + while ((entry = readdir(toplevel))) { + if (entry->d_name[0] == '.') + continue; + snprintf(buf, sizeof(buf), + "/sys/class/scsi_device/%s/device/type", + entry->d_name); + fd = open(buf, O_RDONLY); + if (fd < 0) + continue; + + type = 0; + buf[0] = '\0'; + read(fd, buf, sizeof(buf)); + sscanf(buf, "%d", &type); + close(fd); + + if (type != 17) /* TYPE_OSD */ + continue; + + snprintf(buf, sizeof(buf), "/dev/bsg/%s", entry->d_name); + + fd = open(buf, O_RDWR); + if (fd < 0) + continue; + + serial = osd_get_drive_serial(fd); + close(fd); + + if (!serial) + continue; + + ret[count].targetname = serial; + ret[count].chardev = strdup(buf); + ++count; + } + +out: + if (toplevel) + closedir(toplevel); + *drives = ret; + *num_drives = count; + return 0; +} + +void osd_free_drive_list(struct osd_drive_description *drives, int num_drives) +{ + int i; + + for (i=0; i + * + * 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 . + */ +#ifndef _DRIVELIST_H +#define _DRIVELIST_H + +struct osd_drive_description { + char *targetname; + char *chardev; +}; + +int osd_get_drive_list(struct osd_drive_description **drives, int *num_drives); +void osd_free_drive_list(struct osd_drive_description *drives, int num_drives); + +#endif diff --git a/osd-initiator/python/Makefile b/osd-initiator/python/Makefile new file mode 100644 index 0000000..3c4b292 --- /dev/null +++ b/osd-initiator/python/Makefile @@ -0,0 +1,61 @@ +# +# Build python shared library from osd initiator sources. +# + +# edit these files +SRC := pyosd.c attr.c command.c device.c drivelist.c const.c +OBJ := $(SRC:.c=.o) +LIB := pyosd.so + +# external libraries being wrapped for python +HASPYCFG := $(shell hash python-config > /dev/null 2>&1 && echo yes) +ifeq ($(HASPYCFG),yes) + INCDIR := $(shell python-config --includes) +else + # mad guess + INCDIR := -I/usr/include/python2.4 +endif +INCDIR += -I../.. +OSDLIBS := ../libosdinit.a ../../osd-util/libosdutil.a + +-include ../../Makedefs + +# usual definitions +CC := gcc +CPP_M := -MM +LD := $(CC) +COPTS := $(OPT) -fPIC +CWARN := -Wall -W -Wno-missing-field-initializers \ + -Wpointer-arith -Wcast-align -Wcast-qual \ + -Wbad-function-cast -Wundef -Wmissing-prototypes -Wstrict-prototypes \ + -Wmissing-declarations -Wnested-externs +CFLAGS := $(COPTS) $(CWARN) $(INCDIR) + +all:: $(LIB) writefile.py + +$(LIB): $(OBJ) $(OSDLIBS) + gcc -shared -o $@ $^ + +%.o: %.c + $(CC) $(CFLAGS) -c $< -o $@ + +writefile.py: + ln -fs readfile.py writefile.py + +ifeq (,$(filter clean distclean dist,$(MAKECMDGOALS))) +-include .depend +endif +all:: .depend +.depend: $(INC) Makefile + $(CC) $(CPP_M) $(INCDIR) $(SRC) > .depend + +clean: + rm -f $(LIB) $(OBJ) + rm -f .depend tags + rm -f writefile.py + +tags: FORCE + ctags $(SRC) + +FORCE:; + diff --git a/osd-initiator/python/README b/osd-initiator/python/README new file mode 100644 index 0000000..ae6cc47 --- /dev/null +++ b/osd-initiator/python/README @@ -0,0 +1,26 @@ + +Documentation for OSD Initiator library, Python interface. + +Copyright (C) 2007 Dennis Dalessandro (dennis@osc.edu) +Copyright (C) 2007 Ananth Devulapalli (ananth@osc.edu) +Copyright (C) 2007 Pete Wyckoff (pw@osc.edu) + +This directory contains files that build into a shared module that can +be used by Python programs to use the OSD initiator C library. + +File contents. Many of these parallel their C versions in the directory +above this one: + + pyosd.c - Main entry point for Python, some utility functions + command.c - A function for each OSD command, plus some getters and + setters + attr.c - Attribute type + device.c - Command submission and retrieval + drivelist.c - Device identification + const.c - Initialize a bunch of handy constants in a dictionary + +See the module help in Python, and the test .py programs for hints +on how to use this. It is quite handy for rapidly testing new functions +when writing a new C program would take longer. + +# vim: set tw=72 : diff --git a/osd-initiator/python/attr.c b/osd-initiator/python/attr.c new file mode 100644 index 0000000..f48fc68 --- /dev/null +++ b/osd-initiator/python/attr.c @@ -0,0 +1,188 @@ +/* + * Attribute bits. + * + * Copyright (C) 2007 OSD Team + * + * 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 /* T_UBYTE */ +#include "osd-initiator/command.h" +#include "osd-util/osd-util.h" +#include "pyosd.h" + +/* + * Constructor function. + * GET page number expected_len + * GETPAGE page expected_len + * GETMULTI page number expected_len_each + * SET page number val + * SET page number None == delete + */ +static int pyosd_attr_init(PyObject *self, PyObject *args, + PyObject *keywords __attribute__((unused))) +{ + struct pyosd_attr *py_attr = (struct pyosd_attr *) self; + struct attribute_list *attr = &py_attr->attr; + int attr_type; + unsigned int page, number = 0, len = 0; + PyObject *val = NULL; + + if (!PyArg_ParseTuple(args, "iO|OO:init", &attr_type, &val, &val, &val)) + return -1; + + if (attr_type == ATTR_GET || attr_type == ATTR_GET_MULTI) { + if (!PyArg_ParseTuple(args, "iIII:init", &attr_type, &page, + &number, &len)) + return -1; + } else if (attr_type == ATTR_GET_PAGE) { + if (!PyArg_ParseTuple(args, "iII:init", &attr_type, &page, + &len)) + return -1; + } else if (attr_type == ATTR_SET) { + if (!PyArg_ParseTuple(args, "iIIO:init", &attr_type, &page, + &number, &val)) + return -1; + } else { + PyErr_Format(PyExc_TypeError, "type %d unknown", attr_type); + return -1; + } + + attr->type = attr_type; + attr->page = page; + attr->number = number; + if (attr_type == ATTR_SET) { + /* memcpy now to avoid complexity with figuring out freeing */ + if (PyString_Check(val)) { + attr->len = PyString_Size(val); + attr->val = PyMem_Malloc(attr->len); + memcpy(attr->val, PyString_AsString(val), + attr->len); + } else if (PyInt_Check(val)) { + uint32_t x; + attr->len = 4; + attr->val = PyMem_Malloc(4); + x = PyInt_AsLong(val); + memcpy(attr->val, &x, attr->len); + } else if (PyLong_Check(val)) { + uint64_t x; + attr->len = 8; + attr->val = PyMem_Malloc(8); + x = PyLong_AsUnsignedLongLong(val); + memcpy(attr->val, &x, attr->len); + } else if (val == Py_None) { + attr->len = 0; + attr->val = NULL; /* delete attribute */ + } else { + PyErr_SetString(PyExc_RuntimeError, + "cannot linearize this type"); + return -1; + } + } else { + attr->val = NULL; + attr->len = len; + } + return 0; +} + +/* + * Destructor. Free val member if it was created for a get, or drop + * its ref if it was passed in for a set. + */ +static void pyosd_attr_dealloc(PyObject *self) +{ + struct pyosd_attr *py_attr = (struct pyosd_attr *) self; + struct attribute_list *attr = &py_attr->attr; + + /* + * If set, data was copied in constructor; if get, was copied + * as part of resolve. + */ + PyMem_Free(attr->val); +} + +/* + * Pretty-print object. + */ +static PyObject *pyosd_attr_print(PyObject *self) +{ + struct pyosd_attr *py_attr = (struct pyosd_attr *) self; + const struct attribute_list *attr = &py_attr->attr; + const char *type = NULL; + PyObject *o; + + if (attr->type == ATTR_GET) + type = "ATTR_GET"; + else if (attr->type == ATTR_GET_PAGE) + type = "ATTR_GET_PAGE"; + else if (attr->type == ATTR_GET_MULTI) + type = "ATTR_GET_MULTI"; + else if (attr->type == ATTR_SET) + type = "ATTR_SET"; + o = PyString_FromFormat( + "type %s page 0x%x number 0x%x len %d outlen %d", + type, attr->page, attr->number, attr->len, + attr->outlen); + return o; +} + +/* + * Return val. Should keep a ref to the command to check if it is done. + */ +static PyObject *pyosd_attr_get_val(PyObject *self, + void *closure __attribute__((unused))) +{ + struct pyosd_attr *py_attr = (struct pyosd_attr *) self; + const struct attribute_list *attr = &py_attr->attr; + + if (attr->type == ATTR_GET_MULTI) { + PyErr_SetString(PyExc_RuntimeError, "handle this case."); + return NULL; + } else if (attr->type == ATTR_GET || attr->type == ATTR_GET_PAGE) { + /* return None for 0xffff, but "" for 0 */ + if (attr->outlen < 0xffff) + return Py_BuildValue("s#", attr->val, attr->outlen); + else + Py_RETURN_NONE; + } else { + PyErr_SetString(PyExc_RuntimeError, + "only GET values can be retrieved"); + return NULL; + } +} + +struct PyGetSetDef pyosd_attr_getset[] = { + { "val", pyosd_attr_get_val, NULL, "returned attribute value", NULL }, + { NULL } +}; + +struct PyMemberDef pyosd_attr_members[] = { + { "outlen", T_USHORT, offsetof(struct pyosd_attr, attr.outlen), + READONLY, "returned attribute length" }, + { NULL } +}; + +PyTypeObject pyosd_attr_type = { + PyObject_HEAD_INIT(NULL) + .tp_name = "OSDAttr", + .tp_basicsize = sizeof(struct pyosd_attr), + .tp_flags = Py_TPFLAGS_DEFAULT, + .tp_doc = "Python encapsulation of struct attribute_list", + .tp_dealloc = pyosd_attr_dealloc, + .tp_init = pyosd_attr_init, + .tp_new = PyType_GenericNew, + .tp_getset = pyosd_attr_getset, + .tp_members = pyosd_attr_members, + .tp_str = pyosd_attr_print, +}; + diff --git a/osd-initiator/python/command.c b/osd-initiator/python/command.c new file mode 100644 index 0000000..b524b4c --- /dev/null +++ b/osd-initiator/python/command.c @@ -0,0 +1,1080 @@ +/* + * Commands. + * + * Copyright (C) 2007-8 OSD Team + * + * 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 /* T_UBYTE */ +#include "osd-initiator/command.h" +#include "osd-initiator/sense.h" +#include "osd-util/osd-util.h" +#include "pyosd.h" + +/* + * Clear everything. Command will be cleared by a set function. + */ +static int pyosd_command_init(PyObject *self, + PyObject *args __attribute__((unused)), + PyObject *keywords __attribute__((unused))) +{ + struct pyosd_command *py_command = (struct pyosd_command *) self; + + py_command->set = 0; + py_command->complete = 0; + return 0; +} + +static void pyosd_command_dealloc(PyObject *self) +{ + struct pyosd_command *py_command = (struct pyosd_command *) self; + struct osd_command *command = &py_command->command; + + if (command->attr) { + osd_command_attr_resolve(command); + osd_command_attr_free(command); + } + PyMem_Free(command->indata); + PyMem_Free((void *)(uintptr_t) command->outdata); +} + +static PyObject *pyosd_command_get_indata(PyObject *self, + void *closure __attribute__((unused))) +{ + struct pyosd_command *py_command = (struct pyosd_command *) self; + struct osd_command *command = &py_command->command; + + if (!py_command->complete) { + PyErr_SetString(PyExc_RuntimeError, "command not complete"); + return NULL; + } + if (command->indata) + return PyString_FromStringAndSize(command->indata, + command->inlen); + Py_RETURN_NONE; +} + +static PyObject *pyosd_command_get_sense(PyObject *self, void *closure) +{ + struct pyosd_command *py_command = (struct pyosd_command *) self; + struct osd_command *command = &py_command->command; + const char *which = closure; + int key, code; + + if (!py_command->complete) { + PyErr_SetString(PyExc_RuntimeError, "command not complete"); + return NULL; + } + osd_sense_extract(command->sense, command->sense_len, &key, &code); + if (strcmp(which, "key") == 0) + return PyInt_FromLong(key); + if (strcmp(which, "code") == 0) + return PyInt_FromLong(code); + Py_RETURN_NONE; +} + +static PyObject *pyosd_command_show_sense(PyObject *self, PyObject *args) +{ + struct pyosd_command *py_command = (struct pyosd_command *) self; + struct osd_command *command = &py_command->command; + char *s; + PyObject *o; + + if (!PyArg_ParseTuple(args, ":show_sense")) + return NULL; + if (!py_command->complete) { + PyErr_SetString(PyExc_RuntimeError, "command not complete"); + return NULL; + } + + if (command->status == 0) + return Py_BuildValue("s", "No sense"); + if (command->status != 2) + return Py_BuildValue("s", "Unknown status code"); + s = osd_sense_as_string(command->sense, command->sense_len); + o = Py_BuildValue("s", s); + free(s); + return o; +} + +/* + * Called once to linearize the list of attributes (or single). + */ +static struct attribute_list *attr_flatten(PyObject *o, int *numattr) +{ + struct pyosd_attr *py_attr; + struct attribute_list *attr = NULL; + *numattr = 0; + + if (PyObject_TypeCheck(o, &pyosd_attr_type)) { + py_attr = (struct pyosd_attr *) o; + attr = malloc(sizeof(*attr)); + if (attr == NULL) { + PyErr_NoMemory(); + return attr; + } + memcpy(attr, &py_attr->attr, sizeof(*attr)); + *numattr = 1; + } + + if (PyList_Check(o)) { + int i, num = PyList_Size(o); + if (num < 1) + goto out; + + attr = malloc(num * sizeof(*attr)); + if (attr == NULL) { + PyErr_NoMemory(); + return NULL; + } + for (i=0; iattr, sizeof(*attr)); + } + *numattr = num; + } + +out: + if (*numattr == 0) { + PyErr_SetString(PyExc_RuntimeError, + "need an OSDAttr or list of OSDAttr"); + return NULL; + } + return attr; +} + +/* + * Attributes. Valid argument to this function is OSDAttr or list of OSDAttr. + */ +static PyObject *pyosd_command_attr_build(PyObject *self, PyObject *args) +{ + struct pyosd_command *py_command = (struct pyosd_command *) self; + struct osd_command *command = &py_command->command; + struct attribute_list *attr; + int numattr; + PyObject *o; + int ret; + + if (!PyArg_ParseTuple(args, "O:attr_build", &o)) + return NULL; + + if (!py_command->set) { + PyErr_SetString(PyExc_RuntimeError, "command not set"); + return NULL; + } + if (py_command->complete) { + PyErr_SetString(PyExc_RuntimeError, "command already complete"); + return NULL; + } + if (command->attr) { + PyErr_SetString(PyExc_RuntimeError, "attributes already built"); + return NULL; + } + + /* + * Pull attrs into a single flat allocated list and keep it around + * for resolve to write back into. + */ + attr = attr_flatten(o, &numattr); + if (!attr) + return NULL; + + /* + * Copies values out of attr but does not need to hold onto it. + */ + ret = osd_command_attr_build(command, attr, numattr); + if (ret) { + PyErr_SetString(PyExc_RuntimeError, "attr_build failed"); + return NULL; + } + free(attr); + + Py_IncRef(self); + return self; +} + +static PyObject *attr_build_manual(struct attribute_list *cmdattr) +{ + PyObject *o; + struct pyosd_attr *py_attr; + struct attribute_list *attr; + + o = pyosd_attr_type.tp_new(&pyosd_attr_type, NULL, NULL); + if (!o) + return PyErr_NoMemory(); + + py_attr = (struct pyosd_attr *) o; + attr = &py_attr->attr; + memcpy(attr, cmdattr, sizeof(*attr)); + + /* 0xffff means not found */ + attr->val = NULL; + if (attr->outlen > 0 && attr->outlen < 0xffff) { + attr->val = PyMem_Malloc(attr->outlen); + if (!o) + return PyErr_NoMemory(); + memcpy(attr->val, cmdattr->val, attr->outlen); + } + return o; +} + +/* + * Must be called after the command runs, and only if attr_build was + * used. Could hide this in the run, but want to keep the python and + * C APIs similar. + * + * The attr list is kept with the command, but we force user to pass + * it in again to keep the API similar to C. + */ +static PyObject *pyosd_command_attr_resolve(PyObject *self, PyObject *args) +{ + struct pyosd_command *py_command = (struct pyosd_command *) self; + struct osd_command *command = &py_command->command; + PyObject *o; + int ret; + + if (!PyArg_ParseTuple(args, ":attr_resolve")) + return NULL; + + if (!py_command->set) { + PyErr_SetString(PyExc_RuntimeError, "command not set"); + return NULL; + } + if (!py_command->complete) { + PyErr_SetString(PyExc_RuntimeError, "command not complete"); + return NULL; + } + + /* not an error */ + if (command->attr == NULL) + Py_RETURN_NONE; + + ret = osd_command_attr_resolve(command); + if (ret) { + PyErr_SetString(PyExc_RuntimeError, "attr_resolve failed"); + return NULL; + } + + /* + * Build new OSDAttr with output values. + */ + if (command->numattr == 0) + o = Py_BuildValue(""); + else if (command->numattr == 1) + o = attr_build_manual(command->attr); + else { + int i; + o = PyList_New(command->numattr); + for (i=0; inumattr; i++) { + PyObject *oi = attr_build_manual(&command->attr[i]); + PyList_SetItem(o, i, oi); + } + } + return o; +} + +/* + * One of these for each of the major set functions. + */ +static PyObject *pyosd_command_set_test_unit_ready(PyObject *self, + PyObject *args) +{ + struct pyosd_command *py_command = (struct pyosd_command *) self; + struct osd_command *command = &py_command->command; + unsigned int page_code = 0; + + if (!PyArg_ParseTuple(args, "|I:test_unit_ready", &page_code)) + return NULL; + if (py_command->set) { + PyErr_SetString(PyExc_RuntimeError, "command already set"); + return NULL; + } + + py_command->set = 1; + osd_command_set_test_unit_ready(command); + Py_IncRef(self); + return self; +} + +static PyObject *pyosd_command_set_inquiry(PyObject *self, PyObject *args) +{ + struct pyosd_command *py_command = (struct pyosd_command *) self; + struct osd_command *command = &py_command->command; + unsigned int page_code = 0; + + if (!PyArg_ParseTuple(args, "|I:inquiry", &page_code)) + return NULL; + if (py_command->set) { + PyErr_SetString(PyExc_RuntimeError, "command already set"); + return NULL; + } + + py_command->set = 1; + osd_command_set_inquiry(command, page_code, 80); + command->indata = PyMem_Malloc(80); + if (command->indata == NULL) + return PyErr_NoMemory(); + command->inlen_alloc = 80; + Py_IncRef(self); + return self; +} + +static PyObject *pyosd_command_set_append(PyObject *self, PyObject *args) +{ + struct pyosd_command *py_command = (struct pyosd_command *) self; + struct osd_command *command = &py_command->command; + uint8_t *buf; + uint64_t pid, oid, len; + + if (!PyArg_ParseTuple(args, "KKs#:set_append", &pid, &oid, &buf, &len)) + return NULL; + if (py_command->set) { + PyErr_SetString(PyExc_RuntimeError, "command already set"); + return NULL; + } + + py_command->set = 1; + osd_command_set_append(command, pid, oid, len); + command->outdata = PyMem_Malloc(len); + if (!command->outdata) + return PyErr_NoMemory(); + memcpy((void *)(uintptr_t) command->outdata, buf, len); + command->outlen = len; + Py_IncRef(self); + return self; +} + +static PyObject *pyosd_command_set_create(PyObject *self, PyObject *args) +{ + struct pyosd_command *py_command = (struct pyosd_command *) self; + struct osd_command *command = &py_command->command; + uint64_t pid = 0, oid = 0, numobj = 1; + + if (!PyArg_ParseTuple(args, "K|KK:set_create", &pid, &oid, &numobj)) + return NULL; + if (py_command->set) { + PyErr_SetString(PyExc_RuntimeError, "command already set"); + return NULL; + } + + py_command->set = 1; + osd_command_set_create(command, pid, oid, numobj); + Py_IncRef(self); + return self; +} + +static PyObject *pyosd_command_set_create_and_write(PyObject *self, + PyObject *args) +{ + struct pyosd_command *py_command = (struct pyosd_command *) self; + struct osd_command *command = &py_command->command; + uint8_t *buf; + int len; + uint64_t pid, oid, offset = 0; + + if (!PyArg_ParseTuple(args, "KKs#|K:set_create_and_write", &pid, &oid, + &buf, &len, &offset)) + return NULL; + if (py_command->set) { + PyErr_SetString(PyExc_RuntimeError, "command already set"); + return NULL; + } + + py_command->set = 1; + osd_command_set_create_and_write(command, pid, oid, len, offset); + command->outdata = PyMem_Malloc(len); + if (!command->outdata) + return PyErr_NoMemory(); + /* XXX: take a ref on buf rather than copying it */ + memcpy((void *)(uintptr_t) command->outdata, buf, len); + command->outlen = len; + Py_IncRef(self); + return self; +} + +static PyObject *pyosd_command_set_create_collection(PyObject *self, + PyObject *args) +{ + struct pyosd_command *py_command = (struct pyosd_command *) self; + struct osd_command *command = &py_command->command; + uint64_t pid = 0, cid = 0; + + if (!PyArg_ParseTuple(args, "K|K:set_create_collection", &pid, &cid)) + return NULL; + if (py_command->set) { + PyErr_SetString(PyExc_RuntimeError, "command already set"); + return NULL; + } + + py_command->set = 1; + osd_command_set_create_collection(command, pid, cid); + Py_IncRef(self); + return self; +} + +static PyObject *pyosd_command_set_create_partition(PyObject *self, + PyObject *args) +{ + struct pyosd_command *py_command = (struct pyosd_command *) self; + struct osd_command *command = &py_command->command; + uint64_t pid = 0; + + if (!PyArg_ParseTuple(args, "|K:set_create_partition", &pid)) + return NULL; + if (py_command->set) { + PyErr_SetString(PyExc_RuntimeError, "command already set"); + return NULL; + } + + py_command->set = 1; + osd_command_set_create_partition(command, pid); + Py_IncRef(self); + return self; +} + +static PyObject *pyosd_command_set_flush(PyObject *self, PyObject *args) +{ + struct pyosd_command *py_command = (struct pyosd_command *) self; + struct osd_command *command = &py_command->command; + uint64_t pid, oid; + int scope; + + if (!PyArg_ParseTuple(args, "KKi:set_flush", &pid, &oid, &scope)) + return NULL; + if (py_command->set) { + PyErr_SetString(PyExc_RuntimeError, "command already set"); + return NULL; + } + + py_command->set = 1; + +/* FIXME: scope=2 needs a start/length params should read these in too + osd_command_set_flush(command, pid, oid, scope);*/ + osd_command_set_flush(command, pid, 0, 0, oid, 0); + Py_IncRef(self); + return self; +} + +static PyObject *pyosd_command_set_flush_collection(PyObject *self, + PyObject *args) +{ + struct pyosd_command *py_command = (struct pyosd_command *) self; + struct osd_command *command = &py_command->command; + uint64_t pid, cid; + int scope; + + if (!PyArg_ParseTuple(args, "KKi:set_flush_collection", &pid, &cid, + &scope)) + return NULL; + if (py_command->set) { + PyErr_SetString(PyExc_RuntimeError, "command already set"); + return NULL; + } + + py_command->set = 1; + osd_command_set_flush_collection(command, pid, cid, scope); + Py_IncRef(self); + return self; +} + +static PyObject *pyosd_command_set_flush_osd(PyObject *self, PyObject *args) +{ + struct pyosd_command *py_command = (struct pyosd_command *) self; + struct osd_command *command = &py_command->command; + int scope; + + if (!PyArg_ParseTuple(args, "i:set_flush_osd", &scope)) + return NULL; + if (py_command->set) { + PyErr_SetString(PyExc_RuntimeError, "command already set"); + return NULL; + } + + py_command->set = 1; + osd_command_set_flush_osd(command, scope); + Py_IncRef(self); + return self; +} + +static PyObject *pyosd_command_set_flush_partition(PyObject *self, + PyObject *args) +{ + struct pyosd_command *py_command = (struct pyosd_command *) self; + struct osd_command *command = &py_command->command; + uint64_t pid; + int scope; + + if (!PyArg_ParseTuple(args, "KKi:set_flush_partition", &pid, &scope)) + return NULL; + if (py_command->set) { + PyErr_SetString(PyExc_RuntimeError, "command already set"); + return NULL; + } + + py_command->set = 1; + osd_command_set_flush_partition(command, pid, scope); + Py_IncRef(self); + return self; +} + +static PyObject *pyosd_command_set_format_osd(PyObject *self, PyObject *args) +{ + struct pyosd_command *py_command = (struct pyosd_command *) self; + struct osd_command *command = &py_command->command; + uint64_t capacity; + + if (!PyArg_ParseTuple(args, "K:set_format_osd", &capacity)) + return NULL; + if (py_command->set) { + PyErr_SetString(PyExc_RuntimeError, "command already set"); + return NULL; + } + + py_command->set = 1; + osd_command_set_format_osd(command, capacity); + Py_IncRef(self); + return self; +} + +static PyObject *pyosd_command_set_get_attributes(PyObject *self, + PyObject *args) +{ + struct pyosd_command *py_command = (struct pyosd_command *) self; + struct osd_command *command = &py_command->command; + uint64_t pid, oid; + + if (!PyArg_ParseTuple(args, "KK:set_get_attributes", &pid, &oid)) + return NULL; + if (py_command->set) { + PyErr_SetString(PyExc_RuntimeError, "command already set"); + return NULL; + } + + py_command->set = 1; + osd_command_set_get_attributes(command, pid, oid); + Py_IncRef(self); + return self; +} + +static PyObject *pyosd_command_set_get_member_attributes(PyObject *self, + PyObject *args) +{ + struct pyosd_command *py_command = (struct pyosd_command *) self; + struct osd_command *command = &py_command->command; + uint64_t pid, cid; + + if (!PyArg_ParseTuple(args, "KK:set_get_member_attributes", &pid, &cid)) + return NULL; + if (py_command->set) { + PyErr_SetString(PyExc_RuntimeError, "command already set"); + return NULL; + } + + py_command->set = 1; + osd_command_set_get_member_attributes(command, pid, cid); + Py_IncRef(self); + return self; +} + +/* + * XXX: This is likely the wrong interface for python. + */ +static PyObject *pyosd_command_set_list(PyObject *self, PyObject *args) +{ + struct pyosd_command *py_command = (struct pyosd_command *) self; + struct osd_command *command = &py_command->command; + uint64_t pid, alloc_len, initial_oid; + uint32_t list_id; + int list_attr; + + if (!PyArg_ParseTuple(args, "KIKKi:set_list", &pid, &list_id, + &alloc_len, &initial_oid, &list_attr)) + return NULL; + if (py_command->set) { + PyErr_SetString(PyExc_RuntimeError, "command already set"); + return NULL; + } + + py_command->set = 1; + osd_command_set_list(command, pid, list_id, alloc_len, initial_oid, + list_attr); + Py_IncRef(self); + return self; +} + +static PyObject *pyosd_command_set_list_collection(PyObject *self, + PyObject *args) +{ + struct pyosd_command *py_command = (struct pyosd_command *) self; + struct osd_command *command = &py_command->command; + uint64_t pid, cid, alloc_len, initial_oid; + uint32_t list_id; + + if (!PyArg_ParseTuple(args, "KIKK:set_list_collection", &pid, &cid, + &list_id, &alloc_len, &initial_oid)) + return NULL; + if (py_command->set) { + PyErr_SetString(PyExc_RuntimeError, "command already set"); + return NULL; + } + + py_command->set = 1; + osd_command_set_list_collection(command, pid, cid, list_id, alloc_len, + initial_oid, 0); + Py_IncRef(self); + return self; +} + +static PyObject *pyosd_command_set_perform_scsi_command( + PyObject *self __attribute__((unused)), + PyObject *args __attribute__((unused))) +{ + PyErr_SetString(PyExc_RuntimeError, "unimplemented"); + return NULL; +} + +static PyObject *pyosd_command_set_perform_task_mgmt_func( + PyObject *self __attribute__((unused)), + PyObject *args __attribute__((unused))) +{ + PyErr_SetString(PyExc_RuntimeError, "unimplemented"); + return NULL; +} + +static PyObject *pyosd_command_set_query(PyObject *self, PyObject *args) +{ + struct pyosd_command *py_command = (struct pyosd_command *) self; + struct osd_command *command = &py_command->command; + uint64_t pid, cid, alloc_len; + uint32_t query_len; + + if (!PyArg_ParseTuple(args, "KKIK:set_query", &pid, &cid, &query_len, + &alloc_len)) + return NULL; + if (py_command->set) { + PyErr_SetString(PyExc_RuntimeError, "command already set"); + return NULL; + } + + py_command->set = 1; + osd_command_set_query(command, pid, cid, query_len, alloc_len); + Py_IncRef(self); + return self; +} + +static PyObject *pyosd_command_set_read(PyObject *self, PyObject *args) +{ + struct pyosd_command *py_command = (struct pyosd_command *) self; + struct osd_command *command = &py_command->command; + uint64_t pid, oid, len, offset = 0; + + if (!PyArg_ParseTuple(args, "KKK|K:set_read", &pid, &oid, &len, + &offset)) + return NULL; + if (py_command->set) { + PyErr_SetString(PyExc_RuntimeError, "command already set"); + return NULL; + } + + py_command->set = 1; + osd_command_set_read(command, pid, oid, len, offset); + command->indata = PyMem_Malloc(len); + command->inlen_alloc = len; + Py_IncRef(self); + return self; +} + +static PyObject *pyosd_command_set_remove(PyObject *self, PyObject *args) +{ + struct pyosd_command *py_command = (struct pyosd_command *) self; + struct osd_command *command = &py_command->command; + uint64_t pid, oid; + + if (!PyArg_ParseTuple(args, "KK:set_remove", &pid, &oid)) + return NULL; + if (py_command->set) { + PyErr_SetString(PyExc_RuntimeError, "command already set"); + return NULL; + } + + py_command->set = 1; + osd_command_set_remove(command, pid, oid); + Py_IncRef(self); + return self; +} + +static PyObject *pyosd_command_set_remove_collection(PyObject *self, + PyObject *args) +{ + struct pyosd_command *py_command = (struct pyosd_command *) self; + struct osd_command *command = &py_command->command; + uint64_t pid, cid; + int force = 0; + + if (!PyArg_ParseTuple(args, "KK|i:set_remove_collection", &pid, &cid, + &force)) + return NULL; + if (py_command->set) { + PyErr_SetString(PyExc_RuntimeError, "command already set"); + return NULL; + } + + py_command->set = 1; + osd_command_set_remove_collection(command, pid, cid, force); + Py_IncRef(self); + return self; +} + +static PyObject *pyosd_command_set_remove_member_objects(PyObject *self, + PyObject *args) +{ + struct pyosd_command *py_command = (struct pyosd_command *) self; + struct osd_command *command = &py_command->command; + uint64_t pid, cid; + + if (!PyArg_ParseTuple(args, "KK:set_remove_member_objects", &pid, &cid)) + return NULL; + if (py_command->set) { + PyErr_SetString(PyExc_RuntimeError, "command already set"); + return NULL; + } + + py_command->set = 1; + osd_command_set_remove_member_objects(command, pid, cid); + Py_IncRef(self); + return self; +} + +static PyObject *pyosd_command_set_remove_partition(PyObject *self, + PyObject *args) +{ + struct pyosd_command *py_command = (struct pyosd_command *) self; + struct osd_command *command = &py_command->command; + uint64_t pid; + + if (!PyArg_ParseTuple(args, "K:set_remove_partition", &pid)) + return NULL; + if (py_command->set) { + PyErr_SetString(PyExc_RuntimeError, "command already set"); + return NULL; + } + + py_command->set = 1; + osd_command_set_remove_partition(command, pid); + Py_IncRef(self); + return self; +} + +static PyObject *pyosd_command_set_set_attributes(PyObject *self, + PyObject *args) +{ + struct pyosd_command *py_command = (struct pyosd_command *) self; + struct osd_command *command = &py_command->command; + uint64_t pid, oid; + + if (!PyArg_ParseTuple(args, "KK:set_set_attributes", &pid, &oid)) + return NULL; + if (py_command->set) { + PyErr_SetString(PyExc_RuntimeError, "command already set"); + return NULL; + } + + py_command->set = 1; + osd_command_set_set_attributes(command, pid, oid); + Py_IncRef(self); + return self; +} + +static PyObject *pyosd_command_set_set_key(PyObject *self, + PyObject *args) +{ + struct pyosd_command *py_command = (struct pyosd_command *) self; + struct osd_command *command = &py_command->command; + int key_to_set; + uint64_t pid, key; + const uint8_t *seed; + int len; + + if (!PyArg_ParseTuple(args, "iKKs#:set_set_key", &key_to_set, &pid, + &key, &seed, &len)) + return NULL; + if (py_command->set) { + PyErr_SetString(PyExc_RuntimeError, "command already set"); + return NULL; + } + + py_command->set = 1; + if (len != 20) { + PyErr_SetString(PyExc_RuntimeError, "key len should be 20"); + return NULL; + } + osd_command_set_set_key(command, key_to_set, pid, key, seed); + Py_IncRef(self); + return self; +} + +static PyObject *pyosd_command_set_set_master_key(PyObject *self, + PyObject *args) +{ + return NULL; + struct pyosd_command *py_command = (struct pyosd_command *) self; + struct osd_command *command = &py_command->command; + int dh_step; + uint64_t key; + uint32_t param_len, alloc_len; + + if (!PyArg_ParseTuple(args, "iKII:set_set_master_key", &dh_step, &key, + ¶m_len, &alloc_len)) + return NULL; + if (py_command->set) { + PyErr_SetString(PyExc_RuntimeError, "command already set"); + return NULL; + } + + py_command->set = 1; + osd_command_set_set_master_key(command, dh_step, key, param_len, + alloc_len); + Py_IncRef(self); + return self; +} + +static PyObject *pyosd_command_set_set_member_attributes(PyObject *self, + PyObject *args) +{ + struct pyosd_command *py_command = (struct pyosd_command *) self; + struct osd_command *command = &py_command->command; + uint64_t pid, cid; + + if (!PyArg_ParseTuple(args, "KK:set_set_member_attributes", &pid, &cid)) + return NULL; + if (py_command->set) { + PyErr_SetString(PyExc_RuntimeError, "command already set"); + return NULL; + } + + py_command->set = 1; + osd_command_set_set_member_attributes(command, pid, cid); + Py_IncRef(self); + return self; +} + +static PyObject *pyosd_command_set_write(PyObject *self, PyObject *args) +{ + struct pyosd_command *py_command = (struct pyosd_command *) self; + struct osd_command *command = &py_command->command; + const char *buf; + int len; + uint64_t pid, oid, offset = 0; + + if (!PyArg_ParseTuple(args, "KKs#|K:set_write", &pid, &oid, &buf, &len, + &offset)) + return NULL; + if (py_command->set) { + PyErr_SetString(PyExc_RuntimeError, "command already set"); + return NULL; + } + + py_command->set = 1; + osd_command_set_write(command, pid, oid, len, offset); + command->outdata = PyMem_Malloc(len); + if (!command->outdata) + return PyErr_NoMemory(); + memcpy((void *)(uintptr_t) command->outdata, buf, len); + command->outlen = len; + Py_IncRef(self); + return self; +} + +/* + * Input: two values, compare and swap; output: original value. + */ +static PyObject *pyosd_command_set_cas(PyObject *self, PyObject *args) +{ + struct pyosd_command *py_command = (struct pyosd_command *) self; + struct osd_command *command = &py_command->command; + uint64_t pid, oid, len = 8, offset = 0; + const char *inbuf, *outbuf; + int inlen, outlen; + + if (!PyArg_ParseTuple(args, "KKs#s#|KK:set_cas", &pid, &oid, + &inbuf, &inlen, &outbuf, &outlen, &len, &offset)) + return NULL; + if (py_command->set) { + PyErr_SetString(PyExc_RuntimeError, "command already set"); + return NULL; + } + + /* + * Various asserts. inlen = 2 * len; outlen = len; + */ + py_command->set = 1; + osd_command_set_cas(command, pid, oid, len, offset); + command->indata = PyMem_Malloc(len); + if (!command->indata) + return PyErr_NoMemory(); + memcpy((void *)(uintptr_t) command->outdata, outbuf, outlen); + command->outlen = outlen; + Py_IncRef(self); + return self; +} + +/* + * Input: one value, add; output: original value. + */ +static PyObject *pyosd_command_set_fa(PyObject *self, PyObject *args) +{ + struct pyosd_command *py_command = (struct pyosd_command *) self; + struct osd_command *command = &py_command->command; + uint64_t pid, oid, len = 8, offset = 0; + const char *inbuf, *outbuf; + int inlen, outlen; + + if (!PyArg_ParseTuple(args, "KKs#s#|KK:set_fa", &pid, &oid, + &inbuf, &inlen, &outbuf, &outlen, &len, &offset)) + return NULL; + if (py_command->set) { + PyErr_SetString(PyExc_RuntimeError, "command already set"); + return NULL; + } + + /* + * Various asserts. inlen = outlen = len; + */ + py_command->set = 1; + osd_command_set_cas(command, pid, oid, len, offset); + command->indata = PyMem_Malloc(len); + if (!command->indata) + return PyErr_NoMemory(); + memcpy((void *)(uintptr_t) command->outdata, outbuf, outlen); + command->outlen = outlen; + Py_IncRef(self); + return self; +} +/* + * OSDCommand instance methods, members, and type. + */ +struct PyMethodDef pyosd_command_methods[] = { + { "show_sense", pyosd_command_show_sense, METH_VARARGS, + "Generate a string of returned sense data." }, + { "set_test_unit_ready", pyosd_command_set_test_unit_ready, + METH_VARARGS, "Build the TUR command." }, + { "set_inquiry", pyosd_command_set_inquiry, METH_VARARGS, + "Build the INQURY command." }, + { "set_append", pyosd_command_set_append, METH_VARARGS, + "Build the APPEND command." }, + { "set_create", pyosd_command_set_create, METH_VARARGS, + "Build the CREATE command." }, + { "set_create_and_write", pyosd_command_set_create_and_write, + METH_VARARGS, "Build the CREATE_AND_WRITE command." }, + { "set_create_collection", pyosd_command_set_create_collection, + METH_VARARGS, "Build the CREATE_COLLECTION command." }, + { "set_create_partition", pyosd_command_set_create_partition, + METH_VARARGS, "Build the CREATE_PARTITION command." }, + { "set_flush", pyosd_command_set_flush, METH_VARARGS, + "Build the FLUSH command." }, + { "set_flush_collection", pyosd_command_set_flush_collection, + METH_VARARGS, "Build the FLUSH_COLLECTION command." }, + { "set_flush_osd", pyosd_command_set_flush_osd, METH_VARARGS, + "Build the FLUSH_OSD command." }, + { "set_flush_partition", pyosd_command_set_flush_partition, + METH_VARARGS, "Build the FLUSH_PARTITION command." }, + { "set_format_osd", pyosd_command_set_format_osd, METH_VARARGS, + "Build the FORMAT_OSD command." }, + { "set_get_attributes", pyosd_command_set_get_attributes, METH_VARARGS, + "Build the GET_ATTRIBUTES command." }, + { "set_get_member_attributes", pyosd_command_set_get_member_attributes, + METH_VARARGS, "Build the GET_MEMBER_ATTRIBUTES command." }, + { "set_list", pyosd_command_set_list, METH_VARARGS, + "Build the LIST command." }, + { "set_list_collection", pyosd_command_set_list_collection, + METH_VARARGS, "Build the LIST_COLLECTION command." }, + { "set_perform_scsi_command", pyosd_command_set_perform_scsi_command, + METH_VARARGS, "Build the PERFORM_SCSI_COMMAND command." }, + { "set_perform_task_mgmt_func", + pyosd_command_set_perform_task_mgmt_func, METH_VARARGS, + "Build the PERFORM_SCSI_TASK_MGMT_FUNC command." }, + { "set_query", pyosd_command_set_query, METH_VARARGS, + "Build the QUERY command." }, + { "set_read", pyosd_command_set_read, METH_VARARGS, + "Build the READ command." }, + { "set_remove", pyosd_command_set_remove, METH_VARARGS, + "Build the REMOVE command." }, + { "set_remove_collection", pyosd_command_set_remove_collection, + METH_VARARGS, "Build the REMOVE_COLLECTION command." }, + { "set_remove_member_objects", pyosd_command_set_remove_member_objects, + METH_VARARGS, "Build the REMOVE_MEMBER_OBJECTS command." }, + { "set_remove_partition", pyosd_command_set_remove_partition, + METH_VARARGS, "Build the REMOVE_PARTITION command." }, + { "set_set_attributes", pyosd_command_set_set_attributes, METH_VARARGS, + "Build the SET_ATTRIBUTES command." }, + { "set_set_key", pyosd_command_set_set_key, METH_VARARGS, + "Build the SET_KEY command." }, + { "set_set_master_key", pyosd_command_set_set_master_key, METH_VARARGS, + "Build the SET_MASTER_KEY command." }, + { "set_set_member_attributes", pyosd_command_set_set_member_attributes, + METH_VARARGS, "Build the SET_MEMBER_ATTRIBUTES command." }, + { "set_write", pyosd_command_set_write, METH_VARARGS, + "Build the WRITE command." }, + /* + * Local extensions. + */ + { "set_cas", pyosd_command_set_cas, METH_VARARGS, + "Build the CAS (compare and swap) command." }, + { "set_fa", pyosd_command_set_fa, METH_VARARGS, + "Build the FA (fetch and add) command." }, + + /* + * Attribute manipulation. + */ + { "attr_build", pyosd_command_attr_build, METH_VARARGS, + "Modify a command to get or set attributes." }, + { "attr_resolve", pyosd_command_attr_resolve, METH_VARARGS, + "After command execution, process the retrieved attributes.\n" + "You must call this even if you are only using ATTR_SET." }, + { NULL } +}; + +struct PyMemberDef pyosd_command_members[] = { + { "status", T_UBYTE, offsetof(struct pyosd_command, command.status), + READONLY, "command status after completion" }, + { "inlen", T_ULONG, offsetof(struct pyosd_command, command.inlen), + READONLY, "returned data length" }, + { NULL } +}; + +/* more complex members with their own functions */ +struct PyGetSetDef pyosd_command_getset[] = { + { "indata", pyosd_command_get_indata, NULL, "returned data", NULL }, + { "sense_key", pyosd_command_get_sense, NULL, "sense key", "key" }, + { "sense_code", pyosd_command_get_sense, NULL, "sense code", "code" }, +}; + +PyTypeObject pyosd_command_type = { + PyObject_HEAD_INIT(NULL) + .tp_name = "OSDCommand", + .tp_basicsize = sizeof(struct pyosd_command), + .tp_flags = Py_TPFLAGS_DEFAULT, + .tp_doc = "Python encapsulation of struct osd_command.", + .tp_init = pyosd_command_init, + .tp_new = PyType_GenericNew, + .tp_dealloc = pyosd_command_dealloc, + .tp_methods = pyosd_command_methods, + .tp_members = pyosd_command_members, + .tp_getset = pyosd_command_getset, +}; + diff --git a/osd-initiator/python/const.c b/osd-initiator/python/const.c new file mode 100644 index 0000000..0ef396d --- /dev/null +++ b/osd-initiator/python/const.c @@ -0,0 +1,172 @@ +/* + * Constants from #defines and enums. Try to keep this list up to date. + * + * Copyright (C) 2007-8 OSD Team + * + * 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 "osd-initiator/command.h" +#include "osd-util/osd-defs.h" +#include "osd-util/osd-sense.h" +#include "pyosd.h" + +/* + * Insert a strings into a dictionary. + */ +static int dict_insert(PyObject *d, const char *name, unsigned long val) +{ + PyObject *v; + + v = PyLong_FromUnsignedLong(val); + if (!v) + return 1; + + if (PyDict_SetItemString(d, name, v) < 0) + return 1; + + Py_DECREF(v); + return 0; +} + +#define add(s) \ + ret += dict_insert(d, #s, s) + +/* + * The object here is the main module dictionary. + */ +int add_consts(PyObject *d) +{ + int ret = 0; + + /* struct osd_command type */ + add(ATTR_GET); + add(ATTR_GET_PAGE); + add(ATTR_GET_MULTI); + add(ATTR_SET); + + /* object constants */ + add(ROOT_PID); + add(ROOT_OID); + add(PARTITION_PID_LB); + add(PARTITION_OID); + add(USEROBJECT_PID_LB); + add(COLLECTION_PID_LB); + add(USEROBJECT_OID_LB); + add(COLLECTION_OID_LB); + + /* attribute page ranges */ + add(USEROBJECT_PG); + add(PARTITION_PG); + add(COLLECTION_PG); + add(ROOT_PG); + add(RESERVED_PG); + add(ANY_PG); + add(CUR_CMD_ATTR_PG); + add(GETALLATTR_PG); + + /* attribute page sets further subdivide page ranges */ + add(LUN_PG_LB); + + /* attribute pages defined by osd spec */ + add(USER_DIR_PG); + add(USER_INFO_PG); + add(USER_QUOTA_PG); + add(USER_TMSTMP_PG); + add(USER_COLL_PG); + + /* contents of current command attribute page */ + add(CCAP_RICV); + add(CCAP_OBJT); + add(CCAP_PID); + add(CCAP_OID); + add(CCAP_APPADDR); + add(CCAP_RICV_LEN); + add(CCAP_OBJT_LEN); + add(CCAP_PID_LEN); + add(CCAP_OID_LEN); + add(CCAP_APPADDR_LEN); + add(CCAP_RICV_OFF); + add(CCAP_OBJT_OFF); + add(CCAP_PID_OFF); + add(CCAP_OID_OFF); + add(CCAP_APPADDR_OFF); + add(CCAP_TOTAL_LEN); + + /* userobject timestamp attribute page osd2r01 sec 7.1.2.18 */ + add(UTSAP_CTIME); + add(UTSAP_ATTR_ATIME); + add(UTSAP_ATTR_MTIME); + add(UTSAP_DATA_ATIME); + add(UTSAP_DATA_MTIME); + add(UTSAP_CTIME_LEN); + add(UTSAP_ATTR_ATIME_LEN); + add(UTSAP_ATTR_MTIME_LEN); + add(UTSAP_DATA_ATIME_LEN); + add(UTSAP_DATA_MTIME_LEN); + add(UTSAP_CTIME_OFF); + add(UTSAP_ATTR_ATIME_OFF); + add(UTSAP_ATTR_MTIME_OFF); + add(UTSAP_DATA_ATIME_OFF); + add(UTSAP_DATA_MTIME_OFF); + add(UTSAP_TOTAL_LEN); + + /* userobject information attribute page osd2r01 sec 7.1.2.11 */ + add(UIAP_PID); + add(UIAP_OID); + add(UIAP_USERNAME); + add(UIAP_USED_CAPACITY); + add(UIAP_LOGICAL_LEN); + add(UIAP_PID_LEN); + add(UIAP_OID_LEN); + add(UIAP_USED_CAPACITY_LEN); + add(UIAP_LOGICAL_LEN_LEN); + + /* selected sense keys and codes */ + add(OSD_SSK_NO_SENSE); + add(OSD_SSK_RECOVERED_ERROR); + add(OSD_SSK_NOT_READY); + add(OSD_SSK_MEDIUM_ERROR); + add(OSD_SSK_HARDWARE_ERROR); + add(OSD_SSK_ILLEGAL_REQUEST); + add(OSD_SSK_UNIT_ATTENTION); + add(OSD_SSK_DATA_PROTECTION); + add(OSD_SSK_BLANK_CHECK); + add(OSD_SSK_VENDOR_SPECIFIC); + add(OSD_SSK_COPY_ABORTED); + add(OSD_SSK_ABORTED_COMMAND); + add(OSD_SSK_OBSOLETE_SENSE_KEY); + add(OSD_SSK_VOLUME_OVERFLOW); + add(OSD_SSK_MISCOMPARE); + add(OSD_SSK_RESERVED_SENSE_KEY); + + add(OSD_ASC_INVALID_COMMAND_OPCODE); + add(OSD_ASC_INVALID_DATA_OUT_BUF_INTEGRITY_CHK_VAL); + add(OSD_ASC_INVALID_FIELD_IN_CDB); + add(OSD_ASC_INVALID_FIELD_IN_PARAM_LIST); + add(OSD_ASC_LOGICAL_UNIT_NOT_RDY_FRMT_IN_PRGRS); + add(OSD_ASC_NONCE_NOT_UNIQUE); + add(OSD_ASC_NONCE_TIMESTAMP_OUT_OF_RANGE); + add(OSD_ASC_POWER_ON_OCCURRED); + add(OSD_ASC_PARAMETER_LIST_LENGTH_ERROR); + add(OSD_ASC_PART_OR_COLL_CONTAINS_USER_OBJECTS); + add(OSD_ASC_READ_PAST_END_OF_USER_OBJECT); + add(OSD_ASC_RESERVATIONS_RELEASED); + add(OSD_ASC_QUOTA_ERROR); + add(OSD_ASC_SECURITY_AUDIT_VALUE_FROZEN); + add(OSD_ASC_SECURITY_WORKING_KEY_FROZEN); + add(OSD_ASC_SYSTEM_RESOURCE_FAILURE); + + return ret; +} + diff --git a/osd-initiator/python/device.c b/osd-initiator/python/device.c new file mode 100644 index 0000000..c3a467c --- /dev/null +++ b/osd-initiator/python/device.c @@ -0,0 +1,148 @@ +/* + * Device interface. + * + * Copyright (C) 2007 OSD Team + * + * 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 "osd-initiator/command.h" +#include "osd-initiator/device.h" +#include "osd-util/osd-util.h" +#include "pyosd.h" + +/* + * OSDDevice type. + */ +struct pyosd_device { + PyObject_HEAD; + int fd; +}; + +/* + * Open an fd. + */ +static PyObject *pyosd_device_open(PyObject *self, PyObject *args) +{ + struct pyosd_device *device = (struct pyosd_device *) self; + char *s; + int fd; + + if (!PyArg_ParseTuple(args, "s:open", &s)) + return NULL; + + fd = open(s, O_RDWR); + if (fd < 0) + return PyErr_SetFromErrnoWithFilename(PyExc_OSError, s); + + device->fd = fd; + + Py_RETURN_NONE; +} + +static int pyosd_device_init(PyObject *self, PyObject *args, + PyObject *keywords __attribute__((unused))) +{ + struct pyosd_device *py_device = (struct pyosd_device *) self; + const char *s = NULL; + + py_device->fd = -1; + + if (!PyArg_ParseTuple(args, "|s:init", &s)) + return -1; + if (s) + pyosd_device_open(self, args); + return 0; +} + +/* + * Close the fd. + */ +static PyObject *pyosd_device_close(PyObject *self, PyObject *args) +{ + struct pyosd_device *device = (struct pyosd_device *) self; + int ret; + + if (!PyArg_ParseTuple(args, ":close")) + return NULL; + + ret = close(device->fd); + if (ret < 0) + return PyErr_SetFromErrno(PyExc_OSError); + + Py_RETURN_NONE; +} + +static void pyosd_device_dealloc(PyObject *self) +{ + struct pyosd_device *py_device = (struct pyosd_device *) self; + + if (py_device->fd >= 0) + pyosd_device_close(self, Py_None); +} + +/* + * Submit the command to the fd. + */ +static PyObject *pyosd_device_submit_and_wait(PyObject *self, PyObject *args) +{ + struct pyosd_device *device = (struct pyosd_device *) self; + PyObject *o; + struct pyosd_command *py_command; + struct osd_command *command; + int ret; + + if (!PyArg_ParseTuple(args, "O:submit_and_wait", &o)) + return NULL; + + if (!PyObject_TypeCheck(o, &pyosd_command_type)) { + PyErr_SetString(PyExc_TypeError, "expecting a pyosd_command"); + return NULL; + } + + py_command = (struct pyosd_command *) o; + command = &py_command->command; + + ret = osd_submit_and_wait(device->fd, command); + py_command->complete = 1; + if (ret) + return PyErr_SetFromErrno(PyExc_OSError); + Py_RETURN_NONE; +} + +/* + * Instance methods and type. + */ +struct PyMethodDef pyosd_device_methods[] = { + { "open", pyosd_device_open, METH_VARARGS, + "Open a device." }, + { "close", pyosd_device_close, METH_VARARGS, + "Close the device." }, + { "submit_and_wait", pyosd_device_submit_and_wait, METH_VARARGS, + "Submit a command and wait for it to complete." }, + { NULL } +}; + +PyTypeObject pyosd_device_type = { + PyObject_HEAD_INIT(NULL) + .tp_name = "OSDDevice", + .tp_basicsize = sizeof(struct pyosd_device), + .tp_flags = Py_TPFLAGS_DEFAULT, + .tp_doc = "Python encapsulation of OSD device", + .tp_init = pyosd_device_init, + .tp_new = PyType_GenericNew, + .tp_dealloc = pyosd_device_dealloc, + .tp_methods = pyosd_device_methods, +}; + diff --git a/osd-initiator/python/drivelist.c b/osd-initiator/python/drivelist.c new file mode 100644 index 0000000..78b4537 --- /dev/null +++ b/osd-initiator/python/drivelist.c @@ -0,0 +1,107 @@ +/* + * Drivelist interface. + * + * Copyright (C) 2007 OSD Team + * + * 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 /* T_OBJECT */ +#include "osd-initiator/command.h" +#include "osd-initiator/drivelist.h" +#include "pyosd.h" + +/* + * OSDDrive type. + */ +struct pyosd_drive { + PyObject_HEAD; + PyObject *targetname; + PyObject *chardev; +}; + +/* + * OSDDriveList type. + */ +struct pyosd_drivelist { + PyListObject list; +}; + +/* + * Get the drive list. + */ +static int pyosd_drivelist_init(PyObject *self, PyObject *args, + PyObject *keywords) +{ + int ret, i; + struct osd_drive_description *drives; + int num_drives; + struct pyosd_drive *drive; + + ret = PyList_Type.tp_init(self, args, keywords); + if (ret) { + PyErr_SetString(PyExc_RuntimeError, "list init failed"); + return ret; + } + + ret = osd_get_drive_list(&drives, &num_drives); + if (ret) { + PyErr_SetString(PyExc_RuntimeError, "get_drive_list failed"); + return -1; + } + + for (i=0; itargetname = PyString_FromString(drives[i].targetname); + drive->chardev = PyString_FromString(drives[i].chardev); + Py_INCREF(drive); + PyList_Append(self, (PyObject *) drive); + } + + osd_free_drive_list(drives, num_drives); + return 0; +} + +/* + * OSDDrive and its two members but no methods. + */ +struct PyMemberDef pyosd_drive_members[] = { + { "targetname", T_OBJECT, offsetof(struct pyosd_drive, targetname), + READONLY, "target name" }, + { "chardev", T_OBJECT, offsetof(struct pyosd_drive, chardev), + READONLY, "character device" }, + { NULL } +}; + +PyTypeObject pyosd_drive_type = { + PyObject_HEAD_INIT(NULL) + .tp_name = "OSDDrive", + .tp_basicsize = sizeof(struct pyosd_drive), + .tp_flags = Py_TPFLAGS_DEFAULT, + .tp_doc = "Python encapsulation of OSD drive", + .tp_members = pyosd_drive_members, +}; + +/* + * OSDDriveList is just a list of OSDDrive. No methods or members. + */ +PyTypeObject pyosd_drivelist_type = { + PyObject_HEAD_INIT(NULL) + .tp_name = "OSDDriveList", + .tp_basicsize = sizeof(struct pyosd_drivelist), + .tp_flags = Py_TPFLAGS_DEFAULT, + .tp_doc = "Python encapsulation of OSD drive list", + .tp_init = pyosd_drivelist_init, + .tp_base = &PyList_Type, +}; + diff --git a/osd-initiator/python/eat-tur.py b/osd-initiator/python/eat-tur.py new file mode 100755 index 0000000..745a53a --- /dev/null +++ b/osd-initiator/python/eat-tur.py @@ -0,0 +1,61 @@ +#!/usr/bin/python +# +# Recent STGT generates a unit attenion for poweron/reset for each +# new initiator that connects. This gets in the way of PVFS, so eat +# it by sending a TUR. +# +# Copyright (C) 2008 Pete Wyckoff +# +# 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 . + +import sys +from pyosd import * + +def usage(): + print >>sys.stderr, "Usage:", sys.argv[0], "" + sys.exit(1) + +set_progname(sys.argv[0]) +if len(sys.argv) == 1: + usage() + +def tur(dev, devname): + # eat poweron attention with a tur + command = OSDCommand().set_test_unit_ready() + dev.submit_and_wait(command) + if command.status == 0: + print "Drive", devname, "okay" + return # no attention, command completed okay + if (command.status == 2 and \ + command.sense_key == OSD_SSK_UNIT_ATTENTION and \ + command.sense_code == OSD_ASC_POWER_ON_OCCURRED): + return + print >>sys.stderr, "Drive", devname, "command failed:", \ + command.show_sense() + + +drives = OSDDriveList() + +for devname in sys.argv[1:]: + dev = False + for d in drives: + if d.targetname == devname: + dev = OSDDevice(d.chardev) + break + if not dev: + print >>sys.stderr, sys.argv[0] + ": Drive", devname, \ + "not found." + continue + + tur(dev, devname) + diff --git a/osd-initiator/python/pvfs-init.py b/osd-initiator/python/pvfs-init.py new file mode 100755 index 0000000..787d49c --- /dev/null +++ b/osd-initiator/python/pvfs-init.py @@ -0,0 +1,136 @@ +#!/usr/bin/python +# +# Initialize OSDs for use in PVFS. Only call this from whatever startup +# script is used to build the fs.conf and start the OSDs. It has a messy +# command line, rather than trying to parse a fs.conf. +# +# Copyright (C) 2007-8 Pete Wyckoff +# +# 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 . + +import sys +from pyosd import * + +# magic constants from pvfs/src/client/sysint/osd.h + +# The partitions; one for datafiles, another for metafiles and dir objects +PVFS_OSD_DATA_PID = 0x10000 +PVFS_OSD_META_PID = 0x20000 +# Pages for object and directory attributes +PVFS_USEROBJECT_DIR_PG = 0x30000 +PVFS_USEROBJECT_ATTR_PG = 0x40000 +# Attribute location of the fs.conf text, a magic object in meta pid space +PVFS_OSD_FSCONF_OID = 0x10000 +# PVFS code for uid | gid | perm | ... +PVFS_ATTR_COMMON_ALL = 0x7f +# PVFS code for directory object type +PVFS_TYPE_DIRECTORY = 1 << 2 + +def hexdump(s, len): + buf = '' + for i in range((len+7)/8): + buf = buf + "%02x:" % (i*8) + for j in range(min(8, len - i*8)): + buf = buf + " %02x" % (ord(s[i*8+j])) + buf = buf + "\n" + return buf + +# submit, check status, print sense, or resolve attributes, returning them +def run(command): + dev.submit_and_wait(command) + if command.status != 0: + print "Command failed:", command.show_sense(), + assert 0 == 1 + return command.attr_resolve() + +def create_any_object(pid): + command = OSDCommand().set_create(pid) + command.attr_build(OSDAttr(ATTR_GET, CUR_CMD_ATTR_PG, CCAP_OID, \ + CCAP_OID_LEN)) + attr = run(command) + return ntohll(attr.val) + +def usage(): + print >>sys.stderr, "Usage:", sys.argv[0], \ + " ", \ + "[ ]" + sys.exit(1) + +set_progname(sys.argv[0]) + +root_handle = 0 +if len(sys.argv) == 4 or len(sys.argv) == 6: + devname = sys.argv[1]; + datalb = int(sys.argv[2]); + metalb = int(sys.argv[3]); + if len(sys.argv) == 6: + root_handle = int(sys.argv[4]) + fsconf = sys.argv[5] +else: + usage() + +drives = OSDDriveList() +dev = False +for d in drives: + if d.targetname == devname: + dev = OSDDevice(d.chardev) + break +if not dev: + print >>sys.stderr, "Drive", devname, "not found." + sys.exit(1) + +# eat poweron attention with a tur +command = OSDCommand().set_test_unit_ready() +dev.submit_and_wait(command) +if command.status != 0: + if not (command.status == 2 and \ + command.sense_key == OSD_SSK_UNIT_ATTENTION and \ + command.sense_code == OSD_ASC_POWER_ON_OCCURRED): + print "Command failed:", command.show_sense(), + assert 0 == 1 + +# format +run(OSDCommand().set_format_osd(1<<30)) + +# Set lower bounds in the data and meta spaces to fake allocation of +# handles in a specified range. Trust that the target will just add +# one to the highest allocated handle and return that, and do not +# allocate and free so many that it would wrap. +run(OSDCommand().set_create_partition(PVFS_OSD_DATA_PID)) +run(OSDCommand().set_create(PVFS_OSD_DATA_PID, datalb)) + +run(OSDCommand().set_create_partition(PVFS_OSD_META_PID)) +if metalb != root_handle: + run(OSDCommand().set_create(PVFS_OSD_META_PID, metalb)) + +# create root handle, the top level directory +if root_handle: + command = OSDCommand().set_create(PVFS_OSD_META_PID, root_handle) + command.attr_build([ \ + OSDAttr(ATTR_SET, PVFS_USEROBJECT_ATTR_PG, 0, 0), \ + OSDAttr(ATTR_SET, PVFS_USEROBJECT_ATTR_PG, 1, 0), \ + OSDAttr(ATTR_SET, PVFS_USEROBJECT_ATTR_PG, 2, 0777), \ + OSDAttr(ATTR_SET, PVFS_USEROBJECT_ATTR_PG, 3, \ + PVFS_ATTR_COMMON_ALL), \ + OSDAttr(ATTR_SET, PVFS_USEROBJECT_ATTR_PG, 4, \ + PVFS_TYPE_DIRECTORY), \ + ]) + run(command) + + # store fs.conf text in a magic object + fh = open(fsconf, "r") + buf = fh.read() + fh.close() + run(OSDCommand().set_create_and_write(PVFS_OSD_META_PID, + PVFS_OSD_FSCONF_OID, buf)) + diff --git a/osd-initiator/python/pvfs-osd-integrated-init.py b/osd-initiator/python/pvfs-osd-integrated-init.py new file mode 100755 index 0000000..6233548 --- /dev/null +++ b/osd-initiator/python/pvfs-osd-integrated-init.py @@ -0,0 +1,53 @@ +#!/usr/bin/python +# +# Initialize the OSD side of a combined PVFS + OSD integrated server. +# +# Copyright (C) 2008 Pete Wyckoff +# +# 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 . + +import sys +from pyosd import * + +# submit, check status, print sense, or resolve attributes, returning them +def run(command): + dev.submit_and_wait(command) + if command.status != 0: + print "Command failed:", command.show_sense(), + assert 0 == 1 + return command.attr_resolve() + +def usage(): + print >>sys.stderr, "Usage:", sys.argv[0], " " + sys.exit(1) + +if len(sys.argv) != 3: + usage() +set_progname(sys.argv[0]) +devname = sys.argv[1] +pid = int(sys.argv[2]) + +drives = OSDDriveList() +dev = False +for d in drives: + if d.targetname == devname: + dev = OSDDevice(d.chardev) + break +if not dev: + print >>sys.stderr, "Drive", devname, "not found." + sys.exit(1) + +# format and build the one partition +run(OSDCommand().set_format_osd(1<<30)) +run(OSDCommand().set_create_partition(pid)) + diff --git a/osd-initiator/python/pyosd.c b/osd-initiator/python/pyosd.c new file mode 100644 index 0000000..1b86d45 --- /dev/null +++ b/osd-initiator/python/pyosd.c @@ -0,0 +1,214 @@ +/* + * Main module. + * + * Copyright (C) 2007 OSD Team + * + * 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 "osd-initiator/command.h" +#include "osd-util/osd-util.h" +#include "pyosd.h" + +/* + * Initialize util.h error reporting. + */ +static PyObject *pyosd_set_progname(PyObject *self __attribute__((unused)), + PyObject *args) +{ + char *argv[1]; + + if (!PyArg_ParseTuple(args, "s:set_progname", &argv[0])) + return NULL; + + osd_set_progname(1, argv); + + Py_RETURN_NONE; +} + +/* + * Retrieve the progname. + */ +static PyObject *pyosd_get_progname(PyObject *self __attribute__((unused)), + PyObject *args) +{ + char *argv[1]; + + if (!PyArg_ParseTuple(args, ":get_progname", &argv[0])) + return NULL; + + return Py_BuildValue("s", osd_get_progname()); +} + +/* + * Byte swappers. + */ +static PyObject *pyosd_ntohs(PyObject *self __attribute__((unused)), + PyObject *args) +{ + const uint8_t *s; + int len; + + if (!PyArg_ParseTuple(args, "s#:ntohs", &s, &len)) + return NULL; + if (len != 2) { + char s[1024]; + sprintf(s, "string len must be 2, was %d", len); + PyErr_SetString(PyExc_RuntimeError, s); + return NULL; + } + return Py_BuildValue("H", get_ntohs(s)); +} + +static PyObject *pyosd_ntohl(PyObject *self __attribute__((unused)), + PyObject *args) +{ + const uint8_t *s; + int len; + + if (!PyArg_ParseTuple(args, "s#:ntohl", &s, &len)) + return NULL; + if (len != 4) { + char s[1024]; + sprintf(s, "string len must be 4, was %d", len); + PyErr_SetString(PyExc_RuntimeError, s); + return NULL; + } + return Py_BuildValue("I", get_ntohl(s)); +} + +static PyObject *pyosd_ntohll(PyObject *self __attribute__((unused)), + PyObject *args) +{ + const uint8_t *s; + int len; + + if (!PyArg_ParseTuple(args, "s#:ntohll", &s, &len)) + return NULL; + if (len != 8) { + char s[1024]; + sprintf(s, "string len must be 8, was %d", len); + PyErr_SetString(PyExc_RuntimeError, s); + return NULL; + } + return Py_BuildValue("K", get_ntohll(s)); +} + +static PyObject *pyosd_htonl(PyObject *self __attribute__((unused)), + PyObject *args) +{ + uint32_t w; + union { + uint8_t s[4]; + uint32_t x; + } u; + + if (!PyArg_ParseTuple(args, "I:htonl", &w)) + return NULL; + set_htonl(u.s, w); + return Py_BuildValue("I", u.x); +} + +static PyObject *pyosd_htonll(PyObject *self __attribute__((unused)), + PyObject *args) +{ + uint64_t w; + union { + uint8_t s[4]; + uint64_t x; + } u; + + if (!PyArg_ParseTuple(args, "K:htonll", &w)) + return NULL; + set_htonll(u.s, w); + return Py_BuildValue("K", u.x); +} + +/* + * Class (not instance) methods. + */ +static PyMethodDef methods[] = { + { "set_progname", pyosd_set_progname, METH_VARARGS, + "Set the program name for error reporting." }, + { "get_progname", pyosd_get_progname, METH_VARARGS, + "Get the program name." }, + { "ntohs", pyosd_ntohs, METH_VARARGS, + "Convert 16-bit word from network to host byte order." }, + { "ntohl", pyosd_ntohl, METH_VARARGS, + "Convert 32-bit word from network to host byte order." }, + { "ntohll", pyosd_ntohll, METH_VARARGS, + "Convert 64-bit word from network to host byte order." }, + { "htonl", pyosd_htonl, METH_VARARGS, + "Convert 32-bit word from host to network byte order." }, + { "htonll", pyosd_htonll, METH_VARARGS, + "Convert 64-bit word from host to network byte order." }, + { NULL } +}; + +/* + * Helper function to build the types. + */ +static int ready_type(PyObject *module, PyTypeObject *type) +{ + if (PyType_Ready(type) < 0) { + fprintf(stderr, "pyosd: PyType_Ready OSDCommand failed\n"); + return 1; + } + Py_INCREF(type); + PyModule_AddObject(module, type->tp_name, (PyObject *) type); + return 0; +} + +#if 0 +/* + * Tracer function, for debugging, not very useful. + */ +static int pyosd_trace(PyObject *self, struct _frame *frame, int what, + PyObject *arg) +{ + if (what == PyTrace_LINE) + printf("%s: line\n", __func__); + else if (what == PyTrace_RETURN) + printf("%s: return %p\n", __func__, arg); + else + printf("%s: what %d\n", __func__, what); + return 0; +} +#endif + +PyMODINIT_FUNC initpyosd(void) +{ + int ret = 0; + PyObject *m; + PyObject *d; + + m = Py_InitModule("pyosd", methods); + if (m == NULL) { + fprintf(stderr, "pyosd: Py_InitModule failed\n"); + return; + } + + d = PyModule_GetDict(m); + add_consts(d); + + ret += ready_type(m, &pyosd_command_type); + ret += ready_type(m, &pyosd_attr_type); + ret += ready_type(m, &pyosd_device_type); + ret += ready_type(m, &pyosd_drive_type); + ret += ready_type(m, &pyosd_drivelist_type); + if (ret) + fprintf(stderr, "pyosd: ready_type failed\n"); + + /* PyEval_SetTrace(pyosd_trace, NULL); */ +} + diff --git a/osd-initiator/python/pyosd.h b/osd-initiator/python/pyosd.h new file mode 100644 index 0000000..728e04c --- /dev/null +++ b/osd-initiator/python/pyosd.h @@ -0,0 +1,47 @@ +/* + * Declarations of objects found in all the source files. + * + * Copyright (C) 2007 OSD Team + * + * 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 . + */ +/* + * OSDAttr type. Wrapper around one attribute_list entry. + */ +struct pyosd_attr { + PyObject_HEAD; + struct attribute_list attr; +}; + +/* + * OSDCommand type. + */ +struct pyosd_command { + PyObject_HEAD; + struct osd_command command; + int set; + int complete; +}; + +extern PyTypeObject pyosd_command_type; +extern PyTypeObject pyosd_attr_type; +extern PyTypeObject pyosd_device_type; +extern PyTypeObject pyosd_drive_type; +extern PyTypeObject pyosd_drivelist_type; + +/* exporting this to python */ +PyMODINIT_FUNC initpyosd(void); + +/* helper function in const.c */ +int add_consts(PyObject *d); + diff --git a/osd-initiator/python/readfile.py b/osd-initiator/python/readfile.py new file mode 100755 index 0000000..7354620 --- /dev/null +++ b/osd-initiator/python/readfile.py @@ -0,0 +1,128 @@ +#!/usr/bin/python +# +# Given a pid,oid from the command line, read the file. Optionally specify +# a target name, else it chooses the first (or only) one that is connected. +# +# Copyright (C) 2008 Pete Wyckoff +# +# 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 . + +import sys +import random +import optparse +from pyosd import * + +# submit, check status, print sense, or resolve attributes, returning them +def run(command): + dev.submit_and_wait(command) + if command.status != 0: + print "Command failed:", command.show_sense(), + assert 0 == 1 + return command.attr_resolve() + +def compare(buf1, buf2): + for i in range(len(buf1)): + if buf1[i] != buf2[i]: + print >>sys.stderr, \ + "Bufs differ at offset %d: %02x vs %02x" \ + % (i, ord(buf1[i]), ord(buf2[i])) + sys.exit(1) + +def readfile(pid, oid): + length = 256 << 10 # 256 kB chunk size + pos = 0 + while True: + command = OSDCommand() + command.set_read(pid, oid, length, pos) + dev.submit_and_wait(command) + # read past end of object, okay + if command.status == 2 and command.sense_key == 1 \ + and command.sense_code == 0x3b17: + sys.stdout.write(command.indata) + break + if command.status != 0: + print "Command failed:", command.show_sense(), + assert 0 == 1 + sys.stdout.write(command.indata) + pos += command.inlen + +def writefile(pid, oid): + length = 256 << 10 # 1 kB chunk size + pos = 0 + while True: + buf = sys.stdin.read(length) + if buf == "": + break + command = OSDCommand() + command.set_write(pid, oid, buf, pos) + run(command) + pos += len(buf) + # truncate to exactly this length, could have been longer + command = OSDCommand() + command.set_set_attributes(pid, oid) + command.attr_build(OSDAttr(ATTR_SET, USER_INFO_PG, UIAP_LOGICAL_LEN, \ + htonll(pos))) + run(command) + +parser = optparse.OptionParser() +parser.add_option("-t", "--target", dest="target", + help="use target with serial name TARGET", metavar="TARGET") +parser.add_option("-p", "--pid", dest="pid", help="pid of file") +parser.add_option("-o", "--oid", dest="oid", help="oid of file") +(options,args) = parser.parse_args() +if len(args) != 0: + parser.error("unknown extra arguments") + sys.exit(1) + +if not options.pid: + parser.error("pid argument is required") + sys.exit(1) + +if not options.oid: + parser.error("oid argument is required") + sys.exit(1) + +pid = long(options.pid) +oid = long(options.oid) + +set_progname(sys.argv[0]) + +random.seed() +drives = OSDDriveList() +if len(drives) < 1: + print >>sys.stderr, "No drives." + sys.exit(1) + +if options.target: + for d in drives: + if d.targetname == options.target: + chardev = d.chardev + break + if not chardev: + print >>sys.stderr, "Drive", options.target, "not found." + sys.exit(1) +else: + chardev = drives[0].chardev + if len(drives) > 1: + priint >>sys.stderr, "More than one drive, choosing first." + +dev = OSDDevice(chardev) +if get_progname() == "readfile.py": + readfile(pid, oid) +elif get_progname() == "writefile.py": + writefile(pid, oid) +else: + print >>sys.stderr, \ + "Expecting to be called as readfile.py or writefile.py." + sys.exit(1) + diff --git a/osd-initiator/python/target-cdb.py b/osd-initiator/python/target-cdb.py new file mode 100755 index 0000000..1a73f58 --- /dev/null +++ b/osd-initiator/python/target-cdb.py @@ -0,0 +1,120 @@ +#!/usr/bin/python +# +# osd-target/tests/cdb-test rewritten in python interface +# +# Copyright (C) 2007 OSD Team +# +# 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 . + +import sys +from pyosd import * + +def hexdump(s, len): + buf = '' + for i in range((len+7)/8): + buf = buf + "%02x:" % (i*8) + for j in range(min(8, len - i*8)): + buf = buf + " %02x" % (ord(s[i*8+j])) + buf = buf + "\n" + return buf + +# submit, check status, print sense, or resolve attributes, returning them +def run(command): + dev.submit_and_wait(command) + if command.status != 0: + print "Command failed:", command.show_sense(), + assert 0 == 1 + return command.attr_resolve() + +def ccap_verify(ccap, len, pid, oid): + assert len == CCAP_TOTAL_LEN + assert ntohl(ccap[0:4]) == CUR_CMD_ATTR_PG + assert ntohl(ccap[4:8]) == len - 8 + assert ntohll(ccap[32:40]) == pid + assert ntohll(ccap[40:48]) == oid + 5 - 1 + assert ntohll(ccap[48:56]) == 0 + +def test_partition(): + # create partition + empty getpage_setlist + run(OSDCommand().set_create_partition(PARTITION_PID_LB)) + + # remove partition + empty getpage_setlist + run(OSDCommand().set_remove_partition(PARTITION_PID_LB)) + + # create partition + empty getlist_setlist + command = OSDCommand().set_create_partition(PARTITION_PID_LB) + command.attr_build(OSDAttr(ATTR_GET_PAGE, 0, 0)) + run(command) + + # remove partition + empty getpage_setlist + command = OSDCommand().set_remove_partition(PARTITION_PID_LB) + command.attr_build(OSDAttr(ATTR_GET_PAGE, 0, 0)) + run(command) + +def test_create(): + # create partition + empty getpage_setlist + run(OSDCommand().set_create_partition(PARTITION_PID_LB)) + + # create 1 object + run(OSDCommand().set_create(USEROBJECT_PID_LB, USEROBJECT_OID_LB)) + + # remove the object + run(OSDCommand().set_remove(USEROBJECT_PID_LB, USEROBJECT_OID_LB)) + + # create 5 objects & get ccap + command = OSDCommand().set_create(USEROBJECT_PID_LB, 0, 5) + command.attr_build(OSDAttr(ATTR_GET_PAGE, CUR_CMD_ATTR_PG,\ + CCAP_TOTAL_LEN)) + attr = run(command) + ccap_verify(attr.val, attr.outlen, USEROBJECT_PID_LB, USEROBJECT_OID_LB) + + # remove 5 objects + for i in range(5): + run(OSDCommand().set_remove(USEROBJECT_PID_LB, \ + USEROBJECT_OID_LB + i)) + + # create 5 objects, set 2 attr on each + command = OSDCommand().set_create(USEROBJECT_PID_LB, 0, 5) + command.attr_build([OSDAttr(ATTR_SET, USEROBJECT_PG + LUN_PG_LB, \ + 111, "Madhuri Dixit Rocks!!"), \ + OSDAttr(ATTR_SET, USEROBJECT_PG + LUN_PG_LB + 1, \ + 321, "A cigarrete a day etc.")]) + run(command) + + # remove 5 objects and get previously set attributes for each + attr = [ OSDAttr(ATTR_GET, USEROBJECT_PG + LUN_PG_LB + 1, 321, 100), \ + OSDAttr(ATTR_GET, USEROBJECT_PG + LUN_PG_LB, 111, 100) ] + for i in range(5): + command = OSDCommand() + command.set_remove(USEROBJECT_PID_LB, USEROBJECT_OID_LB + i) + attr = run(command.attr_build(attr)) + assert attr[0].val == "A cigarrete a day etc." + assert attr[1].val == "Madhuri Dixit Rocks!!" + + # remove partition + run(OSDCommand().set_remove_partition(USEROBJECT_PID_LB)) + + +set_progname(sys.argv[0]) +drives = OSDDriveList() +if len(drives) < 1: + print >>sys.stderr, "No drives." + sys.exit(1) + +dev = OSDDevice(drives[0].chardev) + +run(OSDCommand().set_format_osd(1<<30)) + +test_partition() +test_create() + diff --git a/osd-initiator/python/target-osd.py b/osd-initiator/python/target-osd.py new file mode 100755 index 0000000..6b4dfa1 --- /dev/null +++ b/osd-initiator/python/target-osd.py @@ -0,0 +1,232 @@ +#!/usr/bin/python +# +# osd-target/tests/osd-test rewritten in python interface +# +# Copyright (C) 2007 OSD Team +# +# 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 . + +import sys +from pyosd import * + +def hexdump(s, len): + buf = '' + for i in range((len+7)/8): + buf = buf + "%02x:" % (i*8) + for j in range(min(8, len - i*8)): + buf = buf + " %02x" % (ord(s[i*8+j])) + buf = buf + "\n" + return buf + +# submit, check status, print sense, or resolve attributes, returning them +def run(command): + dev.submit_and_wait(command) + if command.status != 0: + # ignore short read status + if not(command.status == 2 and command.sense_key == 1 \ + and command.sense_code == 0x3b17): + print "Command failed:", command.show_sense(), + assert 0 == 1 + return command.attr_resolve() + +def runfail(command): + dev.submit_and_wait(command) + if command.status == 0: + print "Command was supposed to fail." + assert 0 == 1 + command.attr_resolve() + +def ccap_verify(ccap, len, pid, oid): + assert len == CCAP_TOTAL_LEN + assert ntohl(ccap[0:4]) == CUR_CMD_ATTR_PG + assert ntohl(ccap[4:8]) == len - 8 + assert ntohll(ccap[32:40]) == pid + assert ntohll(ccap[40:48]) == oid + 5 - 1 + assert ntohll(ccap[48:56]) == 0 + +def test_osd_format(): + run(OSDCommand().set_format_osd(1<<30)) + +def test_osd_create(): + # invalid pid/oid, test must fail + runfail(OSDCommand().set_create(0, 1)) + + # invalid oid test must fail + runfail(OSDCommand().set_create(USEROBJECT_PID_LB, 1)) + + # num > 1 cannot request oid, test must fail + runfail(OSDCommand().set_create(USEROBJECT_PID_LB, USEROBJECT_OID_LB, \ + 2)) + + run(OSDCommand().set_create_partition(PARTITION_PID_LB)) + + run(OSDCommand().set_create(USEROBJECT_PID_LB, USEROBJECT_OID_LB)) + + run(OSDCommand().set_remove(USEROBJECT_PID_LB, USEROBJECT_OID_LB)) + + # remove non-existing object, test must fail + runfail(OSDCommand().set_remove(USEROBJECT_PID_LB, USEROBJECT_OID_LB)) + + run(OSDCommand().set_remove_partition(PARTITION_PID_LB)) + +def test_osd_set_attributes(): + run(OSDCommand().set_create_partition(PARTITION_PID_LB)) + run(OSDCommand().set_create(USEROBJECT_PID_LB, USEROBJECT_OID_LB)) + + # setting root attr must fail (need to use list format here, as + # 0 in the set for page format means do nothing) + oneattr = OSDAttr(ATTR_SET, 0, 0, "") + command = OSDCommand().set_set_attributes(ROOT_PID, ROOT_OID) + runfail(command.attr_build(oneattr)) + + # unsettable page modification must fail + command = OSDCommand().set_set_attributes(PARTITION_PID_LB, \ + PARTITION_OID) + runfail(command.attr_build(oneattr)) + + # unsettable collection page must fail + command = OSDCommand().set_set_attributes(COLLECTION_PID_LB, \ + COLLECTION_OID_LB) + runfail(command.attr_build(oneattr)) + + # unsettable userobject page must fail + command = OSDCommand().set_set_attributes(USEROBJECT_PID_LB, \ + USEROBJECT_OID_LB) + runfail(command.attr_build(oneattr)) + + # info attr > 40 bytes, test must fail + command = OSDCommand().set_set_attributes(USEROBJECT_PID_LB, \ + USEROBJECT_OID_LB) + attr = OSDAttr(ATTR_SET, USEROBJECT_PG + LUN_PG_LB, 0, \ + "This is test, long test more than forty bytes") + runfail(command.attr_build(attr)) + + # this test is normal setattr, must succeed + command = OSDCommand().set_set_attributes(USEROBJECT_PID_LB, \ + USEROBJECT_OID_LB) + attr = OSDAttr(ATTR_SET, USEROBJECT_PG + LUN_PG_LB + 1, 1, \ + "Madhuri Dixit") + run(command.attr_build(attr)) + + run(OSDCommand().set_remove(USEROBJECT_PID_LB, USEROBJECT_OID_LB)) + run(OSDCommand().set_remove_partition(PARTITION_PID_LB)) + +def test_osd_read_write(): + run(OSDCommand().set_create_partition(PARTITION_PID_LB)) + run(OSDCommand().set_create(USEROBJECT_PID_LB, USEROBJECT_OID_LB)) + + s = "Hello World! Get life\n" + run(OSDCommand().set_write(USEROBJECT_PID_LB, USEROBJECT_OID_LB, s)) + + command = OSDCommand().set_read(USEROBJECT_PID_LB, USEROBJECT_OID_LB, \ + 100) + run(command) + assert command.indata == s + + run(OSDCommand().set_remove(USEROBJECT_PID_LB, USEROBJECT_OID_LB)) + run(OSDCommand().set_remove_partition(PARTITION_PID_LB)) + +def test_osd_create_partition(): + run(OSDCommand().set_create_partition(0)) + + # create dup pid must fail + run(OSDCommand().set_create_partition(PARTITION_PID_LB + 1)) + runfail(OSDCommand().set_create_partition(PARTITION_PID_LB + 1)) + + run(OSDCommand().set_remove_partition(PARTITION_PID_LB)) + run(OSDCommand().set_remove_partition(PARTITION_PID_LB + 1)) + # XXX: this works (missing pid), but should not + run(OSDCommand().set_remove_partition(PARTITION_PID_LB + 2)) + +def test_osd_get_ccap(pid, oid): + command = OSDCommand().set_get_attributes(pid, oid) + command.attr_build(OSDAttr(ATTR_GET_PAGE, CUR_CMD_ATTR_PG, 1024)) + attr = run(command) + assert attr.outlen == CCAP_TOTAL_LEN + assert ntohl(attr.val[0:4]) == CUR_CMD_ATTR_PG + assert ntohl(attr.val[4:8]) == CCAP_TOTAL_LEN - 8 + assert ntohll(attr.val[32:40]) == pid + assert ntohll(attr.val[40:48]) == oid + assert ntohll(attr.val[48:56]) == 0 + +def ntoh_time(buf): + return (ntohs(buf[0:2]) << 32) | ntohl(buf[2:6]) + +def test_osd_get_utsap(): + s = "Hello World! Get life blah blah blah\n" + run(OSDCommand().set_write(USEROBJECT_PID_LB, USEROBJECT_OID_LB, s)) + command = OSDCommand().set_read(USEROBJECT_PID_LB, USEROBJECT_OID_LB, \ + 100) + run(command) + assert command.indata == s + + command = OSDCommand().set_set_attributes(USEROBJECT_PID_LB, \ + USEROBJECT_OID_LB) + command.attr_build(OSDAttr(ATTR_SET, USEROBJECT_PG + LUN_PG_LB, 2, s)) + run(command) + + command = OSDCommand().set_get_attributes(USEROBJECT_PID_LB, \ + USEROBJECT_OID_LB) + command.attr_build(OSDAttr(ATTR_GET_PAGE, USER_TMSTMP_PG, 100)) + attr = run(command) + + assert ntohl(attr.val[0:4]) == USER_TMSTMP_PG + assert ntohl(attr.val[4:8]) == UTSAP_TOTAL_LEN - 8 + + # XXX: these should be different, but they are not. + atime = ntoh_time(attr.val[26:32]) + mtime = ntoh_time(attr.val[32:38]) + assert atime != 0 and mtime != 0 and mtime <= atime + + atime = ntoh_time(attr.val[14:20]) + mtime = ntoh_time(attr.val[20:26]) + assert atime != 0 and mtime != 0 and mtime == atime + +def test_osd_get_attributes(): + run(OSDCommand().set_create_partition(PARTITION_PID_LB)) + run(OSDCommand().set_create(USEROBJECT_PID_LB, USEROBJECT_OID_LB)) + + s = "Madhuri Dixit" + command = OSDCommand().set_set_attributes(USEROBJECT_PID_LB, \ + USEROBJECT_OID_LB) + command.attr_build(OSDAttr(ATTR_SET, USEROBJECT_PG + LUN_PG_LB, 1, s)) + run(command) + + command = OSDCommand().set_get_attributes(USEROBJECT_PID_LB, \ + USEROBJECT_OID_LB) + command.attr_build(OSDAttr(ATTR_GET, USEROBJECT_PG + LUN_PG_LB, 1, 100)) + attr = run(command) + assert attr.val == s + + # run different get attr tests + test_osd_get_ccap(USEROBJECT_PID_LB, USEROBJECT_OID_LB) + test_osd_get_utsap() + + run(OSDCommand().set_remove(USEROBJECT_PID_LB, USEROBJECT_OID_LB)) + run(OSDCommand().set_remove_partition(PARTITION_PID_LB)) + +set_progname(sys.argv[0]) +drives = OSDDriveList() +if len(drives) < 1: + print >>sys.stderr, "No drives." + sys.exit(1) + +dev = OSDDevice(drives[0].chardev) + +test_osd_format() +test_osd_create() +test_osd_set_attributes() +test_osd_read_write() +test_osd_create_partition() +test_osd_get_attributes() + diff --git a/osd-initiator/python/test-bidi.py b/osd-initiator/python/test-bidi.py new file mode 100755 index 0000000..6a4ea28 --- /dev/null +++ b/osd-initiator/python/test-bidi.py @@ -0,0 +1,208 @@ +#!/usr/bin/python +# +# Do things that force bidirectional commands. +# +# Copyright (C) 2007 OSD Team +# +# 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 . + +import sys +import random +from pyosd import * + +def hexdump(s, len): + buf = '' + for i in range((len+7)/8): + buf = buf + "%02x:" % (i*8) + for j in range(min(8, len - i*8)): + buf = buf + " %02x" % (ord(s[i*8+j])) + buf = buf + "\n" + return buf + +# submit, check status, print sense, or resolve attributes, returning them +def run(command): + dev.submit_and_wait(command) + if command.status != 0: + print "Command failed:", command.show_sense(), + assert 0 == 1 + return command.attr_resolve() + +def build_buf(size): + off = random.randint(0, 255) + buf = '' + for i in range(size): + buf += chr(off) + off += 1 + if off >= 256: + off = 0 + return buf + +def compare(buf1, buf2): + #print "buf1" + #print hexdump(buf1, len(buf1)) + #print "buf2" + #print hexdump(buf2, len(buf2)) + for i in range(len(buf1)): + if buf1[i] != buf2[i]: + print >>sys.stderr, \ + "Bufs differ at offset %d: expected %02x, got %02x" \ + % (i, ord(buf1[i]), ord(buf2[i])) + sys.exit(1) + +def ccap_verify(ccap, len, pid, oid): + #print "ccap" + #print hexdump(ccap, len) + assert len == CCAP_TOTAL_LEN + assert ntohl(ccap[0:4]) == CUR_CMD_ATTR_PG + assert ntohl(ccap[4:8]) == len - 8 + assert ntohll(ccap[32:40]) == pid + assert ntohll(ccap[40:48]) == oid + assert ntohll(ccap[48:56]) == 0 + +def test_one_bidi(pid, oid, page, number, buf, size, attrbuf, attrsize): + print "Write" + command = OSDCommand() + command.set_write(pid, oid, buf) + run(command) + + print "Read" + command = OSDCommand() + command.set_read(pid, oid, size) + run(command) + compare(buf, command.indata) + + print "Setattr (one)" + command = OSDCommand() + command.set_set_attributes(pid, oid) + command.attr_build(OSDAttr(ATTR_SET, page, number, attrbuf)) + run(command) + + print "Setattr (list)" + command = OSDCommand() + command.set_set_attributes(pid, oid) + command.attr_build([ \ + OSDAttr(ATTR_SET, page, number, attrbuf), \ + OSDAttr(ATTR_SET, page, number + 1, attrbuf)]) + run(command) + + print "Getattr (page)" + command = OSDCommand() + command.set_get_attributes(pid, oid) + command.attr_build(OSDAttr(ATTR_GET_PAGE, CUR_CMD_ATTR_PG, \ + CCAP_TOTAL_LEN)) + attr = run(command) + ccap_verify(attr.val, attr.outlen, pid, oid) + + print "Getattr (list)" + command = OSDCommand() + command.set_get_attributes(pid, oid) + command.attr_build(OSDAttr(ATTR_GET, page, number, attrsize)) + attr = run(command) + compare(attrbuf, attr.val) + + print "Write and getattr (page)" + command = OSDCommand() + command.set_write(pid, oid, buf) + command.attr_build(OSDAttr(ATTR_GET_PAGE, CUR_CMD_ATTR_PG, \ + CCAP_TOTAL_LEN)) + attr = run(command) + ccap_verify(attr.val, attr.outlen, pid, oid) + + print "Write and getattr (list)" + command = OSDCommand() + command.set_write(pid, oid, buf) + command.attr_build(OSDAttr(ATTR_GET, page, number, attrsize)) + attr = run(command) + compare(attrbuf, attr.val) + + print "Write and setattr (one)" + command = OSDCommand() + command.set_write(pid, oid, buf) + command.attr_build(OSDAttr(ATTR_SET, page, number, attrbuf)) + run(command) + + print "Write and setattr (list)" + command = OSDCommand() + command.set_write(pid, oid, buf) + command.attr_build([ \ + OSDAttr(ATTR_SET, page, number, attrbuf), \ + OSDAttr(ATTR_SET, page, number + 1, attrbuf)]) + run(command) + + print "Read and getattr (page)" + command = OSDCommand() + command.set_read(pid, oid, size) + command.attr_build(OSDAttr(ATTR_GET_PAGE, CUR_CMD_ATTR_PG, \ + CCAP_TOTAL_LEN)) + attr = run(command) + compare(buf, command.indata) + ccap_verify(attr.val, attr.outlen, pid, oid) + + print "Read and getattr (list)" + command = OSDCommand() + command.set_read(pid, oid, size) + command.attr_build(OSDAttr(ATTR_GET, page, number, attrsize)) + attr = run(command) + compare(buf, command.indata) + compare(attrbuf, attr.val) + + print "Read and setattr (one)" + command = OSDCommand() + command.set_read(pid, oid, size) + command.attr_build(OSDAttr(ATTR_SET, page, number, attrbuf)) + run(command) + compare(buf, command.indata) + + print "Read and setattr (list)" + command = OSDCommand() + command.set_read(pid, oid, size) + command.attr_build([ \ + OSDAttr(ATTR_SET, page, number, attrbuf), \ + OSDAttr(ATTR_SET, page, number + 1, attrbuf)]) + run(command) + compare(buf, command.indata) + + +def test_bidi(pid, oid): + page = USEROBJECT_PG + LUN_PG_LB + number = 27 + + #for size in [ 2, 4096, 8192, 65534, 262144 ]: + for size in [ 2 ]: + buf = build_buf(size) + #for attrsize in [ 2, 4096, 8192, 65534 ]: + for attrsize in [ 65534 ]: + attrbuf = build_buf(attrsize) + + print "Buf size", size, "attrbuf size", attrsize + test_one_bidi(pid, oid, page, number, buf, size, \ + attrbuf, attrsize) + +set_progname(sys.argv[0]) +random.seed() +drives = OSDDriveList() +if len(drives) < 1: + print >>sys.stderr, "No drives." + sys.exit(1) + +dev = OSDDevice(drives[0].chardev) + +pid = PARTITION_PID_LB +oid = USEROBJECT_OID_LB + +run(OSDCommand().set_format_osd(1<<30)) +run(OSDCommand().set_create_partition(oid)) +run(OSDCommand().set_create(oid)) + +test_bidi(pid, oid) + diff --git a/osd-initiator/python/test-collection.py b/osd-initiator/python/test-collection.py new file mode 100755 index 0000000..5febfa0 --- /dev/null +++ b/osd-initiator/python/test-collection.py @@ -0,0 +1,197 @@ +#!/usr/bin/python +# +# Test collections. +# +# Copyright (C) 2007 OSD Team +# +# 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 . + +import sys +from pyosd import * + +def hexdump(s, len): + buf = '' + for i in range((len+7)/8): + buf = buf + "%02x:" % (i*8) + for j in range(min(8, len - i*8)): + buf = buf + " %02x" % (ord(s[i*8+j])) + buf = buf + "\n" + return buf + +# submit, check status, print sense, or resolve attributes, returning them +def run(command): + dev.submit_and_wait(command) + if command.status != 0: + print "Command failed:", command.show_sense(), + assert 0 == 1 + return command.attr_resolve() + +def runfail(command): + dev.submit_and_wait(command) + if command.status == 0: + print "Command was supposed to fail." + assert 0 == 1 + command.attr_resolve() + +def create_any_collection(pid): + command = OSDCommand().set_create_collection(pid) + command.attr_build(OSDAttr(ATTR_GET, CUR_CMD_ATTR_PG, CCAP_OID, \ + CCAP_OID_LEN)) + attr = run(command) + return ntohll(attr.val) + +def create_any_object(pid): + command = OSDCommand().set_create(pid) + command.attr_build(OSDAttr(ATTR_GET, CUR_CMD_ATTR_PG, CCAP_OID, \ + CCAP_OID_LEN)) + attr = run(command) + return ntohll(attr.val) + +set_progname(sys.argv[0]) +drives = OSDDriveList() +if len(drives) < 1: + print >>sys.stderr, "No drives." + sys.exit(1) + +dev = OSDDevice(drives[0].chardev) + +run(OSDCommand().set_format_osd(1<<30)) +pid = PARTITION_PID_LB +run(OSDCommand().set_create_partition(pid)) + +# basic create/remove +cid = COLLECTION_OID_LB +run(OSDCommand().set_create_collection(pid, cid)) +run(OSDCommand().set_remove_collection(pid, cid)) + +# with an object that gets in the way +oid = USEROBJECT_OID_LB +run(OSDCommand().set_create(pid, oid)) +runfail(OSDCommand().set_create_collection(pid, oid)) +run(OSDCommand().set_remove(pid, oid)) + +# with a collection that gets in the way +cid = COLLECTION_OID_LB +run(OSDCommand().set_create_collection(pid, cid)) +runfail(OSDCommand().set_create_collection(pid, cid)) +run(OSDCommand().set_remove_collection(pid, cid)) + +# create, target chooses +cid = create_any_collection(pid) +run(OSDCommand().set_remove_collection(pid, cid)) + +# put objects into and out of collections +cid1 = create_any_collection(pid) +cid2 = create_any_collection(pid) +cid3 = create_any_collection(pid) +oid = create_any_object(pid) +command = OSDCommand().set_set_attributes(pid, oid) +command.attr_build(OSDAttr(ATTR_SET, USER_COLL_PG, 1, htonll(cid1))) +run(command) +command = OSDCommand().set_set_attributes(pid, oid) +command.attr_build(OSDAttr(ATTR_SET, USER_COLL_PG, 2, htonll(cid2))) +run(command) +# replace existing one +command = OSDCommand().set_set_attributes(pid, oid) +command.attr_build(OSDAttr(ATTR_SET, USER_COLL_PG, 2, htonll(cid3))) +run(command) +# can't remove if something is in it +runfail(OSDCommand().set_remove_collection(pid, cid3)) +# delete one +command = OSDCommand().set_set_attributes(pid, oid) +command.attr_build(OSDAttr(ATTR_SET, USER_COLL_PG, 2, None)) +run(command) +# set same collection twice +command = OSDCommand().set_set_attributes(pid, oid) +command.attr_build(OSDAttr(ATTR_SET, USER_COLL_PG, 3, htonll(cid1))) +runfail(command) +# remove this one +command = OSDCommand().set_set_attributes(pid, oid) +command.attr_build(OSDAttr(ATTR_SET, USER_COLL_PG, 1, None)) +run(command) +run(OSDCommand().set_remove_collection(pid, cid1)) +run(OSDCommand().set_remove_collection(pid, cid2)) +run(OSDCommand().set_remove_collection(pid, cid3)) +run(OSDCommand().set_remove(pid, oid)) + +# remove a collection with something in it +cid = create_any_collection(pid) +oid = create_any_object(pid) +command = OSDCommand().set_set_attributes(pid, oid) +command.attr_build(OSDAttr(ATTR_SET, USER_COLL_PG, cid, htonll(cid))) +run(command) +runfail(OSDCommand().set_remove_collection(pid, cid)) +run(OSDCommand().set_remove(pid, oid)) +run(OSDCommand().set_remove_collection(pid, cid)) + +# remove a collection with something in it, using force flag +cid = create_any_collection(pid) +oid = create_any_object(pid) +command = OSDCommand().set_set_attributes(pid, oid) +command.attr_build(OSDAttr(ATTR_SET, USER_COLL_PG, cid, htonll(cid))) +run(command) +run(OSDCommand().set_remove_collection(pid, cid, 1)) +run(OSDCommand().set_remove(pid, oid)) +runfail(OSDCommand().set_remove_collection(pid, cid)) + +# get member attributes, currently unimplemented in target +cid = create_any_collection(pid) +oid1 = create_any_object(pid) +oid2 = create_any_object(pid) +s1 = "hiya" +s2 = "himon" +run(OSDCommand().set_write(pid, oid1, s1)) +run(OSDCommand().set_write(pid, oid2, s2)) +command = OSDCommand().set_set_attributes(pid, oid1) +command.attr_build(OSDAttr(ATTR_SET, USER_COLL_PG, cid, htonll(cid))) +run(command) +command = OSDCommand().set_set_attributes(pid, oid2) +command.attr_build(OSDAttr(ATTR_SET, USER_COLL_PG, cid, htonll(cid))) +run(command) +command = OSDCommand().set_get_member_attributes(pid, cid) +command.attr_build(OSDAttr(ATTR_GET, USER_INFO_PG, UIAP_LOGICAL_LEN, 8)) +attr = run(command) +assert attr[0].val == len(s1) +assert attr[1].val == len(s2) +run(OSDCommand().set_remove(pid, oid1)) +run(OSDCommand().set_remove(pid, oid2)) +run(OSDCommand().set_remove_collection(pid, cid)) + +# set member attributes +cid = create_any_collection(pid) +oid1 = create_any_object(pid) +oid2 = create_any_object(pid) +command = OSDCommand().set_attributes(pid, oid1) +command.attr_build(OSDAttr(ATTR_SET, USER_COLL_PG, cid, htonll(cid))) +run(command) +command = OSDCommand().set_attributes(pid, oid2) +command.attr_build(OSDAttr(ATTR_SET, USER_COLL_PG, cid, htonll(cid))) +run(command) +s = "hello" +command = OSDCommand().get_member_attributes(pid, cid) +command.attr_build(OSDAttr(ATTR_SET, LUN_PG_LB+5, 26, s)) +attr = run(command) +command = OSDCommand().get_attributes(pid, oid1) +command.attr_build(OSDAttr(ATTR_GET, LUN_PG_LB+5, 26, 100)) +attr = run(command) +assert attr.val == s +command = OSDCommand().get_attributes(pid, oid2) +command.attr_build(OSDAttr(ATTR_GET, LUN_PG_LB+5, 26, 100)) +attr = run(command) +assert attr.val == s +run(OSDCommand().set_remove(pid, oid1)) +run(OSDCommand().set_remove(pid, oid2)) +run(OSDCommand().set_remove_collection(pid, cid)) + +# list collection + diff --git a/osd-initiator/python/test-rw.py b/osd-initiator/python/test-rw.py new file mode 100755 index 0000000..5ef71fe --- /dev/null +++ b/osd-initiator/python/test-rw.py @@ -0,0 +1,90 @@ +#!/usr/bin/python +# +# Make sure data gets read and written correctly, for sizes big enough +# to force RDMA in the iSER case. +# +# Copyright (C) 2007 OSD Team +# +# 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 . + +import sys +import random +from pyosd import * + +def hexdump(s, len): + buf = '' + for i in range((len+7)/8): + buf = buf + "%02x:" % (i*8) + for j in range(min(8, len - i*8)): + buf = buf + " %02x" % (ord(s[i*8+j])) + buf = buf + "\n" + return buf + +# submit, check status, print sense, or resolve attributes, returning them +def run(command): + dev.submit_and_wait(command) + if command.status != 0: + print "Command failed:", command.show_sense(), + assert 0 == 1 + return command.attr_resolve() + +def build_buf(size): + off = random.randint(0, 255) + buf = '' + for i in range(size): + buf += chr(off) + off += 1 + if off >= 256: + off = 0 + return buf + +def compare(buf1, buf2): + for i in range(len(buf1)): + if buf1[i] != buf2[i]: + print >>sys.stderr, \ + "Bufs differ at offset %d: %02x vs %02x" \ + % (i, ord(buf1[i]), ord(buf2[i])) + sys.exit(1) + +def test_rw(pid, oid): + for size in map(lambda x: 1 << x, range(1, 19)) \ + + [ (1<<18) + (1<<17) ]: + print "Test size", size + buf = build_buf(size) + command = OSDCommand() + command.set_write(pid, oid, buf) + run(command) + + command = OSDCommand() + command.set_read(pid, oid, size) + run(command) + compare(buf, command.indata) + +set_progname(sys.argv[0]) +random.seed() +drives = OSDDriveList() +if len(drives) < 1: + print >>sys.stderr, "No drives." + sys.exit(1) + +dev = OSDDevice(drives[0].chardev) + +pid = PARTITION_PID_LB +oid = USEROBJECT_OID_LB + +run(OSDCommand().set_format_osd(1<<30)) +run(OSDCommand().set_create_partition(oid)) +run(OSDCommand().set_create(oid)) + +test_rw(pid, oid) + diff --git a/osd-initiator/python/test.py b/osd-initiator/python/test.py new file mode 100755 index 0000000..43809be --- /dev/null +++ b/osd-initiator/python/test.py @@ -0,0 +1,190 @@ +#!/usr/bin/python +# +# test program for python interface +# +# Copyright (C) 2007 OSD Team +# +# 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 . + +import sys +import pyosd + +def hexdump(s, len): + buf = '' + for i in range((len+7)/8): + buf = buf + "%02x:" % (i*8) + for j in range(min(8, len - i*8)): + buf = buf + " %02x" % (ord(s[i*8+j])) + buf = buf + "\n" + return buf + +# for error reporting +pyosd.set_progname(sys.argv[0]) + +# find and show all the drives +drives = pyosd.OSDDriveList() +if len(drives) < 1: + print >>sys.stderr, "No drives." + sys.exit(1) + +print "Available drives:" +for i in range(len(drives)): + print " drive", i, "name", drives[i].targetname, \ + "device", drives[i].chardev + +# open the first device +dev = pyosd.OSDDevice() +dev.open(drives[0].chardev) + +# inquiry +print "inquiry" +command = pyosd.OSDCommand() +command.set_inquiry() +dev.submit_and_wait(command) +if command.status: + print "status", command.status + print command.show_sense(), +else: + print "inquiry indata (%d):" % command.inlen + if command.inlen: + print hexdump(command.indata, command.inlen), + +# format +print "format" +command = pyosd.OSDCommand() +command.set_format_osd(1 << 30) +dev.submit_and_wait(command) +if command.status: + print "status", command.status + print command.show_sense(), + +pid = 0x10000 + +# create paritition +print "create partition" +command = pyosd.OSDCommand() +command.set_create_partition(pid) +dev.submit_and_wait(command) +if command.status: + print "status", command.status + print command.show_sense(), + +oid = 0x10010 + +# create particular oid +print "create particular object" +command = pyosd.OSDCommand() +command.set_create(pid, oid) +dev.submit_and_wait(command) +if command.status: + print "status", command.status + print command.show_sense(), + +ccap_page = 0xfffffffe +ccap_number_oid = 4 + +uiap_page = 0x1 +uiap_number_len = 0x82 + +# create any oid, ask for result +print "create any object" +command = pyosd.OSDCommand() +command.set_create(pid) +attr = pyosd.OSDAttr(pyosd.ATTR_GET, ccap_page, ccap_number_oid, 8) +command.attr_build(attr) +dev.submit_and_wait(command) +if command.status: + print "status", command.status + print command.show_sense(), +else: + attr = command.attr_resolve() + oid = pyosd.ntohll(attr.val) + print "created oid", oid + +# write it +print "write that object" +command = pyosd.OSDCommand() +command.set_write(pid, oid, "Some data from python.") +dev.submit_and_wait(command) +if command.status: + print "status", command.status + print command.show_sense(), + +# two attrs +print "two getattr" +command = pyosd.OSDCommand() +command.set_get_attributes(pid, oid) +attr = [ pyosd.OSDAttr(pyosd.ATTR_GET, ccap_page, ccap_number_oid, 8), \ + pyosd.OSDAttr(pyosd.ATTR_GET, uiap_page, uiap_number_len, 8) ] +command.attr_build(attr) +dev.submit_and_wait(command) +if command.status: + print "status", command.status + print command.show_sense(), +else: + attr = command.attr_resolve() + verify_oid = pyosd.ntohll(attr[0].val) + len = pyosd.ntohll(attr[1].val) + print "should be same oid:", oid, "has len", len + +# read it +command = pyosd.OSDCommand() +command.set_read(pid, oid, 1024) +dev.submit_and_wait(command) +good = 1 +if command.status: + good = 0 + if command.status == 2 and command.sense_key == 1 \ + and command.sense_code == 0x3b17: + print "expected this sense:", command.show_sense(), + good = 1 + else: + print "status", command.status + print command.show_sense(), +if good: + print "read:", command.indata + +# two set attrs +print "two setattr" +command = pyosd.OSDCommand() +command.set_set_attributes(pid, oid) +attr = [ pyosd.OSDAttr(pyosd.ATTR_SET, 0x10000, 12, "testattr1"), \ + pyosd.OSDAttr(pyosd.ATTR_SET, 0x10201, 18, "testattr2") ] +command.attr_build(attr) +dev.submit_and_wait(command) +if command.status: + print "status", command.status + print command.show_sense(), +else: + command.attr_resolve() + print "attrs set" + +# two set attrs +print "read back two setattr (and one undefined)" +command = pyosd.OSDCommand() +command.set_get_attributes(pid, oid) +attr = [ pyosd.OSDAttr(pyosd.ATTR_GET, 0x10201, 18, 40), \ + pyosd.OSDAttr(pyosd.ATTR_GET, 0x10000, 12, 40), \ + pyosd.OSDAttr(pyosd.ATTR_GET, 0x10999, 14, 40) ] +command.attr_build(attr) +dev.submit_and_wait(command) +if command.status: + print "status", command.status + print command.show_sense(), +else: + attr = command.attr_resolve() + print "got attrs", attr[0].val, "and", attr[1].val, "and", \ + attr[2].val + +dev.close() + diff --git a/osd-initiator/sense.c b/osd-initiator/sense.c new file mode 100644 index 0000000..fec4a3e --- /dev/null +++ b/osd-initiator/sense.c @@ -0,0 +1,1011 @@ +/* + * ASCII values for a number of symbolic constants, printing functions, + * etc. + * Additions for SCSI 2 and Linux 2.2.x by D. Gilbert (990422) + * Additions for SCSI 3+ (SPC-3 T10/1416-D Rev 07 3 May 2002) + * by D. Gilbert and aeb (20020609) + * Additions for SPC-3 T10/1416-D Rev 21 22 Sept 2004, D. Gilbert 20041025 + * Update to SPC-4 T10/1713-D Rev 5a, 14 June 2006, D. Gilbert 20060702 + * + * Copied from linux-2.6.20 and edited for userspace use. Printing is + * done into a string rather than with printk. New parts are... + * + * Copyright (C) 2007 OSD Team + * + * 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 "osd-util/osd-util.h" +#include "sense.h" + +#if 0 +static const char *scsi_status_string(unsigned char scsi_status) +{ + const char *ccp; + + switch (scsi_status) { + case 0: ccp = "Good"; break; + case 0x2: ccp = "Check Condition"; break; + case 0x4: ccp = "Condition Met"; break; + case 0x8: ccp = "Busy"; break; + case 0x10: ccp = "Intermediate"; break; + case 0x14: ccp = "Intermediate-Condition Met"; break; + case 0x18: ccp = "Reservation Conflict"; break; + case 0x22: ccp = "Command Terminated"; break; /* obsolete */ + case 0x28: ccp = "Task set Full"; break; /* was: Queue Full */ + case 0x30: ccp = "ACA Active"; break; + case 0x40: ccp = "Task Aborted"; break; + default: ccp = "Unknown status"; + } + return ccp; +} +#endif + +/* description of the sense key values */ +static const char *const snstext[] = { + "No Sense", /* 0: There is no sense information */ + "Recovered Error", /* 1: The last command completed successfully + but used error correction */ + "Not Ready", /* 2: The addressed target is not ready */ + "Medium Error", /* 3: Data error detected on the medium */ + "Hardware Error", /* 4: Controller or device failure */ + "Illegal Request", /* 5: Error in request */ + "Unit Attention", /* 6: Removable medium was changed, or + the target has been reset, or ... */ + "Data Protect", /* 7: Access to the data is blocked */ + "Blank Check", /* 8: Reached unexpected written or unwritten + region of the medium */ + "Vendor Specific(9)", + "Copy Aborted", /* A: COPY or COMPARE was aborted */ + "Aborted Command", /* B: The target aborted the command */ + "Equal", /* C: A SEARCH DATA command found data equal */ + "Volume Overflow", /* D: Medium full with still data to be written */ + "Miscompare", /* E: Source data and data on the medium + do not agree */ +}; + +static const char *sense_key_string(unsigned char key) +{ + if (key <= 0xE) + return snstext[key]; + return NULL; +} + +struct error_info { + unsigned short code12; /* 0x0302 looks better than 0x03,0x02 */ + const char *text; +}; + +static struct error_info additional[] = +{ + {0x0000, "No additional sense information"}, + {0x0001, "Filemark detected"}, + {0x0002, "End-of-partition/medium detected"}, + {0x0003, "Setmark detected"}, + {0x0004, "Beginning-of-partition/medium detected"}, + {0x0005, "End-of-data detected"}, + {0x0006, "I/O process terminated"}, + {0x0011, "Audio play operation in progress"}, + {0x0012, "Audio play operation paused"}, + {0x0013, "Audio play operation successfully completed"}, + {0x0014, "Audio play operation stopped due to error"}, + {0x0015, "No current audio status to return"}, + {0x0016, "Operation in progress"}, + {0x0017, "Cleaning requested"}, + {0x0018, "Erase operation in progress"}, + {0x0019, "Locate operation in progress"}, + {0x001A, "Rewind operation in progress"}, + {0x001B, "Set capacity operation in progress"}, + {0x001C, "Verify operation in progress"}, + {0x001D, "ATA pass through information available"}, + + {0x0100, "No index/sector signal"}, + + {0x0200, "No seek complete"}, + + {0x0300, "Peripheral device write fault"}, + {0x0301, "No write current"}, + {0x0302, "Excessive write errors"}, + + {0x0400, "Logical unit not ready, cause not reportable"}, + {0x0401, "Logical unit is in process of becoming ready"}, + {0x0402, "Logical unit not ready, initializing command required"}, + {0x0403, "Logical unit not ready, manual intervention required"}, + {0x0404, "Logical unit not ready, format in progress"}, + {0x0405, "Logical unit not ready, rebuild in progress"}, + {0x0406, "Logical unit not ready, recalculation in progress"}, + {0x0407, "Logical unit not ready, operation in progress"}, + {0x0408, "Logical unit not ready, long write in progress"}, + {0x0409, "Logical unit not ready, self-test in progress"}, + {0x040A, "Logical unit not accessible, asymmetric access state " + "transition"}, + {0x040B, "Logical unit not accessible, target port in standby state"}, + {0x040C, "Logical unit not accessible, target port in unavailable " + "state"}, + {0x0410, "Logical unit not ready, auxiliary memory not accessible"}, + {0x0411, "Logical unit not ready, notify (enable spinup) required"}, + {0x0412, "Logical unit not ready, offline"}, + + {0x0500, "Logical unit does not respond to selection"}, + + {0x0600, "No reference position found"}, + + {0x0700, "Multiple peripheral devices selected"}, + + {0x0800, "Logical unit communication failure"}, + {0x0801, "Logical unit communication time-out"}, + {0x0802, "Logical unit communication parity error"}, + {0x0803, "Logical unit communication CRC error (Ultra-DMA/32)"}, + {0x0804, "Unreachable copy target"}, + + {0x0900, "Track following error"}, + {0x0901, "Tracking servo failure"}, + {0x0902, "Focus servo failure"}, + {0x0903, "Spindle servo failure"}, + {0x0904, "Head select fault"}, + + {0x0A00, "Error log overflow"}, + + {0x0B00, "Warning"}, + {0x0B01, "Warning - specified temperature exceeded"}, + {0x0B02, "Warning - enclosure degraded"}, + {0x0B03, "Warning - background self-test failed"}, + {0x0B04, "Warning - background pre-scan detected medium error"}, + {0x0B05, "Warning - background medium scan detected medium error"}, + + {0x0C00, "Write error"}, + {0x0C01, "Write error - recovered with auto reallocation"}, + {0x0C02, "Write error - auto reallocation failed"}, + {0x0C03, "Write error - recommend reassignment"}, + {0x0C04, "Compression check miscompare error"}, + {0x0C05, "Data expansion occurred during compression"}, + {0x0C06, "Block not compressible"}, + {0x0C07, "Write error - recovery needed"}, + {0x0C08, "Write error - recovery failed"}, + {0x0C09, "Write error - loss of streaming"}, + {0x0C0A, "Write error - padding blocks added"}, + {0x0C0B, "Auxiliary memory write error"}, + {0x0C0C, "Write error - unexpected unsolicited data"}, + {0x0C0D, "Write error - not enough unsolicited data"}, + {0x0C0F, "Defects in error window"}, + + {0x0D00, "Error detected by third party temporary initiator"}, + {0x0D01, "Third party device failure"}, + {0x0D02, "Copy target device not reachable"}, + {0x0D03, "Incorrect copy target device type"}, + {0x0D04, "Copy target device data underrun"}, + {0x0D05, "Copy target device data overrun"}, + + {0x0E00, "Invalid information unit"}, + {0x0E01, "Information unit too short"}, + {0x0E02, "Information unit too long"}, + {0x0E03, "Invalid field in command information unit"}, + + {0x1000, "Id CRC or ECC error"}, + {0x1001, "Logical block guard check failed"}, + {0x1002, "Logical block application tag check failed"}, + {0x1003, "Logical block reference tag check failed"}, + + {0x1100, "Unrecovered read error"}, + {0x1101, "Read retries exhausted"}, + {0x1102, "Error too long to correct"}, + {0x1103, "Multiple read errors"}, + {0x1104, "Unrecovered read error - auto reallocate failed"}, + {0x1105, "L-EC uncorrectable error"}, + {0x1106, "CIRC unrecovered error"}, + {0x1107, "Data re-synchronization error"}, + {0x1108, "Incomplete block read"}, + {0x1109, "No gap found"}, + {0x110A, "Miscorrected error"}, + {0x110B, "Unrecovered read error - recommend reassignment"}, + {0x110C, "Unrecovered read error - recommend rewrite the data"}, + {0x110D, "De-compression CRC error"}, + {0x110E, "Cannot decompress using declared algorithm"}, + {0x110F, "Error reading UPC/EAN number"}, + {0x1110, "Error reading ISRC number"}, + {0x1111, "Read error - loss of streaming"}, + {0x1112, "Auxiliary memory read error"}, + {0x1113, "Read error - failed retransmission request"}, + {0x1114, "Read error - lba marked bad by application client"}, + + {0x1200, "Address mark not found for id field"}, + + {0x1300, "Address mark not found for data field"}, + + {0x1400, "Recorded entity not found"}, + {0x1401, "Record not found"}, + {0x1402, "Filemark or setmark not found"}, + {0x1403, "End-of-data not found"}, + {0x1404, "Block sequence error"}, + {0x1405, "Record not found - recommend reassignment"}, + {0x1406, "Record not found - data auto-reallocated"}, + {0x1407, "Locate operation failure"}, + + {0x1500, "Random positioning error"}, + {0x1501, "Mechanical positioning error"}, + {0x1502, "Positioning error detected by read of medium"}, + + {0x1600, "Data synchronization mark error"}, + {0x1601, "Data sync error - data rewritten"}, + {0x1602, "Data sync error - recommend rewrite"}, + {0x1603, "Data sync error - data auto-reallocated"}, + {0x1604, "Data sync error - recommend reassignment"}, + + {0x1700, "Recovered data with no error correction applied"}, + {0x1701, "Recovered data with retries"}, + {0x1702, "Recovered data with positive head offset"}, + {0x1703, "Recovered data with negative head offset"}, + {0x1704, "Recovered data with retries and/or circ applied"}, + {0x1705, "Recovered data using previous sector id"}, + {0x1706, "Recovered data without ECC - data auto-reallocated"}, + {0x1707, "Recovered data without ECC - recommend reassignment"}, + {0x1708, "Recovered data without ECC - recommend rewrite"}, + {0x1709, "Recovered data without ECC - data rewritten"}, + + {0x1800, "Recovered data with error correction applied"}, + {0x1801, "Recovered data with error corr. & retries applied"}, + {0x1802, "Recovered data - data auto-reallocated"}, + {0x1803, "Recovered data with CIRC"}, + {0x1804, "Recovered data with L-EC"}, + {0x1805, "Recovered data - recommend reassignment"}, + {0x1806, "Recovered data - recommend rewrite"}, + {0x1807, "Recovered data with ECC - data rewritten"}, + {0x1808, "Recovered data with linking"}, + + {0x1900, "Defect list error"}, + {0x1901, "Defect list not available"}, + {0x1902, "Defect list error in primary list"}, + {0x1903, "Defect list error in grown list"}, + + {0x1A00, "Parameter list length error"}, + + {0x1B00, "Synchronous data transfer error"}, + + {0x1C00, "Defect list not found"}, + {0x1C01, "Primary defect list not found"}, + {0x1C02, "Grown defect list not found"}, + + {0x1D00, "Miscompare during verify operation"}, + + {0x1E00, "Recovered id with ECC correction"}, + + {0x1F00, "Partial defect list transfer"}, + + {0x2000, "Invalid command operation code"}, + {0x2001, "Access denied - initiator pending-enrolled"}, + {0x2002, "Access denied - no access rights"}, + {0x2003, "Access denied - invalid mgmt id key"}, + {0x2004, "Illegal command while in write capable state"}, + {0x2005, "Obsolete"}, + {0x2006, "Illegal command while in explicit address mode"}, + {0x2007, "Illegal command while in implicit address mode"}, + {0x2008, "Access denied - enrollment conflict"}, + {0x2009, "Access denied - invalid LU identifier"}, + {0x200A, "Access denied - invalid proxy token"}, + {0x200B, "Access denied - ACL LUN conflict"}, + + {0x2100, "Logical block address out of range"}, + {0x2101, "Invalid element address"}, + {0x2102, "Invalid address for write"}, + {0x2103, "Invalid write crossing layer jump"}, + + {0x2200, "Illegal function (use 20 00, 24 00, or 26 00)"}, + + {0x2400, "Invalid field in cdb"}, + {0x2401, "CDB decryption error"}, + {0x2402, "Obsolete"}, + {0x2403, "Obsolete"}, + {0x2404, "Security audit value frozen"}, + {0x2405, "Security working key frozen"}, + {0x2406, "Nonce not unique"}, + {0x2407, "Nonce timestamp out of range"}, + + {0x2500, "Logical unit not supported"}, + + {0x2600, "Invalid field in parameter list"}, + {0x2601, "Parameter not supported"}, + {0x2602, "Parameter value invalid"}, + {0x2603, "Threshold parameters not supported"}, + {0x2604, "Invalid release of persistent reservation"}, + {0x2605, "Data decryption error"}, + {0x2606, "Too many target descriptors"}, + {0x2607, "Unsupported target descriptor type code"}, + {0x2608, "Too many segment descriptors"}, + {0x2609, "Unsupported segment descriptor type code"}, + {0x260A, "Unexpected inexact segment"}, + {0x260B, "Inline data length exceeded"}, + {0x260C, "Invalid operation for copy source or destination"}, + {0x260D, "Copy segment granularity violation"}, + {0x260E, "Invalid parameter while port is enabled"}, + {0x260F, "Invalid data-out buffer integrity check value"}, + {0x2610, "Data decryption key fail limit reached"}, + {0x2611, "Incomplete key-associated data set"}, + {0x2612, "Vendor specific key reference not found"}, + + {0x2700, "Write protected"}, + {0x2701, "Hardware write protected"}, + {0x2702, "Logical unit software write protected"}, + {0x2703, "Associated write protect"}, + {0x2704, "Persistent write protect"}, + {0x2705, "Permanent write protect"}, + {0x2706, "Conditional write protect"}, + + {0x2800, "Not ready to ready change, medium may have changed"}, + {0x2801, "Import or export element accessed"}, + {0x2802, "Format-layer may have changed"}, + + {0x2900, "Power on, reset, or bus device reset occurred"}, + {0x2901, "Power on occurred"}, + {0x2902, "Scsi bus reset occurred"}, + {0x2903, "Bus device reset function occurred"}, + {0x2904, "Device internal reset"}, + {0x2905, "Transceiver mode changed to single-ended"}, + {0x2906, "Transceiver mode changed to lvd"}, + {0x2907, "I_T nexus loss occurred"}, + + {0x2A00, "Parameters changed"}, + {0x2A01, "Mode parameters changed"}, + {0x2A02, "Log parameters changed"}, + {0x2A03, "Reservations preempted"}, + {0x2A04, "Reservations released"}, + {0x2A05, "Registrations preempted"}, + {0x2A06, "Asymmetric access state changed"}, + {0x2A07, "Implicit asymmetric access state transition failed"}, + {0x2A08, "Priority changed"}, + {0x2A09, "Capacity data has changed"}, + {0x2A10, "Timestamp changed"}, + {0x2A11, "Data encryption parameters changed by another i_t nexus"}, + {0x2A12, "Data encryption parameters changed by vendor specific " + "event"}, + {0x2A13, "Data encryption key instance counter has changed"}, + + {0x2B00, "Copy cannot execute since host cannot disconnect"}, + + {0x2C00, "Command sequence error"}, + {0x2C01, "Too many windows specified"}, + {0x2C02, "Invalid combination of windows specified"}, + {0x2C03, "Current program area is not empty"}, + {0x2C04, "Current program area is empty"}, + {0x2C05, "Illegal power condition request"}, + {0x2C06, "Persistent prevent conflict"}, + {0x2C07, "Previous busy status"}, + {0x2C08, "Previous task set full status"}, + {0x2C09, "Previous reservation conflict status"}, + {0x2C0A, "Partition or collection contains user objects"}, + {0x2C0B, "Not reserved"}, + + {0x2D00, "Overwrite error on update in place"}, + + {0x2E00, "Insufficient time for operation"}, + + {0x2F00, "Commands cleared by another initiator"}, + {0x2F01, "Commands cleared by power loss notification"}, + + {0x3000, "Incompatible medium installed"}, + {0x3001, "Cannot read medium - unknown format"}, + {0x3002, "Cannot read medium - incompatible format"}, + {0x3003, "Cleaning cartridge installed"}, + {0x3004, "Cannot write medium - unknown format"}, + {0x3005, "Cannot write medium - incompatible format"}, + {0x3006, "Cannot format medium - incompatible medium"}, + {0x3007, "Cleaning failure"}, + {0x3008, "Cannot write - application code mismatch"}, + {0x3009, "Current session not fixated for append"}, + {0x300A, "Cleaning request rejected"}, + {0x300C, "WORM medium - overwrite attempted"}, + {0x300D, "WORM medium - integrity check"}, + {0x3010, "Medium not formatted"}, + + {0x3100, "Medium format corrupted"}, + {0x3101, "Format command failed"}, + {0x3102, "Zoned formatting failed due to spare linking"}, + + {0x3200, "No defect spare location available"}, + {0x3201, "Defect list update failure"}, + + {0x3300, "Tape length error"}, + + {0x3400, "Enclosure failure"}, + + {0x3500, "Enclosure services failure"}, + {0x3501, "Unsupported enclosure function"}, + {0x3502, "Enclosure services unavailable"}, + {0x3503, "Enclosure services transfer failure"}, + {0x3504, "Enclosure services transfer refused"}, + {0x3505, "Enclosure services checksum error"}, + + {0x3600, "Ribbon, ink, or toner failure"}, + + {0x3700, "Rounded parameter"}, + + {0x3800, "Event status notification"}, + {0x3802, "Esn - power management class event"}, + {0x3804, "Esn - media class event"}, + {0x3806, "Esn - device busy class event"}, + + {0x3900, "Saving parameters not supported"}, + + {0x3A00, "Medium not present"}, + {0x3A01, "Medium not present - tray closed"}, + {0x3A02, "Medium not present - tray open"}, + {0x3A03, "Medium not present - loadable"}, + {0x3A04, "Medium not present - medium auxiliary memory accessible"}, + + {0x3B00, "Sequential positioning error"}, + {0x3B01, "Tape position error at beginning-of-medium"}, + {0x3B02, "Tape position error at end-of-medium"}, + {0x3B03, "Tape or electronic vertical forms unit not ready"}, + {0x3B04, "Slew failure"}, + {0x3B05, "Paper jam"}, + {0x3B06, "Failed to sense top-of-form"}, + {0x3B07, "Failed to sense bottom-of-form"}, + {0x3B08, "Reposition error"}, + {0x3B09, "Read past end of medium"}, + {0x3B0A, "Read past beginning of medium"}, + {0x3B0B, "Position past end of medium"}, + {0x3B0C, "Position past beginning of medium"}, + {0x3B0D, "Medium destination element full"}, + {0x3B0E, "Medium source element empty"}, + {0x3B0F, "End of medium reached"}, + {0x3B11, "Medium magazine not accessible"}, + {0x3B12, "Medium magazine removed"}, + {0x3B13, "Medium magazine inserted"}, + {0x3B14, "Medium magazine locked"}, + {0x3B15, "Medium magazine unlocked"}, + {0x3B16, "Mechanical positioning or changer error"}, + {0x3B17, "Read past end of user object"}, + + {0x3D00, "Invalid bits in identify message"}, + + {0x3E00, "Logical unit has not self-configured yet"}, + {0x3E01, "Logical unit failure"}, + {0x3E02, "Timeout on logical unit"}, + {0x3E03, "Logical unit failed self-test"}, + {0x3E04, "Logical unit unable to update self-test log"}, + + {0x3F00, "Target operating conditions have changed"}, + {0x3F01, "Microcode has been changed"}, + {0x3F02, "Changed operating definition"}, + {0x3F03, "Inquiry data has changed"}, + {0x3F04, "Component device attached"}, + {0x3F05, "Device identifier changed"}, + {0x3F06, "Redundancy group created or modified"}, + {0x3F07, "Redundancy group deleted"}, + {0x3F08, "Spare created or modified"}, + {0x3F09, "Spare deleted"}, + {0x3F0A, "Volume set created or modified"}, + {0x3F0B, "Volume set deleted"}, + {0x3F0C, "Volume set deassigned"}, + {0x3F0D, "Volume set reassigned"}, + {0x3F0E, "Reported luns data has changed"}, + {0x3F0F, "Echo buffer overwritten"}, + {0x3F10, "Medium loadable"}, + {0x3F11, "Medium auxiliary memory accessible"}, + {0x3F12, "iSCSI IP address added"}, + {0x3F13, "iSCSI IP address removed"}, + {0x3F14, "iSCSI IP address changed"}, +/* + * {0x40NN, "Ram failure"}, + * {0x40NN, "Diagnostic failure on component nn"}, + * {0x41NN, "Data path failure"}, + * {0x42NN, "Power-on or self-test failure"}, + */ + {0x4300, "Message error"}, + + {0x4400, "Internal target failure"}, + {0x4471, "ATA device failed set features"}, + + {0x4500, "Select or reselect failure"}, + + {0x4600, "Unsuccessful soft reset"}, + + {0x4700, "Scsi parity error"}, + {0x4701, "Data phase CRC error detected"}, + {0x4702, "Scsi parity error detected during st data phase"}, + {0x4703, "Information unit iuCRC error detected"}, + {0x4704, "Asynchronous information protection error detected"}, + {0x4705, "Protocol service CRC error"}, + {0x4706, "Phy test function in progress"}, + {0x477f, "Some commands cleared by iSCSI Protocol event"}, + + {0x4800, "Initiator detected error message received"}, + + {0x4900, "Invalid message error"}, + + {0x4A00, "Command phase error"}, + + {0x4B00, "Data phase error"}, + {0x4B01, "Invalid target port transfer tag received"}, + {0x4B02, "Too much write data"}, + {0x4B03, "Ack/nak timeout"}, + {0x4B04, "Nak received"}, + {0x4B05, "Data offset error"}, + {0x4B06, "Initiator response timeout"}, + + {0x4C00, "Logical unit failed self-configuration"}, +/* + * {0x4DNN, "Tagged overlapped commands (nn = queue tag)"}, + */ + {0x4E00, "Overlapped commands attempted"}, + + {0x5000, "Write append error"}, + {0x5001, "Write append position error"}, + {0x5002, "Position error related to timing"}, + + {0x5100, "Erase failure"}, + {0x5101, "Erase failure - incomplete erase operation detected"}, + + {0x5200, "Cartridge fault"}, + + {0x5300, "Media load or eject failed"}, + {0x5301, "Unload tape failure"}, + {0x5302, "Medium removal prevented"}, + {0x5303, "Medium removal prevented by data transfer element"}, + {0x5304, "Medium thread or unthread failure"}, + + {0x5400, "Scsi to host system interface failure"}, + + {0x5500, "System resource failure"}, + {0x5501, "System buffer full"}, + {0x5502, "Insufficient reservation resources"}, + {0x5503, "Insufficient resources"}, + {0x5504, "Insufficient registration resources"}, + {0x5505, "Insufficient access control resources"}, + {0x5506, "Auxiliary memory out of space"}, + {0x5507, "Quota error"}, + {0x5508, "Maximum number of supplemental decryption keys exceeded"}, + + {0x5700, "Unable to recover table-of-contents"}, + + {0x5800, "Generation does not exist"}, + + {0x5900, "Updated block read"}, + + {0x5A00, "Operator request or state change input"}, + {0x5A01, "Operator medium removal request"}, + {0x5A02, "Operator selected write protect"}, + {0x5A03, "Operator selected write permit"}, + + {0x5B00, "Log exception"}, + {0x5B01, "Threshold condition met"}, + {0x5B02, "Log counter at maximum"}, + {0x5B03, "Log list codes exhausted"}, + + {0x5C00, "Rpl status change"}, + {0x5C01, "Spindles synchronized"}, + {0x5C02, "Spindles not synchronized"}, + + {0x5D00, "Failure prediction threshold exceeded"}, + {0x5D01, "Media failure prediction threshold exceeded"}, + {0x5D02, "Logical unit failure prediction threshold exceeded"}, + {0x5D03, "Spare area exhaustion prediction threshold exceeded"}, + {0x5D10, "Hardware impending failure general hard drive failure"}, + {0x5D11, "Hardware impending failure drive error rate too high"}, + {0x5D12, "Hardware impending failure data error rate too high"}, + {0x5D13, "Hardware impending failure seek error rate too high"}, + {0x5D14, "Hardware impending failure too many block reassigns"}, + {0x5D15, "Hardware impending failure access times too high"}, + {0x5D16, "Hardware impending failure start unit times too high"}, + {0x5D17, "Hardware impending failure channel parametrics"}, + {0x5D18, "Hardware impending failure controller detected"}, + {0x5D19, "Hardware impending failure throughput performance"}, + {0x5D1A, "Hardware impending failure seek time performance"}, + {0x5D1B, "Hardware impending failure spin-up retry count"}, + {0x5D1C, "Hardware impending failure drive calibration retry count"}, + {0x5D20, "Controller impending failure general hard drive failure"}, + {0x5D21, "Controller impending failure drive error rate too high"}, + {0x5D22, "Controller impending failure data error rate too high"}, + {0x5D23, "Controller impending failure seek error rate too high"}, + {0x5D24, "Controller impending failure too many block reassigns"}, + {0x5D25, "Controller impending failure access times too high"}, + {0x5D26, "Controller impending failure start unit times too high"}, + {0x5D27, "Controller impending failure channel parametrics"}, + {0x5D28, "Controller impending failure controller detected"}, + {0x5D29, "Controller impending failure throughput performance"}, + {0x5D2A, "Controller impending failure seek time performance"}, + {0x5D2B, "Controller impending failure spin-up retry count"}, + {0x5D2C, "Controller impending failure drive calibration retry count"}, + {0x5D30, "Data channel impending failure general hard drive failure"}, + {0x5D31, "Data channel impending failure drive error rate too high"}, + {0x5D32, "Data channel impending failure data error rate too high"}, + {0x5D33, "Data channel impending failure seek error rate too high"}, + {0x5D34, "Data channel impending failure too many block reassigns"}, + {0x5D35, "Data channel impending failure access times too high"}, + {0x5D36, "Data channel impending failure start unit times too high"}, + {0x5D37, "Data channel impending failure channel parametrics"}, + {0x5D38, "Data channel impending failure controller detected"}, + {0x5D39, "Data channel impending failure throughput performance"}, + {0x5D3A, "Data channel impending failure seek time performance"}, + {0x5D3B, "Data channel impending failure spin-up retry count"}, + {0x5D3C, "Data channel impending failure drive calibration retry " + "count"}, + {0x5D40, "Servo impending failure general hard drive failure"}, + {0x5D41, "Servo impending failure drive error rate too high"}, + {0x5D42, "Servo impending failure data error rate too high"}, + {0x5D43, "Servo impending failure seek error rate too high"}, + {0x5D44, "Servo impending failure too many block reassigns"}, + {0x5D45, "Servo impending failure access times too high"}, + {0x5D46, "Servo impending failure start unit times too high"}, + {0x5D47, "Servo impending failure channel parametrics"}, + {0x5D48, "Servo impending failure controller detected"}, + {0x5D49, "Servo impending failure throughput performance"}, + {0x5D4A, "Servo impending failure seek time performance"}, + {0x5D4B, "Servo impending failure spin-up retry count"}, + {0x5D4C, "Servo impending failure drive calibration retry count"}, + {0x5D50, "Spindle impending failure general hard drive failure"}, + {0x5D51, "Spindle impending failure drive error rate too high"}, + {0x5D52, "Spindle impending failure data error rate too high"}, + {0x5D53, "Spindle impending failure seek error rate too high"}, + {0x5D54, "Spindle impending failure too many block reassigns"}, + {0x5D55, "Spindle impending failure access times too high"}, + {0x5D56, "Spindle impending failure start unit times too high"}, + {0x5D57, "Spindle impending failure channel parametrics"}, + {0x5D58, "Spindle impending failure controller detected"}, + {0x5D59, "Spindle impending failure throughput performance"}, + {0x5D5A, "Spindle impending failure seek time performance"}, + {0x5D5B, "Spindle impending failure spin-up retry count"}, + {0x5D5C, "Spindle impending failure drive calibration retry count"}, + {0x5D60, "Firmware impending failure general hard drive failure"}, + {0x5D61, "Firmware impending failure drive error rate too high"}, + {0x5D62, "Firmware impending failure data error rate too high"}, + {0x5D63, "Firmware impending failure seek error rate too high"}, + {0x5D64, "Firmware impending failure too many block reassigns"}, + {0x5D65, "Firmware impending failure access times too high"}, + {0x5D66, "Firmware impending failure start unit times too high"}, + {0x5D67, "Firmware impending failure channel parametrics"}, + {0x5D68, "Firmware impending failure controller detected"}, + {0x5D69, "Firmware impending failure throughput performance"}, + {0x5D6A, "Firmware impending failure seek time performance"}, + {0x5D6B, "Firmware impending failure spin-up retry count"}, + {0x5D6C, "Firmware impending failure drive calibration retry count"}, + {0x5DFF, "Failure prediction threshold exceeded (false)"}, + + {0x5E00, "Low power condition on"}, + {0x5E01, "Idle condition activated by timer"}, + {0x5E02, "Standby condition activated by timer"}, + {0x5E03, "Idle condition activated by command"}, + {0x5E04, "Standby condition activated by command"}, + {0x5E41, "Power state change to active"}, + {0x5E42, "Power state change to idle"}, + {0x5E43, "Power state change to standby"}, + {0x5E45, "Power state change to sleep"}, + {0x5E47, "Power state change to device control"}, + + {0x6000, "Lamp failure"}, + + {0x6100, "Video acquisition error"}, + {0x6101, "Unable to acquire video"}, + {0x6102, "Out of focus"}, + + {0x6200, "Scan head positioning error"}, + + {0x6300, "End of user area encountered on this track"}, + {0x6301, "Packet does not fit in available space"}, + + {0x6400, "Illegal mode for this track"}, + {0x6401, "Invalid packet size"}, + + {0x6500, "Voltage fault"}, + + {0x6600, "Automatic document feeder cover up"}, + {0x6601, "Automatic document feeder lift up"}, + {0x6602, "Document jam in automatic document feeder"}, + {0x6603, "Document miss feed automatic in document feeder"}, + + {0x6700, "Configuration failure"}, + {0x6701, "Configuration of incapable logical units failed"}, + {0x6702, "Add logical unit failed"}, + {0x6703, "Modification of logical unit failed"}, + {0x6704, "Exchange of logical unit failed"}, + {0x6705, "Remove of logical unit failed"}, + {0x6706, "Attachment of logical unit failed"}, + {0x6707, "Creation of logical unit failed"}, + {0x6708, "Assign failure occurred"}, + {0x6709, "Multiply assigned logical unit"}, + {0x670A, "Set target port groups command failed"}, + {0x670B, "ATA device feature not enabled"}, + + {0x6800, "Logical unit not configured"}, + + {0x6900, "Data loss on logical unit"}, + {0x6901, "Multiple logical unit failures"}, + {0x6902, "Parity/data mismatch"}, + + {0x6A00, "Informational, refer to log"}, + + {0x6B00, "State change has occurred"}, + {0x6B01, "Redundancy level got better"}, + {0x6B02, "Redundancy level got worse"}, + + {0x6C00, "Rebuild failure occurred"}, + + {0x6D00, "Recalculate failure occurred"}, + + {0x6E00, "Command to logical unit failed"}, + + {0x6F00, "Copy protection key exchange failure - authentication " + "failure"}, + {0x6F01, "Copy protection key exchange failure - key not present"}, + {0x6F02, "Copy protection key exchange failure - key not established"}, + {0x6F03, "Read of scrambled sector without authentication"}, + {0x6F04, "Media region code is mismatched to logical unit region"}, + {0x6F05, "Drive region must be permanent/region reset count error"}, + {0x6F06, "Insufficient block count for binding nonce recording"}, + {0x6F07, "Conflict in binding nonce recording"}, +/* + * {0x70NN, "Decompression exception short algorithm id of nn"}, + */ + {0x7100, "Decompression exception long algorithm id"}, + + {0x7200, "Session fixation error"}, + {0x7201, "Session fixation error writing lead-in"}, + {0x7202, "Session fixation error writing lead-out"}, + {0x7203, "Session fixation error - incomplete track in session"}, + {0x7204, "Empty or partially written reserved track"}, + {0x7205, "No more track reservations allowed"}, + {0x7206, "RMZ extension is not allowed"}, + {0x7207, "No more test zone extensions are allowed"}, + + {0x7300, "Cd control error"}, + {0x7301, "Power calibration area almost full"}, + {0x7302, "Power calibration area is full"}, + {0x7303, "Power calibration area error"}, + {0x7304, "Program memory area update failure"}, + {0x7305, "Program memory area is full"}, + {0x7306, "RMA/PMA is almost full"}, + {0x7310, "Current power calibration area almost full"}, + {0x7311, "Current power calibration area is full"}, + {0x7317, "RDZ is full"}, + + {0x7400, "Security error"}, + {0x7401, "Unable to decrypt data"}, + {0x7402, "Unencrypted data encountered while decrypting"}, + {0x7403, "Incorrect data encryption key"}, + {0x7404, "Cryptographic integrity validation failed"}, + {0x7405, "Error decrypting data"}, + {0x7471, "Logical unit access not authorized"}, + + {0, NULL} +}; + +struct error_info2 { + unsigned char code1, code2_min, code2_max; + const char *fmt; +}; + +static const struct error_info2 additional2[] = +{ + {0x40,0x00,0x7f,"Ram failure"}, + {0x40,0x80,0xff,"Diagnostic failure on component"}, + {0x41,0x00,0xff,"Data path failure"}, + {0x42,0x00,0xff,"Power-on or self-test failure"}, + {0x4D,0x00,0xff,"Tagged overlapped commands"}, + {0x70,0x00,0xff,"Decompression exception short algorithm"}, + {0, 0, 0, NULL} +}; + +/* + * Get additional sense code string or NULL if not available. + * Returns 1 if it should be printed with ascq as arg. + */ +static const char *asc_ascq_string(unsigned char asc, unsigned char ascq) +{ + int i; + unsigned short code = ((asc << 8) | ascq); + + for (i=0; additional[i].text; i++) + if (additional[i].code12 == code) + return additional[i].text; + for (i=0; additional2[i].fmt; i++) + if (additional2[i].code1 == asc && + additional2[i].code2_min >= ascq && + additional2[i].code2_max <= ascq) + return additional2[i].fmt; + return NULL; +} + +/* + * Additional sense descriptors, see 4.14.2.1 page 62 and thereabouts. + */ +static int sense_parse_descriptor(char *s, int *ppos, const uint8_t *info, + int addl_len) +{ + int pos = *ppos; + int len = 0; + + if (addl_len < 2) + return 0; + if (info[0] == 0x6) { + /* object identification */ + uint64_t pid, oid; + if (info[1]+2 != 32) { + pos += sprintf(s+pos, + "invalid object ident length %d\n", + info[1]); + goto out; + } + if (addl_len < 32) { + pos += sprintf(s+pos, + "insufficient senselen %d for object ident\n", + addl_len); + goto out; + } + len = 32; + /* ignore not_init and completed funcs */ + pid = get_ntohll(&info[16]); + oid = get_ntohll(&info[24]); + pos += sprintf(s+pos, + " Offending pid 0x%016llx oid 0x%016llx\n", + llu(pid), llu(oid)); + } + + if (info[0] == 0x8) { + /* attribute identification */ + int i; + if (addl_len < info[1]+1) { + pos += sprintf(s+pos, + "insufficient senselen %d for attr %d\n", + addl_len, info[1]+1); + goto out; + } + if ((info[1]+1-4) % 8 != 0) { + pos += sprintf(s+pos, "info len %d not 4 + N * 8\n", + info[1]+1); + goto out; + } + len = info[1]+1; + for (i=0; i 0) { + int len = sense_parse_descriptor(s, &pos, info, additional_len); + if (len == 0) + break; + info += len; + additional_len -= len; + } + + if (additional_len > 0) + sprintf(s+pos, "Descriptor type 0x%02x len %d not shown", + info[0], additional_len); + +out: + return strdup(s); +} + +/* + * Return a pointer to the 8-byte CSI string. Not necessarily an int. + * Assumes proper descriptor format sense buffer. + */ +const uint8_t *osd_sense_extract_csi(const uint8_t *sense, int len) +{ + const uint8_t *s = NULL; + + /* skip header */ + sense += 8; + len -= 8; + while (len > 0) { + int nextlen; + if (len < 2) + break; + nextlen = sense[1] + 2; + if (sense[0] == 0x1) { + if (nextlen != 12) + break; + if (len < nextlen) + break; + s = sense + 4; + break; + } + sense += nextlen; + len -= nextlen; + } + return s; +} + diff --git a/osd-initiator/sense.h b/osd-initiator/sense.h new file mode 100644 index 0000000..5a82c31 --- /dev/null +++ b/osd-initiator/sense.h @@ -0,0 +1,25 @@ +/* + * Sense extraction and printing. + * + * Copyright (C) 2007 OSD Team + * + * 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 . + */ +#ifndef _SENSE_H +#define _SENSE_H + +void osd_sense_extract(const uint8_t *sense, int len, int *key, int *asc_ascq); +char *osd_sense_as_string(const uint8_t *sense, int len); +const uint8_t *osd_sense_extract_csi(const uint8_t *sense, int len); + +#endif /* _SENSE_H */ diff --git a/osd-initiator/sync.c b/osd-initiator/sync.c new file mode 100644 index 0000000..0fb3b45 --- /dev/null +++ b/osd-initiator/sync.c @@ -0,0 +1,1033 @@ +/* + * Test the use of the existing SG_IO interface to transport OSD commands. + * + * Copyright (C) 2007-8 OSD Team + * + * 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 "osd-util/osd-util.h" +#include "osd-util/osd-sense.h" +#include "command.h" +#include "device.h" +#include "sync.h" +#include "sense.h" + +static int check_response(int ret, struct osd_command *command, + uint8_t *buf __attribute__((unused))) +{ + if (ret) { + osd_error("%s: submit_and_wait failed", __func__); + return ret; + } + if ((command->status != 0) && (command->sense_len != 0)) { + osd_error("status: %u sense len: %u inlen: %zu", + command->status, command->sense_len, command->inlen); + osd_error("%s ", osd_sense_as_string(command->sense, + command->sense_len)); + + } else if (command->inlen > 0) { + osd_debug("Successfully performed task. BUF: <<%s>>", buf); + } else { + osd_debug("Successfully performed task"); + } + + return 0; +} + +int inquiry(int fd) +{ + const int INQUIRY_RSP_LEN = 80; + int ret; + uint8_t inquiry_rsp[INQUIRY_RSP_LEN]; + struct osd_command command; + + osd_debug("****** INQUIRY ******"); + + osd_debug("....creating command"); + memset(inquiry_rsp, 0xaa, sizeof(inquiry_rsp)); + osd_command_set_inquiry(&command, 0, sizeof(inquiry_rsp)); + + osd_debug("....building command"); + command.indata = inquiry_rsp; + command.inlen_alloc = sizeof(inquiry_rsp); + + osd_debug("....submitting command"); + ret = osd_submit_command(fd, &command); + if (ret) + osd_error("%s: osd_submit_command failed: %d", __func__, ret); + + osd_debug("....retrieving response"); + ret = osd_wait_this_response(fd, &command); + check_response(ret, &command, inquiry_rsp); + +#ifndef NDEBUG + osd_hexdump(inquiry_rsp, command.inlen); + printf("\n"); +#endif + + return 0; +} + +int query(int fd, uint64_t pid, uint64_t cid, const uint8_t *query) +{ + int ret; + uint8_t buf[100] = ""; + struct osd_command command; + + osd_debug("****** QUERY ******"); + osd_debug("PID: %llu CID: %llu QUERY: %s", llu(pid), llu(cid), query); + + if (!query) { + osd_error("%s: no query sent", __func__); + return 1; + } + + osd_debug("....creating command"); + osd_command_set_query(&command, pid, cid, + strlen((const char *)query), sizeof(buf)); + + osd_debug("....building command"); + command.outdata = query; + command.outlen = strlen((const char *)query) + 1; + command.indata = buf; + command.inlen_alloc = sizeof(buf); + + osd_debug("....submitting command"); + ret = osd_submit_command(fd, &command); + check_response(ret, &command, NULL); + + osd_debug("....retrieving response"); + ret = osd_wait_this_response(fd, &command); + check_response(ret, &command, buf); + + return 0; +} + +int create_osd(int fd, uint64_t pid, uint64_t requested_oid, + uint16_t num_user_objects) +{ + int ret; + struct osd_command command; + + osd_debug("****** CREATE OBJECT ******"); + osd_debug("Creating %u objects", num_user_objects); + osd_debug("PID: %llu OID: %llu OBJ: %u", + llu(pid), llu(requested_oid), num_user_objects); + + osd_debug("....creating command"); + osd_command_set_create(&command, pid, requested_oid, num_user_objects); + + osd_debug("....submitting command"); + ret = osd_submit_command(fd, &command); + check_response(ret, &command, NULL); + + osd_debug("....retrieving response"); + ret = osd_wait_this_response(fd, &command); + check_response(ret, &command, NULL); + + return 0; +} + +int create_partition(int fd, uint64_t requested_pid) +{ + int ret; + struct osd_command command; + + osd_debug("****** CREATE PARTITION ******"); + osd_debug("PID: %llu", llu(requested_pid)); + + osd_debug("....creating command"); + osd_command_set_create_partition(&command, requested_pid); + + osd_debug("....submitting command"); + ret = osd_submit_command(fd, &command); + check_response(ret, &command, NULL); + + osd_debug("....retrieving response"); + ret = osd_wait_this_response(fd, &command); + check_response(ret, &command, NULL); + + return 0; +} + +int create_collection(int fd, uint64_t pid, uint64_t requested_cid) +{ + int ret; + struct osd_command command; + + osd_debug("****** CREATE COLLECTION ******"); + osd_debug("PID: %llu CID: %llu", llu(pid), llu(requested_cid)); + + osd_debug("....creating command"); + osd_command_set_create_collection(&command, pid, requested_cid); + + osd_debug("....submitting command"); + ret = osd_submit_command(fd, &command); + check_response(ret, &command, NULL); + + osd_debug("....retrieving response"); + ret = osd_wait_this_response(fd, &command); + check_response(ret, &command, NULL); + + return 0; +} + +int remove_osd(int fd, uint64_t pid, uint64_t requested_oid) +{ + int ret; + struct osd_command command; + + osd_debug("****** REMOVE OBJECT ******"); + osd_debug("PID: %llu OID: %llu", llu(pid), llu(requested_oid)); + + osd_debug("....creating command"); + osd_command_set_remove(&command, pid, requested_oid); + + osd_debug("....submitting command"); + ret = osd_submit_command(fd, &command); + check_response(ret, &command, NULL); + + osd_debug("....retrieving response"); + ret = osd_wait_this_response(fd, &command); + check_response(ret, &command, NULL); + + return 0; +} + +int remove_partition(int fd, uint64_t pid) +{ + int ret; + struct osd_command command; + + osd_debug("****** REMOVE PARTITION ******"); + osd_debug("PID: %llu", llu(pid)); + + osd_debug("....creating command"); + osd_command_set_remove_partition(&command, pid); + + osd_debug("....submitting command"); + ret = osd_submit_command(fd, &command); + check_response(ret, &command, NULL); + + osd_debug("....retrieving response"); + ret = osd_wait_this_response(fd, &command); + check_response(ret, &command, NULL); + + return 0; +} + +int remove_collection(int fd, uint64_t pid, uint64_t cid, int force) +{ + int ret; + struct osd_command command; + + osd_debug("****** REMOVE COLLECTION ******"); + osd_debug("PID: %llu CID: %llu", llu(pid), llu(cid)); + + osd_debug("....creating command"); + osd_command_set_remove_collection(&command, pid, cid, force); + + osd_debug("....submitting command"); + ret = osd_submit_command(fd, &command); + check_response(ret, &command, NULL); + + osd_debug("....retrieving response"); + ret = osd_wait_this_response(fd, &command); + check_response(ret, &command, NULL); + + return 0; +} + +int remove_member_objects(int fd, uint64_t pid, uint64_t cid) +{ + int ret; + struct osd_command command; + + osd_debug("****** REMOVE MEMBER OBJECTS ******"); + osd_debug("PID: %llu CID: %llu", llu(pid), llu(cid)); + + osd_debug("....creating command"); + osd_command_set_remove_member_objects(&command, pid, cid); + + osd_debug("....submitting command"); + ret = osd_submit_command(fd, &command); + check_response(ret, &command, NULL); + + osd_debug("....retrieving response"); + ret = osd_wait_this_response(fd, &command); + check_response(ret, &command, NULL); + + return 0; +} + +int create_and_write_osd(int fd, uint64_t pid, uint64_t requested_oid, + const uint8_t *buf, uint64_t len, uint64_t offset) +{ + int ret; + struct osd_command command; + + osd_debug("****** CREATE / WRITE ******"); + osd_debug("PID: %llu OID: %llu BUF: %s", llu(pid), llu(requested_oid), + buf); + + if (!buf) { + osd_error("%s: no data sent", __func__); + return 1; + } + + osd_debug("....creating command"); + osd_command_set_create_and_write(&command, pid, requested_oid, len, + offset); + + osd_debug("....building command"); + command.outdata = buf; + command.outlen = len; + + osd_debug("....submitting command"); + ret = osd_submit_command(fd, &command); + check_response(ret, &command, NULL); + + osd_debug("....retrieving response"); + ret = osd_wait_this_response(fd, &command); + check_response(ret, &command, NULL); + + return 0; +} + +int create_and_write_sgl_osd(int fd, uint64_t pid, uint64_t requested_oid, + const uint8_t *buf, uint64_t len, uint64_t offset) +{ + int ret; + struct osd_command command; + + osd_debug("****** CREATE / WRITE SGL ******"); + osd_debug("PID: %llu OID: %llu BUF: %s", llu(pid), llu(requested_oid), + buf); + + if (!buf) { + osd_error("%s: no data sent", __func__); + return 1; + } + + osd_command_set_create_and_write(&command, pid, requested_oid, len, + offset); + + osd_command_set_ddt(&command, DDT_SGL); + + command.outdata = buf; + command.outlen = len; + + ret = osd_submit_command(fd, &command); + check_response(ret, &command, NULL); + + ret = osd_wait_this_response(fd, &command); + check_response(ret, &command, NULL); + + return 0; +} + +int create_and_write_vec_osd(int fd, uint64_t pid, uint64_t requested_oid, + const uint8_t *buf, uint64_t len, uint64_t offset) +{ + int ret; + struct osd_command command; + + osd_debug("****** CREATE / WRITE VEC ******"); + osd_debug("PID: %llu OID: %llu BUF: %s", llu(pid), llu(requested_oid), + buf); + + if (!buf) { + osd_error("%s: no data sent", __func__); + return 1; + } + + osd_command_set_create_and_write(&command, pid, requested_oid, len, + offset); + + osd_command_set_ddt(&command, DDT_VEC); + + command.outdata = buf; + command.outlen = len; + + ret = osd_submit_command(fd, &command); + check_response(ret, &command, NULL); + + ret = osd_wait_this_response(fd, &command); + check_response(ret, &command, NULL); + + return 0; +} + +int write_osd(int fd, uint64_t pid, uint64_t oid, const uint8_t *buf, + uint64_t len, uint64_t offset) +{ + int ret; + struct osd_command command; + struct attribute_list attr = { + .type = ATTR_GET, + .page = USER_INFO_PG, + .number = UIAP_LOGICAL_LEN, + .len = 8, + }; + uint64_t size; + + osd_debug("****** WRITE ******"); + osd_debug("PID: %llu OID: %llu BUF: %s", llu(pid), llu(oid), buf); + + if (!buf) { + osd_error("%s: no data sent", __func__); + return 1; + } + + osd_command_set_write(&command, pid, oid, len, offset); + command.outdata = buf; + command.outlen = len; + + osd_command_attr_build(&command, &attr, 1); + + ret = osd_submit_command(fd, &command); + check_response(ret, &command, NULL); + + ret = osd_wait_this_response(fd, &command); + check_response(ret, &command, NULL); + if (command.status != 0) + return 1; + + ret = osd_command_attr_resolve(&command); + if (ret) { + osd_error_xerrno(ret, "%s: attr_resolve failed", __func__); + exit(1); + } + size = get_ntohll(command.attr[0].val); + + osd_debug("Object Data Size is now %llu", llu(size)); + + osd_command_attr_free(&command); + + return 0; +} + +int write_sgl_osd(int fd, uint64_t pid, uint64_t oid, const uint8_t *buf, + uint64_t len, uint64_t offset) +{ + int ret; + struct osd_command command; + struct attribute_list attr = { + .type = ATTR_GET, + .page = USER_INFO_PG, + .number = UIAP_LOGICAL_LEN, + .len = 8, + }; + uint64_t size; + + osd_debug("****** WRITE SGL ******"); + osd_debug("PID: %llu OID: %llu LEN: %llu", llu(pid), llu(oid), llu(len)); + + if (!buf) { + osd_error("%s: no data sent", __func__); + return 1; + } + + /*setting the command is now a two stage process and not doing it + will default to contiguous buffer model*/ + osd_command_set_write(&command, pid, oid, len, offset); + + osd_command_set_ddt(&command, DDT_SGL); + + command.outdata = buf; + command.outlen = len; + + osd_command_attr_build(&command, &attr, 1); + + ret = osd_submit_command(fd, &command); + check_response(ret, &command, NULL); + + ret = osd_wait_this_response(fd, &command); + check_response(ret, &command, NULL); + + ret = osd_command_attr_resolve(&command); + if (ret) { + osd_error_xerrno(ret, "%s: attr_resolve failed", __func__); + exit(1); + } + size = get_ntohll(command.attr[0].val); + + osd_debug("Object Data Size is %llu\n", llu(size)); + + return 0; +} + +int write_vec_osd(int fd, uint64_t pid, uint64_t oid, const uint8_t *buf, + uint64_t len, uint64_t offset) +{ + int ret; + struct osd_command command; + struct attribute_list attr = { + .type = ATTR_GET, + .page = USER_INFO_PG, + .number = UIAP_LOGICAL_LEN, + .len = 8, + }; + uint64_t size; + + osd_debug("****** WRITE VEC ******"); + osd_debug("PID: %llu OID: %llu LEN: %llu", llu(pid), llu(oid), llu(len)); + + if (!buf) { + osd_error("%s: no data sent", __func__); + return 1; + } + + /*setting the command is now a two stage process and not doing it + will default to contiguous buffer model*/ + osd_command_set_write(&command, pid, oid, len, offset); + + osd_command_set_ddt(&command, DDT_VEC); + + command.outdata = buf; + command.outlen = len; + + osd_command_attr_build(&command, &attr, 1); + + ret = osd_submit_command(fd, &command); + check_response(ret, &command, NULL); + + ret = osd_wait_this_response(fd, &command); + check_response(ret, &command, NULL); + + ret = osd_command_attr_resolve(&command); + if (ret) { + osd_error_xerrno(ret, "%s: attr_resolve failed", __func__); + exit(1); + } + size = get_ntohll(command.attr[0].val); + + osd_debug("Object Data Size is %llu\n", llu(size)); + + + return 0; +} + +int append_osd(int fd, uint64_t pid, uint64_t oid, const uint8_t *buf, + uint64_t len) +{ + int ret; + struct osd_command command; + + osd_debug("****** APPEND ******"); + osd_debug("PID: %llu OID: %llu BUF: %s", llu(pid), llu(oid), buf); + + if (!buf) { + osd_error("%s: no data sent", __func__); + return 1; + } + + osd_debug("....creating command"); + osd_command_set_append(&command, pid, oid, len); + + osd_debug("....building command"); + command.outdata = buf; + command.outlen = len; + + osd_debug("....submitting command"); + ret = osd_submit_command(fd, &command); + check_response(ret, &command, NULL); + + osd_debug("....retrieving response"); + ret = osd_wait_this_response(fd, &command); + check_response(ret, &command, NULL); + + return 0; +} + +int append_sgl_osd(int fd, uint64_t pid, uint64_t oid, const uint8_t *buf, + uint64_t len) +{ + int ret; + struct osd_command command; + + osd_debug("****** APPEND ******"); + osd_debug("PID: %llu OID: %llu BUF: %s", llu(pid), llu(oid), buf); + + if (!buf) { + osd_error("%s: no data sent", __func__); + return 1; + } + + osd_command_set_append(&command, pid, oid, len); + + osd_command_set_ddt(&command, DDT_SGL); + + + command.outdata = buf; + command.outlen = len; + + ret = osd_submit_command(fd, &command); + check_response(ret, &command, NULL); + + ret = osd_wait_this_response(fd, &command); + check_response(ret, &command, NULL); + + return 0; +} + +int append_vec_osd(int fd, uint64_t pid, uint64_t oid, const uint8_t *buf, + uint64_t len) +{ + int ret; + struct osd_command command; + + osd_debug("****** APPEND ******"); + osd_debug("PID: %llu OID: %llu BUF: %s", llu(pid), llu(oid), buf); + + if (!buf) { + osd_error("%s: no data sent", __func__); + return 1; + } + + osd_command_set_append(&command, pid, oid, len); + + osd_command_set_ddt(&command, DDT_VEC); + + + command.outdata = buf; + command.outlen = len; + + ret = osd_submit_command(fd, &command); + check_response(ret, &command, NULL); + + ret = osd_wait_this_response(fd, &command); + check_response(ret, &command, NULL); + + return 0; +} + +int read_osd(int fd, uint64_t pid, uint64_t oid, uint8_t *buf, uint64_t len, + uint64_t offset) +{ + int ret; + struct osd_command command; + + osd_debug("****** READ ******"); + osd_debug("PID: %llu OID: %llu", llu(pid), llu(oid)); + + osd_debug("....creating command"); + osd_command_set_read(&command, pid, oid, len, offset); + + osd_debug("....building command"); + command.indata = buf; + command.inlen_alloc = len; + + osd_debug("....submitting command"); + ret = osd_submit_command(fd, &command); + check_response(ret, &command, NULL); + + osd_debug("....retrieving response"); + ret = osd_wait_this_response(fd, &command); + check_response(ret, &command, buf); + + return 0; +} + +int read_sgl_osd(int fd, uint64_t pid, uint64_t oid, uint8_t *ddt_buf, + uint64_t ddt_len, uint8_t *buf, uint64_t len, uint64_t offset) +{ + int ret; + struct osd_command command; + + osd_debug("****** READ ******"); + osd_debug("PID: %llu OID: %llu", llu(pid), llu(oid)); + osd_debug("ddt len: %llu len: %llu", llu(ddt_len), llu(len)); + + osd_command_set_read(&command, pid, oid, len, offset); + + osd_command_set_ddt(&command, DDT_SGL); + + command.indata = buf; + command.inlen_alloc = len; + + command.outdata = ddt_buf; + command.outlen = ddt_len; + + ret = osd_submit_command(fd, &command); + check_response(ret, &command, NULL); + + ret = osd_wait_this_response(fd, &command); + check_response(ret, &command, buf); + + return 0; +} + +int read_vec_osd(int fd, uint64_t pid, uint64_t oid, uint8_t *ddt_buf, + uint64_t ddt_len, uint8_t *buf, uint64_t len, uint64_t offset) +{ + int ret; + struct osd_command command; + + osd_debug("****** READ ******"); + osd_debug("PID: %llu OID: %llu", llu(pid), llu(oid)); + osd_debug("ddt len: %llu len: %llu", llu(ddt_len), llu(len)); + + osd_command_set_read(&command, pid, oid, len, offset); + + osd_command_set_ddt(&command, DDT_VEC); + + command.indata = buf; + command.inlen_alloc = len; + + command.outdata = ddt_buf; + command.outlen = ddt_len; + + ret = osd_submit_command(fd, &command); + check_response(ret, &command, NULL); + + ret = osd_wait_this_response(fd, &command); + check_response(ret, &command, buf); + + return 0; +} + +int format_osd(int fd, int capacity) +{ + int ret; + struct osd_command command; + + osd_debug("****** FORMAT OSD ******"); + osd_debug("Capacity: %i", capacity); + + osd_debug("eat the reset ua with a tur"); + osd_command_set_test_unit_ready(&command); + ret = osd_submit_and_wait(fd, &command); + if (command.status == 0) + goto okay; + if (command.status == 2) { + int key, asc; + osd_sense_extract(command.sense, command.sense_len, &key, &asc); + if (key == OSD_SSK_UNIT_ATTENTION && + asc == OSD_ASC_POWER_ON_OCCURRED) + goto okay; + } + check_response(ret, &command, NULL); + +okay: + osd_debug("....creating command"); + osd_command_set_format_osd(&command, capacity); + + osd_debug("....submitting command"); + ret = osd_submit_command(fd, &command); + check_response(ret, &command, NULL); + + osd_debug("....retrieving response"); + ret = osd_wait_this_response(fd, &command); + check_response(ret, &command, NULL); + + return 0; +} + +int flush_osd(int fd, int flush_scope) +{ + int ret; + struct osd_command command; + + osd_debug("****** FLUSH OSD ******"); + + osd_debug("....creating command"); + osd_command_set_flush_osd(&command, flush_scope); + + osd_debug("....submitting command"); + ret = osd_submit_command(fd, &command); + check_response(ret, &command, NULL); + + osd_debug("....retrieving response"); + ret = osd_wait_this_response(fd, &command); + check_response(ret, &command, NULL); + + return 0; +} + +int flush_partition(int fd, uint64_t pid, int flush_scope) +{ + int ret; + struct osd_command command; + + osd_debug("****** FLUSH PARTITION ******"); + osd_debug("PID: %llu", llu(pid)); + + osd_debug("....creating command"); + osd_command_set_flush_partition(&command, pid, flush_scope); + + osd_debug("....submitting command"); + ret = osd_submit_command(fd, &command); + check_response(ret, &command, NULL); + + osd_debug("....retrieving response"); + ret = osd_wait_this_response(fd, &command); + check_response(ret, &command, NULL); + + return 0; +} + +int flush_collection(int fd, uint64_t pid, uint64_t cid, int flush_scope) +{ + int ret; + struct osd_command command; + + osd_debug("****** FLUSH COLLECTION ******"); + osd_debug("PID: %llu CID: %llu", llu(pid), llu(cid)); + + osd_debug("....creating command"); + osd_command_set_flush_collection(&command, pid, cid, flush_scope); + + osd_debug("....submitting command"); + ret = osd_submit_command(fd, &command); + check_response(ret, &command, NULL); + + osd_debug("....retrieving response"); + ret = osd_wait_this_response(fd, &command); + check_response(ret, &command, NULL); + + return 0; +} + +int flush_object(int fd, uint64_t pid, uint64_t oid, uint64_t len, uint64_t offset, int flush_scope) +{ + int ret; + struct osd_command command; + + osd_debug("****** FLUSH OBJECT ******"); + osd_debug("PID: %llu OID: %llu", llu(pid), llu(oid)); + + osd_command_set_flush(&command, pid, oid, len, offset, flush_scope); + + ret = osd_submit_command(fd, &command); + check_response(ret, &command, NULL); + + ret = osd_wait_this_response(fd, &command); + check_response(ret, &command, NULL); + + return 0; +} + +/* Attributes */ + +/* Unimplemented */ +int get_attributes(int fd, uint64_t pid, uint64_t oid) +{ + int ret; + uint8_t buf[100] = ""; + struct osd_command command; + + osd_debug("****** GET ATTRIBUTES ******"); + osd_debug("PID: %llu OID: %llu", llu(pid), llu(oid)); + + osd_command_set_get_attributes(&command, pid, oid); + command.indata = buf; + command.inlen_alloc = sizeof(buf); + memset(buf, 0, sizeof(buf)); + ret = osd_submit_and_wait(fd, &command); + check_response(ret, &command, buf); + return 0; +} + +/* Unimplemented */ +int set_attributes(int fd, uint64_t pid, uint64_t oid, + const struct attribute_list *attrs) +{ + int ret; + struct osd_command command; + + osd_debug("****** SET ATTRIBUTES ******"); + osd_debug("PID: %llu OID: %llu", llu(pid), llu(oid)); + + if (!attrs) { + osd_error("%s: no attributes sent", __func__); + return 1; + } + + osd_command_set_set_attributes(&command, pid, oid); + command.outdata = attrs; + command.outlen = sizeof(*attrs); + ret = osd_submit_and_wait(fd, &command); + check_response(ret, &command, NULL); + return 0; +} + +/* Unimplemented */ +int set_member_attributes(int fd, uint64_t pid, uint64_t cid, + const struct attribute_list *attrs) +{ + int ret; + struct osd_command command; + + osd_debug("****** SET MEMBER ATTRIBUTES ******"); + osd_debug("PID: %llu CID: %llu", llu(pid), llu(cid)); + if (!attrs) { + osd_error("%s: no attributes sent", __func__); + return 1; + } + + osd_command_set_set_member_attributes(&command, pid, cid); + command.outdata = attrs; + command.outlen = sizeof(*attrs); + ret = osd_submit_and_wait(fd, &command); + check_response(ret, &command, NULL); + return 0; +} + +/* Unimplemented */ +int get_member_attributes(int fd, uint64_t pid, uint64_t cid) +{ + int ret; + uint8_t buf[100] = ""; + struct osd_command command; + + osd_debug("****** GET MEMBER ATTRIBUTES ******"); + osd_debug("PID: %llu CID: %llu", llu(pid), llu(cid)); + + osd_command_set_get_member_attributes(&command, pid, cid); + command.indata = buf; + command.inlen_alloc = sizeof(buf); + memset(buf, 0, sizeof(buf)); + ret = osd_submit_and_wait(fd, &command); + check_response(ret, &command, buf); + return 0; +} + + +/* + * List + * sec 6.14 in spec. +*/ +int list(int fd, uint64_t pid, uint32_t list_id, uint64_t initial_oid, + uint64_t alloc_len, int list_attr) +{ + /* + * buf_len specifies how many bytes the buffer for the returned data + * will be. + * It is 24 bytes larger than alloc_len for header information in the + * returned data. + */ + int ret; + uint64_t buf_len = alloc_len + 24; + uint8_t buf[buf_len]; + memset(buf, 0, buf_len); + struct osd_command command; + + osd_debug("****** LIST ******"); + /* + * If pid is 0, we would like to list all the PIDs that exist, where + * initial_oid represents the PID to start at. If pid is nonzero, + * we wish to list all the OIDs, starting with initial_oid, in the + * partition given by pid. + */ + if (list_id == 0) { + if (pid == 0) { + osd_debug("INITIAL_PID: %llu", llu(initial_oid)); + } else { + osd_debug("PID: %llu INITIAL_OID: %llu", llu(pid), + llu(initial_oid)); + } + } + else { + osd_debug("LIST_ID: %u CONTINUATION_ID: %llu", list_id, + llu(initial_oid)); + } + + osd_debug("....creating command"); + osd_command_set_list(&command, pid, list_id, buf_len, initial_oid, + list_attr); + + osd_debug("....building command"); + command.indata = buf; + command.inlen_alloc = buf_len; + + osd_debug("....submitting command"); + ret = osd_submit_command(fd, &command); + check_response(ret, &command, NULL); + + osd_debug("....retrieving response"); + ret = osd_wait_this_response(fd, &command); + osd_command_list_resolve(&command); + + return 0; +} + +/* + * List Collection + * sec 6.15 in spec. +*/ +int list_collection(int fd, uint64_t pid, uint64_t cid, uint32_t list_id, + uint64_t initial_oid, uint64_t alloc_len, int list_attr) +{ + /* + * buf_len specifies how many bytes the buffer for the returned data + * will be. + * It is 24 bytes larger than alloc_len for header information in the + * returned data. + */ + int ret; + uint64_t buf_len = alloc_len + 24; + uint8_t buf[buf_len]; + memset(buf, 0, buf_len); + struct osd_command command; + + osd_debug("****** LIST COLLECTION ******"); + /* + * If cid is 0, we would like to list all the CIDs in the + * given partition, and initial_oid is the lowest CID to start at. + * If cid is nonzero, we would like to list all the OIDs in the + * collection given by cid, and initial_oid is the lowest OID to start + * at. + */ + if (list_id == 0) { + if (cid == 0) { + osd_debug("PID: %llu INITIAL_CID: %llu", llu(pid), + llu(initial_oid)); + } else { + osd_debug("PID: %llu CID: %llu INITIAL_OID: %llu", + llu(pid), llu(cid), llu(initial_oid)); + } + } + else { + osd_debug("LIST_ID: %u CONTINUATION_ID: %llu", list_id, + llu(initial_oid)); + } + + osd_debug("....creating command"); + osd_command_set_list_collection(&command, pid, cid, list_id, buf_len, + initial_oid, list_attr); + + osd_debug("....building command"); + command.indata = buf; + command.inlen_alloc = buf_len; + + osd_debug("....submitting command"); + ret = osd_submit_command(fd, &command); + check_response(ret, &command, NULL); + + osd_debug("....retrieving response"); + ret = osd_wait_this_response(fd, &command); + osd_command_list_collection_resolve(&command); + + return 0; +} diff --git a/osd-initiator/sync.h b/osd-initiator/sync.h new file mode 100644 index 0000000..68be07f --- /dev/null +++ b/osd-initiator/sync.h @@ -0,0 +1,86 @@ +/* + * Synchronous interface. + * + * Copyright (C) 2007 OSD Team + * + * 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 . + */ +#ifndef _SYNC_H +#define _SYNC_H + +/* + * These functions do everything: build, submit, wait response. + */ + +/* Queries */ +int inquiry(int fd); +int query(int fd, uint64_t pid, uint64_t cid, const uint8_t *query); +/* Create */ +int create_osd(int fd, uint64_t pid, uint64_t requested_oid, + uint16_t num_user_objects); +int create_partition(int fd, uint64_t requested_pid); +int create_collection(int fd, uint64_t pid, uint64_t requested_cid); +int create_and_write_osd(int fd, uint64_t pid, uint64_t requested_oid, + const uint8_t *buf, uint64_t len, uint64_t offset); +int create_and_write_sgl_osd(int fd, uint64_t pid, uint64_t requested_oid, + const uint8_t *buf, uint64_t len, uint64_t offset); +int create_and_write_vec_osd(int fd, uint64_t pid, uint64_t requested_oid, + const uint8_t *buf, uint64_t len, uint64_t offset); +/* Read/Write */ +int write_osd(int fd, uint64_t pid, uint64_t oid, const uint8_t *buf, + uint64_t len, uint64_t offset); +int write_sgl_osd(int fd, uint64_t pid, uint64_t oid, const uint8_t *buf, + uint64_t len, uint64_t offset); +int write_vec_osd(int fd, uint64_t pid, uint64_t oid, const uint8_t *buf, + uint64_t len, uint64_t offset); +int read_osd(int fd, uint64_t pid, uint64_t oid, uint8_t *buf, uint64_t len, + uint64_t offset); +int read_sgl_osd(int fd, uint64_t pid, uint64_t oid, uint8_t *ddt_buf, + uint64_t ddt_len, uint8_t *buf, uint64_t len, uint64_t offset); +int read_vec_osd(int fd, uint64_t pid, uint64_t oid, uint8_t *ddt_buf, + uint64_t ddt_len, uint8_t *buf, uint64_t len, uint64_t offset); +int append_osd(int fd, uint64_t pid, uint64_t oid, const uint8_t *buf, + uint64_t len); +int append_sgl_osd(int fd, uint64_t pid, uint64_t oid, const uint8_t *buf, + uint64_t len); +int append_vec_osd(int fd, uint64_t pid, uint64_t oid, const uint8_t *buf, + uint64_t len); + +/* Remove */ +int remove_osd(int fd, uint64_t pid, uint64_t requested_oid); +int remove_partition(int fd, uint64_t pid); +int remove_collection(int fd, uint64_t pid, uint64_t cid, int force); +int remove_member_objects(int fd, uint64_t pid, uint64_t cid); +/* Format/Flush */ +int format_osd(int fd, int capacity); +int flush_osd(int fd, int flush_scope); +int flush_partition(int fd, uint64_t pid, int flush_scope); +int flush_collection(int fd, uint64_t pid, uint64_t cid, int flush_scope); +int flush_object(int fd, uint64_t pid, uint64_t oid, uint64_t len, uint64_t offset, int flush_scope); +/* + * Get/Set Attributes - none of these work at the moment, use attr_build + * and attr_resolve in command.c + */ +int get_attributes(int fd, uint64_t pid, uint64_t oid); +int get_member_attributes(int fd, uint64_t pid, uint64_t cid); +int set_attributes(int fd, uint64_t pid, uint64_t oid, + const struct attribute_list *attrs); +int set_member_attributes(int fd, uint64_t pid, uint64_t cid, + const struct attribute_list *attrs); +/* List */ +int list(int fd, uint64_t pid, uint32_t list_id, uint64_t initial_oid, + uint64_t alloc_len, int list_attr); +int list_collection(int fd, uint64_t pid, uint64_t cid, uint32_t list_id, + uint64_t initial_oid, uint64_t alloc_len, int list_attr); + +#endif diff --git a/osd-initiator/tests/Makefile b/osd-initiator/tests/Makefile new file mode 100644 index 0000000..84ced0a --- /dev/null +++ b/osd-initiator/tests/Makefile @@ -0,0 +1,70 @@ +# +# OSD initiator tests +# + +PROGSRC := sgio.c generic_iface_test.c iovec.c iospeed.c latency.c atomic.c \ + attr-all.c iospeed-ddt.c +PROGSRC += blk-iospeed.c sgl_test.c alignment.c alignment-rdma.c +PROGOBJ := $(PROGSRC:.c=.o) +PROGEXE := $(PROGSRC:.c=) +LIB := ../libosdinit.a ../../osd-util/libosdutil.a + +# auto-find by walking up from your path to mpicc +MPIDIR := $(dir $(shell type -p mpicc 2>/dev/null)) +MPICC := $(shell type -p mpicc 2>/dev/null) +ifneq (,$(MPIDIR)) + MPIDIR := $(MPIDIR).. + MPISRC := iospeed-mpi.c contention.c + MPIOBJ := $(MPISRC:.c=.o) + MPIEXE := $(MPISRC:.c=) + # needed for some MPI builds + PVFSDIR := $(dir $(shell type -p pvfs2-ls 2>/dev/null)).. + MPIINC := -I$(MPIDIR)/include + MPILIB := -L/usr/local/mpich2-eth/lib -lmpich -L$(PVFSDIR)/lib -lpvfs2 -lrt +endif + +-include ../../Makedefs + +CC := gcc +CPP_M := -MM +LD := $(CC) +COPTS := $(OPT) -D_GNU_SOURCE +CWARN := -Wall -W -Wpointer-arith -Wwrite-strings -Wcast-align -Wcast-qual \ + -Wbad-function-cast -Wundef -Wmissing-prototypes \ + -Wmissing-declarations -Wnested-externs +CFLAGS := $(COPTS) $(CWARN) -I.. -I../.. + +# for blk-iospeed +#CFLAGS += -Dsg_io_v4=sg_io_v4_v2622 +CFLAGS += -Dsg_io_v4=sg_io_v4_v2624 + +all:: $(LIB) $(PROGEXE) $(MPIEXE) + +$(PROGEXE): %: %.o $(LIB) + $(CC) -o $@ $^ $(LIB) -lm + +$(MPIEXE): %: %.o $(LIB) + $(MPICC) -o $@ $^ $(MPILIB) $(LIB) -lm + +%.o: %.c + $(CC) $(CFLAGS) -c $< -o $@ + +$(MPIOBJ): %.o: %.c + $(MPICC) $(CFLAGS) $(MPIINC) -c $< -o $@ + +ifeq (,$(filter clean distclean dist,$(MAKECMDGOALS))) +-include .depend +endif +all:: .depend +.depend: $(PROGSRC) $(INC) Makefile + @$(CC) $(CPP_M) $(CFLAGS) $(PROGSRC) > .depend + +clean: + rm -f $(PROGOBJ) $(PROGEXE) $(MPIOBJ) $(MPIEXE) + rm -f .depend + +tags: FORCE + ctags $(PROGSRC) $(MPISRC) $(INC) + +FORCE:; + diff --git a/osd-initiator/tests/alignment-rdma.c b/osd-initiator/tests/alignment-rdma.c new file mode 100644 index 0000000..3fbd913 --- /dev/null +++ b/osd-initiator/tests/alignment-rdma.c @@ -0,0 +1,397 @@ +/* + * Probe performance issues when iser rdma alignment violations occur. + * + * Copyright (C) 2008 Pete Wyckoff + * + * 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 "osd-util/osd-util.h" +#include "command.h" +#include "device.h" +#include "drivelist.h" +#include "sync.h" + +static uint64_t obj_create_any(int fd, uint64_t pid) +{ + struct osd_command command; + struct attribute_list attr = { + .type = ATTR_GET, + .page = CUR_CMD_ATTR_PG, + .number = CCAP_OID, + .len = 8, + }; + int ret; + uint64_t oid; + + osd_command_set_create(&command, pid, 0, 0); + osd_command_attr_build(&command, &attr, 1); + ret = osd_submit_and_wait(fd, &command); + if (ret) { + osd_error_xerrno(ret, "%s: submit_and_wait failed", __func__); + exit(1); + } + osd_command_attr_resolve(&command); + oid = get_ntohll(command.attr[0].val); + osd_command_attr_free(&command); + return oid; +} + +static void obj_remove(int fd, uint64_t pid, uint64_t oid) +{ + struct osd_command command; + int ret; + + osd_command_set_remove(&command, pid, oid); + ret = osd_submit_and_wait(fd, &command); + if (ret) { + osd_error_xerrno(ret, "%s: submit_and_wait failed", __func__); + exit(1); + } +} + + +/* + * For various amounts of discontiguity, compare rdma-realigned performance + * vs multiple transfers vs memcpy in userspace; + */ +static void una_write_test(int fd, uint64_t pid) +{ + struct osd_command command, *commandp; + struct bsg_iovec *vec; + int ret, i, j, numvec; + uint64_t oid; + uint32_t onesize, pos; + uint8_t *buf, *linbuf; + double mu, sd; + double *v; + uint64_t start, end; + int pagesize = getpagesize(); + const int numpages = (256 << 10) / pagesize; + const uint32_t buflen = numpages * pagesize; + const int iter = 1000, warmup = 10; + + printf("write numpages %d buflen %d\n", numpages, buflen); + v = malloc((iter + warmup) * sizeof(*v)); + if (!v) + osd_error_fatal("out of memory"); + + /* source buf */ + ret = posix_memalign((void *) &buf, pagesize, buflen + pagesize); + if (ret) { + perror("malloc"); + exit(1); + } + memset(buf, 0x5a, buflen + pagesize); + buf += pagesize / 2; /* force unaligned */ + + /* linear buf */ + ret = posix_memalign((void *) &linbuf, pagesize, buflen); + if (!buf) { + perror("malloc linbuf"); + exit(1); + } + + /* iovec */ + vec = malloc(numpages * sizeof(*vec)); + + oid = obj_create_any(fd, pid); + + for (numvec=1; numvec<=numpages; numvec *= 2) { + + /* build iovec */ + onesize = buflen / numvec; + pos = 0; + + for (i = numvec-1; i >= 0; i--) { + vec[i].iov_base = (uintptr_t) buf + pos; + vec[i].iov_len = onesize; + pos += onesize; + } + + /* contig */ + osd_command_set_write(&command, pid, oid, buflen, 0); + command.outlen = buflen; + command.outdata = buf; + for (i=0; i= 0; j--) { + memcpy(&linbuf[pos], (void *) vec[j].iov_base, + vec[j].iov_len); + pos += vec[j].iov_len; + } + ret = osd_submit_and_wait(fd, &command); + rdtsc(end); + assert(ret == 0); + v[i] = (double) (end - start) / mhz; /* in us */ + } + mu = mean(v + warmup, iter); + sd = stddev(v + warmup, mu, iter); + printf("write memcpy %2d %9.3lf +- %9.3lf\n", numvec, mu, sd); + + /* multiple xfers, all at the same time */ + for (i=0; i= 0; j--) { + osd_command_set_write(&command, pid, oid, + onesize, pos); + command.outlen = onesize; + command.outdata = (void *) vec[j].iov_base; + ret = osd_submit_command(fd, &command); + assert(ret == 0); + pos += vec[j].iov_len; + } + for (j = numvec-1; j >= 0; j--) { + ret = osd_wait_response(fd, &commandp); + assert(ret == 0); + } + rdtsc(end); + v[i] = (double) (end - start) / mhz; /* in us */ + } + mu = mean(v + warmup, iter); + sd = stddev(v + warmup, mu, iter); + printf("write multi %2d %9.3lf +- %9.3lf\n", numvec, mu, sd); + } + + obj_remove(fd, pid, oid); + free(vec); + free(linbuf); + free(buf - pagesize/2); + free(v); +} + +static void una_read_test(int fd, uint64_t pid) +{ + struct osd_command command, *commandp; + struct bsg_iovec *vec; + int ret, i, j, numvec; + uint64_t oid; + uint32_t onesize, pos; + uint8_t *buf, *linbuf; + double mu, sd; + double *v; + uint64_t start, end; + int pagesize = getpagesize(); + const int numpages = (256 << 10) / pagesize; + const uint32_t buflen = numpages * pagesize; + const int iter = 1000, warmup = 10; + + printf("read numpages %d buflen %d\n", numpages, buflen); + v = malloc((iter + warmup) * sizeof(*v)); + if (!v) + osd_error_fatal("out of memory"); + + /* source buf */ + ret = posix_memalign((void *) &buf, pagesize, buflen + pagesize); + if (ret) { + perror("malloc"); + exit(1); + } + memset(buf, 0x5a, buflen + pagesize); + buf += pagesize / 2; /* force unaligned */ + + /* linear buf */ + ret = posix_memalign((void *) &linbuf, pagesize, buflen); + if (!buf) { + perror("malloc linbuf"); + exit(1); + } + + /* iovec */ + vec = malloc(numpages * sizeof(*vec)); + + oid = obj_create_any(fd, pid); + + /* give it some data */ + osd_command_set_write(&command, pid, oid, buflen, 0); + command.outlen = buflen; + command.outdata = buf; + ret = osd_submit_and_wait(fd, &command); + assert(ret == 0); + + for (numvec=1; numvec<=numpages; numvec *= 2) { + + /* build iovec */ + onesize = buflen / numvec; + pos = 0; + + for (i = numvec-1; i >= 0; i--) { + vec[i].iov_base = (uintptr_t) buf + pos; + vec[i].iov_len = onesize; + pos += onesize; + } + + /* contig */ + osd_command_set_read(&command, pid, oid, buflen, 0); + command.inlen_alloc = buflen; + command.indata = buf; + for (i=0; i= 0; j--) { + memcpy((void *) vec[j].iov_base, &linbuf[pos], + vec[j].iov_len); + pos += vec[j].iov_len; + } + rdtsc(end); + assert(ret == 0); + v[i] = (double) (end - start) / mhz; /* in us */ + } + mu = mean(v + warmup, iter); + sd = stddev(v + warmup, mu, iter); + printf("read memcpy %2d %9.3lf +- %9.3lf\n", numvec, mu, sd); + + /* multiple xfers, all at the same time */ + for (i=0; i= 0; j--) { + osd_command_set_read(&command, pid, oid, + onesize, pos); + command.inlen_alloc = onesize; + command.indata = (void *) vec[j].iov_base; + ret = osd_submit_command(fd, &command); + assert(ret == 0); + pos += vec[j].iov_len; + } + for (j = numvec-1; j >= 0; j--) { + ret = osd_wait_response(fd, &commandp); + assert(ret == 0); + } + rdtsc(end); + v[i] = (double) (end - start) / mhz; /* in us */ + } + mu = mean(v + warmup, iter); + sd = stddev(v + warmup, mu, iter); + printf("read multi %2d %9.3lf +- %9.3lf\n", numvec, mu, sd); + } + + obj_remove(fd, pid, oid); + free(vec); + free(linbuf); + free(buf - pagesize/2); + free(v); +} + +int main(int argc, char *argv[]) +{ + int fd, ret, num_drives, i; + struct osd_drive_description *drives; + + osd_set_progname(argc, argv); + if (argc != 1) { + fprintf(stderr, "Usage: %s\n", osd_get_progname()); + return 1; + } + + ret = osd_get_drive_list(&drives, &num_drives); + if (ret < 0) { + osd_error("%s: get drive error", __func__); + return 1; + } + if (num_drives == 0) { + osd_error("%s: no drives", __func__); + return 1; + } + + i = 0; + printf("%s: drive %s name %s\n", osd_get_progname(), drives[i].chardev, + drives[i].targetname); + fd = open(drives[i].chardev, O_RDWR); + if (fd < 0) { + osd_error_errno("%s: open %s", __func__, drives[i].chardev); + return 1; + } + osd_free_drive_list(drives, num_drives); + + format_osd(fd, 1<<30); + create_partition(fd, PARTITION_PID_LB); + + una_write_test(fd, PARTITION_PID_LB); + una_read_test(fd, PARTITION_PID_LB); + + close(fd); + return 0; +} + diff --git a/osd-initiator/tests/alignment.c b/osd-initiator/tests/alignment.c new file mode 100644 index 0000000..fef19ce --- /dev/null +++ b/osd-initiator/tests/alignment.c @@ -0,0 +1,102 @@ +/* + * Test alignment issues with submitting userspace buffers through bsg. + * + * Copyright (C) 2008 Pete Wyckoff + * + * 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 "osd-util/osd-util.h" +#include "command.h" +#include "device.h" +#include "drivelist.h" + +int main(int argc, char *argv[]) +{ + int fd, ret, num_drives, i, shift; + struct osd_drive_description *drives; + struct osd_command command; + void *buf; + + osd_set_progname(argc, argv); + + ret = osd_get_drive_list(&drives, &num_drives); + if (ret < 0) { + osd_error("%s: get drive error", __func__); + return 1; + } + if (num_drives == 0) { + osd_error("%s: no drives", __func__); + return 1; + } + + ret = posix_memalign(&buf, 4096, 4096); + if (ret < 0) { + osd_error_errno("%s: posix_memasign", __func__); + return 1; + } + + for (i=0; i= 0; shift--) { + + osd_command_set_inquiry(&command, 0, 80); + command.indata = (uint8_t *) buf + (1 << shift); + command.inlen_alloc = 512; + + printf("buf %lx len %zu\n", (uintptr_t) command.indata, + command.inlen_alloc); + ret = osd_submit_command(fd, &command); + if (ret < 0) { + osd_error_errno("%s: osd_submit_command failed", + __func__); + continue; + } + + ret = osd_wait_this_response(fd, &command); + if (ret < 0) { + osd_error_errno( + "%s: osd_wait_this_response failed", + __func__); + continue; + } + + if (command.status != 0) { + osd_error("%s: status %d", __func__, + command.status); + continue; + } + } + + close(fd); + } + + osd_free_drive_list(drives, num_drives); + + return 0; +} diff --git a/osd-initiator/tests/atomic.c b/osd-initiator/tests/atomic.c new file mode 100644 index 0000000..d3ec868 --- /dev/null +++ b/osd-initiator/tests/atomic.c @@ -0,0 +1,267 @@ +/* + * Test atomics, with timing. + * + * Copyright (C) 2007 OSD Team + * + * 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 "osd-util/osd-util.h" +#include "command.h" +#include "device.h" +#include "drivelist.h" +#include "sync.h" + +static void fa_test(int fd, uint64_t pid) +{ + int i, ret; + uint64_t start, end, delta; + double mu, stdev; + double *v; + struct osd_command command; + uint64_t outbuf[1], inbuf[1]; + uint64_t oid = USEROBJECT_OID_LB; + /* const int iter = 10000; */ + const int iter = 1; + + v = malloc(iter * sizeof(*v)); + if (!v) + osd_error_fatal("out of memory"); + + osd_command_set_create(&command, pid, oid, 1); + ret = osd_submit_and_wait(fd, &command); + assert(ret == 0); + + /* len must be uint64_t, offset is where the results go */ + memset(&command, 0, sizeof(command)); + osd_command_set_fa(&command, pid, oid, 8, 0); + set_htonll((uint8_t *)outbuf, 1); + command.outdata = outbuf; + command.outlen = sizeof(outbuf); + command.indata = inbuf; + command.inlen_alloc = sizeof(inbuf); + + /* warm up */ + for (i=0; i<10; i++) { + inbuf[0] = 0xf000000000000000; + ret = osd_submit_and_wait(fd, &command); + assert(ret == 0); + assert(get_ntohll((uint8_t *)inbuf) == (uint64_t) (i)); + } + + for (i=0; i + * + * 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 "osd-util/osd-util.h" +#include "command.h" +#include "device.h" +#include "drivelist.h" +#include "sync.h" + +static void attr_test(int fd, uint64_t pid) +{ + int i, j, ret; + uint64_t start, end, delta; + double mu, stdev; + double *v; + struct osd_command command; + uint64_t oid = USEROBJECT_OID_LB; + /* const int iter = 10000; */ + const int iter = 1; + const uint32_t page = LUN_PG_LB + 27; + char bufs[3][20]; + const struct attribute_list attrs[] = { + { .type = ATTR_SET, .page = page, .number = 3, .val = bufs[0], + .len = 2 }, + { .type = ATTR_SET, .page = page, .number = 5, .val = bufs[1], + .len = 4 }, + { .type = ATTR_SET, .page = page, .number = 8, .val = bufs[2], + .len = 6 }, + }; + const int numattrs = sizeof(attrs)/sizeof(attrs[0]); + + strcpy(bufs[0], "a"); + strcpy(bufs[1], "bcd"); + strcpy(bufs[2], "efghi"); + + v = malloc(iter * sizeof(*v)); + if (!v) + osd_error_fatal("out of memory"); + + osd_command_set_create(&command, pid, oid, 1); + ret = osd_submit_and_wait(fd, &command); + assert(ret == 0); + + /* toss in some attrs */ + osd_command_set_set_attributes(&command, pid, oid); + osd_command_attr_build(&command, attrs, numattrs); + ret = osd_submit_and_wait(fd, &command); + assert(ret == 0); + osd_command_attr_free(&command); + + /* the get command */ + osd_command_set_get_attributes(&command, pid, oid); + osd_command_attr_all_build(&command, page); + + /* warm up */ + for (i=0; i<10; i++) { + ret = osd_submit_and_wait(fd, &command); + assert(ret == 0); + } + + for (i=0; i +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "osd-util/osd-util.h" +#include "osd-util/bsg.h" + +/* + * Be sure to pick the disk correctly. Write test will destroy it. + */ +#define BLOCK_DEV "/dev/sdb" + +/* + * Figure out /dev/bsg/... name from block device using sysfs. + */ +static const char *bsg_dev(const char *block_dev) +{ + const char *sd = block_dev + strlen("/dev/"); + char s[1024], t[1024], *cp; + static char *u = NULL; + int ret; + + if (u == NULL) { + sprintf(s, "/sys/block/%s/queue/bsg", sd); + ret = readlink(s, t, sizeof(t)); + if (ret < 0) + osd_error_fatal("%s: readlink %s", __func__, s); + t[ret] = 0; /* dumb readlink api */ + cp = strrchr(t, '/') + 1; + u = malloc(strlen("/dev/bsg/") + strlen(cp) + 1); + sprintf(u, "/dev/bsg/%s", cp); + } + return u; +} + +static double *b; +static void *buf; +static int pbsg_parallelism = 1; + +static void block_write_bw(int iters, int bursts __attribute__((unused)), + int sz) +{ + int i, ret, fd; + uint64_t start, end; + + fd = open(BLOCK_DEV, O_WRONLY | O_DIRECT); + if (fd < 0) + osd_error_fatal("%s: cannot open %s", __func__, BLOCK_DEV); + + /* warm up */ + for (i=0; i<5; i++) { + ret = pwrite(fd, buf, sz, 0); + assert(ret == sz); + } + + for (i=0; i> 8) & 0xff; + cdb[8] = blocks & 0xff; + + memset(&sg, 0, sizeof(sg)); + sg.interface_id = 'S'; + sg.cmd_len = sizeof(cdb); + sg.cmdp = cdb; + sg.dxfer_direction = SG_DXFER_TO_DEV; + sg.dxfer_len = sz; + sg.dxferp = buf; + sg.timeout = 30000; /* 30 sec */ + + /* warm up */ + for (i=0; i<5; i++) { + ret = ioctl(fd, SG_IO, &sg); + assert(ret == 0); + assert((sg.status & 0x7e) == 0 && sg.host_status == 0 && + (sg.driver_status & 0xf) == 0); + } + + for (i=0; i> 8) & 0xff; + cdb[8] = blocks & 0xff; + + memset(&sg, 0, sizeof(sg)); + sg.interface_id = 'S'; + sg.cmd_len = sizeof(cdb); + sg.cmdp = cdb; + sg.dxfer_direction = SG_DXFER_FROM_DEV; + sg.dxfer_len = sz; + sg.dxferp = buf; + sg.timeout = 30000; /* 30 sec */ + + /* warm up */ + for (i=0; i<5; i++) { + ret = ioctl(fd, SG_IO, &sg); + assert(ret == 0); + assert((sg.status & 0x7e) == 0 && sg.host_status == 0 && + (sg.driver_status & 0xf) == 0); + } + + for (i=0; i> 8) & 0xff; + cdb[8] = blocks & 0xff; + + memset(&bsg, 0, sizeof(bsg)); + bsg.guard = 'Q'; + bsg.request_len = sizeof(cdb); + bsg.request = (uint64_t) (uintptr_t) cdb; + bsg.dout_xfer_len = sz; + bsg.dout_xferp = (uint64_t) (uintptr_t) buf; + bsg.timeout = 30000; /* 30 sec */ + + /* warm up */ + for (i=0; i<5; i++) { + ret = ioctl(fd, SG_IO, &bsg); + assert(ret == 0); + assert(bsg.device_status == 0); + } + + for (i=0; i> 8) & 0xff; + cdb[8] = blocks & 0xff; + + memset(&bsg, 0, sizeof(bsg)); + bsg.guard = 'Q'; + bsg.request_len = sizeof(cdb); + bsg.request = (uint64_t) (uintptr_t) cdb; + bsg.din_xfer_len = sz; + bsg.din_xferp = (uint64_t) (uintptr_t) buf; + bsg.timeout = 30000; /* 30 sec */ + + /* warm up */ + for (i=0; i<5; i++) { + ret = ioctl(fd, SG_IO, &bsg); + assert(ret == 0); + assert(bsg.device_status == 0 && bsg.din_resid == 0); + } + + for (i=0; i> 8) & 0xff; + cdb[8] = blocks & 0xff; + + memset(&bsg, 0, sizeof(bsg)); + bsg.guard = 'Q'; + bsg.request_len = sizeof(cdb); + bsg.request = (uint64_t) (uintptr_t) cdb; + bsg.dout_xfer_len = sz; + bsg.dout_xferp = (uint64_t) (uintptr_t) buf; + bsg.timeout = 30000; /* 30 sec */ + + /* this many per timed burst */ + iters /= bursts; + + /* warm up */ + for (j=0; j<5; j++) { + ret = ioctl(fd, SG_IO, &bsg); + assert(ret == 0); + assert(bsg.device_status == 0); + } + + for (k=0; k= pbsg_parallelism) { + /* resubmit */ + bsg.usr_ptr = j; + ret = write(fd, &bsg, sizeof(bsg)); + assert(ret == sizeof(bsg)); + } + } + rdtsc(end); + b[k] = (double) (end - start) / iters; + } + + close(fd); +} + +static void pbsg_read_bw(int iters, int bursts, int sz) +{ + int i, j, k, ret, fd; + uint64_t start, end; + uint8_t cdb[10]; + uint16_t blocks; + struct sg_io_v4 bsg, bsgo; + const char *s = bsg_dev(BLOCK_DEV); + + fd = open(s, O_RDWR); + if (fd < 0) + osd_error_fatal("%s: cannot open %s", __func__, s); + + memset(cdb, 0, sizeof(cdb)); + cdb[0] = 0x28; /* READ_10 */ + assert(sz <= 65535 * 512); + assert((sz & 511) == 0); + blocks = sz / 512; + cdb[7] = (blocks >> 8) & 0xff; + cdb[8] = blocks & 0xff; + + memset(&bsg, 0, sizeof(bsg)); + bsg.guard = 'Q'; + bsg.request_len = sizeof(cdb); + bsg.request = (uint64_t) (uintptr_t) cdb; + bsg.din_xfer_len = sz; + bsg.din_xferp = (uint64_t) (uintptr_t) buf; + bsg.timeout = 30000; /* 30 sec */ + + /* this many per timed burst */ + iters /= bursts; + + /* warm up */ + for (i=0; i<5; i++) { + ret = ioctl(fd, SG_IO, &bsg); + assert(ret == 0); + assert(bsg.device_status == 0 && bsg.din_resid == 0); + } + + for (k=0; k= pbsg_parallelism) { + /* resubmit */ + bsg.usr_ptr = j; + ret = write(fd, &bsg, sizeof(bsg)); + assert(ret == sizeof(bsg)); + } + } + rdtsc(end); + b[k] = (double) (end - start) / iters; + } + + close(fd); +} + +static void *pagealign(void *b) +{ + int pgsz = getpagesize(); + void *x = (void *)(((unsigned long)(b) + pgsz - 1) & ~(pgsz - 1)); + return x; +} + +static void usage(void) +{ + fprintf(stderr, "Usage: %s [--type={block|sg|bsg|pbsg}] [--parallel=N] [--latency]\n", + osd_get_progname()); + exit(1); +} + +int main(int argc, char *const *argv) +{ + int sz, i; + void *x; + double mhz = get_mhz(); + double mu, sd; + const char *type = "block"; + void (*write_bw)(int iters, int bursts, int sz); + void (*read_bw)(int iters, int bursts, int sz); + struct option opt[] = { + { + .name = "type", + .has_arg = 1, + .val = 't', + }, + { + .name = "parallel", + .has_arg = 1, + .val = 'p', + }, + { + .name = "latency", + .val = 'l', + }, + { .name = NULL } + }; + + int maxsize, iters, bursts; + int latency = 0; + + osd_set_progname(argc, argv); + + for (;;) { + i = getopt_long(argc, argv, "t:p:l", opt, NULL); + switch (i) { + case -1: + break; + case '?': + usage(); + break; + case 't': + type = optarg; + break; + case 'p': + pbsg_parallelism = atoi(optarg); + break; + case 'l': + latency = 1; + break; + } + if (i == -1) + break; + } + + if (!strcmp(type, "block")) { + write_bw = block_write_bw; + read_bw = block_read_bw; + } else if (!strcmp(type, "sg")) { + write_bw = sg_write_bw; + read_bw = sg_read_bw; + } else if (!strcmp(type, "bsg")) { + write_bw = bsg_write_bw; + read_bw = bsg_read_bw; + } else if (!strcmp(type, "pbsg")) { + write_bw = pbsg_write_bw; + read_bw = pbsg_read_bw; + } else + usage(); + + if (!strcmp(type, "pbsg")) + printf("# %s type %s %d\n", osd_get_progname(), type, pbsg_parallelism); + else + printf("# %s type %s\n", osd_get_progname(), type); + + if (latency) { + maxsize = 10 * 1024; + iters = 1000; + } else { + maxsize = 512 * 1024; + iters = 100; /*** XXX: bigger is better for reporting */ + } + + bursts = iters; + if (!strcmp(type, "pbsg")) + bursts = 10; + + b = malloc(iters * sizeof(*b)); + x = malloc(maxsize + getpagesize()); + if (!b || !x) + osd_error_fatal("%s: out of memory", __func__); + + buf = pagealign(x); + memset(buf, 'A', maxsize); + + /* cannot make use of more than this */ + if (pbsg_parallelism > bursts) + pbsg_parallelism = bursts; + + if (latency) { + int step = 16; + for (sz = 0; sz <= maxsize; sz += step) { + if (!strcmp(type, "block")) /* O_DIRECT */ + if (sz == 0 || (sz % 512) != 0) + continue; + write_bw(iters, bursts, sz); + for (i = 0; i < bursts; i++) + b[i] /= mhz; /* time in us */ + mu = mean(b, bursts); + sd = stddev(b, mu, bursts); + printf("write\t %3d %7.3lf +- %7.3lf\n", sz, mu, sd); + } + + printf("\n\n"); + + for (sz = 0; sz <= maxsize; sz += step) { + if (!strcmp(type, "block")) /* O_DIRECT */ + if (sz == 0 || (sz % 512) != 0) + continue; + read_bw(iters, bursts, sz); + for (i = 0; i < bursts; i++) + b[i] /= mhz; + mu = mean(b, bursts); + sd = stddev(b, mu, bursts); + printf("read\t %3d %7.3lf +- %7.3lf\n", sz, mu, sd); + } + } else { + for (sz = 4096; sz <= maxsize; sz += 4096) { + write_bw(iters, bursts, sz); + for (i = 0; i < bursts; i++) + b[i] = sz / (b[i] / mhz); /* bw in MB/s */ + mu = mean(b, bursts); + sd = stddev(b, mu, bursts); + printf("write\t %3d %7.3lf +- %7.3lf\n", sz>>10, + mu, sd); + } + + printf("\n\n"); + + for (sz = 4096; sz <= maxsize; sz += 4096) { + read_bw(iters, bursts, sz); + for (i = 0; i < bursts; i++) + b[i] = sz / (b[i] / mhz); + mu = mean(b, bursts); + sd = stddev(b, mu, bursts); + printf("read\t %3d %7.3lf +- %7.3lf\n", + sz>>10, mu, sd); + } + } + + free(b); + free(x); + return 0; +} + diff --git a/osd-initiator/tests/contention.c b/osd-initiator/tests/contention.c new file mode 100644 index 0000000..032cc98 --- /dev/null +++ b/osd-initiator/tests/contention.c @@ -0,0 +1,1161 @@ +/* + * contention tests + * + * Copyright (C) 2007 OSD Team + * + * 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 . + */ + + +/* + * string for test, + * clean code, + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "osd-util/osd-util.h" +#include "command.h" +#include "device.h" +#include "drivelist.h" +#include "sync.h" + +static int rank, numprocs; +static const int WORK_MAX = 600; +static const int test_lb = 1; +static const int test_ub = 24; + +static void usage(void) +{ + osd_error_fatal("Usage: ./%s {test}\n" + "\t{test} must belong to set [%d, %d]", + osd_get_progname(), test_lb, test_ub); +} + +static void println(const char *fmt, ...) +{ + va_list ap; + + fprintf(stdout, "%d: ", rank); + va_start(ap, fmt); + vfprintf(stdout, fmt, ap); + va_end(ap); + fprintf(stdout, ".\n"); +} + +static void hires_pause(uint64_t delta) +{ + uint64_t start, end, clk; + + rdtsc(start); + end = start + delta; + rdtsc(clk); + while (clk < end) + rdtsc(clk); +} + +static inline void local_work(void) +{ + usleep(1 + (int)(rand()*WORK_MAX/(RAND_MAX+1.))); +} + +static void remote_work(int fd, uint64_t pid, uint64_t oid) +{ + int ret; + struct osd_command cmd; + struct attribute_list attr = { + .type = ATTR_GET, + .page = USER_INFO_PG, + .number = UIAP_LOGICAL_LEN, + .len = 8, + }; + + osd_command_set_get_attributes(&cmd, pid, oid); + osd_command_attr_build(&cmd, &attr, 1); + ret = osd_submit_and_wait(fd, &cmd); + assert(ret == 0); + osd_command_attr_resolve(&cmd); +} + +/* + * return values + * -1: error + * 0: cas failed + * 1: cas succeeded + */ +static int cas(int fd, struct osd_command *cmd, uint64_t pid, uint64_t oid, + uint64_t cmp, uint64_t swap) +{ + int ret; + uint64_t outbuf[2], inbuf[1]; + + osd_command_set_cas(cmd, pid, oid, 8, 0); + cmd->outdata = outbuf; + cmd->outlen = sizeof(outbuf); + cmd->indata = inbuf; + cmd->inlen_alloc = sizeof(inbuf); + + inbuf[0] = 0xdeadbeef; + set_htonll((uint8_t *)&outbuf[0], cmp); + set_htonll((uint8_t *)&outbuf[1], swap); + ret = osd_submit_and_wait(fd, cmd); + if (ret != 0) + return -1; + if (get_ntohll((uint8_t *)inbuf) == get_ntohll((uint8_t *)outbuf)) + return 1; + else + return 0; +} + + +static int fa(int fd, struct osd_command *cmd, uint64_t pid, uint64_t oid, + int64_t add, int64_t *orig) +{ + int ret; + uint64_t outbuf[1], inbuf[1]; + + osd_command_set_fa(cmd, pid, oid, 8, 0); + cmd->outdata = outbuf; + cmd->outlen = sizeof(outbuf); + cmd->indata = inbuf; + cmd->inlen_alloc = sizeof(inbuf); + + inbuf[0] = 0xdeadbeef; + set_htonll((uint8_t *)&outbuf[0], add); + ret = osd_submit_and_wait(fd, cmd); + if (ret != 0) + return -1; + *orig = get_ntohll((uint8_t *)inbuf); + return 0; +} + + +static void busy_wait(int fd, uint64_t pid, uint64_t oid, const int numlocks, + const int dowork) +{ + int i; + int ret; + int reqs, attempts; + int locks = numlocks; + double *global_req, *global_att; + int *gather; + uint64_t start, end; + double mu_req, sd_req, mu_att, sd_att; + double time, mhz = get_mhz(); + struct osd_command cmd; + + if (rank == 0) { + gather = calloc(numprocs, sizeof(*gather)); + global_att = calloc(numprocs, sizeof(*global_att)); + global_req = calloc(numprocs, sizeof(*global_req)); + if (!gather || !global_req || !global_att) { + osd_error_xerrno(-ENOMEM, "out of memory"); + exit(1); + } + + } + + if (dowork) + osd_srand(); + + MPI_Barrier(MPI_COMM_WORLD); + if (rank == 0) + rdtsc(start); + + reqs = 0, attempts = 0; + while (locks) { + ret = cas(fd, &cmd, pid, oid, 0, 1); /* lock */ + ++attempts; + ++reqs; + if (ret == 1) { + --locks; + if (dowork == 1) + local_work(); + else if (dowork == 2) + remote_work(fd, pid, oid); + ret = cas(fd, &cmd, pid, oid, 1, 0); /* unlock */ + assert(ret == 1); + ++reqs; + } else if (ret == 0) { + continue; + } else { + osd_error_fatal("cas error"); + } + } + + MPI_Barrier(MPI_COMM_WORLD); + if (rank == 0) { + rdtsc(end); + time = ((double)(end-start))/mhz; + } + + ret = MPI_Gather(&reqs, 1, MPI_INT, gather, 1, MPI_INT, 0, + MPI_COMM_WORLD); + if (ret != 0) { + osd_error("MPI_Gather failed"); + exit(1); + } + if (rank == 0) + for (i = 0; i < numprocs; i++) + global_req[i] = (double)gather[i]; + ret = MPI_Gather(&attempts, 1, MPI_INT, gather, 1, MPI_INT, 0, + MPI_COMM_WORLD); + if (ret != 0) { + osd_error("MPI_Gather failed"); + exit(1); + } + if (rank == 0) + for (i = 0; i < numprocs; i++) + global_att[i] = (double)gather[i]; + + if (rank != 0) + return; + mu_req = mean(global_req, numprocs); + sd_req = stddev(global_req, mu_req, numprocs); + mu_att = mean(global_att, numprocs); + sd_att = stddev(global_att, mu_att, numprocs); + + for (i = 0; i < numprocs; i++) + printf("global_reqs[%u] = %lf, global_att[%u] = %lf\n", i, + global_req[i], i, global_att[i]); + printf("Numlocks: %u \n" + "Numprocs: %u \n" + "Work: %u \n" + "Total requests %7.3lf \n" + "Total attempts %7.3lf \n" + "Per node attempts: %6.3lf +- %6.3lf \n" + "Efficiency: %7.3lf \n" + "Time %7.3lf us\n" + "Throughput: %7.3lf locks/sec\n", + numlocks, numprocs, dowork, mu_req*numprocs, mu_att*numprocs, + mu_att, sd_att, numlocks*2/mu_req, time, + numlocks*numprocs*1e6/time); + + free(gather); + free(global_req); + free(global_att); +} + + +static void buggy(int fd, uint64_t pid, uint64_t oid, int numlocks, + int dowork, int *my_att, int *my_req, double *latency) +{ + int ret; + int reqs, attempts; + int locks = 0; + int64_t num_waiters, queued; + int first_attempt; + double rtt, mhz = get_mhz(); + uint64_t start, end, lat_beg, lat_end; + struct osd_command cmd; + + reqs = 0, attempts = 0, num_waiters = 0, queued = 0, first_attempt = 1; + while (locks < numlocks) { + if (first_attempt) { + rdtsc(lat_beg); + first_attempt = 0; + } + ret = cas(fd, &cmd, pid, oid, 0, 1); /* lock */ + ++attempts; + ++reqs; + if (ret == 1) { + rdtsc(lat_end); + latency[locks] = ((double)(lat_end - lat_beg)/mhz); + first_attempt = 1; + if (queued) { + ret = fa(fd, &cmd, pid, oid, -1, + &num_waiters); + if (ret == -1) + osd_error_fatal("fa failed"); + ++reqs; + --queued; + } + ++locks; + if (dowork == 1) + local_work(); + else if (dowork == 2) + remote_work(fd, pid, oid); + ret = cas(fd, &cmd, pid, oid, 1, 0); /* unlock */ + assert(ret == 1); + ++reqs; + } else if (ret == 0) { + rdtsc(start); + ret = fa(fd, &cmd, pid, oid, 1, &num_waiters); + rdtsc(end); + if (ret == -1) + osd_error_fatal("fa error"); + ++reqs; + ++queued; + if (num_waiters == 0) + continue; + + /* speculative delay estimation */ + rtt = ((double)(end-start))/mhz; + //println("num waiters %u rtt %7.3lf", num_waiters, rtt); + if (dowork == 0) { + usleep((uint64_t)((num_waiters-1)*(rtt) + + rtt/2)); + } else if (dowork == 1) { + usleep((uint64_t)((num_waiters-1)*(rtt) + + rtt/2 + WORK_MAX/2.)); + } else if (dowork == 2) { + usleep((uint64_t)((num_waiters)*(rtt) + + rtt/2)); + } + continue; + } else { + osd_error_fatal("cas error"); + } + } + *my_att = attempts; + *my_req = reqs; +} + +static void queueback(int fd, uint64_t pid, uint64_t oid, int numlocks, + int dowork, int *my_att, int *my_req, double *latency) +{ + int ret; + int reqs, attempts; + int locks = 0; + int64_t num_waiters, queued; + int first_attempt; + double rtt, pause, mhz = get_mhz(); + uint64_t start, end, lat_beg, lat_end; + struct osd_command cmd; + + reqs = 0, attempts = 0, num_waiters = 0, queued = 0, first_attempt = 1; + while (locks < numlocks) { + if (first_attempt) { + rdtsc(lat_beg); + first_attempt = 0; + } + ret = cas(fd, &cmd, pid, oid, 0, 1); /* lock */ + ++attempts; + ++reqs; + if (ret == 1) { + rdtsc(lat_end); + latency[locks] = ((double)(lat_end - lat_beg)/mhz); + first_attempt = 1; + if (queued) { + ret = fa(fd, &cmd, pid, oid, -queued, + &num_waiters); + if (ret == -1) + osd_error_fatal("fa failed"); + ++reqs; + queued = 0; + } + ++locks; + if (dowork == 1) + local_work(); + else if (dowork == 2) + remote_work(fd, pid, oid); + ret = cas(fd, &cmd, pid, oid, 1, 0); /* unlock */ + assert(ret == 1); + ++reqs; + } else if (ret == 0) { + rdtsc(start); + ret = fa(fd, &cmd, pid, oid, 1, &num_waiters); + rdtsc(end); + if (ret == -1) + osd_error_fatal("fa error"); + ++reqs; + ++queued; + + /* speculative delay estimation */ + rtt = ((double)(end-start))/mhz; + //println("num waiters %u rtt %7.3lf", num_waiters, rtt); + if (num_waiters == 0) { + if (dowork != 2) + hires_pause(rtt*mhz/2.); + continue; + } + if (dowork == 0) { + pause = (num_waiters - .5)*(rtt); + } else if (dowork == 1) { + pause = (num_waiters - .5)*(rtt) + WORK_MAX/2.; + } else if (dowork == 2) { + pause = (num_waiters + .5)*(rtt); + } + usleep((uint64_t)(pause)); + continue; + } else { + osd_error_fatal("cas error"); + } + } + *my_att = attempts; + *my_req = reqs; +} + + +static void fixed_spin(int fd, uint64_t pid, uint64_t oid, int numlocks, + int dowork, int *my_att, int *my_req, double *latency) +{ + int ret; + int reqs, attempts; + int locks = 0; + int64_t num_waiters, queued; + int first_attempt; + double rtt, pause, mhz = get_mhz(); + uint64_t start, end, lat_beg, lat_end; + struct osd_command cmd; + + reqs = 0, attempts = 0, num_waiters = 0, queued = 0, first_attempt = 1; + while (locks < numlocks) { + if (first_attempt) { + rdtsc(lat_beg); + first_attempt = 0; + } + ret = cas(fd, &cmd, pid, oid, 0, 1); /* lock */ + ++attempts; + ++reqs; + if (ret == 1) { + rdtsc(lat_end); + latency[locks] = ((double)(lat_end - lat_beg)/mhz); + first_attempt = 1; + if (queued) { + ret = fa(fd, &cmd, pid, oid, -1, &num_waiters); + if (ret == -1) + osd_error_fatal("fa failed"); + ++reqs; + queued = 0; + } + ++locks; + if (dowork == 1) + local_work(); + else if (dowork == 2) + remote_work(fd, pid, oid); + ret = cas(fd, &cmd, pid, oid, 1, 0); /* unlock */ + assert(ret == 1); + ++reqs; + } else if (ret == 0) { + if (!queued) { + rdtsc(start); + ret = fa(fd, &cmd, pid, oid, 1, &num_waiters); + rdtsc(end); + if (ret == -1) + osd_error_fatal("fa error"); + ++reqs; + queued = 1; + } + if (num_waiters == 0) + continue; + + /* speculative delay estimation */ + rtt = ((double)(end-start))/mhz; + /* println("num waiters %u rtt %7.3lf", num_waiters, rtt); */ + if (dowork == 0) { + pause = (num_waiters - .5)*(rtt); + } else if (dowork == 1) { + pause = (num_waiters - .5)*(rtt) + WORK_MAX/2.; + } else if (dowork == 2) { + pause = (num_waiters + .5)*(rtt); + } + usleep((uint64_t)pause); + continue; + } else { + osd_error_fatal("cas error"); + } + } + *my_att = attempts; + *my_req = reqs; +} + + +static void expwait(int fd, uint64_t pid, uint64_t oid, int numlocks, + int dowork, int *my_att, int *my_req, double *latency) +{ + int ret; + int reqs, attempts; + int locks = 0; + int64_t num_waiters, queued; + int start_clk; + double rtt, pause, mhz = get_mhz(); + uint64_t start, end, lat_beg, lat_end; + struct osd_command cmd; + + reqs = 0, attempts = 0, num_waiters = 0, queued = 0, start_clk = 1; + while (locks < numlocks) { + if (start_clk) { + rdtsc(lat_beg); + start_clk = 0; + } + rdtsc(start); + ret = cas(fd, &cmd, pid, oid, 0, 1); /* lock */ + rdtsc(end); + ++attempts; + ++reqs; + if (ret == 1) { + rdtsc(lat_end); + latency[locks] = ((double)(lat_end - lat_beg)/mhz); + start_clk = 1; + ++locks; + if (dowork == 1) + local_work(); + else if (dowork == 2) + remote_work(fd, pid, oid); + ret = cas(fd, &cmd, pid, oid, 1, 0); /* unlock */ + assert(ret == 1); + ++reqs; + } else if (ret == 0) { + rtt = ((double)(end-start))/mhz; + + if (dowork == 0) { + pause = rtt; + } else if (dowork == 1) { + pause = (rtt + WORK_MAX/2.); + } else if (dowork == 2) { + pause = 2*rtt; + } + hires_pause((uint64_t)(pause*mhz)); + continue; + } else { + osd_error_fatal("cas error"); + } + } + *my_att = attempts; + *my_req = reqs; +} + + +/* + * trancated binary exponential backoff algo + */ +static void beb(int fd, uint64_t pid, uint64_t oid, int numlocks, + const int dowork, int *my_att, int *my_reqs, double *latency) +{ + int ret; + int reqs, attempts; + uint64_t start, end; + double rtt, pause, mhz = get_mhz(); + int64_t backoff, failures; + + struct osd_command cmd; + + reqs = 0, attempts = 0, failures = 0, pause = 0.; + while (numlocks) { + rdtsc(start); + ret = cas(fd, &cmd, pid, oid, 0, 1); /* lock */ + rdtsc(end); + ++attempts; + ++reqs; + if (ret == 1) { + failures = 0; + --numlocks; + if (dowork == 1) + local_work(); + else if (dowork == 2) + remote_work(fd, pid, oid); + ret = cas(fd, &cmd, pid, oid, 1, 0); /* unlock */ + assert(ret == 1); + ++reqs; + } else if (ret == 0) { + if (failures < 5) + ++failures; + rtt = ((double)(end-start))/mhz; + backoff = (1UL<< failures) * (rand()/(RAND_MAX+1.)); + if (dowork == 0) { + pause = backoff*rtt/2.; + } else if (dowork == 1) { + pause = backoff*(rtt/2. + WORK_MAX/2.); + } else if (dowork == 2) { + pause = backoff*rtt; + } + usleep((uint64_t)pause); + continue; + } else { + osd_error_fatal("cas error"); + } + } + + *my_att = attempts; + *my_reqs = reqs; +} + + +static void spec_idle(int fd, uint64_t pid, uint64_t oid, int numlocks, + int dowork, int scheme) +{ + int i; + int ret; + double *global_req, *global_att, *global_lat, *latency; + int *gather; + int reqs, attempts; + uint64_t exp_beg, exp_end; + double mu_req, sd_req, mu_att, sd_att; + double exp_time, mhz = get_mhz(); + struct osd_command cmd; + int64_t num_waiters; + + if (rank == 0) { + gather = calloc(numprocs, sizeof(*gather)); + global_att = calloc(numprocs, sizeof(*global_att)); + global_req = calloc(numprocs, sizeof(*global_req)); + global_lat = calloc(numprocs*3, sizeof(*global_lat)); + if (!gather || !global_req || !global_att || !global_lat) { + osd_error_xerrno(-ENOMEM, "out of memory"); + exit(1); + } + + /* initialize lock counter */ + num_waiters = 0xdeadbeef; + ret = fa(fd, &cmd, pid, oid, 0, &num_waiters); + if (ret == -1) + osd_error_fatal("fa failed"); + if (num_waiters != 0) { + ret = fa(fd, &cmd, pid, oid, -1*num_waiters, + &num_waiters); + if (ret == -1) + osd_error_fatal("fa failed"); + } + } + + latency = calloc(numlocks, sizeof(*latency)); + if (!latency) { + osd_error_xerrno(-ENOMEM, "out of memory"); + exit(1); + } + osd_srand(); /* seed PRNG */ + + MPI_Barrier(MPI_COMM_WORLD); + if (rank == 0) + rdtsc(exp_beg); + + if (scheme == 1) { + buggy(fd, pid, oid, numlocks, dowork, &attempts, &reqs, + latency); + } else if (scheme == 2) { + queueback(fd, pid, oid, numlocks, dowork, &attempts, &reqs, + latency); + } else if (scheme == 3) { + fixed_spin(fd, pid, oid, numlocks, dowork, &attempts, &reqs, + latency); + } else if (scheme == 4) { + expwait(fd, pid, oid, numlocks, dowork, &attempts, &reqs, + latency); + } else if (scheme == 5) { + beb(fd, pid, oid, numlocks, dowork, &attempts, &reqs, latency); + } + + + MPI_Barrier(MPI_COMM_WORLD); + if (rank == 0) { + rdtsc(exp_end); + exp_time = ((double)(exp_end - exp_beg))/mhz; + } + + ret = MPI_Gather(&reqs, 1, MPI_INT, gather, 1, MPI_INT, 0, + MPI_COMM_WORLD); + if (ret != 0) { + osd_error("MPI_Gather failed"); + exit(1); + } + if (rank == 0) + for (i = 0; i < numprocs; i++) + global_req[i] = (double)gather[i]; + ret = MPI_Gather(&attempts, 1, MPI_INT, gather, 1, MPI_INT, 0, + MPI_COMM_WORLD); + if (ret != 0) { + osd_error("MPI_Gather failed"); + exit(1); + } + if (rank == 0) + for (i = 0; i < numprocs; i++) + global_att[i] = (double)gather[i]; + + double stats[3]; + stats[0] = median(latency, numlocks); + stats[1] = mean(latency, numlocks); + stats[2] = stddev(latency, stats[0], numlocks); + ret = MPI_Gather(stats, 3, MPI_DOUBLE, global_lat, 3, MPI_DOUBLE, 0, + MPI_COMM_WORLD); + if (ret != 0) { + osd_error("MPI_Gather failed"); + exit(1); + } + + if (rank != 0) + return; + mu_req = mean(global_req, numprocs); + sd_req = stddev(global_req, mu_req, numprocs); + mu_att = mean(global_att, numprocs); + sd_att = stddev(global_att, mu_att, numprocs); + + for (i = 0; i < numprocs; i++) + println("global_reqs[%u] = %lf, global_att[%u] = %lf\n" + "lat[%d]: %8.3lf, %8.3lf +- %8.3lf", i, global_req[i], + i, global_att[i], i, global_lat[3*i], global_lat[3*i+1], + global_lat[3*i+2]); + printf("Numlocks: %u \n" + "Numprocs: %u \n" + "Work: %u \n" + "Total requests %7.3lf \n" + "Total attempts %7.3lf \n" + "Per node reqs: %6.3lf +- %6.3lf \n" + "Per node attempts: %6.3lf +- %6.3lf \n" + "Efficiency: %7.3lf \n" + "Time %7.3lf us\n" + "Throughput: %7.3lf locks/sec\n", + numlocks, numprocs, dowork, mu_req*numprocs, mu_att*numprocs, + mu_req, sd_req, mu_att, sd_att, numlocks*2/mu_req, exp_time, + numlocks*numprocs*1e6/exp_time); + + free(gather); + free(global_req); + free(global_att); +} + + +static void lin_backoff(int fd, uint64_t pid, uint64_t oid, + const int numlocks, const int dowork) +{ + int i; + int ret; + int reqs, attempts; + int locks = numlocks; + double *global_req = NULL, *global_att = NULL; + int *gather = NULL; + uint64_t exp_beg=0, exp_end=0, start, end; + double mu_req, sd_req, mu_att, sd_att; + double rtt, exp_time, mhz = get_mhz(); + struct osd_command cmd; + int failures; + + if (rank == 0) { + gather = calloc(numprocs, sizeof(*gather)); + global_att = calloc(numprocs, sizeof(*global_att)); + global_req = calloc(numprocs, sizeof(*global_req)); + if (!gather || !global_req || !global_att) { + osd_error_xerrno(-ENOMEM, "out of memory"); + exit(1); + } + } + + osd_srand(); /* seed PRNG */ + + MPI_Barrier(MPI_COMM_WORLD); + if (rank == 0) + rdtsc(exp_beg); + + reqs = 0, attempts = 0, failures = 0; + while (locks) { + rdtsc(start); + ret = cas(fd, &cmd, pid, oid, 0, 1); /* lock */ + rdtsc(end); + ++attempts; + ++reqs; + if (ret == 1) { + --locks; + failures = 0; + if (dowork == 1) + local_work(); + else if (dowork == 2) + remote_work(fd, pid, oid); + ret = cas(fd, &cmd, pid, oid, 1, 0); /* unlock */ + assert(ret == 1); + ++reqs; + } else if (ret == 0) { + ++failures; + rtt = ((double)(end - start)/mhz); + usleep((uint64_t)failures*rtt/2.); + continue; + } else { + osd_error_fatal("cas error"); + } + } + + MPI_Barrier(MPI_COMM_WORLD); + if (rank == 0) { + rdtsc(exp_end); + exp_time = ((double)(exp_end - exp_beg))/mhz; + } + + ret = MPI_Gather(&reqs, 1, MPI_INT, gather, 1, MPI_INT, 0, + MPI_COMM_WORLD); + if (ret != 0) { + osd_error("MPI_Gather failed"); + exit(1); + } + if (rank == 0) + for (i = 0; i < numprocs; i++) + global_req[i] = (double)gather[i]; + ret = MPI_Gather(&attempts, 1, MPI_INT, gather, 1, MPI_INT, 0, + MPI_COMM_WORLD); + if (ret != 0) { + osd_error("MPI_Gather failed"); + exit(1); + } + if (rank == 0) + for (i = 0; i < numprocs; i++) + global_att[i] = (double)gather[i]; + + if (rank != 0) + return; + mu_req = mean(global_req, numprocs); + sd_req = stddev(global_req, mu_req, numprocs); + mu_att = mean(global_att, numprocs); + sd_att = stddev(global_att, mu_att, numprocs); + + for (i = 0; i < numprocs; i++) + printf("global_reqs[%u] = %lf, global_att[%u] = %lf\n", i, + global_req[i], i, global_att[i]); + printf("Numlocks: %u \n" + "Numprocs: %u \n" + "Work: %u \n" + "Total requests %7.3lf \n" + "Total attempts %7.3lf \n" + "Per node attempts: %6.3lf +- %6.3lf \n" + "Efficiency: %7.3lf \n" + "Time %7.3lf us\n" + "Throughput: %7.3lf locks/sec\n", + numlocks, numprocs, dowork, mu_req*numprocs, mu_att*numprocs, + mu_att, sd_att, numlocks*2/mu_req, exp_time, + numlocks*numprocs*1e6/exp_time); + + free(gather); + free(global_req); + free(global_att); +} + +int main(int argc, char *argv[]) +{ + int fd, ret, num_drives, i; + struct osd_drive_description *drives; + uint64_t pid = USEROBJECT_PID_LB, oid = USEROBJECT_OID_LB; + struct osd_command cmd; + int test; + + MPI_Init(&argc, &argv); + MPI_Comm_size(MPI_COMM_WORLD, &numprocs); + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + osd_set_progname(argc, argv); + + if (argc != 2) + usage(); + + test = strtol(argv[1], NULL, 10); + assert(test_lb <= test && test <= test_ub); + + ret = osd_get_drive_list(&drives, &num_drives); + if (ret < 0) { + osd_error("%s: get drive error", __func__); + return 1; + } + if (num_drives == 0) { + osd_error("%s: no drives", __func__); + return 1; + } + + i = 0; + //osd_debug("drive %s name %s", drives[i].chardev, drives[i].targetname); + fd = open(drives[i].chardev, O_RDWR); + if (fd < 0) { + osd_error_errno("%s: open %s", __func__, drives[i].chardev); + return 1; + } + osd_free_drive_list(drives, num_drives); + + + switch (test) { + case 1: /* No contention, no work */ + case 2: /* No contention, local work */ + case 3: /* No contention, remote work */ + if (rank == 0) { + osd_command_set_format_osd(&cmd, 1<<30); + ret = osd_submit_and_wait(fd, &cmd); + assert(ret == 0); + + osd_command_set_create_partition(&cmd, pid); + ret = osd_submit_and_wait(fd, &cmd); + assert(ret == 0); + + for (i = oid; (uint32_t)i < oid+numprocs; i++) { + osd_command_set_create(&cmd, pid, i, 1); + ret = osd_submit_and_wait(fd, &cmd); + assert(ret == 0); + } + } + if (test == 1) + busy_wait(fd, pid, oid+rank, 100, 0); + else if (test == 2) + busy_wait(fd, pid, oid+rank, 100, 1); + else if (test == 3) + busy_wait(fd, pid, oid+rank, 100, 2); + + if (rank == 0) { + for (i = oid; (uint32_t)i < oid+numprocs; i++) { + osd_command_set_remove(&cmd, pid, i); + ret = osd_submit_and_wait(fd, &cmd); + assert(ret == 0); + } + + osd_command_set_remove_partition(&cmd, pid); + ret = osd_submit_and_wait(fd, &cmd); + assert(ret == 0); + } + break; + case 4: /* contention no work */ + case 5: /* contention with local work */ + case 6: /* contention with remote work */ + if (rank == 0) { + osd_command_set_format_osd(&cmd, 1<<30); + ret = osd_submit_and_wait(fd, &cmd); + assert(ret == 0); + + osd_command_set_create_partition(&cmd, pid); + ret = osd_submit_and_wait(fd, &cmd); + assert(ret == 0); + + osd_command_set_create(&cmd, pid, oid, 1); + ret = osd_submit_and_wait(fd, &cmd); + assert(ret == 0); + } + if (test == 4) + busy_wait(fd, pid, oid, 100, 0); + else if (test == 5) + busy_wait(fd, pid, oid, 100, 1); + else if (test == 6) + busy_wait(fd, pid, oid, 100, 2); + if (rank == 0) { + osd_command_set_remove(&cmd, pid, oid); + ret = osd_submit_and_wait(fd, &cmd); + assert(ret == 0); + + osd_command_set_remove_partition(&cmd, pid); + ret = osd_submit_and_wait(fd, &cmd); + assert(ret == 0); + } + break; + case 7: /* Contention, lin backoff, no work */ + case 8: /* Contention, lin backoff, local work */ + case 9: /* Contention, lin backoff, remote work */ + if (rank == 0) { + osd_command_set_format_osd(&cmd, 1<<30); + ret = osd_submit_and_wait(fd, &cmd); + assert(ret == 0); + + osd_command_set_create_partition(&cmd, pid); + ret = osd_submit_and_wait(fd, &cmd); + assert(ret == 0); + + osd_command_set_create(&cmd, pid, oid, 1); + ret = osd_submit_and_wait(fd, &cmd); + assert(ret == 0); + } + + if (test == 7) + lin_backoff(fd, pid, oid, 100, 0); + else if (test == 8) + lin_backoff(fd, pid, oid, 100, 1); + else if (test == 9) + lin_backoff(fd, pid, oid, 100, 2); + + if (rank == 0) { + osd_command_set_remove(&cmd, pid, oid); + ret = osd_submit_and_wait(fd, &cmd); + assert(ret == 0); + + osd_command_set_remove_partition(&cmd, pid); + ret = osd_submit_and_wait(fd, &cmd); + assert(ret == 0); + } + break; + case 10: /* Contention, speculative idling, buggy, no work */ + case 11: /* Contention, speculative idling, buggy, local work */ + case 12: /* Contention, speculative idling, buggy, remote work */ + if (rank == 0) { + osd_command_set_format_osd(&cmd, 1<<30); + ret = osd_submit_and_wait(fd, &cmd); + assert(ret == 0); + + osd_command_set_create_partition(&cmd, pid); + ret = osd_submit_and_wait(fd, &cmd); + assert(ret == 0); + + osd_command_set_create(&cmd, pid, oid, 1); + ret = osd_submit_and_wait(fd, &cmd); + assert(ret == 0); + } + + if (test == 10) + spec_idle(fd, pid, oid, 100, 0, 1); + else if (test == 11) + spec_idle(fd, pid, oid, 100, 1, 1); + else if (test == 12) + spec_idle(fd, pid, oid, 100, 2, 1); + + if (rank == 0) { + osd_command_set_remove(&cmd, pid, oid); + ret = osd_submit_and_wait(fd, &cmd); + assert(ret == 0); + + osd_command_set_remove_partition(&cmd, pid); + ret = osd_submit_and_wait(fd, &cmd); + assert(ret == 0); + } + break; + case 13: /* Contention, speculative idling, queueback no work */ + case 14: /* Contention, speculative idling, queueback local work */ + case 15: /* Contention, speculative idling, queueback remote work */ + if (rank == 0) { + osd_command_set_format_osd(&cmd, 1<<30); + ret = osd_submit_and_wait(fd, &cmd); + assert(ret == 0); + + osd_command_set_create_partition(&cmd, pid); + ret = osd_submit_and_wait(fd, &cmd); + assert(ret == 0); + + osd_command_set_create(&cmd, pid, oid, 1); + ret = osd_submit_and_wait(fd, &cmd); + assert(ret == 0); + } + + if (test == 13) + spec_idle(fd, pid, oid, 100, 0, 2); + else if (test == 14) + spec_idle(fd, pid, oid, 100, 1, 2); + else if (test == 15) + spec_idle(fd, pid, oid, 100, 2, 2); + + if (rank == 0) { + osd_command_set_remove(&cmd, pid, oid); + ret = osd_submit_and_wait(fd, &cmd); + assert(ret == 0); + + osd_command_set_remove_partition(&cmd, pid); + ret = osd_submit_and_wait(fd, &cmd); + assert(ret == 0); + } + break; + case 16: /* Contention, speculative idling, fixed_spin no work */ + case 17: /* Contention, speculative idling, fixed_spin local work */ + case 18: /* Contention, speculative idling, fixed_spin remote work */ + if (rank == 0) { + osd_command_set_format_osd(&cmd, 1<<30); + ret = osd_submit_and_wait(fd, &cmd); + assert(ret == 0); + + osd_command_set_create_partition(&cmd, pid); + ret = osd_submit_and_wait(fd, &cmd); + assert(ret == 0); + + osd_command_set_create(&cmd, pid, oid, 1); + ret = osd_submit_and_wait(fd, &cmd); + assert(ret == 0); + } + + if (test == 16) + spec_idle(fd, pid, oid, 100, 0, 3); + else if (test == 17) + spec_idle(fd, pid, oid, 100, 1, 3); + else if (test == 18) + spec_idle(fd, pid, oid, 100, 2, 3); + + if (rank == 0) { + osd_command_set_remove(&cmd, pid, oid); + ret = osd_submit_and_wait(fd, &cmd); + assert(ret == 0); + + osd_command_set_remove_partition(&cmd, pid); + ret = osd_submit_and_wait(fd, &cmd); + assert(ret == 0); + } + break; + case 19: /* Contention, speculative idling, expwait no work */ + case 20: /* Contention, speculative idling, expwait local work */ + case 21: /* Contention, speculative idling, expwait remote work */ + if (rank == 0) { + osd_command_set_format_osd(&cmd, 1<<30); + ret = osd_submit_and_wait(fd, &cmd); + assert(ret == 0); + + osd_command_set_create_partition(&cmd, pid); + ret = osd_submit_and_wait(fd, &cmd); + assert(ret == 0); + + osd_command_set_create(&cmd, pid, oid, 1); + ret = osd_submit_and_wait(fd, &cmd); + assert(ret == 0); + } + + if (test == 19) + spec_idle(fd, pid, oid, 100, 0, 4); + else if (test == 20) + spec_idle(fd, pid, oid, 100, 1, 4); + else if (test == 21) + spec_idle(fd, pid, oid, 100, 2, 4); + + if (rank == 0) { + osd_command_set_remove(&cmd, pid, oid); + ret = osd_submit_and_wait(fd, &cmd); + assert(ret == 0); + + osd_command_set_remove_partition(&cmd, pid); + ret = osd_submit_and_wait(fd, &cmd); + assert(ret == 0); + } + break; + case 22: /* Contention, beb, no work */ + case 23: /* Contention, beb, local work */ + case 24: /* Contention, beb, remote work */ + if (rank == 0) { + osd_command_set_format_osd(&cmd, 1<<30); + ret = osd_submit_and_wait(fd, &cmd); + assert(ret == 0); + + osd_command_set_create_partition(&cmd, pid); + ret = osd_submit_and_wait(fd, &cmd); + assert(ret == 0); + + osd_command_set_create(&cmd, pid, oid, 1); + ret = osd_submit_and_wait(fd, &cmd); + assert(ret == 0); + } + + if (test == 22) + spec_idle(fd, pid, oid, 100, 0, 5); + else if (test == 23) + spec_idle(fd, pid, oid, 100, 1, 5); + else if (test == 24) + spec_idle(fd, pid, oid, 100, 2, 5); + + if (rank == 0) { + osd_command_set_remove(&cmd, pid, oid); + ret = osd_submit_and_wait(fd, &cmd); + assert(ret == 0); + + osd_command_set_remove_partition(&cmd, pid); + ret = osd_submit_and_wait(fd, &cmd); + assert(ret == 0); + } + break; + + default: + usage(); + } + + close(fd); + MPI_Finalize(); + return 0; +} diff --git a/osd-initiator/tests/generic_iface_test.c b/osd-initiator/tests/generic_iface_test.c new file mode 100644 index 0000000..711af75 --- /dev/null +++ b/osd-initiator/tests/generic_iface_test.c @@ -0,0 +1,456 @@ +/* + * General test of the interface. + * + * Copyright (C) 2007 OSD Team + * + * 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 "osd-util/osd-util.h" +#include "command.h" +#include "device.h" +#include "drivelist.h" +#include "sense.h" + +#define FIRST_PID USEROBJECT_PID_LB +#define FIRST_OID USEROBJECT_OID_LB + +#define FULL_TEST /*comment out for simple test - create 1 part and 1 obj + //~ no read or write, etc*/ + +static int fd; +static char *buf; +static char *buf2; + +static void init(void) +{ + int ret; + struct osd_drive_description *drive_list; + int num_drives; + + printf("Initialization\n"); + ret = osd_get_drive_list(&drive_list, &num_drives); + if (ret < 0) { + osd_error_errno("%s: get drive list", __func__); + exit(1); + } + if (num_drives == 0) { + osd_error("%s: No drives found: %d", __func__, num_drives); + exit(1); + } + + printf("Found %d Drives Now open drive #0\n", num_drives); + fd = open(drive_list[0].chardev, O_RDWR); + if (fd < 0) { + osd_error_errno("%s: open %s", __func__, drive_list[0].chardev); + exit(1); + } +} + +static void format(void) +{ + int ret; + struct osd_command command; + const int OBJ_CAPACITY = 1<<30; /* 1 GB */ + + printf("BEGIN FORMAT TEST\n"); + + printf("Formatting active disk\n"); + printf(".....Set format command\n"); + ret = osd_command_set_format_osd(&command, OBJ_CAPACITY); + if (ret != 0){ + printf("Couldn't set format command\n"); + exit(1); + } + printf(".....Submit the command\n"); + ret = osd_submit_command(fd, &command); + if (ret != 0){ + printf("Submit command failed\n"); + exit(1); + } + printf(".....Get the status of the command\n"); + ret = osd_wait_this_response(fd, &command); + if (ret != 0) { + printf("Unable to get result\n"); + exit(1); + } + + if(command.sense_len != 0){ + printf("Sense data found!\n"); + exit(1); + } + + if(command.status != 0){ + printf("Command FAILED!\n"); + exit(1); + } + printf(".....Response len is %d\n", (int) command.inlen); + printf(".....%s\n", (char *) command.indata); + + printf("END FORMAT TEST\n"); + +} + +static void fini(void) +{ + int ret; + + printf("Close the drive\n"); + ret = close(fd); + if (ret) { + osd_error_errno("%s: close drive", __func__); + exit(1); + } +} + +static void test_create_partition(void) +{ + int ret; + struct osd_command command; + struct osd_command command2; + + printf("BEGIN CREATE PARTITION\n"); + +#ifdef FULL_TEST + printf("Create Partition command that should fail\n"); + ret = osd_command_set_create_partition(&command, 2); + if (ret != 0){ + printf("Couldn't set create partition command\n"); + exit(1); + } +#endif + printf("Create Partition command that should work\n"); + ret = osd_command_set_create_partition(&command2, FIRST_PID); + if (ret != 0){ + printf("Couldn't set create partition command\n"); + exit(1); + } + +#ifdef FULL_TEST + printf("Submit the commands\n"); + ret = osd_submit_command(fd, &command); + if (ret != 0){ + printf("Submit command failed\n"); + exit(1); + } +#endif + ret = osd_submit_command(fd, &command2); + if (ret != 0){ + printf("Submit command failed\n"); + exit(1); + } + + printf("Get the status of the commands\n\n"); +#ifdef FULL_TEST + ret = osd_wait_this_response(fd, &command); + if (ret != 0) { + printf("Unable to get result\n"); + exit(1); + } + if(command.sense_len == 0){ + printf("No Sense data found when there should have been!\n"); + exit(1); + } + if(command.status == 0){ + printf("Command did not FAIL when it should!\n"); + exit(1); + } + + printf(".....Response len is %d\n", (int) command.inlen); + printf(".....%s\n", (char *) command.indata); + printf("Command failed as expected\n"); +#endif + ret = osd_wait_this_response(fd, &command2); + if (ret != 0) { + printf("Unable to get result\n"); + exit(1); + } + if((command2.sense_len != 0) || (command2.status != 0)){ + printf("Sense data found means error!\n"); + fputs(osd_sense_as_string(command2.sense, command2.sense_len), stderr); + exit(1); + } + printf(".....Response len is %d\n", (int) command2.inlen); + printf(".....%s\n", (char *) command2.indata); + printf("Command worked successfully\n\n"); + + printf("Problem with the first command was:\n"); + fputs(osd_sense_as_string(command.sense, command.sense_len), stderr); + + printf("END CREATE PARTITION TEST\n"); + +} + +static void create_objects(void) +{ + int ret; + struct osd_command command; + +#ifdef FULL_TEST + uint16_t count = 10; +#else + uint16_t count = 1; +#endif + + printf("BEGIN CREATE OBJECT TEST\n"); + + printf("Create the command\n"); + ret = osd_command_set_create(&command, FIRST_PID, 0, count); + if (ret != 0 ){ + printf("Unable to set command\n"); + exit(1); + } + + printf("Submit the create obj command\n"); + ret = osd_submit_command(fd, &command); + if (ret != 0){ + printf("Submit command failed\n"); + exit(1); + } + + ret = osd_wait_this_response(fd, &command); + if (ret != 0) { + printf("Unable to get result\n"); + exit(1); + } + if((command.sense_len != 0) || (command.status != 0)){ + printf("Sense data found means error!\n"); + fputs(osd_sense_as_string(command.sense, command.sense_len), stderr); + exit(1); + } + printf(".....Response len is %d\n", (int) command.inlen); + printf(".....%s\n", (char *) command.indata); + printf("Command worked successfully\n\n"); + + printf("END CREATE OBJECT TEST\n"); +} + +static void remove_objects(void) +{ + /*Since create object with get attributes not implemented yet + just assume we know the first object ID which we do do here*/ + + printf("BEGIN REMOVE OBJECTS TEST\n"); + + int ret; + struct osd_command command; + int i; + uint64_t oid, pid; + + oid = FIRST_OID; + oid++; + pid = FIRST_PID; + for(i=0; i< 5; i++){ + oid+=1; + + printf("Create the command\n"); + ret = osd_command_set_remove(&command, pid, oid); + if (ret != 0 ){ + printf("Unable to set command\n"); + exit(1); + } + + printf("Submit the remove obj command\n"); + ret = osd_submit_command(fd, &command); + if (ret != 0){ + printf("Submit command failed\n"); + exit(1); + } + + ret = osd_wait_this_response(fd, &command); + if (ret != 0) { + printf("Unable to get result\n"); + exit(1); + } + + if((command.sense_len != 0) || (command.status != 0)){ + printf("Sense data found means error!\n"); + fputs(osd_sense_as_string(command.sense, command.sense_len), stderr); + exit(1); + } + + printf("Just removed object %ld\n", oid); + } + printf("END REMOVE OBJECTS TEST\n"); + printf("\n"); +} + +static void write_objects(void) +{ + printf("BEGIN WRITE OBJECTS TEST\n"); + int ret; + struct osd_command command; + int i; + uint64_t oid, pid; + size_t len; + size_t offset; + + buf = malloc(1024); + for(i=0; i<1024; i++){ + memset(buf+i, 'D', 1); + } + + oid = FIRST_OID; + pid = FIRST_PID; + len = 1024; + offset = 0; + + printf("%s: Create the command\n", __func__); + ret = osd_command_set_write(&command, pid, oid, len, offset); + if (ret != 0 ){ + printf("Unable to set command\n"); + exit(1); + } + command.outdata = buf; + command.outlen = len; + + printf("Submit the write command\n"); + ret = osd_submit_command(fd, &command); + if (ret != 0){ + printf("Submit command failed\n"); + exit(1); + } + + ret = osd_wait_this_response(fd, &command); + if (ret != 0) { + printf("Unable to get result\n"); + exit(1); + } + + if((command.sense_len != 0) || (command.status != 0)){ + printf("Sense data found means error!\n"); + fputs(osd_sense_as_string(command.sense, command.sense_len), stderr); + exit(1); + } + + + printf("END WRITE OBJECTS TEST\n"); +} + +static void read_objects(void) +{ + printf("BEGIN READ OBJECTS TEST\n"); + int ret; + struct osd_command command; + int i; + uint64_t oid, pid; + size_t len, offset; + + buf2 = malloc(1024); + + oid = FIRST_OID; + pid = FIRST_PID; + len = 1024; + offset = 0; + + printf("Create the command\n"); + ret = osd_command_set_read(&command, pid, oid, len, offset); + if (ret != 0 ){ + printf("Unable to set command\n"); + exit(1); + } + command.indata = buf2; + command.inlen_alloc = len; + + printf("Submit the read command\n"); + ret = osd_submit_command(fd, &command); + if (ret != 0){ + printf("Submit command failed\n"); + exit(1); + } + + ret = osd_wait_this_response(fd, &command); + if (ret != 0) { + printf("Unable to get result\n"); + exit(1); + } + + if((command.sense_len != 0) || (command.status != 0)){ + printf("Sense data found means error!\n"); + fputs(osd_sense_as_string(command.sense, command.sense_len), stderr); + exit(1); + } + + + for (i=0; i< 1024; i++){ + if (buf[i] != buf2[i]){ + printf("Error Data Invalid!\n"); + exit(1); + } + } + + printf("Buffers Check Out\n\n"); + printf("END READ OBJECTS TEST\n"); +} + +static void create_collection(void) +{ + int ret; + struct osd_command command; + + + printf("BEGIN CREATE COLLECTION TEST\n"); + + printf("Create the command\n"); + ret = osd_command_set_create_collection(&command, FIRST_PID, FIRST_OID+1); + if (ret != 0 ){ + printf("Unable to set command\n"); + exit(1); + } + + printf("Submit the command\n"); + ret = osd_submit_command(fd, &command); + if (ret != 0){ + printf("Submit command failed\n"); + exit(1); + } + + ret = osd_wait_this_response(fd, &command); + if (ret != 0) { + printf("Unable to get result\n"); + exit(1); + } + if((command.sense_len != 0) || (command.status != 0)){ + printf("Sense data found means error!\n"); + fputs(osd_sense_as_string(command.sense, command.sense_len), stderr); + //~ exit(1); + } + printf(".....Response len is %d\n", (int) command.inlen); + printf(".....%s\n", (char *) command.indata); + printf("Command worked successfully\n\n"); + + printf("END CREATE OBJECT TEST\n"); + + +} + +int main(void) +{ + init(); + format(); + test_create_partition(); + create_objects(); + create_collection(); + #ifdef FULL_TEST + remove_objects(); + write_objects(); + read_objects(); + #endif + fini(); + return 0; +} diff --git a/osd-initiator/tests/init-cont.py b/osd-initiator/tests/init-cont.py new file mode 100755 index 0000000..951238b --- /dev/null +++ b/osd-initiator/tests/init-cont.py @@ -0,0 +1,83 @@ +#!/usr/bin/python -tu +# +# Start/stop a bunch of OSD targets on nodes from PBS. +# +# Copyright (C) 2007 OSD Team +# +# 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 . + +import os, sys, commands, pwd + +nodes = [] +osdnodes = [] +compnodes = [] +testdir = os.environ["TMPDIR"]; +id = pwd.getpwuid(os.getuid())[0] +tgtd = "~/osd/stgt/tgtd" + +def usage(): + print >>sys.stderr, "Usage:", sys.argv[0], "" + os._exit(1) + +def allify(n): + (fdto, fdfrom) = os.popen2("/home/ananth/bin/allify") + for i in n: + print >>fdto, i + fdto.close() + s = fdfrom.read() + fdfrom.close() + s = s[:-1] + return s + +def init(): + global nodes, osdnodes, compnodes + ret = commands.getoutput("echo $PBS_NODEFILE") + if ret != "": + o = commands.getoutput("uniq $PBS_NODEFILE") + nodes = o.split('\n') + osdnodes = [nodes[-1]] + compnodes = nodes[:-1] + else: + print "Run the program from PBS root node" + os._exit(1) + +def start(): + assert osdnodes != [] + os.system("all -p " + allify(osdnodes) + " " + + "cd " + testdir + " \;" + + "rm -rf /tmp/tgt-" + id + " \; " + + "mkdir /tmp/tgt-" + id + " \; " + + tgtd + "\< /dev/null \&\> tgtd.log") + assert compnodes != [] + os.system("all -p " + allify(compnodes) + " " + + "~/osd/osd-util/initiator start " + " ".join(osdnodes)) + +def stop(): + assert osdnodes != [] + os.system("all -p " + allify(compnodes) + " " + + "~/osd/osd-util/initiator stop; ") + + assert compnodes != [] + os.system("all -p " + allify(osdnodes) + " " + + "cd " + testdir + " \; " + + "killall -9 tgtd \; " + + "rm -f tgtd.log") + + +init() +if len(sys.argv) != 2: + usage() +if sys.argv[1] == "start": + start() +elif sys.argv[1] == "stop": + stop() diff --git a/osd-initiator/tests/iospeed-ddt.c b/osd-initiator/tests/iospeed-ddt.c new file mode 100644 index 0000000..82cd24e --- /dev/null +++ b/osd-initiator/tests/iospeed-ddt.c @@ -0,0 +1,565 @@ +/* + * IO throughput using SGL and Vectored. + * + * Copyright (C) 2007-8 OSD Team + * + * 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 "osd-util/osd-util.h" +#include "command.h" +#include "device.h" +#include "drivelist.h" +#include "sync.h" + + +enum { + INVALID, + WRITE, + READ, + FLUSH, + NOFLUSH, + CONTIG, + VEC, + SGL, +}; + +static uint64_t obj_create_any(int fd, uint64_t pid) +{ + struct osd_command command; + struct attribute_list attr = { + .type = ATTR_GET, + .page = CUR_CMD_ATTR_PG, + .number = CCAP_OID, + .len = 8, + }; + int ret; + uint64_t oid; + + osd_command_set_create(&command, pid, 0, 0); + osd_command_attr_build(&command, &attr, 1); + ret = osd_submit_and_wait(fd, &command); + if (ret) { + osd_error_xerrno(ret, "%s: submit_and_wait failed", __func__); + exit(1); + } + ret = osd_command_attr_resolve(&command); + if (ret) { + osd_error_xerrno(ret, "%s: attr_resolve failed", __func__); + exit(1); + } + oid = get_ntohll(command.attr[0].val); + osd_command_attr_free(&command); + return oid; +} + +static void usage(void) +{ + fprintf(stderr, "Usage: %s [OPTIONS]\n", osd_get_progname()); + fprintf(stderr, "There are two main ways to run this program\n"); + fprintf(stderr, " 1. Using the default data distribution (contiguous)\n"); + fprintf(stderr, " -o [read|write]\n"); + fprintf(stderr, " -l [max size]\n"); + fprintf(stderr, " 2. Using a specialized data distribution\n"); + fprintf(stderr, " -o [read|write]\n"); + fprintf(stderr, " -d [sgl|vec]\n"); + fprintf(stderr, " -l [max size]\n"); + fprintf(stderr, " -s [stride]\n"); + fprintf(stderr, " -n [number bytes per segment]\n"); + fprintf(stderr, "Both can use the following options:\n"); + fprintf(stderr, " -t [trials]\n"); + fprintf(stderr, " -f Flush writes to disk\n"); + fprintf(stderr, " -i Start test at 4K and increasing by i up to max size\n"); + fprintf(stderr, " -w [warm up]\n"); + fprintf(stderr, "*For size values a k, m, or g can be appended for KB, MB, or GB ie 100k\n"); + exit(1); +} + +static void obj_remove(int fd, uint64_t pid, uint64_t oid) +{ + struct osd_command command; + int ret; + + osd_command_set_remove(&command, pid, oid); + ret = osd_submit_and_wait(fd, &command); + if (ret) { + osd_error_xerrno(ret, "%s: submit_and_wait failed", __func__); + exit(1); + } +} + +static uint64_t parse_number(const char *cp) +{ + uint64_t v; + char *cq; + + v = strtoul(cp, &cq, 0); + if (*cq) { + if (!strcasecmp(cq, "k")) + v <<= 10; + else if (!strcasecmp(cq, "m")) + v <<= 20; + else if (!strcasecmp(cq, "g")) + v <<= 30; + else + usage(); + } + return v; +} + +static void show_stats(double *bw_array, int trials, uint64_t size, int mode, + int flush) +{ + double mu, sd; + const char *s; + + mu = mean(bw_array, trials); + sd = stddev(bw_array, mu, trials); + s = "read"; + if (mode == WRITE) { + if (flush == FLUSH) + s = "write-flush"; + else + s = "write"; + } + printf("%-11s %3zu kB %7.3lf +- %7.3lf MB/s\n", s, size>>10, mu, sd); +} + +static void do_contig(int fd, int mode, uint64_t pid, uint64_t oid, int trials, + uint64_t size, uint64_t numbytes, uint64_t stride, + int flush, int warmup) +{ + + uint64_t start, stop, delta; + int ret; + void *buf; + double *bw_array; + double mhz = get_mhz(); + double time; + int i, j; + uint64_t offset; + uint64_t total_size; + int segments; + int bytes_left; + + assert(mode != READ && flush != FLUSH); + + bw_array = malloc(trials * sizeof(*bw_array)); + assert(bw_array != NULL); + buf = malloc(size); + assert(buf != NULL); + memset(buf, 'X', size); /* fill with something */ + + if (mode == READ) + /* put something there to read */ + ret = write_osd(fd, pid, oid, buf, size, 0); + + if (numbytes == 0 || stride == 0) + /* default to a single write */ + numbytes = size; + + /* figure out how many segments we are going to have */ + segments = size / numbytes; + bytes_left = size % numbytes; + total_size = segments * numbytes + bytes_left; + if (bytes_left > 0) + /* need one last segment for the remainder of bytes */ + ++segments; + assert(total_size == size); + + for (i = 0; i < trials+warmup; i++) { + offset = 0; + rdtsc(start); + for (j = 0; j < segments; j++) { + if (j + 1 == segments && bytes_left != 0) { + if (mode == WRITE) + ret = write_osd(fd, pid, oid, buf, bytes_left, offset); + else + ret = read_osd(fd, pid, oid, buf, bytes_left, offset); + assert(ret == 0); + } else { + if (mode == WRITE) + ret = write_osd(fd, pid, oid, buf, numbytes, offset); + else + ret = read_osd(fd, pid, oid, buf, numbytes, offset); + assert(ret == 0); + offset += stride; + } + } + rdtsc(stop); + + delta = stop - start; + + if (flush == FLUSH) { + rdtsc(start); + ret = flush_object(fd, pid, oid, 0 ,0 ,0); + rdtsc(stop); + assert(ret == 0); + delta += stop - start; + } + time = ((double)delta)/mhz; /* time in usec */ + if (i >= warmup) + bw_array[i-warmup] = size/time; /* BW in MegaBytes/sec */ + } + + show_stats(bw_array, trials, size, mode, flush); + free(buf); + free(bw_array); +} + +static void do_sgl(int fd, int mode, uint64_t pid, uint64_t oid, int trials, + uint64_t size, uint64_t numbytes, uint64_t stride, + int flush, int warmup) +{ + uint64_t start, stop, delta; + int ret; + void *buf, *ret_buf = NULL; + double *bw_array; + double mhz = get_mhz(); + double time; + int i; + uint64_t total_size; + int segments; + int bytes_left; + uint64_t offset, hdr_offset; + uint64_t ddt_size; + + if (mode == READ) + assert(flush != FLUSH); + + bw_array = malloc(trials * sizeof(*bw_array)); + assert(bw_array != NULL); + + if (mode == READ) { + ret_buf = malloc(size); + assert(ret_buf != NULL); + memset(ret_buf, 'X', size); + } + + /* figure out how many segments we are going to have */ + segments = size / numbytes; + bytes_left = size % numbytes; + total_size = segments * numbytes + bytes_left; + if (bytes_left > 0) + /* need one last segment for the remainder of bytes */ + ++segments; + assert(total_size == size); + + /* to rep each segment need 2 uint64_ts, and need #segments */ + ddt_size = (sizeof(uint64_t) * segments * 2) + sizeof(uint64_t); + total_size += ddt_size; + + if (mode == WRITE) { + buf = malloc(total_size); + assert(buf != NULL); + memset(buf, 'X', total_size); /* fill with something */ + } else { + buf = malloc(ddt_size); + assert(buf != NULL); + } + + /* prepare the buffer: |#segments|offset|len|offset|len|....|DATA| */ + hdr_offset = 0; + offset = 0; + set_htonll(buf, segments); + hdr_offset += sizeof(uint64_t); + for (i = 0; i < segments; i++) { + set_htonll((uint8_t *)buf + hdr_offset, offset); + offset += stride; + hdr_offset += sizeof(uint64_t); + if (i + 1 == segments && bytes_left != 0) + set_htonll((uint8_t *)buf + hdr_offset, bytes_left); + else + set_htonll((uint8_t *)buf + hdr_offset, numbytes); + hdr_offset += sizeof(uint64_t); + } + + + if (mode == READ) + /* put something there to read */ + ret = write_osd(fd, pid, oid, ret_buf, size, 0); + + for (i = 0; i < trials+warmup; i++) { + rdtsc(start); + if (mode == WRITE) + ret = write_sgl_osd(fd, pid, oid, buf, size, 0); + else + ret = read_sgl_osd(fd, pid, oid, buf, ddt_size, ret_buf, + size, 0); + + rdtsc(stop); + assert(ret == 0); + delta = stop - start; + + if (flush == FLUSH) { + rdtsc(start); + ret = flush_object(fd, pid, oid, 0, 0, 0); + rdtsc(stop); + assert(ret == 0); + delta += stop - start; + } + time = ((double)delta)/mhz; /* time in usec */ + if (i >= warmup) + bw_array[i-warmup] = size/time; /* BW in MegaBytes/sec */ + } + + show_stats(bw_array, trials, size, mode, flush); + free(buf); + free(bw_array); +} + +static void do_vec(int fd, int mode, uint64_t pid, uint64_t oid, int trials, + uint64_t size, uint64_t numbytes, uint64_t stride, + int flush, int warmup) +{ + uint64_t start, stop, delta; + int ret; + void *buf, *ret_buf = NULL; + double *bw_array = NULL; + double mhz = get_mhz(); + double time = 0.0; + int i; + uint64_t total_size; + uint64_t hdr_offset = 0; + uint64_t ddt_size = 0; + + if (mode == READ) + assert(flush != FLUSH); + + bw_array = malloc(trials * sizeof(*bw_array)); + assert(bw_array != NULL); + + if (mode == READ) { + ret_buf = malloc(size); + assert(ret_buf != NULL); + memset(ret_buf, 'X', size); + } + + ddt_size = (sizeof(uint64_t) * 2); + total_size = ddt_size + size; + + if (mode == WRITE) { + buf = malloc(total_size); + assert(buf != NULL); + memset(buf, 'X', total_size); /* fill with something */ + } else { + buf = malloc(ddt_size); + assert(buf != NULL); + } + + /* prepare the buffer: |stride|len|DATA| */ + + set_htonll(buf, stride); + hdr_offset = sizeof(uint64_t); + set_htonll((uint8_t *)buf + hdr_offset, numbytes); + + if (mode == READ) + /* put something there to read */ + ret = write_osd(fd, pid, oid, ret_buf, size, 0); + + for (i = 0; i < trials+warmup; i++) { + rdtsc(start); + if (mode == WRITE) + ret = write_vec_osd(fd, pid, oid, buf, total_size, 0); + else + ret = read_vec_osd(fd, pid, oid, buf, ddt_size, ret_buf, + size, 0); + + rdtsc(stop); + assert(ret == 0); + delta = stop - start; + + if (flush == FLUSH) { + rdtsc(start); + ret = flush_object(fd, pid, oid, 0, 0, 0); + rdtsc(stop); + assert(ret == 0); + delta += stop - start; + } + time = ((double)delta)/mhz; /* time in usec */ + if (i >= warmup) + bw_array[i-warmup] = size/time; /* BW in MegaBytes/sec */ + } + + show_stats(bw_array, trials, size, mode, flush); + free(buf); + free(bw_array); +} + +int main(int argc, char *argv[]) +{ + int fd, ret, num_drives, i; + struct osd_drive_description *drives; + uint64_t oid, pid; + int c; + uint64_t length = 0; + uint64_t stride = 0; + uint64_t num = 0; + int trials = 10; + int warmup = 5; + uint64_t size; + uint64_t iter = 0; + int mode = INVALID; + int flush = NOFLUSH; + int ddt = CONTIG; + + osd_set_progname(argc, argv); + + /* note: when passing params ensure end up with a contiguous file + especially for reads or else we won't be able to stage a file ahead + of time */ + + while ((c = getopt (argc, argv, "l:o:d:s:n:t:i:w:f")) != -1) + switch (c) { + case 'l': + length = parse_number(optarg); + break; + case 'o': + if (!strcmp(optarg, "write")) + mode = WRITE; + else if (!strcmp(optarg, "read")) + mode = READ; + else + usage(); + break; + case 'd': + if (!strcmp(optarg, "sgl")) + ddt = SGL; + else if (!strcmp(optarg, "vec")) + ddt = VEC; + break; + case 's': + stride = parse_number(optarg); + break; + case 'n': + num = parse_number(optarg); + break; + case 'f': + flush = FLUSH; + break; + case 't': + trials = atoi(optarg); + break; + case 'i': + iter = parse_number(optarg); + break; + case 'w': + warmup = atoi(optarg); + break; + default: + usage(); + } + + if (mode == INVALID) { + fprintf(stderr, "mode is invalid\n"); + usage(); + } + + if (mode == READ && flush == FLUSH) { + fprintf(stderr, "flush has no meaning for writes \n"); + usage(); + } + + if (ddt != CONTIG && num <= 0) { + fprintf(stderr, "num segments required when specifying ddt\n"); + usage(); + } + + if (ddt != CONTIG && stride == 0) { + fprintf(stderr, "Stride length must be specifyed\n"); + usage(); + } + + if (ddt != CONTIG && stride < num) { + fprintf(stderr, "Stride must be at least as big as num\n"); + usage(); + } + + + if (trials <= 0) { + fprintf(stderr, "problem with number of trials\n"); + usage(); + } + + if (iter > 0 && length <= 4096) { + fprintf(stderr, "if using an iter value total length must be > 4k\n"); + usage(); + } + + if (length <= (unsigned)iter) { + fprintf(stderr, "length must be bigger than iter value\n"); + usage(); + } + + ret = osd_get_drive_list(&drives, &num_drives); + if (ret < 0) { + osd_error("%s: get drive error", __func__); + return 1; + } + if (num_drives == 0) { + osd_error("%s: no drives", __func__); + return 1; + } + + i = 0; + osd_debug("drive %s name %s", drives[i].chardev, drives[i].targetname); + fd = open(drives[i].chardev, O_RDWR); + if (fd < 0) { + osd_error_errno("%s: open %s", __func__, drives[i].chardev); + return 1; + } + osd_free_drive_list(drives, num_drives); + + inquiry(fd); + + format_osd(fd, 1<<30); + + pid = PARTITION_PID_LB; + create_partition(fd, pid); + + oid = obj_create_any(fd, pid); + + for (size = 4096; size <= length; size += iter) { + /* run test */ + switch (ddt) { + case CONTIG: + do_contig(fd, mode, pid, oid, trials, size, + num, stride, flush, warmup); + break; + case SGL: + do_sgl(fd, mode, pid, oid, trials, size, num, + stride, flush, warmup); + break; + case VEC: + do_vec(fd, mode, pid, oid, trials, size, num, + stride, flush, warmup); + break; + default: + exit(1); /* should never happen */ + } + } + + obj_remove(fd, pid, oid); + close(fd); + return 0; +} diff --git a/osd-initiator/tests/iospeed-mpi.c b/osd-initiator/tests/iospeed-mpi.c new file mode 100644 index 0000000..bd9661c --- /dev/null +++ b/osd-initiator/tests/iospeed-mpi.c @@ -0,0 +1,397 @@ +/* + * MPI version of now-classic iospeed benchmark. Start like: + * + * mpiexec -pernode -n 2 iospeed-mpi + * + * Copyright (C) 2007 OSD Team + * + * 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 "osd-util/osd-util.h" +#include "command.h" +#include "device.h" +#include "drivelist.h" +#include "sync.h" + +static int rank, numproc; + +static uint64_t obj_create_any(int fd, uint64_t pid) +{ + struct osd_command command; + struct attribute_list attr = { + .type = ATTR_GET, + .page = CUR_CMD_ATTR_PG, + .number = CCAP_OID, + .len = 8, + }; + int ret; + uint64_t oid; + + osd_command_set_create(&command, pid, 0, 0); + osd_command_attr_build(&command, &attr, 1); + ret = osd_submit_and_wait(fd, &command); + if (ret) { + osd_error_xerrno(ret, "%s: submit_and_wait failed", __func__); + exit(1); + } + ret = osd_command_attr_resolve(&command); + if (ret) { + osd_error_xerrno(ret, "%s: attr_resolve failed", __func__); + exit(1); + } + oid = get_ntohll(command.attr[0].val); + osd_command_attr_free(&command); + return oid; +} + +static void obj_remove(int fd, uint64_t pid, uint64_t oid) +{ + struct osd_command command; + int ret; + + osd_command_set_remove(&command, pid, oid); + ret = osd_submit_and_wait(fd, &command); + if (ret) { + osd_error_xerrno(ret, "%s: submit_and_wait failed", __func__); + exit(1); + } +} + +static void read_bw(int fd, uint64_t pid, uint64_t oid, + size_t sz, int iters, int dosync) +{ + int i = 0; + int ret = 0; + uint64_t start, end, delta, total_start, total_stop; + double mhz = get_mhz(); + double time = 0.0; + double max_time = 0.0; + double min_time = 0.0; + double *b = NULL; + void *buf = NULL; + size_t total_size; + + buf = malloc(sz); + b = malloc(iters * sizeof(*b)); + + if (!buf || !b) + osd_error_fatal("out of memory"); + + /* warm up */ + if (iters > 5) + for (i=0; i<5; i++) { + ret = read_osd(fd, pid, oid, buf, sz, 0); + assert(ret == 0); + } + + memset(buf, '\0', sz); + + MPI_Barrier(MPI_COMM_WORLD); + rdtsc(total_start); + for (i=0; i< iters; i++) { + if (dosync) { + rdtsc(start); + ret = read_osd(fd, pid, oid, buf, sz, 0); + rdtsc(end); + assert(ret == 0); + delta = end - start; + + rdtsc(start); + ret = flush_object(fd, pid, oid, 2); + rdtsc(end); + assert(ret == 0); + delta += (end - start); + } else { + rdtsc(start); + ret = read_osd(fd, pid, oid, buf, sz, 0); + rdtsc(end); + assert(ret == 0); + delta = end - start; + } + + time = ((double)delta)/mhz; /* time in usec */ + b[i] = sz/time; /* BW in MegaBytes/sec */ + } + + MPI_Barrier(MPI_COMM_WORLD); /*everyone is done reading*/ + rdtsc(total_stop); + + unsigned int j; + for (j=0; j>10, total_size/max_time, + max_time - min_time, 100. * (max_time - min_time) / max_time); + } +#else + { + double mu, sd, total; + + mu = mean(b, iters); + sd = stddev(b, mu, iters); + + total = mu * numproc; + + if (dosync) + printf("rank %d read-sync %3lu %7.3lf +- %7.3lf\n", + rank, sz>>10, mu, sd); + else + printf("rank %d read %3lu %7.3lf +- %7.3lf\n", + rank, sz>>10, mu, sd); + } +#endif + free(buf); + free(b); +} + +static void write_bw(int fd, uint64_t pid, uint64_t oid, + size_t sz, int iters, int dosync) +{ + int i = 0; + int ret = 0; + uint64_t start, end, delta, total_start, total_stop;; + double mhz = get_mhz(); + double time = 0.0; + double max_time = 0.0; + double min_time = 0.0; + double *b = NULL; + void *buf = NULL; + size_t total_size; + + buf = malloc(sz); + b = malloc(iters * sizeof(*b)); + + if (!buf || !b) + osd_error_fatal("out of memory"); + + /* warm up */ + if (iters > 5) + for (i=0; i<5; i++) { + ret = write_osd(fd, pid, oid, buf, sz, 0); + assert(ret == 0); + } + + memset(buf, 'D', sz); + + MPI_Barrier(MPI_COMM_WORLD); + rdtsc(total_start); + for (i=0; i< iters; i++) { + if (dosync) { + rdtsc(start); + ret = write_osd(fd, pid, oid, buf, sz, 0); + rdtsc(end); + assert(ret == 0); + delta = end - start; + + rdtsc(start); + /* XXX: this may be broken, numbers reported + * are too big to be reasonable */ + ret = flush_object(fd, pid, oid, 2); + rdtsc(end); + assert(ret == 0); + + delta += end - start; + } else { + rdtsc(start); + ret = write_osd(fd, pid, oid, buf, sz, 0); + rdtsc(end); + assert(ret == 0); + delta = end - start; + } + + time = ((double)delta)/mhz; /* time in usec */ + b[i] = sz/time; /* BW in MegaBytes/sec */ + } + MPI_Barrier(MPI_COMM_WORLD); /*everyone is done reading*/ + rdtsc(total_stop); + +#if 1 + delta = total_stop - total_start; + time = ((double)delta)/mhz; /*time in usec*/ + + ret = MPI_Reduce(&time, &max_time, 1, MPI_DOUBLE, MPI_MAX, + 0, MPI_COMM_WORLD); + if (ret != MPI_SUCCESS) { + printf("MPI ERROR\n"); + } + ret = MPI_Reduce(&time, &min_time, 1, MPI_DOUBLE, MPI_MIN, + 0, MPI_COMM_WORLD); + if (ret != MPI_SUCCESS) { + printf("MPI ERROR\n"); + } + if (rank == 0) { + total_size = sz * iters * numproc; /*total bytes moved*/ + printf("write %3d %3lu %7.3lf --- Discrep %.0f is %.1f%%\n", + numproc, sz>>10, total_size/max_time, + max_time - min_time, 100. * (max_time - min_time) / max_time); + } +#else + { + double mu, sd; + + mu = mean(b, iters); + sd = stddev(b, mu, iters); + if (dosync) + printf("rank %d write-sync %3lu %7.3lf +- %7.3lf\n", + rank, sz>>10, mu, sd); + else + printf("rank %d write %3lu %7.3lf +- %7.3lf\n", + rank, sz>>10, mu, sd); + } +#endif + free(buf); + free(b); +} + +static void usage(void) +{ + fprintf(stderr, "Usage: %s []\n", osd_get_progname()); + exit(1); +} + +int main(int argc, char *argv[]) +{ + int fd, ret, num_drives, i; + struct osd_drive_description *drives; + const int iter = 1000; + uint64_t oid; + int onesize = 0; + + MPI_Init(&argc, &argv); + MPI_Comm_size(MPI_COMM_WORLD, &numproc); + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + osd_set_progname(argc, argv); + + if (argc == 1) { + ; + } else if (argc == 2) { + char *cp; + onesize = strtoul(argv[1], &cp, 0); + if (*cp != '\0') + usage(); + onesize <<= 10; + } else { + usage(); + } + + + ret = osd_get_drive_list(&drives, &num_drives); + if (ret < 0) { + osd_error("%s: get drive error", __func__); + return 1; + } + if (num_drives == 0) { + osd_error("%s: no drives", __func__); + return 1; + } + + i = 0; + osd_debug("%s: drive %s name %s", osd_get_progname(), drives[i].chardev, + drives[i].targetname); + fd = open(drives[i].chardev, O_RDWR); + if (fd < 0) { + osd_error_errno("%s: open %s", __func__, drives[i].chardev); + return 1; + } + osd_free_drive_list(drives, num_drives); + + inquiry(fd); + + if (rank == 0) { + format_osd(fd, 1<<30); + create_partition(fd, PARTITION_PID_LB); + } + MPI_Barrier(MPI_COMM_WORLD); + + /* each works on a different object */ + oid = obj_create_any(fd, PARTITION_PID_LB); + + if (onesize) { + if (rank == 0) { + printf("# osd_initiator/tests/iospeed %d\n", + onesize>>10); + printf("# type numproc size (kB) rate (MB/s) --- time diff (us)\n"); + } + MPI_Barrier(MPI_COMM_WORLD); + write_bw(fd, PARTITION_PID_LB, oid, onesize, iter, 0); + MPI_Barrier(MPI_COMM_WORLD); + read_bw(fd, PARTITION_PID_LB, oid, onesize, iter, 0); + } else { + if (rank == 0) { + printf("# osd_initiator/tests/iospeed\n"); + printf("# type size (kB) rate (MB/s) +- stdev\n"); + } + for (i=4096; i<=(1<<19); i+=4096) { + MPI_Barrier(MPI_COMM_WORLD); + write_bw(fd, PARTITION_PID_LB, oid, i, iter, 0); + } + for (i=4096; i<=(1<<19); i+=4096) { + MPI_Barrier(MPI_COMM_WORLD); + read_bw(fd, PARTITION_PID_LB, oid, i, iter, 0); + } +#if 0 + for (i=4096; i<=(1<<19); i+=4096) + write_bw(fd, PARTITION_PID_LB, oid, i, iter, 1); + if (rank == 0) { + printf("\n\n"); + } + for (i=4096; i<=(1<<19); i+=4096) + read_bw(fd, PARTITION_PID_LB, oid, i, iter, 1); + if (rank == 0) { + printf("\n\n"); + } +#endif + } + + obj_remove(fd, PARTITION_PID_LB, oid); + + close(fd); + MPI_Finalize(); + return 0; +} + diff --git a/osd-initiator/tests/iospeed.c b/osd-initiator/tests/iospeed.c new file mode 100644 index 0000000..8594561 --- /dev/null +++ b/osd-initiator/tests/iospeed.c @@ -0,0 +1,305 @@ +/* + * IO throughput. + * + * Copyright (C) 2007 OSD Team + * + * 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 "osd-util/osd-util.h" +#include "command.h" +#include "device.h" +#include "drivelist.h" +#include "sync.h" + +static uint64_t obj_create_any(int fd, uint64_t pid) +{ + struct osd_command command; + struct attribute_list attr = { + .type = ATTR_GET, + .page = CUR_CMD_ATTR_PG, + .number = CCAP_OID, + .len = 8, + }; + int ret; + uint64_t oid; + + osd_command_set_create(&command, pid, 0, 0); + osd_command_attr_build(&command, &attr, 1); + ret = osd_submit_and_wait(fd, &command); + if (ret) { + osd_error_xerrno(ret, "%s: submit_and_wait failed", __func__); + exit(1); + } + ret = osd_command_attr_resolve(&command); + if (ret) { + osd_error_xerrno(ret, "%s: attr_resolve failed", __func__); + exit(1); + } + oid = get_ntohll(command.attr[0].val); + osd_command_attr_free(&command); + return oid; +} + +static void obj_remove(int fd, uint64_t pid, uint64_t oid) +{ + struct osd_command command; + int ret; + + osd_command_set_remove(&command, pid, oid); + ret = osd_submit_and_wait(fd, &command); + if (ret) { + osd_error_xerrno(ret, "%s: submit_and_wait failed", __func__); + exit(1); + } +} + +static void read_bw(int fd, uint64_t pid, uint64_t oid, + size_t sz, int iters, int dosync) +{ + int i = 0; + int ret = 0; + uint64_t start, end, delta; + double mhz = get_mhz(); + double time = 0.0; + double mu, sd; + double *b = NULL; + void *buf = NULL; + + buf = malloc(sz); + b = malloc(iters * sizeof(*b)); + + if (!buf || !b) + osd_error_fatal("out of memory"); + + /* warm up */ + for (i=0; i<5; i++) { + ret = read_osd(fd, pid, oid, buf, sz, 0); + assert(ret == 0); + } + + for (i=0; i< iters; i++) { + if (dosync) { + rdtsc(start); + ret = read_osd(fd, pid, oid, buf, sz, 0); + rdtsc(end); + assert(ret == 0); + delta = end - start; + + rdtsc(start); + ret = flush_object(fd, pid, oid, 0, 0, 0); + rdtsc(end); + assert(ret == 0); + delta += (end - start); + } else { + rdtsc(start); + ret = read_osd(fd, pid, oid, buf, sz, 0); + rdtsc(end); + assert(ret == 0); + delta = end - start; + } + + time = ((double)delta)/mhz; /* time in usec */ + b[i] = sz/time; /* BW in MegaBytes/sec */ + } + + mu = mean(b, iters); + sd = stddev(b, mu, iters); + if (dosync) + printf("read-sync %3zu %7.3lf +- %7.3lf\n", sz>>10, mu, sd); + else + printf("read %3zu %7.3lf +- %7.3lf\n", sz>>10, mu, sd); + +/* uint8_t *p = buf; + printf("%x %x %x %x %x\n", p[0], p[127], p[sz-256], p[sz-2], p[sz-1]); */ + free(buf); + free(b); +} + +static void write_bw(int fd, uint64_t pid, uint64_t oid, + size_t sz, int iters, int dosync) +{ + int i = 0; + int ret = 0; + uint64_t start, end, delta; + double mhz = get_mhz(); + double time = 0.0; + double mu, sd; + double *b = NULL; + void *buf = NULL; + /* uint8_t *p = NULL; */ + + buf = malloc(sz); + b = malloc(iters * sizeof(*b)); + + if (!buf || !b) + osd_error_fatal("out of memory"); + +#if 0 + assert(sz > 256); + + p = buf; + memset(p, 0xde, 128); + p[0] = 0; + p += 128; + memset(p, 0xa5, sz - 256); + p += (sz - 256); + memset(p, 0xef, 128); + p[127] = 0; +#endif + + /* warm up */ + for (i=0; i< 5; i++) { + ret = write_osd(fd, pid, oid, buf, sz, 0); + assert(ret == 0); + } + + for (i=0; i< iters; i++) { + if (dosync) { + rdtsc(start); + ret = write_osd(fd, pid, oid, buf, sz, 0); + rdtsc(end); + assert(ret == 0); + delta = end - start; + + rdtsc(start); + /* XXX: this may be broken, numbers reported + * are too big to be reasonable */ + ret = flush_object(fd, pid, oid, 0, 0, 0); + rdtsc(end); + assert(ret == 0); + + delta += end - start; + } else { + rdtsc(start); + ret = write_osd(fd, pid, oid, buf, sz, 0); + rdtsc(end); + assert(ret == 0); + delta = end - start; + } + + time = ((double)delta)/mhz; /* time in usec */ + b[i] = sz/time; /* BW in MegaBytes/sec */ + } + + mu = mean(b, iters); + sd = stddev(b, mu, iters); + if (dosync) + printf("write-sync %3zu %7.3lf +- %7.3lf\n", sz>>10, mu, sd); + else + printf("write %3zu %7.3lf +- %7.3lf\n", sz>>10, mu, sd); + + free(buf); + free(b); +} + +static void usage(void) +{ + fprintf(stderr, "Usage: %s []\n", osd_get_progname()); + exit(1); +} + +int main(int argc, char *argv[]) +{ + int fd, ret, num_drives, i; + struct osd_drive_description *drives; + const int iter = 100; + uint64_t oid; + int onesize = 0; + + osd_set_progname(argc, argv); + + if (argc == 1) { + ; + } else if (argc == 2) { + char *cp; + onesize = strtoul(argv[1], &cp, 0); + if (*cp != '\0') + usage(); + onesize <<= 10; + } else { + usage(); + } + + ret = osd_get_drive_list(&drives, &num_drives); + if (ret < 0) { + osd_error("%s: get drive error", __func__); + return 1; + } + if (num_drives == 0) { + osd_error("%s: no drives", __func__); + return 1; + } + + i = 0; + osd_debug("drive %s name %s", drives[i].chardev, drives[i].targetname); + fd = open(drives[i].chardev, O_RDWR); + if (fd < 0) { + osd_error_errno("%s: open %s", __func__, drives[i].chardev); + return 1; + } + osd_free_drive_list(drives, num_drives); + + inquiry(fd); + + format_osd(fd, 1<<30); + create_partition(fd, PARTITION_PID_LB); + + oid = obj_create_any(fd, PARTITION_PID_LB); + +#if 0 + for (i=(1<<19); i<=(1<<19); i <<= 1) { + printf("write/read %d\n", i); + write_bw(fd, PARTITION_PID_LB, oid, i, iter, 0); + read_bw(fd, PARTITION_PID_LB, oid, i, iter, 0); + } +#else + printf("# osd_initiator/tests/iospeed\n"); + printf("# type size (kB) rate (MB/s) +- stdev\n"); + if (onesize) { + write_bw(fd, PARTITION_PID_LB, oid, onesize, iter, 0); + read_bw(fd, PARTITION_PID_LB, oid, onesize, iter, 0); + } else { + for (i=4096; i<(1<<19); i+=4096) + write_bw(fd, PARTITION_PID_LB, oid, i, iter, 0); + printf("\n\n"); + for (i=4096; i<(1<<19); i+=4096) + read_bw(fd, PARTITION_PID_LB, oid, i, iter, 0); + printf("\n\n"); +#if 0 + for (i=4096; i<=(1<<19); i+=4096) + write_bw(fd, PARTITION_PID_LB, oid, i, iter, 1); + printf("\n\n"); + for (i=4096; i<=(1<<19); i+=4096) + read_bw(fd, PARTITION_PID_LB, oid, i, iter, 1); + printf("\n\n"); +#endif + } +#endif + + obj_remove(fd, PARTITION_PID_LB, oid); + + close(fd); + return 0; +} + diff --git a/osd-initiator/tests/iovec.c b/osd-initiator/tests/iovec.c new file mode 100644 index 0000000..61d8a03 --- /dev/null +++ b/osd-initiator/tests/iovec.c @@ -0,0 +1,240 @@ +/* + * Test big and odd iovecs for read and write. + * + * Copyright (C) 2007 OSD Team + * + * 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 "osd-util/osd-util.h" +#include "command.h" +#include "device.h" +#include "drivelist.h" +#include "sync.h" + +static uint64_t obj_create_any(int fd, uint64_t pid) +{ + struct osd_command command; + struct attribute_list attr = { + .type = ATTR_GET, + .page = CUR_CMD_ATTR_PG, + .number = CCAP_OID, + .len = 8, + }; + int ret; + uint64_t oid; + + osd_command_set_create(&command, pid, 0, 0); + osd_command_attr_build(&command, &attr, 1); + ret = osd_submit_and_wait(fd, &command); + if (ret) { + osd_error_xerrno(ret, "%s: submit_and_wait failed", __func__); + exit(1); + } + osd_command_attr_resolve(&command); + oid = get_ntohll(command.attr[0].val); + osd_command_attr_free(&command); + return oid; +} + +static void obj_remove(int fd, uint64_t pid, uint64_t oid) +{ + struct osd_command command; + int ret; + + osd_command_set_remove(&command, pid, oid); + ret = osd_submit_and_wait(fd, &command); + if (ret) { + osd_error_xerrno(ret, "%s: submit_and_wait failed", __func__); + exit(1); + } +} + +/* + * Numvec limit imposed by bio when allocating bvecs. 256 is max, but + * if something strattles a page boundary, that costs an extra vec. So + * here 256 is likely to fail. + */ +static void iovec_write_test(int fd, uint64_t pid) +{ + struct osd_command command; + struct bsg_iovec *vec; + int ret, i, numvec; + uint64_t oid; + uint8_t *buf; + const uint32_t buflen = (300 << 10) + 12; + + buf = Malloc(buflen); + for (numvec=1; numvec<=128; numvec *= 2) { + memset(buf, 0x5a, buflen); + oid = obj_create_any(fd, pid); + osd_info("%s: write numvec %d buflen %d", __func__, numvec, + buflen); + if (numvec == 0) + vec = (void *) buf; + else { + uint32_t onesize = buflen / numvec; + uint32_t pos = 0; + vec = Malloc(numvec * sizeof(*vec)); + for (i=0; i 0) + free(vec); + + /* read it back, non-iov */ + osd_info("%s: read numvec %d buflen %d", __func__, numvec, + buflen); + memset(buf, 0, buflen); + osd_command_set_read(&command, pid, oid, buflen, 0); + command.inlen_alloc = buflen; + command.indata = buf; + ret = osd_submit_and_wait(fd, &command); + if (ret) + osd_error("%s: read submit_and_wait failed", __func__); + + for (i=0; i<(int)buflen; i++) + if (buf[i] != 0x5a) { + osd_error("%s: wrong byte %02x at pos %d", + __func__, buf[i], i); + return; + } + + obj_remove(fd, pid, oid); + } + free(buf); +} + +static void iovec_read_test(int fd, uint64_t pid) +{ + struct osd_command command; + struct bsg_iovec *vec; + int ret, i, numvec; + uint64_t oid; + uint8_t *buf; + const uint32_t buflen = (300 << 10) + 12; + + buf = Malloc(buflen); + for (numvec=1; numvec<=128; numvec *= 2) { + oid = obj_create_any(fd, pid); + + /* write it, non-iov */ + osd_info("%s: write numvec %d buflen %d", __func__, numvec, + buflen); + memset(buf, 0x5a, buflen); + osd_command_set_write(&command, pid, oid, buflen, 0); + command.outlen = buflen; + command.outdata = buf; + ret = osd_submit_and_wait(fd, &command); + if (ret) + osd_error("%s: write submit_and_wait failed", __func__); + + osd_info("%s: read numvec %d buflen %d", __func__, numvec, + buflen); + memset(buf, 0, buflen); + if (numvec == 0) + vec = (void *) buf; + else { + uint32_t onesize = buflen / numvec; + uint32_t pos = 0; + vec = Malloc(numvec * sizeof(*vec)); + for (i=0; i 0) + free(vec); + + for (i=0; i<(int)buflen; i++) + if (buf[i] != 0x5a) { + osd_error("%s: wrong byte %02x at pos %d", + __func__, buf[i], i); + return; + } + + obj_remove(fd, pid, oid); + } + free(buf); +} + +int main(int argc, char *argv[]) +{ + int fd, ret, num_drives, i; + struct osd_drive_description *drives; + + osd_set_progname(argc, argv); + + ret = osd_get_drive_list(&drives, &num_drives); + if (ret < 0) { + osd_error("%s: get drive error", __func__); + return 1; + } + if (num_drives == 0) { + osd_error("%s: no drives", __func__); + return 1; + } + + i = 0; + printf("%s: drive %s name %s\n", osd_get_progname(), drives[i].chardev, + drives[i].targetname); + fd = open(drives[i].chardev, O_RDWR); + if (fd < 0) { + osd_error_errno("%s: open %s", __func__, drives[i].chardev); + return 1; + } + osd_free_drive_list(drives, num_drives); + + inquiry(fd); + + format_osd(fd, 1<<30); + create_partition(fd, PARTITION_PID_LB); + + iovec_write_test(fd, PARTITION_PID_LB); + iovec_read_test(fd, PARTITION_PID_LB); + + close(fd); + return 0; +} + diff --git a/osd-initiator/tests/latency.c b/osd-initiator/tests/latency.c new file mode 100644 index 0000000..ebc99a9 --- /dev/null +++ b/osd-initiator/tests/latency.c @@ -0,0 +1,572 @@ +/* + * Per-operation timings for a few critical operations. + * + * Copyright (C) 2007 OSD Team + * + * 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 "osd-util/osd-util.h" +#include "command.h" +#include "device.h" +#include "drivelist.h" +#include "sync.h" + +static void noop_test(int fd) +{ + int i, ret; + uint64_t start, end, delta; + double mu, stdev; + double *v; + struct osd_command command; + const int iter = 10000; + /* const int iter = 1; */ + + v = malloc(iter * sizeof(*v)); + if (!v) + osd_error_fatal("out of memory"); + + osd_command_set_test_unit_ready(&command); + + /* warm up */ + for (i=0; i<50; i++) { + ret = osd_submit_and_wait(fd, &command); + assert(ret == 0); + } + + for (i=0; i> out_$j; + done; +done diff --git a/osd-initiator/tests/run-lat.sh b/osd-initiator/tests/run-lat.sh new file mode 100755 index 0000000..761aaf7 --- /dev/null +++ b/osd-initiator/tests/run-lat.sh @@ -0,0 +1,59 @@ +#!/bin/bash +# +# Run the latency test for different iSCSI networks. +# +# Copyright (C) 2007 OSD Team +# +# 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 . + +out=./latency.dat +bin=./latency +#bin="echo ran" +node=ib29 +tcp=10.100.2.69 +ipoib=10.100.5.29 +iser=$ipoib +updatetcp="--op update -n node.transport_name -v tcp" +updateiser="--op update -n node.transport_name -v iser" + +iscsiadm -m discovery -t sendtargets -p $tcp:3260 +iscsiadm -m discovery -t sendtargets -p $ipoib:3260 + +iscsiadm -m node -T $node -p $tcp:3260 $updatetcp +iscsiadm -m node -T $node -p $tcp:3260 --login +echo "# TCP" > $out +$bin >> $out +echo >> $out +iscsiadm -m node -T $node -p $tcp:3260 --logout + + +iscsiadm -m node -T $node -p $ipoib:3260 $updatetcp +iscsiadm -m node -T $node -p $ipoib:3260 --login +echo "# IPoIB" >> $out +$bin >> $out +echo >> $out +iscsiadm -m node -T $node -p $ipoib:3260 --logout + +iscsiadm -m node -T $node -p $iser:3260 $updateiser +iscsiadm -m node -T $node -p $iser:3260 --login + +if [ "$?" -eq "0" ]; +then + echo "# iSER" >> $out +else + echo "Cannot login" && exit +fi + +$bin >> $out +echo >> $out +iscsiadm -m node -T $node -p $iser:3260 --logout diff --git a/osd-initiator/tests/sgdd.py b/osd-initiator/tests/sgdd.py new file mode 100755 index 0000000..9e540e1 --- /dev/null +++ b/osd-initiator/tests/sgdd.py @@ -0,0 +1,53 @@ +#!/usr/bin/python -tu +# +# Run sg_dd throughput timings. +# +# Copyright (C) 2007 OSD Team +# +# 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 . + +import re, sys +import commands +from stats import stats + +st = stats() + +time = re.compile("^time", re.IGNORECASE) +sp = re.compile("\s+") + +# iflag=direct if=/dev/sdb of=/tmp/z +def run(exp): + if exp == "read": + var = "if=/dev/sdb of=/dev/null" + elif exp == "write": + var = "of=/dev/sdb if=/dev/zero" + + for bs in range(4, 512, 4): + r = [] + for j in range(100): + s = "sg_dd blk_sgio=1 " + var +" bs=" + str(bs) + "K bpt=1 " + \ + "count=1 time=1 2>&1" + o = commands.getoutput(str(s)) + assert bool(time.match(o)) + w = sp.split(o) + t = float(w[4]) + r.append(bs*1024/(t*1e6)) + print bs, st.mean(r), "+-", st.stdev(r) + + +if __name__ == "__main__": + run("write") + print "\n\n" + run("read") + + diff --git a/osd-initiator/tests/sgio.c b/osd-initiator/tests/sgio.c new file mode 100644 index 0000000..93076bd --- /dev/null +++ b/osd-initiator/tests/sgio.c @@ -0,0 +1,459 @@ +/* + * Test the use of the existing SG_IO interface to transport OSD commands. + * + * Copyright (C) 2007-8 OSD Team + * + * 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 "osd-util/osd-util.h" +#include "command.h" +#include "device.h" +#include "drivelist.h" +#include "sync.h" + +static const uint64_t PID = 0x10000LLU; +static const uint64_t OID = 0x10000LLU; +static const uint64_t PAGE = 0; +static const uint16_t NUM_USER_OBJ = 1; +static const uint64_t OFFSET = 0; +static const int FLUSH_SCOPE = 2; /* Flush everything */ +static const int OBJ_CAPACITY = 1<<30; /* 1 GB */ +static const uint8_t WRITEDATA[] = "Write some data"; +static const uint8_t WRITEDATA2[] = "Test #2"; +static const uint8_t WRITEDATA3[] = "write data 3"; + +static int bidi_test(int fd, uint64_t pid, uint64_t oid) +{ + int ret; + struct osd_command command; + struct attribute_list *attr, attr_proto = { + .page = 0x1, + .number = 0x82, /* logical length (not used capacity) */ + .len = sizeof(uint64_t), + }; + + osd_info(__func__); + ret = osd_command_set_get_attributes(&command, pid, oid); + if (ret) { + osd_error_xerrno(ret, "%s: get_attributes failed", __func__); + printf("\n"); + return 1; + } + ret = osd_command_attr_build(&command, &attr_proto, 1); + if (ret) { + osd_error_xerrno(ret, "%s: attr_build failed", __func__); + printf("\n"); + return 1; + } + memset(command.indata, 0xaa, command.inlen_alloc); + ret = osd_submit_and_wait(fd, &command); + if (ret) { + osd_error_xerrno(ret, "%s: submit failed", __func__); + printf("\n"); + return 1; + } + printf("%s: status %u sense len %u inlen %zu\n", __func__, + command.status, command.sense_len, command.inlen); + + /* verify retrieved list */ + osd_hexdump(command.indata, command.inlen_alloc); + ret = osd_command_attr_resolve(&command); + if (ret) { + osd_error("%s: attr_resolve failed", __func__); + printf("\n"); + exit(1); + } + attr = command.attr; + + if (attr->outlen != attr->len) { + osd_error("%s: short attr outlen %d", __func__, + attr->outlen); + exit(1); + } + + printf("%s: logical length 0x%016llx\n\n", __func__, + llu(get_ntohll(attr->val))); + + osd_command_attr_free(&command); + return 0; +} + +static void iovec_write_test(int fd, uint64_t pid, uint64_t oid) +{ + struct osd_command command; + const char buf1[] = "If iovec_write_test works,"; + const char buf2[] = " you will see this sentence."; + char bufout[200]; + struct bsg_iovec vec[2]; + size_t tot_len; + int ret; + + osd_info(__func__); + vec[0].iov_base = (uint64_t)(uintptr_t) buf1; + vec[0].iov_len = sizeof(buf1)-1; + vec[1].iov_base = (uint64_t)(uintptr_t) buf2; + vec[1].iov_len = sizeof(buf2); + tot_len = sizeof(buf1)-1 + sizeof(buf2); + memset(&command, 0, sizeof(command)); + osd_command_set_write(&command, pid, oid, tot_len, 0); + command.cdb_len = OSD_CDB_SIZE; + command.outlen = tot_len; + command.outdata = vec; + command.iov_outlen = 2; + + ret = osd_submit_and_wait(fd, &command); + if (ret) { + osd_error("%s: submit_and_wait failed", __func__); + return; + } + printf("%s: seemed to work\n", __func__); + + /* read it back, non-iov */ + memset(&command, 0, sizeof(command)); + memset(bufout, 0, sizeof(bufout)); + osd_command_set_read(&command, pid, oid, sizeof(bufout), 0); + command.cdb_len = OSD_CDB_SIZE; + command.inlen_alloc = sizeof(bufout); + command.indata = bufout; + + ret = osd_submit_and_wait(fd, &command); + if (ret) + osd_error("%s: submit_and_wait failed", __func__); + printf("%s: read some bytes (%zu): %s\n\n", __func__, + command.inlen, bufout); +} + +static void iovec_read_test(int fd, uint64_t pid, uint64_t oid) +{ + struct osd_command command; + const char bufout[] = "A big line of data for iovec_read_test to get."; + char buf1[21]; + char buf2[100]; + struct bsg_iovec vec[2]; + size_t tot_len; + int ret; + + /* write it, non-iov */ + osd_info(__func__); + memset(&command, 0, sizeof(command)); + osd_command_set_write(&command, pid, oid, sizeof(bufout), 0); + command.cdb_len = OSD_CDB_SIZE; + command.outlen = sizeof(bufout); + command.outdata = bufout; + ret = osd_submit_and_wait(fd, &command); + if (ret) { + osd_error("%s: submit_and_wait failed", __func__); + return; + } + + memset(buf1, 0, sizeof(buf1)); + memset(buf2, 0, sizeof(buf2)); + vec[0].iov_base = (uint64_t)(uintptr_t) buf1; + vec[0].iov_len = sizeof(buf1)-1; + vec[1].iov_base = (uint64_t)(uintptr_t) buf2; + vec[1].iov_len = sizeof(buf2); + tot_len = sizeof(buf1)-1 + sizeof(buf2); + memset(&command, 0, sizeof(command)); + osd_command_set_read(&command, pid, oid, tot_len, 0); + command.cdb_len = OSD_CDB_SIZE; + command.inlen_alloc = tot_len; + command.indata = vec; + command.iov_inlen = 2; + + ret = osd_submit_and_wait(fd, &command); + if (ret) { + osd_error("%s: submit_and_wait failed", __func__); + return; + } + + buf1[sizeof(buf1)-1] = '\0'; /* terminate partial string */ + printf("%s: read some bytes (%zu): %s + %s\n\n", __func__, + command.inlen, buf1, buf2); +} + +static void attr_test(int fd, uint64_t pid, uint64_t oid) +{ + int i, ret; + uint64_t len; + uint8_t *ts; /* odd 6-byte timestamp */ + const uint8_t data[] = "Some data."; + /* const char attr_data[] = "An attribute.\n"; */ + struct osd_command command; + + struct attribute_list *attr, attr_proto[] = { + { + .type = ATTR_GET, + .page = 0x1, /* user info page */ + .number = 0x82, /* logical length */ + .len = sizeof(uint64_t), + }, + { + .type = ATTR_GET, + .page = 0x3, /* user timestamp page */ + .number = 0x1, /* ctitme */ + .len = 6, + }, +#if 0 + { + .type = ATTR_SET, + .page = 0x92, + .number = 0x4, + .val = attr_data, + .len = sizeof(attr_data), + }, + { + .type = ATTR_GET_PAGE, + .page = 0x3, /* user timestamp page */ + .number = 0x1, /* ctitme */ + .val = &ts, + .len = 6, + }, +#endif + }; + + /* so length will be interesting */ + write_osd(fd, pid, oid, data, sizeof(data), 0); + + ret = osd_command_set_get_attributes(&command, pid, oid); + if (ret) { + osd_error("%s: set_get_attributes failed", __func__); + printf("\n"); + return; + } + ret = osd_command_attr_build(&command, attr_proto, + ARRAY_SIZE(attr_proto)); + if (ret) { + osd_error("%s: attr_build failed", __func__); + printf("\n"); + return; + } + ret = osd_submit_and_wait(fd, &command); + if (ret) { + osd_error("%s: submit_and_wait failed", __func__); + printf("\n"); + return; + } + ret = osd_command_attr_resolve(&command); + if (ret) { + osd_error("%s: attr_resolve failed", __func__); + printf("\n"); + return; + } + attr = command.attr; + for (i=0; i + * + * 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 "osd-util/osd-util.h" +#include "command.h" +#include "device.h" +#include "drivelist.h" +#include "sync.h" + + + +static uint64_t obj_create_any(int fd, uint64_t pid) +{ + struct osd_command command; + struct attribute_list attr = { + .type = ATTR_GET, + .page = CUR_CMD_ATTR_PG, + .number = CCAP_OID, + .len = 8, + }; + int ret; + uint64_t oid; + + osd_command_set_create(&command, pid, 0, 0); + osd_command_attr_build(&command, &attr, 1); + ret = osd_submit_and_wait(fd, &command); + if (ret) { + osd_error_xerrno(ret, "%s: submit_and_wait failed", __func__); + exit(1); + } + ret = osd_command_attr_resolve(&command); + if (ret) { + osd_error_xerrno(ret, "%s: attr_resolve failed", __func__); + exit(1); + } + oid = get_ntohll(command.attr[0].val); + osd_command_attr_free(&command); + return oid; +} + +static void obj_remove(int fd, uint64_t pid, uint64_t oid) +{ + struct osd_command command; + int ret; + + osd_command_set_remove(&command, pid, oid); + ret = osd_submit_and_wait(fd, &command); + if (ret) { + osd_error_xerrno(ret, "%s: submit_and_wait failed", __func__); + exit(1); + } +} + + +static void basic_test_sgl(int fd, uint64_t pid, uint64_t oid) +{ + + /* format of the data buff is: + |#offset,len pairs| |offset| |length|....[DATA] + all values are 8 bytes */ + + void *dbuf; + void *xbuf; + void *buf; + uint64_t length = 10; + int size; + int ret; + int offset = 0; + int hdr_offset = 0; + int i; + + /* -------------------------- */ + /* write a buffer of 100 Xs */ + /* -------------------------- */ + xbuf = malloc(100); + memset(xbuf, 'X', 100); + write_osd(fd, pid, oid, xbuf, 100, 0); + + /* --------------------------------------------------------------- */ + /* use scatter and fill in with Ds every other chunk of 10 chars */ + /* --------------------------------------------------------------- */ + + /* 50 bytes of data plus 5 (offset,len) pairs plus the length value */ + size = 50 + (2*sizeof(uint64_t) * 5) + sizeof(uint64_t); + + dbuf = malloc(size); + memset(dbuf, 'D', size); + + set_htonll(dbuf, 5); + hdr_offset += sizeof(uint64_t); + + for(i=0; i<5; i++){ + osd_debug("Offset= %llu Length= %llu", llu(offset), llu(length)); + set_htonll((uint8_t *)dbuf + hdr_offset, offset); + offset += length*2; + hdr_offset += sizeof(uint64_t); + set_htonll((uint8_t *)dbuf + hdr_offset, length); + hdr_offset += sizeof(uint64_t); + } + + ret = write_sgl_osd(fd, pid, oid, dbuf, size, 0); + assert(ret == 0); + + /* ------------------------------------------------------- */ + /* Now check what we just wrote with existing read command */ + /* ------------------------------------------------------- */ + memset(xbuf, '0', 100); + read_osd(fd, pid, oid, xbuf, 100, 0); + + + /* ---------------------------------------------- */ + /* Now try to gather the Ds we wrote previously */ + /* ---------------------------------------------- */ + + size = (2*sizeof(uint64_t) * 5) + sizeof(uint64_t); + buf = malloc(size); + memset(dbuf, 'Z', size); + + set_htonll(buf, 5); + hdr_offset = 0; + hdr_offset += sizeof(uint64_t); + offset = 0; + for (i=0; i<5; i++) { + osd_debug("Offset= %llu Length= %llu", llu(offset), llu(length)); + set_htonll((uint8_t *)buf + hdr_offset, offset); + offset += length*2; + hdr_offset += sizeof(uint64_t); + set_htonll((uint8_t *)buf + hdr_offset, length); + hdr_offset += sizeof(uint64_t); + } + dbuf = malloc(50); /* return buffer */ + ret = read_sgl_osd(fd, pid, oid, buf, size, dbuf, 50, 0); + + /* ---------------------------- */ + /* Test Append command with SGL */ + /* ---------------------------- */ + + /* 50 bytes of data plus 5 (offset,len) pairs plus the length value */ + size = 50 + (2*sizeof(uint64_t) * 5) + sizeof(uint64_t); + + free(dbuf); + dbuf = malloc(size); + memset(dbuf, 'A', size); + + set_htonll(dbuf, 5); + hdr_offset = sizeof(uint64_t); + + offset = 0; + length = 10; + for (i=0; i<5; i++ ) { + osd_debug("Offset= %llu Length= %llu", llu(offset), llu(length)); + set_htonll((uint8_t *)dbuf + hdr_offset, offset); + offset += length*2; + hdr_offset += sizeof(uint64_t); + set_htonll((uint8_t *)dbuf + hdr_offset, length); + hdr_offset += sizeof(uint64_t); + } + + ret = append_sgl_osd(fd, pid, oid, dbuf, size); + assert(ret == 0); + + /* ------------------------------------------------------- */ + /* Now check what we just wrote with existing read command */ + /* ------------------------------------------------------- */ + free(xbuf); + xbuf = malloc(200); + memset(xbuf, 'Z', 200); + read_osd(fd, pid, oid, xbuf, 190, 0); /*10 less b/c way we scatter*/ + + for (i=0; i<190; i++) { + char ch; + memcpy(&ch, (char *)xbuf+i, 1); + if(ch == '\0') + printf("~"); + else + printf("%c", ch); + } + printf("\n"); + + /* ------------------------------------------------ */ + /*Test the create and write functionality - non SGL */ + /* ------------------------------------------------ */ + free(xbuf); + xbuf = malloc(100); + memset(xbuf, 'Y', 100); + + ret = create_and_write_osd(fd, pid, oid+1, xbuf, 100, 0); + assert(ret == 0); + + free(xbuf); + xbuf = malloc(100); + memset(xbuf, '\0', 100); + + read_osd(fd, pid, oid+1, xbuf, 100, 0); + printf("%s\n", (char *) xbuf); + + /* ------------------------------------------------ */ + /*Test the create and write functionality ---- SGL */ + /* ------------------------------------------------ */ + + /* 50 bytes of data plus 5 (offset,len) pairs plus the length value */ + size = 50 + (2*sizeof(uint64_t) * 5) + sizeof(uint64_t); + free(dbuf); + dbuf = malloc(size); + memset(dbuf, 'D', size); + + set_htonll(dbuf, 5); + hdr_offset = sizeof(uint64_t); + offset = 0; + length=10; + + for(i=0; i<5; i++){ + osd_debug("Offset= %llu Length= %llu", llu(offset), llu(length)); + set_htonll((uint8_t *)dbuf + hdr_offset, offset); + offset += length*2; + hdr_offset += sizeof(uint64_t); + set_htonll((uint8_t *)dbuf + hdr_offset, length); + hdr_offset += sizeof(uint64_t); + } + + ret = create_and_write_sgl_osd(fd, pid, oid+2, dbuf, size, 0); + assert(ret == 0); + + free(xbuf); + xbuf = malloc(100); + memset(xbuf, 'Z', 100); + read_osd(fd, pid, oid+2, xbuf, 90, 0); /*10 less b/c way we scatter*/ + + for (i=0; i<100; i++) { + char ch; + memcpy(&ch, (char *)xbuf+i, 1); + if(ch == '\0') + printf("~"); + else + printf("%c", ch); + } + printf("\n"); + + +} + + +static void basic_test_vec(int fd, uint64_t pid, uint64_t oid) +{ + + /* format of the data buff is: + |stride| |length| [DATA] + all values are 8 bytes */ + + void *dbuf; + void *xbuf; + void *buf; + uint64_t length = 10, stride = 20; + int size; + int ret; + int hdr_offset = 0; + int i; + + /* -------------------------- */ + /* write a buffer of 100 Xs */ + /* -------------------------- */ + xbuf = malloc(100); + memset(xbuf, 'X', 100); + write_osd(fd, pid, oid, xbuf, 100, 0); + + /* --------------------------------------------------------------- */ + /* use scatter and fill in with Ds every other chunk of 10 chars */ + /* --------------------------------------------------------------- */ + + /* 50 bytes of data plus stride and length value */ + size = 50 + (2*sizeof(uint64_t)); + + dbuf = malloc(size); + memset(dbuf, 'D', size); + + set_htonll((uint8_t *)dbuf, stride); + hdr_offset = sizeof(uint64_t); + set_htonll((uint8_t *)dbuf + hdr_offset, length); + + ret = write_vec_osd(fd, pid, oid, dbuf, size, 0); + assert(ret == 0); + + /* ------------------------------------------------------- */ + /* Now check what we just wrote with existing read command */ + /* ------------------------------------------------------- */ + memset(xbuf, '0', 100); + read_osd(fd, pid, oid, xbuf, 100, 0); + + /* ---------------------------------------------- */ + /* Now try to gather the Ds we wrote previously */ + /* ---------------------------------------------- */ + free(dbuf); + dbuf = malloc(50); + memset(dbuf, 'Z', 50); + + size = 2*sizeof(uint64_t); + buf = malloc(size); + set_htonll((uint8_t *)buf, stride); + hdr_offset = sizeof(uint64_t); + set_htonll((uint8_t *)buf + hdr_offset, length); + + ret = read_vec_osd(fd, pid, oid, buf, size, dbuf, 50, 0); + + /* ---------------------------- */ + /* Test Append command with SGL */ + /* ---------------------------- */ + + /* 50 bytes of data plus 5 (offset,len) pairs plus the length value */ + size = 50 + (2*sizeof(uint64_t)); + + dbuf = malloc(size); + memset(dbuf, 'A', size); + + set_htonll((uint8_t *)dbuf, stride); + hdr_offset = sizeof(uint64_t); + set_htonll((uint8_t *)dbuf + hdr_offset, length); + + ret = append_vec_osd(fd, pid, oid, dbuf, size); + assert(ret == 0); + + /* ------------------------------------------------------- */ + /* Now check what we just wrote with existing read command */ + /* ------------------------------------------------------- */ + free(xbuf); + xbuf = malloc(200); + memset(xbuf, 'Z', 200); + read_osd(fd, pid, oid, xbuf, 190, 0); /*10 less b/c way we scatter*/ + + for (i=0; i<190; i++) { + char ch; + memcpy(&ch, (char *)xbuf+i, 1); + if(ch == '\0') + printf("~"); + else + printf("%c", ch); + } + printf("\n"); + + /* ------------------------------------------------ */ + /*Test the create and write functionality - non VEC */ + /* ------------------------------------------------ */ + free(xbuf); + xbuf = malloc(100); + memset(xbuf, 'Y', 100); + + ret = create_and_write_osd(fd, pid, oid+1, xbuf, 100, 0); + assert(ret == 0); + + free(xbuf); + xbuf = malloc(100); + memset(xbuf, '\0', 100); + + read_osd(fd, pid, oid+1, xbuf, 100, 0); + printf("%s\n", (char *) xbuf); + + /* ------------------------------------------------ */ + /*Test the create and write functionality ---- VEC */ + /* ------------------------------------------------ */ + + /* 50 bytes of data plus stride and length value */ + size = 50 + (2*sizeof(uint64_t)); + free(dbuf); + dbuf = malloc(size); + memset(dbuf, 'D', size); + + set_htonll((uint8_t *)dbuf, stride); + hdr_offset = sizeof(uint64_t); + set_htonll((uint8_t *)dbuf + hdr_offset, length); + + ret = create_and_write_vec_osd(fd, pid, oid+2, dbuf, size, 0); + assert(ret == 0); + + free(xbuf); + xbuf = malloc(100); + memset(xbuf, 'Z', 100); + read_osd(fd, pid, oid+2, xbuf, 90, 0); /*10 less b/c way we scatter*/ + + for (i=0; i<100; i++) { + char ch; + memcpy(&ch, (char *)xbuf+i, 1); + if(ch == '\0') + printf("~"); + else + printf("%c", ch); + } + printf("\n"); + + +} + +int main(int argc, char *argv[]) +{ + int fd, ret, num_drives, i; + struct osd_drive_description *drives; + uint64_t oid; + + osd_set_progname(argc, argv); + + ret = osd_get_drive_list(&drives, &num_drives); + if (ret < 0) { + osd_error("%s: get drive error", __func__); + return 1; + } + if (num_drives == 0) { + osd_error("%s: no drives", __func__); + return 1; + } + + i = 0; + osd_debug("drive %s name %s", drives[i].chardev, drives[i].targetname); + fd = open(drives[i].chardev, O_RDWR); + if (fd < 0) { + osd_error_errno("%s: open %s", __func__, drives[i].chardev); + return 1; + } + osd_free_drive_list(drives, num_drives); + + inquiry(fd); + + format_osd(fd, 1<<30); + create_partition(fd, PARTITION_PID_LB); + + /*run tests*/ + oid = obj_create_any(fd, PARTITION_PID_LB); + basic_test_sgl(fd, PARTITION_PID_LB, oid); + obj_remove(fd, PARTITION_PID_LB, oid); + obj_remove(fd, PARTITION_PID_LB, oid+1); + obj_remove(fd, PARTITION_PID_LB, oid+2); + + + printf("\n\n\nRunning VECTOR tests now\n\n\n"); + + + oid = obj_create_any(fd, PARTITION_PID_LB); + basic_test_vec(fd, PARTITION_PID_LB, oid); + obj_remove(fd, PARTITION_PID_LB, oid); + + close(fd); + + return 0; +} diff --git a/osd-initiator/tests/stats.py b/osd-initiator/tests/stats.py new file mode 100755 index 0000000..4e89645 --- /dev/null +++ b/osd-initiator/tests/stats.py @@ -0,0 +1,81 @@ +#!/usr/bin/python -tu +# +# Generic statistics. +# +# Copyright (C) 2007 OSD Team +# +# 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 . + +import random +import math + +random.seed() + +class stats: + def mean(self, l): + return sum(l)*1.0/len(l) + + def var(self, l): + mu = self.mean(l) + N = len(l) + var = 0.0 + for i in l: + var += i**2 + var -= N*(mu**2) + return var/(N - 1) + + def stdev(self, l): + return math.sqrt(self.var(l)) + + def median(self, li): + l = li[:] + N = len(l) + if N == 0: + return + if N % 2 == 1: + median = (N+1)/2 - 1 + else: + median = N/2 + return self.select(l, median, 0, N-1) + + def partition(self, l, left, right, pi): + pv = l[pi] + l[right], l[pi] = l[pi], l[right] + ind = left + for i in range(left, right+1): + if l[i] < pv: + l[ind], l[i] = l[i], l[ind] + ind += 1 + l[right], l[ind] = l[ind], l[right] + return ind + + def select(self, l, k, left, right): + while 1: + pi = random.randint(left, right) + ind = self.partition(l, left, right, pi) + if ind == k: + return l[k] + elif ind < k: + left = ind + 1 + else: + right = ind - 1 + def latest(self, l): + return l[len(l) - 1] + + +if __name__ == "__main__": + s = stats() + print s.median([1]) + print s.median([]) + print s.median([10, 11, 14, 9, 2, 5, 7, 23]) + print s.median([5, 3, 4, 2,2, 2,2]) diff --git a/svn_fetch3.sh b/svn_fetch3.sh index cae345f..6928b41 100755 --- a/svn_fetch3.sh +++ b/svn_fetch3.sh @@ -15,6 +15,7 @@ commit_txt=`pwd`/commit.txt start_rev=695 util_first=2918 +initiator_first=2162 log2revlist() { @@ -49,6 +50,7 @@ get_all_revs() { get_rev_list_folder osd-target get_rev_list_folder osd-util +get_rev_list_folder osd-initiator } echo "$0 getting list of revisions to update" @@ -183,6 +185,11 @@ for i in $list; do git_add_files osd-util fi + if [ $(( $i >= $initiator_first )) != 0 ]; then + svn_update_folder osd-initiator $i + git_add_files osd-initiator + fi + echo "DEBUG [" cat $commit_txt echo "] GUBED" diff --git a/svn_fetch_start.sh b/svn_fetch_start.sh index 45a6826..f68216c 100755 --- a/svn_fetch_start.sh +++ b/svn_fetch_start.sh @@ -2,6 +2,7 @@ start_rev=695 util_first=2918 +initiator_first=2162 svn_start_update() { @@ -14,3 +15,4 @@ svn_start_update() svn_start_update osd-target $start_rev svn_start_update osd-util $util_first +svn_start_update osd-initiator $initiator_first