mdctl-0.6

This commit is contained in:
Neil Brown 2002-03-06 23:17:40 +00:00
parent 5282684628
commit cd29a5c835
19 changed files with 2023 additions and 530 deletions

View File

@ -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)) {

66
Build.c
View File

@ -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;
int subdevs = 0;
mddev_dev_t dv;
/* scan all devices, make sure they really are block devices */
for (dv = devlist; dv; dv=dv->next) {
if (stat(dv->devname, &stb)) {
fprintf(stderr, Name ": Cannot find %s: %s\n",
dv->devname, strerror(errno));
return 1;
}
if ((stb.st_mode & S_IFMT) != S_IFBLK) {
fprintf(stderr, Name ": %s is not a block device.\n",
dv->devname);
return 1;
}
subdevs++;
}
if (raiddisks != subdevs) {
fprintf(stderr, Name ": requested %d devices in array but listed %d\n",
raiddisks, subdevs);
return 1;
}
/* scan all devices, make sure they really are block devices */
for (i=0; i<subdevs; i++) {
if (stat(subdev[i], &stb)) {
fprintf(stderr, Name ": Cannot find %s: %s\n",
subdev[i], strerror(errno));
return 1;
}
if ((stb.st_mode & S_IFMT) != S_IFBLK) {
fprintf(stderr, Name ": %s is not a block device.\n",
subdev[i]);
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, &param)) {
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);

340
COPYING Normal file
View File

@ -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.

148
ChangeLog Normal file
View File

@ -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.

View File

@ -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;
}
}

149
Detail.c
View File

@ -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 ((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(" %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");
}
printf("\n");
if ((dv=map_dev(disk.major, disk.minor))) {
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;
}
}
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;
}

273
Examine.c
View File

@ -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-");
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");
}
}
}
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;
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");
}
}
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");
}
return 0;
return rv;
}

View File

@ -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

View File

@ -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;
}
}

View File

@ -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) {

View File

@ -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},

38
TODO
View File

@ -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
spare added

View File

@ -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);

View File

@ -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

674
mdctl.8
View File

