summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorcracauer <cracauer@FreeBSD.org>2006-04-13 20:35:31 +0000
committercracauer <cracauer@FreeBSD.org>2006-04-13 20:35:31 +0000
commit452517900b740c1fd397a45dd60e21139e4ff858 (patch)
treee57019f9c8880c0c23b44e552f1fd6f71f1890a3
parent65aeb543f38c35372f717f654ab9ddab09e77d7b (diff)
downloadFreeBSD-src-452517900b740c1fd397a45dd60e21139e4ff858.zip
FreeBSD-src-452517900b740c1fd397a45dd60e21139e4ff858.tar.gz
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
-rw-r--r--sbin/ccdconfig/ccdconfig.816
-rw-r--r--sbin/ccdconfig/ccdconfig.c10
-rw-r--r--share/man/man4/ccd.428
-rw-r--r--sys/geom/geom_ccd.c26
4 files changed, 78 insertions, 2 deletions
diff --git a/sbin/ccdconfig/ccdconfig.8 b/sbin/ccdconfig/ccdconfig.8
index 4ba08be..bbd7428 100644
--- a/sbin/ccdconfig/ccdconfig.8
+++ b/sbin/ccdconfig/ccdconfig.8
@@ -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
diff --git a/sbin/ccdconfig/ccdconfig.c b/sbin/ccdconfig/ccdconfig.c
index 0c584ff..928aba5 100644
--- a/sbin/ccdconfig/ccdconfig.c
+++ b/sbin/ccdconfig/ccdconfig.c
@@ -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);
diff --git a/share/man/man4/ccd.4 b/share/man/man4/ccd.4
index 4ff4002..d49a82d 100644
--- a/share/man/man4/ccd.4
+++ b/share/man/man4/ccd.4
@@ -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
diff --git a/sys/geom/geom_ccd.c b/sys/geom/geom_ccd.c
index fb66882..e31f23a 100644
--- a/sys/geom/geom_ccd.c
+++ b/sys/geom/geom_ccd.c
@@ -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))
OpenPOWER on IntegriCloud