ld.c revision 1.37.2.7
11.37.2.7Syamt/* $NetBSD: ld.c,v 1.37.2.7 2008/01/21 09:42:26 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.7Syamt__KERNEL_RCSID(0, "$NetBSD: ld.c,v 1.37.2.7 2008/01/21 09:42:26 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/conf.h> 621.1Sad#include <sys/fcntl.h> 631.2Sad#include <sys/vnode.h> 641.1Sad#include <sys/syslog.h> 651.37.2.2Syamt#include <sys/mutex.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.37.2.2Syamt#include <prop/proplib.h> 731.37.2.2Syamt 741.1Sadstatic void ldgetdefaultlabel(struct ld_softc *, struct disklabel *); 751.1Sadstatic void ldgetdisklabel(struct ld_softc *); 761.1Sadstatic void ldminphys(struct buf *bp); 771.1Sadstatic void ldshutdown(void *); 781.37.2.2Syamtstatic void ldstart(struct ld_softc *, struct buf *); 791.37.2.2Syamtstatic void ld_set_properties(struct ld_softc *); 801.37.2.2Syamtstatic void ld_config_interrupts (struct device *); 811.1Sad 821.1Sadextern struct cfdriver ld_cd; 831.1Sad 841.29Sthorpejstatic dev_type_open(ldopen); 851.29Sthorpejstatic dev_type_close(ldclose); 861.29Sthorpejstatic dev_type_read(ldread); 871.29Sthorpejstatic dev_type_write(ldwrite); 881.29Sthorpejstatic dev_type_ioctl(ldioctl); 891.29Sthorpejstatic dev_type_strategy(ldstrategy); 901.29Sthorpejstatic dev_type_dump(lddump); 911.29Sthorpejstatic dev_type_size(ldsize); 921.16Sgehenna 931.16Sgehennaconst struct bdevsw ld_bdevsw = { 941.16Sgehenna ldopen, ldclose, ldstrategy, ldioctl, lddump, ldsize, D_DISK 951.16Sgehenna}; 961.16Sgehenna 971.16Sgehennaconst struct cdevsw ld_cdevsw = { 981.16Sgehenna ldopen, ldclose, ldread, ldwrite, ldioctl, 991.17Sjdolecek nostop, notty, nopoll, nommap, nokqfilter, D_DISK 1001.16Sgehenna}; 1011.16Sgehenna 1021.30Sthorpejstatic struct dkdriver lddkdriver = { ldstrategy, ldminphys }; 1031.1Sadstatic void *ld_sdh; 1041.1Sad 1051.1Sadvoid 1061.1Sadldattach(struct ld_softc *sc) 1071.1Sad{ 1081.37Schristos char tbuf[9]; 1091.1Sad 1101.37.2.6Syamt mutex_init(&sc->sc_mutex, MUTEX_DEFAULT, IPL_VM); 1111.37.2.2Syamt 1121.7Sad if ((sc->sc_flags & LDF_ENABLED) == 0) { 1131.34Sbriggs aprint_normal("%s: disabled\n", sc->sc_dv.dv_xname); 1141.7Sad return; 1151.7Sad } 1161.7Sad 1171.1Sad /* Initialise and attach the disk structure. */ 1181.37.2.4Syamt disk_init(&sc->sc_dk, sc->sc_dv.dv_xname, &lddkdriver); 1191.1Sad disk_attach(&sc->sc_dk); 1201.1Sad 1211.1Sad if (sc->sc_maxxfer > MAXPHYS) 1221.1Sad sc->sc_maxxfer = MAXPHYS; 1231.9Sad 1241.19Sthorpej /* Build synthetic geometry if necessary. */ 1251.19Sthorpej if (sc->sc_nheads == 0 || sc->sc_nsectors == 0 || 1261.19Sthorpej sc->sc_ncylinders == 0) { 1271.28Sdbj uint64_t ncyl; 1281.28Sdbj 1291.19Sthorpej if (sc->sc_secperunit <= 528 * 2048) /* 528MB */ 1301.19Sthorpej sc->sc_nheads = 16; 1311.19Sthorpej else if (sc->sc_secperunit <= 1024 * 2048) /* 1GB */ 1321.19Sthorpej sc->sc_nheads = 32; 1331.19Sthorpej else if (sc->sc_secperunit <= 21504 * 2048) /* 21GB */ 1341.19Sthorpej sc->sc_nheads = 64; 1351.19Sthorpej else if (sc->sc_secperunit <= 43008 * 2048) /* 42GB */ 1361.19Sthorpej sc->sc_nheads = 128; 1371.19Sthorpej else 1381.19Sthorpej sc->sc_nheads = 255; 1391.19Sthorpej 1401.19Sthorpej sc->sc_nsectors = 63; 1411.28Sdbj sc->sc_ncylinders = INT_MAX; 1421.35Sperry ncyl = sc->sc_secperunit / 1431.19Sthorpej (sc->sc_nheads * sc->sc_nsectors); 1441.28Sdbj if (ncyl < INT_MAX) 1451.28Sdbj sc->sc_ncylinders = (int)ncyl; 1461.19Sthorpej } 1471.1Sad 1481.37Schristos format_bytes(tbuf, sizeof(tbuf), sc->sc_secperunit * 1491.1Sad sc->sc_secsize); 1501.34Sbriggs aprint_normal("%s: %s, %d cyl, %d head, %d sec, %d bytes/sect x %"PRIu64" sectors\n", 1511.37Schristos sc->sc_dv.dv_xname, tbuf, sc->sc_ncylinders, sc->sc_nheads, 1521.1Sad sc->sc_nsectors, sc->sc_secsize, sc->sc_secperunit); 1531.1Sad 1541.37.2.2Syamt ld_set_properties(sc); 1551.37.2.2Syamt 1561.1Sad#if NRND > 0 1571.1Sad /* Attach the device into the rnd source list. */ 1581.1Sad rnd_attach_source(&sc->sc_rnd_source, sc->sc_dv.dv_xname, 1591.1Sad RND_TYPE_DISK, 0); 1601.1Sad#endif 1611.1Sad 1621.1Sad /* Set the `shutdownhook'. */ 1631.1Sad if (ld_sdh == NULL) 1641.1Sad ld_sdh = shutdownhook_establish(ldshutdown, NULL); 1651.37.2.1Syamt bufq_alloc(&sc->sc_bufq, BUFQ_DISK_DEFAULT_STRAT, BUFQ_SORT_RAWBLOCK); 1661.30Sthorpej 1671.30Sthorpej /* Discover wedges on this disk. */ 1681.37.2.2Syamt config_interrupts(&sc->sc_dv, ld_config_interrupts); 1691.1Sad} 1701.1Sad 1711.3Sadint 1721.37Schristosldadjqparam(struct ld_softc *sc, int xmax) 1731.3Sad{ 1741.24Sthorpej int s; 1751.7Sad 1761.7Sad s = splbio(); 1771.37Schristos sc->sc_maxqueuecnt = xmax; 1781.7Sad splx(s); 1791.7Sad 1801.24Sthorpej return (0); 1811.7Sad} 1821.7Sad 1831.7Sadint 1841.7Sadldbegindetach(struct ld_softc *sc, int flags) 1851.7Sad{ 1861.24Sthorpej int s, rv = 0; 1871.7Sad 1881.7Sad if ((sc->sc_flags & LDF_ENABLED) == 0) 1891.7Sad return (0); 1901.3Sad 1911.3Sad if ((flags & DETACH_FORCE) == 0 && sc->sc_dk.dk_openmask != 0) 1921.3Sad return (EBUSY); 1931.3Sad 1941.3Sad s = splbio(); 1951.24Sthorpej sc->sc_maxqueuecnt = 0; 1961.7Sad sc->sc_flags |= LDF_DETACH; 1971.24Sthorpej while (sc->sc_queuecnt > 0) { 1981.24Sthorpej sc->sc_flags |= LDF_DRAIN; 1991.24Sthorpej rv = tsleep(&sc->sc_queuecnt, PRIBIO, "lddrn", 0); 2001.24Sthorpej if (rv) 2011.24Sthorpej break; 2021.24Sthorpej } 2031.3Sad splx(s); 2041.7Sad 2051.7Sad return (rv); 2061.3Sad} 2071.3Sad 2081.2Sadvoid 2091.7Sadldenddetach(struct ld_softc *sc) 2101.2Sad{ 2111.13Sdrochner int s, bmaj, cmaj, i, mn; 2121.2Sad 2131.7Sad if ((sc->sc_flags & LDF_ENABLED) == 0) 2141.7Sad return; 2151.7Sad 2161.2Sad /* Wait for commands queued with the hardware to complete. */ 2171.2Sad if (sc->sc_queuecnt != 0) 2181.7Sad if (tsleep(&sc->sc_queuecnt, PRIBIO, "lddtch", 30 * hz)) 2191.7Sad printf("%s: not drained\n", sc->sc_dv.dv_xname); 2201.2Sad 2211.2Sad /* Locate the major numbers. */ 2221.16Sgehenna bmaj = bdevsw_lookup_major(&ld_bdevsw); 2231.16Sgehenna cmaj = cdevsw_lookup_major(&ld_cdevsw); 2241.2Sad 2251.2Sad /* Kill off any queued buffers. */ 2261.2Sad s = splbio(); 2271.37.2.1Syamt bufq_drain(sc->sc_bufq); 2281.36Syamt splx(s); 2291.36Syamt 2301.37.2.1Syamt bufq_free(sc->sc_bufq); 2311.2Sad 2321.2Sad /* Nuke the vnodes for any open instances. */ 2331.13Sdrochner for (i = 0; i < MAXPARTITIONS; i++) { 2341.37.2.1Syamt mn = DISKMINOR(device_unit(&sc->sc_dv), i); 2351.13Sdrochner vdevgone(bmaj, mn, mn, VBLK); 2361.13Sdrochner vdevgone(cmaj, mn, mn, VCHR); 2371.13Sdrochner } 2381.13Sdrochner 2391.30Sthorpej /* Delete all of our wedges. */ 2401.30Sthorpej dkwedge_delall(&sc->sc_dk); 2411.30Sthorpej 2421.2Sad /* Detach from the disk list. */ 2431.2Sad disk_detach(&sc->sc_dk); 2441.37.2.4Syamt disk_destroy(&sc->sc_dk); 2451.2Sad 2461.2Sad#if NRND > 0 2471.2Sad /* Unhook the entropy source. */ 2481.2Sad rnd_detach_source(&sc->sc_rnd_source); 2491.2Sad#endif 2501.2Sad 2511.24Sthorpej /* 2521.24Sthorpej * XXX We can't really flush the cache here, beceause the 2531.24Sthorpej * XXX device may already be non-existent from the controller's 2541.24Sthorpej * XXX perspective. 2551.24Sthorpej */ 2561.24Sthorpej#if 0 2571.2Sad /* Flush the device's cache. */ 2581.2Sad if (sc->sc_flush != NULL) 2591.2Sad if ((*sc->sc_flush)(sc) != 0) 2601.2Sad printf("%s: unable to flush cache\n", 2611.2Sad sc->sc_dv.dv_xname); 2621.24Sthorpej#endif 2631.2Sad} 2641.2Sad 2651.8Slukem/* ARGSUSED */ 2661.1Sadstatic void 2671.1Sadldshutdown(void *cookie) 2681.1Sad{ 2691.1Sad struct ld_softc *sc; 2701.1Sad int i; 2711.1Sad 2721.1Sad for (i = 0; i < ld_cd.cd_ndevs; i++) { 2731.1Sad if ((sc = device_lookup(&ld_cd, i)) == NULL) 2741.1Sad continue; 2751.1Sad if (sc->sc_flush != NULL && (*sc->sc_flush)(sc) != 0) 2761.1Sad printf("%s: unable to flush cache\n", 2771.1Sad sc->sc_dv.dv_xname); 2781.1Sad } 2791.1Sad} 2801.1Sad 2811.8Slukem/* ARGSUSED */ 2821.29Sthorpejstatic int 2831.37.2.1Syamtldopen(dev_t dev, int flags, int fmt, struct lwp *l) 2841.1Sad{ 2851.1Sad struct ld_softc *sc; 2861.30Sthorpej int error, unit, part; 2871.1Sad 2881.1Sad unit = DISKUNIT(dev); 2891.30Sthorpej if ((sc = device_lookup(&ld_cd, unit)) == NULL) 2901.1Sad return (ENXIO); 2911.1Sad if ((sc->sc_flags & LDF_ENABLED) == 0) 2921.1Sad return (ENODEV); 2931.1Sad part = DISKPART(dev); 2941.30Sthorpej 2951.37.2.3Syamt mutex_enter(&sc->sc_dk.dk_openlock); 2961.1Sad 2971.22Sthorpej if (sc->sc_dk.dk_openmask == 0) { 2981.22Sthorpej /* Load the partition info if not already loaded. */ 2991.22Sthorpej if ((sc->sc_flags & LDF_VLABEL) == 0) 3001.22Sthorpej ldgetdisklabel(sc); 3011.22Sthorpej } 3021.1Sad 3031.1Sad /* Check that the partition exists. */ 3041.1Sad if (part != RAW_PART && (part >= sc->sc_dk.dk_label->d_npartitions || 3051.1Sad sc->sc_dk.dk_label->d_partitions[part].p_fstype == FS_UNUSED)) { 3061.30Sthorpej error = ENXIO; 3071.30Sthorpej goto bad1; 3081.1Sad } 3091.1Sad 3101.1Sad /* Ensure only one open at a time. */ 3111.1Sad switch (fmt) { 3121.1Sad case S_IFCHR: 3131.1Sad sc->sc_dk.dk_copenmask |= (1 << part); 3141.1Sad break; 3151.1Sad case S_IFBLK: 3161.1Sad sc->sc_dk.dk_bopenmask |= (1 << part); 3171.1Sad break; 3181.1Sad } 3191.1Sad sc->sc_dk.dk_openmask = 3201.1Sad sc->sc_dk.dk_copenmask | sc->sc_dk.dk_bopenmask; 3211.1Sad 3221.37.2.3Syamt error = 0; 3231.30Sthorpej bad1: 3241.37.2.3Syamt mutex_exit(&sc->sc_dk.dk_openlock); 3251.30Sthorpej return (error); 3261.1Sad} 3271.1Sad 3281.8Slukem/* ARGSUSED */ 3291.29Sthorpejstatic int 3301.37.2.1Syamtldclose(dev_t dev, int flags, int fmt, struct lwp *l) 3311.1Sad{ 3321.1Sad struct ld_softc *sc; 3331.37.2.3Syamt int part, unit; 3341.1Sad 3351.1Sad unit = DISKUNIT(dev); 3361.1Sad part = DISKPART(dev); 3371.1Sad sc = device_lookup(&ld_cd, unit); 3381.30Sthorpej 3391.37.2.3Syamt mutex_enter(&sc->sc_dk.dk_openlock); 3401.1Sad 3411.1Sad switch (fmt) { 3421.1Sad case S_IFCHR: 3431.1Sad sc->sc_dk.dk_copenmask &= ~(1 << part); 3441.1Sad break; 3451.1Sad case S_IFBLK: 3461.1Sad sc->sc_dk.dk_bopenmask &= ~(1 << part); 3471.1Sad break; 3481.1Sad } 3491.1Sad sc->sc_dk.dk_openmask = 3501.1Sad sc->sc_dk.dk_copenmask | sc->sc_dk.dk_bopenmask; 3511.1Sad 3521.22Sthorpej if (sc->sc_dk.dk_openmask == 0) { 3531.22Sthorpej if (sc->sc_flush != NULL && (*sc->sc_flush)(sc) != 0) 3541.1Sad printf("%s: unable to flush cache\n", 3551.1Sad sc->sc_dv.dv_xname); 3561.22Sthorpej if ((sc->sc_flags & LDF_KLABEL) == 0) 3571.22Sthorpej sc->sc_flags &= ~LDF_VLABEL; 3581.22Sthorpej } 3591.1Sad 3601.37.2.3Syamt mutex_exit(&sc->sc_dk.dk_openlock); 3611.1Sad return (0); 3621.1Sad} 3631.1Sad 3641.8Slukem/* ARGSUSED */ 3651.29Sthorpejstatic int 3661.1Sadldread(dev_t dev, struct uio *uio, int ioflag) 3671.1Sad{ 3681.1Sad 3691.1Sad return (physio(ldstrategy, NULL, dev, B_READ, ldminphys, uio)); 3701.1Sad} 3711.1Sad 3721.8Slukem/* ARGSUSED */ 3731.29Sthorpejstatic int 3741.1Sadldwrite(dev_t dev, struct uio *uio, int ioflag) 3751.1Sad{ 3761.1Sad 3771.1Sad return (physio(ldstrategy, NULL, dev, B_WRITE, ldminphys, uio)); 3781.1Sad} 3791.1Sad 3801.8Slukem/* ARGSUSED */ 3811.29Sthorpejstatic int 3821.37.2.3Syamtldioctl(dev_t dev, u_long cmd, void *addr, int32_t flag, struct lwp *l) 3831.1Sad{ 3841.1Sad struct ld_softc *sc; 3851.1Sad int part, unit, error; 3861.4Sfvdl#ifdef __HAVE_OLD_DISKLABEL 3871.6Sitojun struct disklabel newlabel; 3881.4Sfvdl#endif 3891.6Sitojun struct disklabel *lp; 3901.1Sad 3911.1Sad unit = DISKUNIT(dev); 3921.1Sad part = DISKPART(dev); 3931.1Sad sc = device_lookup(&ld_cd, unit); 3941.1Sad 3951.37.2.2Syamt error = disk_ioctl(&sc->sc_dk, cmd, addr, flag, l); 3961.37.2.2Syamt if (error != EPASSTHROUGH) 3971.37.2.2Syamt return (error); 3981.37.2.2Syamt 3991.37.2.3Syamt error = 0; 4001.1Sad switch (cmd) { 4011.1Sad case DIOCGDINFO: 4021.1Sad memcpy(addr, sc->sc_dk.dk_label, sizeof(struct disklabel)); 4031.1Sad return (0); 4041.1Sad 4051.4Sfvdl#ifdef __HAVE_OLD_DISKLABEL 4061.4Sfvdl case ODIOCGDINFO: 4071.4Sfvdl newlabel = *(sc->sc_dk.dk_label); 4081.4Sfvdl if (newlabel.d_npartitions > OLDMAXPARTITIONS) 4091.5Sfvdl return ENOTTY; 4101.4Sfvdl memcpy(addr, &newlabel, sizeof(struct olddisklabel)); 4111.4Sfvdl return (0); 4121.4Sfvdl#endif 4131.4Sfvdl 4141.1Sad case DIOCGPART: 4151.1Sad ((struct partinfo *)addr)->disklab = sc->sc_dk.dk_label; 4161.1Sad ((struct partinfo *)addr)->part = 4171.1Sad &sc->sc_dk.dk_label->d_partitions[part]; 4181.1Sad break; 4191.1Sad 4201.1Sad case DIOCWDINFO: 4211.1Sad case DIOCSDINFO: 4221.4Sfvdl#ifdef __HAVE_OLD_DISKLABEL 4231.4Sfvdl case ODIOCWDINFO: 4241.4Sfvdl case ODIOCSDINFO: 4251.4Sfvdl 4261.4Sfvdl if (cmd == ODIOCSDINFO || cmd == ODIOCWDINFO) { 4271.4Sfvdl memset(&newlabel, 0, sizeof newlabel); 4281.4Sfvdl memcpy(&newlabel, addr, sizeof (struct olddisklabel)); 4291.4Sfvdl lp = &newlabel; 4301.4Sfvdl } else 4311.4Sfvdl#endif 4321.4Sfvdl lp = (struct disklabel *)addr; 4331.4Sfvdl 4341.1Sad if ((flag & FWRITE) == 0) 4351.1Sad return (EBADF); 4361.1Sad 4371.37.2.3Syamt mutex_enter(&sc->sc_dk.dk_openlock); 4381.1Sad sc->sc_flags |= LDF_LABELLING; 4391.1Sad 4401.1Sad error = setdisklabel(sc->sc_dk.dk_label, 4411.4Sfvdl lp, /*sc->sc_dk.dk_openmask : */0, 4421.1Sad sc->sc_dk.dk_cpulabel); 4431.4Sfvdl if (error == 0 && (cmd == DIOCWDINFO 4441.4Sfvdl#ifdef __HAVE_OLD_DISKLABEL 4451.4Sfvdl || cmd == ODIOCWDINFO 4461.4Sfvdl#endif 4471.4Sfvdl )) 4481.1Sad error = writedisklabel( 4491.35Sperry MAKEDISKDEV(major(dev), DISKUNIT(dev), RAW_PART), 4501.35Sperry ldstrategy, sc->sc_dk.dk_label, 4511.1Sad sc->sc_dk.dk_cpulabel); 4521.1Sad 4531.1Sad sc->sc_flags &= ~LDF_LABELLING; 4541.37.2.3Syamt mutex_exit(&sc->sc_dk.dk_openlock); 4551.1Sad break; 4561.1Sad 4571.22Sthorpej case DIOCKLABEL: 4581.22Sthorpej if ((flag & FWRITE) == 0) 4591.22Sthorpej return (EBADF); 4601.22Sthorpej if (*(int *)addr) 4611.22Sthorpej sc->sc_flags |= LDF_KLABEL; 4621.22Sthorpej else 4631.22Sthorpej sc->sc_flags &= ~LDF_KLABEL; 4641.22Sthorpej break; 4651.22Sthorpej 4661.1Sad case DIOCWLABEL: 4671.1Sad if ((flag & FWRITE) == 0) 4681.1Sad return (EBADF); 4691.1Sad if (*(int *)addr) 4701.1Sad sc->sc_flags |= LDF_WLABEL; 4711.1Sad else 4721.1Sad sc->sc_flags &= ~LDF_WLABEL; 4731.1Sad break; 4741.1Sad 4751.1Sad case DIOCGDEFLABEL: 4761.1Sad ldgetdefaultlabel(sc, (struct disklabel *)addr); 4771.1Sad break; 4781.4Sfvdl 4791.4Sfvdl#ifdef __HAVE_OLD_DISKLABEL 4801.4Sfvdl case ODIOCGDEFLABEL: 4811.4Sfvdl ldgetdefaultlabel(sc, &newlabel); 4821.4Sfvdl if (newlabel.d_npartitions > OLDMAXPARTITIONS) 4831.5Sfvdl return ENOTTY; 4841.4Sfvdl memcpy(addr, &newlabel, sizeof (struct olddisklabel)); 4851.4Sfvdl break; 4861.4Sfvdl#endif 4871.1Sad 4881.32Sthorpej case DIOCCACHESYNC: 4891.32Sthorpej /* 4901.32Sthorpej * XXX Do we really need to care about having a writable 4911.32Sthorpej * file descriptor here? 4921.32Sthorpej */ 4931.32Sthorpej if ((flag & FWRITE) == 0) 4941.32Sthorpej error = EBADF; 4951.32Sthorpej else if (sc->sc_flush) 4961.32Sthorpej error = (*sc->sc_flush)(sc); 4971.32Sthorpej else 4981.32Sthorpej error = 0; /* XXX Error out instead? */ 4991.32Sthorpej break; 5001.32Sthorpej 5011.30Sthorpej case DIOCAWEDGE: 5021.30Sthorpej { 5031.30Sthorpej struct dkwedge_info *dkw = (void *) addr; 5041.30Sthorpej 5051.30Sthorpej if ((flag & FWRITE) == 0) 5061.30Sthorpej return (EBADF); 5071.30Sthorpej 5081.30Sthorpej /* If the ioctl happens here, the parent is us. */ 5091.30Sthorpej strcpy(dkw->dkw_parent, sc->sc_dv.dv_xname); 5101.30Sthorpej return (dkwedge_add(dkw)); 5111.30Sthorpej } 5121.35Sperry 5131.30Sthorpej case DIOCDWEDGE: 5141.30Sthorpej { 5151.30Sthorpej struct dkwedge_info *dkw = (void *) addr; 5161.30Sthorpej 5171.30Sthorpej if ((flag & FWRITE) == 0) 5181.30Sthorpej return (EBADF); 5191.30Sthorpej 5201.30Sthorpej /* If the ioctl happens here, the parent is us. */ 5211.30Sthorpej strcpy(dkw->dkw_parent, sc->sc_dv.dv_xname); 5221.30Sthorpej return (dkwedge_del(dkw)); 5231.30Sthorpej } 5241.35Sperry 5251.30Sthorpej case DIOCLWEDGES: 5261.30Sthorpej { 5271.30Sthorpej struct dkwedge_list *dkwl = (void *) addr; 5281.30Sthorpej 5291.37.2.1Syamt return (dkwedge_list(&sc->sc_dk, dkwl, l)); 5301.30Sthorpej } 5311.37.2.5Syamt case DIOCGSTRATEGY: 5321.37.2.5Syamt { 5331.37.2.5Syamt struct disk_strategy *dks = (void *)addr; 5341.37.2.5Syamt 5351.37.2.5Syamt mutex_enter(&sc->sc_mutex); 5361.37.2.5Syamt strlcpy(dks->dks_name, bufq_getstrategyname(sc->sc_bufq), 5371.37.2.5Syamt sizeof(dks->dks_name)); 5381.37.2.5Syamt mutex_exit(&sc->sc_mutex); 5391.37.2.5Syamt dks->dks_paramlen = 0; 5401.37.2.5Syamt 5411.37.2.5Syamt return 0; 5421.37.2.5Syamt } 5431.37.2.5Syamt case DIOCSSTRATEGY: 5441.37.2.5Syamt { 5451.37.2.5Syamt struct disk_strategy *dks = (void *)addr; 5461.37.2.5Syamt struct bufq_state *new, *old; 5471.30Sthorpej 5481.37.2.5Syamt if ((flag & FWRITE) == 0) 5491.37.2.5Syamt return EPERM; 5501.37.2.5Syamt 5511.37.2.5Syamt if (dks->dks_param != NULL) 5521.37.2.5Syamt return EINVAL; 5531.37.2.5Syamt 5541.37.2.5Syamt dks->dks_name[sizeof(dks->dks_name) - 1] = 0; /* ensure term */ 5551.37.2.5Syamt error = bufq_alloc(&new, dks->dks_name, 5561.37.2.5Syamt BUFQ_EXACT|BUFQ_SORT_RAWBLOCK); 5571.37.2.5Syamt if (error) 5581.37.2.5Syamt return error; 5591.37.2.5Syamt 5601.37.2.5Syamt mutex_enter(&sc->sc_mutex); 5611.37.2.5Syamt old = sc->sc_bufq; 5621.37.2.5Syamt bufq_move(new, old); 5631.37.2.5Syamt sc->sc_bufq = new; 5641.37.2.5Syamt mutex_exit(&sc->sc_mutex); 5651.37.2.5Syamt bufq_free(old); 5661.37.2.5Syamt 5671.37.2.5Syamt return 0; 5681.37.2.5Syamt } 5691.1Sad default: 5701.1Sad error = ENOTTY; 5711.1Sad break; 5721.1Sad } 5731.1Sad 5741.1Sad return (error); 5751.1Sad} 5761.1Sad 5771.29Sthorpejstatic void 5781.1Sadldstrategy(struct buf *bp) 5791.1Sad{ 5801.1Sad struct ld_softc *sc; 5811.23Sthorpej struct disklabel *lp; 5821.23Sthorpej daddr_t blkno; 5831.23Sthorpej int s, part; 5841.1Sad 5851.1Sad sc = device_lookup(&ld_cd, DISKUNIT(bp->b_dev)); 5861.23Sthorpej part = DISKPART(bp->b_dev); 5871.1Sad 5881.7Sad if ((sc->sc_flags & LDF_DETACH) != 0) { 5891.2Sad bp->b_error = EIO; 5901.37.2.3Syamt goto done; 5911.2Sad } 5921.2Sad 5931.1Sad lp = sc->sc_dk.dk_label; 5941.1Sad 5951.1Sad /* 5961.1Sad * The transfer must be a whole number of blocks and the offset must 5971.1Sad * not be negative. 5981.1Sad */ 5991.1Sad if ((bp->b_bcount % lp->d_secsize) != 0 || bp->b_blkno < 0) { 6001.23Sthorpej bp->b_error = EINVAL; 6011.37.2.3Syamt goto done; 6021.1Sad } 6031.1Sad 6041.23Sthorpej /* If it's a null transfer, return immediately. */ 6051.23Sthorpej if (bp->b_bcount == 0) 6061.23Sthorpej goto done; 6071.1Sad 6081.1Sad /* 6091.1Sad * Do bounds checking and adjust the transfer. If error, process. 6101.1Sad * If past the end of partition, just return. 6111.1Sad */ 6121.1Sad if (part != RAW_PART && 6131.20Sthorpej bounds_check_with_label(&sc->sc_dk, bp, 6141.1Sad (sc->sc_flags & (LDF_WLABEL | LDF_LABELLING)) != 0) <= 0) { 6151.23Sthorpej goto done; 6161.1Sad } 6171.1Sad 6181.1Sad /* 6191.23Sthorpej * Convert the block number to absolute and put it in terms 6201.23Sthorpej * of the device's logical block size. 6211.1Sad */ 6221.23Sthorpej if (lp->d_secsize == DEV_BSIZE) 6231.23Sthorpej blkno = bp->b_blkno; 6241.23Sthorpej else if (lp->d_secsize > DEV_BSIZE) 6251.23Sthorpej blkno = bp->b_blkno / (lp->d_secsize / DEV_BSIZE); 6261.1Sad else 6271.23Sthorpej blkno = bp->b_blkno * (DEV_BSIZE / lp->d_secsize); 6281.1Sad 6291.11Sthorpej if (part != RAW_PART) 6301.23Sthorpej blkno += lp->d_partitions[part].p_offset; 6311.23Sthorpej 6321.23Sthorpej bp->b_rawblkno = blkno; 6331.1Sad 6341.1Sad s = splbio(); 6351.37.2.2Syamt ldstart(sc, bp); 6361.1Sad splx(s); 6371.23Sthorpej return; 6381.23Sthorpej 6391.23Sthorpej done: 6401.23Sthorpej bp->b_resid = bp->b_bcount; 6411.23Sthorpej biodone(bp); 6421.23Sthorpej} 6431.23Sthorpej 6441.23Sthorpejstatic void 6451.37.2.2Syamtldstart(struct ld_softc *sc, struct buf *bp) 6461.23Sthorpej{ 6471.23Sthorpej int error; 6481.1Sad 6491.37.2.2Syamt mutex_enter(&sc->sc_mutex); 6501.37.2.2Syamt 6511.37.2.2Syamt if (bp != NULL) 6521.37.2.2Syamt BUFQ_PUT(sc->sc_bufq, bp); 6531.37.2.2Syamt 6541.23Sthorpej while (sc->sc_queuecnt < sc->sc_maxqueuecnt) { 6551.23Sthorpej /* See if there is work to do. */ 6561.37.2.1Syamt if ((bp = BUFQ_PEEK(sc->sc_bufq)) == NULL) 6571.23Sthorpej break; 6581.23Sthorpej 6591.23Sthorpej disk_busy(&sc->sc_dk); 6601.23Sthorpej sc->sc_queuecnt++; 6611.23Sthorpej 6621.23Sthorpej if (__predict_true((error = (*sc->sc_start)(sc, bp)) == 0)) { 6631.23Sthorpej /* 6641.23Sthorpej * The back-end is running the job; remove it from 6651.23Sthorpej * the queue. 6661.23Sthorpej */ 6671.37.2.1Syamt (void) BUFQ_GET(sc->sc_bufq); 6681.23Sthorpej } else { 6691.23Sthorpej disk_unbusy(&sc->sc_dk, 0, (bp->b_flags & B_READ)); 6701.23Sthorpej sc->sc_queuecnt--; 6711.23Sthorpej if (error == EAGAIN) { 6721.23Sthorpej /* 6731.23Sthorpej * Temporary resource shortage in the 6741.23Sthorpej * back-end; just defer the job until 6751.23Sthorpej * later. 6761.23Sthorpej * 6771.23Sthorpej * XXX We might consider a watchdog timer 6781.23Sthorpej * XXX to make sure we are kicked into action. 6791.23Sthorpej */ 6801.23Sthorpej break; 6811.23Sthorpej } else { 6821.37.2.1Syamt (void) BUFQ_GET(sc->sc_bufq); 6831.23Sthorpej bp->b_error = error; 6841.23Sthorpej bp->b_resid = bp->b_bcount; 6851.37.2.2Syamt mutex_exit(&sc->sc_mutex); 6861.23Sthorpej biodone(bp); 6871.37.2.2Syamt mutex_enter(&sc->sc_mutex); 6881.23Sthorpej } 6891.23Sthorpej } 6901.1Sad } 6911.37.2.2Syamt 6921.37.2.2Syamt mutex_exit(&sc->sc_mutex); 6931.1Sad} 6941.1Sad 6951.1Sadvoid 6961.1Sadlddone(struct ld_softc *sc, struct buf *bp) 6971.1Sad{ 6981.1Sad 6991.37.2.3Syamt if (bp->b_error != 0) { 7001.1Sad diskerr(bp, "ld", "error", LOG_PRINTF, 0, sc->sc_dk.dk_label); 7011.1Sad printf("\n"); 7021.1Sad } 7031.1Sad 7041.18Smrg disk_unbusy(&sc->sc_dk, bp->b_bcount - bp->b_resid, 7051.18Smrg (bp->b_flags & B_READ)); 7061.1Sad#if NRND > 0 7071.1Sad rnd_add_uint32(&sc->sc_rnd_source, bp->b_rawblkno); 7081.1Sad#endif 7091.1Sad biodone(bp); 7101.1Sad 7111.37.2.2Syamt mutex_enter(&sc->sc_mutex); 7121.7Sad if (--sc->sc_queuecnt <= sc->sc_maxqueuecnt) { 7131.24Sthorpej if ((sc->sc_flags & LDF_DRAIN) != 0) { 7141.24Sthorpej sc->sc_flags &= ~LDF_DRAIN; 7151.7Sad wakeup(&sc->sc_queuecnt); 7161.24Sthorpej } 7171.37.2.2Syamt mutex_exit(&sc->sc_mutex); 7181.37.2.2Syamt ldstart(sc, NULL); 7191.37.2.2Syamt } else 7201.37.2.2Syamt mutex_exit(&sc->sc_mutex); 7211.1Sad} 7221.1Sad 7231.29Sthorpejstatic int 7241.1Sadldsize(dev_t dev) 7251.1Sad{ 7261.1Sad struct ld_softc *sc; 7271.1Sad int part, unit, omask, size; 7281.1Sad 7291.1Sad unit = DISKUNIT(dev); 7301.1Sad if ((sc = device_lookup(&ld_cd, unit)) == NULL) 7311.1Sad return (ENODEV); 7321.1Sad if ((sc->sc_flags & LDF_ENABLED) == 0) 7331.1Sad return (ENODEV); 7341.1Sad part = DISKPART(dev); 7351.1Sad 7361.1Sad omask = sc->sc_dk.dk_openmask & (1 << part); 7371.1Sad 7381.1Sad if (omask == 0 && ldopen(dev, 0, S_IFBLK, NULL) != 0) 7391.1Sad return (-1); 7401.1Sad else if (sc->sc_dk.dk_label->d_partitions[part].p_fstype != FS_SWAP) 7411.1Sad size = -1; 7421.1Sad else 7431.1Sad size = sc->sc_dk.dk_label->d_partitions[part].p_size * 7441.1Sad (sc->sc_dk.dk_label->d_secsize / DEV_BSIZE); 7451.1Sad if (omask == 0 && ldclose(dev, 0, S_IFBLK, NULL) != 0) 7461.1Sad return (-1); 7471.1Sad 7481.1Sad return (size); 7491.1Sad} 7501.1Sad 7511.1Sad/* 7521.1Sad * Load the label information from the specified device. 7531.1Sad */ 7541.1Sadstatic void 7551.1Sadldgetdisklabel(struct ld_softc *sc) 7561.1Sad{ 7571.1Sad const char *errstring; 7581.1Sad 7591.1Sad ldgetdefaultlabel(sc, sc->sc_dk.dk_label); 7601.1Sad 7611.1Sad /* Call the generic disklabel extraction routine. */ 7621.37.2.1Syamt errstring = readdisklabel(MAKEDISKDEV(0, device_unit(&sc->sc_dv), 7631.37.2.1Syamt RAW_PART), ldstrategy, sc->sc_dk.dk_label, sc->sc_dk.dk_cpulabel); 7641.1Sad if (errstring != NULL) 7651.1Sad printf("%s: %s\n", sc->sc_dv.dv_xname, errstring); 7661.22Sthorpej 7671.22Sthorpej /* In-core label now valid. */ 7681.22Sthorpej sc->sc_flags |= LDF_VLABEL; 7691.1Sad} 7701.1Sad 7711.1Sad/* 7721.1Sad * Construct a ficticious label. 7731.1Sad */ 7741.1Sadstatic void 7751.1Sadldgetdefaultlabel(struct ld_softc *sc, struct disklabel *lp) 7761.1Sad{ 7771.1Sad 7781.1Sad memset(lp, 0, sizeof(struct disklabel)); 7791.1Sad 7801.1Sad lp->d_secsize = sc->sc_secsize; 7811.1Sad lp->d_ntracks = sc->sc_nheads; 7821.1Sad lp->d_nsectors = sc->sc_nsectors; 7831.1Sad lp->d_ncylinders = sc->sc_ncylinders; 7841.1Sad lp->d_secpercyl = lp->d_ntracks * lp->d_nsectors; 7851.1Sad lp->d_type = DTYPE_LD; 7861.21Sitojun strlcpy(lp->d_typename, "unknown", sizeof(lp->d_typename)); 7871.21Sitojun strlcpy(lp->d_packname, "fictitious", sizeof(lp->d_packname)); 7881.1Sad lp->d_secperunit = sc->sc_secperunit; 7891.1Sad lp->d_rpm = 7200; 7901.1Sad lp->d_interleave = 1; 7911.1Sad lp->d_flags = 0; 7921.1Sad 7931.1Sad lp->d_partitions[RAW_PART].p_offset = 0; 7941.1Sad lp->d_partitions[RAW_PART].p_size = 7951.1Sad lp->d_secperunit * (lp->d_secsize / DEV_BSIZE); 7961.1Sad lp->d_partitions[RAW_PART].p_fstype = FS_UNUSED; 7971.1Sad lp->d_npartitions = RAW_PART + 1; 7981.1Sad 7991.1Sad lp->d_magic = DISKMAGIC; 8001.1Sad lp->d_magic2 = DISKMAGIC; 8011.1Sad lp->d_checksum = dkcksum(lp); 8021.1Sad} 8031.1Sad 8041.1Sad/* 8051.1Sad * Take a dump. 8061.1Sad */ 8071.29Sthorpejstatic int 8081.37.2.3Syamtlddump(dev_t dev, daddr_t blkno, void *vav, size_t size) 8091.1Sad{ 8101.37.2.3Syamt char *va = vav; 8111.1Sad struct ld_softc *sc; 8121.1Sad struct disklabel *lp; 8131.1Sad int unit, part, nsects, sectoff, towrt, nblk, maxblkcnt, rv; 8141.1Sad static int dumping; 8151.1Sad 8161.1Sad unit = DISKUNIT(dev); 8171.1Sad if ((sc = device_lookup(&ld_cd, unit)) == NULL) 8181.1Sad return (ENXIO); 8191.1Sad if ((sc->sc_flags & LDF_ENABLED) == 0) 8201.1Sad return (ENODEV); 8211.3Sad if (sc->sc_dump == NULL) 8221.3Sad return (ENXIO); 8231.3Sad 8241.3Sad /* Check if recursive dump; if so, punt. */ 8251.3Sad if (dumping) 8261.3Sad return (EFAULT); 8271.3Sad dumping = 1; 8281.1Sad 8291.1Sad /* Convert to disk sectors. Request must be a multiple of size. */ 8301.3Sad part = DISKPART(dev); 8311.1Sad lp = sc->sc_dk.dk_label; 8321.1Sad if ((size % lp->d_secsize) != 0) 8331.1Sad return (EFAULT); 8341.1Sad towrt = size / lp->d_secsize; 8351.1Sad blkno = dbtob(blkno) / lp->d_secsize; /* blkno in DEV_BSIZE units */ 8361.1Sad 8371.1Sad nsects = lp->d_partitions[part].p_size; 8381.1Sad sectoff = lp->d_partitions[part].p_offset; 8391.1Sad 8401.1Sad /* Check transfer bounds against partition size. */ 8411.1Sad if ((blkno < 0) || ((blkno + towrt) > nsects)) 8421.1Sad return (EINVAL); 8431.1Sad 8441.1Sad /* Offset block number to start of partition. */ 8451.1Sad blkno += sectoff; 8461.1Sad 8471.1Sad /* Start dumping and return when done. */ 8481.3Sad maxblkcnt = sc->sc_maxxfer / sc->sc_secsize - 1; 8491.1Sad while (towrt > 0) { 8501.3Sad nblk = min(maxblkcnt, towrt); 8511.1Sad 8521.1Sad if ((rv = (*sc->sc_dump)(sc, va, blkno, nblk)) != 0) 8531.1Sad return (rv); 8541.1Sad 8551.1Sad towrt -= nblk; 8561.1Sad blkno += nblk; 8571.1Sad va += nblk * sc->sc_secsize; 8581.1Sad } 8591.1Sad 8601.1Sad dumping = 0; 8611.1Sad return (0); 8621.1Sad} 8631.1Sad 8641.1Sad/* 8651.1Sad * Adjust the size of a transfer. 8661.1Sad */ 8671.1Sadstatic void 8681.1Sadldminphys(struct buf *bp) 8691.1Sad{ 8701.1Sad struct ld_softc *sc; 8711.1Sad 8721.1Sad sc = device_lookup(&ld_cd, DISKUNIT(bp->b_dev)); 8731.1Sad 8741.1Sad if (bp->b_bcount > sc->sc_maxxfer) 8751.1Sad bp->b_bcount = sc->sc_maxxfer; 8761.1Sad minphys(bp); 8771.1Sad} 8781.37.2.2Syamt 8791.37.2.2Syamtstatic void 8801.37.2.2Syamtld_set_properties(struct ld_softc *ld) 8811.37.2.2Syamt{ 8821.37.2.2Syamt prop_dictionary_t disk_info, odisk_info, geom; 8831.37.2.2Syamt 8841.37.2.2Syamt disk_info = prop_dictionary_create(); 8851.37.2.2Syamt 8861.37.2.2Syamt geom = prop_dictionary_create(); 8871.37.2.2Syamt 8881.37.2.2Syamt prop_dictionary_set_uint64(geom, "sectors-per-unit", 8891.37.2.2Syamt ld->sc_secperunit); 8901.37.2.2Syamt 8911.37.2.2Syamt prop_dictionary_set_uint32(geom, "sector-size", 8921.37.2.2Syamt ld->sc_secsize); 8931.37.2.2Syamt 8941.37.2.2Syamt prop_dictionary_set_uint16(geom, "sectors-per-track", 8951.37.2.2Syamt ld->sc_nsectors); 8961.37.2.2Syamt 8971.37.2.2Syamt prop_dictionary_set_uint16(geom, "tracks-per-cylinder", 8981.37.2.2Syamt ld->sc_nheads); 8991.37.2.2Syamt 9001.37.2.2Syamt prop_dictionary_set_uint64(geom, "cylinders-per-unit", 9011.37.2.2Syamt ld->sc_ncylinders); 9021.37.2.2Syamt 9031.37.2.2Syamt prop_dictionary_set(disk_info, "geometry", geom); 9041.37.2.2Syamt prop_object_release(geom); 9051.37.2.2Syamt 9061.37.2.2Syamt prop_dictionary_set(device_properties(&ld->sc_dv), 9071.37.2.2Syamt "disk-info", disk_info); 9081.37.2.2Syamt 9091.37.2.2Syamt /* 9101.37.2.2Syamt * Don't release disk_info here; we keep a reference to it. 9111.37.2.2Syamt * disk_detach() will release it when we go away. 9121.37.2.2Syamt */ 9131.37.2.2Syamt 9141.37.2.2Syamt odisk_info = ld->sc_dk.dk_info; 9151.37.2.2Syamt ld->sc_dk.dk_info = disk_info; 9161.37.2.2Syamt if (odisk_info) 9171.37.2.2Syamt prop_object_release(odisk_info); 9181.37.2.2Syamt} 9191.37.2.2Syamt 9201.37.2.2Syamtstatic void 9211.37.2.2Syamtld_config_interrupts (struct device *d) 9221.37.2.2Syamt{ 9231.37.2.2Syamt struct ld_softc *sc = (struct ld_softc *)d; 9241.37.2.2Syamt dkwedge_discover(&sc->sc_dk); 9251.37.2.2Syamt} 926