/*-
* Copyright (c) 1996, 1997, 1998, 1999, 2007, 2009 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Jason R. Thorpe, and by Andrew Doran.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Copyright (c) 1988 University of Utah.
* Copyright (c) 1990, 1993
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* the Systems Programming Group of the University of Utah Computer
* Science Department.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* from: Utah $Hdr: cd.c 1.6 90/11/28$
*
* @(#)cd.c 8.2 (Berkeley) 11/16/93
*/
/*
* "Concatenated" disk driver.
*
* Notes on concurrency:
*
* => sc_dvlock serializes access to the device nodes, excluding block I/O.
*
* => sc_iolock serializes access to (sc_flags & CCDF_INITED), disk stats,
* sc_stop, sc_bufq and b_resid from master buffers.
*
* => a combination of CCDF_INITED, sc_inflight, and sc_iolock is used to
* serialize I/O and configuration changes.
*
* => the in-core disk label does not change while the device is open.
*
* On memory consumption: ccd fans out I/O requests and so needs to
* allocate memory. If the system is desperately low on memory, we
* single thread I/O.
*/
/*
* Called by main() during pseudo-device attachment. All we need
* to do is allocate enough space for devices to be configured later.
*/
void
ccdattach(int num)
{
mutex_init(&ccd_lock, MUTEX_DEFAULT, IPL_NONE);
#ifdef DEBUG
if (ccddebug & (CCDB_FOLLOW|CCDB_INIT))
printf("%s: ccdinit\n", cs->sc_xname);
#endif
/* Allocate space for the component info. */
cs->sc_cinfo = kmem_alloc(cs->sc_nccdisks * sizeof(*cs->sc_cinfo),
KM_SLEEP);
tmppath = kmem_alloc(MAXPATHLEN, KM_SLEEP);
cs->sc_size = 0;
/*
* Verify that each component piece exists and record
* relevant information about it.
*/
maxsecsize = 0;
minsize = 0;
for (ix = 0, path_alloced = 0; ix < cs->sc_nccdisks; ix++) {
ci = &cs->sc_cinfo[ix];
ci->ci_vp = vpp[ix];
/*
* Copy in the pathname of the component.
*/
memset(tmppath, 0, MAXPATHLEN); /* sanity */
error = copyinstr(cpaths[ix], tmppath,
MAXPATHLEN, &ci->ci_pathlen);
if (ci->ci_pathlen == 0)
error = EINVAL;
if (error) {
#ifdef DEBUG
if (ccddebug & (CCDB_FOLLOW|CCDB_INIT))
printf("%s: can't copy path, error = %d\n",
cs->sc_xname, error);
#endif
goto out;
}
ci->ci_path = kmem_alloc(ci->ci_pathlen, KM_SLEEP);
memcpy(ci->ci_path, tmppath, ci->ci_pathlen);
path_alloced++;
/*
* Don't allow the interleave to be smaller than
* the biggest component sector.
*/
if ((cs->sc_ileave > 0) &&
(cs->sc_ileave < (maxsecsize / DEV_BSIZE))) {
#ifdef DEBUG
if (ccddebug & (CCDB_FOLLOW|CCDB_INIT))
printf("%s: interleave must be at least %d\n",
cs->sc_xname, (maxsecsize / DEV_BSIZE));
#endif
error = EINVAL;
goto out;
}
/*
* If uniform interleave is desired set all sizes to that of
* the smallest component.
*/
if (cs->sc_flags & CCDF_UNIFORM) {
for (ci = cs->sc_cinfo;
ci < &cs->sc_cinfo[cs->sc_nccdisks]; ci++)
ci->ci_size = minsize;
cs->sc_size = cs->sc_nccdisks * minsize;
}
/*
* Construct the interleave table.
*/
ccdinterleave(cs);
/*
* Only now that everything is set up can we enable the device.
*/
mutex_enter(cs->sc_iolock);
cs->sc_flags |= CCDF_INITED;
mutex_exit(cs->sc_iolock);
kmem_free(tmppath, MAXPATHLEN);
return (0);
#ifdef DEBUG
if (ccddebug & CCDB_INIT)
printf("ccdinterleave(%p): ileave %d\n", cs, cs->sc_ileave);
#endif
/*
* Allocate an interleave table.
* Chances are this is too big, but we don't care.
*/
size = (cs->sc_nccdisks + 1) * sizeof(struct ccdiinfo);
cs->sc_itable = kmem_zalloc(size, KM_SLEEP);
/*
* Trivial case: no interleave (actually interleave of disk size).
* Each table entry represents a single component in its entirety.
*/
if (cs->sc_ileave == 0) {
bn = 0;
ii = cs->sc_itable;
/*
* The following isn't fast or pretty; it doesn't have to be.
*/
size = 0;
bn = lbn = 0;
for (ii = cs->sc_itable; ; ii++) {
/* Allocate space for ii_index. */
ii->ii_indexsz = sizeof(int) * cs->sc_nccdisks;
ii->ii_index = kmem_alloc(ii->ii_indexsz, KM_SLEEP);
/*
* Locate the smallest of the remaining components
*/
smallci = NULL;
for (ci = cs->sc_cinfo;
ci < &cs->sc_cinfo[cs->sc_nccdisks]; ci++)
if (ci->ci_size > size &&
(smallci == NULL ||
ci->ci_size < smallci->ci_size))
smallci = ci;
/*
* Nobody left, all done
*/
if (smallci == NULL) {
ii->ii_ndisk = 0;
break;
}
/*
* Record starting logical block and component offset
*/
ii->ii_startblk = bn / cs->sc_ileave;
ii->ii_startoff = lbn;
/*
* Determine how many disks take part in this interleave
* and record their indices.
*/
ix = 0;
for (ci = cs->sc_cinfo;
ci < &cs->sc_cinfo[cs->sc_nccdisks]; ci++)
if (ci->ci_size >= smallci->ci_size)
ii->ii_index[ix++] = ci - cs->sc_cinfo;
ii->ii_ndisk = ix;
bn += ix * (smallci->ci_size - size);
lbn = smallci->ci_size / cs->sc_ileave;
size = smallci->ci_size;
}
#ifdef DEBUG
if (ccddebug & CCDB_INIT)
printiinfo(cs->sc_itable);
#endif
}
/* ARGSUSED */
static int
ccdopen(dev_t dev, int flags, int fmt, struct lwp *l)
{
int unit = ccdunit(dev);
struct ccd_softc *cs;
struct disklabel *lp;
int error = 0, part, pmask;
#ifdef DEBUG
if (ccddebug & CCDB_FOLLOW)
printf("ccdopen(0x%"PRIx64", 0x%x)\n", dev, flags);
#endif
if ((cs = ccdget(unit, 1)) == NULL)
return ENXIO;
mutex_enter(&cs->sc_dvlock);
lp = cs->sc_dkdev.dk_label;
part = DISKPART(dev);
pmask = (1 << part);
/*
* If we're initialized, check to see if there are any other
* open partitions. If not, then it's safe to update
* the in-core disklabel. Only read the disklabel if it is
* not already valid.
*/
if ((cs->sc_flags & (CCDF_INITED|CCDF_VLABEL)) == CCDF_INITED &&
cs->sc_dkdev.dk_openmask == 0)
ccdgetdisklabel(dev);
/* Check that the partition exists. */
if (part != RAW_PART) {
if (((cs->sc_flags & CCDF_INITED) == 0) ||
((part >= lp->d_npartitions) ||
(lp->d_partitions[part].p_fstype == FS_UNUSED))) {
error = ENXIO;
goto done;
}
}
/* Prevent our unit from being unconfigured while open. */
switch (fmt) {
case S_IFCHR:
cs->sc_dkdev.dk_copenmask |= pmask;
break;
#ifdef DEBUG
if (ccddebug & CCDB_FOLLOW)
printf("ccdthread: hello\n");
#endif
mutex_enter(cs->sc_iolock);
while (__predict_true(!cs->sc_zap)) {
bp = bufq_get(cs->sc_bufq);
if (bp == NULL) {
/* Nothing to do. */
cv_wait(&cs->sc_push, cs->sc_iolock);
continue;
}
#ifdef DEBUG
if (ccddebug & CCDB_FOLLOW)
printf("ccdthread: dispatching I/O\n");
#endif
error = ccdstart(cs, bp, PR_WAITOK);
KASSERT(error == 0);
mutex_enter(cs->sc_iolock);
}
cs->sc_thread = NULL;
mutex_exit(cs->sc_iolock);
#ifdef DEBUG
if (ccddebug & CCDB_FOLLOW)
printf("ccdthread: goodbye\n");
#endif
kthread_exit(0);
}
static void
ccdstrategy(struct buf *bp)
{
int unit = ccdunit(bp->b_dev);
struct ccd_softc *cs;
if ((cs = ccdget(unit, 0)) == NULL)
return;
/* Must be open or reading label. */
KASSERT(cs->sc_dkdev.dk_openmask != 0 ||
(cs->sc_flags & CCDF_RLABEL) != 0);
mutex_enter(cs->sc_iolock);
/* Synchronize with device init/uninit. */
if (__predict_false((cs->sc_flags & CCDF_INITED) == 0)) {
mutex_exit(cs->sc_iolock);
#ifdef DEBUG
if (ccddebug & CCDB_FOLLOW)
printf("ccdstrategy: unit %d: not inited\n", unit);
#endif
bp->b_error = ENXIO;
bp->b_resid = bp->b_bcount;
biodone(bp);
return;
}
if (ccdstart(cs, bp, PR_NOWAIT) != 0) {
/* Defer to thread if system is low on memory. */
bufq_put(cs->sc_bufq, bp);
cv_broadcast(&cs->sc_push);
mutex_exit(cs->sc_iolock);
}
}
static int
ccdstart(struct ccd_softc *cs, struct buf *bp, int wait)
{
daddr_t blkno;
int wlabel;
struct disklabel *lp;
long bcount, rcount;
struct ccdbuf *cbp;
char *addr;
daddr_t bn;
vnode_t *vp;
SIMPLEQ_HEAD(, ccdbuf) cbufq;
/* If it's a nil transfer, wake up the top half now. */
if (bp->b_bcount == 0)
goto done;
lp = cs->sc_dkdev.dk_label;
/*
* Do bounds checking and adjust transfer. If there's an
* error, the bounds check will flag that for us. Convert
* the partition relative block number to an absolute.
*/
blkno = bp->b_blkno;
wlabel = cs->sc_flags & (CCDF_WLABEL|CCDF_LABELLING);
if (DISKPART(bp->b_dev) != RAW_PART) {
if (bounds_check_with_label(&cs->sc_dkdev, bp, wlabel) <= 0)
goto done;
blkno += lp->d_partitions[DISKPART(bp->b_dev)].p_offset;
}
mutex_exit(cs->sc_iolock);
bp->b_rawblkno = blkno;
/* All buffers set up, now fire off the requests. */
while ((cbp = SIMPLEQ_FIRST(&cbufq)) != NULL) {
SIMPLEQ_REMOVE_HEAD(&cbufq, cb_q);
vp = cbp->cb_buf.b_vp;
if ((cbp->cb_buf.b_flags & B_READ) == 0) {
mutex_enter(vp->v_interlock);
vp->v_numoutput++;
mutex_exit(vp->v_interlock);
}
(void)VOP_STRATEGY(vp, &cbp->cb_buf);
}
return 0;
/*
* Called at interrupt time.
* Mark the component as done and if all components are done,
* take a ccd interrupt.
*/
static void
ccdiodone(struct buf *vbp)
{
struct ccdbuf *cbp = (struct ccdbuf *) vbp;
struct buf *bp = cbp->cb_obp;
struct ccd_softc *cs = cbp->cb_sc;
int count;
/* Must be open for writes for these commands... */
switch (cmd) {
case CCDIOCSET:
case CCDIOCCLR:
case DIOCSDINFO:
case DIOCWDINFO:
case DIOCCACHESYNC:
case DIOCAWEDGE:
case DIOCDWEDGE:
case DIOCRMWEDGES:
case DIOCMWEDGES:
#ifdef __HAVE_OLD_DISKLABEL
case ODIOCSDINFO:
case ODIOCWDINFO:
#endif
case DIOCKLABEL:
case DIOCWLABEL:
if ((flag & FWRITE) == 0)
return (EBADF);
}
/* Must be initialized for these... */
switch (cmd) {
case CCDIOCCLR:
case DIOCGDINFO:
case DIOCGSTRATEGY:
case DIOCGCACHE:
case DIOCCACHESYNC:
case DIOCAWEDGE:
case DIOCDWEDGE:
case DIOCLWEDGES:
case DIOCMWEDGES:
case DIOCSDINFO:
case DIOCWDINFO:
case DIOCGPARTINFO:
case DIOCWLABEL:
case DIOCKLABEL:
case DIOCGDEFLABEL:
#ifdef __HAVE_OLD_DISKLABEL
case ODIOCGDINFO:
case ODIOCSDINFO:
case ODIOCWDINFO:
case ODIOCGDEFLABEL:
#endif
if ((cs->sc_flags & CCDF_INITED) == 0)
return ENXIO;
}
/* Fill in some important bits. */
cs->sc_ileave = ccio->ccio_ileave;
cs->sc_nccdisks = ccio->ccio_ndisks;
cs->sc_flags = ccio->ccio_flags & CCDF_USERMASK;
/*
* Allocate space for and copy in the array of
* component pathnames and device numbers.
*/
cpp = kmem_alloc(ccio->ccio_ndisks * sizeof(*cpp), KM_SLEEP);
vpp = kmem_alloc(ccio->ccio_ndisks * sizeof(*vpp), KM_SLEEP);
error = copyin(ccio->ccio_disks, cpp,
ccio->ccio_ndisks * sizeof(*cpp));
if (error) {
kmem_free(vpp, ccio->ccio_ndisks * sizeof(*vpp));
kmem_free(cpp, ccio->ccio_ndisks * sizeof(*cpp));
goto out;
}
#ifdef DEBUG
if (ccddebug & CCDB_INIT)
for (i = 0; i < ccio->ccio_ndisks; ++i)
printf("ccdioctl: component %d: %p\n",
i, cpp[i]);
#endif
for (i = 0; i < ccio->ccio_ndisks; ++i) {
#ifdef DEBUG
if (ccddebug & CCDB_INIT)
printf("ccdioctl: lookedup = %d\n", lookedup);
#endif
error = pathbuf_copyin(cpp[i], &pb);
if (error == 0) {
error = vn_bdev_openpath(pb, &vpp[i], l);
pathbuf_destroy(pb);
}
if (error != 0) {
for (j = 0; j < lookedup; ++j)
(void)vn_close(vpp[j], FREAD|FWRITE,
uc);
kmem_free(vpp, ccio->ccio_ndisks *
sizeof(*vpp));
kmem_free(cpp, ccio->ccio_ndisks *
sizeof(*cpp));
/*
* No component data is allocated,
* nothing is to be freed.
*/
cs->sc_nccdisks = 0;
goto out;
}
++lookedup;
}
/* Attach the disk. */
disk_attach(&cs->sc_dkdev);
bufq_alloc(&cs->sc_bufq, "fcfs", 0);
/*
* Initialize the ccd. Fills in the softc for us.
*/
if ((error = ccdinit(cs, cpp, vpp, l)) != 0) {
for (j = 0; j < lookedup; ++j)
(void)vn_close(vpp[j], FREAD|FWRITE,
uc);
kmem_free(vpp, ccio->ccio_ndisks * sizeof(*vpp));
kmem_free(cpp, ccio->ccio_ndisks * sizeof(*cpp));
disk_detach(&cs->sc_dkdev);
mutex_exit(&cs->sc_dvlock);
bufq_free(cs->sc_bufq);
return error;
}
/* We can free the temporary variables now. */
kmem_free(vpp, ccio->ccio_ndisks * sizeof(*vpp));
kmem_free(cpp, ccio->ccio_ndisks * sizeof(*cpp));
/*
* The ccd has been successfully initialized, so
* we can place it into the array. Don't try to
* read the disklabel until the disk has been attached,
* because space for the disklabel is allocated
* in disk_attach();
*/
ccio->ccio_unit = unit;
ccio->ccio_size = cs->sc_size;
/* Try and read the disklabel. */
ccdgetdisklabel(dev);
disk_set_info(NULL, &cs->sc_dkdev, NULL);
case CCDIOCCLR:
/*
* Don't unconfigure if any other partitions are open
* or if both the character and block flavors of this
* partition are open.
*/
part = DISKPART(dev);
pmask = (1 << part);
if ((cs->sc_dkdev.dk_openmask & ~pmask) ||
((cs->sc_dkdev.dk_bopenmask & pmask) &&
(cs->sc_dkdev.dk_copenmask & pmask))) {
error = EBUSY;
goto out;
}
/* Delete all of our wedges. */
dkwedge_delall(&cs->sc_dkdev);
/* Stop new I/O, wait for in-flight I/O to complete. */
mutex_enter(cs->sc_iolock);
cs->sc_flags &= ~(CCDF_INITED|CCDF_VLABEL);
cs->sc_zap = true;
while (disk_isbusy(&cs->sc_dkdev) ||
bufq_peek(cs->sc_bufq) != NULL ||
cs->sc_thread != NULL) {
cv_broadcast(&cs->sc_push);
(void)cv_timedwait(&cs->sc_stop, cs->sc_iolock, hz);
}
mutex_exit(cs->sc_iolock);
/*
* Free ccd_softc information and clear entry.
*/
/* Close the components and free their pathnames. */
for (i = 0; i < cs->sc_nccdisks; ++i) {
/*
* XXX: this close could potentially fail and
* cause Bad Things. Maybe we need to force
* the close to happen?
*/
#ifdef DEBUG
if (ccddebug & CCDB_VNODE)
vprint("CCDIOCCLR: vnode info",
cs->sc_cinfo[i].ci_vp);
#endif
(void)vn_close(cs->sc_cinfo[i].ci_vp, FREAD|FWRITE,
uc);
kmem_free(cs->sc_cinfo[i].ci_path,
cs->sc_cinfo[i].ci_pathlen);
}
if (cs->sc_nccdisks != 0) {
/* Free interleave index. */
for (i = 0; cs->sc_itable[i].ii_ndisk; ++i) {
kmem_free(cs->sc_itable[i].ii_index,
cs->sc_itable[i].ii_indexsz);
}
/* Free component info and interleave table. */
kmem_free(cs->sc_cinfo, cs->sc_nccdisks *
sizeof(struct ccdcinfo));
kmem_free(cs->sc_itable, (cs->sc_nccdisks + 1) *
sizeof(struct ccdiinfo));
}
aprint_normal("%s: detached\n", cs->sc_xname);
/* Detach the disk. */
disk_detach(&cs->sc_dkdev);
bufq_free(cs->sc_bufq);
/*
* We pass this call down to all components and report
* intersection of the flags returned by the components.
* If any errors out, we return error. CCD components
* can not change unless the device is unconfigured, so
* device feature flags will remain static. RCE/WCE can change
* of course, if set directly on underlying device.
*/
for (error = 0, i = 0; i < cs->sc_nccdisks; i++) {
error = VOP_IOCTL(cs->sc_cinfo[i].ci_vp, cmd, &j,
flag, uc);
if (error)
break;
if (i == 0)
dkcache = j;
else
dkcache = DKCACHE_COMBINE(dkcache, j);
}
*((int *)data) = dkcache;
break;
}
case DIOCCACHESYNC:
/*
* We pass this call down to all components and report
* the first error we encounter.
*/
for (error = 0, i = 0; i < cs->sc_nccdisks; i++) {
j = VOP_IOCTL(cs->sc_cinfo[i].ci_vp, cmd, data,
flag, uc);
if (j != 0 && error == 0)
error = j;
}
break;
/*
* Read the disklabel from the ccd. If one is not present, fake one
* up.
*/
static void
ccdgetdisklabel(dev_t dev)
{
int unit = ccdunit(dev);
struct ccd_softc *cs;
const char *errstring;
struct disklabel *lp;
struct cpu_disklabel *clp;
/*
* Sanity check whether the found disklabel is valid.
*
* This is necessary since total size of ccd may vary
* when an interleave is changed even though exactly
* same components are used, and old disklabel may used
* if that is found.
*/
if (lp->d_secperunit < UINT32_MAX ?
lp->d_secperunit != cs->sc_size :
lp->d_secperunit > cs->sc_size)
printf("WARNING: %s: "
"total sector size in disklabel (%ju) != "
"the size of ccd (%ju)\n", cs->sc_xname,
(uintmax_t)lp->d_secperunit,
(uintmax_t)cs->sc_size);
for (i = 0; i < lp->d_npartitions; i++) {
pp = &lp->d_partitions[i];
if (pp->p_offset + pp->p_size > cs->sc_size)
printf("WARNING: %s: end of partition `%c' "
"exceeds the size of ccd (%ju)\n",
cs->sc_xname, 'a' + i, (uintmax_t)cs->sc_size);
}
}
#ifdef DEBUG
/* It's actually extremely common to have unlabeled ccds. */
if (ccddebug & CCDB_LABEL)
if (errstring != NULL)
printf("%s: %s\n", cs->sc_xname, errstring);
#endif
/*
* Take care of things one might want to take care of in the event
* that a disklabel isn't present.
*/
static void
ccdmakedisklabel(struct ccd_softc *cs)
{
struct disklabel *lp = cs->sc_dkdev.dk_label;
/*
* For historical reasons, if there's no disklabel present
* the raw partition must be marked FS_BSDFFS.
*/
lp->d_partitions[RAW_PART].p_fstype = FS_BSDFFS;