diff options
author | Neil Brown <neilb@suse.de> | 2002-03-06 23:17:40 +0000 |
---|---|---|
committer | Neil Brown <neilb@suse.de> | 2002-03-06 23:17:40 +0000 |
commit | cd29a5c835c11cbcedc10487677eac6a946ad61b (patch) | |
tree | d4bb1a92ee76d8680522baace561aaa1ceaaebae | |
parent | 52826846282e9e224e05dde6d2e4cb38d1fefec7 (diff) | |
download | mdadm-cd29a5c835c11cbcedc10487677eac6a946ad61b.tar.gz |
mdctl-0.6mdctl-0.6
-rw-r--r-- | Assemble.c | 76 | ||||
-rw-r--r-- | Build.c | 56 | ||||
-rw-r--r-- | COPYING | 340 | ||||
-rw-r--r-- | ChangeLog | 148 | ||||
-rw-r--r-- | Create.c | 38 | ||||
-rw-r--r-- | Detail.c | 147 | ||||
-rw-r--r-- | Examine.c | 273 | ||||
-rw-r--r-- | Makefile | 17 | ||||
-rw-r--r-- | Manage.c | 50 | ||||
-rw-r--r-- | Monitor.c | 20 | ||||
-rw-r--r-- | ReadMe.c | 22 | ||||
-rw-r--r-- | TODO | 38 | ||||
-rw-r--r-- | config.c | 10 | ||||
-rwxr-xr-x | makedist | 2 | ||||
-rw-r--r-- | mdctl.8 | 668 | ||||
-rw-r--r-- | mdctl.c | 115 | ||||
-rw-r--r-- | mdctl.h | 24 | ||||
-rw-r--r-- | mdctl.man | 476 | ||||
-rw-r--r-- | util.c | 15 |
19 files changed, 2014 insertions, 521 deletions
@@ -1,7 +1,7 @@ /* * mdctl - manage Linux "md" devices aka RAID arrays. * - * Copyright (C) 2001 Neil Brown <neilb@cse.unsw.edu.au> + * Copyright (C) 2001-2002 Neil Brown <neilb@cse.unsw.edu.au> * * * This program is free software; you can redistribute it and/or modify @@ -33,7 +33,7 @@ int Assemble(char *mddev, int mdfd, mddev_ident_t ident, char *conffile, - int subdevs, char **subdev, + mddev_dev_t devlist, int readonly, int runstop, int verbose, int force) { @@ -67,7 +67,7 @@ int Assemble(char *mddev, int mdfd, * * If !uuidset and scan, look in conf-file for uuid * If not found, give up - * If !subdevs and scan and uuidset, get list of devs from conf-file + * If !devlist and scan and uuidset, get list of devs from conf-file * * For each device: * Check superblock - discard if bad @@ -94,7 +94,6 @@ int Assemble(char *mddev, int mdfd, int old_linux = 0; int vers; mdu_array_info_t array; - mddev_dev_t devlist = NULL; mdp_super_t first_super, super; struct { char *devname; @@ -108,8 +107,10 @@ int Assemble(char *mddev, int mdfd, int devcnt = 0, okcnt, sparecnt; int i; int most_recent = 0; - int chosen_drive = -1; + int chosen_drive; int change = 0; + int inargv = 0; + int start_partial_ok = force || devlist==NULL; vers = md_get_version(mdfd); if (vers <= 0) { @@ -139,7 +140,7 @@ int Assemble(char *mddev, int mdfd, * there must be something in the identity */ - if (subdevs == 0 && + if (!devlist && ident->uuid_set == 0 && ident->super_minor < 0 && ident->devices == NULL) { @@ -147,8 +148,9 @@ int Assemble(char *mddev, int mdfd, mddev); return 1; } - if (subdevs==0) + if (devlist == NULL) devlist = conf_get_devs(conffile); + else inargv = 1; first_super.md_magic = 0; for (i=0; i<MD_SB_DISKS; i++) @@ -158,23 +160,15 @@ int Assemble(char *mddev, int mdfd, fprintf(stderr, Name ": looking for devices for %s\n", mddev); - while (subdevs || devlist) { + while ( devlist) { char *devname; int this_uuid[4]; int dfd; struct stat stb; - int inargv; int havesuper=0; - if (subdevs) { - devname = *subdev++; - subdevs--; - inargv=1; - } else { - devname = devlist->devname; - devlist = devlist->next; - inargv=0; - } + devname = devlist->devname; + devlist = devlist->next; if (ident->devices && !match_oneof(ident->devices, devname)) @@ -190,11 +184,11 @@ int Assemble(char *mddev, int mdfd, fprintf(stderr, Name ": fstat failed for %s: %s\n", devname, strerror(errno)); close(dfd); - } if ((stb.st_mode & S_IFMT) != S_IFBLK) { - fprintf(stderr, Name ": %d is not a block device.\n", + } else if ((stb.st_mode & S_IFMT) != S_IFBLK) { + fprintf(stderr, Name ": %s is not a block device.\n", devname); close(dfd); - } if (load_super(dfd, &super)) { + } else if (load_super(dfd, &super)) { if (inargv || verbose) fprintf( stderr, Name ": no RAID superblock on %s\n", devname); @@ -219,14 +213,25 @@ int Assemble(char *mddev, int mdfd, devname); continue; } + if (ident->level != -10 && + (!havesuper|| ident->level != super.level)) { + if (inargv || verbose) + fprintf(stderr, Name ": %s has wrong raid level.\n", + devname); + continue; + } + if (ident->raid_disks != -1 && + (!havesuper || ident->raid_disks!= super.raid_disks)) { + if (inargv || verbose) + fprintf(stderr, Name ": %s requires wrong number of drives.\n", + devname); + continue; + } /* If we are this far, then we are commited to this device. * If the super_block doesn't exist, or doesn't match others, * then we cannot continue */ - if (verbose) - fprintf(stderr, Name ": %s is identified as a member of %s.\n", - devname, mddev); if (!havesuper) { fprintf(stderr, Name ": %s has no superblock - assembly aborted\n", @@ -244,6 +249,9 @@ int Assemble(char *mddev, int mdfd, devname); continue; } + if (verbose) + fprintf(stderr, Name ": %s is identified as a member of %s, slot %d.\n", + devname, mddev, super.this_disk.raid_disk); devices[devcnt].devname = devname; devices[devcnt].major = MAJOR(stb.st_rdev); devices[devcnt].minor = MINOR(stb.st_rdev); @@ -277,8 +285,9 @@ int Assemble(char *mddev, int mdfd, sparecnt=0; for (i=0; i< MD_SB_DISKS;i++) { int j = best[i]; + int event_margin = !force; if (j < 0) continue; - if (devices[j].events+1 >= + if (devices[j].events+event_margin >= devices[most_recent].events) { devices[j].uptodate = 1; if (i < first_super.raid_disks) @@ -293,6 +302,7 @@ int Assemble(char *mddev, int mdfd, * and add it. */ int fd; + chosen_drive = -1; for (i=0; i<first_super.raid_disks; i++) { int j = best[i]; if (j>=0 && @@ -344,6 +354,7 @@ int Assemble(char *mddev, int mdfd, * If there are differences and --force is given, then update this chosen * superblock. */ + chosen_drive = -1; for (i=0; chosen_drive < 0 && i<MD_SB_DISKS; i++) { int j = best[i]; int fd; @@ -368,6 +379,7 @@ int Assemble(char *mddev, int mdfd, for (i=0; i<MD_SB_DISKS; i++) { int j = best[i]; + int active_sync = (1<<MD_DISK_ACTIVE) | (1<<MD_DISK_SYNC); if (j<0) continue; if (!devices[j].uptodate) @@ -379,12 +391,12 @@ int Assemble(char *mddev, int mdfd, super.disks[j].minor = devices[j].minor; } if (devices[j].uptodate && - (super.disks[i].state & (1 << MD_DISK_FAULTY))) { + (super.disks[i].state != active_sync)) { if (force) { fprintf(stderr, Name ": " - "clearing FAULT flag for device %d in %s for %s\n", + "clearing FAULTY flag for device %d in %s for %s\n", j, mddev, devices[j].devname); - super.disks[i].state &= ~(1<<MD_DISK_FAULTY); + super.disks[i].state = active_sync; change |= 2; } else { fprintf(stderr, Name ": " @@ -460,7 +472,9 @@ int Assemble(char *mddev, int mdfd, if (runstop == 1 || (runstop == 0 && - enough(first_super.level, first_super.raid_disks, okcnt))) { + ( first_super.raid_disks == okcnt + || start_partial_ok && enough(first_super.level, first_super.raid_disks, okcnt)) + )) { if (ioctl(mdfd, RUN_ARRAY, NULL)==0) { fprintf(stderr, Name ": %s has been started with %d drive%s", mddev, okcnt, okcnt==1?"":"s"); @@ -478,7 +492,7 @@ int Assemble(char *mddev, int mdfd, mddev, okcnt, okcnt==1?"":"s"); return 0; } - fprintf(stderr, Name ": %s assembled from %d drive%s - not enough to start it.\n", + fprintf(stderr, Name ": %s assembled from %d drive%s - not enough to start it (use --run to insist).\n", mddev, okcnt, okcnt==1?"":"s"); return 1; } else { @@ -486,7 +500,7 @@ int Assemble(char *mddev, int mdfd, * been updated to point to the current locations of devices. * so we can just start the array */ - int dev; + unsigned long dev; dev = MKDEV(devices[chosen_drive].major, devices[chosen_drive].minor); if (ioctl(mdfd, START_ARRAY, dev)) { @@ -1,7 +1,7 @@ /* * mdctl - manage Linux "md" devices aka RAID arrays. * - * Copyright (C) 2001 Neil Brown <neilb@cse.unsw.edu.au> + * Copyright (C) 2001-2002 Neil Brown <neilb@cse.unsw.edu.au> * * * This program is free software; you can redistribute it and/or modify @@ -35,7 +35,7 @@ int Build(char *mddev, int mdfd, int chunk, int level, int raiddisks, - int subdevs, char *subdev[]) + mddev_dev_t devlist) { /* Build a linear or raid0 arrays without superblocks * We cannot really do any checks, we just do it. @@ -53,30 +53,34 @@ int Build(char *mddev, int mdfd, int chunk, int level, int i; int vers; struct stat stb; - if (raiddisks != subdevs) { - fprintf(stderr, Name ": requested %d devices in array but listed %d\n", - raiddisks, subdevs); - return 1; - } + int subdevs = 0; + mddev_dev_t dv; /* scan all devices, make sure they really are block devices */ - for (i=0; i<subdevs; i++) { - if (stat(subdev[i], &stb)) { + for (dv = devlist; dv; dv=dv->next) { + if (stat(dv->devname, &stb)) { fprintf(stderr, Name ": Cannot find %s: %s\n", - subdev[i], strerror(errno)); + dv->devname, strerror(errno)); return 1; } if ((stb.st_mode & S_IFMT) != S_IFBLK) { fprintf(stderr, Name ": %s is not a block device.\n", - subdev[i]); + dv->devname); return 1; } + subdevs++; + } + + if (raiddisks != subdevs) { + fprintf(stderr, Name ": requested %d devices in array but listed %d\n", + raiddisks, subdevs); + return 1; } vers = md_get_version(mdfd); /* looks Ok, go for it */ - if (vers >= 90000) { + if (vers >= 9000) { mdu_array_info_t array; array.level = level; array.size = 0; @@ -85,12 +89,14 @@ int Build(char *mddev, int mdfd, int chunk, int level, array.md_minor = 0; if (fstat(mdfd, &stb)==0) array.md_minor = MINOR(stb.st_rdev); - array.not_persistent = 0; + array.not_persistent = 1; array.state = 0; /* not clean, but no errors */ array.active_disks = raiddisks; array.working_disks = raiddisks; array.spare_disks = 0; array.failed_disks = 0; + if (chunk == 0) + chunk = 64; array.chunk_size = chunk*1024; if (ioctl(mdfd, SET_ARRAY_INFO, &array)) { fprintf(stderr, Name ": SET_ARRAY_INFO failed for %s: %s\n", @@ -99,18 +105,18 @@ int Build(char *mddev, int mdfd, int chunk, int level, } } /* now add the devices */ - for (i=0; i<subdevs; i++) { - if (stat(subdev[i], &stb)) { + for ((i=0), (dv = devlist) ; dv ; i++, dv=dv->next) { + if (stat(dv->devname, &stb)) { fprintf(stderr, Name ": Wierd: %s has disappeared.\n", - subdev[i]); + dv->devname); goto abort; } - if ((stb.st_rdev & S_IFMT)!= S_IFBLK) { + if ((stb.st_mode & S_IFMT)!= S_IFBLK) { fprintf(stderr, Name ": Wierd: %s is no longer a block device.\n", - subdev[i]); + dv->devname); goto abort; } - if (vers> 90000) { + if (vers>= 9000) { mdu_disk_info_t disk; disk.number = i; disk.raid_disk = i; @@ -119,27 +125,27 @@ int Build(char *mddev, int mdfd, int chunk, int level, disk.minor = MINOR(stb.st_rdev); if (ioctl(mdfd, ADD_NEW_DISK, &disk)) { fprintf(stderr, Name ": ADD_NEW_DISK failed for %s: %s\n", - subdev[i], strerror(errno)); + dv->devname, strerror(errno)); goto abort; } } else { if (ioctl(mdfd, REGISTER_DEV, &stb.st_rdev)) { fprintf(stderr, Name ": REGISTER_DEV failed for %s.\n", - subdev[i], strerror(errno)); + dv->devname, strerror(errno)); goto abort; } } } /* now to start it */ - if (vers > 90000) { + if (vers >= 9000) { mdu_param_t param; /* not used by syscall */ - if (ioctl(mdfd, RUN_ARRAY, param)) { + if (ioctl(mdfd, RUN_ARRAY, ¶m)) { fprintf(stderr, Name ": RUN_ARRAY failed: %s\n", strerror(errno)); goto abort; } } else { - int arg; + unsigned long arg; arg=0; while (chunk > 4096) { arg++; @@ -159,7 +165,7 @@ int Build(char *mddev, int mdfd, int chunk, int level, return 0; abort: - if (vers > 900000) + if (vers >= 9000) ioctl(mdfd, STOP_ARRAY, 0); else ioctl(mdfd, STOP_MD, 0); @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) 19yy <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..b27e2d5 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,148 @@ + +Changes Prior to 0.6 release + + - Remove the limit on the number of device names that can be + given on the command line. + - Fix bug in --assemble --force where it would only update a + single superblock. + - Fix bogus printing of big numbers not being block devices + when given names of devices that don't exist. + - When --assemble --force, consider superblocks with an event + count that is 1 behind as out-of-date. Normally they are + considered up-to-date (as the kernel assumes this too). + - When marking drives as not-failed in the superblock, + we also mark them as ACTIVE and SYNC. + - Don't start arrays for which not all drives are available unless: + --scan which implies that all drives were found automatically + --run which means the user knows what they want + --force which means that we are fixing something broken + - Make sure all device numbers passed as 3rd arg of ioctl + are passed as unsigned lock, so that it works on SPARC + - If HOT_ADD_DISK failes for -a, then only try ADD_NEW_DISK + if we cannot read from the array, i.e. if the array is + not started yet. + - man page update + - Taught Examine to handle --scan. It examines all devices listed + on DEVICE lines in the config file. + - Added --brief (-b) flag for Examine and Detail to print out + and mdctl.conf compatible description with uuid=, level=, + disks= and - for Examine - devices= + --examine --brief collects all devices the make the one array and + list them as one entry. + - Added level= and disks= options to ARRAY lines in config files + so --brief output could be used as-is. + - Make parity style ({left,right}-{,a}symmetric) consistantly use -, + never _. + - Add "Array Size" to --detail output + - Change "Size" to "Device Size" and exclude from Detail of arrays + that do not have a consistent device size. + - Add Human readable MiB or GiB value on size lines of Detail and Examine + - --assemble --scan doesn't complain about active drives + - require number of spares given in -x to be listed. + - Made --build actually work. +Changes Prior to 0.5 release + + --assemble: + spare drives are handled properly. + + --force can be used to recover from 2-drive failures on RAID5 + If you belive that /dev/hda1 /dev/hdb1 /dev/hdc1 /dev/hdd1 should + make a raid5 array, but it has experienced multiple failures and + wont start, then + + mdctl --assemble --force /dev/md0 /dev/hd[abcd]1 + + Should update the superblock on the newest failed drive and + restart the array in degraded mode. You should then remove the + remaining failed drive and re-add it (if you are happy that it + might work). + + Ofcourse whenever you have a 2-drive failure, you have a risk + of corruption in data that hasn't be changed for a long time. So + this doesn't give you your array back all nice and happy, but it + does allow you to recover data that might not be corrupt. + + More flexibility in identifying a RAID array in the mdctl.conf + e.g. + array /dev/md4 super-minor=4 + + assembles /dev/md4 from all devices found that have a raid + superblock that says the minor number of the array is 4. + If the blocks with the right minor number do not all have the + same UUID, an error is flags and no assembly happens. + + array /dev/md3 devices=/dev/hd[abc]2 + + Assembles /dev/md3 drom /dev/hda2 /dev/hdb2 and/dev/hdc2. All + devices must exist and have raid superblock with the same uuid. + + If two identity specifiers are used, only devices that match all + of them are considered, so + + array /dev/md2 devices=/dev/hd?2 super-minor=2 + + will assemble /dev/md2 using all /dev/hd?2 devices which have a + raid superblock with minor number 2. + + --create: + When listing devices for --create, the word "missing" can be + used to indicate that the respective slot does not have a + working drive currently. This is similar to the "failed-disk" + directive in mkraid/raidtab. + e.g. + mdctl --create --level=5 -raid-disks=4 --spare-disks=2 + /dev/md0 /dev/sda /dev/sdb missing /dev/sdc /dev/sdd /dev/sde + + will create a raid5 array with the third slot empty, and two + spares. + + By default, raid5 arrays are created with the last slot empty + and drive listed for the last slot added as a spare. If a + "missing" slot is given, or if --force is given, then --create + does exactly what you ask and doesn't try to be clever. + + + --follow / --monitor: + + This is a new mode. I couldn't stop my self from picking a name + starting with F (as current modes start A,B,C,D,E) but I + relented and provided an alternate name that is somewhat more + meaningful. + + In this mode, mdctl does not exit, but runs continuously and + periodically polls all the md devices to see if they have had + any interested state change. + The changes that it currently notices are: + Fail - an active disc fails + FailSpare - a spare, that was presumably being build, fails + ActiveSpare - a spare becomes active, presumably after a rebuild. + + Options: + --mail mailaddress - send Email on any Fail* event + --program program - run the program on any event. + Args are: eventname mddevice subdevice(if-known) + --delay seconds - change from the default 60second pause + between polls. + + I plan to add functionality to this mode to allow sharing of + spare drives. If an array is marks "spare-group=fred", and it + has a failed drive and no spares, and if some other array is + also "spare-group=fred" and it has no failed drives, but does + have a spare drive that is big enough, the spare will be moved + to the first array. + + I also have the idea of adding a --grow mode which will re-organise + the data on an N disk raid0/4/5 array to be on an N+M disk array. + I have no concrete plans for this though. + + I got rid of the "v" in the archive file name, and include the + version number in the directory created by the archive. + + There is now a man page and mdctl.spec (for rpm) thanks to + Danilo Godec <danci@agenda.si>. + + Ofcourse, the man page is now out of date and despite being based on + the --help output, is not wholy correct. After I get --follow + working properly, I plan to revise the various documentation and/or + the code to make sure the two match. + @@ -1,7 +1,7 @@ /* * mdctl - manage Linux "md" devices aka RAID arrays. * - * Copyright (C) 2001 Neil Brown <neilb@cse.unsw.edu.au> + * Copyright (C) 2001-2002 Neil Brown <neilb@cse.unsw.edu.au> * * * This program is free software; you can redistribute it and/or modify @@ -33,7 +33,7 @@ int Create(char *mddev, int mdfd, int chunk, int level, int layout, int size, int raiddisks, int sparedisks, - int subdevs, char *subdev[], + int subdevs, mddev_dev_t devlist, int runstop, int verbose, int force) { /* @@ -53,8 +53,10 @@ int Create(char *mddev, int mdfd, * RUN_ARRAY */ int minsize, maxsize; - int maxdisc= -1, mindisc = -1; + char *mindisc = NULL; + char *maxdisc = NULL; int i; + mddev_dev_t dv; int fail=0, warn=0; struct stat stb; int first_missing = MD_SB_DISKS*2; @@ -93,7 +95,7 @@ int Create(char *mddev, int mdfd, fprintf(stderr, Name ": You have listed more disks (%d) than are in the array(%d)!\n", subdevs, raiddisks+sparedisks); return 1; } - if (subdevs < raiddisks) { + if (subdevs < raiddisks+sparedisks) { fprintf(stderr, Name ": You haven't given enough devices (real or missing) to create this array\n"); return 1; } @@ -121,11 +123,11 @@ int Create(char *mddev, int mdfd, /* now look at the subdevs */ array.active_disks = 0; array.working_disks = 0; - for (i=0; i<subdevs; i++) { - char *dname = subdev[i]; + for (dv=devlist; dv; dv=dv->next) { + char *dname = dv->devname; int dsize, freesize; int fd; - if (strcasecmp(subdev[i], "missing")==0) { + if (strcasecmp(dname, "missing")==0) { if (first_missing > i) first_missing = i; missing_disks ++; @@ -165,12 +167,12 @@ int Create(char *mddev, int mdfd, close(fd); continue; } - if (maxdisc< 0 || (maxdisc>=0 && freesize > maxsize)) { - maxdisc = i; + if (maxdisc == NULL || (maxdisc && freesize > maxsize)) { + maxdisc = dname; maxsize = freesize; } - if (mindisc < 0 || (mindisc >=0 && freesize < minsize)) { - mindisc = i; + if (mindisc ==NULL || (mindisc && freesize < minsize)) { + mindisc = dname; minsize = freesize; } warn |= check_ext2(fd, dname); @@ -183,7 +185,7 @@ int Create(char *mddev, int mdfd, return 1; } if (size == 0) { - if (mindisc == -1) { + if (mindisc == NULL) { fprintf(stderr, Name ": no size and no drives given - aborting create.\n"); return 1; } @@ -193,7 +195,7 @@ int Create(char *mddev, int mdfd, } if ((maxsize-size)*100 > maxsize) { fprintf(stderr, Name ": largest drive (%s) exceed size (%dK) by more than 1%\n", - subdev[maxdisc], size); + maxdisc, size); warn = 1; } @@ -267,7 +269,7 @@ int Create(char *mddev, int mdfd, return 1; } - for (i=0; i<subdevs; i++) { + for (i=0, dv = devlist ; dv ; dv=dv->next, i++) { int fd; struct stat stb; mdu_disk_info_t disk; @@ -280,15 +282,15 @@ int Create(char *mddev, int mdfd, disk.state = 6; /* active and in sync */ else disk.state = 0; - if (strcasecmp(subdev[i], "missing")==0) { + if (strcasecmp(dv->devname, "missing")==0) { disk.major = 0; disk.minor = 0; disk.state = 1; /* faulty */ } else { - fd = open(subdev[i], O_RDONLY, 0); + fd = open(dv->devname, O_RDONLY, 0); if (fd < 0) { fprintf(stderr, Name ": failed to open %s after earlier success - aborting\n", - subdev[i]); + dv->devname); return 1; } fstat(fd, &stb); @@ -298,7 +300,7 @@ int Create(char *mddev, int mdfd, } if (ioctl(mdfd, ADD_NEW_DISK, &disk)) { fprintf(stderr, Name ": ADD_NEW_DISK for %s failed: %s\n", - subdev[i], strerror(errno)); + dv->devname, strerror(errno)); return 1; } } @@ -1,7 +1,7 @@ /* * mdctl - manage Linux "md" devices aka RAID arrays. * - * Copyright (C) 2001 Neil Brown <neilb@cse.unsw.edu.au> + * Copyright (C) 2001-2002 Neil Brown <neilb@cse.unsw.edu.au> * * * This program is free software; you can redistribute it and/or modify @@ -31,7 +31,7 @@ #include "md_p.h" #include "md_u.h" -int Detail(char *dev) +int Detail(char *dev, int brief) { /* * Print out details for an md array by using @@ -77,48 +77,59 @@ int Detail(char *dev) return 1; } /* Ok, we have some info to print... */ - printf("%s:\n", dev); - printf(" Version : %02d.%02d.%02d\n", - array.major_version, array.minor_version, array.patch_version); - atime = array.ctime; - printf(" Creation Time : %.24s\n", ctime(&atime)); c = map_num(pers, array.level); - printf(" Raid Level : %s\n", c?c:"-unknown-"); - printf(" Size : %d\n", array.size); - printf(" Raid Disks : %d\n", array.raid_disks); - printf(" Total Disks : %d\n", array.nr_disks); - printf("Preferred Minor : %d\n", array.md_minor); - printf(" Persistance : Superblock is %spersistant\n", - array.not_persistent?"not ":""); - printf("\n"); - atime = array.utime; - printf(" Update Time : %.24s\n", ctime(&atime)); - printf(" State : %s, %serrors\n", - (array.state&(1<<MD_SB_CLEAN))?"clean":"dirty", - (array.state&(1<<MD_SB_ERRORS))?"":"no-"); - printf(" Active Drives : %d\n", array.active_disks); - printf(" Working Drives : %d\n", array.working_disks); - printf(" Failed Drives : %d\n", array.failed_disks); - printf(" Spare Drives : %d\n", array.spare_disks); - printf("\n"); - if (array.level == 5) { - c = map_num(r5layout, array.layout); - printf(" Layout : %s\n", c?c:"-unknown-"); - } - switch (array.level) { - case 0: - case 4: - case 5: - printf(" Chunk Size : %dK\n", array.chunk_size/1024); - break; - case -1: - printf(" Rounding : %dK\n", array.chunk_size/1024); - break; - default: break; - } + if (brief) + printf("ARRAY %s level=%s disks=%d", dev, c?c:"-unknown-",array.raid_disks ); + else { + int array_size; + if (ioctl(fd, BLKGETSIZE, &array_size)) + array_size = 0; + else array_size>>= 1; + printf("%s:\n", dev); + printf(" Version : %02d.%02d.%02d\n", + array.major_version, array.minor_version, array.patch_version); + atime = array.ctime; + printf(" Creation Time : %.24s\n", ctime(&atime)); + printf(" Raid Level : %s\n", c?c:"-unknown-"); + if (array_size) + printf(" Array Size : %d%s\n", array_size, human_size(array_size)); + if (array.level >= 1) + printf(" Device Size : %d%s\n", array.size, human_size(array.size)); + printf(" Raid Disks : %d\n", array.raid_disks); + printf(" Total Disks : %d\n", array.nr_disks); + printf("Preferred Minor : %d\n", array.md_minor); + printf(" Persistance : Superblock is %spersistant\n", + array.not_persistent?"not ":""); + printf("\n"); + atime = array.utime; + printf(" Update Time : %.24s\n", ctime(&atime)); + printf(" State : %s, %serrors\n", + (array.state&(1<<MD_SB_CLEAN))?"clean":"dirty", + (array.state&(1<<MD_SB_ERRORS))?"":"no-"); + printf(" Active Drives : %d\n", array.active_disks); + printf(" Working Drives : %d\n", array.working_disks); + printf(" Failed Drives : %d\n", array.failed_disks); + printf(" Spare Drives : %d\n", array.spare_disks); + printf("\n"); + if (array.level == 5) { + c = map_num(r5layout, array.layout); + printf(" Layout : %s\n", c?c:"-unknown-"); + } + switch (array.level) { + case 0: + case 4: + case 5: + printf(" Chunk Size : %dK\n", array.chunk_size/1024); + break; + case -1: + printf(" Rounding : %dK\n", array.chunk_size/1024); + break; + default: break; + } - printf("\n"); - printf(" Number Major Minor RaidDisk State\n"); + printf("\n"); + printf(" Number Major Minor RaidDisk State\n"); + } for (d= 0; d<array.raid_disks+array.spare_disks; d++) { mdu_disk_info_t disk; char *dv; @@ -128,34 +139,40 @@ int Detail(char *dev) d, strerror(errno)); continue; } - printf(" %5d %5d %5d %5d ", - disk.number, disk.major, disk.minor, disk.raid_disk); - if (disk.state & (1<<MD_DISK_FAULTY)) printf(" faulty"); - if (disk.state & (1<<MD_DISK_ACTIVE)) printf(" active"); - if (disk.state & (1<<MD_DISK_SYNC)) printf(" sync"); - if (disk.state & (1<<MD_DISK_REMOVED)) printf(" removed"); + if (!brief) { + printf(" %5d %5d %5d %5d ", + disk.number, disk.major, disk.minor, disk.raid_disk); + if (disk.state & (1<<MD_DISK_FAULTY)) printf(" faulty"); + if (disk.state & (1<<MD_DISK_ACTIVE)) printf(" active"); + if (disk.state & (1<<MD_DISK_SYNC)) printf(" sync"); + if (disk.state & (1<<MD_DISK_REMOVED)) printf(" removed"); + } if ((dv=map_dev(disk.major, disk.minor))) { - printf(" %s", dv); - if (!have_super) { - /* try to read the superblock from this device - * to get more info - */ - int fd = open(dv, O_RDONLY); - if (fd >=0 && - load_super(fd, &super) ==0 && - super.ctime == array.ctime && - super.level == array.level) - have_super = 1; - } + if (!brief) printf(" %s", dv); + if (!have_super) { + /* try to read the superblock from this device + * to get more info + */ + int fd = open(dv, O_RDONLY); + if (fd >=0 && + load_super(fd, &super) ==0 && + super.ctime == array.ctime && + super.level == array.level) + have_super = 1; + } } - printf("\n"); + if (!brief) printf("\n"); } if (have_super) { + if (brief) printf(" UUID="); + else printf(" UUID : "); if (super.minor_version >= 90) - printf(" UUID : %08x:%08x:%08x:%08x\n", super.set_uuid0, super.set_uuid1, - super.set_uuid2, super.set_uuid3); - else - printf(" UUID : %08x\n", super.set_uuid0); + printf("%08x:%08x:%08x:%08x", super.set_uuid0, super.set_uuid1, + super.set_uuid2, super.set_uuid3); + else + printf("%08x", super.set_uuid0); + if (!brief) printf("\n"); } + if (brief) printf("\n"); return 0; } @@ -1,7 +1,7 @@ /* * mdctl - manage Linux "md" devices aka RAID arrays. * - * Copyright (C) 2001 Neil Brown <neilb@cse.unsw.edu.au> + * Copyright (C) 2001-2002 Neil Brown <neilb@cse.unsw.edu.au> * * * This program is free software; you can redistribute it and/or modify @@ -28,13 +28,14 @@ */ #include "mdctl.h" +#include "dlink.h" #if ! defined(__BIG_ENDIAN) && ! defined(__LITTLE_ENDIAN) #error no endian defined #endif #include "md_u.h" #include "md_p.h" -int Examine(char *dev) +int Examine(mddev_dev_t devlist, int brief, char *conffile) { /* Read the raid superblock from a device and @@ -49,120 +50,186 @@ int Examine(char *dev) * * utime, state etc * + * If (brief) gather devices for same array and just print a mdctl.conf line including devices= + * if devlist==NULL, use conf_get_devs( */ - int fd = open(dev, O_RDONLY); + int fd; time_t atime; mdp_super_t super; int d; char *c; - int rv; + int rv = 0; + int err; + int scan= 0; - if (fd < 0) { - fprintf(stderr,Name ": cannot open %s: %s\n", - dev, strerror(errno)); + struct array { + mdp_super_t super; + void *devs; + struct array *next; + } *arrays = NULL; + + if (devlist == NULL) { + devlist = conf_get_devs(conffile); + scan=1; + } + if (devlist == NULL) { + fprintf(stderr, Name ": No devices listed in %s\n", conffile); return 1; } - rv = load_super(fd, &super); - close(fd); - switch(rv) { - case 1: - fprintf(stderr, Name ": cannot find device size for %s: %s\n", - dev, strerror(errno)); - return 1; - case 2: + for (; devlist ; devlist=devlist->next) { + fd = open(devlist->devname, O_RDONLY); + if (fd < 0) { + if (!scan) + fprintf(stderr,Name ": cannot open %s: %s\n", + devlist->devname, strerror(errno)); + err = 1; + } + else { + err = load_super(fd, &super); + close(fd); + } + if (err && (brief||scan)) + continue; + if (err) rv =1; + switch(err) { + case 1: + fprintf(stderr, Name ": cannot find device size for %s: %s\n", + devlist->devname, strerror(errno)); + continue; + case 2: /* fprintf(stderr, Name ": %s is too small for md: size is %ld sectors\n", - dev, size); + devlist->devname, size); */ - fprintf(stderr, Name ": %s is too small for md\n", - dev); - return 1; - case 3: - fprintf(stderr, Name ": Cannot seek to superblock on %s: %s\n", - dev, strerror(errno)); - return 1; - case 4: - fprintf(stderr, Name ": Cannot read superblock on %s\n", - dev); - return 1; - case 5: - fprintf(stderr, Name ": No super block found on %s (Expected magic %08x, got %08x)\n", - dev, MD_SB_MAGIC, super.md_magic); - return 1; - case 6: - fprintf(stderr, Name ": Cannot interpret superblock on %s - version is %d\n", - dev, super.major_version); - return 1; - } + fprintf(stderr, Name ": %s is too small for md\n", + devlist->devname); + continue; + case 3: + fprintf(stderr, Name ": Cannot seek to superblock on %s: %s\n", + devlist->devname, strerror(errno)); + continue; + case 4: + fprintf(stderr, Name ": Cannot read superblock on %s\n", + devlist->devname); + continue; + case 5: + fprintf(stderr, Name ": No super block found on %s (Expected magic %08x, got %08x)\n", + devlist->devname, MD_SB_MAGIC, super.md_magic); + continue; + case 6: + fprintf(stderr, Name ": Cannot interpret superblock on %s - version is %d\n", + devlist->devname, super.major_version); + continue; + } - /* Ok, its good enough to try, though the checksum could be wrong */ - printf("%s:\n",dev); - printf(" Magic : %08x\n", super.md_magic); - printf(" Version : %02d.%02d.%02d\n", super.major_version, super.minor_version, - super.patch_version); - if (super.minor_version >= 90) - printf(" UUID : %08x:%08x:%08x:%08x\n", super.set_uuid0, super.set_uuid1, - super.set_uuid2, super.set_uuid3); - else - printf(" UUID : %08x\n", super.set_uuid0); + /* Ok, its good enough to try, though the checksum could be wrong */ + if (brief) { + struct array *ap; + char *d; + for (ap=arrays; ap; ap=ap->next) { + if (compare_super(&ap->super, &super)==0) + break; + } + if (!ap) { + ap = malloc(sizeof(*ap)); + ap->super = super; + ap->devs = dl_head(); + ap->next = arrays; + arrays = ap; + } + d = dl_strdup(devlist->devname); + dl_add(ap->devs, d); + } else { + printf("%s:\n",devlist->devname); + printf(" Magic : %08x\n", super.md_magic); + printf(" Version : %02d.%02d.%02d\n", super.major_version, super.minor_version, + super.patch_version); + if (super.minor_version >= 90) + printf(" UUID : %08x:%08x:%08x:%08x\n", super.set_uuid0, super.set_uuid1, + super.set_uuid2, super.set_uuid3); + else + printf(" UUID : %08x\n", super.set_uuid0); - atime = super.ctime; - printf(" Creation Time : %.24s\n", ctime(&atime)); - c=map_num(pers, super.level); - printf(" Raid Level : %s\n", c?c:"-unknown-"); - printf(" Size : %d\n", super.size); - printf(" Raid Disks : %d\n", super.raid_disks); - printf(" Total Disks : %d\n", super.nr_disks); - printf("Preferred Minor : %d\n", super.md_minor); - printf("\n"); - atime = super.utime; - printf(" Update Time : %.24s\n", ctime(&atime)); - printf(" State : %s, %serrors\n", - (super.state&(1<<MD_SB_CLEAN))?"clean":"dirty", - (super.state&(1<<MD_SB_ERRORS))?"":"no-"); - printf(" Active Drives : %d\n", super.active_disks); - printf(" Working Drives : %d\n", super.working_disks); - printf(" Failed Drives : %d\n", super.failed_disks); - printf(" Spare Drives : %d\n", super.spare_disks); - if (calc_sb_csum(&super) == super.sb_csum) - printf(" Checksum : %x - correct\n", super.sb_csum); - else - printf(" Checksum : %x - expected %x\n", super.sb_csum, calc_sb_csum(&super)); - printf(" Events : %d.%d\n", super.events_hi, super.events_lo); - printf("\n"); - if (super.level == 5) { - c = map_num(r5layout, super.layout); - printf(" Layout : %s\n", c?c:"-unknown-"); - } - switch(super.level) { - case 0: - case 4: - case 5: - printf(" Chunk Size : %dK\n", super.chunk_size/1024); - break; - case -1: - printf(" Rounding : %dK\n", super.chunk_size/1024); - break; - default: break; + atime = super.ctime; + printf(" Creation Time : %.24s\n", ctime(&atime)); + c=map_num(pers, super.level); + printf(" Raid Level : %s\n", c?c:"-unknown-"); + printf(" Device Size : %d%s\n", super.size, human_size(super.size)); + printf(" Raid Disks : %d\n", super.raid_disks); + printf(" Total Disks : %d\n", super.nr_disks); + printf("Preferred Minor : %d\n", super.md_minor); + printf("\n"); + atime = super.utime; + printf(" Update Time : %.24s\n", ctime(&atime)); + printf(" State : %s, %serrors\n", + (super.state&(1<<MD_SB_CLEAN))?"clean":"dirty", + (super.state&(1<<MD_SB_ERRORS))?"":"no-"); + printf(" Active Drives : %d\n", super.active_disks); + printf(" Working Drives : %d\n", super.working_disks); + printf(" Failed Drives : %d\n", super.failed_disks); + printf(" Spare Drives : %d\n", super.spare_disks); + if (calc_sb_csum(&super) == super.sb_csum) + printf(" Checksum : %x - correct\n", super.sb_csum); + else + printf(" Checksum : %x - expected %x\n", super.sb_csum, calc_sb_csum(&super)); + printf(" Events : %d.%d\n", super.events_hi, super.events_lo); + printf("\n"); + if (super.level == 5) { + c = map_num(r5layout, super.layout); + printf(" Layout : %s\n", c?c:"-unknown-"); + } + switch(super.level) { + case 0: + case 4: + case 5: + printf(" Chunk Size : %dK\n", super.chunk_size/1024); + break; + case -1: + printf(" Rounding : %dK\n", super.chunk_size/1024); + break; + default: break; + } + printf("\n"); + printf(" Number Major Minor RaidDisk State\n"); + for (d= -1; d<(signed int)(super.raid_disks+super.spare_disks); d++) { + mdp_disk_t *dp; + char *dv; + char nb[5]; + if (d>=0) dp = &super.disks[d]; + else dp = &super.this_disk; + sprintf(nb, "%4d", d); + printf("%4s %5d %5d %5d %5d ", d < 0 ? "this" : nb, + dp->number, dp->major, dp->minor, dp->raid_disk); + if (dp->state & (1<<MD_DISK_FAULTY)) printf(" faulty"); + if (dp->state & (1<<MD_DISK_ACTIVE)) printf(" active"); + if (dp->state & (1<<MD_DISK_SYNC)) printf(" sync"); + if (dp->state & (1<<MD_DISK_REMOVED)) printf(" removed"); + if ((dv=map_dev(dp->major, dp->minor))) + printf(" %s", dv); + printf("\n"); + } + } } - printf("\n"); - printf(" Number Major Minor RaidDisk State\n"); - for (d= -1; d<(signed int)(super.raid_disks+super.spare_disks); d++) { - mdp_disk_t *dp; - char *dv; - char nb[5]; - if (d>=0) dp = &super.disks[d]; - else dp = &super.this_disk; - sprintf(nb, "%4d", d); - printf("%4s %5d %5d %5d %5d ", d < 0 ? "this" : nb, - dp->number, dp->major, dp->minor, dp->raid_disk); - if (dp->state & (1<<MD_DISK_FAULTY)) printf(" faulty"); - if (dp->state & (1<<MD_DISK_ACTIVE)) printf(" active"); - if (dp->state & (1<<MD_DISK_SYNC)) printf(" sync"); - if (dp->state & (1<<MD_DISK_REMOVED)) printf(" removed"); - if ((dv=map_dev(dp->major, dp->minor))) - printf(" %s", dv); - printf("\n"); + if (brief) { + struct array *ap; + for (ap=arrays; ap; ap=ap->next) { + char sep='='; + char *c=map_num(pers, ap->super.level); + char *d; + printf("ARRAY /dev/md%d level=%s disks=%d UUID=", + ap->super.md_minor, c?c:"-unknown-", ap->super.raid_disks); + if (ap->super.minor_version >= 90) + printf("%08x:%08x:%08x:%08x", ap->super.set_uuid0, ap->super.set_uuid1, + ap->super.set_uuid2, ap->super.set_uuid3); + else + printf("%08x", ap->super.set_uuid0); + printf("\n devices"); + for (d=dl_next(ap->devs); d!= ap->devs; d=dl_next(d)) { + printf("%c%s", sep, d); + sep=','; + } + printf("\n"); + } } - return 0; + return rv; } @@ -1,7 +1,7 @@ # # mdctl - manage Linux "md" devices aka RAID arrays. # -# Copyright (C) 2001 Neil Brown <neilb@cse.unsw.edu.au> +# Copyright (C) 2001-2002 Neil Brown <neilb@cse.unsw.edu.au> # # # This program is free software; you can redistribute it and/or modify @@ -27,18 +27,29 @@ # Australia # +CC = gcc CFLAGS = -Wall,error,strict-prototypes -ggdb +INSTALL = /usr/bin/install +DESTDIR = /sbin + OBJS = mdctl.o config.o ReadMe.o util.o Manage.o Assemble.o Build.o Create.o Detail.o Examine.o Monitor.o dlink.o -all : mdctl + +all : mdctl mdctl.man mdctl : $(OBJS) $(CC) -o mdctl $^ +mdctl.man : mdctl.8 + nroff -man mdctl.8 > mdctl.man + $(OBJS) : mdctl.h +install : mdctl + $(INSTALL) -m 755 mdctl $(DESTDIR)/sbin + clean : - rm -f mdctl $(OBJS) core + rm -f mdctl $(OBJS) core mdctl.man dist : clean ./makedist @@ -1,7 +1,7 @@ /* * mdctl - manage Linux "md" devices aka RAID arrays. * - * Copyright (C) 2001 Neil Brown <neilb@cse.unsw.edu.au> + * Copyright (C) 2001-2002 Neil Brown <neilb@cse.unsw.edu.au> * * * This program is free software; you can redistribute it and/or modify @@ -116,8 +116,8 @@ int Manage_runstop(char *devname, int fd, int runstop) } int Manage_subdevs(char *devname, int fd, - int devcnt, char *devnames[], int devmodes[]) - { + mddev_dev_t devlist) +{ /* do something to each dev. * devmode can be * 'a' - add the device @@ -128,37 +128,49 @@ int Manage_subdevs(char *devname, int fd, */ mdu_array_info_t array; mdu_disk_info_t disc; + mddev_dev_t dv; struct stat stb; int i,j; + int save_errno; + static buf[4096]; if (ioctl(fd, GET_ARRAY_INFO, &array)) { fprintf(stderr, Name ": cannot get array info for %s\n", devname); return 1; } - for (i=0 ; i<devcnt; i++) { - if (stat(devnames[i], &stb)) { + for (dv = devlist ; dv; dv=dv->next) { + if (stat(dv->devname, &stb)) { fprintf(stderr, Name ": cannot find %s: %s\n", - devnames[i], strerror(errno)); + dv->devname, strerror(errno)); return 1; } if ((stb.st_mode & S_IFMT) != S_IFBLK) { fprintf(stderr, Name ": %s is not a block device.\n", - devnames[i]); + dv->devname); return 1; } - switch(devmodes[i]){ + switch(dv->disposition){ default: fprintf(stderr, Name ": internal error - devmode[%d]=%d\n", - i, devmodes[i]); + i, dv->disposition); return 1; case 'a': /* add the device - hot or cold */ - if (ioctl(fd, HOT_ADD_DISK, stb.st_rdev)==0) { + if (ioctl(fd, HOT_ADD_DISK, (unsigned long)stb.st_rdev)==0) { fprintf(stderr, Name ": hot added %s\n", - devnames[i]); + dv->devname); continue; } + save_errno = errno; + if (read(fd, buf, sizeof(buf)) > 0) { + /* array is active, so don't try to add. + * i.e. something is wrong + */ + fprintf(stderr, Name ": hot add failed for %s: %s\n", + dv->devname, strerror(save_errno)); + return 1; + } /* try ADD_NEW_DISK. * we might be creating, we might be assembling, * it is hard to tell. @@ -180,32 +192,32 @@ int Manage_subdevs(char *devname, int fd, disc.minor = MINOR(stb.st_rdev); if (ioctl(fd,ADD_NEW_DISK, &disc)) { fprintf(stderr, Name ": add new disk failed for %s: %s\n", - devnames[i], strerror(errno)); + dv->devname, strerror(errno)); return 1; } - fprintf(stderr, Name ": added %s\n", devnames[i]); + fprintf(stderr, Name ": added %s\n", dv->devname); break; case 'r': /* hot remove */ /* FIXME check that it is a current member */ - if (ioctl(fd, HOT_REMOVE_DISK, stb.st_rdev)) { + if (ioctl(fd, HOT_REMOVE_DISK, (unsigned long)stb.st_rdev)) { fprintf(stderr, Name ": hot remove failed for %s: %s\n", - devnames[i], strerror(errno)); + dv->devname, strerror(errno)); return 1; } - fprintf(stderr, Name ": hot removed %s\n", devnames[i]); + fprintf(stderr, Name ": hot removed %s\n", dv->devname); break; case 'f': /* set faulty */ /* FIXME check current member */ - if (ioctl(fd, SET_DISK_FAULTY, stb.st_rdev)) { + if (ioctl(fd, SET_DISK_FAULTY, (unsigned long) stb.st_rdev)) { fprintf(stderr, Name ": set disk faulty failed for %s: %s\n", - devnames[i], strerror(errno)); + dv->devname, strerror(errno)); return 1; } fprintf(stderr, Name ": set %s faulty in %s\n", - devnames[i], devname); + dv->devname, devname); break; } } @@ -1,7 +1,7 @@ /* * mdctl - manage Linux "md" devices aka RAID arrays. * - * Copyright (C) 2001 Neil Brown <neilb@cse.unsw.edu.au> + * Copyright (C) 2001-2002 Neil Brown <neilb@cse.unsw.edu.au> * * * This program is free software; you can redistribute it and/or modify @@ -34,7 +34,7 @@ static void alert(char *event, char *dev, char *disc, char *mailaddr, char *cmd); -int Monitor(int num_devs, char *devlist[], +int Monitor(mddev_dev_t devlist, char *mailaddr, char *alert_cmd, int period, char *config) @@ -75,10 +75,12 @@ int Monitor(int num_devs, char *devlist[], int finished = 0; while (! finished) { mddev_ident_t mdlist = NULL; + mddev_dev_t dv; int dnum=0; - if (num_devs == 0) + if (devlist== NULL) mdlist = conf_get_ident(config, NULL); - while (dnum < num_devs || mdlist) { + dv = devlist; + while (dv || mdlist) { mddev_ident_t mdident; struct state *st; mdu_array_info_t array; @@ -87,9 +89,10 @@ int Monitor(int num_devs, char *devlist[], char *event = NULL; int i; char *event_disc = NULL; - if (num_devs) { - dev = devlist[dnum++]; + if (dv) { + dev = dv->devname; mdident = conf_get_ident(config, dev); + dv = dv->next; } else { mdident = mdlist; dev = mdident->devname; @@ -171,6 +174,11 @@ int Monitor(int num_devs, char *devlist[], static void alert(char *event, char *dev, char *disc, char *mailaddr, char *cmd) { + if (!cmd && !mailaddr) { + time_t now = time(0); + + printf("%0.15s: %s on %s %s\n", ctime(&now)+4, event, dev, disc?disc:"unknown device"); + } if (cmd) { int pid = fork(); switch(pid) { @@ -1,7 +1,7 @@ /* * mdctl - manage Linux "md" devices aka RAID arrays. * - * Copyright (C) 2001 Neil Brown <neilb@cse.unsw.edu.au> + * Copyright (C) 2001-2002 Neil Brown <neilb@cse.unsw.edu.au> * * * This program is free software; you can redistribute it and/or modify @@ -29,7 +29,7 @@ #include "mdctl.h" -char Version[] = Name " - v0.5 - 23 August 2001\n"; +char Version[] = Name " - v0.6 - 7 March 2002\n"; /* * File: ReadMe.c * @@ -78,7 +78,7 @@ char Version[] = Name " - v0.5 - 23 August 2001\n"; * command, subsequent Manage commands can finish the job. */ -char short_options[]="-ABCDEFhVvc:l:p:m:n:x:u:c:d:z:sarfRSow"; +char short_options[]="-ABCDEFhVvbc:l:p:m:n:x:u:c:d:z:sarfRSow"; struct option long_options[] = { {"manage", 0, 0, '@'}, {"assemble", 0, 0, 'A'}, @@ -122,6 +122,9 @@ struct option long_options[] = { {"readonly", 0, 0, 'o'}, {"readwrite", 0, 0, 'w'}, + /* For Detail/Examine */ + {"brief", 0, 0, 'b'}, + /* For Follow/monitor */ {"mail", 1, 0, 'm'}, {"program", 1, 0, 'p'}, @@ -174,7 +177,7 @@ char Help[] = " --paritiy= -p : raid5 parity algorith: {left,right}-{,a}symmetric\n" " --layout= : same as --parity\n" " --raid-disks= -n : number of active devices in array\n" -" --spare-disks= -x : number of spares (eXtras) to allow space for\n" +" --spare-disks= -x : number of spares (eXtras) devices in initial array\n" " --size= -z : Size (in K) of each drive in RAID1/4/5 - optional\n" " --force -f : Honour devices as listed on command line. Don't\n" " : insert a missing drive for RAID5.\n" @@ -188,6 +191,9 @@ char Help[] = " --scan -s : scan config file for missing information\n" " --force -f : Assemble the array even if some superblocks appear out-of-date\n" "\n" +" For detail or examine:\n" +" --brief -b : Just print device name and UUID\n" +"\n" " For follow/monitor:\n" " --mail= -m : Address to mail alerts of failure to\n" " --program= -p : Program to run when an event is detected\n" @@ -306,10 +312,10 @@ char Help_assemble[] = /* name/number mappings */ mapping_t r5layout[] = { - { "left_asymmetric", 0}, - { "right_asymmetric", 1}, - { "left_symmetric", 2}, - { "right_symmetric", 3}, + { "left-asymmetric", 0}, + { "right-asymmetric", 1}, + { "left-symmetric", 2}, + { "right-symmetric", 3}, { "default", 2}, { "la", 0}, @@ -1,21 +1,47 @@ + +?? Allow -S /dev/md? - current complains subsequent not a/d/r + +* write proc.c to parse /proc/mdstat file, and maybe /proc/partitions too. + Build list of arrays: name, rebuild-percent + +* --detail --scan to read mdctl.conf, and then iterate over these, + but assume --brief. --verbose can override + check each subdevice to see if it is in conf_get_devs. + Warn if not. + +* Support multipath ... maybe... + +* --follow to syslog + +* --follow to move spares around + +* --follow to notice other events: + rebuild started + spare activated + spare removed + spare added + +------------------------------------ +- --examine --scan scans all drives and build an mdctl.conf file DONE + - check superblock checksum in examine DONE - report "chunk" or "rounding" depending on raid level DONE - report "linear" instead of "-1" for raid level DONE - decode ayout depending on raid level DONE -- --verbose and --force flags. +- --verbose and --force flags. DONE - set md_minor, *_disks for Create - DONE - for create raid5, how to choose between all working, but not insync - one missing, one spare, insync -- and for raid1 - some failed drives... + one missing, one spare, insync DONE (--force) +- and for raid1 - some failed drives... (missing) - when RUN_ARRAY, make sure *_disks counts are right - get --detail to extract extra stuff from superblock, like uuid DONE -- --detail --brief to give a config file line +- --detail --brief to give a config file line DONE - parse config file. DONE - test... @@ -23,7 +49,7 @@ then try to assemble that device first. -- mdctl -S /dev/md0 /dev/md1 gives internal error +- mdctl -S /dev/md0 /dev/md1 gives internal error FIXED - mdctl --detail --scan print summary of what it can find? @@ -74,4 +100,4 @@ New mode: --Monitor (or --Follow) rebuild started spare activated spare removed - spare added
\ No newline at end of file + spare added @@ -1,7 +1,7 @@ /* * mdctl - manage Linux "md" devices aka RAID arrays. * - * Copyright (C) 2001 Neil Brown <neilb@cse.unsw.edu.au> + * Copyright (C) 2001-2002 Neil Brown <neilb@cse.unsw.edu.au> * * * This program is free software; you can redistribute it and/or modify @@ -229,6 +229,8 @@ void arrayline(char *line) mis.uuid_set = 0; mis.super_minor = -1; + mis.level = -10; + mis.raid_disks = -1; mis.devices = NULL; mis.devname = NULL; @@ -273,6 +275,12 @@ void arrayline(char *line) w); else mis.spare_group = strdup(w+12); + } else if (strncasecmp(w, "level=", 6) == 0 ) { + /* this is mainly for compatability with --brief output */ + mis.level = map_name(pers, w+6); + } else if (strncasecmp(w, "disks=", 6) == 0 ) { + /* again, for compat */ + mis.raid_disks = atoi(w+6); } else { fprintf(stderr, Name ": unrecognised word on ARRAY line: %s\n", w); @@ -16,6 +16,6 @@ then exit 1 fi trap "rm $target/$base; exit" 1 2 3 -( cd .. ; ln -s mdctl mdctl-$version ; tar czhvf - --exclude='*,v' --exclude='*.o' --exclude=RCS mdctl-$version ; rm mdctl-$version ) > $target/$base +( cd .. ; ln -s mdctl mdctl-$version ; tar czhvf - --exclude='*,v' --exclude='*.o' --exclude mdctl --exclude=RCS mdctl-$version ; rm mdctl-$version ) > $target/$base chmod a+r $target/$base ls -l $target/$base @@ -1,286 +1,574 @@ .\" -*- nroff -*- .TH mdctl 8 .SH NAME -mdctl \- a single program that can be used to control Linux md devices +mdctl \- manage MD devices +.I aka +Linux Software Raid. + .SH SYNOPSIS -.BI mdctl -[mode] <raiddevice> [options] +.BI mdctl " [mode] <raiddevice> [options] <subdevices>" .SH DESCRIPTION RAID devices are virtual devices created from two or more -real block devices. This allows multiple disks to be combined into a single -filesystem, possibly with integrated redundancy to survive drive failure.. Linux RAID devices -are implemented through the md device driver. - -If you're using the -.B /proc -filesystem, -.B /proc/mdstat -gives you informations about md devices status. - -Currently, Linux supports linear md devices, RAID0 (striping), RAID1 -(mirrroring), RAID4 and RAID5. For information on the various levels of -RAID, check out: - - http://ostenfeld.dk/~jakob/Software-RAID.HOWTO/ - -for new releases of the RAID driver check out: - - ftp://ftp.kernel.org/pub/linux/kernel/people/mingo/raid-patches - -.B mdctl -is a single program that can be used to control Linux md devices. It -is intended to provide all the functionality (and more) of the mdtools -and raidtools but with a very different interface. - -mdctl can perform all functions without a configuration file. There is the -option of using a configuration file, but not in the same way that raidtools -uses one. raidtools uses a configuration file to describe how to create a -RAID array, and also uses this file partially to start a previously created -RAID array. Further, raidtools requires the configuration file for such -things as stopping a raid array which needs to know nothing about the array. - -The configuration file that can be used by mdctl lists two different things: - -.IP "\fB\-\fP" -a list of md devices and information about how to identify each. The -identity can consist of a UUID, and minor-number as recorded on the -superblock, or a list of devices. +real block devices. This allows multiple devices (typically disk +drives or partitions there-of) to be combined into a single device to +hold (for example) a single filesystem. +Some RAID levels included redundancy and so can survive some degree of +device failure. + +Linux Software RAID devices are implemented through the md (Multiple Devices) device driver. + +Currently, Linux supports +.B LINEAR +md devices, +.B RAID0 +(striping), +.B RAID1 +(mirroring), +.B RAID4 +and +.B RAID5. + +Recent kernels (2002) also support a mode known as +.BR MULTIPATH . +.B mdctl +does not support MULTIPATH as yet. -.IP "\fB\-\fP" -a list of devices that should be scanned for md sub-devices. +.B mdctl +is a program that can be used to create and manage MD devices. As +such it provides a similar set of functionality to the +.B raidtools +packages. +The key differences between +.B mdctl +and +.B raidtools +are: +.IP \(bu 4 +.B mdctl +is a single program and not a collection of programs. +.IP \(bu 4 +.B mdctl +can perform (almost) all of its functions without having a +configuration file. Also mdctl helps with management of the configuration +file. +.IP \(bu 4 +.B mdctl +can provide information about your arrays (through Detail and Examine) +that +.B raidtools +cannot. +.IP \(bu 4 +.B raidtools +can manage MULTIPATH devices which +.B mdctl +cannot yet manage. .SH MODES -mdctl has 4 major modes of operation: -.IP "\fBCreate\fP" -This mode is used to create a new array with a superblock. It can progress -in several step create-add-add-run or it can all happen with one command. - -.IP "\fBAssemble\fP" -This mode is used to assemble the parts of a previously created +mdctl has 7 major modes of operation: +.TP +.B Assemble +Assemble the parts of a previously created array into an active array. Components can be explicitly given or can be searched for. .B mdctl -(optionally) checks that the components -do form a bonafide array, and can, on request, fiddle superblock -version numbers so as to assemble a faulty array. - -.IP "\fBBuild\fP" -This is for building legacy arrays without superblocks. - -.IP "\fBManage\fP" +checks that the components +do form a bona fide array, and can, on request, fiddle superblock +information so as to assemble a faulty array. + +.TP +.B Build +Build a legacy array without per-device superblocks. + +.TP +.B Create +Create a new array with per-device superblocks. +'''It can progress +'''in several step create-add-add-run or it can all happen with one command. + +.TP +.B Detail +Display the details of a given md device. Details include the RAID +level, the number of devices, which ones are faulty (if any), and the +array UUID. + +.TP +.B Examine +Examine a device to see if it is part of an md array, and print out +the details of that array. +This mode can also be used to examine a large number of devices and to +print out a summary of the arrays found in a format suitable for the +.B mdctl.conf +configuration file. + +.TP +.B "Follow or Monitor" +Monitor one or more md devices and act on any state changes. + +.TP +.B Manage This is for odd bits an pieces like hotadd, hotremove, setfaulty, stop, -readonly,readwrite If an array is only partially setup by the -Create/Assemble/Build command, subsequent Manage commands can finish the -job. +readonly, readwrite. +'''If an array is only partially setup by the +'''Create or Assemble commands, subsequent Manage commands can finish the +'''job. .SH OPTIONS Available options are: -.IP "\fB\-C\fP, \fB\-\-create\fP" -Create a new array +.TP +.BR -A ", " --assemble +Assemble an existing array. -.IP "\fB-A\fP, \fB\-\-assemble\fP" -Assemble an existing array +.TP +.BR -B ", " --build +Build a legacy array without superblocks. -.IP "\fB\-B\fP, \fB\-\-build\fP" -Build a legacy array without superblock +.TP +.BR -C ", " --create +Create a new array. -.IP "\fB\-D\fP, \fB\-\-detail\fP" -Print detail of a given md array +.TP +.BR -D ", " --detail +Print detail of one or more md devices. -.IP "\fB\-E\fP, \fB\-\-examine\fP" -Print content of md superblock on device +.TP +.BR -E ", " --examine +Print content of md superblock on device(s). -.IP "\fB\-h\fP, \fB\-\-help\fP" -This help message or, after above option, mode specific help message +.TP +.BR -F ", " --follow ", " --monitor +Select +.B Monitor +mode. -.IP "\fB\-V\fP, \fB\-\-version\fP" -Print version information for mdctl +.TP +.BR -h ", " --help +Display help message or, after above option, mode specific help message. -.IP "\fB\-v\fP, \fB\-\-verbose\fP" -Be more verbose about what is happening +.TP +.BR -V ", " --version +Print version information for mdctl. -.SH For create or build: +.TP +.BR -v ", " --verbose +Be more verbose about what is happening. -.IP "\fB\-c\fP, \fB\-\-chunk=\fP" -chunk size of kibibytes +.TP +.BR -b ", " --brief +Be less verbose. This is used with +.B --detail +and +.BR --examine . -.IP "\fB\-\-rounding=\fP" -rounding factor for linear array (==chunk size) +.SH For create or build: -.IP "\fB\-l\fP, \fB\-\-level=\fP" -raid level: 0,1,4,5,linear. 0 or linear for build +.TP +.BR -c ", " --chunk= +Specify chunk size of kibibytes. The default is 64. -.IP "\fB\-p\fP, \fB\-\-parity=\fP" -raid5 parity algorithm: {left,right}-{,a}symmetric +.TP +.BR --rounding= +Specify rounding factor for linear array (==chunk size) -.IP "\fB\-\-layout=\fP" -same as --parity +.TP +.BR -l ", " --level= +Set raid level. Options are: linear, raid0, 0, stripe, raid1, 1, mirror, raid5, 4, +raid5, 5. Obviously some of these are synonymous. +Only the first 4 are valid when Building. -.IP "\fB\-n\fP, \fB\-\-raid-disks=\fP" -number of active devices in array +.TP +.BR -p ", " --parity= +Set raid5 parity algorithm. Options are: +{left,right}-{,a}symmetric, la, ra, ls, rs. The default is left-symmetric. -.IP "\fB\-x\fP, \fB\-\-spare-disks=\fP" -number of spares (eXtras) to allow space for +.TP +.BR --layout= +same as --parity -.IP "\fB\-z\fP, \fB\-\-size=\fP" -Size (in K) of each drive in RAID1/4/5 - optional +.TP +.BR -n ", " --raid-disks= +number of active devices in array. + +.TP +.BR -x ", " --spare-disks= +number of spare (eXtra) disks in initial array. Spares can be added +and removed later. + +.TP +.BR -z ", " --size= +Amount (in Kibibytes) of space to use from each drive in RAID1/4/5. +This must be a multiple of the chunk size, and must leave about 128Kb +of space at the end of the drive for the RAID superblock. +If this is not specified +(as it normally is not) the smallest drive (or partition) sets the +size, though if there is a variance among the drives of greater than 1%, a warning is +issued. .SH For assemble: -.IP "\fB\-u\fP, \fB\-\-uuid=\fP" -uuid of array to assemble. Devices which don't have this uuid are excluded - -.IP "\fB\-c\fP, \fB\-\-config=\fP" -config file - -.IP "\fB\-s\fP, \fB\-\-scan\fP" +.TP +.BR -u ", " --uuid= +uuid of array to assemble. Devices which don't have this uuid are +excluded + +.TP +.BR -m ", " --super-minor= +Minor number of device that array was created for. Devices which +don't have this minor number are excluded. If you create an array as +/dev/md1, then all superblock will contain the minor number 1, even if +the array is later assembled as /dev/md2. + +.TP +.BR -c ", " --config= +config file. Default is +.BR /etc/mdctl.conf . + +.TP +.BR -s ", " --scan scan config file for missing information -.IP "\fB\-f\fP, \fB\-\-force\fP" +.TP +.BR -f ", " --force Assemble the array even if some superblocks appear out-of-date -.SH General management - -.IP "\fB\-a\fP, \fB\-\-add\fP" -add, or hotadd subsequent devices - -.IP "\fB\-r\fP, \fB\-\-remove\fP" -remove subsequent devices - -.IP "\fB\-f\fP, \fB\-\-fail\fP" -mark subsequent devices a faulty - -.IP "\fB\-\-set-faulty\fP" -same as --fail - -.IP "\fB\-R\fP, \fB\-\-run\fP" -start a partially built array +.TP +.BR -R ", " --run +Attempt to start the array even if fewer drives were given than are +needed for a full array. Normally if not all drives are found and +.B --scan +is not used, then the array will be assembled but not started. +With +.B --run +an attempt will be made to start it anyway. -.IP "\fB\-S\fP, \fB\-\-stop\fP" -deactivate array, releasing all resources +.SH General management -.IP "\fB\-o\fP, \fB\-\-readonly\fP" -mark array as readonly +.TP +.BR -a ", " --add +'''add, or +hotadd listed devices. -.IP "\fB\-w\fP, \fB\-\-readwrite\fP" -mark array as readwrite +.TP +.BR -r ", " --remove +remove listed devices. The must not be active. i.e. they should +be failed or spare devices. -.SH CREATE MODE +.TP +.BR -f ", " --fail +mark listed devices as faulty. -Usage: +.TP +.BR --set-faulty +same as --fail. -.B mdctl ---create device --chunk=X --level=Y --raid-disks=Z devices +.TP +.BR -R ", " --run +start a partially built array. -This usage will initialise a new md array and possibly associate some -devices with it. If enough devices are given to complete the array, the -array will be activated. Otherwise it will be left inactive to be completed -and activated by subsequent management commands. +.TP +.BR -S ", " --stop +deactivate array, releasing all resources. -As devices are added, they are checked to see if they contain raid -superblocks or filesystems. They are also check to see if the variance in -device size exceeds 1%. +.TP +.BR -o ", " --readonly +mark array as readonly. -If any discrepancy is found, the array will not automatically be run, though -the presence of a -.B --run -can override this caution. +.TP +.BR -w ", " --readwrite +mark array as readwrite. -If the -.B --size -option is given, it is not necessary to list any subdevices in this command. -They can be added later, before a -.B --run. -If no -.B --size -is given, the apparent size of the smallest drive given is used. - -The General management options that are valid with --create are: -.IP "\fB\-\-run\fP" -insist of running the array even if not all devices are present or some look -odd. - -.IP "\fB\-\-readonly\fP" -start the array readonly - not supported yet. .SH ASSEMBLY MODE -Usage: - -.B mdctl ---assemble device options... - -.B mdctl ---assemble --scan options... +.HP 12 +Usage: +.B mdctl --assemble +.I device options... +.HP 12 +Usage: +.B mdctl --assemble --scan +.I options... +.PP This usage assembles one or more raid arrays from pre-existing components. -For each array, mdctl needs to know the md device, the uuid, and a number of -sub devices. These can be found in a number of ways. +For each array, mdctl needs to know the md device, the identity of the +array, and a number of sub devices. These can be found in a number of ways. The md device is either given before .B --scan or is found from the config file. In the latter case, multiple md devices can be started with a single mdctl command. -The uuid can be given with the +The identity can be given with the .B --uuid -option, or can be found in in the config file, or will be taken from the -super block on the first subdevice listed on the command line or in a -subsequent -.B --add -command. +option, with the +.B --super-minor +option, can be found in in the config file, or will be taken from the +super block on the first subdevice listed on the command line. Devices can be given on the .B --assemble -command line, on subsequent -.B 'mdctl --add' -command lines, or from the config file. Only devices which have an md -superblock which contains the right uuid will be considered for any device. +command line or from the config file. Only devices which have an md +superblock which contains the right identity will be considered for any device. The config file is only used if explicitly named with .B --config or requested with .B --scan. In the later case, -.B /etc/md.conf +.B /etc/mdctl.conf is used. If .B --scan -is not given, then the config file will only be used to find uuids for md -arrays. - -The format of the config file is: - not yet documented +is not given, then the config file will only be used to find the +identity of md arrays. -.SH BUILD MDOE +Normally the array will be started after it is assembled. However is +.B --scan +is not given and insufficient drives were lists to start a complete +(non-degraded) array, then the array is not started (to guard against +usage errors). To insist that the array be started in this case (as +may work for RAID1 or RAID5), give the +.B --run +flag. -Usage: -.B mdctl ---build device -chunk=X --level=Y --raid-disks=Z devices +.SH BUILD MODE +.HP 12 +Usage: +.B mdctl --build +.I device +.BI --chunk= X +.BI --level= Y +.BI --raid-disks= Z +.I devices + +.PP This usage is similar to -.B --create. +.BR --create . The difference is that it creates a legacy array without a superblock. With -these arrays there is no different between initially creating the array and +these arrays there is no difference between initially creating the array and subsequently assembling the array, except that hopefully there is useful data there in the second case. -The level may only be 0 or linear. All devices must be listed and the array -will be started once complete. +The level may only be 0, raid0, or linear. All devices must be listed +and the array will be started once complete. + +.SH CREATE MODE + +.HP 12 +Usage: +.B mdctl --create +.I device +.BI --chunk= X +.BI --level= Y +.br +.BI --raid-disks= Z +.I devices + +.PP +This usage will initialise a new md array, associate some devices with +it, and activate the array. + +As devices are added, they are checked to see if they contain raid +superblocks or filesystems. They are also check to see if the variance in +device size exceeds 1%. + +If any discrepancy is found, the array will not automatically be run, though +the presence of a +.B --run +can override this caution. + +'''If the +'''.B --size +'''option is given, it is not necessary to list any subdevices in this command. +'''They can be added later, before a +'''.B --run. +'''If no +'''.B --size +'''is given, the apparent size of the smallest drive given is used. + +The General Management options that are valid with --create are: +.TP +.B --run +insist of running the array even if some devices look like they might +be in use. + +.TP +.B --readonly +start the array readonly - not supported yet. -.SH BUGS -no known bugs. +.SH DETAIL MODE +.HP 12 +Usage: +.B mdctl --detail +.RB [ --brief ] +.I device ... +.PP + +This usage sill print out the details of the given array including a +list of component devices. To determine names for the devices, +.B mdctl +searches +.B /dev +for device files with the right major and minor numbers. + +With +.B --brief +.B mdctl +prints a single line that identifies the level, number of disks, and +UUID of the array. This line is suitable for inclusion in +.BR /etc/mdctl.conf . + +.SH EXAMINE MODE +.HP 12 +Usage: +.B mdctl --examine +.RB [ --scan ] +.RB [ --brief ] +.I device ... +.PP +This usage will examine some block devices to see if that have a valid +RAID superblock on them. The information in each valid raid +superblock will be printed. + +If +.B --scan +is used, the no devices should be listed, and the complete set of +devices identified in the configuration file are checked. +.B --scan +implies +.B --brief +but this implication can be countered by specifying +.BR --verbose . + +With +.B --brief +.B mdctl +will output an config file entry of each distinct array that was +found. This entry will list the UUID, the raid level, and a list of +the individual devices on which a superblock for that array was found. +This output will by syntactically suitable for inclusion in the +configuration file, but should +.B NOT +be used blindly. Often the array description that you want in the +configuration file is much less specific than that given by +.BR "mdctl -Bs" . +For example, you normally do not want to list the devices, +particularly if they are SCSI devices. + +'''.SH BUGS +'''no known bugs. + +.SH FILES + +.SS /proc/mdstat + +If you're using the +.B /proc +filesystem, +.B /proc/mdstat +gives you informations about md devices status. +This file is not currently used by +.BR mdctl . + +.SS /etc/mdctl.conf + +The config file is line oriented with, as usual, blank lines and lines +beginning with a hash (or pound sign or sharp or number sign, +whichever you like to call it) ignored. +Lines that start with a blank are treated as continuations of the +previous line (I don't like trailing slashes). + +Each line contains a sequence of space-separated words, the first of +which identified the type of line. Keywords are case-insensitive, and +the first work on a line can be abbreviated to 3 letters. + +There are two types of lines. ARRAY and DEVICE. + +The DEVICE lines usually come first. All remaining words on the line +are treated as names of devices, possibly containing wild cards (see +.IR glob (7)). +These list all the devices that +.B mdctl +is allowed to scan +when looking for devices with RAID superblocks. +Each line can contain multiple device names, and there can be multiple +DEVICE lines. For example: +.IP +DEVICE /dev/hda* /dev/hdc* +.br +DEV /dev/sd* +.br +DEVICE /dev/discs/disc*/disc +.PP +The ARRAY lines identify actual arrays. The second word on the line +should be the name of the device where the array is normally +assembled, such as /dev/md1. +Subsequent words identify the array. If multiple identities are given, +then the array much match ALL identities to be considered a match. +Each identity word has a tag, and equals sign, and some value. +The options are: + +.TP +.B uuid= +The value should be a 128 bit uuid in hexadecimal, with punctuation +interspersed if desired. This must match the uuid stored in the +superblock. +.TP +.B super-minor= +The value is an integer which indicates the minor number that was +stored in the superblock when the array was created. When an array is +created as /dev/mdX, then the minor number X is stored. +.TP +.B devices= +The value is a comma separated list of device names. Precisely these +devices will be used to assemble the array. Note that the devices +listed there must also be listed on a DEVICE line. +.TP +.B level= +The value is a raid level. This is normally used to identify an +array, but is supported so that the output of +.B "mdctl --examine --scan" +can be use directly in the configuration file. +.TP +.B disks= +The value is the number of disks in a complete active array. As with +.B level= +this is mainly for compatibility with the output of +.BR "mdctl --examine --scan" . .SH TODO +Finish and document Follow mode. .SH SEE ALSO +For information on the various levels of +RAID, check out: + +.IP +.UR http://ostenfeld.dk/~jakob/Software-RAID.HOWTO/ +http://ostenfeld.dk/~jakob/Software-RAID.HOWTO/ +.UE +.PP +for new releases of the RAID driver check out: + +.IP +.UR ftp://ftp.kernel.org/pub/linux/kernel/people/mingo/raid-patches +ftp://ftp.kernel.org/pub/linux/kernel/people/mingo/raid-patches +.UE +.PP +or +.IP +.UR http://www.cse.unsw.edu.au/~neilb/patches/linux-stable/ +http://www.cse.unsw.edu.au/~neilb/patches/linux-stable/ +.URk +.PP .IR raidtab (5), .IR raid0run (8), .IR raidstop (8), @@ -1,7 +1,7 @@ /* * mdctl - manage Linux "md" devices aka RAID arrays. * - * Copyright (C) 2001 Neil Brown <neilb@cse.unsw.edu.au> + * Copyright (C) 2001-2002 Neil Brown <neilb@cse.unsw.edu.au> * * * This program is free software; you can redistribute it and/or modify @@ -64,14 +64,17 @@ int main(int argc, char *argv[]) int sparedisks = 0; struct mddev_ident_s ident; char *configfile = NULL; + char *cp; int scan = 0; char devmode = 0; int runstop = 0; int readonly = 0; - char *devs[MD_SB_DISKS+1]; - int devmodes[MD_SB_DISKS+1]; + mddev_dev_t devlist = NULL; + mddev_dev_t *devlistend = & devlist; + mddev_dev_t dv; int devs_found = 0; int verbose = 0; + int brief = 0; int force = 0; char *mailaddr = NULL; @@ -81,6 +84,8 @@ int main(int argc, char *argv[]) int mdfd = -1; ident.uuid_set=0; + ident.level = -10; + ident.raid_disks = -1; ident.super_minor= -1; ident.devices=0; @@ -124,19 +129,36 @@ int main(int argc, char *argv[]) case 'v': verbose = 1; continue; + case 'b': brief = 1; + continue; + case 1: /* an undecorated option - must be a device name. * Depending on mode, it could be that: - * All devices listed are "md" devices : --Detail, -As - * No devices are "md" devices : --Examine - * First device is "md", others are component: -A,-B,-C + * All devices listed are "md" devices : --Detail, -As + * No devices are "md" devices : --Examine + * First device is "md", others are component: -A,-B,-C + * Only accept on device before mode is determined. + * If mode is @, then require devmode for other devices. */ - if (devs_found >= MD_SB_DISKS+1) { - fprintf(stderr, Name ": too many devices at %s - current limit -s %d\n", - optarg, MD_SB_DISKS+1); + if (devs_found > 0 && !mode ) { + fprintf(stderr, Name ": Must give mode flag before second device name at %s\n", optarg); + exit(2); + } + if (devs_found > 0 && mode == '@' && !devmode) { + fprintf(stderr, Name ": Must give on of -a/-r/-f for subsequent devices at %s\n", optarg); exit(2); } - devs[devs_found] = optarg; - devmodes[devs_found] = devmode; + dv = malloc(sizeof(*dv)); + if (dv == NULL) { + fprintf(stderr, Name ": malloc failed\n"); + exit(3); + } + dv->devname = optarg; + dv->disposition = devmode; + dv->next = NULL; + *devlistend = dv; + devlistend = &dv->next; + devs_found++; continue; @@ -205,6 +227,7 @@ int main(int argc, char *argv[]) optarg); exit(2); } + ident.level = level; continue; case O('C','p'): /* raid5 layout */ @@ -246,6 +269,7 @@ int main(int argc, char *argv[]) optarg); exit(2); } + ident.raid_disks = raiddisks; continue; case O('C','x'): /* number of spare (eXtra) discs */ @@ -276,7 +300,7 @@ int main(int argc, char *argv[]) continue; case O('A','u'): /* uuid of array */ if (ident.uuid_set) { - fprintf(stderr, Name ": uuid cannot bet set twice. " + fprintf(stderr, Name ": uuid cannot be set twice. " "Second value %s.\n", optarg); exit(2); } @@ -288,6 +312,19 @@ int main(int argc, char *argv[]) } continue; + case O('A','m'): /* super-minor for array */ + if (ident.super_minor >= 0) { + fprintf(stderr, Name ": super-minor cannot be set twice. " + "Second value: %s.\n", optarg); + exit(2); + } + ident.super_minor = strtoul(optarg, &cp, 10); + if (!optarg[0] || *cp) { + fprintf(stderr, Name ": Bad super-minor number: %s.\n", optarg); + exit(2); + } + continue; + case O('A','c'): /* config file */ case O('F','c'): if (configfile) { @@ -299,6 +336,7 @@ int main(int argc, char *argv[]) /* FIXME possibly check that config file exists. Even parse it */ continue; case O('A','s'): /* scan */ + case O('E','s'): scan = 1; continue; @@ -409,7 +447,7 @@ int main(int argc, char *argv[]) fprintf(stderr, Name ": an md device must be given in this mode\n"); exit(2); } - mdfd = open_mddev(devs[0]); + mdfd = open_mddev(devlist->devname); if (mdfd < 0) exit(1); } @@ -420,36 +458,36 @@ int main(int argc, char *argv[]) case '@':/* Management */ /* readonly, add/remove, readwrite, runstop */ if (readonly>0) - rv = Manage_ro(devs[0], mdfd, readonly); + rv = Manage_ro(devlist->devname, mdfd, readonly); if (!rv && devs_found>1) - rv = Manage_subdevs(devs[0], mdfd, - devs_found-1, devs+1, devmodes+1); + rv = Manage_subdevs(devlist->devname, mdfd, + devlist->next); if (!rv && readonly < 0) - rv = Manage_ro(devs[0], mdfd, readonly); + rv = Manage_ro(devlist->devname, mdfd, readonly); if (!rv && runstop) - rv = Manage_runstop(devs[0], mdfd, runstop); + rv = Manage_runstop(devlist->devname, mdfd, runstop); break; case 'A': /* Assemble */ if (!scan) - rv = Assemble(devs[0], mdfd, &ident, configfile, - devs_found-1, devs+1, + rv = Assemble(devlist->devname, mdfd, &ident, configfile, + devlist->next, readonly, runstop, verbose, force); else if (devs_found>0) - for (i=0; i<devs_found; i++) { - mddev_ident_t array_ident = conf_get_ident(configfile, devs[i]); - mdfd = open_mddev(devs[i]); + for (dv = devlist ; dv ; dv=dv->next) { + mddev_ident_t array_ident = conf_get_ident(configfile, dv->devname); + mdfd = open_mddev(dv->devname); if (mdfd < 0) { rv |= 1; continue; } if (array_ident == NULL) { fprintf(stderr, Name ": %s not identified in config file.\n", - devs[i]); + dv->devname); rv |= 1; continue; } - rv |= Assemble(devs[i], mdfd, array_ident, configfile, - 0, NULL, + rv |= Assemble(dv->devname, mdfd, array_ident, configfile, + NULL, readonly, runstop, verbose, force); } else { @@ -459,36 +497,43 @@ int main(int argc, char *argv[]) rv = 1; } else for (; array_list; array_list = array_list->next) { + mdu_array_info_t array; mdfd = open_mddev(array_list->devname); if (mdfd < 0) { rv |= 1; continue; } + if (ioctl(mdfd, GET_ARRAY_INFO, &array)>=0) + /* already assembled, skip */ + continue; rv |= Assemble(array_list->devname, mdfd, array_list, configfile, - 0, NULL, + NULL, readonly, runstop, verbose, force); } } break; case 'B': /* Build */ - rv = Build(devs[0], mdfd, chunk, level, raiddisks, devs_found-1,devs+1); + rv = Build(devlist->devname, mdfd, chunk, level, raiddisks, devlist->next); break; case 'C': /* Create */ - rv = Create(devs[0], mdfd, chunk, level, layout, size, + rv = Create(devlist->devname, mdfd, chunk, level, layout, size, raiddisks, sparedisks, - devs_found-1,devs+1, runstop, verbose, force); + devs_found-1, devlist->next, runstop, verbose, force); break; case 'D': /* Detail */ - for (i=0; i<devs_found; i++) - rv |= Detail(devs[i]); + for (dv=devlist ; dv; dv=dv->next) + rv |= Detail(dv->devname, brief); break; case 'E': /* Examine */ - for (i=0; i<devs_found; i++) - rv |= Examine(devs[i]); + if (devlist == NULL && scan==0) { + fprintf(stderr, Name ": No devices to examine\n"); + exit(2); + } + rv = Examine(devlist, devlist?brief:!verbose, configfile); break; case 'F': /* Follow */ - rv= Monitor(devs_found, devs, mailaddr, program, + rv= Monitor(devlist, mailaddr, program, delay?delay:60, configfile); } exit(rv); @@ -1,7 +1,7 @@ /* * mdctl - manage Linux "md" devices aka RAID arrays. * - * Copyright (C) 2001 Neil Brown <neilb@cse.unsw.edu.au> + * Copyright (C) 2001-2002 Neil Brown <neilb@cse.unsw.edu.au> * * * This program is free software; you can redistribute it and/or modify @@ -76,7 +76,8 @@ typedef struct mddev_ident_s { char *devices; /* comma separated list of device * names with wild cards */ - + int level; /* -10 if not set */ + int raid_disks; /* -1 if not set */ char *spare_group; struct mddev_ident_s *next; } *mddev_ident_t; @@ -84,6 +85,9 @@ typedef struct mddev_ident_s { /* List of device names - wildcards expanded */ typedef struct mddev_dev_s { char *devname; + char disposition; /* 'a' for add, 'r' for remove, 'f' for fail. + * Not set for names read from .config + */ struct mddev_dev_s *next; } *mddev_dev_t; @@ -106,29 +110,29 @@ extern char *map_dev(int major, int minor); extern int Manage_ro(char *devname, int fd, int readonly); extern int Manage_runstop(char *devname, int fd, int runstop); extern int Manage_subdevs(char *devname, int fd, - int devcnt, char *devnames[], int devmodes[]); + mddev_dev_t devlist); extern int Assemble(char *mddev, int mdfd, mddev_ident_t ident, char *conffile, - int subdevs, char *subdev[], + mddev_dev_t devlist, int readonly, int runstop, int verbose, int force); extern int Build(char *mddev, int mdfd, int chunk, int level, int raiddisks, - int subdevs, char *subdev[]); + mddev_dev_t devlist); extern int Create(char *mddev, int mdfd, int chunk, int level, int layout, int size, int raiddisks, int sparedisks, - int subdevs, char *subdev[], + int subdevs, mddev_dev_t devlist, int runstop, int verbose, int force); -extern int Detail(char *dev); -extern int Examine(char *dev); -extern int Monitor(int num_devs, char *devlist[], +extern int Detail(char *dev, int brief); +extern int Examine(mddev_dev_t devlist, int brief, char *conffile); +extern int Monitor(mddev_dev_t devlist, char *mailaddr, char *alert_cmd, int period, char *config); @@ -142,3 +146,5 @@ extern int check_raid(int fd, char *name); extern mddev_ident_t conf_get_ident(char *, char*); extern mddev_dev_t conf_get_devs(char *); + +extern char *human_size(long kbytes); diff --git a/mdctl.man b/mdctl.man new file mode 100644 index 0000000..682bc82 --- /dev/null +++ b/mdctl.man @@ -0,0 +1,476 @@ +mdctl(8) mdctl(8) + + + +NNAAMMEE + mdctl - manage MD devices _a_k_a Linux Software Raid. + + +SSYYNNOOPPSSIISS + mmddccttll _[_m_o_d_e_] _<_r_a_i_d_d_e_v_i_c_e_> _[_o_p_t_i_o_n_s_] _<_s_u_b_d_e_v_i_c_e_s_> + + +DDEESSCCRRIIPPTTIIOONN + RAID devices are virtual devices created from two or more + real block devices. This allows multiple devices (typi- + cally disk drives or partitions there-of) to be combined + into a single device to hold (for example) a single + filesystem. Some RAID levels included redundancy and so + can survive some degree of device failure. + + Linux Software RAID devices are implemented through the md + (Multiple Devices) device driver. + + Currently, Linux supports LLIINNEEAARR md devices, RRAAIIDD00 (strip- + ing), RRAAIIDD11 (mirroring), RRAAIIDD44 and RRAAIIDD55.. + + Recent kernels (2002) also support a mode known as MMUULLTTII-- + PPAATTHH. mmddccttll does not support MULTIPATH as yet. + + mmddccttll is a program that can be used to create and manage + MD devices. As such it provides a similar set of func- + tionality to the rraaiiddttoooollss packages. The key differences + between mmddccttll and rraaiiddttoooollss are: + + +o mmddccttll is a single program and not a collection of pro- + grams. + + +o mmddccttll can perform (almost) all of its functions with- + out having a configuration file. Also mdctl helps + with management of the configuration file. + + +o mmddccttll can provide information about your arrays + (through Detail and Examine) that rraaiiddttoooollss cannot. + + +o rraaiiddttoooollss can manage MULTIPATH devices which mmddccttll + cannot yet manage. + + +MMOODDEESS + mdctl has 7 major modes of operation: + + AAsssseemmbbllee + Assemble the parts of a previously created array + into an active array. Components can be explicitly + given or can be searched for. mmddccttll checks that + the components do form a bona fide array, and can, + on request, fiddle superblock information so as to + assemble a faulty array. + + + BBuuiilldd Build a legacy array without per-device + superblocks. + + + CCrreeaattee Create a new array with per-device superblocks. + + + DDeettaaiill Display the details of a given md device. Details + include the RAID level, the number of devices, + which ones are faulty (if any), and the array UUID. + + + EExxaammiinnee + Examine a device to see if it is part of an md + array, and print out the details of that array. + This mode can also be used to examine a large num- + ber of devices and to print out a summary of the + arrays found in a format suitable for the + mmddccttll..ccoonnff configuration file. + + + FFoollllooww oorr MMoonniittoorr + Monitor one or more md devices and act on any state + changes. + + + MMaannaaggee This is for odd bits an pieces like hotadd, + hotremove, setfaulty, stop, readonly, readwrite. + + +OOPPTTIIOONNSS + Available options are: + + + --AA, ----aasssseemmbbllee + Assemble an existing array. + + + --BB, ----bbuuiilldd + Build a legacy array without superblocks. + + + --CC, ----ccrreeaattee + Create a new array. + + + --DD, ----ddeettaaiill + Print detail of one or more md devices. + + + --EE, ----eexxaammiinnee + Print content of md superblock on device(s). + + + --FF, ----ffoollllooww, ----mmoonniittoorr + Select MMoonniittoorr mode. + + + --hh, ----hheellpp + Display help message or, after above option, mode + specific help message. + + + --VV, ----vveerrssiioonn + Print version information for mdctl. + + + --vv, ----vveerrbboossee + Be more verbose about what is happening. + + + --bb, ----bbrriieeff + Be less verbose. This is used with ----ddeettaaiill and + ----eexxaammiinnee. + + +FFoorr ccrreeaattee oorr bbuuiilldd:: + --cc, ----cchhuunnkk== + Specify chunk size of kibibytes. The default is + 64. + + + ----rroouunnddiinngg== + Specify rounding factor for linear array (==chunk + size) + + + --ll, ----lleevveell== + Set raid level. Options are: linear, raid0, 0, + stripe, raid1, 1, mirror, raid5, 4, raid5, 5. + Obviously some of these are synonymous. Only the + first 4 are valid when Building. + + + --pp, ----ppaarriittyy== + Set raid5 parity algorithm. Options are: + {left,right}-{,a}symmetric, la, ra, ls, rs. The + default is left-symmetric. + + + ----llaayyoouutt== + same as --parity + + + --nn, ----rraaiidd--ddiisskkss== + number of active devices in array. + + + --xx, ----ssppaarree--ddiisskkss== + number of spare (eXtra) disks in initial array. + Spares can be added and removed later. + + + --zz, ----ssiizzee== + Amount (in Kibibytes) of space to use from each + drive in RAID1/4/5. This must be a multiple of the + chunk size, and must leave about 128Kb of space at + the end of the drive for the RAID superblock. If + this is not specified (as it normally is not) the + smallest drive (or partition) sets the size, though + if there is a variance among the drives of greater + than 1%, a warning is issued. + + +FFoorr aasssseemmbbllee:: + --uu, ----uuuuiidd== + uuid of array to assemble. Devices which don't have + this uuid are excluded + + + --mm, ----ssuuppeerr--mmiinnoorr== + Minor number of device that array was created for. + Devices which don't have this minor number are + excluded. If you create an array as /dev/md1, then + all superblock will contain the minor number 1, + even if the array is later assembled as /dev/md2. + + + --cc, ----ccoonnffiigg== + config file. Default is //eettcc//mmddccttll..ccoonnff. + + + --ss, ----ssccaann + scan config file for missing information + + + --ff, ----ffoorrccee + Assemble the array even if some superblocks appear + out-of-date + + + --RR, ----rruunn + Attempt to start the array even if fewer drives + were given than are needed for a full array. Nor- + mally if not all drives are found and ----ssccaann is not + used, then the array will be assembled but not + started. With ----rruunn an attempt will be made to + start it anyway. + + +GGeenneerraall mmaannaaggeemmeenntt + --aa, ----aadddd + hotadd listed devices. + + + --rr, ----rreemmoovvee + remove listed devices. The must not be active. + i.e. they should be failed or spare devices. + + + --ff, ----ffaaiill + mark listed devices as faulty. + + + ----sseett--ffaauullttyy + same as --fail. + + + --RR, ----rruunn + start a partially built array. + + + --SS, ----ssttoopp + deactivate array, releasing all resources. + + + --oo, ----rreeaaddoonnllyy + mark array as readonly. + + + --ww, ----rreeaaddwwrriittee + mark array as readwrite. + + + +AASSSSEEMMBBLLYY MMOODDEE + Usage: mmddccttll ----aasssseemmbbllee _d_e_v_i_c_e _o_p_t_i_o_n_s_._._. + + Usage: mmddccttll ----aasssseemmbbllee ----ssccaann _o_p_t_i_o_n_s_._._. + + + This usage assembles one or more raid arrays from pre- + existing components. For each array, mdctl needs to know + the md device, the identity of the array, and a number of + sub devices. These can be found in a number of ways. + + The md device is either given before ----ssccaann or is found + from the config file. In the latter case, multiple md + devices can be started with a single mdctl command. + + The identity can be given with the ----uuuuiidd option, with the + ----ssuuppeerr--mmiinnoorr option, can be found in in the config file, + or will be taken from the super block on the first subde- + vice listed on the command line. + + Devices can be given on the ----aasssseemmbbllee command line or + from the config file. Only devices which have an md + superblock which contains the right identity will be con- + sidered for any device. + + The config file is only used if explicitly named with + ----ccoonnffiigg or requested with ----ssccaann.. In the later case, + //eettcc//mmddccttll..ccoonnff is used. + + If ----ssccaann is not given, then the config file will only be + used to find the identity of md arrays. + + Normally the array will be started after it is assembled. + However is ----ssccaann is not given and insufficient drives + were lists to start a complete (non-degraded) array, then + the array is not started (to guard against usage errors). + To insist that the array be started in this case (as may + work for RAID1 or RAID5), give the ----rruunn flag. + + + +BBUUIILLDD MMOODDEE + Usage: mmddccttll ----bbuuiilldd _d_e_v_i_c_e ----cchhuunnkk==_X ----lleevveell==_Y ----rraaiidd-- + ddiisskkss==_Z _d_e_v_i_c_e_s + + + This usage is similar to ----ccrreeaattee. The difference is that + it creates a legacy array without a superblock. With these + arrays there is no difference between initially creating + the array and subsequently assembling the array, except + that hopefully there is useful data there in the second + case. + + The level may only be 0, raid0, or linear. All devices + must be listed and the array will be started once com- + plete. + + +CCRREEAATTEE MMOODDEE + Usage: mmddccttll ----ccrreeaattee _d_e_v_i_c_e ----cchhuunnkk==_X ----lleevveell==_Y + ----rraaiidd--ddiisskkss==_Z _d_e_v_i_c_e_s + + + This usage will initialise a new md array, associate some + devices with it, and activate the array. + + As devices are added, they are checked to see if they con- + tain raid superblocks or filesystems. They are also check + to see if the variance in device size exceeds 1%. + + If any discrepancy is found, the array will not automati- + cally be run, though the presence of a ----rruunn can override + this caution. + + + The General Management options that are valid with --cre- + ate are: + + ----rruunn insist of running the array even if some devices + look like they might be in use. + + + ----rreeaaddoonnllyy + start the array readonly - not supported yet. + + +DDEETTAAIILL MMOODDEE + Usage: mmddccttll ----ddeettaaiill [----bbrriieeff] _d_e_v_i_c_e _._._. + + + This usage sill print out the details of the given array + including a list of component devices. To determine names + for the devices, mmddccttll searches //ddeevv for device files with + the right major and minor numbers. + + With ----bbrriieeff mmddccttll prints a single line that identifies + the level, number of disks, and UUID of the array. This + line is suitable for inclusion in //eettcc//mmddccttll..ccoonnff. + + +EEXXAAMMIINNEE MMOODDEE + Usage: mmddccttll ----eexxaammiinnee [----ssccaann] [----bbrriieeff] _d_e_v_i_c_e _._._. + + This usage will examine some block devices to see if that + have a valid RAID superblock on them. The information in + each valid raid superblock will be printed. + + If ----ssccaann is used, the no devices should be listed, and + the complete set of devices identified in the configura- + tion file are checked. ----ssccaann implies ----bbrriieeff but this + implication can be countered by specifying ----vveerrbboossee. + + With ----bbrriieeff mmddccttll will output an config file entry of + each distinct array that was found. This entry will list + the UUID, the raid level, and a list of the individual + devices on which a superblock for that array was found. + This output will by syntactically suitable for inclusion + in the configuration file, but should NNOOTT be used blindly. + Often the array description that you want in the configu- + ration file is much less specific than that given by mmddccttll + --BBss. For example, you normally do not want to list the + devices, particularly if they are SCSI devices. + + + +FFIILLEESS + //pprroocc//mmddssttaatt + If you're using the //pprroocc filesystem, //pprroocc//mmddssttaatt gives + you informations about md devices status. This file is + not currently used by mmddccttll. + + + //eettcc//mmddccttll..ccoonnff + The config file is line oriented with, as usual, blank + lines and lines beginning with a hash (or pound sign or + sharp or number sign, whichever you like to call it) + ignored. Lines that start with a blank are treated as + continuations of the previous line (I don't like trailing + slashes). + + Each line contains a sequence of space-separated words, + the first of which identified the type of line. Keywords + are case-insensitive, and the first work on a line can be + abbreviated to 3 letters. + + There are two types of lines. ARRAY and DEVICE. + + The DEVICE lines usually come first. All remaining words + on the line are treated as names of devices, possibly con- + taining wild cards (see _g_l_o_b(7)). These list all the + devices that mmddccttll is allowed to scan when looking for + devices with RAID superblocks. Each line can contain mul- + tiple device names, and there can be multiple DEVICE + lines. For example: + + DEVICE /dev/hda* /dev/hdc* + DEV /dev/sd* + DEVICE /dev/discs/disc*/disc + + The ARRAY lines identify actual arrays. The second word + on the line should be the name of the device where the + array is normally assembled, such as /dev/md1. Subsequent + words identify the array. If multiple identities are + given, then the array much match ALL identities to be con- + sidered a match. Each identity word has a tag, and equals + sign, and some value. The options are: + + + uuuuiidd== The value should be a 128 bit uuid in hexadecimal, + with punctuation interspersed if desired. This + must match the uuid stored in the superblock. + + ssuuppeerr--mmiinnoorr== + The value is an integer which indicates the minor + number that was stored in the superblock when the + array was created. When an array is created as + /dev/mdX, then the minor number X is stored. + + ddeevviicceess== + The value is a comma separated list of device + names. Precisely these devices will be used to + assemble the array. Note that the devices listed + there must also be listed on a DEVICE line. + + lleevveell== The value is a raid level. This is normally used + to identify an array, but is supported so that the + output of mmddccttll ----eexxaammiinnee ----ssccaann can be use + directly in the configuration file. + + ddiisskkss== The value is the number of disks in a complete + active array. As with lleevveell== this is mainly for + compatibility with the output of mmddccttll ----eexxaammiinnee + ----ssccaann. + + +TTOODDOO + Finish and document Follow mode. + + +SSEEEE AALLSSOO + For information on the various levels of RAID, check out: + + + http://ostenfeld.dk/~jakob/Software-RAID.HOWTO/ + + for new releases of the RAID driver check out: + + + ftp://ftp.kernel.org/pub/linux/kernel/peo- + ple/mingo/raid-patches + + or + + http://www.cse.unsw.edu.au/~neilb/patches/linux- + stable/ + + _r_a_i_d_t_a_b(5), _r_a_i_d_0_r_u_n(8), _r_a_i_d_s_t_o_p(8), _m_k_r_a_i_d(8) + + + + mdctl(8) @@ -1,7 +1,7 @@ /* * mdctl - manage Linux "md" devices aka RAID arrays. * - * Copyright (C) 2001 Neil Brown <neilb@cse.unsw.edu.au> + * Copyright (C) 2001-2002 Neil Brown <neilb@cse.unsw.edu.au> * * * This program is free software; you can redistribute it and/or modify @@ -424,3 +424,16 @@ int calc_sb_csum(mdp_super_t *super) super->sb_csum = oldcsum; return csum; } + +char *human_size(long kbytes) +{ + static char buf[30]; + + if (kbytes < 2000) + buf[0]=0; + else if (kbytes < 2*1024*1024) + sprintf(buf, " (%d MiB)", kbytes>>10); + else + sprintf(buf, " (%d GiB)", kbytes>>20); + return buf; +} |