17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5bd07e074Sheppo * Common Development and Distribution License (the "License"). 6bd07e074Sheppo * You may not use this file except in compliance with the License. 77c478bd9Sstevel@tonic-gate * 87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 117c478bd9Sstevel@tonic-gate * and limitations under the License. 127c478bd9Sstevel@tonic-gate * 137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 187c478bd9Sstevel@tonic-gate * 197c478bd9Sstevel@tonic-gate * CDDL HEADER END 207c478bd9Sstevel@tonic-gate */ 217c478bd9Sstevel@tonic-gate /* 22*3d7072f8Seschrock * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 237c478bd9Sstevel@tonic-gate * Use is subject to license terms. 247c478bd9Sstevel@tonic-gate */ 257c478bd9Sstevel@tonic-gate 267c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 277c478bd9Sstevel@tonic-gate 287c478bd9Sstevel@tonic-gate /* 297c478bd9Sstevel@tonic-gate * lofi (loopback file) driver - allows you to attach a file to a device, 307c478bd9Sstevel@tonic-gate * which can then be accessed through that device. The simple model is that 317c478bd9Sstevel@tonic-gate * you tell lofi to open a file, and then use the block device you get as 327c478bd9Sstevel@tonic-gate * you would any block device. lofi translates access to the block device 337c478bd9Sstevel@tonic-gate * into I/O on the underlying file. This is mostly useful for 347c478bd9Sstevel@tonic-gate * mounting images of filesystems. 357c478bd9Sstevel@tonic-gate * 367c478bd9Sstevel@tonic-gate * lofi is controlled through /dev/lofictl - this is the only device exported 377c478bd9Sstevel@tonic-gate * during attach, and is minor number 0. lofiadm communicates with lofi through 387c478bd9Sstevel@tonic-gate * ioctls on this device. When a file is attached to lofi, block and character 397c478bd9Sstevel@tonic-gate * devices are exported in /dev/lofi and /dev/rlofi. Currently, these devices 407c478bd9Sstevel@tonic-gate * are identified by their minor number, and the minor number is also used 417c478bd9Sstevel@tonic-gate * as the name in /dev/lofi. If we ever decide to support virtual disks, 427c478bd9Sstevel@tonic-gate * we'll have to divide the minor number space to identify fdisk partitions 437c478bd9Sstevel@tonic-gate * and slices, and the name will then be the minor number shifted down a 447c478bd9Sstevel@tonic-gate * few bits. Minor devices are tracked with state structures handled with 457c478bd9Sstevel@tonic-gate * ddi_soft_state(9F) for simplicity. 467c478bd9Sstevel@tonic-gate * 477c478bd9Sstevel@tonic-gate * A file attached to lofi is opened when attached and not closed until 487c478bd9Sstevel@tonic-gate * explicitly detached from lofi. This seems more sensible than deferring 497c478bd9Sstevel@tonic-gate * the open until the /dev/lofi device is opened, for a number of reasons. 507c478bd9Sstevel@tonic-gate * One is that any failure is likely to be noticed by the person (or script) 517c478bd9Sstevel@tonic-gate * running lofiadm. Another is that it would be a security problem if the 527c478bd9Sstevel@tonic-gate * file was replaced by another one after being added but before being opened. 537c478bd9Sstevel@tonic-gate * 547c478bd9Sstevel@tonic-gate * The only hard part about lofi is the ioctls. In order to support things 557c478bd9Sstevel@tonic-gate * like 'newfs' on a lofi device, it needs to support certain disk ioctls. 567c478bd9Sstevel@tonic-gate * So it has to fake disk geometry and partition information. More may need 577c478bd9Sstevel@tonic-gate * to be faked if your favorite utility doesn't work and you think it should 587c478bd9Sstevel@tonic-gate * (fdformat doesn't work because it really wants to know the type of floppy 597c478bd9Sstevel@tonic-gate * controller to talk to, and that didn't seem easy to fake. Or possibly even 607c478bd9Sstevel@tonic-gate * necessary, since we have mkfs_pcfs now). 617c478bd9Sstevel@tonic-gate * 62*3d7072f8Seschrock * Normally, a lofi device cannot be detached if it is open (i.e. busy). To 63*3d7072f8Seschrock * support simulation of hotplug events, an optional force flag is provided. 64*3d7072f8Seschrock * If a lofi device is open when a force detach is requested, then the 65*3d7072f8Seschrock * underlying file is closed and any subsequent operations return EIO. When the 66*3d7072f8Seschrock * device is closed for the last time, it will be cleaned up at that time. In 67*3d7072f8Seschrock * addition, the DKIOCSTATE ioctl will return DKIO_DEV_GONE when the device is 68*3d7072f8Seschrock * detached but not removed. 69*3d7072f8Seschrock * 707c478bd9Sstevel@tonic-gate * Known problems: 717c478bd9Sstevel@tonic-gate * 727c478bd9Sstevel@tonic-gate * UFS logging. Mounting a UFS filesystem image "logging" 737c478bd9Sstevel@tonic-gate * works for basic copy testing but wedges during a build of ON through 747c478bd9Sstevel@tonic-gate * that image. Some deadlock in lufs holding the log mutex and then 757c478bd9Sstevel@tonic-gate * getting stuck on a buf. So for now, don't do that. 767c478bd9Sstevel@tonic-gate * 777c478bd9Sstevel@tonic-gate * Direct I/O. Since the filesystem data is being cached in the buffer 787c478bd9Sstevel@tonic-gate * cache, _and_ again in the underlying filesystem, it's tempting to 797c478bd9Sstevel@tonic-gate * enable direct I/O on the underlying file. Don't, because that deadlocks. 807c478bd9Sstevel@tonic-gate * I think to fix the cache-twice problem we might need filesystem support. 817c478bd9Sstevel@tonic-gate * 827c478bd9Sstevel@tonic-gate * lofi on itself. The simple lock strategy (lofi_lock) precludes this 837c478bd9Sstevel@tonic-gate * because you'll be in lofi_ioctl, holding the lock when you open the 847c478bd9Sstevel@tonic-gate * file, which, if it's lofi, will grab lofi_lock. We prevent this for 857c478bd9Sstevel@tonic-gate * now, though not using ddi_soft_state(9F) would make it possible to 867c478bd9Sstevel@tonic-gate * do. Though it would still be silly. 877c478bd9Sstevel@tonic-gate * 887c478bd9Sstevel@tonic-gate * Interesting things to do: 897c478bd9Sstevel@tonic-gate * 907c478bd9Sstevel@tonic-gate * Allow multiple files for each device. A poor-man's metadisk, basically. 917c478bd9Sstevel@tonic-gate * 927c478bd9Sstevel@tonic-gate * Pass-through ioctls on block devices. You can (though it's not 937c478bd9Sstevel@tonic-gate * documented), give lofi a block device as a file name. Then we shouldn't 947c478bd9Sstevel@tonic-gate * need to fake a geometry. But this is also silly unless you're replacing 957c478bd9Sstevel@tonic-gate * metadisk. 967c478bd9Sstevel@tonic-gate * 977c478bd9Sstevel@tonic-gate * Encryption. tpm would like this. Apparently Windows 2000 has it, and 987c478bd9Sstevel@tonic-gate * so does Linux. 997c478bd9Sstevel@tonic-gate */ 1007c478bd9Sstevel@tonic-gate 1017c478bd9Sstevel@tonic-gate #include <sys/types.h> 1027c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h> 1037c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h> 1047c478bd9Sstevel@tonic-gate #include <sys/uio.h> 1057c478bd9Sstevel@tonic-gate #include <sys/kmem.h> 1067c478bd9Sstevel@tonic-gate #include <sys/cred.h> 1077c478bd9Sstevel@tonic-gate #include <sys/mman.h> 1087c478bd9Sstevel@tonic-gate #include <sys/errno.h> 1097c478bd9Sstevel@tonic-gate #include <sys/aio_req.h> 1107c478bd9Sstevel@tonic-gate #include <sys/stat.h> 1117c478bd9Sstevel@tonic-gate #include <sys/file.h> 1127c478bd9Sstevel@tonic-gate #include <sys/modctl.h> 1137c478bd9Sstevel@tonic-gate #include <sys/conf.h> 1147c478bd9Sstevel@tonic-gate #include <sys/debug.h> 1157c478bd9Sstevel@tonic-gate #include <sys/vnode.h> 1167c478bd9Sstevel@tonic-gate #include <sys/lofi.h> 1177c478bd9Sstevel@tonic-gate #include <sys/fcntl.h> 1187c478bd9Sstevel@tonic-gate #include <sys/pathname.h> 1197c478bd9Sstevel@tonic-gate #include <sys/filio.h> 1207c478bd9Sstevel@tonic-gate #include <sys/fdio.h> 1217c478bd9Sstevel@tonic-gate #include <sys/open.h> 1227c478bd9Sstevel@tonic-gate #include <sys/disp.h> 1237c478bd9Sstevel@tonic-gate #include <vm/seg_map.h> 1247c478bd9Sstevel@tonic-gate #include <sys/ddi.h> 1257c478bd9Sstevel@tonic-gate #include <sys/sunddi.h> 1267c478bd9Sstevel@tonic-gate 1277c478bd9Sstevel@tonic-gate /* seems safer than having to get the string right many times */ 1287c478bd9Sstevel@tonic-gate #define NBLOCKS_PROP_NAME "Nblocks" 1297c478bd9Sstevel@tonic-gate #define SIZE_PROP_NAME "Size" 1307c478bd9Sstevel@tonic-gate 1317c478bd9Sstevel@tonic-gate static dev_info_t *lofi_dip; 1327c478bd9Sstevel@tonic-gate static void *lofi_statep; 1337c478bd9Sstevel@tonic-gate static kmutex_t lofi_lock; /* state lock */ 1347c478bd9Sstevel@tonic-gate 1357c478bd9Sstevel@tonic-gate /* 1367c478bd9Sstevel@tonic-gate * Because lofi_taskq_nthreads limits the actual swamping of the device, the 1377c478bd9Sstevel@tonic-gate * maxalloc parameter (lofi_taskq_maxalloc) should be tuned conservatively 1387c478bd9Sstevel@tonic-gate * high. If we want to be assured that the underlying device is always busy, 1397c478bd9Sstevel@tonic-gate * we must be sure that the number of bytes enqueued when the number of 1407c478bd9Sstevel@tonic-gate * enqueued tasks exceeds maxalloc is sufficient to keep the device busy for 1417c478bd9Sstevel@tonic-gate * the duration of the sleep time in taskq_ent_alloc(). That is, lofi should 1427c478bd9Sstevel@tonic-gate * set maxalloc to be the maximum throughput (in bytes per second) of the 1437c478bd9Sstevel@tonic-gate * underlying device divided by the minimum I/O size. We assume a realistic 1447c478bd9Sstevel@tonic-gate * maximum throughput of one hundred megabytes per second; we set maxalloc on 1457c478bd9Sstevel@tonic-gate * the lofi task queue to be 104857600 divided by DEV_BSIZE. 1467c478bd9Sstevel@tonic-gate */ 1477c478bd9Sstevel@tonic-gate static int lofi_taskq_maxalloc = 104857600 / DEV_BSIZE; 1487c478bd9Sstevel@tonic-gate static int lofi_taskq_nthreads = 4; /* # of taskq threads per device */ 1497c478bd9Sstevel@tonic-gate 1507c478bd9Sstevel@tonic-gate uint32_t lofi_max_files = LOFI_MAX_FILES; 1517c478bd9Sstevel@tonic-gate 1527c478bd9Sstevel@tonic-gate static int 1537c478bd9Sstevel@tonic-gate lofi_busy(void) 1547c478bd9Sstevel@tonic-gate { 1557c478bd9Sstevel@tonic-gate minor_t minor; 1567c478bd9Sstevel@tonic-gate 1577c478bd9Sstevel@tonic-gate /* 1587c478bd9Sstevel@tonic-gate * We need to make sure no mappings exist - mod_remove won't 1597c478bd9Sstevel@tonic-gate * help because the device isn't open. 1607c478bd9Sstevel@tonic-gate */ 1617c478bd9Sstevel@tonic-gate mutex_enter(&lofi_lock); 1627c478bd9Sstevel@tonic-gate for (minor = 1; minor <= lofi_max_files; minor++) { 1637c478bd9Sstevel@tonic-gate if (ddi_get_soft_state(lofi_statep, minor) != NULL) { 1647c478bd9Sstevel@tonic-gate mutex_exit(&lofi_lock); 1657c478bd9Sstevel@tonic-gate return (EBUSY); 1667c478bd9Sstevel@tonic-gate } 1677c478bd9Sstevel@tonic-gate } 1687c478bd9Sstevel@tonic-gate mutex_exit(&lofi_lock); 1697c478bd9Sstevel@tonic-gate return (0); 1707c478bd9Sstevel@tonic-gate } 1717c478bd9Sstevel@tonic-gate 1727c478bd9Sstevel@tonic-gate static int 1737c478bd9Sstevel@tonic-gate is_opened(struct lofi_state *lsp) 1747c478bd9Sstevel@tonic-gate { 1757c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(&lofi_lock)); 1767c478bd9Sstevel@tonic-gate return (lsp->ls_chr_open || lsp->ls_blk_open || lsp->ls_lyr_open_count); 1777c478bd9Sstevel@tonic-gate } 1787c478bd9Sstevel@tonic-gate 1797c478bd9Sstevel@tonic-gate static int 1807c478bd9Sstevel@tonic-gate mark_opened(struct lofi_state *lsp, int otyp) 1817c478bd9Sstevel@tonic-gate { 1827c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(&lofi_lock)); 1837c478bd9Sstevel@tonic-gate switch (otyp) { 1847c478bd9Sstevel@tonic-gate case OTYP_CHR: 1857c478bd9Sstevel@tonic-gate lsp->ls_chr_open = 1; 1867c478bd9Sstevel@tonic-gate break; 1877c478bd9Sstevel@tonic-gate case OTYP_BLK: 1887c478bd9Sstevel@tonic-gate lsp->ls_blk_open = 1; 1897c478bd9Sstevel@tonic-gate break; 1907c478bd9Sstevel@tonic-gate case OTYP_LYR: 1917c478bd9Sstevel@tonic-gate lsp->ls_lyr_open_count++; 1927c478bd9Sstevel@tonic-gate break; 1937c478bd9Sstevel@tonic-gate default: 1947c478bd9Sstevel@tonic-gate return (-1); 1957c478bd9Sstevel@tonic-gate } 1967c478bd9Sstevel@tonic-gate return (0); 1977c478bd9Sstevel@tonic-gate } 1987c478bd9Sstevel@tonic-gate 1997c478bd9Sstevel@tonic-gate static void 2007c478bd9Sstevel@tonic-gate mark_closed(struct lofi_state *lsp, int otyp) 2017c478bd9Sstevel@tonic-gate { 2027c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(&lofi_lock)); 2037c478bd9Sstevel@tonic-gate switch (otyp) { 2047c478bd9Sstevel@tonic-gate case OTYP_CHR: 2057c478bd9Sstevel@tonic-gate lsp->ls_chr_open = 0; 2067c478bd9Sstevel@tonic-gate break; 2077c478bd9Sstevel@tonic-gate case OTYP_BLK: 2087c478bd9Sstevel@tonic-gate lsp->ls_blk_open = 0; 2097c478bd9Sstevel@tonic-gate break; 2107c478bd9Sstevel@tonic-gate case OTYP_LYR: 2117c478bd9Sstevel@tonic-gate lsp->ls_lyr_open_count--; 2127c478bd9Sstevel@tonic-gate break; 2137c478bd9Sstevel@tonic-gate default: 2147c478bd9Sstevel@tonic-gate break; 2157c478bd9Sstevel@tonic-gate } 2167c478bd9Sstevel@tonic-gate } 2177c478bd9Sstevel@tonic-gate 218*3d7072f8Seschrock static void 219*3d7072f8Seschrock lofi_free_handle(dev_t dev, minor_t minor, struct lofi_state *lsp, 220*3d7072f8Seschrock cred_t *credp) 221*3d7072f8Seschrock { 222*3d7072f8Seschrock dev_t newdev; 223*3d7072f8Seschrock char namebuf[50]; 224*3d7072f8Seschrock 225*3d7072f8Seschrock if (lsp->ls_vp) { 226*3d7072f8Seschrock (void) VOP_CLOSE(lsp->ls_vp, lsp->ls_openflag, 1, 0, credp); 227*3d7072f8Seschrock VN_RELE(lsp->ls_vp); 228*3d7072f8Seschrock lsp->ls_vp = NULL; 229*3d7072f8Seschrock } 230*3d7072f8Seschrock 231*3d7072f8Seschrock newdev = makedevice(getmajor(dev), minor); 232*3d7072f8Seschrock (void) ddi_prop_remove(newdev, lofi_dip, SIZE_PROP_NAME); 233*3d7072f8Seschrock (void) ddi_prop_remove(newdev, lofi_dip, NBLOCKS_PROP_NAME); 234*3d7072f8Seschrock 235*3d7072f8Seschrock (void) snprintf(namebuf, sizeof (namebuf), "%d", minor); 236*3d7072f8Seschrock ddi_remove_minor_node(lofi_dip, namebuf); 237*3d7072f8Seschrock (void) snprintf(namebuf, sizeof (namebuf), "%d,raw", minor); 238*3d7072f8Seschrock ddi_remove_minor_node(lofi_dip, namebuf); 239*3d7072f8Seschrock 240*3d7072f8Seschrock kmem_free(lsp->ls_filename, lsp->ls_filename_sz); 241*3d7072f8Seschrock taskq_destroy(lsp->ls_taskq); 242*3d7072f8Seschrock if (lsp->ls_kstat) { 243*3d7072f8Seschrock kstat_delete(lsp->ls_kstat); 244*3d7072f8Seschrock mutex_destroy(&lsp->ls_kstat_lock); 245*3d7072f8Seschrock } 246*3d7072f8Seschrock ddi_soft_state_free(lofi_statep, minor); 247*3d7072f8Seschrock } 248*3d7072f8Seschrock 249*3d7072f8Seschrock /*ARGSUSED*/ 2507c478bd9Sstevel@tonic-gate static int 2517c478bd9Sstevel@tonic-gate lofi_open(dev_t *devp, int flag, int otyp, struct cred *credp) 2527c478bd9Sstevel@tonic-gate { 2537c478bd9Sstevel@tonic-gate minor_t minor; 2547c478bd9Sstevel@tonic-gate struct lofi_state *lsp; 2557c478bd9Sstevel@tonic-gate 2567c478bd9Sstevel@tonic-gate mutex_enter(&lofi_lock); 2577c478bd9Sstevel@tonic-gate minor = getminor(*devp); 2587c478bd9Sstevel@tonic-gate if (minor == 0) { 2597c478bd9Sstevel@tonic-gate /* master control device */ 2607c478bd9Sstevel@tonic-gate /* must be opened exclusively */ 2617c478bd9Sstevel@tonic-gate if (((flag & FEXCL) != FEXCL) || (otyp != OTYP_CHR)) { 2627c478bd9Sstevel@tonic-gate mutex_exit(&lofi_lock); 2637c478bd9Sstevel@tonic-gate return (EINVAL); 2647c478bd9Sstevel@tonic-gate } 2657c478bd9Sstevel@tonic-gate lsp = ddi_get_soft_state(lofi_statep, 0); 2667c478bd9Sstevel@tonic-gate if (lsp == NULL) { 2677c478bd9Sstevel@tonic-gate mutex_exit(&lofi_lock); 2687c478bd9Sstevel@tonic-gate return (ENXIO); 2697c478bd9Sstevel@tonic-gate } 2707c478bd9Sstevel@tonic-gate if (is_opened(lsp)) { 2717c478bd9Sstevel@tonic-gate mutex_exit(&lofi_lock); 2727c478bd9Sstevel@tonic-gate return (EBUSY); 2737c478bd9Sstevel@tonic-gate } 2747c478bd9Sstevel@tonic-gate (void) mark_opened(lsp, OTYP_CHR); 2757c478bd9Sstevel@tonic-gate mutex_exit(&lofi_lock); 2767c478bd9Sstevel@tonic-gate return (0); 2777c478bd9Sstevel@tonic-gate } 2787c478bd9Sstevel@tonic-gate 2797c478bd9Sstevel@tonic-gate /* otherwise, the mapping should already exist */ 2807c478bd9Sstevel@tonic-gate lsp = ddi_get_soft_state(lofi_statep, minor); 2817c478bd9Sstevel@tonic-gate if (lsp == NULL) { 2827c478bd9Sstevel@tonic-gate mutex_exit(&lofi_lock); 2837c478bd9Sstevel@tonic-gate return (EINVAL); 2847c478bd9Sstevel@tonic-gate } 2857c478bd9Sstevel@tonic-gate 286*3d7072f8Seschrock if (lsp->ls_vp == NULL) { 287*3d7072f8Seschrock mutex_exit(&lofi_lock); 288*3d7072f8Seschrock return (ENXIO); 289*3d7072f8Seschrock } 290*3d7072f8Seschrock 2917c478bd9Sstevel@tonic-gate if (mark_opened(lsp, otyp) == -1) { 2927c478bd9Sstevel@tonic-gate mutex_exit(&lofi_lock); 2937c478bd9Sstevel@tonic-gate return (EINVAL); 2947c478bd9Sstevel@tonic-gate } 2957c478bd9Sstevel@tonic-gate 2967c478bd9Sstevel@tonic-gate mutex_exit(&lofi_lock); 2977c478bd9Sstevel@tonic-gate return (0); 2987c478bd9Sstevel@tonic-gate } 2997c478bd9Sstevel@tonic-gate 300*3d7072f8Seschrock /*ARGSUSED*/ 3017c478bd9Sstevel@tonic-gate static int 3027c478bd9Sstevel@tonic-gate lofi_close(dev_t dev, int flag, int otyp, struct cred *credp) 3037c478bd9Sstevel@tonic-gate { 3047c478bd9Sstevel@tonic-gate minor_t minor; 3057c478bd9Sstevel@tonic-gate struct lofi_state *lsp; 3067c478bd9Sstevel@tonic-gate 3077c478bd9Sstevel@tonic-gate mutex_enter(&lofi_lock); 3087c478bd9Sstevel@tonic-gate minor = getminor(dev); 3097c478bd9Sstevel@tonic-gate lsp = ddi_get_soft_state(lofi_statep, minor); 3107c478bd9Sstevel@tonic-gate if (lsp == NULL) { 3117c478bd9Sstevel@tonic-gate mutex_exit(&lofi_lock); 3127c478bd9Sstevel@tonic-gate return (EINVAL); 3137c478bd9Sstevel@tonic-gate } 3147c478bd9Sstevel@tonic-gate mark_closed(lsp, otyp); 315*3d7072f8Seschrock 316*3d7072f8Seschrock /* 317*3d7072f8Seschrock * If we have forcibly closed the underlying device, and this is the 318*3d7072f8Seschrock * last close, then tear down the rest of the device. 319*3d7072f8Seschrock */ 320*3d7072f8Seschrock if (minor != 0 && lsp->ls_vp == NULL && !is_opened(lsp)) 321*3d7072f8Seschrock lofi_free_handle(dev, minor, lsp, credp); 3227c478bd9Sstevel@tonic-gate mutex_exit(&lofi_lock); 3237c478bd9Sstevel@tonic-gate return (0); 3247c478bd9Sstevel@tonic-gate } 3257c478bd9Sstevel@tonic-gate 3267c478bd9Sstevel@tonic-gate /* 3277c478bd9Sstevel@tonic-gate * This is basically what strategy used to be before we found we 3287c478bd9Sstevel@tonic-gate * needed task queues. 3297c478bd9Sstevel@tonic-gate */ 3307c478bd9Sstevel@tonic-gate static void 3317c478bd9Sstevel@tonic-gate lofi_strategy_task(void *arg) 3327c478bd9Sstevel@tonic-gate { 3337c478bd9Sstevel@tonic-gate struct buf *bp = (struct buf *)arg; 3347c478bd9Sstevel@tonic-gate int error; 3357c478bd9Sstevel@tonic-gate struct lofi_state *lsp; 3367c478bd9Sstevel@tonic-gate offset_t offset, alignedoffset; 3377c478bd9Sstevel@tonic-gate offset_t mapoffset; 3387c478bd9Sstevel@tonic-gate caddr_t bufaddr; 3397c478bd9Sstevel@tonic-gate caddr_t mapaddr; 3407c478bd9Sstevel@tonic-gate size_t xfersize; 3417c478bd9Sstevel@tonic-gate size_t len; 3427c478bd9Sstevel@tonic-gate int isread; 3437c478bd9Sstevel@tonic-gate int smflags; 3447c478bd9Sstevel@tonic-gate enum seg_rw srw; 3457c478bd9Sstevel@tonic-gate 3467c478bd9Sstevel@tonic-gate lsp = ddi_get_soft_state(lofi_statep, getminor(bp->b_edev)); 3477c478bd9Sstevel@tonic-gate if (lsp->ls_kstat) { 3487c478bd9Sstevel@tonic-gate mutex_enter(lsp->ls_kstat->ks_lock); 3497c478bd9Sstevel@tonic-gate kstat_waitq_to_runq(KSTAT_IO_PTR(lsp->ls_kstat)); 3507c478bd9Sstevel@tonic-gate mutex_exit(lsp->ls_kstat->ks_lock); 3517c478bd9Sstevel@tonic-gate } 3527c478bd9Sstevel@tonic-gate bp_mapin(bp); 3537c478bd9Sstevel@tonic-gate bufaddr = bp->b_un.b_addr; 3547c478bd9Sstevel@tonic-gate offset = bp->b_lblkno * DEV_BSIZE; /* offset within file */ 3557c478bd9Sstevel@tonic-gate 3567c478bd9Sstevel@tonic-gate /* 3577c478bd9Sstevel@tonic-gate * We used to always use vn_rdwr here, but we cannot do that because 3587c478bd9Sstevel@tonic-gate * we might decide to read or write from the the underlying 3597c478bd9Sstevel@tonic-gate * file during this call, which would be a deadlock because 3607c478bd9Sstevel@tonic-gate * we have the rw_lock. So instead we page, unless it's not 3617c478bd9Sstevel@tonic-gate * mapable or it's a character device. 3627c478bd9Sstevel@tonic-gate */ 363*3d7072f8Seschrock if (lsp->ls_vp == NULL || lsp->ls_vp_closereq) { 364*3d7072f8Seschrock error = EIO; 365*3d7072f8Seschrock } else if (((lsp->ls_vp->v_flag & VNOMAP) == 0) && 3667c478bd9Sstevel@tonic-gate (lsp->ls_vp->v_type != VCHR)) { 3677c478bd9Sstevel@tonic-gate /* 3687c478bd9Sstevel@tonic-gate * segmap always gives us an 8K (MAXBSIZE) chunk, aligned on 3697c478bd9Sstevel@tonic-gate * an 8K boundary, but the buf transfer address may not be 3707c478bd9Sstevel@tonic-gate * aligned on more than a 512-byte boundary (we don't 3717c478bd9Sstevel@tonic-gate * enforce that, though we could). This matters since the 3727c478bd9Sstevel@tonic-gate * initial part of the transfer may not start at offset 0 3737c478bd9Sstevel@tonic-gate * within the segmap'd chunk. So we have to compensate for 3747c478bd9Sstevel@tonic-gate * that with 'mapoffset'. Subsequent chunks always start 3757c478bd9Sstevel@tonic-gate * off at the beginning, and the last is capped by b_resid. 3767c478bd9Sstevel@tonic-gate */ 3777c478bd9Sstevel@tonic-gate mapoffset = offset & MAXBOFFSET; 3787c478bd9Sstevel@tonic-gate alignedoffset = offset - mapoffset; /* now map-aligned */ 3797c478bd9Sstevel@tonic-gate bp->b_resid = bp->b_bcount; 3807c478bd9Sstevel@tonic-gate isread = bp->b_flags & B_READ; 3817c478bd9Sstevel@tonic-gate srw = isread ? S_READ : S_WRITE; 3827c478bd9Sstevel@tonic-gate do { 3837c478bd9Sstevel@tonic-gate xfersize = MIN(lsp->ls_vp_size - offset, 3847c478bd9Sstevel@tonic-gate MIN(MAXBSIZE - mapoffset, bp->b_resid)); 3857c478bd9Sstevel@tonic-gate len = roundup(mapoffset + xfersize, PAGESIZE); 3867c478bd9Sstevel@tonic-gate mapaddr = segmap_getmapflt(segkmap, lsp->ls_vp, 3877c478bd9Sstevel@tonic-gate alignedoffset, MAXBSIZE, 1, srw); 3887c478bd9Sstevel@tonic-gate /* 3897c478bd9Sstevel@tonic-gate * Now fault in the pages. This lets us check 3907c478bd9Sstevel@tonic-gate * for errors before we reference mapaddr and 3917c478bd9Sstevel@tonic-gate * try to resolve the fault in bcopy (which would 3927c478bd9Sstevel@tonic-gate * panic instead). And this can easily happen, 3937c478bd9Sstevel@tonic-gate * particularly if you've lofi'd a file over NFS 3947c478bd9Sstevel@tonic-gate * and someone deletes the file on the server. 3957c478bd9Sstevel@tonic-gate */ 3967c478bd9Sstevel@tonic-gate error = segmap_fault(kas.a_hat, segkmap, mapaddr, 3977c478bd9Sstevel@tonic-gate len, F_SOFTLOCK, srw); 3987c478bd9Sstevel@tonic-gate if (error) { 3997c478bd9Sstevel@tonic-gate (void) segmap_release(segkmap, mapaddr, 0); 4007c478bd9Sstevel@tonic-gate if (FC_CODE(error) == FC_OBJERR) 4017c478bd9Sstevel@tonic-gate error = FC_ERRNO(error); 4027c478bd9Sstevel@tonic-gate else 4037c478bd9Sstevel@tonic-gate error = EIO; 4047c478bd9Sstevel@tonic-gate break; 4057c478bd9Sstevel@tonic-gate } 4067c478bd9Sstevel@tonic-gate smflags = 0; 4077c478bd9Sstevel@tonic-gate if (isread) { 4087c478bd9Sstevel@tonic-gate bcopy(mapaddr + mapoffset, bufaddr, xfersize); 4097c478bd9Sstevel@tonic-gate } else { 4107c478bd9Sstevel@tonic-gate smflags |= SM_WRITE; 4117c478bd9Sstevel@tonic-gate bcopy(bufaddr, mapaddr + mapoffset, xfersize); 4127c478bd9Sstevel@tonic-gate } 4137c478bd9Sstevel@tonic-gate bp->b_resid -= xfersize; 4147c478bd9Sstevel@tonic-gate bufaddr += xfersize; 4157c478bd9Sstevel@tonic-gate offset += xfersize; 4167c478bd9Sstevel@tonic-gate (void) segmap_fault(kas.a_hat, segkmap, mapaddr, 4177c478bd9Sstevel@tonic-gate len, F_SOFTUNLOCK, srw); 4187c478bd9Sstevel@tonic-gate error = segmap_release(segkmap, mapaddr, smflags); 4197c478bd9Sstevel@tonic-gate /* only the first map may start partial */ 4207c478bd9Sstevel@tonic-gate mapoffset = 0; 4217c478bd9Sstevel@tonic-gate alignedoffset += MAXBSIZE; 4227c478bd9Sstevel@tonic-gate } while ((error == 0) && (bp->b_resid > 0) && 4237c478bd9Sstevel@tonic-gate (offset < lsp->ls_vp_size)); 4247c478bd9Sstevel@tonic-gate } else { 4257c478bd9Sstevel@tonic-gate ssize_t resid; 4267c478bd9Sstevel@tonic-gate enum uio_rw rw; 4277c478bd9Sstevel@tonic-gate 4287c478bd9Sstevel@tonic-gate if (bp->b_flags & B_READ) 4297c478bd9Sstevel@tonic-gate rw = UIO_READ; 4307c478bd9Sstevel@tonic-gate else 4317c478bd9Sstevel@tonic-gate rw = UIO_WRITE; 4327c478bd9Sstevel@tonic-gate error = vn_rdwr(rw, lsp->ls_vp, bufaddr, bp->b_bcount, 4337c478bd9Sstevel@tonic-gate offset, UIO_SYSSPACE, 0, RLIM64_INFINITY, kcred, &resid); 4347c478bd9Sstevel@tonic-gate bp->b_resid = resid; 4357c478bd9Sstevel@tonic-gate } 4367c478bd9Sstevel@tonic-gate 4377c478bd9Sstevel@tonic-gate if (lsp->ls_kstat) { 4387c478bd9Sstevel@tonic-gate size_t n_done = bp->b_bcount - bp->b_resid; 4397c478bd9Sstevel@tonic-gate kstat_io_t *kioptr; 4407c478bd9Sstevel@tonic-gate 4417c478bd9Sstevel@tonic-gate mutex_enter(lsp->ls_kstat->ks_lock); 4427c478bd9Sstevel@tonic-gate kioptr = KSTAT_IO_PTR(lsp->ls_kstat); 4437c478bd9Sstevel@tonic-gate if (bp->b_flags & B_READ) { 4447c478bd9Sstevel@tonic-gate kioptr->nread += n_done; 4457c478bd9Sstevel@tonic-gate kioptr->reads++; 4467c478bd9Sstevel@tonic-gate } else { 4477c478bd9Sstevel@tonic-gate kioptr->nwritten += n_done; 4487c478bd9Sstevel@tonic-gate kioptr->writes++; 4497c478bd9Sstevel@tonic-gate } 4507c478bd9Sstevel@tonic-gate kstat_runq_exit(kioptr); 4517c478bd9Sstevel@tonic-gate mutex_exit(lsp->ls_kstat->ks_lock); 4527c478bd9Sstevel@tonic-gate } 453*3d7072f8Seschrock 454*3d7072f8Seschrock mutex_enter(&lsp->ls_vp_lock); 455*3d7072f8Seschrock if (--lsp->ls_vp_iocount == 0) 456*3d7072f8Seschrock cv_broadcast(&lsp->ls_vp_cv); 457*3d7072f8Seschrock mutex_exit(&lsp->ls_vp_lock); 458*3d7072f8Seschrock 4597c478bd9Sstevel@tonic-gate bioerror(bp, error); 4607c478bd9Sstevel@tonic-gate biodone(bp); 4617c478bd9Sstevel@tonic-gate } 4627c478bd9Sstevel@tonic-gate 4637c478bd9Sstevel@tonic-gate static int 4647c478bd9Sstevel@tonic-gate lofi_strategy(struct buf *bp) 4657c478bd9Sstevel@tonic-gate { 4667c478bd9Sstevel@tonic-gate struct lofi_state *lsp; 4677c478bd9Sstevel@tonic-gate offset_t offset; 4687c478bd9Sstevel@tonic-gate 4697c478bd9Sstevel@tonic-gate /* 4707c478bd9Sstevel@tonic-gate * We cannot just do I/O here, because the current thread 4717c478bd9Sstevel@tonic-gate * _might_ end up back in here because the underlying filesystem 4727c478bd9Sstevel@tonic-gate * wants a buffer, which eventually gets into bio_recycle and 4737c478bd9Sstevel@tonic-gate * might call into lofi to write out a delayed-write buffer. 4747c478bd9Sstevel@tonic-gate * This is bad if the filesystem above lofi is the same as below. 4757c478bd9Sstevel@tonic-gate * 4767c478bd9Sstevel@tonic-gate * We could come up with a complex strategy using threads to 4777c478bd9Sstevel@tonic-gate * do the I/O asynchronously, or we could use task queues. task 4787c478bd9Sstevel@tonic-gate * queues were incredibly easy so they win. 4797c478bd9Sstevel@tonic-gate */ 4807c478bd9Sstevel@tonic-gate lsp = ddi_get_soft_state(lofi_statep, getminor(bp->b_edev)); 481*3d7072f8Seschrock mutex_enter(&lsp->ls_vp_lock); 482*3d7072f8Seschrock if (lsp->ls_vp == NULL || lsp->ls_vp_closereq) { 483*3d7072f8Seschrock bioerror(bp, EIO); 484*3d7072f8Seschrock biodone(bp); 485*3d7072f8Seschrock mutex_exit(&lsp->ls_vp_lock); 486*3d7072f8Seschrock return (0); 487*3d7072f8Seschrock } 488*3d7072f8Seschrock 4897c478bd9Sstevel@tonic-gate offset = bp->b_lblkno * DEV_BSIZE; /* offset within file */ 4907c478bd9Sstevel@tonic-gate if (offset == lsp->ls_vp_size) { 4917c478bd9Sstevel@tonic-gate /* EOF */ 4927c478bd9Sstevel@tonic-gate if ((bp->b_flags & B_READ) != 0) { 4937c478bd9Sstevel@tonic-gate bp->b_resid = bp->b_bcount; 4947c478bd9Sstevel@tonic-gate bioerror(bp, 0); 4957c478bd9Sstevel@tonic-gate } else { 4967c478bd9Sstevel@tonic-gate /* writes should fail */ 4977c478bd9Sstevel@tonic-gate bioerror(bp, ENXIO); 4987c478bd9Sstevel@tonic-gate } 4997c478bd9Sstevel@tonic-gate biodone(bp); 500*3d7072f8Seschrock mutex_exit(&lsp->ls_vp_lock); 5017c478bd9Sstevel@tonic-gate return (0); 5027c478bd9Sstevel@tonic-gate } 5037c478bd9Sstevel@tonic-gate if (offset > lsp->ls_vp_size) { 5047c478bd9Sstevel@tonic-gate bioerror(bp, ENXIO); 5057c478bd9Sstevel@tonic-gate biodone(bp); 506*3d7072f8Seschrock mutex_exit(&lsp->ls_vp_lock); 5077c478bd9Sstevel@tonic-gate return (0); 5087c478bd9Sstevel@tonic-gate } 509*3d7072f8Seschrock lsp->ls_vp_iocount++; 510*3d7072f8Seschrock mutex_exit(&lsp->ls_vp_lock); 511*3d7072f8Seschrock 5127c478bd9Sstevel@tonic-gate if (lsp->ls_kstat) { 5137c478bd9Sstevel@tonic-gate mutex_enter(lsp->ls_kstat->ks_lock); 5147c478bd9Sstevel@tonic-gate kstat_waitq_enter(KSTAT_IO_PTR(lsp->ls_kstat)); 5157c478bd9Sstevel@tonic-gate mutex_exit(lsp->ls_kstat->ks_lock); 5167c478bd9Sstevel@tonic-gate } 5177c478bd9Sstevel@tonic-gate (void) taskq_dispatch(lsp->ls_taskq, lofi_strategy_task, bp, KM_SLEEP); 5187c478bd9Sstevel@tonic-gate return (0); 5197c478bd9Sstevel@tonic-gate } 5207c478bd9Sstevel@tonic-gate 5217c478bd9Sstevel@tonic-gate /*ARGSUSED2*/ 5227c478bd9Sstevel@tonic-gate static int 5237c478bd9Sstevel@tonic-gate lofi_read(dev_t dev, struct uio *uio, struct cred *credp) 5247c478bd9Sstevel@tonic-gate { 5257c478bd9Sstevel@tonic-gate if (getminor(dev) == 0) 5267c478bd9Sstevel@tonic-gate return (EINVAL); 5277c478bd9Sstevel@tonic-gate return (physio(lofi_strategy, NULL, dev, B_READ, minphys, uio)); 5287c478bd9Sstevel@tonic-gate } 5297c478bd9Sstevel@tonic-gate 5307c478bd9Sstevel@tonic-gate /*ARGSUSED2*/ 5317c478bd9Sstevel@tonic-gate static int 5327c478bd9Sstevel@tonic-gate lofi_write(dev_t dev, struct uio *uio, struct cred *credp) 5337c478bd9Sstevel@tonic-gate { 5347c478bd9Sstevel@tonic-gate if (getminor(dev) == 0) 5357c478bd9Sstevel@tonic-gate return (EINVAL); 5367c478bd9Sstevel@tonic-gate return (physio(lofi_strategy, NULL, dev, B_WRITE, minphys, uio)); 5377c478bd9Sstevel@tonic-gate } 5387c478bd9Sstevel@tonic-gate 5397c478bd9Sstevel@tonic-gate /*ARGSUSED2*/ 5407c478bd9Sstevel@tonic-gate static int 5417c478bd9Sstevel@tonic-gate lofi_aread(dev_t dev, struct aio_req *aio, struct cred *credp) 5427c478bd9Sstevel@tonic-gate { 5437c478bd9Sstevel@tonic-gate if (getminor(dev) == 0) 5447c478bd9Sstevel@tonic-gate return (EINVAL); 5457c478bd9Sstevel@tonic-gate return (aphysio(lofi_strategy, anocancel, dev, B_READ, minphys, aio)); 5467c478bd9Sstevel@tonic-gate } 5477c478bd9Sstevel@tonic-gate 5487c478bd9Sstevel@tonic-gate /*ARGSUSED2*/ 5497c478bd9Sstevel@tonic-gate static int 5507c478bd9Sstevel@tonic-gate lofi_awrite(dev_t dev, struct aio_req *aio, struct cred *credp) 5517c478bd9Sstevel@tonic-gate { 5527c478bd9Sstevel@tonic-gate if (getminor(dev) == 0) 5537c478bd9Sstevel@tonic-gate return (EINVAL); 5547c478bd9Sstevel@tonic-gate return (aphysio(lofi_strategy, anocancel, dev, B_WRITE, minphys, aio)); 5557c478bd9Sstevel@tonic-gate } 5567c478bd9Sstevel@tonic-gate 5577c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 5587c478bd9Sstevel@tonic-gate static int 5597c478bd9Sstevel@tonic-gate lofi_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) 5607c478bd9Sstevel@tonic-gate { 5617c478bd9Sstevel@tonic-gate switch (infocmd) { 5627c478bd9Sstevel@tonic-gate case DDI_INFO_DEVT2DEVINFO: 5637c478bd9Sstevel@tonic-gate *result = lofi_dip; 5647c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 5657c478bd9Sstevel@tonic-gate case DDI_INFO_DEVT2INSTANCE: 5667c478bd9Sstevel@tonic-gate *result = 0; 5677c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 5687c478bd9Sstevel@tonic-gate } 5697c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 5707c478bd9Sstevel@tonic-gate } 5717c478bd9Sstevel@tonic-gate 5727c478bd9Sstevel@tonic-gate static int 5737c478bd9Sstevel@tonic-gate lofi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 5747c478bd9Sstevel@tonic-gate { 5757c478bd9Sstevel@tonic-gate int error; 5767c478bd9Sstevel@tonic-gate 5777c478bd9Sstevel@tonic-gate if (cmd != DDI_ATTACH) 5787c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 5797c478bd9Sstevel@tonic-gate error = ddi_soft_state_zalloc(lofi_statep, 0); 5807c478bd9Sstevel@tonic-gate if (error == DDI_FAILURE) { 5817c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 5827c478bd9Sstevel@tonic-gate } 5837c478bd9Sstevel@tonic-gate error = ddi_create_minor_node(dip, LOFI_CTL_NODE, S_IFCHR, 0, 5847c478bd9Sstevel@tonic-gate DDI_PSEUDO, NULL); 5857c478bd9Sstevel@tonic-gate if (error == DDI_FAILURE) { 5867c478bd9Sstevel@tonic-gate ddi_soft_state_free(lofi_statep, 0); 5877c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 5887c478bd9Sstevel@tonic-gate } 5897c478bd9Sstevel@tonic-gate lofi_dip = dip; 5907c478bd9Sstevel@tonic-gate ddi_report_dev(dip); 5917c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 5927c478bd9Sstevel@tonic-gate } 5937c478bd9Sstevel@tonic-gate 5947c478bd9Sstevel@tonic-gate static int 5957c478bd9Sstevel@tonic-gate lofi_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 5967c478bd9Sstevel@tonic-gate { 5977c478bd9Sstevel@tonic-gate if (cmd != DDI_DETACH) 5987c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 5997c478bd9Sstevel@tonic-gate if (lofi_busy()) 6007c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 6017c478bd9Sstevel@tonic-gate lofi_dip = NULL; 6027c478bd9Sstevel@tonic-gate ddi_remove_minor_node(dip, NULL); 6037c478bd9Sstevel@tonic-gate ddi_soft_state_free(lofi_statep, 0); 6047c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 6057c478bd9Sstevel@tonic-gate } 6067c478bd9Sstevel@tonic-gate 6077c478bd9Sstevel@tonic-gate /* 6087c478bd9Sstevel@tonic-gate * These two just simplify the rest of the ioctls that need to copyin/out 6097c478bd9Sstevel@tonic-gate * the lofi_ioctl structure. 6107c478bd9Sstevel@tonic-gate */ 6117c478bd9Sstevel@tonic-gate struct lofi_ioctl * 612bd07e074Sheppo copy_in_lofi_ioctl(const struct lofi_ioctl *ulip, int flag) 6137c478bd9Sstevel@tonic-gate { 6147c478bd9Sstevel@tonic-gate struct lofi_ioctl *klip; 6157c478bd9Sstevel@tonic-gate int error; 6167c478bd9Sstevel@tonic-gate 6177c478bd9Sstevel@tonic-gate klip = kmem_alloc(sizeof (struct lofi_ioctl), KM_SLEEP); 618bd07e074Sheppo error = ddi_copyin(ulip, klip, sizeof (struct lofi_ioctl), flag); 6197c478bd9Sstevel@tonic-gate if (error) { 6207c478bd9Sstevel@tonic-gate kmem_free(klip, sizeof (struct lofi_ioctl)); 6217c478bd9Sstevel@tonic-gate return (NULL); 6227c478bd9Sstevel@tonic-gate } 6237c478bd9Sstevel@tonic-gate 6247c478bd9Sstevel@tonic-gate /* make sure filename is always null-terminated */ 6257c478bd9Sstevel@tonic-gate klip->li_filename[MAXPATHLEN] = '\0'; 6267c478bd9Sstevel@tonic-gate 6277c478bd9Sstevel@tonic-gate /* validate minor number */ 6287c478bd9Sstevel@tonic-gate if (klip->li_minor > lofi_max_files) { 6297c478bd9Sstevel@tonic-gate kmem_free(klip, sizeof (struct lofi_ioctl)); 6307c478bd9Sstevel@tonic-gate return (NULL); 6317c478bd9Sstevel@tonic-gate } 6327c478bd9Sstevel@tonic-gate return (klip); 6337c478bd9Sstevel@tonic-gate } 6347c478bd9Sstevel@tonic-gate 6357c478bd9Sstevel@tonic-gate int 636bd07e074Sheppo copy_out_lofi_ioctl(const struct lofi_ioctl *klip, struct lofi_ioctl *ulip, 637bd07e074Sheppo int flag) 6387c478bd9Sstevel@tonic-gate { 6397c478bd9Sstevel@tonic-gate int error; 6407c478bd9Sstevel@tonic-gate 641bd07e074Sheppo error = ddi_copyout(klip, ulip, sizeof (struct lofi_ioctl), flag); 6427c478bd9Sstevel@tonic-gate if (error) 6437c478bd9Sstevel@tonic-gate return (EFAULT); 6447c478bd9Sstevel@tonic-gate return (0); 6457c478bd9Sstevel@tonic-gate } 6467c478bd9Sstevel@tonic-gate 6477c478bd9Sstevel@tonic-gate void 6487c478bd9Sstevel@tonic-gate free_lofi_ioctl(struct lofi_ioctl *klip) 6497c478bd9Sstevel@tonic-gate { 6507c478bd9Sstevel@tonic-gate kmem_free(klip, sizeof (struct lofi_ioctl)); 6517c478bd9Sstevel@tonic-gate } 6527c478bd9Sstevel@tonic-gate 6537c478bd9Sstevel@tonic-gate /* 6547c478bd9Sstevel@tonic-gate * Return the minor number 'filename' is mapped to, if it is. 6557c478bd9Sstevel@tonic-gate */ 6567c478bd9Sstevel@tonic-gate static int 6577c478bd9Sstevel@tonic-gate file_to_minor(char *filename) 6587c478bd9Sstevel@tonic-gate { 6597c478bd9Sstevel@tonic-gate minor_t minor; 6607c478bd9Sstevel@tonic-gate struct lofi_state *lsp; 6617c478bd9Sstevel@tonic-gate 6627c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(&lofi_lock)); 6637c478bd9Sstevel@tonic-gate for (minor = 1; minor <= lofi_max_files; minor++) { 6647c478bd9Sstevel@tonic-gate lsp = ddi_get_soft_state(lofi_statep, minor); 6657c478bd9Sstevel@tonic-gate if (lsp == NULL) 6667c478bd9Sstevel@tonic-gate continue; 6677c478bd9Sstevel@tonic-gate if (strcmp(lsp->ls_filename, filename) == 0) 6687c478bd9Sstevel@tonic-gate return (minor); 6697c478bd9Sstevel@tonic-gate } 6707c478bd9Sstevel@tonic-gate return (0); 6717c478bd9Sstevel@tonic-gate } 6727c478bd9Sstevel@tonic-gate 6737c478bd9Sstevel@tonic-gate /* 6747c478bd9Sstevel@tonic-gate * lofiadm does some validation, but since Joe Random (or crashme) could 6757c478bd9Sstevel@tonic-gate * do our ioctls, we need to do some validation too. 6767c478bd9Sstevel@tonic-gate */ 6777c478bd9Sstevel@tonic-gate static int 6787c478bd9Sstevel@tonic-gate valid_filename(const char *filename) 6797c478bd9Sstevel@tonic-gate { 6807c478bd9Sstevel@tonic-gate static char *blkprefix = "/dev/" LOFI_BLOCK_NAME "/"; 6817c478bd9Sstevel@tonic-gate static char *charprefix = "/dev/" LOFI_CHAR_NAME "/"; 6827c478bd9Sstevel@tonic-gate 6837c478bd9Sstevel@tonic-gate /* must be absolute path */ 6847c478bd9Sstevel@tonic-gate if (filename[0] != '/') 6857c478bd9Sstevel@tonic-gate return (0); 6867c478bd9Sstevel@tonic-gate /* must not be lofi */ 6877c478bd9Sstevel@tonic-gate if (strncmp(filename, blkprefix, strlen(blkprefix)) == 0) 6887c478bd9Sstevel@tonic-gate return (0); 6897c478bd9Sstevel@tonic-gate if (strncmp(filename, charprefix, strlen(charprefix)) == 0) 6907c478bd9Sstevel@tonic-gate return (0); 6917c478bd9Sstevel@tonic-gate return (1); 6927c478bd9Sstevel@tonic-gate } 6937c478bd9Sstevel@tonic-gate 6947c478bd9Sstevel@tonic-gate /* 6957c478bd9Sstevel@tonic-gate * Fakes up a disk geometry, and one big partition, based on the size 6967c478bd9Sstevel@tonic-gate * of the file. This is needed because we allow newfs'ing the device, 6977c478bd9Sstevel@tonic-gate * and newfs will do several disk ioctls to figure out the geometry and 6987c478bd9Sstevel@tonic-gate * partition information. It uses that information to determine the parameters 69963360950Smp204432 * to pass to mkfs. Geometry is pretty much irrelevant these days, but we 7007c478bd9Sstevel@tonic-gate * have to support it. 7017c478bd9Sstevel@tonic-gate */ 7027c478bd9Sstevel@tonic-gate static void 7037c478bd9Sstevel@tonic-gate fake_disk_geometry(struct lofi_state *lsp) 7047c478bd9Sstevel@tonic-gate { 7057c478bd9Sstevel@tonic-gate /* dk_geom - see dkio(7I) */ 7067c478bd9Sstevel@tonic-gate /* 7077c478bd9Sstevel@tonic-gate * dkg_ncyl _could_ be set to one here (one big cylinder with gobs 7087c478bd9Sstevel@tonic-gate * of sectors), but that breaks programs like fdisk which want to 7097c478bd9Sstevel@tonic-gate * partition a disk by cylinder. With one cylinder, you can't create 7107c478bd9Sstevel@tonic-gate * an fdisk partition and put pcfs on it for testing (hard to pick 7117c478bd9Sstevel@tonic-gate * a number between one and one). 7127c478bd9Sstevel@tonic-gate * 7137c478bd9Sstevel@tonic-gate * The cheezy floppy test is an attempt to not have too few cylinders 7147c478bd9Sstevel@tonic-gate * for a small file, or so many on a big file that you waste space 7157c478bd9Sstevel@tonic-gate * for backup superblocks or cylinder group structures. 7167c478bd9Sstevel@tonic-gate */ 7177c478bd9Sstevel@tonic-gate if (lsp->ls_vp_size < (2 * 1024 * 1024)) /* floppy? */ 7187c478bd9Sstevel@tonic-gate lsp->ls_dkg.dkg_ncyl = lsp->ls_vp_size / (100 * 1024); 7197c478bd9Sstevel@tonic-gate else 7207c478bd9Sstevel@tonic-gate lsp->ls_dkg.dkg_ncyl = lsp->ls_vp_size / (300 * 1024); 7217c478bd9Sstevel@tonic-gate /* in case file file is < 100k */ 7227c478bd9Sstevel@tonic-gate if (lsp->ls_dkg.dkg_ncyl == 0) 7237c478bd9Sstevel@tonic-gate lsp->ls_dkg.dkg_ncyl = 1; 7247c478bd9Sstevel@tonic-gate lsp->ls_dkg.dkg_acyl = 0; 7257c478bd9Sstevel@tonic-gate lsp->ls_dkg.dkg_bcyl = 0; 7267c478bd9Sstevel@tonic-gate lsp->ls_dkg.dkg_nhead = 1; 7277c478bd9Sstevel@tonic-gate lsp->ls_dkg.dkg_obs1 = 0; 7287c478bd9Sstevel@tonic-gate lsp->ls_dkg.dkg_intrlv = 0; 7297c478bd9Sstevel@tonic-gate lsp->ls_dkg.dkg_obs2 = 0; 7307c478bd9Sstevel@tonic-gate lsp->ls_dkg.dkg_obs3 = 0; 7317c478bd9Sstevel@tonic-gate lsp->ls_dkg.dkg_apc = 0; 7327c478bd9Sstevel@tonic-gate lsp->ls_dkg.dkg_rpm = 7200; 7337c478bd9Sstevel@tonic-gate lsp->ls_dkg.dkg_pcyl = lsp->ls_dkg.dkg_ncyl + lsp->ls_dkg.dkg_acyl; 7347c478bd9Sstevel@tonic-gate lsp->ls_dkg.dkg_nsect = lsp->ls_vp_size / 7357c478bd9Sstevel@tonic-gate (DEV_BSIZE * lsp->ls_dkg.dkg_ncyl); 7367c478bd9Sstevel@tonic-gate lsp->ls_dkg.dkg_write_reinstruct = 0; 7377c478bd9Sstevel@tonic-gate lsp->ls_dkg.dkg_read_reinstruct = 0; 7387c478bd9Sstevel@tonic-gate 7397c478bd9Sstevel@tonic-gate /* vtoc - see dkio(7I) */ 7407c478bd9Sstevel@tonic-gate bzero(&lsp->ls_vtoc, sizeof (struct vtoc)); 7417c478bd9Sstevel@tonic-gate lsp->ls_vtoc.v_sanity = VTOC_SANE; 7427c478bd9Sstevel@tonic-gate lsp->ls_vtoc.v_version = V_VERSION; 7437c478bd9Sstevel@tonic-gate bcopy(LOFI_DRIVER_NAME, lsp->ls_vtoc.v_volume, 7); 7447c478bd9Sstevel@tonic-gate lsp->ls_vtoc.v_sectorsz = DEV_BSIZE; 7457c478bd9Sstevel@tonic-gate lsp->ls_vtoc.v_nparts = 1; 7467c478bd9Sstevel@tonic-gate lsp->ls_vtoc.v_part[0].p_tag = V_UNASSIGNED; 7477c478bd9Sstevel@tonic-gate lsp->ls_vtoc.v_part[0].p_flag = V_UNMNT; 7487c478bd9Sstevel@tonic-gate lsp->ls_vtoc.v_part[0].p_start = (daddr_t)0; 7497c478bd9Sstevel@tonic-gate /* 7507c478bd9Sstevel@tonic-gate * The partition size cannot just be the number of sectors, because 7517c478bd9Sstevel@tonic-gate * that might not end on a cylinder boundary. And if that's the case, 7527c478bd9Sstevel@tonic-gate * newfs/mkfs will print a scary warning. So just figure the size 7537c478bd9Sstevel@tonic-gate * based on the number of cylinders and sectors/cylinder. 7547c478bd9Sstevel@tonic-gate */ 7557c478bd9Sstevel@tonic-gate lsp->ls_vtoc.v_part[0].p_size = lsp->ls_dkg.dkg_pcyl * 7567c478bd9Sstevel@tonic-gate lsp->ls_dkg.dkg_nsect * lsp->ls_dkg.dkg_nhead; 7577c478bd9Sstevel@tonic-gate 7587c478bd9Sstevel@tonic-gate /* dk_cinfo - see dkio(7I) */ 7597c478bd9Sstevel@tonic-gate bzero(&lsp->ls_ci, sizeof (struct dk_cinfo)); 7607c478bd9Sstevel@tonic-gate (void) strcpy(lsp->ls_ci.dki_cname, LOFI_DRIVER_NAME); 7617c478bd9Sstevel@tonic-gate lsp->ls_ci.dki_ctype = DKC_MD; 7627c478bd9Sstevel@tonic-gate lsp->ls_ci.dki_flags = 0; 7637c478bd9Sstevel@tonic-gate lsp->ls_ci.dki_cnum = 0; 7647c478bd9Sstevel@tonic-gate lsp->ls_ci.dki_addr = 0; 7657c478bd9Sstevel@tonic-gate lsp->ls_ci.dki_space = 0; 7667c478bd9Sstevel@tonic-gate lsp->ls_ci.dki_prio = 0; 7677c478bd9Sstevel@tonic-gate lsp->ls_ci.dki_vec = 0; 7687c478bd9Sstevel@tonic-gate (void) strcpy(lsp->ls_ci.dki_dname, LOFI_DRIVER_NAME); 7697c478bd9Sstevel@tonic-gate lsp->ls_ci.dki_unit = 0; 7707c478bd9Sstevel@tonic-gate lsp->ls_ci.dki_slave = 0; 7717c478bd9Sstevel@tonic-gate lsp->ls_ci.dki_partition = 0; 7727c478bd9Sstevel@tonic-gate /* 7737c478bd9Sstevel@tonic-gate * newfs uses this to set maxcontig. Must not be < 16, or it 7747c478bd9Sstevel@tonic-gate * will be 0 when newfs multiplies it by DEV_BSIZE and divides 7757c478bd9Sstevel@tonic-gate * it by the block size. Then tunefs doesn't work because 7767c478bd9Sstevel@tonic-gate * maxcontig is 0. 7777c478bd9Sstevel@tonic-gate */ 7787c478bd9Sstevel@tonic-gate lsp->ls_ci.dki_maxtransfer = 16; 7797c478bd9Sstevel@tonic-gate } 7807c478bd9Sstevel@tonic-gate 7817c478bd9Sstevel@tonic-gate /* 7827c478bd9Sstevel@tonic-gate * map a file to a minor number. Return the minor number. 7837c478bd9Sstevel@tonic-gate */ 7847c478bd9Sstevel@tonic-gate static int 7857c478bd9Sstevel@tonic-gate lofi_map_file(dev_t dev, struct lofi_ioctl *ulip, int pickminor, 786bd07e074Sheppo int *rvalp, struct cred *credp, int ioctl_flag) 7877c478bd9Sstevel@tonic-gate { 7887c478bd9Sstevel@tonic-gate minor_t newminor; 7897c478bd9Sstevel@tonic-gate struct lofi_state *lsp; 7907c478bd9Sstevel@tonic-gate struct lofi_ioctl *klip; 7917c478bd9Sstevel@tonic-gate int error; 7927c478bd9Sstevel@tonic-gate struct vnode *vp; 7937c478bd9Sstevel@tonic-gate int64_t Nblocks_prop_val; 7947c478bd9Sstevel@tonic-gate int64_t Size_prop_val; 7957c478bd9Sstevel@tonic-gate vattr_t vattr; 7967c478bd9Sstevel@tonic-gate int flag; 7977c478bd9Sstevel@tonic-gate enum vtype v_type; 7987c478bd9Sstevel@tonic-gate int zalloced = 0; 799*3d7072f8Seschrock dev_t newdev; 800*3d7072f8Seschrock char namebuf[50]; 8017c478bd9Sstevel@tonic-gate 802bd07e074Sheppo klip = copy_in_lofi_ioctl(ulip, ioctl_flag); 8037c478bd9Sstevel@tonic-gate if (klip == NULL) 8047c478bd9Sstevel@tonic-gate return (EFAULT); 8057c478bd9Sstevel@tonic-gate 8067c478bd9Sstevel@tonic-gate mutex_enter(&lofi_lock); 8077c478bd9Sstevel@tonic-gate 8087c478bd9Sstevel@tonic-gate if (!valid_filename(klip->li_filename)) { 8097c478bd9Sstevel@tonic-gate error = EINVAL; 8107c478bd9Sstevel@tonic-gate goto out; 8117c478bd9Sstevel@tonic-gate } 8127c478bd9Sstevel@tonic-gate 8137c478bd9Sstevel@tonic-gate if (file_to_minor(klip->li_filename) != 0) { 8147c478bd9Sstevel@tonic-gate error = EBUSY; 8157c478bd9Sstevel@tonic-gate goto out; 8167c478bd9Sstevel@tonic-gate } 8177c478bd9Sstevel@tonic-gate 8187c478bd9Sstevel@tonic-gate if (pickminor) { 8197c478bd9Sstevel@tonic-gate /* Find a free one */ 8207c478bd9Sstevel@tonic-gate for (newminor = 1; newminor <= lofi_max_files; newminor++) 8217c478bd9Sstevel@tonic-gate if (ddi_get_soft_state(lofi_statep, newminor) == NULL) 8227c478bd9Sstevel@tonic-gate break; 8237c478bd9Sstevel@tonic-gate if (newminor >= lofi_max_files) { 8247c478bd9Sstevel@tonic-gate error = EAGAIN; 8257c478bd9Sstevel@tonic-gate goto out; 8267c478bd9Sstevel@tonic-gate } 8277c478bd9Sstevel@tonic-gate } else { 8287c478bd9Sstevel@tonic-gate newminor = klip->li_minor; 8297c478bd9Sstevel@tonic-gate if (ddi_get_soft_state(lofi_statep, newminor) != NULL) { 8307c478bd9Sstevel@tonic-gate error = EEXIST; 8317c478bd9Sstevel@tonic-gate goto out; 8327c478bd9Sstevel@tonic-gate } 8337c478bd9Sstevel@tonic-gate } 8347c478bd9Sstevel@tonic-gate 8357c478bd9Sstevel@tonic-gate /* make sure it's valid */ 8367c478bd9Sstevel@tonic-gate error = lookupname(klip->li_filename, UIO_SYSSPACE, FOLLOW, 8377c478bd9Sstevel@tonic-gate NULLVPP, &vp); 8387c478bd9Sstevel@tonic-gate if (error) { 8397c478bd9Sstevel@tonic-gate goto out; 8407c478bd9Sstevel@tonic-gate } 8417c478bd9Sstevel@tonic-gate v_type = vp->v_type; 8427c478bd9Sstevel@tonic-gate VN_RELE(vp); 8437c478bd9Sstevel@tonic-gate if (!V_ISLOFIABLE(v_type)) { 8447c478bd9Sstevel@tonic-gate error = EINVAL; 8457c478bd9Sstevel@tonic-gate goto out; 8467c478bd9Sstevel@tonic-gate } 8477c478bd9Sstevel@tonic-gate flag = FREAD | FWRITE | FOFFMAX | FEXCL; 8487c478bd9Sstevel@tonic-gate error = vn_open(klip->li_filename, UIO_SYSSPACE, flag, 0, &vp, 0, 0); 8497c478bd9Sstevel@tonic-gate if (error) { 8507c478bd9Sstevel@tonic-gate /* try read-only */ 8517c478bd9Sstevel@tonic-gate flag &= ~FWRITE; 8527c478bd9Sstevel@tonic-gate error = vn_open(klip->li_filename, UIO_SYSSPACE, flag, 0, 8537c478bd9Sstevel@tonic-gate &vp, 0, 0); 8547c478bd9Sstevel@tonic-gate if (error) { 8557c478bd9Sstevel@tonic-gate goto out; 8567c478bd9Sstevel@tonic-gate } 8577c478bd9Sstevel@tonic-gate } 8587c478bd9Sstevel@tonic-gate vattr.va_mask = AT_SIZE; 8597c478bd9Sstevel@tonic-gate error = VOP_GETATTR(vp, &vattr, 0, credp); 8607c478bd9Sstevel@tonic-gate if (error) { 8617c478bd9Sstevel@tonic-gate goto closeout; 8627c478bd9Sstevel@tonic-gate } 8637c478bd9Sstevel@tonic-gate /* the file needs to be a multiple of the block size */ 8647c478bd9Sstevel@tonic-gate if ((vattr.va_size % DEV_BSIZE) != 0) { 8657c478bd9Sstevel@tonic-gate error = EINVAL; 8667c478bd9Sstevel@tonic-gate goto closeout; 8677c478bd9Sstevel@tonic-gate } 8687c478bd9Sstevel@tonic-gate newdev = makedevice(getmajor(dev), newminor); 8697c478bd9Sstevel@tonic-gate Size_prop_val = vattr.va_size; 8707c478bd9Sstevel@tonic-gate if ((ddi_prop_update_int64(newdev, lofi_dip, 8717c478bd9Sstevel@tonic-gate SIZE_PROP_NAME, Size_prop_val)) != DDI_PROP_SUCCESS) { 8727c478bd9Sstevel@tonic-gate error = EINVAL; 8737c478bd9Sstevel@tonic-gate goto closeout; 8747c478bd9Sstevel@tonic-gate } 8757c478bd9Sstevel@tonic-gate Nblocks_prop_val = vattr.va_size / DEV_BSIZE; 8767c478bd9Sstevel@tonic-gate if ((ddi_prop_update_int64(newdev, lofi_dip, 8777c478bd9Sstevel@tonic-gate NBLOCKS_PROP_NAME, Nblocks_prop_val)) != DDI_PROP_SUCCESS) { 8787c478bd9Sstevel@tonic-gate error = EINVAL; 8797c478bd9Sstevel@tonic-gate goto propout; 8807c478bd9Sstevel@tonic-gate } 8817c478bd9Sstevel@tonic-gate error = ddi_soft_state_zalloc(lofi_statep, newminor); 8827c478bd9Sstevel@tonic-gate if (error == DDI_FAILURE) { 8837c478bd9Sstevel@tonic-gate error = ENOMEM; 8847c478bd9Sstevel@tonic-gate goto propout; 8857c478bd9Sstevel@tonic-gate } 8867c478bd9Sstevel@tonic-gate zalloced = 1; 8877c478bd9Sstevel@tonic-gate (void) snprintf(namebuf, sizeof (namebuf), "%d", newminor); 8887c478bd9Sstevel@tonic-gate (void) ddi_create_minor_node(lofi_dip, namebuf, S_IFBLK, newminor, 8897c478bd9Sstevel@tonic-gate DDI_PSEUDO, NULL); 8907c478bd9Sstevel@tonic-gate if (error != DDI_SUCCESS) { 8917c478bd9Sstevel@tonic-gate error = ENXIO; 8927c478bd9Sstevel@tonic-gate goto propout; 8937c478bd9Sstevel@tonic-gate } 8947c478bd9Sstevel@tonic-gate (void) snprintf(namebuf, sizeof (namebuf), "%d,raw", newminor); 8957c478bd9Sstevel@tonic-gate error = ddi_create_minor_node(lofi_dip, namebuf, S_IFCHR, newminor, 8967c478bd9Sstevel@tonic-gate DDI_PSEUDO, NULL); 8977c478bd9Sstevel@tonic-gate if (error != DDI_SUCCESS) { 8987c478bd9Sstevel@tonic-gate /* remove block node */ 8997c478bd9Sstevel@tonic-gate (void) snprintf(namebuf, sizeof (namebuf), "%d", newminor); 9007c478bd9Sstevel@tonic-gate ddi_remove_minor_node(lofi_dip, namebuf); 9017c478bd9Sstevel@tonic-gate error = ENXIO; 9027c478bd9Sstevel@tonic-gate goto propout; 9037c478bd9Sstevel@tonic-gate } 9047c478bd9Sstevel@tonic-gate lsp = ddi_get_soft_state(lofi_statep, newminor); 9057c478bd9Sstevel@tonic-gate lsp->ls_filename_sz = strlen(klip->li_filename) + 1; 9067c478bd9Sstevel@tonic-gate lsp->ls_filename = kmem_alloc(lsp->ls_filename_sz, KM_SLEEP); 9077c478bd9Sstevel@tonic-gate (void) snprintf(namebuf, sizeof (namebuf), "%s_taskq_%d", 9087c478bd9Sstevel@tonic-gate LOFI_DRIVER_NAME, newminor); 9097c478bd9Sstevel@tonic-gate lsp->ls_taskq = taskq_create(namebuf, lofi_taskq_nthreads, 9107c478bd9Sstevel@tonic-gate minclsyspri, 1, lofi_taskq_maxalloc, 0); 9117c478bd9Sstevel@tonic-gate lsp->ls_kstat = kstat_create(LOFI_DRIVER_NAME, newminor, 9127c478bd9Sstevel@tonic-gate NULL, "disk", KSTAT_TYPE_IO, 1, 0); 9137c478bd9Sstevel@tonic-gate if (lsp->ls_kstat) { 9147c478bd9Sstevel@tonic-gate mutex_init(&lsp->ls_kstat_lock, NULL, MUTEX_DRIVER, NULL); 9157c478bd9Sstevel@tonic-gate lsp->ls_kstat->ks_lock = &lsp->ls_kstat_lock; 9167c478bd9Sstevel@tonic-gate kstat_install(lsp->ls_kstat); 9177c478bd9Sstevel@tonic-gate } 918*3d7072f8Seschrock cv_init(&lsp->ls_vp_cv, NULL, CV_DRIVER, NULL); 919*3d7072f8Seschrock mutex_init(&lsp->ls_vp_lock, NULL, MUTEX_DRIVER, NULL); 920*3d7072f8Seschrock 9217c478bd9Sstevel@tonic-gate /* 9227c478bd9Sstevel@tonic-gate * save open mode so file can be closed properly and vnode counts 9237c478bd9Sstevel@tonic-gate * updated correctly. 9247c478bd9Sstevel@tonic-gate */ 9257c478bd9Sstevel@tonic-gate lsp->ls_openflag = flag; 9267c478bd9Sstevel@tonic-gate 9277c478bd9Sstevel@tonic-gate /* 9287c478bd9Sstevel@tonic-gate * Try to handle stacked lofs vnodes. 9297c478bd9Sstevel@tonic-gate */ 9307c478bd9Sstevel@tonic-gate if (vp->v_type == VREG) { 9317c478bd9Sstevel@tonic-gate if (VOP_REALVP(vp, &lsp->ls_vp) != 0) { 9327c478bd9Sstevel@tonic-gate lsp->ls_vp = vp; 9337c478bd9Sstevel@tonic-gate } else { 9347c478bd9Sstevel@tonic-gate /* 9357c478bd9Sstevel@tonic-gate * Even though vp was obtained via vn_open(), we 9367c478bd9Sstevel@tonic-gate * can't call vn_close() on it, since lofs will 9377c478bd9Sstevel@tonic-gate * pass the VOP_CLOSE() on down to the realvp 9387c478bd9Sstevel@tonic-gate * (which we are about to use). Hence we merely 9397c478bd9Sstevel@tonic-gate * drop the reference to the lofs vnode and hold 9407c478bd9Sstevel@tonic-gate * the realvp so things behave as if we've 9417c478bd9Sstevel@tonic-gate * opened the realvp without any interaction 9427c478bd9Sstevel@tonic-gate * with lofs. 9437c478bd9Sstevel@tonic-gate */ 9447c478bd9Sstevel@tonic-gate VN_HOLD(lsp->ls_vp); 9457c478bd9Sstevel@tonic-gate VN_RELE(vp); 9467c478bd9Sstevel@tonic-gate } 9477c478bd9Sstevel@tonic-gate } else { 9487c478bd9Sstevel@tonic-gate lsp->ls_vp = vp; 9497c478bd9Sstevel@tonic-gate } 9507c478bd9Sstevel@tonic-gate lsp->ls_vp_size = vattr.va_size; 9517c478bd9Sstevel@tonic-gate (void) strcpy(lsp->ls_filename, klip->li_filename); 9527c478bd9Sstevel@tonic-gate if (rvalp) 9537c478bd9Sstevel@tonic-gate *rvalp = (int)newminor; 9547c478bd9Sstevel@tonic-gate klip->li_minor = newminor; 9557c478bd9Sstevel@tonic-gate 9567c478bd9Sstevel@tonic-gate fake_disk_geometry(lsp); 9577c478bd9Sstevel@tonic-gate mutex_exit(&lofi_lock); 958bd07e074Sheppo (void) copy_out_lofi_ioctl(klip, ulip, ioctl_flag); 9597c478bd9Sstevel@tonic-gate free_lofi_ioctl(klip); 9607c478bd9Sstevel@tonic-gate return (0); 9617c478bd9Sstevel@tonic-gate 9627c478bd9Sstevel@tonic-gate propout: 9637c478bd9Sstevel@tonic-gate (void) ddi_prop_remove(newdev, lofi_dip, SIZE_PROP_NAME); 9647c478bd9Sstevel@tonic-gate (void) ddi_prop_remove(newdev, lofi_dip, NBLOCKS_PROP_NAME); 9657c478bd9Sstevel@tonic-gate closeout: 9667c478bd9Sstevel@tonic-gate (void) VOP_CLOSE(vp, flag, 1, 0, credp); 9677c478bd9Sstevel@tonic-gate VN_RELE(vp); 9687c478bd9Sstevel@tonic-gate out: 9697c478bd9Sstevel@tonic-gate if (zalloced) 9707c478bd9Sstevel@tonic-gate ddi_soft_state_free(lofi_statep, newminor); 9717c478bd9Sstevel@tonic-gate mutex_exit(&lofi_lock); 9727c478bd9Sstevel@tonic-gate free_lofi_ioctl(klip); 9737c478bd9Sstevel@tonic-gate return (error); 9747c478bd9Sstevel@tonic-gate } 9757c478bd9Sstevel@tonic-gate 9767c478bd9Sstevel@tonic-gate /* 9777c478bd9Sstevel@tonic-gate * unmap a file. 9787c478bd9Sstevel@tonic-gate */ 9797c478bd9Sstevel@tonic-gate static int 9807c478bd9Sstevel@tonic-gate lofi_unmap_file(dev_t dev, struct lofi_ioctl *ulip, int byfilename, 981bd07e074Sheppo struct cred *credp, int ioctl_flag) 9827c478bd9Sstevel@tonic-gate { 9837c478bd9Sstevel@tonic-gate struct lofi_state *lsp; 9847c478bd9Sstevel@tonic-gate struct lofi_ioctl *klip; 9857c478bd9Sstevel@tonic-gate minor_t minor; 9867c478bd9Sstevel@tonic-gate 987bd07e074Sheppo klip = copy_in_lofi_ioctl(ulip, ioctl_flag); 9887c478bd9Sstevel@tonic-gate if (klip == NULL) 9897c478bd9Sstevel@tonic-gate return (EFAULT); 9907c478bd9Sstevel@tonic-gate 9917c478bd9Sstevel@tonic-gate mutex_enter(&lofi_lock); 9927c478bd9Sstevel@tonic-gate if (byfilename) { 9937c478bd9Sstevel@tonic-gate minor = file_to_minor(klip->li_filename); 9947c478bd9Sstevel@tonic-gate } else { 9957c478bd9Sstevel@tonic-gate minor = klip->li_minor; 9967c478bd9Sstevel@tonic-gate } 9977c478bd9Sstevel@tonic-gate if (minor == 0) { 9987c478bd9Sstevel@tonic-gate mutex_exit(&lofi_lock); 9997c478bd9Sstevel@tonic-gate free_lofi_ioctl(klip); 10007c478bd9Sstevel@tonic-gate return (ENXIO); 10017c478bd9Sstevel@tonic-gate } 10027c478bd9Sstevel@tonic-gate lsp = ddi_get_soft_state(lofi_statep, minor); 1003*3d7072f8Seschrock if (lsp == NULL || lsp->ls_vp == NULL) { 10047c478bd9Sstevel@tonic-gate mutex_exit(&lofi_lock); 10057c478bd9Sstevel@tonic-gate free_lofi_ioctl(klip); 10067c478bd9Sstevel@tonic-gate return (ENXIO); 10077c478bd9Sstevel@tonic-gate } 1008*3d7072f8Seschrock 10097c478bd9Sstevel@tonic-gate if (is_opened(lsp)) { 1010*3d7072f8Seschrock /* 1011*3d7072f8Seschrock * If the 'force' flag is set, then we forcibly close the 1012*3d7072f8Seschrock * underlying file. Subsequent operations will fail, and the 1013*3d7072f8Seschrock * DKIOCSTATE ioctl will return DKIO_DEV_GONE. When the device 1014*3d7072f8Seschrock * is last closed, the device will be cleaned up appropriately. 1015*3d7072f8Seschrock * 1016*3d7072f8Seschrock * This is complicated by the fact that we may have outstanding 1017*3d7072f8Seschrock * dispatched I/Os. Rather than having a single mutex to 1018*3d7072f8Seschrock * serialize all I/O, we keep a count of the number of 1019*3d7072f8Seschrock * outstanding I/O requests, as well as a flag to indicate that 1020*3d7072f8Seschrock * no new I/Os should be dispatched. We set the flag, wait for 1021*3d7072f8Seschrock * the number of outstanding I/Os to reach 0, and then close the 1022*3d7072f8Seschrock * underlying vnode. 1023*3d7072f8Seschrock */ 1024*3d7072f8Seschrock if (klip->li_force) { 1025*3d7072f8Seschrock mutex_enter(&lsp->ls_vp_lock); 1026*3d7072f8Seschrock lsp->ls_vp_closereq = B_TRUE; 1027*3d7072f8Seschrock while (lsp->ls_vp_iocount > 0) 1028*3d7072f8Seschrock cv_wait(&lsp->ls_vp_cv, &lsp->ls_vp_lock); 1029*3d7072f8Seschrock (void) VOP_CLOSE(lsp->ls_vp, lsp->ls_openflag, 1, 0, 1030*3d7072f8Seschrock credp); 1031*3d7072f8Seschrock VN_RELE(lsp->ls_vp); 1032*3d7072f8Seschrock lsp->ls_vp = NULL; 1033*3d7072f8Seschrock cv_broadcast(&lsp->ls_vp_cv); 1034*3d7072f8Seschrock mutex_exit(&lsp->ls_vp_lock); 1035*3d7072f8Seschrock mutex_exit(&lofi_lock); 1036*3d7072f8Seschrock klip->li_minor = minor; 1037*3d7072f8Seschrock (void) copy_out_lofi_ioctl(klip, ulip, ioctl_flag); 1038*3d7072f8Seschrock free_lofi_ioctl(klip); 1039*3d7072f8Seschrock return (0); 1040*3d7072f8Seschrock } 10417c478bd9Sstevel@tonic-gate mutex_exit(&lofi_lock); 10427c478bd9Sstevel@tonic-gate free_lofi_ioctl(klip); 10437c478bd9Sstevel@tonic-gate return (EBUSY); 10447c478bd9Sstevel@tonic-gate } 10457c478bd9Sstevel@tonic-gate 1046*3d7072f8Seschrock lofi_free_handle(dev, minor, lsp, credp); 10477c478bd9Sstevel@tonic-gate 10487c478bd9Sstevel@tonic-gate klip->li_minor = minor; 10497c478bd9Sstevel@tonic-gate mutex_exit(&lofi_lock); 1050bd07e074Sheppo (void) copy_out_lofi_ioctl(klip, ulip, ioctl_flag); 10517c478bd9Sstevel@tonic-gate free_lofi_ioctl(klip); 10527c478bd9Sstevel@tonic-gate return (0); 10537c478bd9Sstevel@tonic-gate } 10547c478bd9Sstevel@tonic-gate 10557c478bd9Sstevel@tonic-gate /* 10567c478bd9Sstevel@tonic-gate * get the filename given the minor number, or the minor number given 10577c478bd9Sstevel@tonic-gate * the name. 10587c478bd9Sstevel@tonic-gate */ 1059*3d7072f8Seschrock /*ARGSUSED*/ 10607c478bd9Sstevel@tonic-gate static int 10617c478bd9Sstevel@tonic-gate lofi_get_info(dev_t dev, struct lofi_ioctl *ulip, int which, 1062bd07e074Sheppo struct cred *credp, int ioctl_flag) 10637c478bd9Sstevel@tonic-gate { 10647c478bd9Sstevel@tonic-gate struct lofi_state *lsp; 10657c478bd9Sstevel@tonic-gate struct lofi_ioctl *klip; 10667c478bd9Sstevel@tonic-gate int error; 10677c478bd9Sstevel@tonic-gate minor_t minor; 10687c478bd9Sstevel@tonic-gate 1069bd07e074Sheppo klip = copy_in_lofi_ioctl(ulip, ioctl_flag); 10707c478bd9Sstevel@tonic-gate if (klip == NULL) 10717c478bd9Sstevel@tonic-gate return (EFAULT); 10727c478bd9Sstevel@tonic-gate 10737c478bd9Sstevel@tonic-gate switch (which) { 10747c478bd9Sstevel@tonic-gate case LOFI_GET_FILENAME: 10757c478bd9Sstevel@tonic-gate minor = klip->li_minor; 10767c478bd9Sstevel@tonic-gate if (minor == 0) { 10777c478bd9Sstevel@tonic-gate free_lofi_ioctl(klip); 10787c478bd9Sstevel@tonic-gate return (EINVAL); 10797c478bd9Sstevel@tonic-gate } 10807c478bd9Sstevel@tonic-gate 10817c478bd9Sstevel@tonic-gate mutex_enter(&lofi_lock); 10827c478bd9Sstevel@tonic-gate lsp = ddi_get_soft_state(lofi_statep, minor); 10837c478bd9Sstevel@tonic-gate if (lsp == NULL) { 10847c478bd9Sstevel@tonic-gate mutex_exit(&lofi_lock); 10857c478bd9Sstevel@tonic-gate free_lofi_ioctl(klip); 10867c478bd9Sstevel@tonic-gate return (ENXIO); 10877c478bd9Sstevel@tonic-gate } 10887c478bd9Sstevel@tonic-gate (void) strcpy(klip->li_filename, lsp->ls_filename); 10897c478bd9Sstevel@tonic-gate mutex_exit(&lofi_lock); 1090bd07e074Sheppo error = copy_out_lofi_ioctl(klip, ulip, ioctl_flag); 10917c478bd9Sstevel@tonic-gate free_lofi_ioctl(klip); 10927c478bd9Sstevel@tonic-gate return (error); 10937c478bd9Sstevel@tonic-gate case LOFI_GET_MINOR: 10947c478bd9Sstevel@tonic-gate mutex_enter(&lofi_lock); 10957c478bd9Sstevel@tonic-gate klip->li_minor = file_to_minor(klip->li_filename); 10967c478bd9Sstevel@tonic-gate mutex_exit(&lofi_lock); 10977c478bd9Sstevel@tonic-gate if (klip->li_minor == 0) { 10987c478bd9Sstevel@tonic-gate free_lofi_ioctl(klip); 10997c478bd9Sstevel@tonic-gate return (ENOENT); 11007c478bd9Sstevel@tonic-gate } 1101bd07e074Sheppo error = copy_out_lofi_ioctl(klip, ulip, ioctl_flag); 11027c478bd9Sstevel@tonic-gate free_lofi_ioctl(klip); 11037c478bd9Sstevel@tonic-gate return (error); 11047c478bd9Sstevel@tonic-gate default: 11057c478bd9Sstevel@tonic-gate free_lofi_ioctl(klip); 11067c478bd9Sstevel@tonic-gate return (EINVAL); 11077c478bd9Sstevel@tonic-gate } 11087c478bd9Sstevel@tonic-gate 11097c478bd9Sstevel@tonic-gate } 11107c478bd9Sstevel@tonic-gate 11117c478bd9Sstevel@tonic-gate static int 11127c478bd9Sstevel@tonic-gate lofi_ioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *credp, 11137c478bd9Sstevel@tonic-gate int *rvalp) 11147c478bd9Sstevel@tonic-gate { 11157c478bd9Sstevel@tonic-gate int error; 11167c478bd9Sstevel@tonic-gate enum dkio_state dkstate; 11177c478bd9Sstevel@tonic-gate struct lofi_state *lsp; 11187c478bd9Sstevel@tonic-gate minor_t minor; 11197c478bd9Sstevel@tonic-gate 11207c478bd9Sstevel@tonic-gate #ifdef lint 11217c478bd9Sstevel@tonic-gate credp = credp; 11227c478bd9Sstevel@tonic-gate #endif 11237c478bd9Sstevel@tonic-gate 11247c478bd9Sstevel@tonic-gate minor = getminor(dev); 11257c478bd9Sstevel@tonic-gate /* lofi ioctls only apply to the master device */ 11267c478bd9Sstevel@tonic-gate if (minor == 0) { 11277c478bd9Sstevel@tonic-gate struct lofi_ioctl *lip = (struct lofi_ioctl *)arg; 11287c478bd9Sstevel@tonic-gate 11297c478bd9Sstevel@tonic-gate /* 11307c478bd9Sstevel@tonic-gate * the query command only need read-access - i.e., normal 11317c478bd9Sstevel@tonic-gate * users are allowed to do those on the ctl device as 11327c478bd9Sstevel@tonic-gate * long as they can open it read-only. 11337c478bd9Sstevel@tonic-gate */ 11347c478bd9Sstevel@tonic-gate switch (cmd) { 11357c478bd9Sstevel@tonic-gate case LOFI_MAP_FILE: 11367c478bd9Sstevel@tonic-gate if ((flag & FWRITE) == 0) 11377c478bd9Sstevel@tonic-gate return (EPERM); 1138bd07e074Sheppo return (lofi_map_file(dev, lip, 1, rvalp, credp, flag)); 11397c478bd9Sstevel@tonic-gate case LOFI_MAP_FILE_MINOR: 11407c478bd9Sstevel@tonic-gate if ((flag & FWRITE) == 0) 11417c478bd9Sstevel@tonic-gate return (EPERM); 1142bd07e074Sheppo return (lofi_map_file(dev, lip, 0, rvalp, credp, flag)); 11437c478bd9Sstevel@tonic-gate case LOFI_UNMAP_FILE: 11447c478bd9Sstevel@tonic-gate if ((flag & FWRITE) == 0) 11457c478bd9Sstevel@tonic-gate return (EPERM); 1146bd07e074Sheppo return (lofi_unmap_file(dev, lip, 1, credp, flag)); 11477c478bd9Sstevel@tonic-gate case LOFI_UNMAP_FILE_MINOR: 11487c478bd9Sstevel@tonic-gate if ((flag & FWRITE) == 0) 11497c478bd9Sstevel@tonic-gate return (EPERM); 1150bd07e074Sheppo return (lofi_unmap_file(dev, lip, 0, credp, flag)); 11517c478bd9Sstevel@tonic-gate case LOFI_GET_FILENAME: 11527c478bd9Sstevel@tonic-gate return (lofi_get_info(dev, lip, LOFI_GET_FILENAME, 1153bd07e074Sheppo credp, flag)); 11547c478bd9Sstevel@tonic-gate case LOFI_GET_MINOR: 11557c478bd9Sstevel@tonic-gate return (lofi_get_info(dev, lip, LOFI_GET_MINOR, 1156bd07e074Sheppo credp, flag)); 11577c478bd9Sstevel@tonic-gate case LOFI_GET_MAXMINOR: 1158bd07e074Sheppo error = ddi_copyout(&lofi_max_files, &lip->li_minor, 1159bd07e074Sheppo sizeof (lofi_max_files), flag); 11607c478bd9Sstevel@tonic-gate if (error) 11617c478bd9Sstevel@tonic-gate return (EFAULT); 11627c478bd9Sstevel@tonic-gate return (0); 11637c478bd9Sstevel@tonic-gate default: 11647c478bd9Sstevel@tonic-gate break; 11657c478bd9Sstevel@tonic-gate } 11667c478bd9Sstevel@tonic-gate } 11677c478bd9Sstevel@tonic-gate 11687c478bd9Sstevel@tonic-gate lsp = ddi_get_soft_state(lofi_statep, minor); 11697c478bd9Sstevel@tonic-gate if (lsp == NULL) 11707c478bd9Sstevel@tonic-gate return (ENXIO); 11717c478bd9Sstevel@tonic-gate 1172*3d7072f8Seschrock /* 1173*3d7072f8Seschrock * We explicitly allow DKIOCSTATE, but all other ioctls should fail with 1174*3d7072f8Seschrock * EIO as if the device was no longer present. 1175*3d7072f8Seschrock */ 1176*3d7072f8Seschrock if (lsp->ls_vp == NULL && cmd != DKIOCSTATE) 1177*3d7072f8Seschrock return (EIO); 1178*3d7072f8Seschrock 11797c478bd9Sstevel@tonic-gate /* these are for faking out utilities like newfs */ 11807c478bd9Sstevel@tonic-gate switch (cmd) { 11817c478bd9Sstevel@tonic-gate case DKIOCGVTOC: 11827c478bd9Sstevel@tonic-gate switch (ddi_model_convert_from(flag & FMODELS)) { 11837c478bd9Sstevel@tonic-gate case DDI_MODEL_ILP32: { 11847c478bd9Sstevel@tonic-gate struct vtoc32 vtoc32; 11857c478bd9Sstevel@tonic-gate 11867c478bd9Sstevel@tonic-gate vtoctovtoc32(lsp->ls_vtoc, vtoc32); 11877c478bd9Sstevel@tonic-gate if (ddi_copyout(&vtoc32, (void *)arg, 11887c478bd9Sstevel@tonic-gate sizeof (struct vtoc32), flag)) 11897c478bd9Sstevel@tonic-gate return (EFAULT); 11907c478bd9Sstevel@tonic-gate break; 11917c478bd9Sstevel@tonic-gate } 11927c478bd9Sstevel@tonic-gate 11937c478bd9Sstevel@tonic-gate case DDI_MODEL_NONE: 11947c478bd9Sstevel@tonic-gate if (ddi_copyout(&lsp->ls_vtoc, (void *)arg, 11957c478bd9Sstevel@tonic-gate sizeof (struct vtoc), flag)) 11967c478bd9Sstevel@tonic-gate return (EFAULT); 11977c478bd9Sstevel@tonic-gate break; 11987c478bd9Sstevel@tonic-gate } 11997c478bd9Sstevel@tonic-gate return (0); 12007c478bd9Sstevel@tonic-gate case DKIOCINFO: 1201bd07e074Sheppo error = ddi_copyout(&lsp->ls_ci, (void *)arg, 1202bd07e074Sheppo sizeof (struct dk_cinfo), flag); 12037c478bd9Sstevel@tonic-gate if (error) 12047c478bd9Sstevel@tonic-gate return (EFAULT); 12057c478bd9Sstevel@tonic-gate return (0); 12067c478bd9Sstevel@tonic-gate case DKIOCG_VIRTGEOM: 12077c478bd9Sstevel@tonic-gate case DKIOCG_PHYGEOM: 12087c478bd9Sstevel@tonic-gate case DKIOCGGEOM: 1209bd07e074Sheppo error = ddi_copyout(&lsp->ls_dkg, (void *)arg, 1210bd07e074Sheppo sizeof (struct dk_geom), flag); 12117c478bd9Sstevel@tonic-gate if (error) 12127c478bd9Sstevel@tonic-gate return (EFAULT); 12137c478bd9Sstevel@tonic-gate return (0); 12147c478bd9Sstevel@tonic-gate case DKIOCSTATE: 1215*3d7072f8Seschrock /* 1216*3d7072f8Seschrock * Normally, lofi devices are always in the INSERTED state. If 1217*3d7072f8Seschrock * a device is forcefully unmapped, then the device transitions 1218*3d7072f8Seschrock * to the DKIO_DEV_GONE state. 1219*3d7072f8Seschrock */ 1220*3d7072f8Seschrock if (ddi_copyin((void *)arg, &dkstate, sizeof (dkstate), 1221*3d7072f8Seschrock flag) != 0) 1222*3d7072f8Seschrock return (EFAULT); 1223*3d7072f8Seschrock 1224*3d7072f8Seschrock mutex_enter(&lsp->ls_vp_lock); 1225*3d7072f8Seschrock while ((dkstate == DKIO_INSERTED && lsp->ls_vp != NULL) || 1226*3d7072f8Seschrock (dkstate == DKIO_DEV_GONE && lsp->ls_vp == NULL)) { 1227*3d7072f8Seschrock /* 1228*3d7072f8Seschrock * By virtue of having the device open, we know that 1229*3d7072f8Seschrock * 'lsp' will remain valid when we return. 1230*3d7072f8Seschrock */ 1231*3d7072f8Seschrock if (!cv_wait_sig(&lsp->ls_vp_cv, 1232*3d7072f8Seschrock &lsp->ls_vp_lock)) { 1233*3d7072f8Seschrock mutex_exit(&lsp->ls_vp_lock); 1234*3d7072f8Seschrock return (EINTR); 1235*3d7072f8Seschrock } 1236*3d7072f8Seschrock } 1237*3d7072f8Seschrock 1238*3d7072f8Seschrock dkstate = (lsp->ls_vp != NULL ? DKIO_INSERTED : DKIO_DEV_GONE); 1239*3d7072f8Seschrock mutex_exit(&lsp->ls_vp_lock); 1240*3d7072f8Seschrock 1241*3d7072f8Seschrock if (ddi_copyout(&dkstate, (void *)arg, 1242*3d7072f8Seschrock sizeof (dkstate), flag) != 0) 12437c478bd9Sstevel@tonic-gate return (EFAULT); 12447c478bd9Sstevel@tonic-gate return (0); 12457c478bd9Sstevel@tonic-gate default: 12467c478bd9Sstevel@tonic-gate return (ENOTTY); 12477c478bd9Sstevel@tonic-gate } 12487c478bd9Sstevel@tonic-gate } 12497c478bd9Sstevel@tonic-gate 12507c478bd9Sstevel@tonic-gate static struct cb_ops lofi_cb_ops = { 12517c478bd9Sstevel@tonic-gate lofi_open, /* open */ 12527c478bd9Sstevel@tonic-gate lofi_close, /* close */ 12537c478bd9Sstevel@tonic-gate lofi_strategy, /* strategy */ 12547c478bd9Sstevel@tonic-gate nodev, /* print */ 12557c478bd9Sstevel@tonic-gate nodev, /* dump */ 12567c478bd9Sstevel@tonic-gate lofi_read, /* read */ 12577c478bd9Sstevel@tonic-gate lofi_write, /* write */ 12587c478bd9Sstevel@tonic-gate lofi_ioctl, /* ioctl */ 12597c478bd9Sstevel@tonic-gate nodev, /* devmap */ 12607c478bd9Sstevel@tonic-gate nodev, /* mmap */ 12617c478bd9Sstevel@tonic-gate nodev, /* segmap */ 12627c478bd9Sstevel@tonic-gate nochpoll, /* poll */ 12637c478bd9Sstevel@tonic-gate ddi_prop_op, /* prop_op */ 12647c478bd9Sstevel@tonic-gate 0, /* streamtab */ 12657c478bd9Sstevel@tonic-gate D_64BIT | D_NEW | D_MP, /* Driver compatibility flag */ 12667c478bd9Sstevel@tonic-gate CB_REV, 12677c478bd9Sstevel@tonic-gate lofi_aread, 12687c478bd9Sstevel@tonic-gate lofi_awrite 12697c478bd9Sstevel@tonic-gate }; 12707c478bd9Sstevel@tonic-gate 12717c478bd9Sstevel@tonic-gate static struct dev_ops lofi_ops = { 12727c478bd9Sstevel@tonic-gate DEVO_REV, /* devo_rev, */ 12737c478bd9Sstevel@tonic-gate 0, /* refcnt */ 12747c478bd9Sstevel@tonic-gate lofi_info, /* info */ 12757c478bd9Sstevel@tonic-gate nulldev, /* identify */ 12767c478bd9Sstevel@tonic-gate nulldev, /* probe */ 12777c478bd9Sstevel@tonic-gate lofi_attach, /* attach */ 12787c478bd9Sstevel@tonic-gate lofi_detach, /* detach */ 12797c478bd9Sstevel@tonic-gate nodev, /* reset */ 12807c478bd9Sstevel@tonic-gate &lofi_cb_ops, /* driver operations */ 12817c478bd9Sstevel@tonic-gate NULL /* no bus operations */ 12827c478bd9Sstevel@tonic-gate }; 12837c478bd9Sstevel@tonic-gate 12847c478bd9Sstevel@tonic-gate static struct modldrv modldrv = { 12857c478bd9Sstevel@tonic-gate &mod_driverops, 12867c478bd9Sstevel@tonic-gate "loopback file driver (%I%)", 12877c478bd9Sstevel@tonic-gate &lofi_ops, 12887c478bd9Sstevel@tonic-gate }; 12897c478bd9Sstevel@tonic-gate 12907c478bd9Sstevel@tonic-gate static struct modlinkage modlinkage = { 12917c478bd9Sstevel@tonic-gate MODREV_1, 12927c478bd9Sstevel@tonic-gate &modldrv, 12937c478bd9Sstevel@tonic-gate NULL 12947c478bd9Sstevel@tonic-gate }; 12957c478bd9Sstevel@tonic-gate 12967c478bd9Sstevel@tonic-gate int 12977c478bd9Sstevel@tonic-gate _init(void) 12987c478bd9Sstevel@tonic-gate { 12997c478bd9Sstevel@tonic-gate int error; 13007c478bd9Sstevel@tonic-gate 13017c478bd9Sstevel@tonic-gate error = ddi_soft_state_init(&lofi_statep, 13027c478bd9Sstevel@tonic-gate sizeof (struct lofi_state), 0); 13037c478bd9Sstevel@tonic-gate if (error) 13047c478bd9Sstevel@tonic-gate return (error); 13057c478bd9Sstevel@tonic-gate 13067c478bd9Sstevel@tonic-gate mutex_init(&lofi_lock, NULL, MUTEX_DRIVER, NULL); 13077c478bd9Sstevel@tonic-gate error = mod_install(&modlinkage); 13087c478bd9Sstevel@tonic-gate if (error) { 13097c478bd9Sstevel@tonic-gate mutex_destroy(&lofi_lock); 13107c478bd9Sstevel@tonic-gate ddi_soft_state_fini(&lofi_statep); 13117c478bd9Sstevel@tonic-gate } 13127c478bd9Sstevel@tonic-gate 13137c478bd9Sstevel@tonic-gate return (error); 13147c478bd9Sstevel@tonic-gate } 13157c478bd9Sstevel@tonic-gate 13167c478bd9Sstevel@tonic-gate int 13177c478bd9Sstevel@tonic-gate _fini(void) 13187c478bd9Sstevel@tonic-gate { 13197c478bd9Sstevel@tonic-gate int error; 13207c478bd9Sstevel@tonic-gate 13217c478bd9Sstevel@tonic-gate if (lofi_busy()) 13227c478bd9Sstevel@tonic-gate return (EBUSY); 13237c478bd9Sstevel@tonic-gate 13247c478bd9Sstevel@tonic-gate error = mod_remove(&modlinkage); 13257c478bd9Sstevel@tonic-gate if (error) 13267c478bd9Sstevel@tonic-gate return (error); 13277c478bd9Sstevel@tonic-gate 13287c478bd9Sstevel@tonic-gate mutex_destroy(&lofi_lock); 13297c478bd9Sstevel@tonic-gate ddi_soft_state_fini(&lofi_statep); 13307c478bd9Sstevel@tonic-gate 13317c478bd9Sstevel@tonic-gate return (error); 13327c478bd9Sstevel@tonic-gate } 13337c478bd9Sstevel@tonic-gate 13347c478bd9Sstevel@tonic-gate int 13357c478bd9Sstevel@tonic-gate _info(struct modinfo *modinfop) 13367c478bd9Sstevel@tonic-gate { 13377c478bd9Sstevel@tonic-gate return (mod_info(&modlinkage, modinfop)); 13387c478bd9Sstevel@tonic-gate } 1339