@ -1,185 +1,379 @@
.\" -*- 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.
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.
If you're using the
.B /proc
filesystem,
.B /proc/mdstat
gives you informations about md devices status.
Linux Software RAID devices are implemented through the md (Multiple Devices) device driver.
Currently, Linux supports linear md devices, RAID0 (striping), RAID1
(mirrroring), RAID4 and RAID5. For information on the various levels of
RAID, check out:
Currently, Linux supports
.B LINEAR
md devices,
.B RAID0
(striping),
.B RAID1
(mirroring),
.B RAID4
and
.B RAID5.
http://ostenfeld.dk/~jakob/Software-RAID.HOWTO/
Recent kernels (2002) also support a mode known as
.BR MULTIPATH .
.B mdctl
does not support MULTIPATH as yet.
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.
.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.
checks that the components
do form a bona fide array, and can, on request, fiddle superblock
information so as to assemble a faulty array.
.IP "\fBBuild\fP"
This is for building legacy arrays without superblocks.
.TP
.B Build
Build a legacy array without per-device superblocks.
.IP "\fBManage\fP"
.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.
.TP
.BR -v ", " --verbose
Be more verbose about what is happening.
.TP
.BR -b ", " --brief
Be less verbose. This is used with
.B --detail
and
.BR --examine .
.SH For create or build:
.IP "\fB\-c\fP, \fB\-\-chunk=\fP"
chunk size of kibibytes
.TP
.BR -c ", " --chunk=
Specify chunk size of kibibytes. The default is 64.
.IP "\fB\-\-rounding=\fP"
rounding factor for linear array (==chunk size)
.TP
.BR --rounding=
Specify rounding factor for linear array (==chunk size)
.IP "\fB\-l\fP, \fB\-\-level=\fP"
raid level: 0,1,4,5,linear. 0 or linear for build
.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\-p\fP, \fB\-\-parity=\fP"
raid5 parity algorithm: {left,right}-{,a}symmetric
.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\-\-layout=\fP"
.TP
.BR --layout=
same as --parity
.IP "\fB\-n\fP, \fB\-\-raid-disks=\fP"
number of active devices in array
.TP
.BR -n ", " --raid-disks=
number of active devices in array.
.IP "\fB\-x\fP, \fB\-\-spare-disks=\fP"
number of spares (eXtras) to allow space for
.TP
.BR -x ", " --spare-disks=
number of spare (eXtra) disks in initial array. Spares can be added
and removed later.
.IP "\fB\-z\fP, \fB\-\-size=\fP"
Size (in K) of each drive in RAID1/4/5 - optional
.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
.TP
.BR -u ", " --uuid=
uuid of array to assemble. Devices which don't have this uuid are
excluded
.IP "\fB\-c\fP, \fB\-\-config=\fP"
config file
.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.
.IP "\fB\-s\fP, \fB\-\-scan\fP"
.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
.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.
.SH General management
.IP "\fB\-a\fP, \fB\-\-add\fP"
add, or hotadd subsequent devices
.TP
.BR -a ", " --add
'''add, or
hotadd listed devices.
.IP "\fB\-r\fP, \fB\-\-remove\fP"
remove subsequent devices
.TP
.BR -r ", " --remove
remove listed devices. The must not be active. i.e. they should
be failed or spare devices.
.IP "\fB\-f\fP, \fB\-\-fail\fP"
mark subsequent devices a faulty
.TP
.BR -f ", " --fail
mark listed devices as faulty.
.IP "\fB\-\-set-faulty\fP"
same as --fail
.TP
.BR --set-faulty
same as --fail.
.IP "\fB\-R\fP, \fB\-\-run\fP"
start a partially built array
.TP
.BR -R ", " --run
start a partially built array.
.IP "\fB\-S\fP, \fB\-\-stop\fP"
deactivate array, releasing all resources
.TP
.BR -S ", " --stop
deactivate array, releasing all resources.
.IP "\fB\-o\fP, \fB\-\-readonly\fP"
mark array as readonly
.TP
.BR -o ", " --readonly
mark array as readonly.
.IP "\fB\-w\fP, \fB\-\-readwrite\fP"
mark array as readwrite
.TP
.BR -w ", " --readwrite
mark array as readwrite.
.SH ASSEMBLY MODE
.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 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 identity can be given with the
.B --uuid
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 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/mdctl.conf
is used.
If
.B --scan
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
.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.
.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
.BR --create .
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 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
.B mdctl
--create device --chunk=X --level=Y --raid-disks=Z devices
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.
.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
@ -190,97 +384,191 @@ 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.
'''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.
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.
.IP "\fB\-\-readonly\fP"
.TP
.B --readonly
start the array readonly - not supported yet.
.SH ASSEMBLY MODE
Usage:
.B mdctl
--assemble device options...
.B mdctl
--assemble --scan options...
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.
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
.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.
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.
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
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
.SH BUILD MDOE
.SH DETAIL MODE
.HP 12
Usage:
.B mdctl --detail
.RB [ --brief ]
.I device ...
.PP
.B mdctl
--build device -chunk=X --level=Y --raid-disks=Z devices
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.
This usage is similar to
.B --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
subsequently assembling the array, except that hopefully there is useful
data there in the second case.
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 .
The level may only be 0 or linear. All devices must be listed and the array
will be started once complete.
.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.
.SH BUGS
no known bugs.
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),

115
mdctl.c
View File

@ -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);
}
devs[devs_found] = optarg;
devmodes[devs_found] = devmode;
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);
}
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);

24
mdctl.h
View File

@ -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);

476
mdctl.man Normal file
View File

@ -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)

15
util.c
View File

@ -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;
}