1 /* 2 * Copyright (c) 2006 The DragonFly Project. All rights reserved. 3 * 4 * This code is derived from software contributed to The DragonFly Project 5 * by Matthew Dillon <dillon@backplane.com> 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the 16 * distribution. 17 * 3. Neither the name of The DragonFly Project nor the names of its 18 * contributors may be used to endorse or promote products derived 19 * from this software without specific, prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 * 34 */ 35 36 /* 37 * Virtual disk driver 38 */ 39 #include <sys/types.h> 40 #include <sys/param.h> 41 #include <sys/systm.h> 42 #include <sys/kernel.h> 43 #include <sys/malloc.h> 44 #include <sys/conf.h> 45 #include <sys/bus.h> 46 #include <sys/buf.h> 47 #include <sys/devicestat.h> 48 #include <sys/disk.h> 49 #include <machine/cothread.h> 50 #include <machine/md_var.h> 51 52 #include <sys/buf2.h> 53 54 #include <sys/stat.h> 55 #include <unistd.h> 56 57 struct vkd_softc { 58 struct bio_queue_head bio_queue; 59 struct devstat stats; 60 struct disk disk; 61 cothread_t cotd; 62 TAILQ_HEAD(, bio) cotd_queue; 63 TAILQ_HEAD(, bio) cotd_done; 64 cdev_t dev; 65 int unit; 66 int fd; 67 }; 68 69 static void vkd_io_thread(cothread_t cotd); 70 static void vkd_io_intr(cothread_t cotd); 71 static void vkd_doio(struct vkd_softc *sc, struct bio *bio); 72 73 static d_strategy_t vkdstrategy; 74 static d_open_t vkdopen; 75 76 static struct dev_ops vkd_ops = { 77 { "vkd", 0, D_DISK }, 78 .d_open = vkdopen, 79 .d_close = nullclose, 80 .d_read = physread, 81 .d_write = physwrite, 82 .d_strategy = vkdstrategy, 83 }; 84 85 static void 86 vkdinit(void *dummy __unused) 87 { 88 struct vkdisk_info *dsk; 89 struct vkd_softc *sc; 90 struct disk_info info; 91 struct stat st; 92 int i; 93 94 for (i = 0; i < DiskNum; i++) { 95 /* check that the 'bus device' has been initialized */ 96 dsk = &DiskInfo[i]; 97 if (dsk == NULL || dsk->type != VKD_DISK) 98 continue; 99 if (dsk->fd < 0 || fstat(dsk->fd, &st) < 0) 100 continue; 101 102 /* 103 * Devices may return a st_size of 0, try to use 104 * lseek. 105 */ 106 if (st.st_size == 0) { 107 st.st_size = lseek(dsk->fd, 0L, SEEK_END); 108 if (st.st_size == -1) 109 st.st_size = 0; 110 } 111 112 /* and create a new device */ 113 sc = kmalloc(sizeof(*sc), M_DEVBUF, M_WAITOK | M_ZERO); 114 sc->unit = dsk->unit; 115 sc->fd = dsk->fd; 116 bioq_init(&sc->bio_queue); 117 devstat_add_entry(&sc->stats, "vkd", sc->unit, DEV_BSIZE, 118 DEVSTAT_NO_ORDERED_TAGS, 119 DEVSTAT_TYPE_DIRECT | DEVSTAT_TYPE_IF_OTHER, 120 DEVSTAT_PRIORITY_DISK); 121 sc->dev = disk_create(sc->unit, &sc->disk, &vkd_ops); 122 sc->dev->si_drv1 = sc; 123 sc->dev->si_iosize_max = min(MAXPHYS,256*1024); 124 125 TAILQ_INIT(&sc->cotd_queue); 126 TAILQ_INIT(&sc->cotd_done); 127 sc->cotd = cothread_create(vkd_io_thread, vkd_io_intr, sc, 128 "vkd"); 129 130 bzero(&info, sizeof(info)); 131 info.d_media_blksize = DEV_BSIZE; 132 info.d_media_blocks = st.st_size / info.d_media_blksize; 133 134 info.d_nheads = 1; 135 info.d_ncylinders = 1; 136 info.d_secpertrack = info.d_media_blocks; 137 info.d_secpercyl = info.d_secpertrack * info.d_nheads; 138 139 if (dsk->serno) { 140 info.d_serialno = kmalloc(SERNOLEN, M_TEMP, M_WAITOK | M_ZERO); 141 strlcpy(info.d_serialno, dsk->serno, SERNOLEN); 142 } 143 disk_setdiskinfo(&sc->disk, &info); 144 145 /* Announce disk details */ 146 kprintf("vkd%d: <Virtual disk> ", i); 147 if (info.d_serialno) 148 kprintf("Serial Number %s", info.d_serialno); 149 kprintf("\nvkd%d: %dMB (%ju %d byte sectors)\n", 150 i, (int)(st.st_size / 1024 / 1024), info.d_media_blocks, info.d_media_blksize); 151 } 152 } 153 154 SYSINIT(vkdisk, SI_SUB_DRIVERS, SI_ORDER_FIRST, vkdinit, NULL); 155 156 static int 157 vkdopen(struct dev_open_args *ap) 158 { 159 struct vkd_softc *sc; 160 /* struct disk_info info; */ 161 struct stat st; 162 cdev_t dev; 163 164 dev = ap->a_head.a_dev; 165 sc = dev->si_drv1; 166 if (fstat(sc->fd, &st) < 0) 167 return(ENXIO); 168 169 /* 170 * Devices may return a st_size of 0, try to use 171 * lseek. 172 */ 173 if (st.st_size == 0) { 174 st.st_size = lseek(sc->fd, 0L, SEEK_END); 175 if (st.st_size == -1) 176 st.st_size = 0; 177 } 178 if (st.st_size == 0) 179 return(ENXIO); 180 181 /* 182 bzero(&info, sizeof(info)); 183 info.d_media_blksize = DEV_BSIZE; 184 info.d_media_blocks = st.st_size / info.d_media_blksize; 185 186 info.d_nheads = 1; 187 info.d_ncylinders = 1; 188 info.d_secpertrack = info.d_media_blocks; 189 info.d_secpercyl = info.d_secpertrack * info.d_nheads; 190 191 disk_setdiskinfo(&sc->disk, &info); */ 192 return(0); 193 } 194 195 static int 196 vkdstrategy(struct dev_strategy_args *ap) 197 { 198 struct bio *bio = ap->a_bio; 199 struct vkd_softc *sc; 200 cdev_t dev; 201 202 dev = ap->a_head.a_dev; 203 sc = dev->si_drv1; 204 205 devstat_start_transaction(&sc->stats); 206 cothread_lock(sc->cotd, 0); 207 TAILQ_INSERT_TAIL(&sc->cotd_queue, bio, bio_act); 208 cothread_signal(sc->cotd); 209 cothread_unlock(sc->cotd, 0); 210 211 return(0); 212 } 213 214 static 215 void 216 vkd_io_intr(cothread_t cotd) 217 { 218 struct vkd_softc *sc; 219 struct bio *bio; 220 TAILQ_HEAD(, bio) tmpq; 221 222 sc = cotd->arg; 223 224 /* 225 * We can't call back into the kernel while holding cothread! 226 */ 227 TAILQ_INIT(&tmpq); 228 cothread_lock(cotd, 0); 229 while ((bio = TAILQ_FIRST(&sc->cotd_done)) != NULL) { 230 TAILQ_REMOVE(&sc->cotd_done, bio, bio_act); 231 TAILQ_INSERT_TAIL(&tmpq, bio, bio_act); 232 } 233 cothread_unlock(cotd, 0); 234 235 while ((bio = TAILQ_FIRST(&tmpq)) != NULL) { 236 TAILQ_REMOVE(&tmpq, bio, bio_act); 237 devstat_end_transaction_buf(&sc->stats, bio->bio_buf); 238 biodone(bio); 239 } 240 } 241 242 /* 243 * WARNING! This runs as a cothread and has no access to mycpu nor can it 244 * make vkernel-specific calls other then cothread_*() calls. 245 * 246 * WARNING! A signal can occur and be discarded prior to our initial 247 * call to cothread_lock(). Process pending I/O before waiting. 248 */ 249 static 250 void 251 vkd_io_thread(cothread_t cotd) 252 { 253 struct bio *bio; 254 struct vkd_softc *sc = cotd->arg; 255 int count; 256 257 cothread_lock(cotd, 1); 258 for (;;) { 259 count = 0; 260 while ((bio = TAILQ_FIRST(&sc->cotd_queue)) != NULL) { 261 TAILQ_REMOVE(&sc->cotd_queue, bio, bio_act); 262 cothread_unlock(cotd, 1); 263 vkd_doio(sc, bio); 264 cothread_lock(cotd, 1); 265 TAILQ_INSERT_TAIL(&sc->cotd_done, bio, bio_act); 266 if (++count == 8) { 267 cothread_intr(cotd); 268 count = 0; 269 } 270 } 271 if (count) 272 cothread_intr(cotd); 273 cothread_wait(cotd); /* interlocks cothread lock */ 274 } 275 /* NOT REACHED */ 276 cothread_unlock(cotd, 1); 277 } 278 279 static 280 void 281 vkd_doio(struct vkd_softc *sc, struct bio *bio) 282 { 283 struct buf *bp = bio->bio_buf; 284 int n; 285 286 switch(bp->b_cmd) { 287 case BUF_CMD_READ: 288 n = pread(sc->fd, bp->b_data, bp->b_bcount, bio->bio_offset); 289 break; 290 case BUF_CMD_WRITE: 291 /* XXX HANDLE SHORT WRITE XXX */ 292 n = pwrite(sc->fd, bp->b_data, bp->b_bcount, bio->bio_offset); 293 break; 294 case BUF_CMD_FLUSH: 295 if (fsync(sc->fd) < 0) 296 n = -1; 297 else 298 n = bp->b_bcount; 299 break; 300 default: 301 panic("vkd: bad b_cmd %d", bp->b_cmd); 302 break; /* not reached */ 303 } 304 if (n != bp->b_bcount) { 305 bp->b_error = EIO; 306 bp->b_flags |= B_ERROR; 307 } 308 bp->b_resid = bp->b_bcount - n; 309 } 310