Make CCD be able to read and write Linux software raids.

Supported for raid-0 with <n> disks, raid-1 with 2 disks.

Manpages have examples, warnings etc.

Test scripts on
http://www.cons.org/cracauer/ccdconfig-linux/
Reviewed by:	alfred
This commit is contained in:
Martin Cracauer 2006-04-13 20:35:31 +00:00
parent 447be3f1e4
commit 3f4f4a1465
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=157740
4 changed files with 78 additions and 2 deletions

View File

@ -104,6 +104,8 @@ The flags are as follows:
.Bd -literal -offset indent
CCDF_UNIFORM 0x02 Use uniform interleave
CCDF_MIRROR 0x04 Support mirroring
CCDF_NO_OFFSET 0x08 Do not use an offset
CCDF_LINUX 0x0A Linux md(4) compatibility
.Ed
.Pp
The format in the
@ -127,6 +129,10 @@ The component devices need to name partitions of type
.Dq 4.2BSD
as shown by
.Xr disklabel 8 ) .
.Pp
If you want to use the Linux md(4) compatibility mode, please be sure
to read the notes in
.Xr ccd 4 .
.Sh FILES
.Bl -tag -width /etc/ccd.conf -compact
.It Pa /etc/ccd.conf
@ -159,6 +165,16 @@ and assigned to ccd0.
# ccdconfig ccd0 128 CCDF_MIRROR /dev/da8s2 /dev/da9s3
.Ed
.Pp
The following are matching commands in Linux and FreeBSD to create a
raid-0 in Linux and read it from FreeBSD.
.Bd -literal
# Create a raid-0 on Linux:
mdadm --create --chunk=32 --level=0 --raid-devices=2 /dev/md0 \\
/dev/hda1 /dev/hdb1
# Make the raid-0 just created available on FreeBSD:
ccdconfig -c /dev/ccd0 32 linux /dev/ad0s1 /dev/ad0s2
.Ed
.Pp
When you create a new ccd disk you generally want to
.Xr fdisk 8
and

View File

