├── .gitignore ├── COPYING ├── HACKING ├── NEWS ├── README ├── aoe.c ├── ata.c ├── bpf.c ├── config.h ├── config ├── config.h.in └── u64.c ├── contrib ├── README ├── persistence │ ├── vblade-generator │ ├── vblade-persistence.txt │ ├── vblade.init.daemon │ ├── vblade.init.generate │ ├── vblade.init.in │ ├── vblade.init.lsb-daemon │ ├── vblade.service │ └── vblade@.service ├── vblade-17-aio.2.README └── vblade-17-aio.2.diff ├── dat.c ├── dat.h ├── fns.h ├── freebsd.c ├── linux.c ├── linux.h ├── makefile ├── sparsefile ├── vblade.8 └── vbladed /.gitignore: -------------------------------------------------------------------------------- 1 | *.orig 2 | cscope.* 3 | *.rej 4 | *~ 5 | *.o 6 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc. 5 | 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Library General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License 307 | along with this program; if not, write to the Free Software 308 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 309 | 310 | 311 | Also add information on how to contact you by electronic and paper mail. 312 | 313 | If the program is interactive, make it output a short notice like this 314 | when it starts in an interactive mode: 315 | 316 | Gnomovision version 69, Copyright (C) year name of author 317 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 318 | This is free software, and you are welcome to redistribute it 319 | under certain conditions; type `show c' for details. 320 | 321 | The hypothetical commands `show w' and `show c' should show the appropriate 322 | parts of the General Public License. Of course, the commands you use may 323 | be called something other than `show w' and `show c'; they could even be 324 | mouse-clicks or menu items--whatever suits your program. 325 | 326 | You should also get your employer (if you work as a programmer) or your 327 | school, if any, to sign a "copyright disclaimer" for the program, if 328 | necessary. Here is a sample; alter the names: 329 | 330 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 331 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 332 | 333 | , 1 April 1989 334 | Ty Coon, President of Vice 335 | 336 | This General Public License does not permit incorporating your program into 337 | proprietary programs. If your program is a subroutine library, you may 338 | consider it more useful to permit linking proprietary applications with the 339 | library. If this is what you want to do, use the GNU Library General 340 | Public License instead of this License. 341 | -------------------------------------------------------------------------------- /HACKING: -------------------------------------------------------------------------------- 1 | Contributions to the vblade are welcome. 2 | 3 | In contributing, though, please stay true to the original simplicity 4 | of the software. Many open source projects suffer from the "creeping 5 | feature demon" phenomenon. If you think the vblade needs a great new 6 | feature, first seriously try to think of a way to accomplish your goal 7 | without adding to the vblade itself. 8 | 9 | Patches should be clean (to the point and easy to read) and should do 10 | one thing. (Avoid, for example, mixing style changes with substantive 11 | changes.) Send multiple patches if necessary. Patches should be 12 | generated with "diff -uprN" if possible, and should be designed to be 13 | applied with "patch -p1". 14 | 15 | When possible, the best way to submit a patch is by sending it to the 16 | aoetools-discuss list. You can subscribe at the aoetools project web 17 | page on sourceforge.net. 18 | 19 | When you send your patch, here are some things to cover: 20 | 21 | * What version of the vblade did you use to generate the patch? 22 | (Hopefully it was the latest.) 23 | 24 | * What was your motivation for creating the patch? That is, what 25 | problem does it solve? 26 | 27 | * What testing did you perform to ensure that your patch did not 28 | introduce bugs and accomplished what you intended? 29 | 30 | * If your changes affect the end-user experience, have you updated 31 | the vblade documentation? 32 | 33 | * Is your email client able to send a patch without changing it? 34 | Many email clients and servers corrupt patches. Please test your 35 | email chain by sending an applying a patch before sending your 36 | patch to the mailing list. 37 | -------------------------------------------------------------------------------- /NEWS: -------------------------------------------------------------------------------- 1 | -*- change-log -*- 2 | 2020-12-02 Andreas Kölbl 3 | Support recent compilers by avoiding multiple definitions 4 | vblade-25 5 | 6 | 2018-08-25 Christoph Biedl 7 | Print helpful error and exit immediately for missing device 8 | vblade-24 9 | 10 | 2017-11-19 Catalin Salgau 11 | On FreeBSD limit used MTU to address BPF limitation 12 | 13 | 2015-06-14 Ed Cashin 14 | Add convenience script for creating sparse files 15 | vblade-23 16 | 17 | 2015-02-25 Catalin Salgau 18 | Warn about Windows problem with CHS misalignment 19 | 20 | 2014-09-13 Ed Cashin 21 | code cleanup: remove unused variables 22 | 23 | 2014-08-10 Ed Cashin 24 | vblade-22 25 | 26 | 2014-06-08 Ed Cashin 27 | update version for v22 release candidate 1 28 | buffer boundary cleanups 29 | FreeBSD BPF and MTU fixes from Catalin Salgau 30 | offset and size options by Christoph Biedl 31 | vblade-22rc1 32 | 33 | 2013-03-18 Ed Cashin 34 | add big-endian support from Daniel Mealha Cabrita 35 | vblade-21 36 | 37 | 2009-08-14 Sam Hopkins 38 | bugfix: aoe command error did not set Error bit in flags 39 | add support for AoEr11 40 | set ident serial to shelf.slot:hostname 41 | vblade-20 42 | 43 | 2008-10-08 Ed Cashin 44 | add Chris Webb's bpf fix for FreeBSD 45 | add Ryan Thomas's fix to stop bufcnt being overridden 46 | vblade-19 47 | 48 | 2008-07-14 Ed Cashin 49 | add Chris Webb's block device options patch 50 | add Chris Webb's socket options patch for better jumbo handling 51 | remove obsolete contrib/o_direct.diff 52 | vblade-18 53 | 54 | 2008-06-09 Ed Cashin 55 | add Chris Webb's latest BPF patch to vblade, remove from contrib 56 | update contributed AIO patch for compatibility with current sources 57 | vblade-17 58 | 59 | 2008-05-07 Ed Cashin 60 | add Chris Webb's AIO patch to the contributions 61 | add Chris Webb's BPF patch to the contributions 62 | vblade-16 63 | 64 | 2008-02-20 Ed Cashin 65 | require the amount of data we use, not the amount ethernet requires 66 | make sure the packet length agrees with the config query length 67 | make sure the packet length agrees with the amount to write 68 | remove newline embedded in fw version field of ATA dev ID response 69 | vblade-15 70 | 71 | 2006-11-20 Sam Hopkins 72 | apply contrib jumbo patch to standard distribution 73 | add jumbo configuration app. note in README 74 | add jumbo README reference to manpage 75 | add mask feature; -m flag 76 | update manpage to describe -m flag 77 | vblade-14 78 | 79 | 2006-10-05 Sam Hopkins 80 | fix confcmd memcpy bug 81 | correct scnt return value in read/write ata response 82 | replace O_RDONLY fallback with explicit stat. root always wins. 83 | vblade-13 84 | 85 | 2006-10-04 Sam Hopkins 86 | fix confcmd buglets 87 | fix atacmd buglets 88 | add atacmd handling for bad argument errors 89 | add O_RDONLY open if O_RDWR fails 90 | add contrib patch directory 91 | add contrib/README 92 | add jumbo patch to contrib 93 | add o_direct patch to contrib 94 | vblade-12 95 | 96 | 2006-09-21 "Adam J. Richter" 97 | add install target for makefile 98 | vblade-11 99 | 100 | 2005-12-06 Ed Cashin 101 | fix u64 configuration on FreeBSD 102 | release vblade-10 103 | 104 | 2005-12-06 Valeriy Glushkov 105 | implemented config string support 106 | added handler for ATA Check power mode command 107 | 108 | 2005-11-15 Ed Cashin 109 | add compatibility with platforms lacking u64 (e.g., Slackware) 110 | release vblade-9 111 | 112 | 2005-11-10 Ed Cashin 113 | call atainit on program startup 114 | put VBLADE_VERSION in dat.h and use it in firmware version 115 | release vblade-7 116 | include Stacey's patch to use p{read,write} on FreeBSD 117 | include Stacey's patch to typedef ulong on FreeBSD 118 | fix makefile dependencies (e.g., rebuild on new aoe.c) 119 | fix config string length specification 120 | include Stacey's patch to avoid compile warnings on FreeBSD 121 | release vblade-8 122 | 123 | 2005-11-10 "Stacey D. Son" 124 | include FreeBSD support 125 | 126 | 2005-10-03 Ed Cashin 127 | don't invoke vblade with dash from vbladed 128 | 129 | 2005-08-31 20:14:12 GMT Ed Cashin 130 | ATA identify: don't juggle bytes in shorts on big endian arch 131 | add manpage for vblade, vbladed 132 | release vblade-6 133 | 134 | 2005-03-17 15:24:30 GMT Ed Cashin 135 | follow up on vblade-2's off-by-one patch, making end of device usable 136 | release vblade-5 137 | 138 | 2005-03-15 22:03:17 GMT Ed Cashin 139 | don't rely on kernel headers for defining the aoe type 0x88a2 140 | release vblade-4 141 | 142 | 2005-03-15 17:27:01 GMT Ed Cashin 143 | docs: aoe-2.6-7 is the first driver to support multiple blades per mac 144 | release vblade-3 145 | 146 | 2005-03-11 18:30:26 GMT Ed Cashin 147 | put 64-bit configuration into config.h file 148 | don't use uninitialized variables 149 | broadcast config query on startup 150 | clarify desired patch format in HACKING 151 | add sah@coraid.com's vblade-1.ata.c.patch: fix off-by-one and ext LBA 152 | add docs, remove daemonizing code from vblade 153 | release vblade-2 154 | 155 | 2005-02-08 20:21:52 GMT Ed Cashin 156 | starting documentation 157 | add script that daemonizes vblade process, logging output 158 | make vblade sources -Wall clean, use daemon(3) 159 | release vblade-1 160 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | 2 | INTRODUCTION 3 | ------------ 4 | 5 | The vblade is a minimal ATA over Ethernet (AoE) storage target. Its 6 | focus is simplicity, not performance or richness of features. It 7 | exports a seekable file available over an ethernet local area network 8 | (LAN) via the AoE data storage protocol. 9 | 10 | The name, "vblade," is historical: It is a virtual EtherDrive (R) 11 | blade. The first AoE target hardware sold by Coraid was in a blade 12 | form factor, ten to a 4-rack-unit chassis. 13 | 14 | The seekable file is typically a block device like /dev/md0 but even 15 | regular files will work. Sparse files can be especially convenient. 16 | When vblade exports the block storage over AoE it becomes a storage 17 | target. Another host on the same LAN can access the storage if it has 18 | a compatible aoe kernel driver. 19 | 20 | BUILDING 21 | -------- 22 | 23 | The following command should build the vblade program on a Linux-based 24 | system: 25 | 26 | make 27 | 28 | For FreeBSD systems, include an extra parameter like so: 29 | 30 | make PLATFORM=freebsd 31 | 32 | EXAMPLES 33 | -------- 34 | 35 | There is a "vbladed" script that daemonizes the program and sends its 36 | output to the logger program. Make sure you have logger installed if 37 | you would like to run vblade as a daemon with the vbladed script. 38 | 39 | ecashin@kokone vblade$ echo 'I have logger' | logger 40 | ecashin@kokone vblade$ tail -3 /var/log/messages 41 | Feb 8 14:52:49 kokone -- MARK -- 42 | Feb 8 15:12:49 kokone -- MARK -- 43 | Feb 8 15:19:56 kokone logger: I have logger 44 | 45 | Here is a short example showing how to export a block device with a 46 | vblade. (This is a loop device backed by a sparse file, but you could 47 | use any seekable file instead of /dev/loop7.) 48 | 49 | ecashin@kokone vblade$ make 50 | cc -Wall -c -o aoe.o aoe.c 51 | cc -Wall -c -o linux.o linux.c 52 | cc -Wall -c -o ata.o ata.c 53 | cc -o vblade aoe.o linux.o ata.o 54 | ecashin@kokone vblade$ su 55 | Password: 56 | root@kokone vblade# modprobe loop 57 | root@kokone vblade# dd if=/dev/zero bs=1k count=1 seek=`expr 1024 \* 4096` of=bd 58 | -file 59 | 1+0 records in 60 | 1+0 records out 61 | 1024 bytes transferred in 0.009901 seconds (103423 bytes/sec) 62 | root@kokone vblade# losetup /dev/loop7 bd-file 63 | root@kokone vblade# ./vblade 9 0 eth0 /dev/loop7 64 | ioctl returned 0 65 | 4294968320 bytes 66 | pid 16967: e9.0, 8388610 sectors 67 | 68 | Here's how you can use the Linux aoe driver to access the storage from 69 | another host on the LAN. 70 | 71 | ecashin@kokone ecashin$ ssh makki 72 | Last login: Mon Feb 7 10:25:04 2005 73 | ecashin@makki ~$ su 74 | Password: 75 | root@makki ecashin# modprobe aoe 76 | root@makki ecashin# aoe-stat 77 | e9.0 eth1 up 78 | root@makki ecashin# mkfs -t ext3 /dev/etherd/e9.0 79 | mke2fs 1.35 (28-Feb-2004) 80 | ... 81 | Creating journal (8192 blocks): done 82 | Writing superblocks and filesystem accounting information: done 83 | 84 | This filesystem will be automatically checked every 24 mounts or 85 | 180 days, whichever comes first. Use tune2fs -c or -i to override. 86 | root@makki ecashin# mkdir /mnt/e9.0 87 | root@makki ecashin# mount /dev/etherd/e9.0 /mnt/e9.0 88 | root@makki ecashin# echo hooray > /mnt/e9.0/test.txt 89 | root@makki ecashin# cat /mnt/e9.0/test.txt 90 | hooray 91 | 92 | Remember: be as careful with these devices as you would with /dev/hda! 93 | 94 | Jumbo Frame Compatibility 95 | ------------------------- 96 | 97 | Vblade can use jumbo frames provided your initiator is jumbo frame 98 | capable. There is one small configuration gotcha to consider 99 | to avoid having the vblade kernel frequently drop frames. 100 | 101 | Vblade uses a raw socket to perform AoE. The linux kernel will 102 | only buffer a certain amount of data for a raw socket. For 2.6 103 | kernels, this value is managed through /proc: 104 | 105 | root@nai aoe# grep . /proc/sys/net/core/rmem_* 106 | /proc/sys/net/core/rmem_default:128000 107 | /proc/sys/net/core/rmem_max:128000 108 | 109 | rmem_max is the max amount a user process may expand the receive 110 | buffer to -- through setsockopt(...) -- and rmem_default is, as you 111 | might expect, the default. 112 | 113 | The gotcha is that this amount to buffer does not relate 114 | to the amount of user data buffered, but the amount of 115 | real data buffered. As an example, the Intel GbE controller 116 | must be given 16KB frames to use an MTU over 8KB. 117 | For each received frame, the kernel must be able to buffer 118 | 16KB, even if the aoe frame is only 60 bytes in length. 119 | 120 | The linux aoe initiator will use 16 outstanding frames when 121 | used with vblade. A good default for ensuring frames are 122 | not dropped is to allocate 16KB for 17 frames: 123 | 124 | for f in /proc/sys/net/core/rmem_*; do echo $((17 * 16 * 1024)) >$f; done 125 | 126 | Be sure to start vblade after changing the buffering defaults 127 | as the buffer value is set when the socket is opened. 128 | 129 | AoE Initiator Compatibility 130 | --------------------------- 131 | 132 | The Linux aoe driver for the 2.6 kernel is compatible if you use 133 | aoe-2.6-7 or newer. You can use older aoe drivers but you will only 134 | be able to see one vblade per MAC address. 135 | 136 | Contrib Patches 137 | --------------- 138 | 139 | see contrib/README 140 | 141 | Kvblade 142 | ------- 143 | 144 | While vblade runs as a userland process (like "ls" or "vi"), there 145 | is another program that runs inside the kernel. It is called 146 | kvblade. It is alpha software. 147 | -------------------------------------------------------------------------------- /aoe.c: -------------------------------------------------------------------------------- 1 | // aoe.c: the ATA over Ethernet virtual EtherDrive (R) blade 2 | #define _GNU_SOURCE 3 | #include "config.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "dat.h" 13 | #include "fns.h" 14 | 15 | enum { 16 | Nmasks= 32, 17 | Nsrr= 256, 18 | Alen= 6, 19 | }; 20 | 21 | uchar masks[Nmasks*Alen]; 22 | int nmasks; 23 | uchar srr[Nsrr*Alen]; 24 | int nsrr; 25 | char config[Nconfig]; 26 | int nconfig = 0; 27 | int maxscnt = 2; 28 | char *ifname; 29 | int bufcnt = Bufcount; 30 | 31 | void 32 | aoead(int fd) // advertise the virtual blade 33 | { 34 | uchar buf[2000]; 35 | Conf *p; 36 | int i; 37 | 38 | p = (Conf *)buf; 39 | memset(p, 0, sizeof *p); 40 | memset(p->h.dst, 0xff, 6); 41 | memmove(p->h.src, mac, 6); 42 | p->h.type = htons(0x88a2); 43 | p->h.flags = Resp; 44 | p->h.maj = htons(shelf); 45 | p->h.min = slot; 46 | p->h.cmd = Config; 47 | p->bufcnt = htons(bufcnt); 48 | p->scnt = maxscnt = (getmtu(sfd, ifname) - sizeof (Ata)) / 512; 49 | p->firmware = htons(FWV); 50 | p->vercmd = 0x10 | Qread; 51 | memcpy(p->data, config, nconfig); 52 | p->len = htons(nconfig); 53 | if (nmasks == 0) 54 | if (putpkt(fd, buf, sizeof *p - sizeof p->data + nconfig) == -1) { 55 | perror("putpkt aoe id"); 56 | return; 57 | } 58 | for (i=0; ih.dst, &masks[i*Alen], Alen); 60 | if (putpkt(fd, buf, sizeof *p - sizeof p->data + nconfig) == -1) 61 | perror("putpkt aoe id"); 62 | } 63 | } 64 | 65 | int 66 | isbcast(uchar *ea) 67 | { 68 | uchar *b = (uchar *)"\377\377\377\377\377\377"; 69 | 70 | return memcmp(ea, b, 6) == 0; 71 | } 72 | 73 | long long 74 | getlba(uchar *p) 75 | { 76 | vlong v; 77 | int i; 78 | 79 | v = 0; 80 | for (i = 0; i < 6; i++) 81 | v |= (vlong)(*p++) << i * 8; 82 | return v; 83 | } 84 | 85 | int 86 | aoeata(Ata *p, int pktlen) // do ATA reqeust 87 | { 88 | Ataregs r; 89 | int len = 60; 90 | int n; 91 | 92 | r.lba = getlba(p->lba); 93 | r.sectors = p->sectors; 94 | r.feature = p->err; 95 | r.cmd = p->cmd; 96 | if (r.cmd != 0xec) 97 | if (!rrok(p->h.src)) { 98 | p->h.flags |= Error; 99 | p->h.error = Res; 100 | return len; 101 | } 102 | if (atacmd(&r, (uchar *)(p+1), maxscnt*512, pktlen - sizeof(*p)) < 0) { 103 | p->h.flags |= Error; 104 | p->h.error = BadArg; 105 | return len; 106 | } 107 | if (!(p->aflag & Write)) 108 | if ((n = p->sectors)) { 109 | n -= r.sectors; 110 | len = sizeof (Ata) + (n*512); 111 | } 112 | p->sectors = r.sectors; 113 | p->err = r.err; 114 | p->cmd = r.status; 115 | return len; 116 | } 117 | 118 | #define QCMD(x) ((x)->vercmd & 0xf) 119 | 120 | // yes, this makes unnecessary copies. 121 | 122 | int 123 | confcmd(Conf *p, int payload) // process conf request 124 | { 125 | int len; 126 | 127 | len = ntohs(p->len); 128 | if (QCMD(p) != Qread) 129 | if (len > Nconfig || len > payload) 130 | return 0; // if you can't play nice ... 131 | switch (QCMD(p)) { 132 | case Qtest: 133 | if (len != nconfig) 134 | return 0; 135 | // fall thru 136 | case Qprefix: 137 | if (len > nconfig) 138 | return 0; 139 | if (memcmp(config, p->data, len)) 140 | return 0; 141 | // fall thru 142 | case Qread: 143 | break; 144 | case Qset: 145 | if (nconfig) 146 | if (nconfig != len || memcmp(config, p->data, len)) { 147 | p->h.flags |= Error; 148 | p->h.error = ConfigErr; 149 | break; 150 | } 151 | // fall thru 152 | case Qfset: 153 | nconfig = len; 154 | memcpy(config, p->data, nconfig); 155 | break; 156 | default: 157 | p->h.flags |= Error; 158 | p->h.error = BadArg; 159 | } 160 | memmove(p->data, config, nconfig); 161 | p->len = htons(nconfig); 162 | p->bufcnt = htons(bufcnt); 163 | p->scnt = maxscnt = (getmtu(sfd, ifname) - sizeof (Ata)) / 512; 164 | p->firmware = htons(FWV); 165 | p->vercmd = 0x10 | QCMD(p); // aoe v.1 166 | return nconfig + sizeof *p - sizeof p->data; 167 | } 168 | 169 | static int 170 | aoesrr(Aoesrr *sh, int len) 171 | { 172 | uchar *m, *e; 173 | int n; 174 | 175 | e = (uchar *) sh + len; 176 | m = (uchar *) sh + Nsrrhdr; 177 | switch (sh->rcmd) { 178 | default: 179 | e: sh->h.error = BadArg; 180 | sh->h.flags |= Error; 181 | break; 182 | case 1: // set 183 | if (!rrok(sh->h.src)) { 184 | sh->h.error = Res; 185 | sh->h.flags |= Error; 186 | break; 187 | } 188 | case 2: // force set 189 | n = sh->nmacs * 6; 190 | if (e < m + n) 191 | goto e; 192 | nsrr = sh->nmacs; 193 | memmove(srr, m, n); 194 | case 0: // read 195 | break; 196 | } 197 | sh->nmacs = nsrr; 198 | n = nsrr * 6; 199 | memmove(m, srr, n); 200 | return Nsrrhdr + n; 201 | } 202 | 203 | static int 204 | addmask(uchar *ea) 205 | { 206 | 207 | uchar *p, *e; 208 | 209 | p = masks; 210 | e = p + nmasks; 211 | for (; p= Nmasks) 215 | return 0; 216 | memmove(p, ea, 6); 217 | nmasks++; 218 | return 1; 219 | } 220 | 221 | static void 222 | rmmask(uchar *ea) 223 | { 224 | uchar *p, *e; 225 | 226 | p = masks; 227 | e = p + nmasks; 228 | for (; pcmd) { 245 | case Medit: 246 | mde = md + mh->nmacs; 247 | for (; mdcmd) { 249 | case MDdel: 250 | rmmask(md->mac); 251 | continue; 252 | case MDadd: 253 | if (addmask(md->mac)) 254 | continue; 255 | mh->merror = MEfull; 256 | mh->nmacs = md - mdi; 257 | goto e; 258 | case MDnop: 259 | continue; 260 | default: 261 | mh->merror = MEbaddir; 262 | mh->nmacs = md - mdi; 263 | goto e; 264 | } 265 | } 266 | // success. fall thru to return list 267 | case Mread: 268 | md = mdi; 269 | for (i=0; ires = md->cmd = 0; 271 | memmove(md->mac, &masks[i*6], 6); 272 | md++; 273 | } 274 | mh->merror = 0; 275 | mh->nmacs = nmasks; 276 | n = sizeof *md * nmasks; 277 | break; 278 | default: 279 | mh->h.flags |= Error; 280 | mh->h.error = BadArg; 281 | } 282 | e: return n + Nmaskhdr; 283 | } 284 | 285 | void 286 | doaoe(Aoehdr *p, int n) 287 | { 288 | int len; 289 | 290 | switch (p->cmd) { 291 | case ATAcmd: 292 | if (n < Natahdr) 293 | return; 294 | len = aoeata((Ata*)p, n); 295 | break; 296 | case Config: 297 | if (n < Ncfghdr) 298 | return; 299 | len = confcmd((Conf *)p, n); 300 | break; 301 | case Mask: 302 | if (n < Nmaskhdr) 303 | return; 304 | len = aoemask((Aoemask *)p, n); 305 | break; 306 | case Resrel: 307 | if (n < Nsrrhdr) 308 | return; 309 | len = aoesrr((Aoesrr *)p, n); 310 | break; 311 | default: 312 | p->error = BadCmd; 313 | p->flags |= Error; 314 | len = n; 315 | break; 316 | } 317 | if (len <= 0) 318 | return; 319 | memmove(p->dst, p->src, 6); 320 | memmove(p->src, mac, 6); 321 | p->maj = htons(shelf); 322 | p->min = slot; 323 | p->flags |= Resp; 324 | if (putpkt(sfd, (uchar *) p, len) == -1) { 325 | perror("write to network"); 326 | exit(1); 327 | } 328 | } 329 | 330 | void 331 | aoe(void) 332 | { 333 | Aoehdr *p; 334 | uchar *buf; 335 | int n, sh; 336 | long pagesz; 337 | enum { bufsz = 1<<16, }; 338 | 339 | if ((pagesz = sysconf(_SC_PAGESIZE)) < 0) { 340 | perror("sysconf"); 341 | exit(1); 342 | } 343 | if ((buf = malloc(bufsz + pagesz)) == NULL) { 344 | perror("malloc"); 345 | exit(1); 346 | } 347 | n = (size_t) buf + sizeof(Ata); 348 | if (n & (pagesz - 1)) 349 | buf += pagesz - (n & (pagesz - 1)); 350 | 351 | aoead(sfd); 352 | 353 | for (;;) { 354 | n = getpkt(sfd, buf, bufsz); 355 | if (n < 0) { 356 | perror("read network"); 357 | exit(1); 358 | } 359 | if (n < sizeof(Aoehdr)) 360 | continue; 361 | p = (Aoehdr *) buf; 362 | if (ntohs(p->type) != 0x88a2) 363 | continue; 364 | if (p->flags & Resp) 365 | continue; 366 | sh = ntohs(p->maj); 367 | if (sh != shelf && sh != (ushort)~0) 368 | continue; 369 | if (p->min != slot && p->min != (uchar)~0) 370 | continue; 371 | if (nmasks && !maskok(p->src)) 372 | continue; 373 | doaoe(p, n); 374 | } 375 | } 376 | 377 | void 378 | usage(void) 379 | { 380 | fprintf(stderr, "usage: %s [-b bufcnt] [-o offset] [-l length] [-d ] [-s] [-r] [ -m mac[,mac...] ] shelf slot netif filename\n", 381 | progname); 382 | exit(1); 383 | } 384 | 385 | /* parseether from plan 9 */ 386 | int 387 | parseether(uchar *to, char *from) 388 | { 389 | char nip[4]; 390 | char *p; 391 | int i; 392 | 393 | p = from; 394 | for(i = 0; i < 6; i++){ 395 | if(*p == 0) 396 | return -1; 397 | nip[0] = *p++; 398 | if(*p == 0) 399 | return -1; 400 | nip[1] = *p++; 401 | nip[2] = 0; 402 | to[i] = strtoul(nip, 0, 16); 403 | if(*p == ':') 404 | p++; 405 | } 406 | return 0; 407 | } 408 | 409 | void 410 | setmask(char *ml) 411 | { 412 | char *p; 413 | int n; 414 | 415 | for (; ml; ml=p) { 416 | p = strchr(ml, ','); 417 | if (p) 418 | *p++ = '\0'; 419 | n = parseether(&masks[nmasks*Alen], ml); 420 | if (n < 0) 421 | fprintf(stderr, "ignoring mask %s, parseether failure\n", ml); 422 | else 423 | nmasks++; 424 | } 425 | } 426 | 427 | int 428 | maskok(uchar *ea) 429 | { 430 | int i, ok = 0; 431 | 432 | for (i=0; !ok && i size) { 532 | fprintf(stderr, "Length %llu too big - exceeds size of file!\n", offset); 533 | exit(1); 534 | } 535 | size = length; 536 | } 537 | ifname = argv[2]; 538 | sfd = dial(ifname, bufcnt); 539 | if (sfd < 0) 540 | return 1; 541 | getea(sfd, ifname, mac); 542 | printf("pid %ld: e%d.%d, %lld sectors %s\n", 543 | (long) getpid(), shelf, slot, size, 544 | readonly ? "O_RDONLY" : "O_RDWR"); 545 | fflush(stdout); 546 | atainit(); 547 | aoe(); 548 | return 0; 549 | } 550 | 551 | -------------------------------------------------------------------------------- /ata.c: -------------------------------------------------------------------------------- 1 | // ata.c: ATA simulator for vblade 2 | #include "config.h" 3 | #include 4 | #include 5 | #include 6 | #include "dat.h" 7 | #include "fns.h" 8 | 9 | enum { 10 | // err bits 11 | UNC = 1<<6, 12 | MC = 1<<5, 13 | IDNF = 1<<4, 14 | MCR = 1<<3, 15 | ABRT = 1<<2, 16 | NM = 1<<1, 17 | 18 | // status bits 19 | BSY = 1<<7, 20 | DRDY = 1<<6, 21 | DF = 1<<5, 22 | DRQ = 1<<3, 23 | ERR = 1<<0, 24 | }; 25 | 26 | static ushort ident[256]; 27 | 28 | static void 29 | setfld(ushort *a, int idx, int len, char *str) // set field in ident 30 | { 31 | uchar *p; 32 | 33 | p = (uchar *)(a+idx); 34 | while (len > 0) { 35 | if (*str == 0) 36 | p[1] = ' '; 37 | else 38 | p[1] = *str++; 39 | if (*str == 0) 40 | p[0] = ' '; 41 | else 42 | p[0] = *str++; 43 | p += 2; 44 | len -= 2; 45 | } 46 | } 47 | 48 | static void 49 | setlba28(ushort *ident, vlong lba) 50 | { 51 | uchar *cp; 52 | 53 | cp = (uchar *) &ident[60]; 54 | *cp++ = lba; 55 | *cp++ = lba >>= 8; 56 | *cp++ = lba >>= 8; 57 | *cp++ = (lba >>= 8) & 0xf; 58 | } 59 | 60 | static void 61 | setlba48(ushort *ident, vlong lba) 62 | { 63 | uchar *cp; 64 | 65 | cp = (uchar *) &ident[100]; 66 | *cp++ = lba; 67 | *cp++ = lba >>= 8; 68 | *cp++ = lba >>= 8; 69 | *cp++ = lba >>= 8; 70 | *cp++ = lba >>= 8; 71 | *cp++ = lba >>= 8; 72 | } 73 | 74 | static void 75 | setushort(ushort *a, int i, ushort n) 76 | { 77 | uchar *p; 78 | 79 | p = (uchar *)(a+i); 80 | *p++ = n & 0xff; 81 | *p++ = n >> 8; 82 | } 83 | 84 | void 85 | atainit(void) 86 | { 87 | char buf[64]; 88 | 89 | setushort(ident, 47, 0x8000); 90 | setushort(ident, 49, 0x0200); 91 | setushort(ident, 50, 0x4000); 92 | setushort(ident, 83, 0x5400); 93 | setushort(ident, 84, 0x4000); 94 | setushort(ident, 86, 0x1400); 95 | setushort(ident, 87, 0x4000); 96 | setushort(ident, 93, 0x400b); 97 | setfld(ident, 27, 40, "Coraid EtherDrive vblade"); 98 | sprintf(buf, "V%d", VBLADE_VERSION); 99 | setfld(ident, 23, 8, buf); 100 | setfld(ident, 10, 20, serial); 101 | } 102 | 103 | 104 | /* The ATA spec is weird in that you specify the device size as number 105 | * of sectors and then address the sectors with an offset. That means 106 | * with LBA 28 you shouldn't see an LBA of all ones. Still, we don't 107 | * check for that. 108 | */ 109 | int 110 | atacmd(Ataregs *p, uchar *dp, int ndp, int payload) // do the ata cmd 111 | { 112 | vlong lba; 113 | ushort *ip; 114 | int n; 115 | enum { MAXLBA28SIZE = 0x0fffffff }; 116 | extern int maxscnt; 117 | 118 | p->status = 0; 119 | switch (p->cmd) { 120 | default: 121 | p->status = DRDY | ERR; 122 | p->err = ABRT; 123 | return 0; 124 | case 0xe7: // flush cache 125 | return 0; 126 | case 0xec: // identify device 127 | if (p->sectors != 1 || ndp < 512) 128 | return -1; 129 | memmove(dp, ident, 512); 130 | ip = (ushort *)dp; 131 | if (size & ~MAXLBA28SIZE) 132 | setlba28(ip, MAXLBA28SIZE); 133 | else 134 | setlba28(ip, size); 135 | setlba48(ip, size); 136 | p->err = 0; 137 | p->status = DRDY; 138 | p->sectors = 0; 139 | return 0; 140 | case 0xe5: // check power mode 141 | p->err = 0; 142 | p->sectors = 0xff; // the device is active or idle 143 | p->status = DRDY; 144 | return 0; 145 | case 0x20: // read sectors 146 | case 0x30: // write sectors 147 | lba = p->lba & MAXLBA28SIZE; 148 | break; 149 | case 0x24: // read sectors ext 150 | case 0x34: // write sectors ext 151 | lba = p->lba & 0x0000ffffffffffffLL; // full 48 152 | break; 153 | } 154 | 155 | // we ought not be here unless we are a read/write 156 | 157 | if (p->sectors > maxscnt || p->sectors*512 > ndp) 158 | return -1; 159 | 160 | if (lba + p->sectors > size) { 161 | p->err = IDNF; 162 | p->status = DRDY | ERR; 163 | p->lba = lba; 164 | return 0; 165 | } 166 | if (p->cmd == 0x20 || p->cmd == 0x24) 167 | n = getsec(bfd, dp, lba+offset, p->sectors); 168 | else { 169 | // packet should be big enough to contain the data 170 | if (payload < 512 * p->sectors) 171 | return -1; 172 | n = putsec(bfd, dp, lba+offset, p->sectors); 173 | } 174 | n /= 512; 175 | if (n != p->sectors) { 176 | p->err = ABRT; 177 | p->status = ERR; 178 | } else 179 | p->err = 0; 180 | p->status |= DRDY; 181 | p->lba += n; 182 | p->sectors -= n; 183 | return 0; 184 | } 185 | 186 | -------------------------------------------------------------------------------- /bpf.c: -------------------------------------------------------------------------------- 1 | // bpf.c: bpf packet filter for linux/freebsd 2 | 3 | #include "config.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "dat.h" 9 | #include "fns.h" 10 | 11 | struct bpf_insn { 12 | ushort code; 13 | uchar jt; 14 | uchar jf; 15 | u_int32_t k; 16 | }; 17 | 18 | struct bpf_program { 19 | uint bf_len; 20 | struct bpf_insn *bf_insns; 21 | }; 22 | 23 | /* instruction classes */ 24 | #define BPF_CLASS(code) ((code) & 0x07) 25 | #define BPF_LD 0x00 26 | #define BPF_LDX 0x01 27 | #define BPF_ST 0x02 28 | #define BPF_STX 0x03 29 | #define BPF_ALU 0x04 30 | #define BPF_JMP 0x05 31 | #define BPF_RET 0x06 32 | #define BPF_MISC 0x07 33 | 34 | /* ld/ldx fields */ 35 | #define BPF_SIZE(code) ((code) & 0x18) 36 | #define BPF_W 0x00 37 | #define BPF_H 0x08 38 | #define BPF_B 0x10 39 | #define BPF_MODE(code) ((code) & 0xe0) 40 | #define BPF_IMM 0x00 41 | #define BPF_ABS 0x20 42 | #define BPF_IND 0x40 43 | #define BPF_MEM 0x60 44 | #define BPF_LEN 0x80 45 | #define BPF_MSH 0xa0 46 | 47 | /* alu/jmp fields */ 48 | #define BPF_OP(code) ((code) & 0xf0) 49 | #define BPF_ADD 0x00 50 | #define BPF_SUB 0x10 51 | #define BPF_MUL 0x20 52 | #define BPF_DIV 0x30 53 | #define BPF_OR 0x40 54 | #define BPF_AND 0x50 55 | #define BPF_LSH 0x60 56 | #define BPF_RSH 0x70 57 | #define BPF_NEG 0x80 58 | #define BPF_JA 0x00 59 | #define BPF_JEQ 0x10 60 | #define BPF_JGT 0x20 61 | #define BPF_JGE 0x30 62 | #define BPF_JSET 0x40 63 | #define BPF_SRC(code) ((code) & 0x08) 64 | #define BPF_K 0x00 65 | #define BPF_X 0x08 66 | 67 | /* ret - BPF_K and BPF_X also apply */ 68 | #define BPF_RVAL(code) ((code) & 0x18) 69 | #define BPF_A 0x10 70 | 71 | /* misc */ 72 | #define BPF_MISCOP(code) ((code) & 0xf8) 73 | #define BPF_TAX 0x00 74 | #define BPF_TXA 0x80 75 | 76 | /* macros for insn array initializers */ 77 | #define BPF_STMT(code, k) { (ushort)(code), 0, 0, k } 78 | #define BPF_JUMP(code, k, jt, jf) { (ushort)(code), jt, jf, k } 79 | 80 | void * 81 | create_bpf_program(int shelf, int slot) 82 | { 83 | struct bpf_program *bpf_program; 84 | struct bpf_insn insns[] = { 85 | /* CHECKTYPE: Load the type into register */ 86 | BPF_STMT(BPF_LD+BPF_H+BPF_ABS, 12), 87 | /* Does it match AoE Type (0x88a2)? No, goto INVALID */ 88 | BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 0x88a2, 0, 10), 89 | /* Load the flags into register */ 90 | BPF_STMT(BPF_LD+BPF_B+BPF_ABS, 14), 91 | /* Check to see if the Resp flag is set */ 92 | BPF_STMT(BPF_ALU+BPF_AND+BPF_K, Resp), 93 | /* Yes, goto INVALID */ 94 | BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 0, 0, 7), 95 | /* CHECKSHELF: Load the shelf number into register */ 96 | BPF_STMT(BPF_LD+BPF_H+BPF_ABS, 16), 97 | /* Does it match shelf number? Yes, goto CHECKSLOT */ 98 | BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, shelf, 1, 0), 99 | /* Does it match broadcast? No, goto INVALID */ 100 | BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 0xffff, 0, 4), 101 | /* CHECKSLOT: Load the slot number into register */ 102 | BPF_STMT(BPF_LD+BPF_B+BPF_ABS, 18), 103 | /* Does it match shelf number? Yes, goto VALID */ 104 | BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, slot, 1, 0), 105 | /* Does it match broadcast? No, goto INVALID */ 106 | BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 0xff, 0, 1), 107 | /* VALID: return -1 (allow the packet to be read) */ 108 | BPF_STMT(BPF_RET+BPF_K, -1), 109 | /* INVALID: return 0 (ignore the packet) */ 110 | BPF_STMT(BPF_RET+BPF_K, 0), 111 | }; 112 | if ((bpf_program = malloc(sizeof(struct bpf_program))) == NULL 113 | || (bpf_program->bf_insns = malloc(sizeof(insns))) == NULL) { 114 | perror("malloc"); 115 | exit(1); 116 | } 117 | bpf_program->bf_len = sizeof(insns)/sizeof(struct bpf_insn); 118 | memcpy(bpf_program->bf_insns, insns, sizeof(insns)); 119 | return (void *)bpf_program; 120 | } 121 | 122 | void 123 | free_bpf_program(void *bpf_program) 124 | { 125 | free(((struct bpf_program *) bpf_program)->bf_insns); 126 | free(bpf_program); 127 | } 128 | -------------------------------------------------------------------------------- /config.h: -------------------------------------------------------------------------------- 1 | #define _FILE_OFFSET_BITS 64 2 | typedef unsigned long long u64; 3 | -------------------------------------------------------------------------------- /config/config.h.in: -------------------------------------------------------------------------------- 1 | #define _FILE_OFFSET_BITS 64 2 | //u64 typedef unsigned long long u64; 3 | -------------------------------------------------------------------------------- /config/u64.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(void) 4 | { 5 | u64 n; 6 | printf("%d\n", (int) n+2); 7 | return 0; 8 | } 9 | -------------------------------------------------------------------------------- /contrib/README: -------------------------------------------------------------------------------- 1 | 2 | The patches in the contrib directory enable features 3 | that either don't work completely, aren't well tested, or 4 | are of limited general use. They can be applied by 5 | using patch in the vblade source directory as follows: 6 | 7 | forfeit:~/vblade-12 # patch -p1 /dev/null || continue 19 | 20 | shelf= 21 | slot= 22 | netif= 23 | filename= 24 | options= 25 | 26 | . "$CONFIG_DIR$CONFIG" 27 | 28 | [ "$netif" ] || continue 29 | [ "$shelf" ] || continue 30 | [ "$slot" ] || continue 31 | [ "$filename" ] || continue 32 | 33 | ln -s "$SERVICEFILE" "$LINK" 34 | done 35 | fi 36 | 37 | exit 0 38 | -------------------------------------------------------------------------------- /contrib/persistence/vblade-persistence.txt: -------------------------------------------------------------------------------- 1 | 2 | = VBLADE-PERSISTENCE(5) 3 | 4 | == NAME 5 | 6 | vblade-persistence - description of the vblade persistence 7 | 8 | == DESCRIPTION 9 | 10 | vblade-persistence uses the files in `/etc/vblade.conf.d/` to manage 11 | exports. File names must end in `.conf`. The "instance" name is the 12 | file name without `.conf`. 13 | 14 | The file format is a POSIX shell fragment. 15 | 16 | The following variables *must* be defined: `netif`, `shelf`, `slot`, 17 | and `filename`. See vblade(8) for their meaning. Incomplete 18 | configuration files are ignored, so are files that are not a valid 19 | shell syntax. 20 | 21 | Additionally, the following variables may be defined: 22 | 23 | * `options` 24 | 25 | Any options as provided by vblade(7). 26 | 27 | * `ionice` 28 | 29 | Use these to define an I/O scheduling class and level for that export. 30 | The value must be understood by ionice(1). 31 | 32 | 33 | == EXAMPLE 34 | 35 | ---- 36 | shelf=14 37 | slot=2 38 | netif=ens3 39 | filename=/dev/mapper/export 40 | options='-r -m 11:22:33:44:55:66,22:33:44:55:66:77 -o 8' 41 | ionice='--class best-effort --classdata 7' 42 | ---- 43 | 44 | 45 | == USAGE 46 | 47 | === On systems using systemd 48 | 49 | Install `vblade-generator` in `/lib/systemd/system-generators/`, and 50 | both `vblade.service` and `vblade@.service` in `/lib/systemd/system/`. 51 | Enable the vblade service, reload systemd. Additional units for each 52 | export should appear, named `vblade@.service`. 53 | 54 | === On systems using SysV init 55 | 56 | Individual instances may be controlled by providing their name as 57 | a second option, e.g. 58 | 59 | ---- 60 | /etc/init.d/vblade status demo 61 | ---- 62 | 63 | Two different init scripts are available: 64 | 65 | ==== `vblade.init.lsb-daemon` 66 | 67 | Uses LSB functions and daemon(1) program to control the instance. 68 | 69 | Pros: daemon(1) is a very fine tool for this, providing also respawning 70 | and output redirection. 71 | 72 | ==== `vblade.init.daemon` 73 | 74 | As above, but without using LSB functions. 75 | 76 | Pros: Should be fairly portable, no thrills. 77 | 78 | ==== Template 79 | 80 | The template for these scripts is `vblade.init.in`, the actual 81 | templating is done using tpage(1p), see `vblade.init.generate`. 82 | 83 | Support for using Debian's start-stop-daemon has been prepared but 84 | requires pid file supprt in vblade to be usable. 85 | 86 | 87 | == BUGS 88 | 89 | On SysV init systems, the configuration files are always sourced as 90 | shell scripts. On systemd systems, the configuration file is just 91 | a key/value store without shell expansion. 92 | 93 | It's a wise idea to run `sh -n` against a configuration file after any 94 | modification for basic format validation. 95 | 96 | 97 | == SEE ALSO 98 | 99 | daemon: 100 | 101 | tpage(1p) 102 | 103 | vblade(8) 104 | 105 | == AUTHOR 106 | 107 | Christoph Biedl 108 | -------------------------------------------------------------------------------- /contrib/persistence/vblade.init.daemon: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | PATH=/sbin:/usr/sbin:/bin:/usr/bin 4 | DESC="vblade export" 5 | NAME=vblade 6 | VBLADE="/usr/sbin/$NAME" 7 | DAEMON=/usr/bin/daemon 8 | IONICE=/usr/bin/ionice 9 | PIDDIR="/var/run/vblade/" 10 | 11 | [ -x "$VBLADE" ] || exit 0 12 | [ -x "$DAEMON" ] || exit 0 13 | 14 | mkdir -p "$PIDDIR" 15 | 16 | # Emulation of LSB functions 17 | VERBOSE=1 18 | log_daemon_msg () { 19 | printf '%s ' "$@" 20 | } 21 | log_end_msg () { 22 | local CODE="$1" 23 | if [ "$CODE" -eq 0 ] ; then 24 | echo '.' 25 | else 26 | echo 'failed!' 27 | fi 28 | } 29 | 30 | # Start a vblade instance 31 | # 32 | # Return 33 | # 0 if daemon has been started 34 | # 1 if daemon was already running 35 | # 2 if daemon could not be started 36 | do_start () { 37 | local INSTANCE="$1" 38 | local CONFIG="$2" 39 | 40 | sh -n "$CONFIG" 2>/dev/null || return 2 41 | 42 | shelf= 43 | slot= 44 | filename= 45 | netif= 46 | options= 47 | ionice= 48 | 49 | . "$CONFIG" 50 | 51 | [ "$netif" ] || return 2 52 | [ "$shelf" ] || return 2 53 | [ "$slot" ] || return 2 54 | [ "$filename" ] || return 2 55 | 56 | if [ "$ionice" ] ; then 57 | if [ -x "$IONICE" ] ; then 58 | ionice="$IONICE $ionice" 59 | else 60 | ionice= 61 | fi 62 | fi 63 | 64 | "$DAEMON" \ 65 | --running \ 66 | --name "$INSTANCE" \ 67 | --pidfiles "$PIDDIR" \ 68 | && return 1 69 | $ionice "$DAEMON" \ 70 | --respawn \ 71 | --name "$INSTANCE" \ 72 | --pidfiles "$PIDDIR" \ 73 | --output daemon.notice \ 74 | --stdout daemon.notice \ 75 | --stderr daemon.err -- \ 76 | $VBLADE $options $shelf $slot $netif $filename || return 2 77 | } 78 | 79 | # Stop a vblade instance 80 | # 81 | # Return 82 | # 0 if daemon has been stopped 83 | # 1 if daemon was already stopped 84 | # 2 if daemon could not be stopped 85 | # other if a failure occurred 86 | do_stop () { 87 | local INSTANCE="$1" 88 | 89 | "$DAEMON" \ 90 | --running \ 91 | --name "$INSTANCE" \ 92 | --pidfiles "$PIDDIR" || return 1 93 | "$DAEMON" \ 94 | --stop \ 95 | --name "$INSTANCE" \ 96 | --pidfiles "$PIDDIR" \ 97 | --stop || return 2 98 | # Wait until the process is gone 99 | for i in $(seq 1 10) ; do 100 | "$DAEMON" \ 101 | --running \ 102 | --name "$INSTANCE" \ 103 | --pidfiles "$PIDDIR" || return 0 104 | done 105 | return 2 106 | } 107 | 108 | EXIT=0 109 | 110 | do_action () { 111 | local CONFIG="$1" 112 | 113 | INSTANCE="$(basename "${CONFIG%%.conf}")" 114 | 115 | case "$ACTION" in 116 | start) 117 | [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$INSTANCE" 118 | do_start "$INSTANCE" "$CONFIG" 119 | case "$?" in 120 | 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; 121 | 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; 122 | esac 123 | ;; 124 | stop) 125 | [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$INSTANCE" 126 | do_stop "$INSTANCE" 127 | case "$?" in 128 | 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; 129 | 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; 130 | esac 131 | ;; 132 | status) 133 | if "$DAEMON" \ 134 | --running \ 135 | --name "$INSTANCE" \ 136 | --pidfiles "$PIDDIR" 137 | then 138 | echo "$DESC instance $INSTANCE is running" 139 | else 140 | echo "$DESC instance $INSTANCE is not running" 141 | EXIT=1 142 | fi 143 | ;; 144 | restart|force-reload) 145 | log_daemon_msg "Restarting $DESC" "$INSTANCE" 146 | do_stop "$INSTANCE" 147 | case "$?" in 148 | 0|1) 149 | do_start "$INSTANCE" "$CONFIG" 150 | case "$?" in 151 | 0) log_end_msg 0 ;; 152 | *) 153 | # Old process is still running or 154 | # failed to start 155 | log_end_msg 1 ;; 156 | esac 157 | ;; 158 | *) 159 | # Failed to stop 160 | log_end_msg 1 161 | ;; 162 | esac 163 | ;; 164 | *) 165 | echo "Usage: /etc/init.d/vblade {start|stop|status|restart|force-reload} [ ...]" >&2 166 | exit 3 167 | ;; 168 | esac 169 | } 170 | 171 | 172 | ACTION="$1" 173 | shift 174 | 175 | if [ "$1" ] ; then 176 | while [ "$1" ] ; do 177 | CONFIG="/etc/vblade.conf.d/$1.conf" 178 | if [ -f "$CONFIG" ] ; then 179 | do_action "$CONFIG" 180 | fi 181 | shift 182 | done 183 | else 184 | for CONFIG in /etc/vblade.conf.d/*.conf ; do 185 | if [ -f "$CONFIG" ] ; then 186 | do_action "$CONFIG" 187 | fi 188 | done 189 | fi 190 | 191 | exit $EXIT 192 | -------------------------------------------------------------------------------- /contrib/persistence/vblade.init.generate: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | TEMPDIR="$(mktemp --directory --tmpdir "vblade.init.generate.$$.XXXXX")" 6 | trap "cd / ; rm -rf \"$TEMPDIR\"" EXIT 7 | 8 | run () { 9 | local OUTPUT="$1" 10 | echo "I: Processing $OUTPUT" 11 | TEMP="$TEMPDIR/$OUTPUT" 12 | shift 13 | tpage "$@" vblade.init.in>"$TEMP" 14 | sh -n "$TEMP" 15 | if [ -f "$OUTPUT" ] && cmp -s "$TEMP" "$OUTPUT" ; then 16 | echo "I: $OUTPUT is fresh" 17 | else 18 | cp "$TEMP" "$OUTPUT" 19 | fi 20 | } 21 | 22 | # run 'vblade.init.debian' --define lsb=1 --define control=ssd 23 | run 'vblade.init.lsb-daemon' --define lsb=1 --define control=daemon 24 | run 'vblade.init.daemon' --define lsb= --define control=daemon 25 | -------------------------------------------------------------------------------- /contrib/persistence/vblade.init.in: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | [% IF lsb -%] 4 | ### BEGIN INIT INFO 5 | # Provides: vblade 6 | # Required-Start: $remote_fs $syslog $network 7 | # Required-Stop: $remote_fs $syslog $network 8 | # Default-Start: 2 3 4 5 9 | # Default-Stop: 0 1 6 10 | # Short-Description: vblade exports 11 | # Description: Manage all vlbade exports defined in 12 | # /etc/vblade.conf.d/ 13 | ### END INIT INFO 14 | 15 | [% END -%] 16 | PATH=/sbin:/usr/sbin:/bin:/usr/bin 17 | DESC="vblade export" 18 | NAME=vblade 19 | VBLADE="/usr/sbin/$NAME" 20 | [% IF control == 'ssd' -%] 21 | [% PERL -%]die ('control=ssd cannot be used as long as vblade as no pidfile support');[% END -%] 22 | [% ELSIF control == 'daemon' -%] 23 | DAEMON=/usr/bin/daemon 24 | [% END -%] 25 | IONICE=/usr/bin/ionice 26 | PIDDIR="/var/run/vblade/" 27 | 28 | [ -x "$VBLADE" ] || exit 0 29 | [% IF control == 'daemon' -%] 30 | [ -x "$DAEMON" ] || exit 0 31 | [% END -%] 32 | 33 | mkdir -p "$PIDDIR" 34 | 35 | [% IF lsb -%] 36 | # Load the VERBOSE setting and other rcS variables 37 | . /lib/init/vars.sh 38 | 39 | # Define LSB functions 40 | . /lib/lsb/init-functions 41 | [% ELSE -%] 42 | # Emulation of LSB functions 43 | VERBOSE=1 44 | log_daemon_msg () { 45 | printf '%s ' "$@" 46 | } 47 | log_end_msg () { 48 | local CODE="$1" 49 | if [ "$CODE" -eq 0 ] ; then 50 | echo '.' 51 | else 52 | echo 'failed!' 53 | fi 54 | } 55 | [% END -%] 56 | 57 | # Start a vblade instance 58 | # 59 | # Return 60 | # 0 if daemon has been started 61 | # 1 if daemon was already running 62 | # 2 if daemon could not be started 63 | do_start () { 64 | local INSTANCE="$1" 65 | local CONFIG="$2" 66 | 67 | sh -n "$CONFIG" 2>/dev/null || return 2 68 | 69 | shelf= 70 | slot= 71 | filename= 72 | netif= 73 | options= 74 | ionice= 75 | 76 | . "$CONFIG" 77 | 78 | [ "$netif" ] || return 2 79 | [ "$shelf" ] || return 2 80 | [ "$slot" ] || return 2 81 | [ "$filename" ] || return 2 82 | 83 | if [ "$ionice" ] ; then 84 | if [ -x "$IONICE" ] ; then 85 | ionice="$IONICE $ionice" 86 | else 87 | ionice= 88 | fi 89 | fi 90 | 91 | [% IF control == 'ssd' -%] 92 | local PIDFILE="$PIDDIR/$INSTANCE.pid" 93 | start-stop-daemon --start --quiet \ 94 | --pidfile "$PIDFILE" --exec "$VBLADE" --test > /dev/null \ 95 | || return 1 96 | start-stop-daemon --start --quiet \ 97 | --pidfile "$PIDFILE" \ 98 | --exec $ionice "$VBLADE" -- \ 99 | $shelf $slot $netif $filename $options \ 100 | || return 2 101 | [% ELSIF control == 'daemon' -%] 102 | "$DAEMON" \ 103 | --running \ 104 | --name "$INSTANCE" \ 105 | --pidfiles "$PIDDIR" \ 106 | && return 1 107 | $ionice "$DAEMON" \ 108 | --respawn \ 109 | --name "$INSTANCE" \ 110 | --pidfiles "$PIDDIR" \ 111 | --output daemon.notice \ 112 | --stdout daemon.notice \ 113 | --stderr daemon.err -- \ 114 | $VBLADE $options $shelf $slot $netif $filename || return 2 115 | [% END -%] 116 | } 117 | 118 | # Stop a vblade instance 119 | # 120 | # Return 121 | # 0 if daemon has been stopped 122 | # 1 if daemon was already stopped 123 | # 2 if daemon could not be stopped 124 | # other if a failure occurred 125 | do_stop () { 126 | local INSTANCE="$1" 127 | 128 | [% IF control == 'ssd' -%] 129 | local PIDFILE="$PIDDIR/$INSTANCE.pid" 130 | start-stop-daemon --stop --quiet \ 131 | --retry=TERM/30/KILL/5 --pidfile "$PIDFILE" --name "$NAME" 132 | RETVAL="$?" 133 | [ "$RETVAL" = 2 ] && return 2 134 | # Many daemons don't delete their pidfiles when they exit. 135 | rm -f "$PIDFILE" 136 | return "$RETVAL" 137 | [% ELSIF control == 'daemon' -%] 138 | "$DAEMON" \ 139 | --running \ 140 | --name "$INSTANCE" \ 141 | --pidfiles "$PIDDIR" || return 1 142 | "$DAEMON" \ 143 | --stop \ 144 | --name "$INSTANCE" \ 145 | --pidfiles "$PIDDIR" \ 146 | --stop || return 2 147 | # Wait until the process is gone 148 | for i in $(seq 1 10) ; do 149 | "$DAEMON" \ 150 | --running \ 151 | --name "$INSTANCE" \ 152 | --pidfiles "$PIDDIR" || return 0 153 | done 154 | return 2 155 | [% END -%] 156 | } 157 | 158 | EXIT=0 159 | 160 | do_action () { 161 | local CONFIG="$1" 162 | 163 | INSTANCE="$(basename "${CONFIG%%.conf}")" 164 | 165 | case "$ACTION" in 166 | start) 167 | [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$INSTANCE" 168 | do_start "$INSTANCE" "$CONFIG" 169 | case "$?" in 170 | 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; 171 | 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; 172 | esac 173 | ;; 174 | stop) 175 | [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$INSTANCE" 176 | do_stop "$INSTANCE" 177 | case "$?" in 178 | 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; 179 | 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; 180 | esac 181 | ;; 182 | status) 183 | [% IF lsb -%] 184 | status_of_proc -p "$PIDDIR/$INSTANCE.pid" "$VBLADE" "vblade instance $INSTANCE" || EXIT=$? 185 | [% ELSE -%] 186 | if "$DAEMON" \ 187 | --running \ 188 | --name "$INSTANCE" \ 189 | --pidfiles "$PIDDIR" 190 | then 191 | echo "$DESC instance $INSTANCE is running" 192 | else 193 | echo "$DESC instance $INSTANCE is not running" 194 | EXIT=1 195 | fi 196 | [% END -%] 197 | ;; 198 | restart|force-reload) 199 | log_daemon_msg "Restarting $DESC" "$INSTANCE" 200 | do_stop "$INSTANCE" 201 | case "$?" in 202 | 0|1) 203 | do_start "$INSTANCE" "$CONFIG" 204 | case "$?" in 205 | 0) log_end_msg 0 ;; 206 | *) 207 | # Old process is still running or 208 | # failed to start 209 | log_end_msg 1 ;; 210 | esac 211 | ;; 212 | *) 213 | # Failed to stop 214 | log_end_msg 1 215 | ;; 216 | esac 217 | ;; 218 | *) 219 | echo "Usage: /etc/init.d/vblade {start|stop|status|restart|force-reload} [ ...]" >&2 220 | exit 3 221 | ;; 222 | esac 223 | } 224 | 225 | 226 | ACTION="$1" 227 | shift 228 | 229 | if [ "$1" ] ; then 230 | while [ "$1" ] ; do 231 | CONFIG="/etc/vblade.conf.d/$1.conf" 232 | if [ -f "$CONFIG" ] ; then 233 | do_action "$CONFIG" 234 | fi 235 | shift 236 | done 237 | else 238 | for CONFIG in /etc/vblade.conf.d/*.conf ; do 239 | if [ -f "$CONFIG" ] ; then 240 | do_action "$CONFIG" 241 | fi 242 | done 243 | fi 244 | 245 | exit $EXIT 246 | -------------------------------------------------------------------------------- /contrib/persistence/vblade.init.lsb-daemon: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | ### BEGIN INIT INFO 4 | # Provides: vblade 5 | # Required-Start: $remote_fs $syslog $network 6 | # Required-Stop: $remote_fs $syslog $network 7 | # Default-Start: 2 3 4 5 8 | # Default-Stop: 0 1 6 9 | # Short-Description: vblade exports 10 | # Description: Manage all vlbade exports defined in 11 | # /etc/vblade.conf.d/ 12 | ### END INIT INFO 13 | 14 | PATH=/sbin:/usr/sbin:/bin:/usr/bin 15 | DESC="vblade export" 16 | NAME=vblade 17 | VBLADE="/usr/sbin/$NAME" 18 | DAEMON=/usr/bin/daemon 19 | IONICE=/usr/bin/ionice 20 | PIDDIR="/var/run/vblade/" 21 | 22 | [ -x "$VBLADE" ] || exit 0 23 | [ -x "$DAEMON" ] || exit 0 24 | 25 | mkdir -p "$PIDDIR" 26 | 27 | # Load the VERBOSE setting and other rcS variables 28 | . /lib/init/vars.sh 29 | 30 | # Define LSB functions 31 | . /lib/lsb/init-functions 32 | 33 | # Start a vblade instance 34 | # 35 | # Return 36 | # 0 if daemon has been started 37 | # 1 if daemon was already running 38 | # 2 if daemon could not be started 39 | do_start () { 40 | local INSTANCE="$1" 41 | local CONFIG="$2" 42 | 43 | sh -n "$CONFIG" 2>/dev/null || return 2 44 | 45 | shelf= 46 | slot= 47 | filename= 48 | netif= 49 | options= 50 | ionice= 51 | 52 | . "$CONFIG" 53 | 54 | [ "$netif" ] || return 2 55 | [ "$shelf" ] || return 2 56 | [ "$slot" ] || return 2 57 | [ "$filename" ] || return 2 58 | 59 | if [ "$ionice" ] ; then 60 | if [ -x "$IONICE" ] ; then 61 | ionice="$IONICE $ionice" 62 | else 63 | ionice= 64 | fi 65 | fi 66 | 67 | "$DAEMON" \ 68 | --running \ 69 | --name "$INSTANCE" \ 70 | --pidfiles "$PIDDIR" \ 71 | && return 1 72 | $ionice "$DAEMON" \ 73 | --respawn \ 74 | --name "$INSTANCE" \ 75 | --pidfiles "$PIDDIR" \ 76 | --output daemon.notice \ 77 | --stdout daemon.notice \ 78 | --stderr daemon.err -- \ 79 | $VBLADE $options $shelf $slot $netif $filename || return 2 80 | } 81 | 82 | # Stop a vblade instance 83 | # 84 | # Return 85 | # 0 if daemon has been stopped 86 | # 1 if daemon was already stopped 87 | # 2 if daemon could not be stopped 88 | # other if a failure occurred 89 | do_stop () { 90 | local INSTANCE="$1" 91 | 92 | "$DAEMON" \ 93 | --running \ 94 | --name "$INSTANCE" \ 95 | --pidfiles "$PIDDIR" || return 1 96 | "$DAEMON" \ 97 | --stop \ 98 | --name "$INSTANCE" \ 99 | --pidfiles "$PIDDIR" \ 100 | --stop || return 2 101 | # Wait until the process is gone 102 | for i in $(seq 1 10) ; do 103 | "$DAEMON" \ 104 | --running \ 105 | --name "$INSTANCE" \ 106 | --pidfiles "$PIDDIR" || return 0 107 | done 108 | return 2 109 | } 110 | 111 | EXIT=0 112 | 113 | do_action () { 114 | local CONFIG="$1" 115 | 116 | INSTANCE="$(basename "${CONFIG%%.conf}")" 117 | 118 | case "$ACTION" in 119 | start) 120 | [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$INSTANCE" 121 | do_start "$INSTANCE" "$CONFIG" 122 | case "$?" in 123 | 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; 124 | 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; 125 | esac 126 | ;; 127 | stop) 128 | [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$INSTANCE" 129 | do_stop "$INSTANCE" 130 | case "$?" in 131 | 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; 132 | 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; 133 | esac 134 | ;; 135 | status) 136 | status_of_proc -p "$PIDDIR/$INSTANCE.pid" "$VBLADE" "vblade instance $INSTANCE" || EXIT=$? 137 | ;; 138 | restart|force-reload) 139 | log_daemon_msg "Restarting $DESC" "$INSTANCE" 140 | do_stop "$INSTANCE" 141 | case "$?" in 142 | 0|1) 143 | do_start "$INSTANCE" "$CONFIG" 144 | case "$?" in 145 | 0) log_end_msg 0 ;; 146 | *) 147 | # Old process is still running or 148 | # failed to start 149 | log_end_msg 1 ;; 150 | esac 151 | ;; 152 | *) 153 | # Failed to stop 154 | log_end_msg 1 155 | ;; 156 | esac 157 | ;; 158 | *) 159 | echo "Usage: /etc/init.d/vblade {start|stop|status|restart|force-reload} [ ...]" >&2 160 | exit 3 161 | ;; 162 | esac 163 | } 164 | 165 | 166 | ACTION="$1" 167 | shift 168 | 169 | if [ "$1" ] ; then 170 | while [ "$1" ] ; do 171 | CONFIG="/etc/vblade.conf.d/$1.conf" 172 | if [ -f "$CONFIG" ] ; then 173 | do_action "$CONFIG" 174 | fi 175 | shift 176 | done 177 | else 178 | for CONFIG in /etc/vblade.conf.d/*.conf ; do 179 | if [ -f "$CONFIG" ] ; then 180 | do_action "$CONFIG" 181 | fi 182 | done 183 | fi 184 | 185 | exit $EXIT 186 | -------------------------------------------------------------------------------- /contrib/persistence/vblade.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=vblade exports 3 | Documentation=man:vblade-persistence(5) 4 | Documentation=man:vblade(8) 5 | 6 | [Service] 7 | Type=oneshot 8 | ExecStart=/bin/true 9 | ExecReload=/bin/true 10 | RemainAfterExit=on 11 | 12 | [Install] 13 | WantedBy=multi-user.target 14 | -------------------------------------------------------------------------------- /contrib/persistence/vblade@.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=vblade instance %I 3 | SourcePath=/etc/vblade.conf.d/%I.conf 4 | Documentation=man:vblade(8) 5 | PartOf=vblade.service 6 | After=rc-local.service 7 | 8 | [Service] 9 | Type=simple 10 | Environment="ionice=-c2 -n7" 11 | EnvironmentFile=/etc/vblade.conf.d/%I.conf 12 | ExecStart=/usr/bin/ionice $ionice /usr/sbin/vblade $shelf $slot $netif $filename $options 13 | SyslogIdentifier=vblade 14 | Restart=always 15 | RestartSec=10 16 | 17 | [Install] 18 | WantedBy=multi-user.target 19 | -------------------------------------------------------------------------------- /contrib/vblade-17-aio.2.README: -------------------------------------------------------------------------------- 1 | This proof-of-concept patch modifies vblade to access the underlying block 2 | device using POSIX asynchronous IO (AIO) rather than using normal blocking 3 | read() and write(). AIO allows vblade to receive and queue several several ATA 4 | read/write commands at once, returning the response to the client 5 | asynchronously as each IO operation completes. It should be most beneficial 6 | for devices which experience very non-sequential IO. An AIO-enabled vblade is 7 | also a good starting point if you want to generalise vblade to export multiple 8 | devices without the complexity and overhead of a multithreaded approach. 9 | 10 | The patch implements AIO support for both Linux and FreeBSD, but I have not 11 | tested the FreeBSD support and would therefore be especially interested to 12 | hear success/failure reports for compiling and running AIO vblade on FreeBSD. 13 | A SIGIO handler which writes a single byte to a pipe is used to notify the 14 | main poll() loop that AIO operations have completed and are ready to return to 15 | the client. Running oprofile on a box with a heavily loaded loopback 16 | vblade-aio suggests that it spends an inordinate amount of time in the signal 17 | handler. Some method of poll()ing directly on the AIO events at the same time 18 | as the socket fd could cut this overhead out completely. 19 | 20 | More generally, experimenting on Linux with standard O_DIRECT vblade and 21 | O_DIRECT vblade-aio on a loopback interface with MTU 9000 suggests that the 22 | performance difference on a single RAID1-backed block device is fairly small: 23 | swamped by the performance of the network and the underlying block device. 24 | However, the POSIX AIO in glibc librt is emulated in userspace threads rather 25 | than using the kernel AIO api. A kernel-backed POSIX AIO implementation should 26 | perform better, especially for multiple access to a single block device. 27 | 28 | I would be delighted to hear any feedback and experiences from people running 29 | vblade together with this patch. 30 | 31 | Chris Webb , 2008-04-21. 32 | -------------------------------------------------------------------------------- /contrib/vblade-17-aio.2.diff: -------------------------------------------------------------------------------- 1 | diff -uprN vblade-17.orig/aoe.c vblade-17/aoe.c 2 | --- vblade-17.orig/aoe.c 2008-06-09 10:53:07.000000000 -0400 3 | +++ vblade-17/aoe.c 2008-06-09 11:05:23.000000000 -0400 4 | @@ -8,6 +8,9 @@ 5 | #include 6 | #include 7 | #include 8 | +#include 9 | +#include 10 | +#include 11 | #include "dat.h" 12 | #include "fns.h" 13 | 14 | @@ -22,6 +25,11 @@ char config[Nconfig]; 15 | int nconfig = 0; 16 | int maxscnt = 2; 17 | char *ifname; 18 | +int queuepipe[2]; 19 | +int pktlen[Nplaces], pending[Nplaces]; 20 | +Ata *pkt[Nplaces]; 21 | +Ataregs regs[Nplaces]; 22 | +struct aiocb aiocb[Nplaces]; 23 | 24 | void 25 | aoead(int fd) // advertise the virtual blade 26 | @@ -78,32 +86,52 @@ getlba(uchar *p) 27 | } 28 | 29 | int 30 | -aoeata(Ata *p, int pktlen) // do ATA reqeust 31 | +aoeata(int place) // do ATA reqeust 32 | { 33 | - Ataregs r; 34 | - int len = 60; 35 | int n; 36 | + int len = 60; // minimum ethernet packet size 37 | 38 | - r.lba = getlba(p->lba); 39 | - r.sectors = p->sectors; 40 | - r.feature = p->err; 41 | - r.cmd = p->cmd; 42 | - if (atacmd(&r, (uchar *)(p+1), maxscnt*512, pktlen - sizeof(*p)) < 0) { 43 | - p->h.flags |= Error; 44 | - p->h.error = BadArg; 45 | + regs[place].lba = getlba(pkt[place]->lba); 46 | + regs[place].sectors = pkt[place]->sectors; 47 | + regs[place].feature = pkt[place]->err; 48 | + regs[place].cmd = pkt[place]->cmd; 49 | + n = atacmd(regs + place, (uchar *)(pkt[place] + 1), maxscnt*512, 50 | + pktlen[place] - sizeof(Ata), aiocb + place); 51 | + if (n < 0) { 52 | + pkt[place]->h.flags |= Error; 53 | + pkt[place]->h.error = BadArg; 54 | return len; 55 | + } else if (n > 0) { 56 | + pending[place] = 1; 57 | + return 0; 58 | + } 59 | + if (!(pkt[place]->aflag & Write) && (n = pkt[place]->sectors)) { 60 | + n -= regs[place].sectors; 61 | + len = sizeof (Ata) + (n*512); 62 | } 63 | - if (!(p->aflag & Write)) 64 | - if ((n = p->sectors)) { 65 | - n -= r.sectors; 66 | + pkt[place]->sectors = regs[place].sectors; 67 | + pkt[place]->err = regs[place].err; 68 | + pkt[place]->cmd = regs[place].status; 69 | + return len; 70 | +} 71 | + 72 | +int aoeatacomplete(int place, int pktlen) 73 | +{ 74 | + int n; 75 | + int len = 60; // minimum ethernet packet size 76 | + atacmdcomplete(regs + place, aiocb + place); 77 | + if (!(pkt[place]->aflag & Write) && (n = pkt[place]->sectors)) { 78 | + n -= regs[place].sectors; 79 | len = sizeof (Ata) + (n*512); 80 | } 81 | - p->sectors = r.sectors; 82 | - p->err = r.err; 83 | - p->cmd = r.status; 84 | + pkt[place]->sectors = regs[place].sectors; 85 | + pkt[place]->err = regs[place].err; 86 | + pkt[place]->cmd = regs[place].status; 87 | + pending[place] = 0; 88 | return len; 89 | } 90 | 91 | + 92 | #define QCMD(x) ((x)->vercmd & 0xf) 93 | 94 | // yes, this makes unnecessary copies. 95 | @@ -156,8 +184,9 @@ confcmd(Conf *p, int payload) // process 96 | } 97 | 98 | void 99 | -doaoe(Aoehdr *p, int n) 100 | +doaoe(int place) 101 | { 102 | + Aoehdr *p = (Aoehdr *) pkt[place]; 103 | int len; 104 | enum { // config query header size 105 | CHDR_SIZ = sizeof(Conf) - sizeof(((Conf *)0)->data), 106 | @@ -165,14 +194,16 @@ doaoe(Aoehdr *p, int n) 107 | 108 | switch (p->cmd) { 109 | case ATAcmd: 110 | - if (n < sizeof(Ata)) 111 | + if (pktlen[place] < sizeof(Ata)) 112 | + return; 113 | + len = aoeata(place); 114 | + if (len == 0) 115 | return; 116 | - len = aoeata((Ata*)p, n); 117 | break; 118 | case Config: 119 | - if (n < CHDR_SIZ) 120 | + if (pktlen[place] < CHDR_SIZ) 121 | return; 122 | - len = confcmd((Conf *)p, n - CHDR_SIZ); 123 | + len = confcmd((Conf *)p, pktlen[place] - CHDR_SIZ); 124 | if (len == 0) 125 | return; 126 | break; 127 | @@ -193,25 +224,129 @@ doaoe(Aoehdr *p, int n) 128 | } 129 | 130 | void 131 | +doaoecomplete(int place) 132 | +{ 133 | + Aoehdr *p = (Aoehdr *) pkt[place]; 134 | + int len = aoeatacomplete(place, pktlen[place]); 135 | + memmove(p->dst, p->src, 6); 136 | + memmove(p->src, mac, 6); 137 | + p->maj = htons(shelf); 138 | + p->min = slot; 139 | + p->flags |= Resp; 140 | + if (putpkt(sfd, (uchar *) p, len) == -1) { 141 | + perror("write to network"); 142 | + exit(1); 143 | + } 144 | + 145 | +} 146 | + 147 | +// allocate the buffer so that the ata data area 148 | +// is page aligned for o_direct on linux 149 | + 150 | +void * 151 | +bufalloc(void **buf, long len) 152 | +{ 153 | + long psize; 154 | + unsigned long n; 155 | + 156 | + psize = sysconf(_SC_PAGESIZE); 157 | + if (psize == -1) { 158 | + perror("sysconf"); 159 | + exit(EXIT_FAILURE); 160 | + } 161 | + n = len/psize + 3; 162 | + *buf = malloc(psize * n); 163 | + if (!*buf) { 164 | + perror("malloc"); 165 | + exit(EXIT_FAILURE); 166 | + } 167 | + n = (unsigned long) *buf; 168 | + n += psize * 2; 169 | + n &= ~(psize - 1); 170 | + return (void *) (n - sizeof (Ata)); 171 | +} 172 | + 173 | +void 174 | +sigio(int signo) 175 | +{ 176 | + const char dummy = 0; 177 | + write(queuepipe[1], &dummy, 1); 178 | +} 179 | + 180 | +void 181 | aoe(void) 182 | { 183 | Aoehdr *p; 184 | - uchar *buf; 185 | - int n, sh; 186 | + char dummy; 187 | + int n, place, sh; 188 | enum { bufsz = 1<<16, }; 189 | - 190 | - buf = malloc(bufsz); 191 | + sigset_t mask, oldmask; 192 | + struct sigaction sigact; 193 | + struct pollfd pollfds[2]; 194 | + void *freeme[Nplaces]; 195 | + 196 | + for (n = 0; n < Nplaces; n++) { 197 | + pkt[n] = bufalloc(freeme + n, bufsz); 198 | + pending[n] = 0; 199 | + } 200 | aoead(sfd); 201 | 202 | + pipe(queuepipe); 203 | + fcntl(queuepipe[0], F_SETFL, O_NONBLOCK); 204 | + fcntl(queuepipe[1], F_SETFL, O_NONBLOCK); 205 | + 206 | + sigemptyset(&sigact.sa_mask); 207 | + sigact.sa_flags = 0; 208 | + sigact.sa_sigaction = (void *) sigio; 209 | + sigaction(SIGIO, &sigact, NULL); 210 | + 211 | + sigemptyset(&mask); 212 | + sigaddset(&mask, SIGIO); 213 | + sigprocmask(SIG_BLOCK, &mask, &oldmask); 214 | + 215 | + pollfds[0].fd = queuepipe[0]; 216 | + pollfds[1].fd = sfd; 217 | + pollfds[0].events = pollfds[1].events = POLLIN; 218 | + 219 | for (;;) { 220 | - n = getpkt(sfd, buf, bufsz); 221 | - if (n < 0) { 222 | + sigprocmask(SIG_SETMASK, &oldmask, NULL); 223 | + n = poll(pollfds, 2, 1000); 224 | + sigprocmask(SIG_BLOCK, &mask, NULL); 225 | + 226 | + if (n < 0 && errno != EINTR) { 227 | + perror("poll"); 228 | + continue; 229 | + } else if (n == 0 || pollfds[0].revents & POLLIN) { 230 | + while(read(queuepipe[0], &dummy, 1) > 0); 231 | + for (place = 0; place < Nplaces; place++) { 232 | + if (!pending[place]) 233 | + continue; 234 | + if (aio_error(aiocb + place) == EINPROGRESS) 235 | + continue; 236 | + doaoecomplete(place); 237 | + pollfds[1].events = POLLIN; 238 | + } 239 | + } 240 | + 241 | + if ((pollfds[1].revents & POLLIN) == 0) 242 | + continue; 243 | + 244 | + for (place = 0; pending[place] && place < Nplaces; place++); 245 | + if (place >= Nplaces) { 246 | + pollfds[1].events = 0; 247 | + continue; 248 | + } 249 | + 250 | + pktlen[place] = getpkt(sfd, (uchar *) pkt[place], bufsz); 251 | + if (pktlen[place] < 0) { 252 | + if (errno == EINTR) 253 | + continue; 254 | perror("read network"); 255 | exit(1); 256 | } 257 | - if (n < sizeof(Aoehdr)) 258 | + if (pktlen[place] < sizeof(Aoehdr)) 259 | continue; 260 | - p = (Aoehdr *) buf; 261 | + p = (Aoehdr *) pkt[place]; 262 | if (ntohs(p->type) != 0x88a2) 263 | continue; 264 | if (p->flags & Resp) 265 | @@ -223,9 +358,10 @@ aoe(void) 266 | continue; 267 | if (nmasks && !maskok(p->src)) 268 | continue; 269 | - doaoe(p, n); 270 | + doaoe(place); 271 | } 272 | - free(buf); 273 | + for (place = 0; place < Nplaces; place++) 274 | + free(freeme[place]); 275 | } 276 | 277 | void 278 | @@ -317,7 +453,7 @@ main(int argc, char **argv) 279 | } 280 | if (s.st_mode & (S_IWUSR|S_IWGRP|S_IWOTH)) 281 | omode = O_RDWR; 282 | - bfd = open(argv[3], omode); 283 | + bfd = opendisk(argv[3], omode); 284 | if (bfd == -1) { 285 | perror("open"); 286 | exit(1); 287 | diff -uprN vblade-17.orig/ata.c vblade-17/ata.c 288 | --- vblade-17.orig/ata.c 2008-06-09 10:53:07.000000000 -0400 289 | +++ vblade-17/ata.c 2008-06-09 11:05:23.000000000 -0400 290 | @@ -3,6 +3,8 @@ 291 | #include 292 | #include 293 | #include 294 | +#include 295 | +#include 296 | #include "dat.h" 297 | #include "fns.h" 298 | 299 | @@ -98,7 +100,7 @@ atainit(void) 300 | * check for that. 301 | */ 302 | int 303 | -atacmd(Ataregs *p, uchar *dp, int ndp, int payload) // do the ata cmd 304 | +atacmd(Ataregs *p, uchar *dp, int ndp, int payload, struct aiocb *aiocb) // do the ata cmd 305 | { 306 | vlong lba; 307 | ushort *ip; 308 | @@ -155,14 +157,29 @@ atacmd(Ataregs *p, uchar *dp, int ndp, i 309 | return 0; 310 | } 311 | if (p->cmd == 0x20 || p->cmd == 0x24) 312 | - n = getsec(bfd, dp, lba, p->sectors); 313 | + n = getsec(bfd, dp, lba, p->sectors, aiocb); 314 | else { 315 | // packet should be big enough to contain the data 316 | if (payload < 512 * p->sectors) 317 | return -1; 318 | - n = putsec(bfd, dp, lba, p->sectors); 319 | + n = putsec(bfd, dp, lba, p->sectors, aiocb); 320 | } 321 | - n /= 512; 322 | + if (n < 0) { 323 | + p->err = ABRT; 324 | + p->status = ERR|DRDY; 325 | + p->lba += n; 326 | + p->sectors -= n; 327 | + return 0; 328 | + } 329 | + return 1; // callback expected 330 | +} 331 | + 332 | + 333 | +int 334 | +atacmdcomplete(Ataregs *p, struct aiocb *aiocb) // complete the ata cmd 335 | +{ 336 | + int n; 337 | + n = aio_return(aiocb) / 512; 338 | if (n != p->sectors) { 339 | p->err = ABRT; 340 | p->status = ERR; 341 | @@ -173,4 +190,3 @@ atacmd(Ataregs *p, uchar *dp, int ndp, i 342 | p->sectors -= n; 343 | return 0; 344 | } 345 | - 346 | diff -uprN vblade-17.orig/dat.h vblade-17/dat.h 347 | --- vblade-17.orig/dat.h 2008-06-09 10:53:07.000000000 -0400 348 | +++ vblade-17/dat.h 2008-06-09 11:05:23.000000000 -0400 349 | @@ -111,6 +111,8 @@ enum { 350 | Nconfig = 1024, 351 | 352 | Bufcount = 16, 353 | + 354 | + Nplaces = 32, 355 | }; 356 | 357 | int shelf, slot; 358 | diff -uprN vblade-17.orig/fns.h vblade-17/fns.h 359 | --- vblade-17.orig/fns.h 2008-06-09 10:53:07.000000000 -0400 360 | +++ vblade-17/fns.h 2008-06-09 11:07:21.000000000 -0400 361 | @@ -15,7 +15,8 @@ int maskok(uchar *); 362 | // ata.c 363 | 364 | void atainit(void); 365 | -int atacmd(Ataregs *, uchar *, int, int); 366 | +int atacmd(Ataregs *, uchar *, int, int, struct aiocb *); 367 | +int atacmdcomplete(Ataregs *, struct aiocb *); 368 | 369 | // bpf.c 370 | 371 | @@ -26,8 +27,9 @@ void free_bpf_program(void *); 372 | 373 | int dial(char *); 374 | int getea(int, char *, uchar *); 375 | -int putsec(int, uchar *, vlong, int); 376 | -int getsec(int, uchar *, vlong, int); 377 | +int opendisk(const char *, int); 378 | +int putsec(int, uchar *, vlong, int, struct aiocb *); 379 | +int getsec(int, uchar *, vlong, int, struct aiocb *); 380 | int putpkt(int, uchar *, int); 381 | int getpkt(int, uchar *, int); 382 | vlong getsize(int); 383 | diff -uprN vblade-17.orig/freebsd.c vblade-17/freebsd.c 384 | --- vblade-17.orig/freebsd.c 2008-06-09 10:53:07.000000000 -0400 385 | +++ vblade-17/freebsd.c 2008-06-09 11:05:23.000000000 -0400 386 | @@ -209,19 +209,40 @@ getea(int s, char *eth, uchar *ea) 387 | return(0); 388 | } 389 | 390 | - 391 | int 392 | -getsec(int fd, uchar *place, vlong lba, int nsec) 393 | +opendisk(const char *disk, int omode) 394 | { 395 | - return pread(fd, place, nsec * 512, lba * 512); 396 | + return open(disk, omode); 397 | } 398 | 399 | int 400 | -putsec(int fd, uchar *place, vlong lba, int nsec) 401 | -{ 402 | - return pwrite(fd, place, nsec * 512, lba * 512); 403 | +getsec(int fd, uchar *place, vlong lba, int nsec, struct aiocb *aiocb) 404 | +{ 405 | + bzero((char *) aiocb, sizeof(struct aiocb)); 406 | + aiocb->aio_fildes = fd; 407 | + aiocb->aio_buf = place; 408 | + aiocb->aio_nbytes = nsec * 512; 409 | + aiocb->aio_offset = lba * 512; 410 | + aiocb->aio_sigevent.sigev_notify = SIGEV_SIGNAL; 411 | + aiocb->aio_sigevent.sigev_signo = SIGIO; 412 | + aiocb->aio_sigevent.sigev_value.sival_ptr = aiocb; 413 | + return aio_read(aiocb); 414 | } 415 | 416 | +int 417 | +putsec(int fd, uchar *place, vlong lba, int nsec, struct aiocb *aiocb) 418 | +{ 419 | + bzero((char *) aiocb, sizeof(struct aiocb)); 420 | + aiocb->aio_fildes = fd; 421 | + aiocb->aio_buf = place; 422 | + aiocb->aio_nbytes = nsec * 512; 423 | + aiocb->aio_offset = lba * 512; 424 | + aiocb->aio_sigevent.sigev_notify = SIGEV_SIGNAL; 425 | + aiocb->aio_sigevent.sigev_signo = SIGIO; 426 | + aiocb->aio_sigevent.sigev_value.sival_ptr = aiocb; 427 | + return aio_write(aiocb); 428 | +} 429 | + 430 | static int pktn = 0; 431 | static uchar *pktbp = NULL; 432 | 433 | diff -uprN vblade-17.orig/linux.c vblade-17/linux.c 434 | --- vblade-17.orig/linux.c 2008-06-09 10:53:07.000000000 -0400 435 | +++ vblade-17/linux.c 2008-06-09 11:05:23.000000000 -0400 436 | @@ -1,5 +1,6 @@ 437 | // linux.c: low level access routines for Linux 438 | #include "config.h" 439 | +#define _GNU_SOURCE 440 | #include 441 | #include 442 | #include 443 | @@ -22,6 +23,9 @@ 444 | #include 445 | #include 446 | #include 447 | +#include 448 | +#include 449 | +#include 450 | 451 | #include "dat.h" 452 | #include "fns.h" 453 | @@ -29,8 +33,6 @@ 454 | int getindx(int, char *); 455 | int getea(int, char *, uchar *); 456 | 457 | - 458 | - 459 | int 460 | dial(char *eth) // get us a raw connection to an interface 461 | { 462 | @@ -84,7 +86,7 @@ getea(int s, char *name, uchar *ea) 463 | struct ifreq xx; 464 | int n; 465 | 466 | - strcpy(xx.ifr_name, name); 467 | + strcpy(xx.ifr_name, name); 468 | n = ioctl(s, SIOCGIFHWADDR, &xx); 469 | if (n == -1) { 470 | perror("Can't get hw addr"); 471 | @@ -110,17 +112,37 @@ getmtu(int s, char *name) 472 | } 473 | 474 | int 475 | -getsec(int fd, uchar *place, vlong lba, int nsec) 476 | +opendisk(const char *disk, int omode) 477 | +{ 478 | + return open(disk, omode|O_DIRECT); 479 | +} 480 | + 481 | +int 482 | +getsec(int fd, uchar *place, vlong lba, int nsec, struct aiocb *aiocb) 483 | { 484 | - lseek(fd, lba * 512, 0); 485 | - return read(fd, place, nsec * 512); 486 | + bzero((char *) aiocb, sizeof(struct aiocb)); 487 | + aiocb->aio_fildes = fd; 488 | + aiocb->aio_buf = place; 489 | + aiocb->aio_nbytes = nsec * 512; 490 | + aiocb->aio_offset = lba * 512; 491 | + aiocb->aio_sigevent.sigev_notify = SIGEV_SIGNAL; 492 | + aiocb->aio_sigevent.sigev_signo = SIGIO; 493 | + aiocb->aio_sigevent.sigev_value.sival_ptr = aiocb; 494 | + return aio_read(aiocb); 495 | } 496 | 497 | int 498 | -putsec(int fd, uchar *place, vlong lba, int nsec) 499 | +putsec(int fd, uchar *place, vlong lba, int nsec, struct aiocb *aiocb) 500 | { 501 | - lseek(fd, lba * 512, 0); 502 | - return write(fd, place, nsec * 512); 503 | + bzero((char *) aiocb, sizeof(struct aiocb)); 504 | + aiocb->aio_fildes = fd; 505 | + aiocb->aio_buf = place; 506 | + aiocb->aio_nbytes = nsec * 512; 507 | + aiocb->aio_offset = lba * 512; 508 | + aiocb->aio_sigevent.sigev_notify = SIGEV_SIGNAL; 509 | + aiocb->aio_sigevent.sigev_signo = SIGIO; 510 | + aiocb->aio_sigevent.sigev_value.sival_ptr = aiocb; 511 | + return aio_write(aiocb); 512 | } 513 | 514 | int 515 | diff -uprN vblade-17.orig/linux.h vblade-17/linux.h 516 | --- vblade-17.orig/linux.h 2008-06-09 10:53:07.000000000 -0400 517 | +++ vblade-17/linux.h 2008-06-09 11:05:23.000000000 -0400 518 | @@ -6,6 +6,6 @@ typedef long long vlong; 519 | int dial(char *); 520 | int getindx(int, char *); 521 | int getea(int, char *, uchar *); 522 | -int getsec(int, uchar *, vlong, int); 523 | -int putsec(int, uchar *, vlong, int); 524 | +int getsec(int, uchar *, vlong, int, struct aiocb *); 525 | +int putsec(int, uchar *, vlong, int, struct aiocb *); 526 | vlong getsize(int); 527 | diff -uprN vblade-17.orig/makefile vblade-17/makefile 528 | --- vblade-17.orig/makefile 2008-06-09 10:53:07.000000000 -0400 529 | +++ vblade-17/makefile 2008-06-09 11:05:23.000000000 -0400 530 | @@ -13,7 +13,7 @@ CFLAGS += -Wall -g -O2 531 | CC = gcc 532 | 533 | vblade: $O 534 | - ${CC} -o vblade $O 535 | + ${CC} -lrt -o vblade $O 536 | 537 | aoe.o : aoe.c config.h dat.h fns.h makefile 538 | ${CC} ${CFLAGS} -c $< 539 | -------------------------------------------------------------------------------- /dat.c: -------------------------------------------------------------------------------- 1 | /* dat.c: Global memory for vblade AoE target */ 2 | #include "dat.h" 3 | 4 | int shelf, slot; 5 | ulong aoetag; 6 | uchar mac[6]; 7 | int bfd; // block file descriptor 8 | int sfd; // socket file descriptor 9 | vlong size; // size of vblade 10 | vlong offset; 11 | char *progname; 12 | char serial[Nserial+1]; 13 | -------------------------------------------------------------------------------- /dat.h: -------------------------------------------------------------------------------- 1 | /* dat.h: include file for vblade AoE target */ 2 | #include 3 | 4 | #define nil ((void *)0) 5 | /* 6 | * tunable variables 7 | */ 8 | 9 | enum { 10 | VBLADE_VERSION = 25, 11 | 12 | // Firmware version 13 | FWV = 0x4000 + VBLADE_VERSION, 14 | }; 15 | 16 | #undef major 17 | #undef minor 18 | #undef makedev 19 | 20 | #define major(x) ((x) >> 24 & 0xFF) 21 | #define minor(x) ((x) & 0xffffff) 22 | #define makedev(x, y) ((x) << 24 | (y)) 23 | 24 | typedef unsigned char uchar; 25 | //typedef unsigned short ushort; 26 | #ifdef __FreeBSD__ 27 | typedef unsigned long ulong; 28 | #else 29 | //typedef unsigned long ulong; 30 | #endif 31 | typedef long long vlong; 32 | 33 | typedef struct Aoehdr Aoehdr; 34 | typedef struct Ata Ata; 35 | typedef struct Conf Conf; 36 | typedef struct Ataregs Ataregs; 37 | typedef struct Mdir Mdir; 38 | typedef struct Aoemask Aoemask; 39 | typedef struct Aoesrr Aoesrr; 40 | 41 | struct Ataregs 42 | { 43 | vlong lba; 44 | uchar cmd; 45 | uchar status; 46 | uchar err; 47 | uchar feature; 48 | uchar sectors; 49 | }; 50 | 51 | struct Aoehdr 52 | { 53 | uchar dst[6]; 54 | uchar src[6]; 55 | ushort type; 56 | uchar flags; 57 | uchar error; 58 | ushort maj; 59 | uchar min; 60 | uchar cmd; 61 | uchar tag[4]; 62 | }; 63 | 64 | struct Ata 65 | { 66 | Aoehdr h; 67 | uchar aflag; 68 | uchar err; 69 | uchar sectors; 70 | uchar cmd; 71 | uchar lba[6]; 72 | uchar resvd[2]; 73 | }; 74 | 75 | struct Conf 76 | { 77 | Aoehdr h; 78 | ushort bufcnt; 79 | ushort firmware; 80 | uchar scnt; 81 | uchar vercmd; 82 | ushort len; 83 | uchar data[1024]; 84 | }; 85 | 86 | // mask directive 87 | struct Mdir { 88 | uchar res; 89 | uchar cmd; 90 | uchar mac[6]; 91 | }; 92 | 93 | struct Aoemask { 94 | Aoehdr h; 95 | uchar res; 96 | uchar cmd; 97 | uchar merror; 98 | uchar nmacs; 99 | // struct Mdir m[0]; 100 | }; 101 | 102 | struct Aoesrr { 103 | Aoehdr h; 104 | uchar rcmd; 105 | uchar nmacs; 106 | // uchar mac[6][nmacs]; 107 | }; 108 | 109 | enum { 110 | AoEver = 1, 111 | 112 | ATAcmd = 0, // command codes 113 | Config, 114 | Mask, 115 | Resrel, 116 | 117 | Resp = (1<<3), // flags 118 | Error = (1<<2), 119 | 120 | BadCmd = 1, 121 | BadArg, 122 | DevUnavailable, 123 | ConfigErr, 124 | BadVersion, 125 | Res, 126 | 127 | Write = (1<<0), 128 | Async = (1<<1), 129 | Device = (1<<4), 130 | Extend = (1<<6), 131 | 132 | Qread = 0, 133 | Qtest, 134 | Qprefix, 135 | Qset, 136 | Qfset, 137 | 138 | Nretries = 3, 139 | Nconfig = 1024, 140 | 141 | Bufcount = 16, 142 | 143 | /* mask commands */ 144 | Mread= 0, 145 | Medit, 146 | 147 | /* mask directives */ 148 | MDnop= 0, 149 | MDadd, 150 | MDdel, 151 | 152 | /* mask errors */ 153 | MEunspec= 1, 154 | MEbaddir, 155 | MEfull, 156 | 157 | /* header sizes, including aoe hdr */ 158 | Naoehdr= 24, 159 | Natahdr= Naoehdr + 12, 160 | Ncfghdr= Naoehdr + 8, 161 | Nmaskhdr= Naoehdr + 4, 162 | Nsrrhdr= Naoehdr + 2, 163 | 164 | Nserial= 20, 165 | }; 166 | 167 | extern int shelf, slot; 168 | extern ulong aoetag; 169 | extern uchar mac[6]; 170 | extern int bfd; // block file descriptor 171 | extern int sfd; // socket file descriptor 172 | extern vlong size; // size of vblade 173 | extern vlong offset; 174 | extern char *progname; 175 | extern char serial[Nserial+1]; 176 | -------------------------------------------------------------------------------- /fns.h: -------------------------------------------------------------------------------- 1 | // fns.h: function prototypes 2 | 3 | // aoe.c 4 | 5 | void aoe(void); 6 | void aoeinit(void); 7 | void aoequery(void); 8 | void aoeconfig(void); 9 | void aoead(int); 10 | void aoeflush(int, int); 11 | void aoetick(void); 12 | void aoerequest(int, int, vlong, int, uchar *, int); 13 | int maskok(uchar *); 14 | int rrok(uchar *); 15 | 16 | // ata.c 17 | 18 | void atainit(void); 19 | int atacmd(Ataregs *, uchar *, int, int); 20 | 21 | // bpf.c 22 | 23 | void * create_bpf_program(int, int); 24 | void free_bpf_program(void *); 25 | 26 | // os specific 27 | 28 | int dial(char *, int); 29 | int getea(int, char *, uchar *); 30 | int putsec(int, uchar *, vlong, int); 31 | int getsec(int, uchar *, vlong, int); 32 | int putpkt(int, uchar *, int); 33 | int getpkt(int, uchar *, int); 34 | vlong getsize(int); 35 | int getmtu(int, char *); 36 | -------------------------------------------------------------------------------- /freebsd.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2005, Stacey Son 3 | * All rights reserved. 4 | */ 5 | 6 | // freebsd.c: low level access routines for FreeBSD 7 | #include "config.h" 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include 33 | #include 34 | 35 | #include "dat.h" 36 | #include "fns.h" 37 | 38 | #define BPF_DEV "/dev/bpf0" 39 | 40 | /* Packet buffer for getpkt() */ 41 | static uchar *pktbuf = NULL; 42 | static int pktbufsz = 0; 43 | 44 | int 45 | dial(char *eth, int bufcnt) 46 | { 47 | char m; 48 | int fd = -1; 49 | struct bpf_version bv; 50 | u_int v; 51 | unsigned bufsize, linktype; 52 | char device[sizeof BPF_DEV]; 53 | struct ifreq ifr; 54 | 55 | struct bpf_program *bpf_program = create_bpf_program(shelf, slot); 56 | 57 | strncpy(device, BPF_DEV, sizeof BPF_DEV); 58 | 59 | /* find a bpf device we can use, check /dev/bpf[0-9] */ 60 | for (m = '0'; m <= '9'; m++) { 61 | device[sizeof(BPF_DEV)-2] = m; 62 | 63 | if ((fd = open(device, O_RDWR)) > 0) 64 | break; 65 | } 66 | 67 | if (fd < 0) { 68 | perror("open"); 69 | return -1; 70 | } 71 | 72 | if (ioctl(fd, BIOCVERSION, &bv) < 0) { 73 | perror("BIOCVERSION"); 74 | goto bad; 75 | } 76 | 77 | if (bv.bv_major != BPF_MAJOR_VERSION || 78 | bv.bv_minor < BPF_MINOR_VERSION) { 79 | fprintf(stderr, 80 | "kernel bpf filter out of date\n"); 81 | goto bad; 82 | } 83 | 84 | /* 85 | * Try finding a good size for the buffer; 65536 may be too 86 | * big, so keep cutting it in half until we find a size 87 | * that works, or run out of sizes to try. 88 | * 89 | */ 90 | for (v = 65536; v != 0; v >>= 1) { 91 | (void) ioctl(fd, BIOCSBLEN, (caddr_t)&v); 92 | 93 | (void)strncpy(ifr.ifr_name, eth, 94 | sizeof(ifr.ifr_name)); 95 | if (ioctl(fd, BIOCSETIF, (caddr_t)&ifr) >= 0) 96 | break; /* that size worked; we're done */ 97 | 98 | if (errno != ENOBUFS) { 99 | fprintf(stderr, "BIOCSETIF: %s: %s\n", 100 | eth, strerror(errno)); 101 | goto bad; 102 | } 103 | } 104 | if (v == 0) { 105 | fprintf(stderr, 106 | "BIOCSBLEN: %s: No buffer size worked\n", eth); 107 | goto bad; 108 | } 109 | 110 | /* Allocate memory for the packet buffer */ 111 | pktbufsz = v; 112 | if ((pktbuf = malloc(pktbufsz)) == NULL) { 113 | perror("malloc"); 114 | goto bad; 115 | } 116 | 117 | /* Don't wait for buffer to be full or timeout */ 118 | v = 1; 119 | if (ioctl(fd, BIOCIMMEDIATE, &v) < 0) { 120 | perror("BIOCIMMEDIATE"); 121 | goto bad; 122 | } 123 | 124 | /* Only read incoming packets */ 125 | v = 0; 126 | if (ioctl(fd, BIOCSSEESENT, &v) < 0) { 127 | perror("BIOCSSEESENT"); 128 | goto bad; 129 | } 130 | 131 | /* Don't complete ethernet hdr */ 132 | v = 1; 133 | if (ioctl(fd, BIOCSHDRCMPLT, &v) < 0) { 134 | perror("BIOCSHDRCMPLT"); 135 | goto bad; 136 | } 137 | 138 | /* Get the data link layer type. */ 139 | if (ioctl(fd, BIOCGDLT, (caddr_t)&v) < 0) { 140 | perror("BIOCGDLT"); 141 | goto bad; 142 | } 143 | linktype = v; 144 | 145 | /* Get the filter buf size */ 146 | if (ioctl(fd, BIOCGBLEN, (caddr_t)&v) < 0) { 147 | perror("BIOCGBLEN"); 148 | goto bad; 149 | } 150 | bufsize = v; 151 | 152 | if (ioctl(fd, BIOCSETF, (caddr_t)bpf_program) < 0) { 153 | perror("BIOSETF"); 154 | goto bad; 155 | } 156 | 157 | free_bpf_program(bpf_program); 158 | return(fd); 159 | 160 | bad: 161 | free_bpf_program(bpf_program); 162 | close(fd); 163 | return(-1); 164 | } 165 | 166 | int 167 | getea(int s, char *eth, uchar *ea) 168 | { 169 | int mib[6]; 170 | size_t len; 171 | char *buf, *next, *end; 172 | struct if_msghdr *ifm; 173 | struct sockaddr_dl *sdl; 174 | 175 | 176 | mib[0] = CTL_NET; mib[1] = AF_ROUTE; 177 | mib[2] = 0; mib[3] = AF_LINK; 178 | mib[4] = NET_RT_IFLIST; mib[5] = 0; 179 | 180 | if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) { 181 | return (-1); 182 | } 183 | 184 | if (!(buf = (char *) malloc(len))) { 185 | return (-1); 186 | } 187 | 188 | if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) { 189 | free(buf); 190 | return (-1); 191 | } 192 | end = buf + len; 193 | 194 | for (next = buf; next < end; next += ifm->ifm_msglen) { 195 | ifm = (struct if_msghdr *)next; 196 | if (ifm->ifm_type == RTM_IFINFO) { 197 | sdl = (struct sockaddr_dl *)(ifm + 1); 198 | if (strncmp(&sdl->sdl_data[0], eth, 199 | sdl->sdl_nlen) == 0) { 200 | memcpy(ea, LLADDR(sdl), ETHER_ADDR_LEN); 201 | break; 202 | } 203 | 204 | } 205 | 206 | } 207 | 208 | free(buf); 209 | return(0); 210 | } 211 | 212 | 213 | int 214 | getsec(int fd, uchar *place, vlong lba, int nsec) 215 | { 216 | return pread(fd, place, nsec * 512, lba * 512); 217 | } 218 | 219 | int 220 | putsec(int fd, uchar *place, vlong lba, int nsec) 221 | { 222 | return pwrite(fd, place, nsec * 512, lba * 512); 223 | } 224 | 225 | static int pktn = 0; 226 | static uchar *pktbp = NULL; 227 | 228 | int 229 | getpkt(int fd, uchar *buf, int sz) 230 | { 231 | register struct bpf_hdr *bh; 232 | register int pktlen, retlen; 233 | 234 | if (pktn <= 0) { 235 | if ((pktn = read(fd, pktbuf, pktbufsz)) < 0) { 236 | perror("read"); 237 | exit(1); 238 | } 239 | pktbp = pktbuf; 240 | } 241 | 242 | bh = (struct bpf_hdr *) pktbp; 243 | retlen = (int) bh->bh_caplen; 244 | /* This memcpy() is currently needed */ 245 | memcpy(buf, (void *)(pktbp + bh->bh_hdrlen), 246 | retlen > sz ? sz : retlen); 247 | pktlen = bh->bh_hdrlen + bh->bh_caplen; 248 | 249 | pktbp = pktbp + BPF_WORDALIGN(pktlen); 250 | pktn -= (int) BPF_WORDALIGN(pktlen); 251 | 252 | return retlen; 253 | } 254 | 255 | int 256 | putpkt(int fd, uchar *buf, int sz) 257 | { 258 | return write(fd, buf, sz); 259 | } 260 | 261 | int 262 | getmtu(int fd, char *name) 263 | { 264 | struct ifreq xx; 265 | int s, n, p; 266 | 267 | s = socket(AF_INET, SOCK_RAW, 0); 268 | if (s == -1) { 269 | perror("Can't get mtu"); 270 | return 1500; 271 | } 272 | xx.ifr_addr.sa_family = AF_INET; 273 | snprintf(xx.ifr_name, sizeof xx.ifr_name, "%s", name); 274 | n = ioctl(s, SIOCGIFMTU, &xx); 275 | if (n == -1) { 276 | perror("Can't get mtu"); 277 | return 1500; 278 | } 279 | close(s); 280 | // FreeBSD bpf writes are capped at one PAGESIZE'd mbuf. As such we must 281 | // limit our sector count. See FreeBSD PR 205164, OpenAoE/vblade #7. 282 | p = getpagesize(); 283 | if (xx.ifr_mtu > p) { 284 | return p; 285 | } 286 | return xx.ifr_mtu; 287 | } 288 | 289 | vlong 290 | getsize(int fd) 291 | { 292 | off_t media_size; 293 | vlong size; 294 | struct stat s; 295 | int n; 296 | 297 | // Try getting disklabel from block dev 298 | if ((n = ioctl(fd, DIOCGMEDIASIZE, &media_size)) != -1) { 299 | size = media_size; 300 | } else { 301 | // must not be a block special dev 302 | if (fstat(fd, &s) == -1) { 303 | perror("getsize"); 304 | exit(1); 305 | } 306 | size = s.st_size; 307 | } 308 | printf("ioctl returned %d\n", n); 309 | printf("%lld bytes\n", size); 310 | return size; 311 | } 312 | -------------------------------------------------------------------------------- /linux.c: -------------------------------------------------------------------------------- 1 | // linux.c: low level access routines for Linux 2 | #define _GNU_SOURCE 3 | #include "config.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include /* for the glibc version number */ 11 | #if __GLIBC__ >= 2 && __GLIBC_MINOR >= 1 12 | #include 13 | #include /* the L2 protocols */ 14 | #else 15 | #include 16 | #include 17 | #include /* The L2 protocols */ 18 | #endif 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include "dat.h" 29 | #include "fns.h" 30 | 31 | int getindx(int, char *); 32 | int getea(int, char *, uchar *); 33 | 34 | 35 | 36 | int 37 | dial(char *eth, int bufcnt) // get us a raw connection to an interface 38 | { 39 | int i, n, s; 40 | struct sockaddr_ll sa; 41 | enum { aoe_type = 0x88a2 }; 42 | 43 | memset(&sa, 0, sizeof sa); 44 | s = socket(PF_PACKET, SOCK_RAW, htons(aoe_type)); 45 | if (s == -1) { 46 | perror("got bad socket"); 47 | return -1; 48 | } 49 | i = getindx(s, eth); 50 | if (i < 0) { 51 | perror(eth); 52 | return -1; 53 | } 54 | sa.sll_family = AF_PACKET; 55 | sa.sll_protocol = htons(0x88a2); 56 | sa.sll_ifindex = i; 57 | n = bind(s, (struct sockaddr *)&sa, sizeof sa); 58 | if (n == -1) { 59 | perror("bind funky"); 60 | return -1; 61 | } 62 | 63 | struct bpf_program { 64 | ulong bf_len; 65 | void *bf_insns; 66 | } *bpf_program = create_bpf_program(shelf, slot); 67 | setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, bpf_program, sizeof(*bpf_program)); 68 | free_bpf_program(bpf_program); 69 | 70 | n = bufcnt * getmtu(s, eth); 71 | if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, &n, sizeof(n)) < 0) 72 | perror("setsockopt SOL_SOCKET, SO_SNDBUF"); 73 | if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &n, sizeof(n)) < 0) 74 | perror("setsockopt SOL_SOCKET, SO_RCVBUF"); 75 | 76 | return s; 77 | } 78 | 79 | int 80 | getindx(int s, char *name) // return the index of device 'name' 81 | { 82 | struct ifreq xx; 83 | int n; 84 | 85 | snprintf(xx.ifr_name, sizeof xx.ifr_name, "%s", name); 86 | n = ioctl(s, SIOCGIFINDEX, &xx); 87 | if (n == -1) 88 | return -1; 89 | return xx.ifr_ifindex; 90 | } 91 | 92 | int 93 | getea(int s, char *name, uchar *ea) 94 | { 95 | struct ifreq xx; 96 | int n; 97 | 98 | snprintf(xx.ifr_name, sizeof xx.ifr_name, "%s", name); 99 | n = ioctl(s, SIOCGIFHWADDR, &xx); 100 | if (n == -1) { 101 | perror("Can't get hw addr"); 102 | return 0; 103 | } 104 | memmove(ea, xx.ifr_hwaddr.sa_data, 6); 105 | return 1; 106 | } 107 | 108 | int 109 | getmtu(int s, char *name) 110 | { 111 | struct ifreq xx; 112 | int n; 113 | 114 | snprintf(xx.ifr_name, sizeof xx.ifr_name, "%s", name); 115 | n = ioctl(s, SIOCGIFMTU, &xx); 116 | if (n == -1) { 117 | perror("Can't get mtu"); 118 | return 1500; 119 | } 120 | return xx.ifr_mtu; 121 | } 122 | 123 | int 124 | getsec(int fd, uchar *place, vlong lba, int nsec) 125 | { 126 | return pread(fd, place, nsec * 512, lba * 512); 127 | } 128 | 129 | int 130 | putsec(int fd, uchar *place, vlong lba, int nsec) 131 | { 132 | return pwrite(fd, place, nsec * 512, lba * 512); 133 | } 134 | 135 | int 136 | getpkt(int fd, uchar *buf, int sz) 137 | { 138 | return read(fd, buf, sz); 139 | } 140 | 141 | int 142 | putpkt(int fd, uchar *buf, int sz) 143 | { 144 | return write(fd, buf, sz); 145 | } 146 | 147 | vlong 148 | getsize(int fd) 149 | { 150 | vlong size; 151 | struct stat s; 152 | int n; 153 | 154 | n = ioctl(fd, BLKGETSIZE64, &size); 155 | if (n == -1) { // must not be a block special 156 | n = fstat(fd, &s); 157 | if (n == -1) { 158 | perror("getsize"); 159 | exit(1); 160 | } 161 | size = s.st_size; 162 | } 163 | return size; 164 | } 165 | -------------------------------------------------------------------------------- /linux.h: -------------------------------------------------------------------------------- 1 | // linux.h: header for linux.c 2 | 3 | typedef unsigned char uchar; 4 | typedef long long vlong; 5 | 6 | int dial(char *); 7 | int getindx(int, char *); 8 | int getea(int, char *, uchar *); 9 | int getsec(int, uchar *, vlong, int); 10 | int putsec(int, uchar *, vlong, int); 11 | vlong getsize(int); 12 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | # makefile for vblade 2 | 3 | # see README for others 4 | PLATFORM=linux 5 | 6 | prefix = /usr 7 | sbindir = ${prefix}/sbin 8 | sharedir = ${prefix}/share 9 | mandir = ${sharedir}/man 10 | 11 | O=aoe.o bpf.o ${PLATFORM}.o ata.o dat.o 12 | CFLAGS += -Wall -g -O2 -fno-common 13 | CC = gcc 14 | 15 | vblade: $O 16 | ${CC} -o vblade $O 17 | 18 | aoe.o : aoe.c config.h dat.h fns.h makefile 19 | ${CC} ${CFLAGS} -c $< 20 | 21 | ${PLATFORM}.o : ${PLATFORM}.c config.h dat.h fns.h makefile 22 | ${CC} ${CFLAGS} -c $< 23 | 24 | ata.o : ata.c config.h dat.h fns.h makefile 25 | ${CC} ${CFLAGS} -c $< 26 | 27 | bpf.o : bpf.c config.h 28 | ${CC} ${CFLAGS} -c $< 29 | 30 | dat.o : dat.c config.h 31 | ${CC} ${CFLAGS} -c $< 32 | 33 | config.h : config/config.h.in makefile 34 | @if ${CC} ${CFLAGS} config/u64.c > /dev/null 2>&1; then \ 35 | sh -xc "cp config/config.h.in config.h"; \ 36 | else \ 37 | sh -xc "sed 's!^//u64 !!' config/config.h.in > config.h"; \ 38 | fi 39 | 40 | clean : 41 | rm -f $O config.h vblade 42 | 43 | install : vblade vbladed 44 | install vblade ${sbindir}/ 45 | install vbladed ${sbindir}/ 46 | install vblade.8 ${mandir}/man8/ 47 | -------------------------------------------------------------------------------- /sparsefile: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | # sparsefile - create sparse files conveniently 3 | # 4 | # depends on dd and dc commands. 5 | 6 | usage() { 7 | echo "usage: `basename $0` {10M|10G|10T} {filename}" 1>&2 8 | } 9 | size=$1 10 | if test "$size" = "-h"; then 11 | usage 12 | exit 13 | fi 14 | fnam=$2 15 | 16 | die() { 17 | usage 18 | exit 1 19 | } 20 | set -e 21 | units=`echo "$size" | sed 's!.*\(.\)$!\1!'` 22 | n=`echo "$size" | sed 's!\(.*\).$!\1!'` 23 | test "$units" && test "$n" && test "$units" != "$n" || die 24 | case "$units" in 25 | M) 26 | seek=`echo "$n 1024 * 1 - p" | dc` ;; 27 | G) 28 | seek=`echo "$n 1024 1024 * * 1 - p" | dc` ;; 29 | T) 30 | seek=`echo "$n 1024 1024 1024 * * * 1 - p" | dc` ;; 31 | *) 32 | die 33 | ;; 34 | esac 35 | sh -xc "dd bs=1k count=1 if=/dev/zero of=$fnam seek=$seek" 36 | ls -lh "$fnam" 37 | -------------------------------------------------------------------------------- /vblade.8: -------------------------------------------------------------------------------- 1 | .TH vblade 8 2 | .SH NAME 3 | vblade, vbladed \- export data via ATA over Ethernet 4 | .SH SYNOPSIS 5 | .nf 6 | .B vblade [ -m mac[,mac...] ] shelf slot netif filename 7 | .fi 8 | .SH DESCRIPTION 9 | The 10 | .I vblade 11 | command starts a process that uses raw sockets to perform ATA over 12 | Ethernet, acting like a virtual EtherDrive (R) blade. 13 | .PP 14 | The 15 | .I vbladed 16 | script can be used to daemonize the vblade process, 17 | detaching it from your terminal and sending its output to the system 18 | logs. 19 | .SS Arguments 20 | .TP 21 | \fBshelf\fP 22 | This should be the shelf address (major AoE address) of the AoE device 23 | to create. 24 | .TP 25 | \fBslot\fP 26 | This should be the slot address (minor AoE address) of the AoE device 27 | to create. 28 | .TP 29 | \fBnetif\fP 30 | The name of the ethernet network interface to use for AoE 31 | communications. 32 | .TP 33 | \fBfilename\fP 34 | The name of the regular file or block device to export. 35 | .SS Options 36 | .TP 37 | \fB-b\fP 38 | The \-b flag takes an argument, the advertised buffer count, specifying 39 | the maximum number of outstanding messages the server can queue for 40 | processing. 41 | .TP 42 | \fB-d\fP 43 | The \-d flag selects O_DIRECT mode for accessing the underlying block 44 | device. 45 | .TP 46 | \fB-s\fP 47 | The \-s flag selects O_SYNC mode for accessing the underlying block 48 | device, so all writes are committed to disk before returning to the 49 | client. 50 | .TP 51 | \fB-r\fP 52 | The \-r flag restricts the export of the device to be read-only. 53 | .TP 54 | \fB-m\fP 55 | The \-m flag takes an argument, a comma separated list of MAC addresses 56 | permitted access to the vblade. A MAC address can be specified in upper 57 | or lower case, with or without colons. 58 | .TP 59 | \fB-o\fP 60 | The \-o flag takes an argument, the number of sectors at the beginning 61 | of the exported file that are excluded from AoE export (default zero). 62 | .TP 63 | \fB-l\fP 64 | The \-l flag takes an argument, the number of sectors to export. 65 | Defaults to the file size in sectors minus the offset. 66 | .SH EXAMPLE 67 | In this example, the root user on a host named 68 | .I nai 69 | exports a file named "3TB" to the LAN on eth0 using AoE shelf address 11 70 | and slot address 1. The process runs in the foreground. Using 71 | .I vbladed 72 | would have resulted in the process running as a daemon in the 73 | background. 74 | .IP 75 | .EX 76 | .nf 77 | nai:~# vblade 11 1 eth0 /data/3TB 78 | .fi 79 | .EE 80 | .SH NOTES AND WARNINGS 81 | Users of Jumbo frames should read the README file distributed with 82 | .I vblade 83 | to learn about a workaround for kernel buffering limitations. 84 | .PP 85 | At least one AoE initiator (WinAoE) has been found to enforce legacy 86 | CHS geometry for drives by discarding sectors. You should ensure that 87 | the underlaying regular file or block device size is a multiple of 88 | 8225280 bytes (255 heads, 63 sectors/track, 512 bytes/sector) if you 89 | encounter filesystem corruption. 90 | .SH REPORTING BUGS 91 | Please report bugs to the aoetools-discuss mailing list. 92 | .SH AUTHOR 93 | Brantley Coile (brantley@coraid.com) 94 | -------------------------------------------------------------------------------- /vbladed: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | # run a vblade daemon using a logger process 3 | # output is directed to syslogd 4 | # 5 | 6 | sh -c "`dirname $0`/vblade $* < /dev/null 2>&1 | logger -t vbladed" & 7 | --------------------------------------------------------------------------------