1*7c478bd9Sstevel@tonic-gate /* 2*7c478bd9Sstevel@tonic-gate * CDDL HEADER START 3*7c478bd9Sstevel@tonic-gate * 4*7c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*7c478bd9Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 6*7c478bd9Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 7*7c478bd9Sstevel@tonic-gate * with the License. 8*7c478bd9Sstevel@tonic-gate * 9*7c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10*7c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 11*7c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 12*7c478bd9Sstevel@tonic-gate * and limitations under the License. 13*7c478bd9Sstevel@tonic-gate * 14*7c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 15*7c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16*7c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 17*7c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 18*7c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 19*7c478bd9Sstevel@tonic-gate * 20*7c478bd9Sstevel@tonic-gate * CDDL HEADER END 21*7c478bd9Sstevel@tonic-gate */ 22*7c478bd9Sstevel@tonic-gate /* 23*7c478bd9Sstevel@tonic-gate * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24*7c478bd9Sstevel@tonic-gate * Use is subject to license terms. 25*7c478bd9Sstevel@tonic-gate */ 26*7c478bd9Sstevel@tonic-gate 27*7c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 28*7c478bd9Sstevel@tonic-gate 29*7c478bd9Sstevel@tonic-gate /* 30*7c478bd9Sstevel@tonic-gate * lofi (loopback file) driver - allows you to attach a file to a device, 31*7c478bd9Sstevel@tonic-gate * which can then be accessed through that device. The simple model is that 32*7c478bd9Sstevel@tonic-gate * you tell lofi to open a file, and then use the block device you get as 33*7c478bd9Sstevel@tonic-gate * you would any block device. lofi translates access to the block device 34*7c478bd9Sstevel@tonic-gate * into I/O on the underlying file. This is mostly useful for 35*7c478bd9Sstevel@tonic-gate * mounting images of filesystems. 36*7c478bd9Sstevel@tonic-gate * 37*7c478bd9Sstevel@tonic-gate * lofi is controlled through /dev/lofictl - this is the only device exported 38*7c478bd9Sstevel@tonic-gate * during attach, and is minor number 0. lofiadm communicates with lofi through 39*7c478bd9Sstevel@tonic-gate * ioctls on this device. When a file is attached to lofi, block and character 40*7c478bd9Sstevel@tonic-gate * devices are exported in /dev/lofi and /dev/rlofi. Currently, these devices 41*7c478bd9Sstevel@tonic-gate * are identified by their minor number, and the minor number is also used 42*7c478bd9Sstevel@tonic-gate * as the name in /dev/lofi. If we ever decide to support virtual disks, 43*7c478bd9Sstevel@tonic-gate * we'll have to divide the minor number space to identify fdisk partitions 44*7c478bd9Sstevel@tonic-gate * and slices, and the name will then be the minor number shifted down a 45*7c478bd9Sstevel@tonic-gate * few bits. Minor devices are tracked with state structures handled with 46*7c478bd9Sstevel@tonic-gate * ddi_soft_state(9F) for simplicity. 47*7c478bd9Sstevel@tonic-gate * 48*7c478bd9Sstevel@tonic-gate * A file attached to lofi is opened when attached and not closed until 49*7c478bd9Sstevel@tonic-gate * explicitly detached from lofi. This seems more sensible than deferring 50*7c478bd9Sstevel@tonic-gate * the open until the /dev/lofi device is opened, for a number of reasons. 51*7c478bd9Sstevel@tonic-gate * One is that any failure is likely to be noticed by the person (or script) 52*7c478bd9Sstevel@tonic-gate * running lofiadm. Another is that it would be a security problem if the 53*7c478bd9Sstevel@tonic-gate * file was replaced by another one after being added but before being opened. 54*7c478bd9Sstevel@tonic-gate * 55*7c478bd9Sstevel@tonic-gate * The only hard part about lofi is the ioctls. In order to support things 56*7c478bd9Sstevel@tonic-gate * like 'newfs' on a lofi device, it needs to support certain disk ioctls. 57*7c478bd9Sstevel@tonic-gate * So it has to fake disk geometry and partition information. More may need 58*7c478bd9Sstevel@tonic-gate * to be faked if your favorite utility doesn't work and you think it should 59*7c478bd9Sstevel@tonic-gate * (fdformat doesn't work because it really wants to know the type of floppy 60*7c478bd9Sstevel@tonic-gate * controller to talk to, and that didn't seem easy to fake. Or possibly even 61*7c478bd9Sstevel@tonic-gate * necessary, since we have mkfs_pcfs now). 62*7c478bd9Sstevel@tonic-gate * 63*7c478bd9Sstevel@tonic-gate * Known problems: 64*7c478bd9Sstevel@tonic-gate * 65*7c478bd9Sstevel@tonic-gate * UFS logging. Mounting a UFS filesystem image "logging" 66*7c478bd9Sstevel@tonic-gate * works for basic copy testing but wedges during a build of ON through 67*7c478bd9Sstevel@tonic-gate * that image. Some deadlock in lufs holding the log mutex and then 68*7c478bd9Sstevel@tonic-gate * getting stuck on a buf. So for now, don't do that. 69*7c478bd9Sstevel@tonic-gate * 70*7c478bd9Sstevel@tonic-gate * Direct I/O. Since the filesystem data is being cached in the buffer 71*7c478bd9Sstevel@tonic-gate * cache, _and_ again in the underlying filesystem, it's tempting to 72*7c478bd9Sstevel@tonic-gate * enable direct I/O on the underlying file. Don't, because that deadlocks. 73*7c478bd9Sstevel@tonic-gate * I think to fix the cache-twice problem we might need filesystem support. 74*7c478bd9Sstevel@tonic-gate * 75*7c478bd9Sstevel@tonic-gate * lofi on itself. The simple lock strategy (lofi_lock) precludes this 76*7c478bd9Sstevel@tonic-gate * because you'll be in lofi_ioctl, holding the lock when you open the 77*7c478bd9Sstevel@tonic-gate * file, which, if it's lofi, will grab lofi_lock. We prevent this for 78*7c478bd9Sstevel@tonic-gate * now, though not using ddi_soft_state(9F) would make it possible to 79*7c478bd9Sstevel@tonic-gate * do. Though it would still be silly. 80*7c478bd9Sstevel@tonic-gate * 81*7c478bd9Sstevel@tonic-gate * Interesting things to do: 82*7c478bd9Sstevel@tonic-gate * 83*7c478bd9Sstevel@tonic-gate * Allow multiple files for each device. A poor-man's metadisk, basically. 84*7c478bd9Sstevel@tonic-gate * 85*7c478bd9Sstevel@tonic-gate * Pass-through ioctls on block devices. You can (though it's not 86*7c478bd9Sstevel@tonic-gate * documented), give lofi a block device as a file name. Then we shouldn't 87*7c478bd9Sstevel@tonic-gate * need to fake a geometry. But this is also silly unless you're replacing 88*7c478bd9Sstevel@tonic-gate * metadisk. 89*7c478bd9Sstevel@tonic-gate * 90*7c478bd9Sstevel@tonic-gate * Encryption. tpm would like this. Apparently Windows 2000 has it, and 91*7c478bd9Sstevel@tonic-gate * so does Linux. 92*7c478bd9Sstevel@tonic-gate */ 93*7c478bd9Sstevel@tonic-gate 94*7c478bd9Sstevel@tonic-gate #include <sys/types.h> 95*7c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h> 96*7c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h> 97*7c478bd9Sstevel@tonic-gate #include <sys/uio.h> 98*7c478bd9Sstevel@tonic-gate #include <sys/kmem.h> 99*7c478bd9Sstevel@tonic-gate #include <sys/cred.h> 100*7c478bd9Sstevel@tonic-gate #include <sys/mman.h> 101*7c478bd9Sstevel@tonic-gate #include <sys/errno.h> 102*7c478bd9Sstevel@tonic-gate #include <sys/aio_req.h> 103*7c478bd9Sstevel@tonic-gate #include <sys/stat.h> 104*7c478bd9Sstevel@tonic-gate #include <sys/file.h> 105*7c478bd9Sstevel@tonic-gate #include <sys/modctl.h> 106*7c478bd9Sstevel@tonic-gate #include <sys/conf.h> 107*7c478bd9Sstevel@tonic-gate #include <sys/debug.h> 108*7c478bd9Sstevel@tonic-gate #include <sys/vnode.h> 109*7c478bd9Sstevel@tonic-gate #include <sys/lofi.h> 110*7c478bd9Sstevel@tonic-gate #include <sys/vol.h> 111*7c478bd9Sstevel@tonic-gate #include <sys/fcntl.h> 112*7c478bd9Sstevel@tonic-gate #include <sys/pathname.h> 113*7c478bd9Sstevel@tonic-gate #include <sys/filio.h> 114*7c478bd9Sstevel@tonic-gate #include <sys/fdio.h> 115*7c478bd9Sstevel@tonic-gate #include <sys/open.h> 116*7c478bd9Sstevel@tonic-gate #include <sys/disp.h> 117*7c478bd9Sstevel@tonic-gate #include <vm/seg_map.h> 118*7c478bd9Sstevel@tonic-gate #include <sys/ddi.h> 119*7c478bd9Sstevel@tonic-gate #include <sys/sunddi.h> 120*7c478bd9Sstevel@tonic-gate 121*7c478bd9Sstevel@tonic-gate /* seems safer than having to get the string right many times */ 122*7c478bd9Sstevel@tonic-gate #define NBLOCKS_PROP_NAME "Nblocks" 123*7c478bd9Sstevel@tonic-gate #define SIZE_PROP_NAME "Size" 124*7c478bd9Sstevel@tonic-gate 125*7c478bd9Sstevel@tonic-gate static dev_info_t *lofi_dip; 126*7c478bd9Sstevel@tonic-gate static void *lofi_statep; 127*7c478bd9Sstevel@tonic-gate static kmutex_t lofi_lock; /* state lock */ 128*7c478bd9Sstevel@tonic-gate 129*7c478bd9Sstevel@tonic-gate /* 130*7c478bd9Sstevel@tonic-gate * Because lofi_taskq_nthreads limits the actual swamping of the device, the 131*7c478bd9Sstevel@tonic-gate * maxalloc parameter (lofi_taskq_maxalloc) should be tuned conservatively 132*7c478bd9Sstevel@tonic-gate * high. If we want to be assured that the underlying device is always busy, 133*7c478bd9Sstevel@tonic-gate * we must be sure that the number of bytes enqueued when the number of 134*7c478bd9Sstevel@tonic-gate * enqueued tasks exceeds maxalloc is sufficient to keep the device busy for 135*7c478bd9Sstevel@tonic-gate * the duration of the sleep time in taskq_ent_alloc(). That is, lofi should 136*7c478bd9Sstevel@tonic-gate * set maxalloc to be the maximum throughput (in bytes per second) of the 137*7c478bd9Sstevel@tonic-gate * underlying device divided by the minimum I/O size. We assume a realistic 138*7c478bd9Sstevel@tonic-gate * maximum throughput of one hundred megabytes per second; we set maxalloc on 139*7c478bd9Sstevel@tonic-gate * the lofi task queue to be 104857600 divided by DEV_BSIZE. 140*7c478bd9Sstevel@tonic-gate */ 141*7c478bd9Sstevel@tonic-gate static int lofi_taskq_maxalloc = 104857600 / DEV_BSIZE; 142*7c478bd9Sstevel@tonic-gate static int lofi_taskq_nthreads = 4; /* # of taskq threads per device */ 143*7c478bd9Sstevel@tonic-gate 144*7c478bd9Sstevel@tonic-gate uint32_t lofi_max_files = LOFI_MAX_FILES; 145*7c478bd9Sstevel@tonic-gate 146*7c478bd9Sstevel@tonic-gate static int 147*7c478bd9Sstevel@tonic-gate lofi_busy(void) 148*7c478bd9Sstevel@tonic-gate { 149*7c478bd9Sstevel@tonic-gate minor_t minor; 150*7c478bd9Sstevel@tonic-gate 151*7c478bd9Sstevel@tonic-gate /* 152*7c478bd9Sstevel@tonic-gate * We need to make sure no mappings exist - mod_remove won't 153*7c478bd9Sstevel@tonic-gate * help because the device isn't open. 154*7c478bd9Sstevel@tonic-gate */ 155*7c478bd9Sstevel@tonic-gate mutex_enter(&lofi_lock); 156*7c478bd9Sstevel@tonic-gate for (minor = 1; minor <= lofi_max_files; minor++) { 157*7c478bd9Sstevel@tonic-gate if (ddi_get_soft_state(lofi_statep, minor) != NULL) { 158*7c478bd9Sstevel@tonic-gate mutex_exit(&lofi_lock); 159*7c478bd9Sstevel@tonic-gate return (EBUSY); 160*7c478bd9Sstevel@tonic-gate } 161*7c478bd9Sstevel@tonic-gate } 162*7c478bd9Sstevel@tonic-gate mutex_exit(&lofi_lock); 163*7c478bd9Sstevel@tonic-gate return (0); 164*7c478bd9Sstevel@tonic-gate } 165*7c478bd9Sstevel@tonic-gate 166*7c478bd9Sstevel@tonic-gate static int 167*7c478bd9Sstevel@tonic-gate is_opened(struct lofi_state *lsp) 168*7c478bd9Sstevel@tonic-gate { 169*7c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(&lofi_lock)); 170*7c478bd9Sstevel@tonic-gate return (lsp->ls_chr_open || lsp->ls_blk_open || lsp->ls_lyr_open_count); 171*7c478bd9Sstevel@tonic-gate } 172*7c478bd9Sstevel@tonic-gate 173*7c478bd9Sstevel@tonic-gate static int 174*7c478bd9Sstevel@tonic-gate mark_opened(struct lofi_state *lsp, int otyp) 175*7c478bd9Sstevel@tonic-gate { 176*7c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(&lofi_lock)); 177*7c478bd9Sstevel@tonic-gate switch (otyp) { 178*7c478bd9Sstevel@tonic-gate case OTYP_CHR: 179*7c478bd9Sstevel@tonic-gate lsp->ls_chr_open = 1; 180*7c478bd9Sstevel@tonic-gate break; 181*7c478bd9Sstevel@tonic-gate case OTYP_BLK: 182*7c478bd9Sstevel@tonic-gate lsp->ls_blk_open = 1; 183*7c478bd9Sstevel@tonic-gate break; 184*7c478bd9Sstevel@tonic-gate case OTYP_LYR: 185*7c478bd9Sstevel@tonic-gate lsp->ls_lyr_open_count++; 186*7c478bd9Sstevel@tonic-gate break; 187*7c478bd9Sstevel@tonic-gate default: 188*7c478bd9Sstevel@tonic-gate return (-1); 189*7c478bd9Sstevel@tonic-gate } 190*7c478bd9Sstevel@tonic-gate return (0); 191*7c478bd9Sstevel@tonic-gate } 192*7c478bd9Sstevel@tonic-gate 193*7c478bd9Sstevel@tonic-gate static void 194*7c478bd9Sstevel@tonic-gate mark_closed(struct lofi_state *lsp, int otyp) 195*7c478bd9Sstevel@tonic-gate { 196*7c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(&lofi_lock)); 197*7c478bd9Sstevel@tonic-gate switch (otyp) { 198*7c478bd9Sstevel@tonic-gate case OTYP_CHR: 199*7c478bd9Sstevel@tonic-gate lsp->ls_chr_open = 0; 200*7c478bd9Sstevel@tonic-gate break; 201*7c478bd9Sstevel@tonic-gate case OTYP_BLK: 202*7c478bd9Sstevel@tonic-gate lsp->ls_blk_open = 0; 203*7c478bd9Sstevel@tonic-gate break; 204*7c478bd9Sstevel@tonic-gate case OTYP_LYR: 205*7c478bd9Sstevel@tonic-gate lsp->ls_lyr_open_count--; 206*7c478bd9Sstevel@tonic-gate break; 207*7c478bd9Sstevel@tonic-gate default: 208*7c478bd9Sstevel@tonic-gate break; 209*7c478bd9Sstevel@tonic-gate } 210*7c478bd9Sstevel@tonic-gate } 211*7c478bd9Sstevel@tonic-gate 212*7c478bd9Sstevel@tonic-gate /*ARGSUSED3*/ 213*7c478bd9Sstevel@tonic-gate static int 214*7c478bd9Sstevel@tonic-gate lofi_open(dev_t *devp, int flag, int otyp, struct cred *credp) 215*7c478bd9Sstevel@tonic-gate { 216*7c478bd9Sstevel@tonic-gate minor_t minor; 217*7c478bd9Sstevel@tonic-gate struct lofi_state *lsp; 218*7c478bd9Sstevel@tonic-gate 219*7c478bd9Sstevel@tonic-gate mutex_enter(&lofi_lock); 220*7c478bd9Sstevel@tonic-gate minor = getminor(*devp); 221*7c478bd9Sstevel@tonic-gate if (minor == 0) { 222*7c478bd9Sstevel@tonic-gate /* master control device */ 223*7c478bd9Sstevel@tonic-gate /* must be opened exclusively */ 224*7c478bd9Sstevel@tonic-gate if (((flag & FEXCL) != FEXCL) || (otyp != OTYP_CHR)) { 225*7c478bd9Sstevel@tonic-gate mutex_exit(&lofi_lock); 226*7c478bd9Sstevel@tonic-gate return (EINVAL); 227*7c478bd9Sstevel@tonic-gate } 228*7c478bd9Sstevel@tonic-gate lsp = ddi_get_soft_state(lofi_statep, 0); 229*7c478bd9Sstevel@tonic-gate if (lsp == NULL) { 230*7c478bd9Sstevel@tonic-gate mutex_exit(&lofi_lock); 231*7c478bd9Sstevel@tonic-gate return (ENXIO); 232*7c478bd9Sstevel@tonic-gate } 233*7c478bd9Sstevel@tonic-gate if (is_opened(lsp)) { 234*7c478bd9Sstevel@tonic-gate mutex_exit(&lofi_lock); 235*7c478bd9Sstevel@tonic-gate return (EBUSY); 236*7c478bd9Sstevel@tonic-gate } 237*7c478bd9Sstevel@tonic-gate (void) mark_opened(lsp, OTYP_CHR); 238*7c478bd9Sstevel@tonic-gate mutex_exit(&lofi_lock); 239*7c478bd9Sstevel@tonic-gate return (0); 240*7c478bd9Sstevel@tonic-gate } 241*7c478bd9Sstevel@tonic-gate 242*7c478bd9Sstevel@tonic-gate /* otherwise, the mapping should already exist */ 243*7c478bd9Sstevel@tonic-gate lsp = ddi_get_soft_state(lofi_statep, minor); 244*7c478bd9Sstevel@tonic-gate if (lsp == NULL) { 245*7c478bd9Sstevel@tonic-gate mutex_exit(&lofi_lock); 246*7c478bd9Sstevel@tonic-gate return (EINVAL); 247*7c478bd9Sstevel@tonic-gate } 248*7c478bd9Sstevel@tonic-gate 249*7c478bd9Sstevel@tonic-gate if (mark_opened(lsp, otyp) == -1) { 250*7c478bd9Sstevel@tonic-gate mutex_exit(&lofi_lock); 251*7c478bd9Sstevel@tonic-gate return (EINVAL); 252*7c478bd9Sstevel@tonic-gate } 253*7c478bd9Sstevel@tonic-gate 254*7c478bd9Sstevel@tonic-gate mutex_exit(&lofi_lock); 255*7c478bd9Sstevel@tonic-gate return (0); 256*7c478bd9Sstevel@tonic-gate } 257*7c478bd9Sstevel@tonic-gate 258*7c478bd9Sstevel@tonic-gate /*ARGSUSED3*/ 259*7c478bd9Sstevel@tonic-gate static int 260*7c478bd9Sstevel@tonic-gate lofi_close(dev_t dev, int flag, int otyp, struct cred *credp) 261*7c478bd9Sstevel@tonic-gate { 262*7c478bd9Sstevel@tonic-gate minor_t minor; 263*7c478bd9Sstevel@tonic-gate struct lofi_state *lsp; 264*7c478bd9Sstevel@tonic-gate 265*7c478bd9Sstevel@tonic-gate #ifdef lint 266*7c478bd9Sstevel@tonic-gate flag = flag; 267*7c478bd9Sstevel@tonic-gate #endif 268*7c478bd9Sstevel@tonic-gate mutex_enter(&lofi_lock); 269*7c478bd9Sstevel@tonic-gate minor = getminor(dev); 270*7c478bd9Sstevel@tonic-gate lsp = ddi_get_soft_state(lofi_statep, minor); 271*7c478bd9Sstevel@tonic-gate if (lsp == NULL) { 272*7c478bd9Sstevel@tonic-gate mutex_exit(&lofi_lock); 273*7c478bd9Sstevel@tonic-gate return (EINVAL); 274*7c478bd9Sstevel@tonic-gate } 275*7c478bd9Sstevel@tonic-gate mark_closed(lsp, otyp); 276*7c478bd9Sstevel@tonic-gate mutex_exit(&lofi_lock); 277*7c478bd9Sstevel@tonic-gate return (0); 278*7c478bd9Sstevel@tonic-gate } 279*7c478bd9Sstevel@tonic-gate 280*7c478bd9Sstevel@tonic-gate /* 281*7c478bd9Sstevel@tonic-gate * This is basically what strategy used to be before we found we 282*7c478bd9Sstevel@tonic-gate * needed task queues. 283*7c478bd9Sstevel@tonic-gate */ 284*7c478bd9Sstevel@tonic-gate static void 285*7c478bd9Sstevel@tonic-gate lofi_strategy_task(void *arg) 286*7c478bd9Sstevel@tonic-gate { 287*7c478bd9Sstevel@tonic-gate struct buf *bp = (struct buf *)arg; 288*7c478bd9Sstevel@tonic-gate int error; 289*7c478bd9Sstevel@tonic-gate struct lofi_state *lsp; 290*7c478bd9Sstevel@tonic-gate offset_t offset, alignedoffset; 291*7c478bd9Sstevel@tonic-gate offset_t mapoffset; 292*7c478bd9Sstevel@tonic-gate caddr_t bufaddr; 293*7c478bd9Sstevel@tonic-gate caddr_t mapaddr; 294*7c478bd9Sstevel@tonic-gate size_t xfersize; 295*7c478bd9Sstevel@tonic-gate size_t len; 296*7c478bd9Sstevel@tonic-gate int isread; 297*7c478bd9Sstevel@tonic-gate int smflags; 298*7c478bd9Sstevel@tonic-gate enum seg_rw srw; 299*7c478bd9Sstevel@tonic-gate 300*7c478bd9Sstevel@tonic-gate lsp = ddi_get_soft_state(lofi_statep, getminor(bp->b_edev)); 301*7c478bd9Sstevel@tonic-gate if (lsp->ls_kstat) { 302*7c478bd9Sstevel@tonic-gate mutex_enter(lsp->ls_kstat->ks_lock); 303*7c478bd9Sstevel@tonic-gate kstat_waitq_to_runq(KSTAT_IO_PTR(lsp->ls_kstat)); 304*7c478bd9Sstevel@tonic-gate mutex_exit(lsp->ls_kstat->ks_lock); 305*7c478bd9Sstevel@tonic-gate } 306*7c478bd9Sstevel@tonic-gate bp_mapin(bp); 307*7c478bd9Sstevel@tonic-gate bufaddr = bp->b_un.b_addr; 308*7c478bd9Sstevel@tonic-gate offset = bp->b_lblkno * DEV_BSIZE; /* offset within file */ 309*7c478bd9Sstevel@tonic-gate 310*7c478bd9Sstevel@tonic-gate /* 311*7c478bd9Sstevel@tonic-gate * We used to always use vn_rdwr here, but we cannot do that because 312*7c478bd9Sstevel@tonic-gate * we might decide to read or write from the the underlying 313*7c478bd9Sstevel@tonic-gate * file during this call, which would be a deadlock because 314*7c478bd9Sstevel@tonic-gate * we have the rw_lock. So instead we page, unless it's not 315*7c478bd9Sstevel@tonic-gate * mapable or it's a character device. 316*7c478bd9Sstevel@tonic-gate */ 317*7c478bd9Sstevel@tonic-gate if (((lsp->ls_vp->v_flag & VNOMAP) == 0) && 318*7c478bd9Sstevel@tonic-gate (lsp->ls_vp->v_type != VCHR)) { 319*7c478bd9Sstevel@tonic-gate /* 320*7c478bd9Sstevel@tonic-gate * segmap always gives us an 8K (MAXBSIZE) chunk, aligned on 321*7c478bd9Sstevel@tonic-gate * an 8K boundary, but the buf transfer address may not be 322*7c478bd9Sstevel@tonic-gate * aligned on more than a 512-byte boundary (we don't 323*7c478bd9Sstevel@tonic-gate * enforce that, though we could). This matters since the 324*7c478bd9Sstevel@tonic-gate * initial part of the transfer may not start at offset 0 325*7c478bd9Sstevel@tonic-gate * within the segmap'd chunk. So we have to compensate for 326*7c478bd9Sstevel@tonic-gate * that with 'mapoffset'. Subsequent chunks always start 327*7c478bd9Sstevel@tonic-gate * off at the beginning, and the last is capped by b_resid. 328*7c478bd9Sstevel@tonic-gate */ 329*7c478bd9Sstevel@tonic-gate mapoffset = offset & MAXBOFFSET; 330*7c478bd9Sstevel@tonic-gate alignedoffset = offset - mapoffset; /* now map-aligned */ 331*7c478bd9Sstevel@tonic-gate bp->b_resid = bp->b_bcount; 332*7c478bd9Sstevel@tonic-gate isread = bp->b_flags & B_READ; 333*7c478bd9Sstevel@tonic-gate srw = isread ? S_READ : S_WRITE; 334*7c478bd9Sstevel@tonic-gate do { 335*7c478bd9Sstevel@tonic-gate xfersize = MIN(lsp->ls_vp_size - offset, 336*7c478bd9Sstevel@tonic-gate MIN(MAXBSIZE - mapoffset, bp->b_resid)); 337*7c478bd9Sstevel@tonic-gate len = roundup(mapoffset + xfersize, PAGESIZE); 338*7c478bd9Sstevel@tonic-gate mapaddr = segmap_getmapflt(segkmap, lsp->ls_vp, 339*7c478bd9Sstevel@tonic-gate alignedoffset, MAXBSIZE, 1, srw); 340*7c478bd9Sstevel@tonic-gate /* 341*7c478bd9Sstevel@tonic-gate * Now fault in the pages. This lets us check 342*7c478bd9Sstevel@tonic-gate * for errors before we reference mapaddr and 343*7c478bd9Sstevel@tonic-gate * try to resolve the fault in bcopy (which would 344*7c478bd9Sstevel@tonic-gate * panic instead). And this can easily happen, 345*7c478bd9Sstevel@tonic-gate * particularly if you've lofi'd a file over NFS 346*7c478bd9Sstevel@tonic-gate * and someone deletes the file on the server. 347*7c478bd9Sstevel@tonic-gate */ 348*7c478bd9Sstevel@tonic-gate error = segmap_fault(kas.a_hat, segkmap, mapaddr, 349*7c478bd9Sstevel@tonic-gate len, F_SOFTLOCK, srw); 350*7c478bd9Sstevel@tonic-gate if (error) { 351*7c478bd9Sstevel@tonic-gate (void) segmap_release(segkmap, mapaddr, 0); 352*7c478bd9Sstevel@tonic-gate if (FC_CODE(error) == FC_OBJERR) 353*7c478bd9Sstevel@tonic-gate error = FC_ERRNO(error); 354*7c478bd9Sstevel@tonic-gate else 355*7c478bd9Sstevel@tonic-gate error = EIO; 356*7c478bd9Sstevel@tonic-gate break; 357*7c478bd9Sstevel@tonic-gate } 358*7c478bd9Sstevel@tonic-gate smflags = 0; 359*7c478bd9Sstevel@tonic-gate if (isread) { 360*7c478bd9Sstevel@tonic-gate bcopy(mapaddr + mapoffset, bufaddr, xfersize); 361*7c478bd9Sstevel@tonic-gate } else { 362*7c478bd9Sstevel@tonic-gate smflags |= SM_WRITE; 363*7c478bd9Sstevel@tonic-gate bcopy(bufaddr, mapaddr + mapoffset, xfersize); 364*7c478bd9Sstevel@tonic-gate } 365*7c478bd9Sstevel@tonic-gate bp->b_resid -= xfersize; 366*7c478bd9Sstevel@tonic-gate bufaddr += xfersize; 367*7c478bd9Sstevel@tonic-gate offset += xfersize; 368*7c478bd9Sstevel@tonic-gate (void) segmap_fault(kas.a_hat, segkmap, mapaddr, 369*7c478bd9Sstevel@tonic-gate len, F_SOFTUNLOCK, srw); 370*7c478bd9Sstevel@tonic-gate error = segmap_release(segkmap, mapaddr, smflags); 371*7c478bd9Sstevel@tonic-gate /* only the first map may start partial */ 372*7c478bd9Sstevel@tonic-gate mapoffset = 0; 373*7c478bd9Sstevel@tonic-gate alignedoffset += MAXBSIZE; 374*7c478bd9Sstevel@tonic-gate } while ((error == 0) && (bp->b_resid > 0) && 375*7c478bd9Sstevel@tonic-gate (offset < lsp->ls_vp_size)); 376*7c478bd9Sstevel@tonic-gate } else { 377*7c478bd9Sstevel@tonic-gate ssize_t resid; 378*7c478bd9Sstevel@tonic-gate enum uio_rw rw; 379*7c478bd9Sstevel@tonic-gate 380*7c478bd9Sstevel@tonic-gate if (bp->b_flags & B_READ) 381*7c478bd9Sstevel@tonic-gate rw = UIO_READ; 382*7c478bd9Sstevel@tonic-gate else 383*7c478bd9Sstevel@tonic-gate rw = UIO_WRITE; 384*7c478bd9Sstevel@tonic-gate error = vn_rdwr(rw, lsp->ls_vp, bufaddr, bp->b_bcount, 385*7c478bd9Sstevel@tonic-gate offset, UIO_SYSSPACE, 0, RLIM64_INFINITY, kcred, &resid); 386*7c478bd9Sstevel@tonic-gate bp->b_resid = resid; 387*7c478bd9Sstevel@tonic-gate } 388*7c478bd9Sstevel@tonic-gate 389*7c478bd9Sstevel@tonic-gate if (lsp->ls_kstat) { 390*7c478bd9Sstevel@tonic-gate size_t n_done = bp->b_bcount - bp->b_resid; 391*7c478bd9Sstevel@tonic-gate kstat_io_t *kioptr; 392*7c478bd9Sstevel@tonic-gate 393*7c478bd9Sstevel@tonic-gate mutex_enter(lsp->ls_kstat->ks_lock); 394*7c478bd9Sstevel@tonic-gate kioptr = KSTAT_IO_PTR(lsp->ls_kstat); 395*7c478bd9Sstevel@tonic-gate if (bp->b_flags & B_READ) { 396*7c478bd9Sstevel@tonic-gate kioptr->nread += n_done; 397*7c478bd9Sstevel@tonic-gate kioptr->reads++; 398*7c478bd9Sstevel@tonic-gate } else { 399*7c478bd9Sstevel@tonic-gate kioptr->nwritten += n_done; 400*7c478bd9Sstevel@tonic-gate kioptr->writes++; 401*7c478bd9Sstevel@tonic-gate } 402*7c478bd9Sstevel@tonic-gate kstat_runq_exit(kioptr); 403*7c478bd9Sstevel@tonic-gate mutex_exit(lsp->ls_kstat->ks_lock); 404*7c478bd9Sstevel@tonic-gate } 405*7c478bd9Sstevel@tonic-gate bioerror(bp, error); 406*7c478bd9Sstevel@tonic-gate biodone(bp); 407*7c478bd9Sstevel@tonic-gate } 408*7c478bd9Sstevel@tonic-gate 409*7c478bd9Sstevel@tonic-gate static int 410*7c478bd9Sstevel@tonic-gate lofi_strategy(struct buf *bp) 411*7c478bd9Sstevel@tonic-gate { 412*7c478bd9Sstevel@tonic-gate struct lofi_state *lsp; 413*7c478bd9Sstevel@tonic-gate offset_t offset; 414*7c478bd9Sstevel@tonic-gate 415*7c478bd9Sstevel@tonic-gate /* 416*7c478bd9Sstevel@tonic-gate * We cannot just do I/O here, because the current thread 417*7c478bd9Sstevel@tonic-gate * _might_ end up back in here because the underlying filesystem 418*7c478bd9Sstevel@tonic-gate * wants a buffer, which eventually gets into bio_recycle and 419*7c478bd9Sstevel@tonic-gate * might call into lofi to write out a delayed-write buffer. 420*7c478bd9Sstevel@tonic-gate * This is bad if the filesystem above lofi is the same as below. 421*7c478bd9Sstevel@tonic-gate * 422*7c478bd9Sstevel@tonic-gate * We could come up with a complex strategy using threads to 423*7c478bd9Sstevel@tonic-gate * do the I/O asynchronously, or we could use task queues. task 424*7c478bd9Sstevel@tonic-gate * queues were incredibly easy so they win. 425*7c478bd9Sstevel@tonic-gate */ 426*7c478bd9Sstevel@tonic-gate lsp = ddi_get_soft_state(lofi_statep, getminor(bp->b_edev)); 427*7c478bd9Sstevel@tonic-gate offset = bp->b_lblkno * DEV_BSIZE; /* offset within file */ 428*7c478bd9Sstevel@tonic-gate if (offset == lsp->ls_vp_size) { 429*7c478bd9Sstevel@tonic-gate /* EOF */ 430*7c478bd9Sstevel@tonic-gate if ((bp->b_flags & B_READ) != 0) { 431*7c478bd9Sstevel@tonic-gate bp->b_resid = bp->b_bcount; 432*7c478bd9Sstevel@tonic-gate bioerror(bp, 0); 433*7c478bd9Sstevel@tonic-gate } else { 434*7c478bd9Sstevel@tonic-gate /* writes should fail */ 435*7c478bd9Sstevel@tonic-gate bioerror(bp, ENXIO); 436*7c478bd9Sstevel@tonic-gate } 437*7c478bd9Sstevel@tonic-gate biodone(bp); 438*7c478bd9Sstevel@tonic-gate return (0); 439*7c478bd9Sstevel@tonic-gate } 440*7c478bd9Sstevel@tonic-gate if (offset > lsp->ls_vp_size) { 441*7c478bd9Sstevel@tonic-gate bioerror(bp, ENXIO); 442*7c478bd9Sstevel@tonic-gate biodone(bp); 443*7c478bd9Sstevel@tonic-gate return (0); 444*7c478bd9Sstevel@tonic-gate } 445*7c478bd9Sstevel@tonic-gate if (lsp->ls_kstat) { 446*7c478bd9Sstevel@tonic-gate mutex_enter(lsp->ls_kstat->ks_lock); 447*7c478bd9Sstevel@tonic-gate kstat_waitq_enter(KSTAT_IO_PTR(lsp->ls_kstat)); 448*7c478bd9Sstevel@tonic-gate mutex_exit(lsp->ls_kstat->ks_lock); 449*7c478bd9Sstevel@tonic-gate } 450*7c478bd9Sstevel@tonic-gate (void) taskq_dispatch(lsp->ls_taskq, lofi_strategy_task, bp, KM_SLEEP); 451*7c478bd9Sstevel@tonic-gate return (0); 452*7c478bd9Sstevel@tonic-gate } 453*7c478bd9Sstevel@tonic-gate 454*7c478bd9Sstevel@tonic-gate /*ARGSUSED2*/ 455*7c478bd9Sstevel@tonic-gate static int 456*7c478bd9Sstevel@tonic-gate lofi_read(dev_t dev, struct uio *uio, struct cred *credp) 457*7c478bd9Sstevel@tonic-gate { 458*7c478bd9Sstevel@tonic-gate if (getminor(dev) == 0) 459*7c478bd9Sstevel@tonic-gate return (EINVAL); 460*7c478bd9Sstevel@tonic-gate return (physio(lofi_strategy, NULL, dev, B_READ, minphys, uio)); 461*7c478bd9Sstevel@tonic-gate } 462*7c478bd9Sstevel@tonic-gate 463*7c478bd9Sstevel@tonic-gate /*ARGSUSED2*/ 464*7c478bd9Sstevel@tonic-gate static int 465*7c478bd9Sstevel@tonic-gate lofi_write(dev_t dev, struct uio *uio, struct cred *credp) 466*7c478bd9Sstevel@tonic-gate { 467*7c478bd9Sstevel@tonic-gate if (getminor(dev) == 0) 468*7c478bd9Sstevel@tonic-gate return (EINVAL); 469*7c478bd9Sstevel@tonic-gate return (physio(lofi_strategy, NULL, dev, B_WRITE, minphys, uio)); 470*7c478bd9Sstevel@tonic-gate } 471*7c478bd9Sstevel@tonic-gate 472*7c478bd9Sstevel@tonic-gate /*ARGSUSED2*/ 473*7c478bd9Sstevel@tonic-gate static int 474*7c478bd9Sstevel@tonic-gate lofi_aread(dev_t dev, struct aio_req *aio, struct cred *credp) 475*7c478bd9Sstevel@tonic-gate { 476*7c478bd9Sstevel@tonic-gate if (getminor(dev) == 0) 477*7c478bd9Sstevel@tonic-gate return (EINVAL); 478*7c478bd9Sstevel@tonic-gate return (aphysio(lofi_strategy, anocancel, dev, B_READ, minphys, aio)); 479*7c478bd9Sstevel@tonic-gate } 480*7c478bd9Sstevel@tonic-gate 481*7c478bd9Sstevel@tonic-gate /*ARGSUSED2*/ 482*7c478bd9Sstevel@tonic-gate static int 483*7c478bd9Sstevel@tonic-gate lofi_awrite(dev_t dev, struct aio_req *aio, struct cred *credp) 484*7c478bd9Sstevel@tonic-gate { 485*7c478bd9Sstevel@tonic-gate if (getminor(dev) == 0) 486*7c478bd9Sstevel@tonic-gate return (EINVAL); 487*7c478bd9Sstevel@tonic-gate return (aphysio(lofi_strategy, anocancel, dev, B_WRITE, minphys, aio)); 488*7c478bd9Sstevel@tonic-gate } 489*7c478bd9Sstevel@tonic-gate 490*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 491*7c478bd9Sstevel@tonic-gate static int 492*7c478bd9Sstevel@tonic-gate lofi_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) 493*7c478bd9Sstevel@tonic-gate { 494*7c478bd9Sstevel@tonic-gate switch (infocmd) { 495*7c478bd9Sstevel@tonic-gate case DDI_INFO_DEVT2DEVINFO: 496*7c478bd9Sstevel@tonic-gate *result = lofi_dip; 497*7c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 498*7c478bd9Sstevel@tonic-gate case DDI_INFO_DEVT2INSTANCE: 499*7c478bd9Sstevel@tonic-gate *result = 0; 500*7c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 501*7c478bd9Sstevel@tonic-gate } 502*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 503*7c478bd9Sstevel@tonic-gate } 504*7c478bd9Sstevel@tonic-gate 505*7c478bd9Sstevel@tonic-gate static int 506*7c478bd9Sstevel@tonic-gate lofi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 507*7c478bd9Sstevel@tonic-gate { 508*7c478bd9Sstevel@tonic-gate int error; 509*7c478bd9Sstevel@tonic-gate 510*7c478bd9Sstevel@tonic-gate if (cmd != DDI_ATTACH) 511*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 512*7c478bd9Sstevel@tonic-gate error = ddi_soft_state_zalloc(lofi_statep, 0); 513*7c478bd9Sstevel@tonic-gate if (error == DDI_FAILURE) { 514*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 515*7c478bd9Sstevel@tonic-gate } 516*7c478bd9Sstevel@tonic-gate error = ddi_create_minor_node(dip, LOFI_CTL_NODE, S_IFCHR, 0, 517*7c478bd9Sstevel@tonic-gate DDI_PSEUDO, NULL); 518*7c478bd9Sstevel@tonic-gate if (error == DDI_FAILURE) { 519*7c478bd9Sstevel@tonic-gate ddi_soft_state_free(lofi_statep, 0); 520*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 521*7c478bd9Sstevel@tonic-gate } 522*7c478bd9Sstevel@tonic-gate lofi_dip = dip; 523*7c478bd9Sstevel@tonic-gate ddi_report_dev(dip); 524*7c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 525*7c478bd9Sstevel@tonic-gate } 526*7c478bd9Sstevel@tonic-gate 527*7c478bd9Sstevel@tonic-gate static int 528*7c478bd9Sstevel@tonic-gate lofi_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 529*7c478bd9Sstevel@tonic-gate { 530*7c478bd9Sstevel@tonic-gate if (cmd != DDI_DETACH) 531*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 532*7c478bd9Sstevel@tonic-gate if (lofi_busy()) 533*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 534*7c478bd9Sstevel@tonic-gate lofi_dip = NULL; 535*7c478bd9Sstevel@tonic-gate ddi_remove_minor_node(dip, NULL); 536*7c478bd9Sstevel@tonic-gate ddi_soft_state_free(lofi_statep, 0); 537*7c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 538*7c478bd9Sstevel@tonic-gate } 539*7c478bd9Sstevel@tonic-gate 540*7c478bd9Sstevel@tonic-gate /* 541*7c478bd9Sstevel@tonic-gate * These two just simplify the rest of the ioctls that need to copyin/out 542*7c478bd9Sstevel@tonic-gate * the lofi_ioctl structure. 543*7c478bd9Sstevel@tonic-gate */ 544*7c478bd9Sstevel@tonic-gate struct lofi_ioctl * 545*7c478bd9Sstevel@tonic-gate copy_in_lofi_ioctl(const struct lofi_ioctl *ulip) 546*7c478bd9Sstevel@tonic-gate { 547*7c478bd9Sstevel@tonic-gate struct lofi_ioctl *klip; 548*7c478bd9Sstevel@tonic-gate int error; 549*7c478bd9Sstevel@tonic-gate 550*7c478bd9Sstevel@tonic-gate klip = kmem_alloc(sizeof (struct lofi_ioctl), KM_SLEEP); 551*7c478bd9Sstevel@tonic-gate error = copyin(ulip, klip, sizeof (struct lofi_ioctl)); 552*7c478bd9Sstevel@tonic-gate if (error) { 553*7c478bd9Sstevel@tonic-gate kmem_free(klip, sizeof (struct lofi_ioctl)); 554*7c478bd9Sstevel@tonic-gate return (NULL); 555*7c478bd9Sstevel@tonic-gate } 556*7c478bd9Sstevel@tonic-gate 557*7c478bd9Sstevel@tonic-gate /* make sure filename is always null-terminated */ 558*7c478bd9Sstevel@tonic-gate klip->li_filename[MAXPATHLEN] = '\0'; 559*7c478bd9Sstevel@tonic-gate 560*7c478bd9Sstevel@tonic-gate /* validate minor number */ 561*7c478bd9Sstevel@tonic-gate if (klip->li_minor > lofi_max_files) { 562*7c478bd9Sstevel@tonic-gate kmem_free(klip, sizeof (struct lofi_ioctl)); 563*7c478bd9Sstevel@tonic-gate return (NULL); 564*7c478bd9Sstevel@tonic-gate } 565*7c478bd9Sstevel@tonic-gate return (klip); 566*7c478bd9Sstevel@tonic-gate } 567*7c478bd9Sstevel@tonic-gate 568*7c478bd9Sstevel@tonic-gate int 569*7c478bd9Sstevel@tonic-gate copy_out_lofi_ioctl(const struct lofi_ioctl *klip, struct lofi_ioctl *ulip) 570*7c478bd9Sstevel@tonic-gate { 571*7c478bd9Sstevel@tonic-gate int error; 572*7c478bd9Sstevel@tonic-gate 573*7c478bd9Sstevel@tonic-gate error = copyout(klip, ulip, sizeof (struct lofi_ioctl)); 574*7c478bd9Sstevel@tonic-gate if (error) 575*7c478bd9Sstevel@tonic-gate return (EFAULT); 576*7c478bd9Sstevel@tonic-gate return (0); 577*7c478bd9Sstevel@tonic-gate } 578*7c478bd9Sstevel@tonic-gate 579*7c478bd9Sstevel@tonic-gate void 580*7c478bd9Sstevel@tonic-gate free_lofi_ioctl(struct lofi_ioctl *klip) 581*7c478bd9Sstevel@tonic-gate { 582*7c478bd9Sstevel@tonic-gate kmem_free(klip, sizeof (struct lofi_ioctl)); 583*7c478bd9Sstevel@tonic-gate } 584*7c478bd9Sstevel@tonic-gate 585*7c478bd9Sstevel@tonic-gate /* 586*7c478bd9Sstevel@tonic-gate * Return the minor number 'filename' is mapped to, if it is. 587*7c478bd9Sstevel@tonic-gate */ 588*7c478bd9Sstevel@tonic-gate static int 589*7c478bd9Sstevel@tonic-gate file_to_minor(char *filename) 590*7c478bd9Sstevel@tonic-gate { 591*7c478bd9Sstevel@tonic-gate minor_t minor; 592*7c478bd9Sstevel@tonic-gate struct lofi_state *lsp; 593*7c478bd9Sstevel@tonic-gate 594*7c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(&lofi_lock)); 595*7c478bd9Sstevel@tonic-gate for (minor = 1; minor <= lofi_max_files; minor++) { 596*7c478bd9Sstevel@tonic-gate lsp = ddi_get_soft_state(lofi_statep, minor); 597*7c478bd9Sstevel@tonic-gate if (lsp == NULL) 598*7c478bd9Sstevel@tonic-gate continue; 599*7c478bd9Sstevel@tonic-gate if (strcmp(lsp->ls_filename, filename) == 0) 600*7c478bd9Sstevel@tonic-gate return (minor); 601*7c478bd9Sstevel@tonic-gate } 602*7c478bd9Sstevel@tonic-gate return (0); 603*7c478bd9Sstevel@tonic-gate } 604*7c478bd9Sstevel@tonic-gate 605*7c478bd9Sstevel@tonic-gate /* 606*7c478bd9Sstevel@tonic-gate * lofiadm does some validation, but since Joe Random (or crashme) could 607*7c478bd9Sstevel@tonic-gate * do our ioctls, we need to do some validation too. 608*7c478bd9Sstevel@tonic-gate */ 609*7c478bd9Sstevel@tonic-gate static int 610*7c478bd9Sstevel@tonic-gate valid_filename(const char *filename) 611*7c478bd9Sstevel@tonic-gate { 612*7c478bd9Sstevel@tonic-gate static char *blkprefix = "/dev/" LOFI_BLOCK_NAME "/"; 613*7c478bd9Sstevel@tonic-gate static char *charprefix = "/dev/" LOFI_CHAR_NAME "/"; 614*7c478bd9Sstevel@tonic-gate 615*7c478bd9Sstevel@tonic-gate /* must be absolute path */ 616*7c478bd9Sstevel@tonic-gate if (filename[0] != '/') 617*7c478bd9Sstevel@tonic-gate return (0); 618*7c478bd9Sstevel@tonic-gate /* must not be lofi */ 619*7c478bd9Sstevel@tonic-gate if (strncmp(filename, blkprefix, strlen(blkprefix)) == 0) 620*7c478bd9Sstevel@tonic-gate return (0); 621*7c478bd9Sstevel@tonic-gate if (strncmp(filename, charprefix, strlen(charprefix)) == 0) 622*7c478bd9Sstevel@tonic-gate return (0); 623*7c478bd9Sstevel@tonic-gate return (1); 624*7c478bd9Sstevel@tonic-gate } 625*7c478bd9Sstevel@tonic-gate 626*7c478bd9Sstevel@tonic-gate /* 627*7c478bd9Sstevel@tonic-gate * Fakes up a disk geometry, and one big partition, based on the size 628*7c478bd9Sstevel@tonic-gate * of the file. This is needed because we allow newfs'ing the device, 629*7c478bd9Sstevel@tonic-gate * and newfs will do several disk ioctls to figure out the geometry and 630*7c478bd9Sstevel@tonic-gate * partition information. It uses that information to determine the parameters 631*7c478bd9Sstevel@tonic-gate * to pass to mkfs. Geometry is pretty much irrelevent these days, but we 632*7c478bd9Sstevel@tonic-gate * have to support it. 633*7c478bd9Sstevel@tonic-gate */ 634*7c478bd9Sstevel@tonic-gate static void 635*7c478bd9Sstevel@tonic-gate fake_disk_geometry(struct lofi_state *lsp) 636*7c478bd9Sstevel@tonic-gate { 637*7c478bd9Sstevel@tonic-gate /* dk_geom - see dkio(7I) */ 638*7c478bd9Sstevel@tonic-gate /* 639*7c478bd9Sstevel@tonic-gate * dkg_ncyl _could_ be set to one here (one big cylinder with gobs 640*7c478bd9Sstevel@tonic-gate * of sectors), but that breaks programs like fdisk which want to 641*7c478bd9Sstevel@tonic-gate * partition a disk by cylinder. With one cylinder, you can't create 642*7c478bd9Sstevel@tonic-gate * an fdisk partition and put pcfs on it for testing (hard to pick 643*7c478bd9Sstevel@tonic-gate * a number between one and one). 644*7c478bd9Sstevel@tonic-gate * 645*7c478bd9Sstevel@tonic-gate * The cheezy floppy test is an attempt to not have too few cylinders 646*7c478bd9Sstevel@tonic-gate * for a small file, or so many on a big file that you waste space 647*7c478bd9Sstevel@tonic-gate * for backup superblocks or cylinder group structures. 648*7c478bd9Sstevel@tonic-gate */ 649*7c478bd9Sstevel@tonic-gate if (lsp->ls_vp_size < (2 * 1024 * 1024)) /* floppy? */ 650*7c478bd9Sstevel@tonic-gate lsp->ls_dkg.dkg_ncyl = lsp->ls_vp_size / (100 * 1024); 651*7c478bd9Sstevel@tonic-gate else 652*7c478bd9Sstevel@tonic-gate lsp->ls_dkg.dkg_ncyl = lsp->ls_vp_size / (300 * 1024); 653*7c478bd9Sstevel@tonic-gate /* in case file file is < 100k */ 654*7c478bd9Sstevel@tonic-gate if (lsp->ls_dkg.dkg_ncyl == 0) 655*7c478bd9Sstevel@tonic-gate lsp->ls_dkg.dkg_ncyl = 1; 656*7c478bd9Sstevel@tonic-gate lsp->ls_dkg.dkg_acyl = 0; 657*7c478bd9Sstevel@tonic-gate lsp->ls_dkg.dkg_bcyl = 0; 658*7c478bd9Sstevel@tonic-gate lsp->ls_dkg.dkg_nhead = 1; 659*7c478bd9Sstevel@tonic-gate lsp->ls_dkg.dkg_obs1 = 0; 660*7c478bd9Sstevel@tonic-gate lsp->ls_dkg.dkg_intrlv = 0; 661*7c478bd9Sstevel@tonic-gate lsp->ls_dkg.dkg_obs2 = 0; 662*7c478bd9Sstevel@tonic-gate lsp->ls_dkg.dkg_obs3 = 0; 663*7c478bd9Sstevel@tonic-gate lsp->ls_dkg.dkg_apc = 0; 664*7c478bd9Sstevel@tonic-gate lsp->ls_dkg.dkg_rpm = 7200; 665*7c478bd9Sstevel@tonic-gate lsp->ls_dkg.dkg_pcyl = lsp->ls_dkg.dkg_ncyl + lsp->ls_dkg.dkg_acyl; 666*7c478bd9Sstevel@tonic-gate lsp->ls_dkg.dkg_nsect = lsp->ls_vp_size / 667*7c478bd9Sstevel@tonic-gate (DEV_BSIZE * lsp->ls_dkg.dkg_ncyl); 668*7c478bd9Sstevel@tonic-gate lsp->ls_dkg.dkg_write_reinstruct = 0; 669*7c478bd9Sstevel@tonic-gate lsp->ls_dkg.dkg_read_reinstruct = 0; 670*7c478bd9Sstevel@tonic-gate 671*7c478bd9Sstevel@tonic-gate /* vtoc - see dkio(7I) */ 672*7c478bd9Sstevel@tonic-gate bzero(&lsp->ls_vtoc, sizeof (struct vtoc)); 673*7c478bd9Sstevel@tonic-gate lsp->ls_vtoc.v_sanity = VTOC_SANE; 674*7c478bd9Sstevel@tonic-gate lsp->ls_vtoc.v_version = V_VERSION; 675*7c478bd9Sstevel@tonic-gate bcopy(LOFI_DRIVER_NAME, lsp->ls_vtoc.v_volume, 7); 676*7c478bd9Sstevel@tonic-gate lsp->ls_vtoc.v_sectorsz = DEV_BSIZE; 677*7c478bd9Sstevel@tonic-gate lsp->ls_vtoc.v_nparts = 1; 678*7c478bd9Sstevel@tonic-gate lsp->ls_vtoc.v_part[0].p_tag = V_UNASSIGNED; 679*7c478bd9Sstevel@tonic-gate lsp->ls_vtoc.v_part[0].p_flag = V_UNMNT; 680*7c478bd9Sstevel@tonic-gate lsp->ls_vtoc.v_part[0].p_start = (daddr_t)0; 681*7c478bd9Sstevel@tonic-gate /* 682*7c478bd9Sstevel@tonic-gate * The partition size cannot just be the number of sectors, because 683*7c478bd9Sstevel@tonic-gate * that might not end on a cylinder boundary. And if that's the case, 684*7c478bd9Sstevel@tonic-gate * newfs/mkfs will print a scary warning. So just figure the size 685*7c478bd9Sstevel@tonic-gate * based on the number of cylinders and sectors/cylinder. 686*7c478bd9Sstevel@tonic-gate */ 687*7c478bd9Sstevel@tonic-gate lsp->ls_vtoc.v_part[0].p_size = lsp->ls_dkg.dkg_pcyl * 688*7c478bd9Sstevel@tonic-gate lsp->ls_dkg.dkg_nsect * lsp->ls_dkg.dkg_nhead; 689*7c478bd9Sstevel@tonic-gate 690*7c478bd9Sstevel@tonic-gate /* dk_cinfo - see dkio(7I) */ 691*7c478bd9Sstevel@tonic-gate bzero(&lsp->ls_ci, sizeof (struct dk_cinfo)); 692*7c478bd9Sstevel@tonic-gate (void) strcpy(lsp->ls_ci.dki_cname, LOFI_DRIVER_NAME); 693*7c478bd9Sstevel@tonic-gate lsp->ls_ci.dki_ctype = DKC_MD; 694*7c478bd9Sstevel@tonic-gate lsp->ls_ci.dki_flags = 0; 695*7c478bd9Sstevel@tonic-gate lsp->ls_ci.dki_cnum = 0; 696*7c478bd9Sstevel@tonic-gate lsp->ls_ci.dki_addr = 0; 697*7c478bd9Sstevel@tonic-gate lsp->ls_ci.dki_space = 0; 698*7c478bd9Sstevel@tonic-gate lsp->ls_ci.dki_prio = 0; 699*7c478bd9Sstevel@tonic-gate lsp->ls_ci.dki_vec = 0; 700*7c478bd9Sstevel@tonic-gate (void) strcpy(lsp->ls_ci.dki_dname, LOFI_DRIVER_NAME); 701*7c478bd9Sstevel@tonic-gate lsp->ls_ci.dki_unit = 0; 702*7c478bd9Sstevel@tonic-gate lsp->ls_ci.dki_slave = 0; 703*7c478bd9Sstevel@tonic-gate lsp->ls_ci.dki_partition = 0; 704*7c478bd9Sstevel@tonic-gate /* 705*7c478bd9Sstevel@tonic-gate * newfs uses this to set maxcontig. Must not be < 16, or it 706*7c478bd9Sstevel@tonic-gate * will be 0 when newfs multiplies it by DEV_BSIZE and divides 707*7c478bd9Sstevel@tonic-gate * it by the block size. Then tunefs doesn't work because 708*7c478bd9Sstevel@tonic-gate * maxcontig is 0. 709*7c478bd9Sstevel@tonic-gate */ 710*7c478bd9Sstevel@tonic-gate lsp->ls_ci.dki_maxtransfer = 16; 711*7c478bd9Sstevel@tonic-gate } 712*7c478bd9Sstevel@tonic-gate 713*7c478bd9Sstevel@tonic-gate /* 714*7c478bd9Sstevel@tonic-gate * map a file to a minor number. Return the minor number. 715*7c478bd9Sstevel@tonic-gate */ 716*7c478bd9Sstevel@tonic-gate static int 717*7c478bd9Sstevel@tonic-gate lofi_map_file(dev_t dev, struct lofi_ioctl *ulip, int pickminor, 718*7c478bd9Sstevel@tonic-gate int *rvalp, struct cred *credp) 719*7c478bd9Sstevel@tonic-gate { 720*7c478bd9Sstevel@tonic-gate minor_t newminor; 721*7c478bd9Sstevel@tonic-gate struct lofi_state *lsp; 722*7c478bd9Sstevel@tonic-gate struct lofi_ioctl *klip; 723*7c478bd9Sstevel@tonic-gate int error; 724*7c478bd9Sstevel@tonic-gate char namebuf[50]; 725*7c478bd9Sstevel@tonic-gate struct vnode *vp; 726*7c478bd9Sstevel@tonic-gate int64_t Nblocks_prop_val; 727*7c478bd9Sstevel@tonic-gate int64_t Size_prop_val; 728*7c478bd9Sstevel@tonic-gate vattr_t vattr; 729*7c478bd9Sstevel@tonic-gate int flag; 730*7c478bd9Sstevel@tonic-gate enum vtype v_type; 731*7c478bd9Sstevel@tonic-gate dev_t newdev; 732*7c478bd9Sstevel@tonic-gate int zalloced = 0; 733*7c478bd9Sstevel@tonic-gate 734*7c478bd9Sstevel@tonic-gate klip = copy_in_lofi_ioctl(ulip); 735*7c478bd9Sstevel@tonic-gate if (klip == NULL) 736*7c478bd9Sstevel@tonic-gate return (EFAULT); 737*7c478bd9Sstevel@tonic-gate 738*7c478bd9Sstevel@tonic-gate mutex_enter(&lofi_lock); 739*7c478bd9Sstevel@tonic-gate 740*7c478bd9Sstevel@tonic-gate if (!valid_filename(klip->li_filename)) { 741*7c478bd9Sstevel@tonic-gate error = EINVAL; 742*7c478bd9Sstevel@tonic-gate goto out; 743*7c478bd9Sstevel@tonic-gate } 744*7c478bd9Sstevel@tonic-gate 745*7c478bd9Sstevel@tonic-gate if (file_to_minor(klip->li_filename) != 0) { 746*7c478bd9Sstevel@tonic-gate error = EBUSY; 747*7c478bd9Sstevel@tonic-gate goto out; 748*7c478bd9Sstevel@tonic-gate } 749*7c478bd9Sstevel@tonic-gate 750*7c478bd9Sstevel@tonic-gate if (pickminor) { 751*7c478bd9Sstevel@tonic-gate /* Find a free one */ 752*7c478bd9Sstevel@tonic-gate for (newminor = 1; newminor <= lofi_max_files; newminor++) 753*7c478bd9Sstevel@tonic-gate if (ddi_get_soft_state(lofi_statep, newminor) == NULL) 754*7c478bd9Sstevel@tonic-gate break; 755*7c478bd9Sstevel@tonic-gate if (newminor >= lofi_max_files) { 756*7c478bd9Sstevel@tonic-gate error = EAGAIN; 757*7c478bd9Sstevel@tonic-gate goto out; 758*7c478bd9Sstevel@tonic-gate } 759*7c478bd9Sstevel@tonic-gate } else { 760*7c478bd9Sstevel@tonic-gate newminor = klip->li_minor; 761*7c478bd9Sstevel@tonic-gate if (ddi_get_soft_state(lofi_statep, newminor) != NULL) { 762*7c478bd9Sstevel@tonic-gate error = EEXIST; 763*7c478bd9Sstevel@tonic-gate goto out; 764*7c478bd9Sstevel@tonic-gate } 765*7c478bd9Sstevel@tonic-gate } 766*7c478bd9Sstevel@tonic-gate 767*7c478bd9Sstevel@tonic-gate /* make sure it's valid */ 768*7c478bd9Sstevel@tonic-gate error = lookupname(klip->li_filename, UIO_SYSSPACE, FOLLOW, 769*7c478bd9Sstevel@tonic-gate NULLVPP, &vp); 770*7c478bd9Sstevel@tonic-gate if (error) { 771*7c478bd9Sstevel@tonic-gate goto out; 772*7c478bd9Sstevel@tonic-gate } 773*7c478bd9Sstevel@tonic-gate v_type = vp->v_type; 774*7c478bd9Sstevel@tonic-gate VN_RELE(vp); 775*7c478bd9Sstevel@tonic-gate if (!V_ISLOFIABLE(v_type)) { 776*7c478bd9Sstevel@tonic-gate error = EINVAL; 777*7c478bd9Sstevel@tonic-gate goto out; 778*7c478bd9Sstevel@tonic-gate } 779*7c478bd9Sstevel@tonic-gate flag = FREAD | FWRITE | FOFFMAX | FEXCL; 780*7c478bd9Sstevel@tonic-gate error = vn_open(klip->li_filename, UIO_SYSSPACE, flag, 0, &vp, 0, 0); 781*7c478bd9Sstevel@tonic-gate if (error) { 782*7c478bd9Sstevel@tonic-gate /* try read-only */ 783*7c478bd9Sstevel@tonic-gate flag &= ~FWRITE; 784*7c478bd9Sstevel@tonic-gate error = vn_open(klip->li_filename, UIO_SYSSPACE, flag, 0, 785*7c478bd9Sstevel@tonic-gate &vp, 0, 0); 786*7c478bd9Sstevel@tonic-gate if (error) { 787*7c478bd9Sstevel@tonic-gate goto out; 788*7c478bd9Sstevel@tonic-gate } 789*7c478bd9Sstevel@tonic-gate } 790*7c478bd9Sstevel@tonic-gate vattr.va_mask = AT_SIZE; 791*7c478bd9Sstevel@tonic-gate error = VOP_GETATTR(vp, &vattr, 0, credp); 792*7c478bd9Sstevel@tonic-gate if (error) { 793*7c478bd9Sstevel@tonic-gate goto closeout; 794*7c478bd9Sstevel@tonic-gate } 795*7c478bd9Sstevel@tonic-gate /* the file needs to be a multiple of the block size */ 796*7c478bd9Sstevel@tonic-gate if ((vattr.va_size % DEV_BSIZE) != 0) { 797*7c478bd9Sstevel@tonic-gate error = EINVAL; 798*7c478bd9Sstevel@tonic-gate goto closeout; 799*7c478bd9Sstevel@tonic-gate } 800*7c478bd9Sstevel@tonic-gate newdev = makedevice(getmajor(dev), newminor); 801*7c478bd9Sstevel@tonic-gate Size_prop_val = vattr.va_size; 802*7c478bd9Sstevel@tonic-gate if ((ddi_prop_update_int64(newdev, lofi_dip, 803*7c478bd9Sstevel@tonic-gate SIZE_PROP_NAME, Size_prop_val)) != DDI_PROP_SUCCESS) { 804*7c478bd9Sstevel@tonic-gate error = EINVAL; 805*7c478bd9Sstevel@tonic-gate goto closeout; 806*7c478bd9Sstevel@tonic-gate } 807*7c478bd9Sstevel@tonic-gate Nblocks_prop_val = vattr.va_size / DEV_BSIZE; 808*7c478bd9Sstevel@tonic-gate if ((ddi_prop_update_int64(newdev, lofi_dip, 809*7c478bd9Sstevel@tonic-gate NBLOCKS_PROP_NAME, Nblocks_prop_val)) != DDI_PROP_SUCCESS) { 810*7c478bd9Sstevel@tonic-gate error = EINVAL; 811*7c478bd9Sstevel@tonic-gate goto propout; 812*7c478bd9Sstevel@tonic-gate } 813*7c478bd9Sstevel@tonic-gate error = ddi_soft_state_zalloc(lofi_statep, newminor); 814*7c478bd9Sstevel@tonic-gate if (error == DDI_FAILURE) { 815*7c478bd9Sstevel@tonic-gate error = ENOMEM; 816*7c478bd9Sstevel@tonic-gate goto propout; 817*7c478bd9Sstevel@tonic-gate } 818*7c478bd9Sstevel@tonic-gate zalloced = 1; 819*7c478bd9Sstevel@tonic-gate (void) snprintf(namebuf, sizeof (namebuf), "%d", newminor); 820*7c478bd9Sstevel@tonic-gate (void) ddi_create_minor_node(lofi_dip, namebuf, S_IFBLK, newminor, 821*7c478bd9Sstevel@tonic-gate DDI_PSEUDO, NULL); 822*7c478bd9Sstevel@tonic-gate if (error != DDI_SUCCESS) { 823*7c478bd9Sstevel@tonic-gate error = ENXIO; 824*7c478bd9Sstevel@tonic-gate goto propout; 825*7c478bd9Sstevel@tonic-gate } 826*7c478bd9Sstevel@tonic-gate (void) snprintf(namebuf, sizeof (namebuf), "%d,raw", newminor); 827*7c478bd9Sstevel@tonic-gate error = ddi_create_minor_node(lofi_dip, namebuf, S_IFCHR, newminor, 828*7c478bd9Sstevel@tonic-gate DDI_PSEUDO, NULL); 829*7c478bd9Sstevel@tonic-gate if (error != DDI_SUCCESS) { 830*7c478bd9Sstevel@tonic-gate /* remove block node */ 831*7c478bd9Sstevel@tonic-gate (void) snprintf(namebuf, sizeof (namebuf), "%d", newminor); 832*7c478bd9Sstevel@tonic-gate ddi_remove_minor_node(lofi_dip, namebuf); 833*7c478bd9Sstevel@tonic-gate error = ENXIO; 834*7c478bd9Sstevel@tonic-gate goto propout; 835*7c478bd9Sstevel@tonic-gate } 836*7c478bd9Sstevel@tonic-gate lsp = ddi_get_soft_state(lofi_statep, newminor); 837*7c478bd9Sstevel@tonic-gate lsp->ls_filename_sz = strlen(klip->li_filename) + 1; 838*7c478bd9Sstevel@tonic-gate lsp->ls_filename = kmem_alloc(lsp->ls_filename_sz, KM_SLEEP); 839*7c478bd9Sstevel@tonic-gate (void) snprintf(namebuf, sizeof (namebuf), "%s_taskq_%d", 840*7c478bd9Sstevel@tonic-gate LOFI_DRIVER_NAME, newminor); 841*7c478bd9Sstevel@tonic-gate lsp->ls_taskq = taskq_create(namebuf, lofi_taskq_nthreads, 842*7c478bd9Sstevel@tonic-gate minclsyspri, 1, lofi_taskq_maxalloc, 0); 843*7c478bd9Sstevel@tonic-gate lsp->ls_kstat = kstat_create(LOFI_DRIVER_NAME, newminor, 844*7c478bd9Sstevel@tonic-gate NULL, "disk", KSTAT_TYPE_IO, 1, 0); 845*7c478bd9Sstevel@tonic-gate if (lsp->ls_kstat) { 846*7c478bd9Sstevel@tonic-gate mutex_init(&lsp->ls_kstat_lock, NULL, MUTEX_DRIVER, NULL); 847*7c478bd9Sstevel@tonic-gate lsp->ls_kstat->ks_lock = &lsp->ls_kstat_lock; 848*7c478bd9Sstevel@tonic-gate kstat_install(lsp->ls_kstat); 849*7c478bd9Sstevel@tonic-gate } 850*7c478bd9Sstevel@tonic-gate /* 851*7c478bd9Sstevel@tonic-gate * save open mode so file can be closed properly and vnode counts 852*7c478bd9Sstevel@tonic-gate * updated correctly. 853*7c478bd9Sstevel@tonic-gate */ 854*7c478bd9Sstevel@tonic-gate lsp->ls_openflag = flag; 855*7c478bd9Sstevel@tonic-gate 856*7c478bd9Sstevel@tonic-gate /* 857*7c478bd9Sstevel@tonic-gate * Try to handle stacked lofs vnodes. 858*7c478bd9Sstevel@tonic-gate */ 859*7c478bd9Sstevel@tonic-gate if (vp->v_type == VREG) { 860*7c478bd9Sstevel@tonic-gate if (VOP_REALVP(vp, &lsp->ls_vp) != 0) { 861*7c478bd9Sstevel@tonic-gate lsp->ls_vp = vp; 862*7c478bd9Sstevel@tonic-gate } else { 863*7c478bd9Sstevel@tonic-gate /* 864*7c478bd9Sstevel@tonic-gate * Even though vp was obtained via vn_open(), we 865*7c478bd9Sstevel@tonic-gate * can't call vn_close() on it, since lofs will 866*7c478bd9Sstevel@tonic-gate * pass the VOP_CLOSE() on down to the realvp 867*7c478bd9Sstevel@tonic-gate * (which we are about to use). Hence we merely 868*7c478bd9Sstevel@tonic-gate * drop the reference to the lofs vnode and hold 869*7c478bd9Sstevel@tonic-gate * the realvp so things behave as if we've 870*7c478bd9Sstevel@tonic-gate * opened the realvp without any interaction 871*7c478bd9Sstevel@tonic-gate * with lofs. 872*7c478bd9Sstevel@tonic-gate */ 873*7c478bd9Sstevel@tonic-gate VN_HOLD(lsp->ls_vp); 874*7c478bd9Sstevel@tonic-gate VN_RELE(vp); 875*7c478bd9Sstevel@tonic-gate } 876*7c478bd9Sstevel@tonic-gate } else { 877*7c478bd9Sstevel@tonic-gate lsp->ls_vp = vp; 878*7c478bd9Sstevel@tonic-gate } 879*7c478bd9Sstevel@tonic-gate lsp->ls_vp_size = vattr.va_size; 880*7c478bd9Sstevel@tonic-gate (void) strcpy(lsp->ls_filename, klip->li_filename); 881*7c478bd9Sstevel@tonic-gate if (rvalp) 882*7c478bd9Sstevel@tonic-gate *rvalp = (int)newminor; 883*7c478bd9Sstevel@tonic-gate klip->li_minor = newminor; 884*7c478bd9Sstevel@tonic-gate 885*7c478bd9Sstevel@tonic-gate fake_disk_geometry(lsp); 886*7c478bd9Sstevel@tonic-gate mutex_exit(&lofi_lock); 887*7c478bd9Sstevel@tonic-gate (void) copy_out_lofi_ioctl(klip, ulip); 888*7c478bd9Sstevel@tonic-gate free_lofi_ioctl(klip); 889*7c478bd9Sstevel@tonic-gate return (0); 890*7c478bd9Sstevel@tonic-gate 891*7c478bd9Sstevel@tonic-gate propout: 892*7c478bd9Sstevel@tonic-gate (void) ddi_prop_remove(newdev, lofi_dip, SIZE_PROP_NAME); 893*7c478bd9Sstevel@tonic-gate (void) ddi_prop_remove(newdev, lofi_dip, NBLOCKS_PROP_NAME); 894*7c478bd9Sstevel@tonic-gate closeout: 895*7c478bd9Sstevel@tonic-gate (void) VOP_CLOSE(vp, flag, 1, 0, credp); 896*7c478bd9Sstevel@tonic-gate VN_RELE(vp); 897*7c478bd9Sstevel@tonic-gate out: 898*7c478bd9Sstevel@tonic-gate if (zalloced) 899*7c478bd9Sstevel@tonic-gate ddi_soft_state_free(lofi_statep, newminor); 900*7c478bd9Sstevel@tonic-gate mutex_exit(&lofi_lock); 901*7c478bd9Sstevel@tonic-gate free_lofi_ioctl(klip); 902*7c478bd9Sstevel@tonic-gate return (error); 903*7c478bd9Sstevel@tonic-gate } 904*7c478bd9Sstevel@tonic-gate 905*7c478bd9Sstevel@tonic-gate /* 906*7c478bd9Sstevel@tonic-gate * unmap a file. 907*7c478bd9Sstevel@tonic-gate */ 908*7c478bd9Sstevel@tonic-gate static int 909*7c478bd9Sstevel@tonic-gate lofi_unmap_file(dev_t dev, struct lofi_ioctl *ulip, int byfilename, 910*7c478bd9Sstevel@tonic-gate struct cred *credp) 911*7c478bd9Sstevel@tonic-gate { 912*7c478bd9Sstevel@tonic-gate struct lofi_state *lsp; 913*7c478bd9Sstevel@tonic-gate struct lofi_ioctl *klip; 914*7c478bd9Sstevel@tonic-gate minor_t minor; 915*7c478bd9Sstevel@tonic-gate char namebuf[20]; 916*7c478bd9Sstevel@tonic-gate dev_t newdev; 917*7c478bd9Sstevel@tonic-gate 918*7c478bd9Sstevel@tonic-gate klip = copy_in_lofi_ioctl(ulip); 919*7c478bd9Sstevel@tonic-gate if (klip == NULL) 920*7c478bd9Sstevel@tonic-gate return (EFAULT); 921*7c478bd9Sstevel@tonic-gate 922*7c478bd9Sstevel@tonic-gate mutex_enter(&lofi_lock); 923*7c478bd9Sstevel@tonic-gate if (byfilename) { 924*7c478bd9Sstevel@tonic-gate minor = file_to_minor(klip->li_filename); 925*7c478bd9Sstevel@tonic-gate } else { 926*7c478bd9Sstevel@tonic-gate minor = klip->li_minor; 927*7c478bd9Sstevel@tonic-gate } 928*7c478bd9Sstevel@tonic-gate if (minor == 0) { 929*7c478bd9Sstevel@tonic-gate mutex_exit(&lofi_lock); 930*7c478bd9Sstevel@tonic-gate free_lofi_ioctl(klip); 931*7c478bd9Sstevel@tonic-gate return (ENXIO); 932*7c478bd9Sstevel@tonic-gate } 933*7c478bd9Sstevel@tonic-gate lsp = ddi_get_soft_state(lofi_statep, minor); 934*7c478bd9Sstevel@tonic-gate if (lsp == NULL) { 935*7c478bd9Sstevel@tonic-gate mutex_exit(&lofi_lock); 936*7c478bd9Sstevel@tonic-gate free_lofi_ioctl(klip); 937*7c478bd9Sstevel@tonic-gate return (ENXIO); 938*7c478bd9Sstevel@tonic-gate } 939*7c478bd9Sstevel@tonic-gate if (is_opened(lsp)) { 940*7c478bd9Sstevel@tonic-gate mutex_exit(&lofi_lock); 941*7c478bd9Sstevel@tonic-gate free_lofi_ioctl(klip); 942*7c478bd9Sstevel@tonic-gate return (EBUSY); 943*7c478bd9Sstevel@tonic-gate } 944*7c478bd9Sstevel@tonic-gate /* 945*7c478bd9Sstevel@tonic-gate * Use saved open mode to properly update vnode counts 946*7c478bd9Sstevel@tonic-gate */ 947*7c478bd9Sstevel@tonic-gate (void) VOP_CLOSE(lsp->ls_vp, lsp->ls_openflag, 1, 0, credp); 948*7c478bd9Sstevel@tonic-gate VN_RELE(lsp->ls_vp); 949*7c478bd9Sstevel@tonic-gate lsp->ls_vp = NULL; 950*7c478bd9Sstevel@tonic-gate newdev = makedevice(getmajor(dev), minor); 951*7c478bd9Sstevel@tonic-gate (void) ddi_prop_remove(newdev, lofi_dip, SIZE_PROP_NAME); 952*7c478bd9Sstevel@tonic-gate (void) ddi_prop_remove(newdev, lofi_dip, NBLOCKS_PROP_NAME); 953*7c478bd9Sstevel@tonic-gate 954*7c478bd9Sstevel@tonic-gate (void) snprintf(namebuf, sizeof (namebuf), "%d", minor); 955*7c478bd9Sstevel@tonic-gate ddi_remove_minor_node(lofi_dip, namebuf); 956*7c478bd9Sstevel@tonic-gate (void) snprintf(namebuf, sizeof (namebuf), "%d,raw", minor); 957*7c478bd9Sstevel@tonic-gate ddi_remove_minor_node(lofi_dip, namebuf); 958*7c478bd9Sstevel@tonic-gate 959*7c478bd9Sstevel@tonic-gate kmem_free(lsp->ls_filename, lsp->ls_filename_sz); 960*7c478bd9Sstevel@tonic-gate taskq_destroy(lsp->ls_taskq); 961*7c478bd9Sstevel@tonic-gate if (lsp->ls_kstat) { 962*7c478bd9Sstevel@tonic-gate kstat_delete(lsp->ls_kstat); 963*7c478bd9Sstevel@tonic-gate mutex_destroy(&lsp->ls_kstat_lock); 964*7c478bd9Sstevel@tonic-gate } 965*7c478bd9Sstevel@tonic-gate ddi_soft_state_free(lofi_statep, minor); 966*7c478bd9Sstevel@tonic-gate klip->li_minor = minor; 967*7c478bd9Sstevel@tonic-gate mutex_exit(&lofi_lock); 968*7c478bd9Sstevel@tonic-gate (void) copy_out_lofi_ioctl(klip, ulip); 969*7c478bd9Sstevel@tonic-gate free_lofi_ioctl(klip); 970*7c478bd9Sstevel@tonic-gate return (0); 971*7c478bd9Sstevel@tonic-gate } 972*7c478bd9Sstevel@tonic-gate 973*7c478bd9Sstevel@tonic-gate /* 974*7c478bd9Sstevel@tonic-gate * get the filename given the minor number, or the minor number given 975*7c478bd9Sstevel@tonic-gate * the name. 976*7c478bd9Sstevel@tonic-gate */ 977*7c478bd9Sstevel@tonic-gate /*ARGSUSED3*/ 978*7c478bd9Sstevel@tonic-gate static int 979*7c478bd9Sstevel@tonic-gate lofi_get_info(dev_t dev, struct lofi_ioctl *ulip, int which, 980*7c478bd9Sstevel@tonic-gate struct cred *credp) 981*7c478bd9Sstevel@tonic-gate { 982*7c478bd9Sstevel@tonic-gate struct lofi_state *lsp; 983*7c478bd9Sstevel@tonic-gate struct lofi_ioctl *klip; 984*7c478bd9Sstevel@tonic-gate int error; 985*7c478bd9Sstevel@tonic-gate minor_t minor; 986*7c478bd9Sstevel@tonic-gate 987*7c478bd9Sstevel@tonic-gate #ifdef lint 988*7c478bd9Sstevel@tonic-gate dev = dev; 989*7c478bd9Sstevel@tonic-gate #endif 990*7c478bd9Sstevel@tonic-gate klip = copy_in_lofi_ioctl(ulip); 991*7c478bd9Sstevel@tonic-gate if (klip == NULL) 992*7c478bd9Sstevel@tonic-gate return (EFAULT); 993*7c478bd9Sstevel@tonic-gate 994*7c478bd9Sstevel@tonic-gate switch (which) { 995*7c478bd9Sstevel@tonic-gate case LOFI_GET_FILENAME: 996*7c478bd9Sstevel@tonic-gate minor = klip->li_minor; 997*7c478bd9Sstevel@tonic-gate if (minor == 0) { 998*7c478bd9Sstevel@tonic-gate free_lofi_ioctl(klip); 999*7c478bd9Sstevel@tonic-gate return (EINVAL); 1000*7c478bd9Sstevel@tonic-gate } 1001*7c478bd9Sstevel@tonic-gate 1002*7c478bd9Sstevel@tonic-gate mutex_enter(&lofi_lock); 1003*7c478bd9Sstevel@tonic-gate lsp = ddi_get_soft_state(lofi_statep, minor); 1004*7c478bd9Sstevel@tonic-gate if (lsp == NULL) { 1005*7c478bd9Sstevel@tonic-gate mutex_exit(&lofi_lock); 1006*7c478bd9Sstevel@tonic-gate free_lofi_ioctl(klip); 1007*7c478bd9Sstevel@tonic-gate return (ENXIO); 1008*7c478bd9Sstevel@tonic-gate } 1009*7c478bd9Sstevel@tonic-gate (void) strcpy(klip->li_filename, lsp->ls_filename); 1010*7c478bd9Sstevel@tonic-gate mutex_exit(&lofi_lock); 1011*7c478bd9Sstevel@tonic-gate error = copy_out_lofi_ioctl(klip, ulip); 1012*7c478bd9Sstevel@tonic-gate free_lofi_ioctl(klip); 1013*7c478bd9Sstevel@tonic-gate return (error); 1014*7c478bd9Sstevel@tonic-gate case LOFI_GET_MINOR: 1015*7c478bd9Sstevel@tonic-gate mutex_enter(&lofi_lock); 1016*7c478bd9Sstevel@tonic-gate klip->li_minor = file_to_minor(klip->li_filename); 1017*7c478bd9Sstevel@tonic-gate mutex_exit(&lofi_lock); 1018*7c478bd9Sstevel@tonic-gate if (klip->li_minor == 0) { 1019*7c478bd9Sstevel@tonic-gate free_lofi_ioctl(klip); 1020*7c478bd9Sstevel@tonic-gate return (ENOENT); 1021*7c478bd9Sstevel@tonic-gate } 1022*7c478bd9Sstevel@tonic-gate error = copy_out_lofi_ioctl(klip, ulip); 1023*7c478bd9Sstevel@tonic-gate free_lofi_ioctl(klip); 1024*7c478bd9Sstevel@tonic-gate return (error); 1025*7c478bd9Sstevel@tonic-gate default: 1026*7c478bd9Sstevel@tonic-gate free_lofi_ioctl(klip); 1027*7c478bd9Sstevel@tonic-gate return (EINVAL); 1028*7c478bd9Sstevel@tonic-gate } 1029*7c478bd9Sstevel@tonic-gate 1030*7c478bd9Sstevel@tonic-gate } 1031*7c478bd9Sstevel@tonic-gate 1032*7c478bd9Sstevel@tonic-gate static int 1033*7c478bd9Sstevel@tonic-gate lofi_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *credp, 1034*7c478bd9Sstevel@tonic-gate int *rvalp) 1035*7c478bd9Sstevel@tonic-gate { 1036*7c478bd9Sstevel@tonic-gate int error; 1037*7c478bd9Sstevel@tonic-gate enum dkio_state dkstate; 1038*7c478bd9Sstevel@tonic-gate struct lofi_state *lsp; 1039*7c478bd9Sstevel@tonic-gate minor_t minor; 1040*7c478bd9Sstevel@tonic-gate 1041*7c478bd9Sstevel@tonic-gate #ifdef lint 1042*7c478bd9Sstevel@tonic-gate credp = credp; 1043*7c478bd9Sstevel@tonic-gate #endif 1044*7c478bd9Sstevel@tonic-gate 1045*7c478bd9Sstevel@tonic-gate minor = getminor(dev); 1046*7c478bd9Sstevel@tonic-gate /* lofi ioctls only apply to the master device */ 1047*7c478bd9Sstevel@tonic-gate if (minor == 0) { 1048*7c478bd9Sstevel@tonic-gate struct lofi_ioctl *lip = (struct lofi_ioctl *)arg; 1049*7c478bd9Sstevel@tonic-gate 1050*7c478bd9Sstevel@tonic-gate /* 1051*7c478bd9Sstevel@tonic-gate * the query command only need read-access - i.e., normal 1052*7c478bd9Sstevel@tonic-gate * users are allowed to do those on the ctl device as 1053*7c478bd9Sstevel@tonic-gate * long as they can open it read-only. 1054*7c478bd9Sstevel@tonic-gate */ 1055*7c478bd9Sstevel@tonic-gate switch (cmd) { 1056*7c478bd9Sstevel@tonic-gate case LOFI_MAP_FILE: 1057*7c478bd9Sstevel@tonic-gate if ((flag & FWRITE) == 0) 1058*7c478bd9Sstevel@tonic-gate return (EPERM); 1059*7c478bd9Sstevel@tonic-gate return (lofi_map_file(dev, lip, 1, rvalp, credp)); 1060*7c478bd9Sstevel@tonic-gate case LOFI_MAP_FILE_MINOR: 1061*7c478bd9Sstevel@tonic-gate if ((flag & FWRITE) == 0) 1062*7c478bd9Sstevel@tonic-gate return (EPERM); 1063*7c478bd9Sstevel@tonic-gate return (lofi_map_file(dev, lip, 0, rvalp, credp)); 1064*7c478bd9Sstevel@tonic-gate case LOFI_UNMAP_FILE: 1065*7c478bd9Sstevel@tonic-gate if ((flag & FWRITE) == 0) 1066*7c478bd9Sstevel@tonic-gate return (EPERM); 1067*7c478bd9Sstevel@tonic-gate return (lofi_unmap_file(dev, lip, 1, credp)); 1068*7c478bd9Sstevel@tonic-gate case LOFI_UNMAP_FILE_MINOR: 1069*7c478bd9Sstevel@tonic-gate if ((flag & FWRITE) == 0) 1070*7c478bd9Sstevel@tonic-gate return (EPERM); 1071*7c478bd9Sstevel@tonic-gate return (lofi_unmap_file(dev, lip, 0, credp)); 1072*7c478bd9Sstevel@tonic-gate case LOFI_GET_FILENAME: 1073*7c478bd9Sstevel@tonic-gate return (lofi_get_info(dev, lip, LOFI_GET_FILENAME, 1074*7c478bd9Sstevel@tonic-gate credp)); 1075*7c478bd9Sstevel@tonic-gate case LOFI_GET_MINOR: 1076*7c478bd9Sstevel@tonic-gate return (lofi_get_info(dev, lip, LOFI_GET_MINOR, 1077*7c478bd9Sstevel@tonic-gate credp)); 1078*7c478bd9Sstevel@tonic-gate case LOFI_GET_MAXMINOR: 1079*7c478bd9Sstevel@tonic-gate error = copyout(&lofi_max_files, &lip->li_minor, 1080*7c478bd9Sstevel@tonic-gate sizeof (lofi_max_files)); 1081*7c478bd9Sstevel@tonic-gate if (error) 1082*7c478bd9Sstevel@tonic-gate return (EFAULT); 1083*7c478bd9Sstevel@tonic-gate return (0); 1084*7c478bd9Sstevel@tonic-gate default: 1085*7c478bd9Sstevel@tonic-gate break; 1086*7c478bd9Sstevel@tonic-gate } 1087*7c478bd9Sstevel@tonic-gate } 1088*7c478bd9Sstevel@tonic-gate 1089*7c478bd9Sstevel@tonic-gate lsp = ddi_get_soft_state(lofi_statep, minor); 1090*7c478bd9Sstevel@tonic-gate if (lsp == NULL) 1091*7c478bd9Sstevel@tonic-gate return (ENXIO); 1092*7c478bd9Sstevel@tonic-gate 1093*7c478bd9Sstevel@tonic-gate /* these are for faking out utilities like newfs */ 1094*7c478bd9Sstevel@tonic-gate switch (cmd) { 1095*7c478bd9Sstevel@tonic-gate case VOLIOCINFO: 1096*7c478bd9Sstevel@tonic-gate /* pcfs does this to see if it needs to set PCFS_NOCHK */ 1097*7c478bd9Sstevel@tonic-gate /* 0 means it should set it */ 1098*7c478bd9Sstevel@tonic-gate return (0); 1099*7c478bd9Sstevel@tonic-gate case DKIOCGVTOC: 1100*7c478bd9Sstevel@tonic-gate switch (ddi_model_convert_from(flag & FMODELS)) { 1101*7c478bd9Sstevel@tonic-gate case DDI_MODEL_ILP32: { 1102*7c478bd9Sstevel@tonic-gate struct vtoc32 vtoc32; 1103*7c478bd9Sstevel@tonic-gate 1104*7c478bd9Sstevel@tonic-gate vtoctovtoc32(lsp->ls_vtoc, vtoc32); 1105*7c478bd9Sstevel@tonic-gate if (ddi_copyout(&vtoc32, (void *)arg, 1106*7c478bd9Sstevel@tonic-gate sizeof (struct vtoc32), flag)) 1107*7c478bd9Sstevel@tonic-gate return (EFAULT); 1108*7c478bd9Sstevel@tonic-gate break; 1109*7c478bd9Sstevel@tonic-gate } 1110*7c478bd9Sstevel@tonic-gate 1111*7c478bd9Sstevel@tonic-gate case DDI_MODEL_NONE: 1112*7c478bd9Sstevel@tonic-gate if (ddi_copyout(&lsp->ls_vtoc, (void *)arg, 1113*7c478bd9Sstevel@tonic-gate sizeof (struct vtoc), flag)) 1114*7c478bd9Sstevel@tonic-gate return (EFAULT); 1115*7c478bd9Sstevel@tonic-gate break; 1116*7c478bd9Sstevel@tonic-gate } 1117*7c478bd9Sstevel@tonic-gate return (0); 1118*7c478bd9Sstevel@tonic-gate case DKIOCINFO: 1119*7c478bd9Sstevel@tonic-gate error = copyout(&lsp->ls_ci, (void *)arg, 1120*7c478bd9Sstevel@tonic-gate sizeof (struct dk_cinfo)); 1121*7c478bd9Sstevel@tonic-gate if (error) 1122*7c478bd9Sstevel@tonic-gate return (EFAULT); 1123*7c478bd9Sstevel@tonic-gate return (0); 1124*7c478bd9Sstevel@tonic-gate case DKIOCG_VIRTGEOM: 1125*7c478bd9Sstevel@tonic-gate case DKIOCG_PHYGEOM: 1126*7c478bd9Sstevel@tonic-gate case DKIOCGGEOM: 1127*7c478bd9Sstevel@tonic-gate error = copyout(&lsp->ls_dkg, (void *)arg, 1128*7c478bd9Sstevel@tonic-gate sizeof (struct dk_geom)); 1129*7c478bd9Sstevel@tonic-gate if (error) 1130*7c478bd9Sstevel@tonic-gate return (EFAULT); 1131*7c478bd9Sstevel@tonic-gate return (0); 1132*7c478bd9Sstevel@tonic-gate case DKIOCSTATE: 1133*7c478bd9Sstevel@tonic-gate /* the file is always there */ 1134*7c478bd9Sstevel@tonic-gate dkstate = DKIO_INSERTED; 1135*7c478bd9Sstevel@tonic-gate error = copyout(&dkstate, (void *)arg, 1136*7c478bd9Sstevel@tonic-gate sizeof (enum dkio_state)); 1137*7c478bd9Sstevel@tonic-gate if (error) 1138*7c478bd9Sstevel@tonic-gate return (EFAULT); 1139*7c478bd9Sstevel@tonic-gate return (0); 1140*7c478bd9Sstevel@tonic-gate default: 1141*7c478bd9Sstevel@tonic-gate return (ENOTTY); 1142*7c478bd9Sstevel@tonic-gate } 1143*7c478bd9Sstevel@tonic-gate } 1144*7c478bd9Sstevel@tonic-gate 1145*7c478bd9Sstevel@tonic-gate static struct cb_ops lofi_cb_ops = { 1146*7c478bd9Sstevel@tonic-gate lofi_open, /* open */ 1147*7c478bd9Sstevel@tonic-gate lofi_close, /* close */ 1148*7c478bd9Sstevel@tonic-gate lofi_strategy, /* strategy */ 1149*7c478bd9Sstevel@tonic-gate nodev, /* print */ 1150*7c478bd9Sstevel@tonic-gate nodev, /* dump */ 1151*7c478bd9Sstevel@tonic-gate lofi_read, /* read */ 1152*7c478bd9Sstevel@tonic-gate lofi_write, /* write */ 1153*7c478bd9Sstevel@tonic-gate lofi_ioctl, /* ioctl */ 1154*7c478bd9Sstevel@tonic-gate nodev, /* devmap */ 1155*7c478bd9Sstevel@tonic-gate nodev, /* mmap */ 1156*7c478bd9Sstevel@tonic-gate nodev, /* segmap */ 1157*7c478bd9Sstevel@tonic-gate nochpoll, /* poll */ 1158*7c478bd9Sstevel@tonic-gate ddi_prop_op, /* prop_op */ 1159*7c478bd9Sstevel@tonic-gate 0, /* streamtab */ 1160*7c478bd9Sstevel@tonic-gate D_64BIT | D_NEW | D_MP, /* Driver compatibility flag */ 1161*7c478bd9Sstevel@tonic-gate CB_REV, 1162*7c478bd9Sstevel@tonic-gate lofi_aread, 1163*7c478bd9Sstevel@tonic-gate lofi_awrite 1164*7c478bd9Sstevel@tonic-gate }; 1165*7c478bd9Sstevel@tonic-gate 1166*7c478bd9Sstevel@tonic-gate static struct dev_ops lofi_ops = { 1167*7c478bd9Sstevel@tonic-gate DEVO_REV, /* devo_rev, */ 1168*7c478bd9Sstevel@tonic-gate 0, /* refcnt */ 1169*7c478bd9Sstevel@tonic-gate lofi_info, /* info */ 1170*7c478bd9Sstevel@tonic-gate nulldev, /* identify */ 1171*7c478bd9Sstevel@tonic-gate nulldev, /* probe */ 1172*7c478bd9Sstevel@tonic-gate lofi_attach, /* attach */ 1173*7c478bd9Sstevel@tonic-gate lofi_detach, /* detach */ 1174*7c478bd9Sstevel@tonic-gate nodev, /* reset */ 1175*7c478bd9Sstevel@tonic-gate &lofi_cb_ops, /* driver operations */ 1176*7c478bd9Sstevel@tonic-gate NULL /* no bus operations */ 1177*7c478bd9Sstevel@tonic-gate }; 1178*7c478bd9Sstevel@tonic-gate 1179*7c478bd9Sstevel@tonic-gate static struct modldrv modldrv = { 1180*7c478bd9Sstevel@tonic-gate &mod_driverops, 1181*7c478bd9Sstevel@tonic-gate "loopback file driver (%I%)", 1182*7c478bd9Sstevel@tonic-gate &lofi_ops, 1183*7c478bd9Sstevel@tonic-gate }; 1184*7c478bd9Sstevel@tonic-gate 1185*7c478bd9Sstevel@tonic-gate static struct modlinkage modlinkage = { 1186*7c478bd9Sstevel@tonic-gate MODREV_1, 1187*7c478bd9Sstevel@tonic-gate &modldrv, 1188*7c478bd9Sstevel@tonic-gate NULL 1189*7c478bd9Sstevel@tonic-gate }; 1190*7c478bd9Sstevel@tonic-gate 1191*7c478bd9Sstevel@tonic-gate int 1192*7c478bd9Sstevel@tonic-gate _init(void) 1193*7c478bd9Sstevel@tonic-gate { 1194*7c478bd9Sstevel@tonic-gate int error; 1195*7c478bd9Sstevel@tonic-gate 1196*7c478bd9Sstevel@tonic-gate error = ddi_soft_state_init(&lofi_statep, 1197*7c478bd9Sstevel@tonic-gate sizeof (struct lofi_state), 0); 1198*7c478bd9Sstevel@tonic-gate if (error) 1199*7c478bd9Sstevel@tonic-gate return (error); 1200*7c478bd9Sstevel@tonic-gate 1201*7c478bd9Sstevel@tonic-gate mutex_init(&lofi_lock, NULL, MUTEX_DRIVER, NULL); 1202*7c478bd9Sstevel@tonic-gate error = mod_install(&modlinkage); 1203*7c478bd9Sstevel@tonic-gate if (error) { 1204*7c478bd9Sstevel@tonic-gate mutex_destroy(&lofi_lock); 1205*7c478bd9Sstevel@tonic-gate ddi_soft_state_fini(&lofi_statep); 1206*7c478bd9Sstevel@tonic-gate } 1207*7c478bd9Sstevel@tonic-gate 1208*7c478bd9Sstevel@tonic-gate return (error); 1209*7c478bd9Sstevel@tonic-gate } 1210*7c478bd9Sstevel@tonic-gate 1211*7c478bd9Sstevel@tonic-gate int 1212*7c478bd9Sstevel@tonic-gate _fini(void) 1213*7c478bd9Sstevel@tonic-gate { 1214*7c478bd9Sstevel@tonic-gate int error; 1215*7c478bd9Sstevel@tonic-gate 1216*7c478bd9Sstevel@tonic-gate if (lofi_busy()) 1217*7c478bd9Sstevel@tonic-gate return (EBUSY); 1218*7c478bd9Sstevel@tonic-gate 1219*7c478bd9Sstevel@tonic-gate error = mod_remove(&modlinkage); 1220*7c478bd9Sstevel@tonic-gate if (error) 1221*7c478bd9Sstevel@tonic-gate return (error); 1222*7c478bd9Sstevel@tonic-gate 1223*7c478bd9Sstevel@tonic-gate mutex_destroy(&lofi_lock); 1224*7c478bd9Sstevel@tonic-gate ddi_soft_state_fini(&lofi_statep); 1225*7c478bd9Sstevel@tonic-gate 1226*7c478bd9Sstevel@tonic-gate return (error); 1227*7c478bd9Sstevel@tonic-gate } 1228*7c478bd9Sstevel@tonic-gate 1229*7c478bd9Sstevel@tonic-gate int 1230*7c478bd9Sstevel@tonic-gate _info(struct modinfo *modinfop) 1231*7c478bd9Sstevel@tonic-gate { 1232*7c478bd9Sstevel@tonic-gate return (mod_info(&modlinkage, modinfop)); 1233*7c478bd9Sstevel@tonic-gate } 1234