@ -50,6 +50,8 @@ __FBSDID("$FreeBSD$");
#define CCDF_UNIFORM 0x02 /* use LCCD of sizes for uniform interleave */
#define CCDF_MIRROR 0x04 /* use mirroring */
#define CCDF_NO_OFFSET 0x08 /* do not leave space in front */
#define CCDF_LINUX 0x10 /* use Linux compatibility mode */
#include "pathnames.h"
@ -65,6 +67,10 @@ struct flagval {
{ "uniform", CCDF_UNIFORM },
{ "CCDF_MIRROR", CCDF_MIRROR },
{ "mirror", CCDF_MIRROR },
{ "CCDF_NO_OFFSET", CCDF_NO_OFFSET },
{ "no_offset", CCDF_NO_OFFSET },
{ "CCDF_LINUX", CCDF_LINUX },
{ "linux", CCDF_LINUX },
{ "none", 0 },
{ NULL, 0 },
};
@ -245,6 +251,10 @@ do_single(int argc, char **argv, int action)
gctl_ro_param(grq, "uniform", -1, "");
if (flags & CCDF_MIRROR)
gctl_ro_param(grq, "mirror", -1, "");
if (flags & CCDF_NO_OFFSET)
gctl_ro_param(grq, "no_offset", -1, "");
if (flags & CCDF_LINUX)
gctl_ro_param(grq, "linux", -1, "");
gctl_ro_param(grq, "nprovider", sizeof(argc), &argc);
for (i = 0; i < argc; i++) {
sprintf(buf1, "provider%d", i);

View File

@ -173,6 +173,34 @@ You cannot replace a disk in a mirrored
.Nm
partition without first backing up the partition, then replacing the disk,
then restoring the partition.
.Ss Linux compatibility
The Linux compatibility mode does not try to read the label that Linux'
md(4) driver leaves on the raw devices. You will have to give the order
of devices and the interleave factor on your own. When in Linux
compatibility mode, ccd will convert the interleave factor from Linux
terminology. That means you give the same interleave factor that you
gave as chunk size in Linux.
.Pp
If you have a Linux md(4) device in "legacy" mode, do not use the
CCD_LINUX flag in
.Xr ccdconfig 8 .
Use the CCD_NO_OFFSET flag instead. In that case you have to convert
the interleave factor on your own, usually it is Linux' chunk size
multiplied by two.
.Pp
Using a Linux raid this way is potentially dangerous and can destroy
the data in there. Since FreeBSD does not read the label used by
Linux, changes in Linux might invalidate the compatibility layer.
.Pp
However, using this is reasonably safe if you test the compatibility
before mounting a raid read-write for the first time. Just using
ccdconfig without mounting does not write anything to the Linux raid.
Then you do a fsck.ex2fs on the ccd device using the -n flag. You can
mount the filesystem readonly to check files in there. If all this
works, it is unlikely that there is a problem with ccd. Keep in mind
that even when the Linux compatibility mode in ccd is working
correctly, bugs in FreeBSD's ex2fs implementation would still destroy
your data.
.Sh WARNINGS
If just one (or more) of the disks in a
.Nm

View File

@ -72,6 +72,8 @@ __FBSDID("$FreeBSD$");
/* sc_flags */
#define CCDF_UNIFORM 0x02 /* use LCCD of sizes for uniform interleave */
#define CCDF_MIRROR 0x04 /* use mirroring */
#define CCDF_NO_OFFSET 0x08 /* do not leave space in front */
#define CCDF_LINUX 0x10 /* use Linux compatibility mode */
/* Mask of user-settable ccd flags. */
#define CCDF_USERMASK (CCDF_UNIFORM|CCDF_MIRROR)
@ -136,6 +138,7 @@ struct ccd_s {
u_int32_t sc_secsize; /* # bytes per sector */
int sc_pick; /* side of mirror picked */
daddr_t sc_blk[2]; /* mirror localization */
u_int32_t sc_offset; /* actual offset used */
};
static g_start_t g_ccd_start;
@ -215,6 +218,20 @@ ccdinit(struct gctl_req *req, struct ccd_s *cs)
maxsecsize = 0;
minsize = 0;
if (cs->sc_flags & CCDF_LINUX) {
cs->sc_offset = 0;
cs->sc_ileave *= 2;
if (cs->sc_flags & CCDF_MIRROR && cs->sc_ndisks != 2)
gctl_error(req, "Mirror mode for Linux raids is "
"only supported with 2 devices");
} else {
if (cs->sc_flags & CCDF_NO_OFFSET)
cs->sc_offset = 0;
else
cs->sc_offset = CCD_OFFSET;
}
for (ix = 0; ix < cs->sc_ndisks; ix++) {
ci = &cs->sc_cinfo[ix];
@ -222,7 +239,7 @@ ccdinit(struct gctl_req *req, struct ccd_s *cs)
sectorsize = ci->ci_provider->sectorsize;
if (sectorsize > maxsecsize)
maxsecsize = sectorsize;
size = mediasize / DEV_BSIZE - CCD_OFFSET;
size = mediasize / DEV_BSIZE - cs->sc_offset;
/* Truncate to interleave boundary */
@ -604,7 +621,7 @@ ccdbuffer(struct bio **cb, struct ccd_s *cs, struct bio *bp, daddr_t bn, caddr_t
if (cbp == NULL)
return (ENOMEM);
cbp->bio_done = g_std_done;
cbp->bio_offset = dbtob(cbn + cboff + CCD_OFFSET);
cbp->bio_offset = dbtob(cbn + cboff + cs->sc_offset);
cbp->bio_data = addr;
if (cs->sc_ileave == 0)
cbc = dbtob((off_t)(ci->ci_size - cbn));
@ -740,6 +757,11 @@ g_ccd_create(struct gctl_req *req, struct g_class *mp)
sc->sc_unit = *unit;
sc->sc_ileave = *ileave;
if (gctl_get_param(req, "no_offset", NULL))
sc->sc_flags |= CCDF_NO_OFFSET;
if (gctl_get_param(req, "linux", NULL))
sc->sc_flags |= CCDF_LINUX;
if (gctl_get_param(req, "uniform", NULL))
sc->sc_flags |= CCDF_UNIFORM;
if (gctl_get_param(req, "mirror", NULL))