ld.c revision 1.37.2.2
11.37.2.2Syamt/* $NetBSD: ld.c,v 1.37.2.2 2007/02/26 09:09:54 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.2Syamt__KERNEL_RCSID(0, "$NetBSD: ld.c,v 1.37.2.2 2007/02/26 09:09:54 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.37.2.2Syamt#include <sys/mutex.h> 671.1Sad#if NRND > 0 681.1Sad#include <sys/rnd.h> 691.1Sad#endif 701.1Sad 711.1Sad#include <dev/ldvar.h> 721.1Sad 731.37.2.2Syamt#include <prop/proplib.h> 741.37.2.2Syamt 751.1Sadstatic void ldgetdefaultlabel(struct ld_softc *, struct disklabel *); 761.1Sadstatic void ldgetdisklabel(struct ld_softc *); 771.1Sadstatic void ldminphys(struct buf *bp); 781.1Sadstatic void ldshutdown(void *); 791.37.2.2Syamtstatic void ldstart(struct ld_softc *, struct buf *); 801.37.2.2Syamtstatic void ld_set_properties(struct ld_softc *); 811.37.2.2Syamtstatic void ld_config_interrupts (struct device *); 821.1Sad 831.1Sadextern struct cfdriver ld_cd; 841.1Sad 851.29Sthorpejstatic dev_type_open(ldopen); 861.29Sthorpejstatic dev_type_close(ldclose); 871.29Sthorpejstatic dev_type_read(ldread); 881.29Sthorpejstatic dev_type_write(ldwrite); 891.29Sthorpejstatic dev_type_ioctl(ldioctl); 901.29Sthorpejstatic dev_type_strategy(ldstrategy); 911.29Sthorpejstatic dev_type_dump(lddump); 921.29Sthorpejstatic dev_type_size(ldsize); 931.16Sgehenna 941.16Sgehennaconst struct bdevsw ld_bdevsw = { 951.16Sgehenna ldopen, ldclose, ldstrategy, ldioctl, lddump, ldsize, D_DISK 961.16Sgehenna}; 971.16Sgehenna 981.16Sgehennaconst struct cdevsw ld_cdevsw = { 991.16Sgehenna ldopen, ldclose, ldread, ldwrite, ldioctl, 1001.17Sjdolecek nostop, notty, nopoll, nommap, nokqfilter, D_DISK 1011.16Sgehenna}; 1021.16Sgehenna 1031.30Sthorpejstatic struct dkdriver lddkdriver = { ldstrategy, ldminphys }; 1041.1Sadstatic void *ld_sdh; 1051.1Sad 1061.1Sadvoid 1071.1Sadldattach(struct ld_softc *sc) 1081.1Sad{ 1091.37Schristos char tbuf[9]; 1101.1Sad 1111.37.2.2Syamt mutex_init(&sc->sc_mutex, MUTEX_DRIVER, IPL_BIO); 1121.37.2.2Syamt 1131.7Sad if ((sc->sc_flags & LDF_ENABLED) == 0) { 1141.34Sbriggs aprint_normal("%s: disabled\n", sc->sc_dv.dv_xname); 1151.7Sad return; 1161.7Sad } 1171.7Sad 1181.1Sad /* Initialise and attach the disk structure. */ 1191.1Sad sc->sc_dk.dk_driver = &lddkdriver; 1201.1Sad sc->sc_dk.dk_name = sc->sc_dv.dv_xname; 1211.1Sad disk_attach(&sc->sc_dk); 1221.1Sad 1231.1Sad if (sc->sc_maxxfer > MAXPHYS) 1241.1Sad sc->sc_maxxfer = MAXPHYS; 1251.9Sad 1261.19Sthorpej /* Build synthetic geometry if necessary. */ 1271.19Sthorpej if (sc->sc_nheads == 0 || sc->sc_nsectors == 0 || 1281.19Sthorpej sc->sc_ncylinders == 0) { 1291.28Sdbj uint64_t ncyl; 1301.28Sdbj 1311.19Sthorpej if (sc->sc_secperunit <= 528 * 2048) /* 528MB */ 1321.19Sthorpej sc->sc_nheads = 16; 1331.19Sthorpej else if (sc->sc_secperunit <= 1024 * 2048) /* 1GB */ 1341.19Sthorpej sc->sc_nheads = 32; 1351.19Sthorpej else if (sc->sc_secperunit <= 21504 * 2048) /* 21GB */ 1361.19Sthorpej sc->sc_nheads = 64; 1371.19Sthorpej else if (sc->sc_secperunit <= 43008 * 2048) /* 42GB */ 1381.19Sthorpej sc->sc_nheads = 128; 1391.19Sthorpej else 1401.19Sthorpej sc->sc_nheads = 255; 1411.19Sthorpej 1421.19Sthorpej sc->sc_nsectors = 63; 1431.28Sdbj sc->sc_ncylinders = INT_MAX; 1441.35Sperry ncyl = sc->sc_secperunit / 1451.19Sthorpej (sc->sc_nheads * sc->sc_nsectors); 1461.28Sdbj if (ncyl < INT_MAX) 1471.28Sdbj sc->sc_ncylinders = (int)ncyl; 1481.19Sthorpej } 1491.1Sad 1501.37Schristos format_bytes(tbuf, sizeof(tbuf), sc->sc_secperunit * 1511.1Sad sc->sc_secsize); 1521.34Sbriggs aprint_normal("%s: %s, %d cyl, %d head, %d sec, %d bytes/sect x %"PRIu64" sectors\n", 1531.37Schristos sc->sc_dv.dv_xname, tbuf, sc->sc_ncylinders, sc->sc_nheads, 1541.1Sad sc->sc_nsectors, sc->sc_secsize, sc->sc_secperunit); 1551.1Sad 1561.37.2.2Syamt ld_set_properties(sc); 1571.37.2.2Syamt 1581.1Sad#if NRND > 0 1591.1Sad /* Attach the device into the rnd source list. */ 1601.1Sad rnd_attach_source(&sc->sc_rnd_source, sc->sc_dv.dv_xname, 1611.1Sad RND_TYPE_DISK, 0); 1621.1Sad#endif 1631.1Sad 1641.1Sad /* Set the `shutdownhook'. */ 1651.1Sad if (ld_sdh == NULL) 1661.1Sad ld_sdh = shutdownhook_establish(ldshutdown, NULL); 1671.37.2.1Syamt bufq_alloc(&sc->sc_bufq, BUFQ_DISK_DEFAULT_STRAT, BUFQ_SORT_RAWBLOCK); 1681.30Sthorpej 1691.30Sthorpej /* Discover wedges on this disk. */ 1701.37.2.2Syamt config_interrupts(&sc->sc_dv, ld_config_interrupts); 1711.1Sad} 1721.1Sad 1731.3Sadint 1741.37Schristosldadjqparam(struct ld_softc *sc, int xmax) 1751.3Sad{ 1761.24Sthorpej int s; 1771.7Sad 1781.7Sad s = splbio(); 1791.37Schristos sc->sc_maxqueuecnt = xmax; 1801.7Sad splx(s); 1811.7Sad 1821.24Sthorpej return (0); 1831.7Sad} 1841.7Sad 1851.7Sadint 1861.7Sadldbegindetach(struct ld_softc *sc, int flags) 1871.7Sad{ 1881.24Sthorpej int s, rv = 0; 1891.7Sad 1901.7Sad if ((sc->sc_flags & LDF_ENABLED) == 0) 1911.7Sad return (0); 1921.3Sad 1931.3Sad if ((flags & DETACH_FORCE) == 0 && sc->sc_dk.dk_openmask != 0) 1941.3Sad return (EBUSY); 1951.3Sad 1961.3Sad s = splbio(); 1971.24Sthorpej sc->sc_maxqueuecnt = 0; 1981.7Sad sc->sc_flags |= LDF_DETACH; 1991.24Sthorpej while (sc->sc_queuecnt > 0) { 2001.24Sthorpej sc->sc_flags |= LDF_DRAIN; 2011.24Sthorpej rv = tsleep(&sc->sc_queuecnt, PRIBIO, "lddrn", 0); 2021.24Sthorpej if (rv) 2031.24Sthorpej break; 2041.24Sthorpej } 2051.3Sad splx(s); 2061.7Sad 2071.7Sad return (rv); 2081.3Sad} 2091.3Sad 2101.2Sadvoid 2111.7Sadldenddetach(struct ld_softc *sc) 2121.2Sad{ 2131.13Sdrochner int s, bmaj, cmaj, i, mn; 2141.2Sad 2151.7Sad if ((sc->sc_flags & LDF_ENABLED) == 0) 2161.7Sad return; 2171.7Sad 2181.2Sad /* Wait for commands queued with the hardware to complete. */ 2191.2Sad if (sc->sc_queuecnt != 0) 2201.7Sad if (tsleep(&sc->sc_queuecnt, PRIBIO, "lddtch", 30 * hz)) 2211.7Sad printf("%s: not drained\n", sc->sc_dv.dv_xname); 2221.2Sad 2231.2Sad /* Locate the major numbers. */ 2241.16Sgehenna bmaj = bdevsw_lookup_major(&ld_bdevsw); 2251.16Sgehenna cmaj = cdevsw_lookup_major(&ld_cdevsw); 2261.2Sad 2271.2Sad /* Kill off any queued buffers. */ 2281.2Sad s = splbio(); 2291.37.2.1Syamt bufq_drain(sc->sc_bufq); 2301.36Syamt splx(s); 2311.36Syamt 2321.37.2.1Syamt bufq_free(sc->sc_bufq); 2331.2Sad 2341.2Sad /* Nuke the vnodes for any open instances. */ 2351.13Sdrochner for (i = 0; i < MAXPARTITIONS; i++) { 2361.37.2.1Syamt mn = DISKMINOR(device_unit(&sc->sc_dv), i); 2371.13Sdrochner vdevgone(bmaj, mn, mn, VBLK); 2381.13Sdrochner vdevgone(cmaj, mn, mn, VCHR); 2391.13Sdrochner } 2401.13Sdrochner 2411.30Sthorpej /* Delete all of our wedges. */ 2421.30Sthorpej dkwedge_delall(&sc->sc_dk); 2431.30Sthorpej 2441.2Sad /* Detach from the disk list. */ 2451.2Sad disk_detach(&sc->sc_dk); 2461.2Sad 2471.2Sad#if NRND > 0 2481.2Sad /* Unhook the entropy source. */ 2491.2Sad rnd_detach_source(&sc->sc_rnd_source); 2501.2Sad#endif 2511.2Sad 2521.24Sthorpej /* 2531.24Sthorpej * XXX We can't really flush the cache here, beceause the 2541.24Sthorpej * XXX device may already be non-existent from the controller's 2551.24Sthorpej * XXX perspective. 2561.24Sthorpej */ 2571.24Sthorpej#if 0 2581.2Sad /* Flush the device's cache. */ 2591.2Sad if (sc->sc_flush != NULL) 2601.2Sad if ((*sc->sc_flush)(sc) != 0) 2611.2Sad printf("%s: unable to flush cache\n", 2621.2Sad sc->sc_dv.dv_xname); 2631.24Sthorpej#endif 2641.2Sad} 2651.2Sad 2661.8Slukem/* ARGSUSED */ 2671.1Sadstatic void 2681.1Sadldshutdown(void *cookie) 2691.1Sad{ 2701.1Sad struct ld_softc *sc; 2711.1Sad int i; 2721.1Sad 2731.1Sad for (i = 0; i < ld_cd.cd_ndevs; i++) { 2741.1Sad if ((sc = device_lookup(&ld_cd, i)) == NULL) 2751.1Sad continue; 2761.1Sad if (sc->sc_flush != NULL && (*sc->sc_flush)(sc) != 0) 2771.1Sad printf("%s: unable to flush cache\n", 2781.1Sad sc->sc_dv.dv_xname); 2791.1Sad } 2801.1Sad} 2811.1Sad 2821.8Slukem/* ARGSUSED */ 2831.29Sthorpejstatic int 2841.37.2.1Syamtldopen(dev_t dev, int flags, int fmt, struct lwp *l) 2851.1Sad{ 2861.1Sad struct ld_softc *sc; 2871.30Sthorpej int error, unit, part; 2881.1Sad 2891.1Sad unit = DISKUNIT(dev); 2901.30Sthorpej if ((sc = device_lookup(&ld_cd, unit)) == NULL) 2911.1Sad return (ENXIO); 2921.1Sad if ((sc->sc_flags & LDF_ENABLED) == 0) 2931.1Sad return (ENODEV); 2941.1Sad part = DISKPART(dev); 2951.30Sthorpej 2961.30Sthorpej if ((error = lockmgr(&sc->sc_dk.dk_openlock, LK_EXCLUSIVE, NULL)) != 0) 2971.30Sthorpej return (error); 2981.1Sad 2991.22Sthorpej if (sc->sc_dk.dk_openmask == 0) { 3001.22Sthorpej /* Load the partition info if not already loaded. */ 3011.22Sthorpej if ((sc->sc_flags & LDF_VLABEL) == 0) 3021.22Sthorpej ldgetdisklabel(sc); 3031.22Sthorpej } 3041.1Sad 3051.1Sad /* Check that the partition exists. */ 3061.1Sad if (part != RAW_PART && (part >= sc->sc_dk.dk_label->d_npartitions || 3071.1Sad sc->sc_dk.dk_label->d_partitions[part].p_fstype == FS_UNUSED)) { 3081.30Sthorpej error = ENXIO; 3091.30Sthorpej goto bad1; 3101.1Sad } 3111.1Sad 3121.1Sad /* Ensure only one open at a time. */ 3131.1Sad switch (fmt) { 3141.1Sad case S_IFCHR: 3151.1Sad sc->sc_dk.dk_copenmask |= (1 << part); 3161.1Sad break; 3171.1Sad case S_IFBLK: 3181.1Sad sc->sc_dk.dk_bopenmask |= (1 << part); 3191.1Sad break; 3201.1Sad } 3211.1Sad sc->sc_dk.dk_openmask = 3221.1Sad sc->sc_dk.dk_copenmask | sc->sc_dk.dk_bopenmask; 3231.1Sad 3241.30Sthorpej (void) lockmgr(&sc->sc_dk.dk_openlock, LK_RELEASE, NULL); 3251.1Sad return (0); 3261.30Sthorpej 3271.30Sthorpej bad1: 3281.30Sthorpej (void) lockmgr(&sc->sc_dk.dk_openlock, LK_RELEASE, NULL); 3291.30Sthorpej return (error); 3301.1Sad} 3311.1Sad 3321.8Slukem/* ARGSUSED */ 3331.29Sthorpejstatic int 3341.37.2.1Syamtldclose(dev_t dev, int flags, int fmt, struct lwp *l) 3351.1Sad{ 3361.1Sad struct ld_softc *sc; 3371.30Sthorpej int error, part, unit; 3381.1Sad 3391.1Sad unit = DISKUNIT(dev); 3401.1Sad part = DISKPART(dev); 3411.1Sad sc = device_lookup(&ld_cd, unit); 3421.30Sthorpej 3431.30Sthorpej if ((error = lockmgr(&sc->sc_dk.dk_openlock, LK_EXCLUSIVE, NULL)) != 0) 3441.30Sthorpej return (error); 3451.1Sad 3461.1Sad switch (fmt) { 3471.1Sad case S_IFCHR: 3481.1Sad sc->sc_dk.dk_copenmask &= ~(1 << part); 3491.1Sad break; 3501.1Sad case S_IFBLK: 3511.1Sad sc->sc_dk.dk_bopenmask &= ~(1 << part); 3521.1Sad break; 3531.1Sad } 3541.1Sad sc->sc_dk.dk_openmask = 3551.1Sad sc->sc_dk.dk_copenmask | sc->sc_dk.dk_bopenmask; 3561.1Sad 3571.22Sthorpej if (sc->sc_dk.dk_openmask == 0) { 3581.22Sthorpej if (sc->sc_flush != NULL && (*sc->sc_flush)(sc) != 0) 3591.1Sad printf("%s: unable to flush cache\n", 3601.1Sad sc->sc_dv.dv_xname); 3611.22Sthorpej if ((sc->sc_flags & LDF_KLABEL) == 0) 3621.22Sthorpej sc->sc_flags &= ~LDF_VLABEL; 3631.22Sthorpej } 3641.1Sad 3651.30Sthorpej (void) lockmgr(&sc->sc_dk.dk_openlock, LK_RELEASE, NULL); 3661.1Sad return (0); 3671.1Sad} 3681.1Sad 3691.8Slukem/* ARGSUSED */ 3701.29Sthorpejstatic int 3711.1Sadldread(dev_t dev, struct uio *uio, int ioflag) 3721.1Sad{ 3731.1Sad 3741.1Sad return (physio(ldstrategy, NULL, dev, B_READ, ldminphys, uio)); 3751.1Sad} 3761.1Sad 3771.8Slukem/* ARGSUSED */ 3781.29Sthorpejstatic int 3791.1Sadldwrite(dev_t dev, struct uio *uio, int ioflag) 3801.1Sad{ 3811.1Sad 3821.1Sad return (physio(ldstrategy, NULL, dev, B_WRITE, ldminphys, uio)); 3831.1Sad} 3841.1Sad 3851.8Slukem/* ARGSUSED */ 3861.29Sthorpejstatic int 3871.37.2.1Syamtldioctl(dev_t dev, u_long cmd, caddr_t addr, int32_t flag, struct lwp *l) 3881.1Sad{ 3891.1Sad struct ld_softc *sc; 3901.1Sad int part, unit, error; 3911.4Sfvdl#ifdef __HAVE_OLD_DISKLABEL 3921.6Sitojun struct disklabel newlabel; 3931.4Sfvdl#endif 3941.6Sitojun struct disklabel *lp; 3951.1Sad 3961.1Sad unit = DISKUNIT(dev); 3971.1Sad part = DISKPART(dev); 3981.1Sad sc = device_lookup(&ld_cd, unit); 3991.1Sad error = 0; 4001.1Sad 4011.37.2.2Syamt error = disk_ioctl(&sc->sc_dk, cmd, addr, flag, l); 4021.37.2.2Syamt if (error != EPASSTHROUGH) 4031.37.2.2Syamt return (error); 4041.37.2.2Syamt 4051.1Sad switch (cmd) { 4061.1Sad case DIOCGDINFO: 4071.1Sad memcpy(addr, sc->sc_dk.dk_label, sizeof(struct disklabel)); 4081.1Sad return (0); 4091.1Sad 4101.4Sfvdl#ifdef __HAVE_OLD_DISKLABEL 4111.4Sfvdl case ODIOCGDINFO: 4121.4Sfvdl newlabel = *(sc->sc_dk.dk_label); 4131.4Sfvdl if (newlabel.d_npartitions > OLDMAXPARTITIONS) 4141.5Sfvdl return ENOTTY; 4151.4Sfvdl memcpy(addr, &newlabel, sizeof(struct olddisklabel)); 4161.4Sfvdl return (0); 4171.4Sfvdl#endif 4181.4Sfvdl 4191.1Sad case DIOCGPART: 4201.1Sad ((struct partinfo *)addr)->disklab = sc->sc_dk.dk_label; 4211.1Sad ((struct partinfo *)addr)->part = 4221.1Sad &sc->sc_dk.dk_label->d_partitions[part]; 4231.1Sad break; 4241.1Sad 4251.1Sad case DIOCWDINFO: 4261.1Sad case DIOCSDINFO: 4271.4Sfvdl#ifdef __HAVE_OLD_DISKLABEL 4281.4Sfvdl case ODIOCWDINFO: 4291.4Sfvdl case ODIOCSDINFO: 4301.4Sfvdl 4311.4Sfvdl if (cmd == ODIOCSDINFO || cmd == ODIOCWDINFO) { 4321.4Sfvdl memset(&newlabel, 0, sizeof newlabel); 4331.4Sfvdl memcpy(&newlabel, addr, sizeof (struct olddisklabel)); 4341.4Sfvdl lp = &newlabel; 4351.4Sfvdl } else 4361.4Sfvdl#endif 4371.4Sfvdl lp = (struct disklabel *)addr; 4381.4Sfvdl 4391.1Sad if ((flag & FWRITE) == 0) 4401.1Sad return (EBADF); 4411.1Sad 4421.30Sthorpej if ((error = lockmgr(&sc->sc_dk.dk_openlock, LK_EXCLUSIVE, 4431.31Sjdolecek NULL)) != 0) 4441.1Sad return (error); 4451.1Sad sc->sc_flags |= LDF_LABELLING; 4461.1Sad 4471.1Sad error = setdisklabel(sc->sc_dk.dk_label, 4481.4Sfvdl lp, /*sc->sc_dk.dk_openmask : */0, 4491.1Sad sc->sc_dk.dk_cpulabel); 4501.4Sfvdl if (error == 0 && (cmd == DIOCWDINFO 4511.4Sfvdl#ifdef __HAVE_OLD_DISKLABEL 4521.4Sfvdl || cmd == ODIOCWDINFO 4531.4Sfvdl#endif 4541.4Sfvdl )) 4551.1Sad error = writedisklabel( 4561.35Sperry MAKEDISKDEV(major(dev), DISKUNIT(dev), RAW_PART), 4571.35Sperry ldstrategy, sc->sc_dk.dk_label, 4581.1Sad sc->sc_dk.dk_cpulabel); 4591.1Sad 4601.1Sad sc->sc_flags &= ~LDF_LABELLING; 4611.30Sthorpej (void) lockmgr(&sc->sc_dk.dk_openlock, LK_RELEASE, NULL); 4621.1Sad break; 4631.1Sad 4641.22Sthorpej case DIOCKLABEL: 4651.22Sthorpej if ((flag & FWRITE) == 0) 4661.22Sthorpej return (EBADF); 4671.22Sthorpej if (*(int *)addr) 4681.22Sthorpej sc->sc_flags |= LDF_KLABEL; 4691.22Sthorpej else 4701.22Sthorpej sc->sc_flags &= ~LDF_KLABEL; 4711.22Sthorpej break; 4721.22Sthorpej 4731.1Sad case DIOCWLABEL: 4741.1Sad if ((flag & FWRITE) == 0) 4751.1Sad return (EBADF); 4761.1Sad if (*(int *)addr) 4771.1Sad sc->sc_flags |= LDF_WLABEL; 4781.1Sad else 4791.1Sad sc->sc_flags &= ~LDF_WLABEL; 4801.1Sad break; 4811.1Sad 4821.1Sad case DIOCGDEFLABEL: 4831.1Sad ldgetdefaultlabel(sc, (struct disklabel *)addr); 4841.1Sad break; 4851.4Sfvdl 4861.4Sfvdl#ifdef __HAVE_OLD_DISKLABEL 4871.4Sfvdl case ODIOCGDEFLABEL: 4881.4Sfvdl ldgetdefaultlabel(sc, &newlabel); 4891.4Sfvdl if (newlabel.d_npartitions > OLDMAXPARTITIONS) 4901.5Sfvdl return ENOTTY; 4911.4Sfvdl memcpy(addr, &newlabel, sizeof (struct olddisklabel)); 4921.4Sfvdl break; 4931.4Sfvdl#endif 4941.1Sad 4951.32Sthorpej case DIOCCACHESYNC: 4961.32Sthorpej /* 4971.32Sthorpej * XXX Do we really need to care about having a writable 4981.32Sthorpej * file descriptor here? 4991.32Sthorpej */ 5001.32Sthorpej if ((flag & FWRITE) == 0) 5011.32Sthorpej error = EBADF; 5021.32Sthorpej else if (sc->sc_flush) 5031.32Sthorpej error = (*sc->sc_flush)(sc); 5041.32Sthorpej else 5051.32Sthorpej error = 0; /* XXX Error out instead? */ 5061.32Sthorpej break; 5071.32Sthorpej 5081.30Sthorpej case DIOCAWEDGE: 5091.30Sthorpej { 5101.30Sthorpej struct dkwedge_info *dkw = (void *) addr; 5111.30Sthorpej 5121.30Sthorpej if ((flag & FWRITE) == 0) 5131.30Sthorpej return (EBADF); 5141.30Sthorpej 5151.30Sthorpej /* If the ioctl happens here, the parent is us. */ 5161.30Sthorpej strcpy(dkw->dkw_parent, sc->sc_dv.dv_xname); 5171.30Sthorpej return (dkwedge_add(dkw)); 5181.30Sthorpej } 5191.35Sperry 5201.30Sthorpej case DIOCDWEDGE: 5211.30Sthorpej { 5221.30Sthorpej struct dkwedge_info *dkw = (void *) addr; 5231.30Sthorpej 5241.30Sthorpej if ((flag & FWRITE) == 0) 5251.30Sthorpej return (EBADF); 5261.30Sthorpej 5271.30Sthorpej /* If the ioctl happens here, the parent is us. */ 5281.30Sthorpej strcpy(dkw->dkw_parent, sc->sc_dv.dv_xname); 5291.30Sthorpej return (dkwedge_del(dkw)); 5301.30Sthorpej } 5311.35Sperry 5321.30Sthorpej case DIOCLWEDGES: 5331.30Sthorpej { 5341.30Sthorpej struct dkwedge_list *dkwl = (void *) addr; 5351.30Sthorpej 5361.37.2.1Syamt return (dkwedge_list(&sc->sc_dk, dkwl, l)); 5371.30Sthorpej } 5381.30Sthorpej 5391.1Sad default: 5401.1Sad error = ENOTTY; 5411.1Sad break; 5421.1Sad } 5431.1Sad 5441.1Sad return (error); 5451.1Sad} 5461.1Sad 5471.29Sthorpejstatic void 5481.1Sadldstrategy(struct buf *bp) 5491.1Sad{ 5501.1Sad struct ld_softc *sc; 5511.23Sthorpej struct disklabel *lp; 5521.23Sthorpej daddr_t blkno; 5531.23Sthorpej int s, part; 5541.1Sad 5551.1Sad sc = device_lookup(&ld_cd, DISKUNIT(bp->b_dev)); 5561.23Sthorpej part = DISKPART(bp->b_dev); 5571.1Sad 5581.7Sad if ((sc->sc_flags & LDF_DETACH) != 0) { 5591.2Sad bp->b_error = EIO; 5601.23Sthorpej goto bad; 5611.2Sad } 5621.2Sad 5631.1Sad lp = sc->sc_dk.dk_label; 5641.1Sad 5651.1Sad /* 5661.1Sad * The transfer must be a whole number of blocks and the offset must 5671.1Sad * not be negative. 5681.1Sad */ 5691.1Sad if ((bp->b_bcount % lp->d_secsize) != 0 || bp->b_blkno < 0) { 5701.23Sthorpej bp->b_error = EINVAL; 5711.23Sthorpej goto bad; 5721.1Sad } 5731.1Sad 5741.23Sthorpej /* If it's a null transfer, return immediately. */ 5751.23Sthorpej if (bp->b_bcount == 0) 5761.23Sthorpej goto done; 5771.1Sad 5781.1Sad /* 5791.1Sad * Do bounds checking and adjust the transfer. If error, process. 5801.1Sad * If past the end of partition, just return. 5811.1Sad */ 5821.1Sad if (part != RAW_PART && 5831.20Sthorpej bounds_check_with_label(&sc->sc_dk, bp, 5841.1Sad (sc->sc_flags & (LDF_WLABEL | LDF_LABELLING)) != 0) <= 0) { 5851.23Sthorpej goto done; 5861.1Sad } 5871.1Sad 5881.1Sad /* 5891.23Sthorpej * Convert the block number to absolute and put it in terms 5901.23Sthorpej * of the device's logical block size. 5911.1Sad */ 5921.23Sthorpej if (lp->d_secsize == DEV_BSIZE) 5931.23Sthorpej blkno = bp->b_blkno; 5941.23Sthorpej else if (lp->d_secsize > DEV_BSIZE) 5951.23Sthorpej blkno = bp->b_blkno / (lp->d_secsize / DEV_BSIZE); 5961.1Sad else 5971.23Sthorpej blkno = bp->b_blkno * (DEV_BSIZE / lp->d_secsize); 5981.1Sad 5991.11Sthorpej if (part != RAW_PART) 6001.23Sthorpej blkno += lp->d_partitions[part].p_offset; 6011.23Sthorpej 6021.23Sthorpej bp->b_rawblkno = blkno; 6031.1Sad 6041.1Sad s = splbio(); 6051.37.2.2Syamt ldstart(sc, bp); 6061.1Sad splx(s); 6071.23Sthorpej return; 6081.23Sthorpej 6091.23Sthorpej bad: 6101.23Sthorpej bp->b_flags |= B_ERROR; 6111.23Sthorpej done: 6121.23Sthorpej bp->b_resid = bp->b_bcount; 6131.23Sthorpej biodone(bp); 6141.23Sthorpej} 6151.23Sthorpej 6161.23Sthorpejstatic void 6171.37.2.2Syamtldstart(struct ld_softc *sc, struct buf *bp) 6181.23Sthorpej{ 6191.23Sthorpej int error; 6201.1Sad 6211.37.2.2Syamt mutex_enter(&sc->sc_mutex); 6221.37.2.2Syamt 6231.37.2.2Syamt if (bp != NULL) 6241.37.2.2Syamt BUFQ_PUT(sc->sc_bufq, bp); 6251.37.2.2Syamt 6261.23Sthorpej while (sc->sc_queuecnt < sc->sc_maxqueuecnt) { 6271.23Sthorpej /* See if there is work to do. */ 6281.37.2.1Syamt if ((bp = BUFQ_PEEK(sc->sc_bufq)) == NULL) 6291.23Sthorpej break; 6301.23Sthorpej 6311.23Sthorpej disk_busy(&sc->sc_dk); 6321.23Sthorpej sc->sc_queuecnt++; 6331.23Sthorpej 6341.23Sthorpej if (__predict_true((error = (*sc->sc_start)(sc, bp)) == 0)) { 6351.23Sthorpej /* 6361.23Sthorpej * The back-end is running the job; remove it from 6371.23Sthorpej * the queue. 6381.23Sthorpej */ 6391.37.2.1Syamt (void) BUFQ_GET(sc->sc_bufq); 6401.23Sthorpej } else { 6411.23Sthorpej disk_unbusy(&sc->sc_dk, 0, (bp->b_flags & B_READ)); 6421.23Sthorpej sc->sc_queuecnt--; 6431.23Sthorpej if (error == EAGAIN) { 6441.23Sthorpej /* 6451.23Sthorpej * Temporary resource shortage in the 6461.23Sthorpej * back-end; just defer the job until 6471.23Sthorpej * later. 6481.23Sthorpej * 6491.23Sthorpej * XXX We might consider a watchdog timer 6501.23Sthorpej * XXX to make sure we are kicked into action. 6511.23Sthorpej */ 6521.23Sthorpej break; 6531.23Sthorpej } else { 6541.37.2.1Syamt (void) BUFQ_GET(sc->sc_bufq); 6551.23Sthorpej bp->b_error = error; 6561.23Sthorpej bp->b_flags |= B_ERROR; 6571.23Sthorpej bp->b_resid = bp->b_bcount; 6581.37.2.2Syamt mutex_exit(&sc->sc_mutex); 6591.23Sthorpej biodone(bp); 6601.37.2.2Syamt mutex_enter(&sc->sc_mutex); 6611.23Sthorpej } 6621.23Sthorpej } 6631.1Sad } 6641.37.2.2Syamt 6651.37.2.2Syamt mutex_exit(&sc->sc_mutex); 6661.1Sad} 6671.1Sad 6681.1Sadvoid 6691.1Sadlddone(struct ld_softc *sc, struct buf *bp) 6701.1Sad{ 6711.1Sad 6721.1Sad if ((bp->b_flags & B_ERROR) != 0) { 6731.1Sad diskerr(bp, "ld", "error", LOG_PRINTF, 0, sc->sc_dk.dk_label); 6741.1Sad printf("\n"); 6751.1Sad } 6761.1Sad 6771.18Smrg disk_unbusy(&sc->sc_dk, bp->b_bcount - bp->b_resid, 6781.18Smrg (bp->b_flags & B_READ)); 6791.1Sad#if NRND > 0 6801.1Sad rnd_add_uint32(&sc->sc_rnd_source, bp->b_rawblkno); 6811.1Sad#endif 6821.1Sad biodone(bp); 6831.1Sad 6841.37.2.2Syamt mutex_enter(&sc->sc_mutex); 6851.7Sad if (--sc->sc_queuecnt <= sc->sc_maxqueuecnt) { 6861.24Sthorpej if ((sc->sc_flags & LDF_DRAIN) != 0) { 6871.24Sthorpej sc->sc_flags &= ~LDF_DRAIN; 6881.7Sad wakeup(&sc->sc_queuecnt); 6891.24Sthorpej } 6901.37.2.2Syamt mutex_exit(&sc->sc_mutex); 6911.37.2.2Syamt ldstart(sc, NULL); 6921.37.2.2Syamt } else 6931.37.2.2Syamt mutex_exit(&sc->sc_mutex); 6941.1Sad} 6951.1Sad 6961.29Sthorpejstatic int 6971.1Sadldsize(dev_t dev) 6981.1Sad{ 6991.1Sad struct ld_softc *sc; 7001.1Sad int part, unit, omask, size; 7011.1Sad 7021.1Sad unit = DISKUNIT(dev); 7031.1Sad if ((sc = device_lookup(&ld_cd, unit)) == NULL) 7041.1Sad return (ENODEV); 7051.1Sad if ((sc->sc_flags & LDF_ENABLED) == 0) 7061.1Sad return (ENODEV); 7071.1Sad part = DISKPART(dev); 7081.1Sad 7091.1Sad omask = sc->sc_dk.dk_openmask & (1 << part); 7101.1Sad 7111.1Sad if (omask == 0 && ldopen(dev, 0, S_IFBLK, NULL) != 0) 7121.1Sad return (-1); 7131.1Sad else if (sc->sc_dk.dk_label->d_partitions[part].p_fstype != FS_SWAP) 7141.1Sad size = -1; 7151.1Sad else 7161.1Sad size = sc->sc_dk.dk_label->d_partitions[part].p_size * 7171.1Sad (sc->sc_dk.dk_label->d_secsize / DEV_BSIZE); 7181.1Sad if (omask == 0 && ldclose(dev, 0, S_IFBLK, NULL) != 0) 7191.1Sad return (-1); 7201.1Sad 7211.1Sad return (size); 7221.1Sad} 7231.1Sad 7241.1Sad/* 7251.1Sad * Load the label information from the specified device. 7261.1Sad */ 7271.1Sadstatic void 7281.1Sadldgetdisklabel(struct ld_softc *sc) 7291.1Sad{ 7301.1Sad const char *errstring; 7311.1Sad 7321.1Sad ldgetdefaultlabel(sc, sc->sc_dk.dk_label); 7331.1Sad 7341.1Sad /* Call the generic disklabel extraction routine. */ 7351.37.2.1Syamt errstring = readdisklabel(MAKEDISKDEV(0, device_unit(&sc->sc_dv), 7361.37.2.1Syamt RAW_PART), ldstrategy, sc->sc_dk.dk_label, sc->sc_dk.dk_cpulabel); 7371.1Sad if (errstring != NULL) 7381.1Sad printf("%s: %s\n", sc->sc_dv.dv_xname, errstring); 7391.22Sthorpej 7401.22Sthorpej /* In-core label now valid. */ 7411.22Sthorpej sc->sc_flags |= LDF_VLABEL; 7421.1Sad} 7431.1Sad 7441.1Sad/* 7451.1Sad * Construct a ficticious label. 7461.1Sad */ 7471.1Sadstatic void 7481.1Sadldgetdefaultlabel(struct ld_softc *sc, struct disklabel *lp) 7491.1Sad{ 7501.1Sad 7511.1Sad memset(lp, 0, sizeof(struct disklabel)); 7521.1Sad 7531.1Sad lp->d_secsize = sc->sc_secsize; 7541.1Sad lp->d_ntracks = sc->sc_nheads; 7551.1Sad lp->d_nsectors = sc->sc_nsectors; 7561.1Sad lp->d_ncylinders = sc->sc_ncylinders; 7571.1Sad lp->d_secpercyl = lp->d_ntracks * lp->d_nsectors; 7581.1Sad lp->d_type = DTYPE_LD; 7591.21Sitojun strlcpy(lp->d_typename, "unknown", sizeof(lp->d_typename)); 7601.21Sitojun strlcpy(lp->d_packname, "fictitious", sizeof(lp->d_packname)); 7611.1Sad lp->d_secperunit = sc->sc_secperunit; 7621.1Sad lp->d_rpm = 7200; 7631.1Sad lp->d_interleave = 1; 7641.1Sad lp->d_flags = 0; 7651.1Sad 7661.1Sad lp->d_partitions[RAW_PART].p_offset = 0; 7671.1Sad lp->d_partitions[RAW_PART].p_size = 7681.1Sad lp->d_secperunit * (lp->d_secsize / DEV_BSIZE); 7691.1Sad lp->d_partitions[RAW_PART].p_fstype = FS_UNUSED; 7701.1Sad lp->d_npartitions = RAW_PART + 1; 7711.1Sad 7721.1Sad lp->d_magic = DISKMAGIC; 7731.1Sad lp->d_magic2 = DISKMAGIC; 7741.1Sad lp->d_checksum = dkcksum(lp); 7751.1Sad} 7761.1Sad 7771.1Sad/* 7781.1Sad * Take a dump. 7791.1Sad */ 7801.29Sthorpejstatic int 7811.1Sadlddump(dev_t dev, daddr_t blkno, caddr_t va, size_t size) 7821.1Sad{ 7831.1Sad struct ld_softc *sc; 7841.1Sad struct disklabel *lp; 7851.1Sad int unit, part, nsects, sectoff, towrt, nblk, maxblkcnt, rv; 7861.1Sad static int dumping; 7871.1Sad 7881.1Sad unit = DISKUNIT(dev); 7891.1Sad if ((sc = device_lookup(&ld_cd, unit)) == NULL) 7901.1Sad return (ENXIO); 7911.1Sad if ((sc->sc_flags & LDF_ENABLED) == 0) 7921.1Sad return (ENODEV); 7931.3Sad if (sc->sc_dump == NULL) 7941.3Sad return (ENXIO); 7951.3Sad 7961.3Sad /* Check if recursive dump; if so, punt. */ 7971.3Sad if (dumping) 7981.3Sad return (EFAULT); 7991.3Sad dumping = 1; 8001.1Sad 8011.1Sad /* Convert to disk sectors. Request must be a multiple of size. */ 8021.3Sad part = DISKPART(dev); 8031.1Sad lp = sc->sc_dk.dk_label; 8041.1Sad if ((size % lp->d_secsize) != 0) 8051.1Sad return (EFAULT); 8061.1Sad towrt = size / lp->d_secsize; 8071.1Sad blkno = dbtob(blkno) / lp->d_secsize; /* blkno in DEV_BSIZE units */ 8081.1Sad 8091.1Sad nsects = lp->d_partitions[part].p_size; 8101.1Sad sectoff = lp->d_partitions[part].p_offset; 8111.1Sad 8121.1Sad /* Check transfer bounds against partition size. */ 8131.1Sad if ((blkno < 0) || ((blkno + towrt) > nsects)) 8141.1Sad return (EINVAL); 8151.1Sad 8161.1Sad /* Offset block number to start of partition. */ 8171.1Sad blkno += sectoff; 8181.1Sad 8191.1Sad /* Start dumping and return when done. */ 8201.3Sad maxblkcnt = sc->sc_maxxfer / sc->sc_secsize - 1; 8211.1Sad while (towrt > 0) { 8221.3Sad nblk = min(maxblkcnt, towrt); 8231.1Sad 8241.1Sad if ((rv = (*sc->sc_dump)(sc, va, blkno, nblk)) != 0) 8251.1Sad return (rv); 8261.1Sad 8271.1Sad towrt -= nblk; 8281.1Sad blkno += nblk; 8291.1Sad va += nblk * sc->sc_secsize; 8301.1Sad } 8311.1Sad 8321.1Sad dumping = 0; 8331.1Sad return (0); 8341.1Sad} 8351.1Sad 8361.1Sad/* 8371.1Sad * Adjust the size of a transfer. 8381.1Sad */ 8391.1Sadstatic void 8401.1Sadldminphys(struct buf *bp) 8411.1Sad{ 8421.1Sad struct ld_softc *sc; 8431.1Sad 8441.1Sad sc = device_lookup(&ld_cd, DISKUNIT(bp->b_dev)); 8451.1Sad 8461.1Sad if (bp->b_bcount > sc->sc_maxxfer) 8471.1Sad bp->b_bcount = sc->sc_maxxfer; 8481.1Sad minphys(bp); 8491.1Sad} 8501.37.2.2Syamt 8511.37.2.2Syamtstatic void 8521.37.2.2Syamtld_set_properties(struct ld_softc *ld) 8531.37.2.2Syamt{ 8541.37.2.2Syamt prop_dictionary_t disk_info, odisk_info, geom; 8551.37.2.2Syamt 8561.37.2.2Syamt disk_info = prop_dictionary_create(); 8571.37.2.2Syamt 8581.37.2.2Syamt geom = prop_dictionary_create(); 8591.37.2.2Syamt 8601.37.2.2Syamt prop_dictionary_set_uint64(geom, "sectors-per-unit", 8611.37.2.2Syamt ld->sc_secperunit); 8621.37.2.2Syamt 8631.37.2.2Syamt prop_dictionary_set_uint32(geom, "sector-size", 8641.37.2.2Syamt ld->sc_secsize); 8651.37.2.2Syamt 8661.37.2.2Syamt prop_dictionary_set_uint16(geom, "sectors-per-track", 8671.37.2.2Syamt ld->sc_nsectors); 8681.37.2.2Syamt 8691.37.2.2Syamt prop_dictionary_set_uint16(geom, "tracks-per-cylinder", 8701.37.2.2Syamt ld->sc_nheads); 8711.37.2.2Syamt 8721.37.2.2Syamt prop_dictionary_set_uint64(geom, "cylinders-per-unit", 8731.37.2.2Syamt ld->sc_ncylinders); 8741.37.2.2Syamt 8751.37.2.2Syamt prop_dictionary_set(disk_info, "geometry", geom); 8761.37.2.2Syamt prop_object_release(geom); 8771.37.2.2Syamt 8781.37.2.2Syamt prop_dictionary_set(device_properties(&ld->sc_dv), 8791.37.2.2Syamt "disk-info", disk_info); 8801.37.2.2Syamt 8811.37.2.2Syamt /* 8821.37.2.2Syamt * Don't release disk_info here; we keep a reference to it. 8831.37.2.2Syamt * disk_detach() will release it when we go away. 8841.37.2.2Syamt */ 8851.37.2.2Syamt 8861.37.2.2Syamt odisk_info = ld->sc_dk.dk_info; 8871.37.2.2Syamt ld->sc_dk.dk_info = disk_info; 8881.37.2.2Syamt if (odisk_info) 8891.37.2.2Syamt prop_object_release(odisk_info); 8901.37.2.2Syamt} 8911.37.2.2Syamt 8921.37.2.2Syamtstatic void 8931.37.2.2Syamtld_config_interrupts (struct device *d) 8941.37.2.2Syamt{ 8951.37.2.2Syamt struct ld_softc *sc = (struct ld_softc *)d; 8961.37.2.2Syamt dkwedge_discover(&sc->sc_dk); 8971.37.2.2Syamt} 898