From 452517900b740c1fd397a45dd60e21139e4ff858 Mon Sep 17 00:00:00 2001 From: cracauer Date: Thu, 13 Apr 2006 20:35:31 +0000 Subject: Make CCD be able to read and write Linux software raids. Supported for raid-0 with disks, raid-1 with 2 disks. Manpages have examples, warnings etc. Test scripts on http://www.cons.org/cracauer/ccdconfig-linux/ Reviewed by: alfred --- sbin/ccdconfig/ccdconfig.8 | 16 ++++++++++++++++ sbin/ccdconfig/ccdconfig.c | 10 ++++++++++ share/man/man4/ccd.4 | 28 ++++++++++++++++++++++++++++ sys/geom/geom_ccd.c | 26 ++++++++++++++++++++++++-- 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)) -- cgit v1.1