ld.c revision 1.37.2.1
11.37.2.1Syamt/* $NetBSD: ld.c,v 1.37.2.1 2006/06/21 15:02:12 yamt Exp $ */ 21.1Sad 31.1Sad/*- 41.1Sad * Copyright (c) 1998, 2000 The NetBSD Foundation, Inc. 51.1Sad * All rights reserved. 61.1Sad * 71.1Sad * This code is derived from software contributed to The NetBSD Foundation 81.1Sad * by Andrew Doran and Charles M. Hannum. 91.1Sad * 101.1Sad * Redistribution and use in source and binary forms, with or without 111.1Sad * modification, are permitted provided that the following conditions 121.1Sad * are met: 131.1Sad * 1. Redistributions of source code must retain the above copyright 141.1Sad * notice, this list of conditions and the following disclaimer. 151.1Sad * 2. Redistributions in binary form must reproduce the above copyright 161.1Sad * notice, this list of conditions and the following disclaimer in the 171.1Sad * documentation and/or other materials provided with the distribution. 181.1Sad * 3. All advertising materials mentioning features or use of this software 191.1Sad * must display the following acknowledgement: 201.1Sad * This product includes software developed by the NetBSD 211.1Sad * Foundation, Inc. and its contributors. 221.1Sad * 4. Neither the name of The NetBSD Foundation nor the names of its 231.1Sad * contributors may be used to endorse or promote products derived 241.1Sad * from this software without specific prior written permission. 251.1Sad * 261.1Sad * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 271.1Sad * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 281.1Sad * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 291.1Sad * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 301.1Sad * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 311.1Sad * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 321.1Sad * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 331.1Sad * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 341.1Sad * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 351.1Sad * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 361.1Sad * POSSIBILITY OF SUCH DAMAGE. 371.1Sad */ 381.1Sad 391.1Sad/* 401.1Sad * Disk driver for use by RAID controllers. 411.1Sad */ 421.12Slukem 431.12Slukem#include <sys/cdefs.h> 441.37.2.1Syamt__KERNEL_RCSID(0, "$NetBSD: ld.c,v 1.37.2.1 2006/06/21 15:02:12 yamt Exp $"); 451.1Sad 461.1Sad#include "rnd.h" 471.1Sad 481.1Sad#include <sys/param.h> 491.1Sad#include <sys/systm.h> 501.1Sad#include <sys/kernel.h> 511.1Sad#include <sys/device.h> 521.1Sad#include <sys/queue.h> 531.1Sad#include <sys/proc.h> 541.1Sad#include <sys/buf.h> 551.33Syamt#include <sys/bufq.h> 561.1Sad#include <sys/endian.h> 571.1Sad#include <sys/disklabel.h> 581.1Sad#include <sys/disk.h> 591.1Sad#include <sys/dkio.h> 601.1Sad#include <sys/stat.h> 611.1Sad#include <sys/lock.h> 621.1Sad#include <sys/conf.h> 631.1Sad#include <sys/fcntl.h> 641.2Sad#include <sys/vnode.h> 651.1Sad#include <sys/syslog.h> 661.1Sad#if NRND > 0 671.1Sad#include <sys/rnd.h> 681.1Sad#endif 691.1Sad 701.1Sad#include <dev/ldvar.h> 711.1Sad 721.1Sadstatic void ldgetdefaultlabel(struct ld_softc *, struct disklabel *); 731.1Sadstatic void ldgetdisklabel(struct ld_softc *); 741.1Sadstatic void ldminphys(struct buf *bp); 751.1Sadstatic void ldshutdown(void *); 761.23Sthorpejstatic void ldstart(struct ld_softc *); 771.1Sad 781.1Sadextern struct cfdriver ld_cd; 791.1Sad 801.29Sthorpejstatic dev_type_open(ldopen); 811.29Sthorpejstatic dev_type_close(ldclose); 821.29Sthorpejstatic dev_type_read(ldread); 831.29Sthorpejstatic dev_type_write(ldwrite); 841.29Sthorpejstatic dev_type_ioctl(ldioctl); 851.29Sthorpejstatic dev_type_strategy(ldstrategy); 861.29Sthorpejstatic dev_type_dump(lddump); 871.29Sthorpejstatic dev_type_size(ldsize); 881.16Sgehenna 891.16Sgehennaconst struct bdevsw ld_bdevsw = { 901.16Sgehenna ldopen, ldclose, ldstrategy, ldioctl, lddump, ldsize, D_DISK 911.16Sgehenna}; 921.16Sgehenna 931.16Sgehennaconst struct cdevsw ld_cdevsw = { 941.16Sgehenna ldopen, ldclose, ldread, ldwrite, ldioctl, 951.17Sjdolecek nostop, notty, nopoll, nommap, nokqfilter, D_DISK 961.16Sgehenna}; 971.16Sgehenna 981.30Sthorpejstatic struct dkdriver lddkdriver = { ldstrategy, ldminphys }; 991.1Sadstatic void *ld_sdh; 1001.1Sad 1011.1Sadvoid 1021.1Sadldattach(struct ld_softc *sc) 1031.1Sad{ 1041.37Schristos char tbuf[9]; 1051.1Sad 1061.7Sad if ((sc->sc_flags & LDF_ENABLED) == 0) { 1071.34Sbriggs aprint_normal("%s: disabled\n", sc->sc_dv.dv_xname); 1081.7Sad return; 1091.7Sad } 1101.7Sad 1111.1Sad /* Initialise and attach the disk structure. */ 1121.1Sad sc->sc_dk.dk_driver = &lddkdriver; 1131.1Sad sc->sc_dk.dk_name = sc->sc_dv.dv_xname; 1141.1Sad disk_attach(&sc->sc_dk); 1151.1Sad 1161.1Sad if (sc->sc_maxxfer > MAXPHYS) 1171.1Sad sc->sc_maxxfer = MAXPHYS; 1181.9Sad 1191.19Sthorpej /* Build synthetic geometry if necessary. */ 1201.19Sthorpej if (sc->sc_nheads == 0 || sc->sc_nsectors == 0 || 1211.19Sthorpej sc->sc_ncylinders == 0) { 1221.28Sdbj uint64_t ncyl; 1231.28Sdbj 1241.19Sthorpej if (sc->sc_secperunit <= 528 * 2048) /* 528MB */ 1251.19Sthorpej sc->sc_nheads = 16; 1261.19Sthorpej else if (sc->sc_secperunit <= 1024 * 2048) /* 1GB */ 1271.19Sthorpej sc->sc_nheads = 32; 1281.19Sthorpej else if (sc->sc_secperunit <= 21504 * 2048) /* 21GB */ 1291.19Sthorpej sc->sc_nheads = 64; 1301.19Sthorpej else if (sc->sc_secperunit <= 43008 * 2048) /* 42GB */ 1311.19Sthorpej sc->sc_nheads = 128; 1321.19Sthorpej else 1331.19Sthorpej sc->sc_nheads = 255; 1341.19Sthorpej 1351.19Sthorpej sc->sc_nsectors = 63; 1361.28Sdbj sc->sc_ncylinders = INT_MAX; 1371.35Sperry ncyl = sc->sc_secperunit / 1381.19Sthorpej (sc->sc_nheads * sc->sc_nsectors); 1391.28Sdbj if (ncyl < INT_MAX) 1401.28Sdbj sc->sc_ncylinders = (int)ncyl; 1411.19Sthorpej } 1421.1Sad 1431.37Schristos format_bytes(tbuf, sizeof(tbuf), sc->sc_secperunit * 1441.1Sad sc->sc_secsize); 1451.34Sbriggs aprint_normal("%s: %s, %d cyl, %d head, %d sec, %d bytes/sect x %"PRIu64" sectors\n", 1461.37Schristos sc->sc_dv.dv_xname, tbuf, sc->sc_ncylinders, sc->sc_nheads, 1471.1Sad sc->sc_nsectors, sc->sc_secsize, sc->sc_secperunit); 1481.1Sad 1491.1Sad#if NRND > 0 1501.1Sad /* Attach the device into the rnd source list. */ 1511.1Sad rnd_attach_source(&sc->sc_rnd_source, sc->sc_dv.dv_xname, 1521.1Sad RND_TYPE_DISK, 0); 1531.1Sad#endif 1541.1Sad 1551.1Sad /* Set the `shutdownhook'. */ 1561.1Sad if (ld_sdh == NULL) 1571.1Sad ld_sdh = shutdownhook_establish(ldshutdown, NULL); 1581.37.2.1Syamt bufq_alloc(&sc->sc_bufq, BUFQ_DISK_DEFAULT_STRAT, BUFQ_SORT_RAWBLOCK); 1591.30Sthorpej 1601.30Sthorpej /* Discover wedges on this disk. */ 1611.30Sthorpej dkwedge_discover(&sc->sc_dk); 1621.1Sad} 1631.1Sad 1641.3Sadint 1651.37Schristosldadjqparam(struct ld_softc *sc, int xmax) 1661.3Sad{ 1671.24Sthorpej int s; 1681.7Sad 1691.7Sad s = splbio(); 1701.37Schristos sc->sc_maxqueuecnt = xmax; 1711.7Sad splx(s); 1721.7Sad 1731.24Sthorpej return (0); 1741.7Sad} 1751.7Sad 1761.7Sadint 1771.7Sadldbegindetach(struct ld_softc *sc, int flags) 1781.7Sad{ 1791.24Sthorpej int s, rv = 0; 1801.7Sad 1811.7Sad if ((sc->sc_flags & LDF_ENABLED) == 0) 1821.7Sad return (0); 1831.3Sad 1841.3Sad if ((flags & DETACH_FORCE) == 0 && sc->sc_dk.dk_openmask != 0) 1851.3Sad return (EBUSY); 1861.3Sad 1871.3Sad s = splbio(); 1881.24Sthorpej sc->sc_maxqueuecnt = 0; 1891.7Sad sc->sc_flags |= LDF_DETACH; 1901.24Sthorpej while (sc->sc_queuecnt > 0) { 1911.24Sthorpej sc->sc_flags |= LDF_DRAIN; 1921.24Sthorpej rv = tsleep(&sc->sc_queuecnt, PRIBIO, "lddrn", 0); 1931.24Sthorpej if (rv) 1941.24Sthorpej break; 1951.24Sthorpej } 1961.3Sad splx(s); 1971.7Sad 1981.7Sad return (rv); 1991.3Sad} 2001.3Sad 2011.2Sadvoid 2021.7Sadldenddetach(struct ld_softc *sc) 2031.2Sad{ 2041.13Sdrochner int s, bmaj, cmaj, i, mn; 2051.2Sad 2061.7Sad if ((sc->sc_flags & LDF_ENABLED) == 0) 2071.7Sad return; 2081.7Sad 2091.2Sad /* Wait for commands queued with the hardware to complete. */ 2101.2Sad if (sc->sc_queuecnt != 0) 2111.7Sad if (tsleep(&sc->sc_queuecnt, PRIBIO, "lddtch", 30 * hz)) 2121.7Sad printf("%s: not drained\n", sc->sc_dv.dv_xname); 2131.2Sad 2141.2Sad /* Locate the major numbers. */ 2151.16Sgehenna bmaj = bdevsw_lookup_major(&ld_bdevsw); 2161.16Sgehenna cmaj = cdevsw_lookup_major(&ld_cdevsw); 2171.2Sad 2181.2Sad /* Kill off any queued buffers. */ 2191.2Sad s = splbio(); 2201.37.2.1Syamt bufq_drain(sc->sc_bufq); 2211.36Syamt splx(s); 2221.36Syamt 2231.37.2.1Syamt bufq_free(sc->sc_bufq); 2241.2Sad 2251.2Sad /* Nuke the vnodes for any open instances. */ 2261.13Sdrochner for (i = 0; i < MAXPARTITIONS; i++) { 2271.37.2.1Syamt mn = DISKMINOR(device_unit(&sc->sc_dv), i); 2281.13Sdrochner vdevgone(bmaj, mn, mn, VBLK); 2291.13Sdrochner vdevgone(cmaj, mn, mn, VCHR); 2301.13Sdrochner } 2311.13Sdrochner 2321.30Sthorpej /* Delete all of our wedges. */ 2331.30Sthorpej dkwedge_delall(&sc->sc_dk); 2341.30Sthorpej 2351.2Sad /* Detach from the disk list. */ 2361.2Sad disk_detach(&sc->sc_dk); 2371.2Sad 2381.2Sad#if NRND > 0 2391.2Sad /* Unhook the entropy source. */ 2401.2Sad rnd_detach_source(&sc->sc_rnd_source); 2411.2Sad#endif 2421.2Sad 2431.24Sthorpej /* 2441.24Sthorpej * XXX We can't really flush the cache here, beceause the 2451.24Sthorpej * XXX device may already be non-existent from the controller's 2461.24Sthorpej * XXX perspective. 2471.24Sthorpej */ 2481.24Sthorpej#if 0 2491.2Sad /* Flush the device's cache. */ 2501.2Sad if (sc->sc_flush != NULL) 2511.2Sad if ((*sc->sc_flush)(sc) != 0) 2521.2Sad printf("%s: unable to flush cache\n", 2531.2Sad sc->sc_dv.dv_xname); 2541.24Sthorpej#endif 2551.2Sad} 2561.2Sad 2571.8Slukem/* ARGSUSED */ 2581.1Sadstatic void 2591.1Sadldshutdown(void *cookie) 2601.1Sad{ 2611.1Sad struct ld_softc *sc; 2621.1Sad int i; 2631.1Sad 2641.1Sad for (i = 0; i < ld_cd.cd_ndevs; i++) { 2651.1Sad if ((sc = device_lookup(&ld_cd, i)) == NULL) 2661.1Sad continue; 2671.1Sad if (sc->sc_flush != NULL && (*sc->sc_flush)(sc) != 0) 2681.1Sad printf("%s: unable to flush cache\n", 2691.1Sad sc->sc_dv.dv_xname); 2701.1Sad } 2711.1Sad} 2721.1Sad 2731.8Slukem/* ARGSUSED */ 2741.29Sthorpejstatic int 2751.37.2.1Syamtldopen(dev_t dev, int flags, int fmt, struct lwp *l) 2761.1Sad{ 2771.1Sad struct ld_softc *sc; 2781.30Sthorpej int error, unit, part; 2791.1Sad 2801.1Sad unit = DISKUNIT(dev); 2811.30Sthorpej if ((sc = device_lookup(&ld_cd, unit)) == NULL) 2821.1Sad return (ENXIO); 2831.1Sad if ((sc->sc_flags & LDF_ENABLED) == 0) 2841.1Sad return (ENODEV); 2851.1Sad part = DISKPART(dev); 2861.30Sthorpej 2871.30Sthorpej if ((error = lockmgr(&sc->sc_dk.dk_openlock, LK_EXCLUSIVE, NULL)) != 0) 2881.30Sthorpej return (error); 2891.1Sad 2901.22Sthorpej if (sc->sc_dk.dk_openmask == 0) { 2911.22Sthorpej /* Load the partition info if not already loaded. */ 2921.22Sthorpej if ((sc->sc_flags & LDF_VLABEL) == 0) 2931.22Sthorpej ldgetdisklabel(sc); 2941.22Sthorpej } 2951.1Sad 2961.1Sad /* Check that the partition exists. */ 2971.1Sad if (part != RAW_PART && (part >= sc->sc_dk.dk_label->d_npartitions || 2981.1Sad sc->sc_dk.dk_label->d_partitions[part].p_fstype == FS_UNUSED)) { 2991.30Sthorpej error = ENXIO; 3001.30Sthorpej goto bad1; 3011.1Sad } 3021.1Sad 3031.1Sad /* Ensure only one open at a time. */ 3041.1Sad switch (fmt) { 3051.1Sad case S_IFCHR: 3061.1Sad sc->sc_dk.dk_copenmask |= (1 << part); 3071.1Sad break; 3081.1Sad case S_IFBLK: 3091.1Sad sc->sc_dk.dk_bopenmask |= (1 << part); 3101.1Sad break; 3111.1Sad } 3121.1Sad sc->sc_dk.dk_openmask = 3131.1Sad sc->sc_dk.dk_copenmask | sc->sc_dk.dk_bopenmask; 3141.1Sad 3151.30Sthorpej (void) lockmgr(&sc->sc_dk.dk_openlock, LK_RELEASE, NULL); 3161.1Sad return (0); 3171.30Sthorpej 3181.30Sthorpej bad1: 3191.30Sthorpej (void) lockmgr(&sc->sc_dk.dk_openlock, LK_RELEASE, NULL); 3201.30Sthorpej return (error); 3211.1Sad} 3221.1Sad 3231.8Slukem/* ARGSUSED */ 3241.29Sthorpejstatic int 3251.37.2.1Syamtldclose(dev_t dev, int flags, int fmt, struct lwp *l) 3261.1Sad{ 3271.1Sad struct ld_softc *sc; 3281.30Sthorpej int error, part, unit; 3291.1Sad 3301.1Sad unit = DISKUNIT(dev); 3311.1Sad part = DISKPART(dev); 3321.1Sad sc = device_lookup(&ld_cd, unit); 3331.30Sthorpej 3341.30Sthorpej if ((error = lockmgr(&sc->sc_dk.dk_openlock, LK_EXCLUSIVE, NULL)) != 0) 3351.30Sthorpej return (error); 3361.1Sad 3371.1Sad switch (fmt) { 3381.1Sad case S_IFCHR: 3391.1Sad sc->sc_dk.dk_copenmask &= ~(1 << part); 3401.1Sad break; 3411.1Sad case S_IFBLK: 3421.1Sad sc->sc_dk.dk_bopenmask &= ~(1 << part); 3431.1Sad break; 3441.1Sad } 3451.1Sad sc->sc_dk.dk_openmask = 3461.1Sad sc->sc_dk.dk_copenmask | sc->sc_dk.dk_bopenmask; 3471.1Sad 3481.22Sthorpej if (sc->sc_dk.dk_openmask == 0) { 3491.22Sthorpej if (sc->sc_flush != NULL && (*sc->sc_flush)(sc) != 0) 3501.1Sad printf("%s: unable to flush cache\n", 3511.1Sad sc->sc_dv.dv_xname); 3521.22Sthorpej if ((sc->sc_flags & LDF_KLABEL) == 0) 3531.22Sthorpej sc->sc_flags &= ~LDF_VLABEL; 3541.22Sthorpej } 3551.1Sad 3561.30Sthorpej (void) lockmgr(&sc->sc_dk.dk_openlock, LK_RELEASE, NULL); 3571.1Sad return (0); 3581.1Sad} 3591.1Sad 3601.8Slukem/* ARGSUSED */ 3611.29Sthorpejstatic int 3621.1Sadldread(dev_t dev, struct uio *uio, int ioflag) 3631.1Sad{ 3641.1Sad 3651.1Sad return (physio(ldstrategy, NULL, dev, B_READ, ldminphys, uio)); 3661.1Sad} 3671.1Sad 3681.8Slukem/* ARGSUSED */ 3691.29Sthorpejstatic int 3701.1Sadldwrite(dev_t dev, struct uio *uio, int ioflag) 3711.1Sad{ 3721.1Sad 3731.1Sad return (physio(ldstrategy, NULL, dev, B_WRITE, ldminphys, uio)); 3741.1Sad} 3751.1Sad 3761.8Slukem/* ARGSUSED */ 3771.29Sthorpejstatic int 3781.37.2.1Syamtldioctl(dev_t dev, u_long cmd, caddr_t addr, int32_t flag, struct lwp *l) 3791.1Sad{ 3801.1Sad struct ld_softc *sc; 3811.1Sad int part, unit, error; 3821.4Sfvdl#ifdef __HAVE_OLD_DISKLABEL 3831.6Sitojun struct disklabel newlabel; 3841.4Sfvdl#endif 3851.6Sitojun struct disklabel *lp; 3861.1Sad 3871.1Sad unit = DISKUNIT(dev); 3881.1Sad part = DISKPART(dev); 3891.1Sad sc = device_lookup(&ld_cd, unit); 3901.1Sad error = 0; 3911.1Sad 3921.1Sad switch (cmd) { 3931.1Sad case DIOCGDINFO: 3941.1Sad memcpy(addr, sc->sc_dk.dk_label, sizeof(struct disklabel)); 3951.1Sad return (0); 3961.1Sad 3971.4Sfvdl#ifdef __HAVE_OLD_DISKLABEL 3981.4Sfvdl case ODIOCGDINFO: 3991.4Sfvdl newlabel = *(sc->sc_dk.dk_label); 4001.4Sfvdl if (newlabel.d_npartitions > OLDMAXPARTITIONS) 4011.5Sfvdl return ENOTTY; 4021.4Sfvdl memcpy(addr, &newlabel, sizeof(struct olddisklabel)); 4031.4Sfvdl return (0); 4041.4Sfvdl#endif 4051.4Sfvdl 4061.1Sad case DIOCGPART: 4071.1Sad ((struct partinfo *)addr)->disklab = sc->sc_dk.dk_label; 4081.1Sad ((struct partinfo *)addr)->part = 4091.1Sad &sc->sc_dk.dk_label->d_partitions[part]; 4101.1Sad break; 4111.1Sad 4121.1Sad case DIOCWDINFO: 4131.1Sad case DIOCSDINFO: 4141.4Sfvdl#ifdef __HAVE_OLD_DISKLABEL 4151.4Sfvdl case ODIOCWDINFO: 4161.4Sfvdl case ODIOCSDINFO: 4171.4Sfvdl 4181.4Sfvdl if (cmd == ODIOCSDINFO || cmd == ODIOCWDINFO) { 4191.4Sfvdl memset(&newlabel, 0, sizeof newlabel); 4201.4Sfvdl memcpy(&newlabel, addr, sizeof (struct olddisklabel)); 4211.4Sfvdl lp = &newlabel; 4221.4Sfvdl } else 4231.4Sfvdl#endif 4241.4Sfvdl lp = (struct disklabel *)addr; 4251.4Sfvdl 4261.1Sad if ((flag & FWRITE) == 0) 4271.1Sad return (EBADF); 4281.1Sad 4291.30Sthorpej if ((error = lockmgr(&sc->sc_dk.dk_openlock, LK_EXCLUSIVE, 4301.31Sjdolecek NULL)) != 0) 4311.1Sad return (error); 4321.1Sad sc->sc_flags |= LDF_LABELLING; 4331.1Sad 4341.1Sad error = setdisklabel(sc->sc_dk.dk_label, 4351.4Sfvdl lp, /*sc->sc_dk.dk_openmask : */0, 4361.1Sad sc->sc_dk.dk_cpulabel); 4371.4Sfvdl if (error == 0 && (cmd == DIOCWDINFO 4381.4Sfvdl#ifdef __HAVE_OLD_DISKLABEL 4391.4Sfvdl || cmd == ODIOCWDINFO 4401.4Sfvdl#endif 4411.4Sfvdl )) 4421.1Sad error = writedisklabel( 4431.35Sperry MAKEDISKDEV(major(dev), DISKUNIT(dev), RAW_PART), 4441.35Sperry ldstrategy, sc->sc_dk.dk_label, 4451.1Sad sc->sc_dk.dk_cpulabel); 4461.1Sad 4471.1Sad sc->sc_flags &= ~LDF_LABELLING; 4481.30Sthorpej (void) lockmgr(&sc->sc_dk.dk_openlock, LK_RELEASE, NULL); 4491.1Sad break; 4501.1Sad 4511.22Sthorpej case DIOCKLABEL: 4521.22Sthorpej if ((flag & FWRITE) == 0) 4531.22Sthorpej return (EBADF); 4541.22Sthorpej if (*(int *)addr) 4551.22Sthorpej sc->sc_flags |= LDF_KLABEL; 4561.22Sthorpej else 4571.22Sthorpej sc->sc_flags &= ~LDF_KLABEL; 4581.22Sthorpej break; 4591.22Sthorpej 4601.1Sad case DIOCWLABEL: 4611.1Sad if ((flag & FWRITE) == 0) 4621.1Sad return (EBADF); 4631.1Sad if (*(int *)addr) 4641.1Sad sc->sc_flags |= LDF_WLABEL; 4651.1Sad else 4661.1Sad sc->sc_flags &= ~LDF_WLABEL; 4671.1Sad break; 4681.1Sad 4691.1Sad case DIOCGDEFLABEL: 4701.1Sad ldgetdefaultlabel(sc, (struct disklabel *)addr); 4711.1Sad break; 4721.4Sfvdl 4731.4Sfvdl#ifdef __HAVE_OLD_DISKLABEL 4741.4Sfvdl case ODIOCGDEFLABEL: 4751.4Sfvdl ldgetdefaultlabel(sc, &newlabel); 4761.4Sfvdl if (newlabel.d_npartitions > OLDMAXPARTITIONS) 4771.5Sfvdl return ENOTTY; 4781.4Sfvdl memcpy(addr, &newlabel, sizeof (struct olddisklabel)); 4791.4Sfvdl break; 4801.4Sfvdl#endif 4811.1Sad 4821.32Sthorpej case DIOCCACHESYNC: 4831.32Sthorpej /* 4841.32Sthorpej * XXX Do we really need to care about having a writable 4851.32Sthorpej * file descriptor here? 4861.32Sthorpej */ 4871.32Sthorpej if ((flag & FWRITE) == 0) 4881.32Sthorpej error = EBADF; 4891.32Sthorpej else if (sc->sc_flush) 4901.32Sthorpej error = (*sc->sc_flush)(sc); 4911.32Sthorpej else 4921.32Sthorpej error = 0; /* XXX Error out instead? */ 4931.32Sthorpej break; 4941.32Sthorpej 4951.30Sthorpej case DIOCAWEDGE: 4961.30Sthorpej { 4971.30Sthorpej struct dkwedge_info *dkw = (void *) addr; 4981.30Sthorpej 4991.30Sthorpej if ((flag & FWRITE) == 0) 5001.30Sthorpej return (EBADF); 5011.30Sthorpej 5021.30Sthorpej /* If the ioctl happens here, the parent is us. */ 5031.30Sthorpej strcpy(dkw->dkw_parent, sc->sc_dv.dv_xname); 5041.30Sthorpej return (dkwedge_add(dkw)); 5051.30Sthorpej } 5061.35Sperry 5071.30Sthorpej case DIOCDWEDGE: 5081.30Sthorpej { 5091.30Sthorpej struct dkwedge_info *dkw = (void *) addr; 5101.30Sthorpej 5111.30Sthorpej if ((flag & FWRITE) == 0) 5121.30Sthorpej return (EBADF); 5131.30Sthorpej 5141.30Sthorpej /* If the ioctl happens here, the parent is us. */ 5151.30Sthorpej strcpy(dkw->dkw_parent, sc->sc_dv.dv_xname); 5161.30Sthorpej return (dkwedge_del(dkw)); 5171.30Sthorpej } 5181.35Sperry 5191.30Sthorpej case DIOCLWEDGES: 5201.30Sthorpej { 5211.30Sthorpej struct dkwedge_list *dkwl = (void *) addr; 5221.30Sthorpej 5231.37.2.1Syamt return (dkwedge_list(&sc->sc_dk, dkwl, l)); 5241.30Sthorpej } 5251.30Sthorpej 5261.1Sad default: 5271.1Sad error = ENOTTY; 5281.1Sad break; 5291.1Sad } 5301.1Sad 5311.1Sad return (error); 5321.1Sad} 5331.1Sad 5341.29Sthorpejstatic void 5351.1Sadldstrategy(struct buf *bp) 5361.1Sad{ 5371.1Sad struct ld_softc *sc; 5381.23Sthorpej struct disklabel *lp; 5391.23Sthorpej daddr_t blkno; 5401.23Sthorpej int s, part; 5411.1Sad 5421.1Sad sc = device_lookup(&ld_cd, DISKUNIT(bp->b_dev)); 5431.23Sthorpej part = DISKPART(bp->b_dev); 5441.1Sad 5451.7Sad if ((sc->sc_flags & LDF_DETACH) != 0) { 5461.2Sad bp->b_error = EIO; 5471.23Sthorpej goto bad; 5481.2Sad } 5491.2Sad 5501.1Sad lp = sc->sc_dk.dk_label; 5511.1Sad 5521.1Sad /* 5531.1Sad * The transfer must be a whole number of blocks and the offset must 5541.1Sad * not be negative. 5551.1Sad */ 5561.1Sad if ((bp->b_bcount % lp->d_secsize) != 0 || bp->b_blkno < 0) { 5571.23Sthorpej bp->b_error = EINVAL; 5581.23Sthorpej goto bad; 5591.1Sad } 5601.1Sad 5611.23Sthorpej /* If it's a null transfer, return immediately. */ 5621.23Sthorpej if (bp->b_bcount == 0) 5631.23Sthorpej goto done; 5641.1Sad 5651.1Sad /* 5661.1Sad * Do bounds checking and adjust the transfer. If error, process. 5671.1Sad * If past the end of partition, just return. 5681.1Sad */ 5691.1Sad if (part != RAW_PART && 5701.20Sthorpej bounds_check_with_label(&sc->sc_dk, bp, 5711.1Sad (sc->sc_flags & (LDF_WLABEL | LDF_LABELLING)) != 0) <= 0) { 5721.23Sthorpej goto done; 5731.1Sad } 5741.1Sad 5751.1Sad /* 5761.23Sthorpej * Convert the block number to absolute and put it in terms 5771.23Sthorpej * of the device's logical block size. 5781.1Sad */ 5791.23Sthorpej if (lp->d_secsize == DEV_BSIZE) 5801.23Sthorpej blkno = bp->b_blkno; 5811.23Sthorpej else if (lp->d_secsize > DEV_BSIZE) 5821.23Sthorpej blkno = bp->b_blkno / (lp->d_secsize / DEV_BSIZE); 5831.1Sad else 5841.23Sthorpej blkno = bp->b_blkno * (DEV_BSIZE / lp->d_secsize); 5851.1Sad 5861.11Sthorpej if (part != RAW_PART) 5871.23Sthorpej blkno += lp->d_partitions[part].p_offset; 5881.23Sthorpej 5891.23Sthorpej bp->b_rawblkno = blkno; 5901.1Sad 5911.1Sad s = splbio(); 5921.37.2.1Syamt BUFQ_PUT(sc->sc_bufq, bp); 5931.23Sthorpej ldstart(sc); 5941.1Sad splx(s); 5951.23Sthorpej return; 5961.23Sthorpej 5971.23Sthorpej bad: 5981.23Sthorpej bp->b_flags |= B_ERROR; 5991.23Sthorpej done: 6001.23Sthorpej bp->b_resid = bp->b_bcount; 6011.23Sthorpej biodone(bp); 6021.23Sthorpej} 6031.23Sthorpej 6041.23Sthorpejstatic void 6051.23Sthorpejldstart(struct ld_softc *sc) 6061.23Sthorpej{ 6071.23Sthorpej struct buf *bp; 6081.23Sthorpej int error; 6091.1Sad 6101.23Sthorpej while (sc->sc_queuecnt < sc->sc_maxqueuecnt) { 6111.23Sthorpej /* See if there is work to do. */ 6121.37.2.1Syamt if ((bp = BUFQ_PEEK(sc->sc_bufq)) == NULL) 6131.23Sthorpej break; 6141.23Sthorpej 6151.23Sthorpej disk_busy(&sc->sc_dk); 6161.23Sthorpej sc->sc_queuecnt++; 6171.23Sthorpej 6181.23Sthorpej if (__predict_true((error = (*sc->sc_start)(sc, bp)) == 0)) { 6191.23Sthorpej /* 6201.23Sthorpej * The back-end is running the job; remove it from 6211.23Sthorpej * the queue. 6221.23Sthorpej */ 6231.37.2.1Syamt (void) BUFQ_GET(sc->sc_bufq); 6241.23Sthorpej } else { 6251.23Sthorpej disk_unbusy(&sc->sc_dk, 0, (bp->b_flags & B_READ)); 6261.23Sthorpej sc->sc_queuecnt--; 6271.23Sthorpej if (error == EAGAIN) { 6281.23Sthorpej /* 6291.23Sthorpej * Temporary resource shortage in the 6301.23Sthorpej * back-end; just defer the job until 6311.23Sthorpej * later. 6321.23Sthorpej * 6331.23Sthorpej * XXX We might consider a watchdog timer 6341.23Sthorpej * XXX to make sure we are kicked into action. 6351.23Sthorpej */ 6361.23Sthorpej break; 6371.23Sthorpej } else { 6381.37.2.1Syamt (void) BUFQ_GET(sc->sc_bufq); 6391.23Sthorpej bp->b_error = error; 6401.23Sthorpej bp->b_flags |= B_ERROR; 6411.23Sthorpej bp->b_resid = bp->b_bcount; 6421.23Sthorpej biodone(bp); 6431.23Sthorpej } 6441.23Sthorpej } 6451.1Sad } 6461.1Sad} 6471.1Sad 6481.1Sadvoid 6491.1Sadlddone(struct ld_softc *sc, struct buf *bp) 6501.1Sad{ 6511.1Sad 6521.1Sad if ((bp->b_flags & B_ERROR) != 0) { 6531.1Sad diskerr(bp, "ld", "error", LOG_PRINTF, 0, sc->sc_dk.dk_label); 6541.1Sad printf("\n"); 6551.1Sad } 6561.1Sad 6571.18Smrg disk_unbusy(&sc->sc_dk, bp->b_bcount - bp->b_resid, 6581.18Smrg (bp->b_flags & B_READ)); 6591.1Sad#if NRND > 0 6601.1Sad rnd_add_uint32(&sc->sc_rnd_source, bp->b_rawblkno); 6611.1Sad#endif 6621.1Sad biodone(bp); 6631.1Sad 6641.7Sad if (--sc->sc_queuecnt <= sc->sc_maxqueuecnt) { 6651.24Sthorpej if ((sc->sc_flags & LDF_DRAIN) != 0) { 6661.24Sthorpej sc->sc_flags &= ~LDF_DRAIN; 6671.7Sad wakeup(&sc->sc_queuecnt); 6681.24Sthorpej } 6691.23Sthorpej ldstart(sc); 6701.1Sad } 6711.1Sad} 6721.1Sad 6731.29Sthorpejstatic int 6741.1Sadldsize(dev_t dev) 6751.1Sad{ 6761.1Sad struct ld_softc *sc; 6771.1Sad int part, unit, omask, size; 6781.1Sad 6791.1Sad unit = DISKUNIT(dev); 6801.1Sad if ((sc = device_lookup(&ld_cd, unit)) == NULL) 6811.1Sad return (ENODEV); 6821.1Sad if ((sc->sc_flags & LDF_ENABLED) == 0) 6831.1Sad return (ENODEV); 6841.1Sad part = DISKPART(dev); 6851.1Sad 6861.1Sad omask = sc->sc_dk.dk_openmask & (1 << part); 6871.1Sad 6881.1Sad if (omask == 0 && ldopen(dev, 0, S_IFBLK, NULL) != 0) 6891.1Sad return (-1); 6901.1Sad else if (sc->sc_dk.dk_label->d_partitions[part].p_fstype != FS_SWAP) 6911.1Sad size = -1; 6921.1Sad else 6931.1Sad size = sc->sc_dk.dk_label->d_partitions[part].p_size * 6941.1Sad (sc->sc_dk.dk_label->d_secsize / DEV_BSIZE); 6951.1Sad if (omask == 0 && ldclose(dev, 0, S_IFBLK, NULL) != 0) 6961.1Sad return (-1); 6971.1Sad 6981.1Sad return (size); 6991.1Sad} 7001.1Sad 7011.1Sad/* 7021.1Sad * Load the label information from the specified device. 7031.1Sad */ 7041.1Sadstatic void 7051.1Sadldgetdisklabel(struct ld_softc *sc) 7061.1Sad{ 7071.1Sad const char *errstring; 7081.1Sad 7091.1Sad ldgetdefaultlabel(sc, sc->sc_dk.dk_label); 7101.1Sad 7111.1Sad /* Call the generic disklabel extraction routine. */ 7121.37.2.1Syamt errstring = readdisklabel(MAKEDISKDEV(0, device_unit(&sc->sc_dv), 7131.37.2.1Syamt RAW_PART), ldstrategy, sc->sc_dk.dk_label, sc->sc_dk.dk_cpulabel); 7141.1Sad if (errstring != NULL) 7151.1Sad printf("%s: %s\n", sc->sc_dv.dv_xname, errstring); 7161.22Sthorpej 7171.22Sthorpej /* In-core label now valid. */ 7181.22Sthorpej sc->sc_flags |= LDF_VLABEL; 7191.1Sad} 7201.1Sad 7211.1Sad/* 7221.1Sad * Construct a ficticious label. 7231.1Sad */ 7241.1Sadstatic void 7251.1Sadldgetdefaultlabel(struct ld_softc *sc, struct disklabel *lp) 7261.1Sad{ 7271.1Sad 7281.1Sad memset(lp, 0, sizeof(struct disklabel)); 7291.1Sad 7301.1Sad lp->d_secsize = sc->sc_secsize; 7311.1Sad lp->d_ntracks = sc->sc_nheads; 7321.1Sad lp->d_nsectors = sc->sc_nsectors; 7331.1Sad lp->d_ncylinders = sc->sc_ncylinders; 7341.1Sad lp->d_secpercyl = lp->d_ntracks * lp->d_nsectors; 7351.1Sad lp->d_type = DTYPE_LD; 7361.21Sitojun strlcpy(lp->d_typename, "unknown", sizeof(lp->d_typename)); 7371.21Sitojun strlcpy(lp->d_packname, "fictitious", sizeof(lp->d_packname)); 7381.1Sad lp->d_secperunit = sc->sc_secperunit; 7391.1Sad lp->d_rpm = 7200; 7401.1Sad lp->d_interleave = 1; 7411.1Sad lp->d_flags = 0; 7421.1Sad 7431.1Sad lp->d_partitions[RAW_PART].p_offset = 0; 7441.1Sad lp->d_partitions[RAW_PART].p_size = 7451.1Sad lp->d_secperunit * (lp->d_secsize / DEV_BSIZE); 7461.1Sad lp->d_partitions[RAW_PART].p_fstype = FS_UNUSED; 7471.1Sad lp->d_npartitions = RAW_PART + 1; 7481.1Sad 7491.1Sad lp->d_magic = DISKMAGIC; 7501.1Sad lp->d_magic2 = DISKMAGIC; 7511.1Sad lp->d_checksum = dkcksum(lp); 7521.1Sad} 7531.1Sad 7541.1Sad/* 7551.1Sad * Take a dump. 7561.1Sad */ 7571.29Sthorpejstatic int 7581.1Sadlddump(dev_t dev, daddr_t blkno, caddr_t va, size_t size) 7591.1Sad{ 7601.1Sad struct ld_softc *sc; 7611.1Sad struct disklabel *lp; 7621.1Sad int unit, part, nsects, sectoff, towrt, nblk, maxblkcnt, rv; 7631.1Sad static int dumping; 7641.1Sad 7651.1Sad unit = DISKUNIT(dev); 7661.1Sad if ((sc = device_lookup(&ld_cd, unit)) == NULL) 7671.1Sad return (ENXIO); 7681.1Sad if ((sc->sc_flags & LDF_ENABLED) == 0) 7691.1Sad return (ENODEV); 7701.3Sad if (sc->sc_dump == NULL) 7711.3Sad return (ENXIO); 7721.3Sad 7731.3Sad /* Check if recursive dump; if so, punt. */ 7741.3Sad if (dumping) 7751.3Sad return (EFAULT); 7761.3Sad dumping = 1; 7771.1Sad 7781.1Sad /* Convert to disk sectors. Request must be a multiple of size. */ 7791.3Sad part = DISKPART(dev); 7801.1Sad lp = sc->sc_dk.dk_label; 7811.1Sad if ((size % lp->d_secsize) != 0) 7821.1Sad return (EFAULT); 7831.1Sad towrt = size / lp->d_secsize; 7841.1Sad blkno = dbtob(blkno) / lp->d_secsize; /* blkno in DEV_BSIZE units */ 7851.1Sad 7861.1Sad nsects = lp->d_partitions[part].p_size; 7871.1Sad sectoff = lp->d_partitions[part].p_offset; 7881.1Sad 7891.1Sad /* Check transfer bounds against partition size. */ 7901.1Sad if ((blkno < 0) || ((blkno + towrt) > nsects)) 7911.1Sad return (EINVAL); 7921.1Sad 7931.1Sad /* Offset block number to start of partition. */ 7941.1Sad blkno += sectoff; 7951.1Sad 7961.1Sad /* Start dumping and return when done. */ 7971.3Sad maxblkcnt = sc->sc_maxxfer / sc->sc_secsize - 1; 7981.1Sad while (towrt > 0) { 7991.3Sad nblk = min(maxblkcnt, towrt); 8001.1Sad 8011.1Sad if ((rv = (*sc->sc_dump)(sc, va, blkno, nblk)) != 0) 8021.1Sad return (rv); 8031.1Sad 8041.1Sad towrt -= nblk; 8051.1Sad blkno += nblk; 8061.1Sad va += nblk * sc->sc_secsize; 8071.1Sad } 8081.1Sad 8091.1Sad dumping = 0; 8101.1Sad return (0); 8111.1Sad} 8121.1Sad 8131.1Sad/* 8141.1Sad * Adjust the size of a transfer. 8151.1Sad */ 8161.1Sadstatic void 8171.1Sadldminphys(struct buf *bp) 8181.1Sad{ 8191.1Sad struct ld_softc *sc; 8201.1Sad 8211.1Sad sc = device_lookup(&ld_cd, DISKUNIT(bp->b_dev)); 8221.1Sad 8231.1Sad if (bp->b_bcount > sc->sc_maxxfer) 8241.1Sad bp->b_bcount = sc->sc_maxxfer; 8251.1Sad minphys(bp); 8261.1Sad} 827