1 /* 2 * Copyright (c) 2006,2016 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 int flags; 68 off_t size; /* in bytes */ 69 char *map_buf; /* COW mode only */ 70 }; 71 72 static void vkd_io_thread(cothread_t cotd); 73 static void vkd_io_intr(cothread_t cotd); 74 static void vkd_doio(struct vkd_softc *sc, struct bio *bio); 75 76 static d_strategy_t vkdstrategy; 77 static d_open_t vkdopen; 78 79 static struct dev_ops vkd_ops = { 80 { "vkd", 0, D_DISK }, 81 .d_open = vkdopen, 82 .d_close = nullclose, 83 .d_read = physread, 84 .d_write = physwrite, 85 .d_strategy = vkdstrategy, 86 }; 87 88 static void 89 vkdinit(void *dummy __unused) 90 { 91 struct vkdisk_info *dsk; 92 struct vkd_softc *sc; 93 struct disk_info info; 94 struct stat st; 95 int i; 96 97 for (i = 0; i < DiskNum; i++) { 98 /* check that the 'bus device' has been initialized */ 99 dsk = &DiskInfo[i]; 100 if (dsk == NULL || dsk->type != VKD_DISK) 101 continue; 102 if (dsk->fd < 0 || fstat(dsk->fd, &st) < 0) 103 continue; 104 105 /* 106 * Devices may return a st_size of 0, try to use 107 * lseek. 108 */ 109 if (st.st_size == 0) { 110 st.st_size = lseek(dsk->fd, 0L, SEEK_END); 111 if (st.st_size == -1) 112 st.st_size = 0; 113 } 114 115 /* and create a new device */ 116 sc = kmalloc(sizeof(*sc), M_DEVBUF, M_WAITOK | M_ZERO); 117 sc->unit = dsk->unit; 118 sc->fd = dsk->fd; 119 sc->size = st.st_size; 120 sc->flags = dsk->flags; 121 bioq_init(&sc->bio_queue); 122 devstat_add_entry(&sc->stats, "vkd", sc->unit, DEV_BSIZE, 123 DEVSTAT_NO_ORDERED_TAGS, 124 DEVSTAT_TYPE_DIRECT | DEVSTAT_TYPE_IF_OTHER, 125 DEVSTAT_PRIORITY_DISK); 126 sc->dev = disk_create(sc->unit, &sc->disk, &vkd_ops); 127 sc->dev->si_drv1 = sc; 128 sc->dev->si_iosize_max = min(MAXPHYS,256*1024); 129 130 /* 131 * Use a private mmap if COW mode is requested. 132 */ 133 if (sc->flags & 1) { 134 sc->map_buf = mmap(NULL, sc->size, 135 PROT_READ|PROT_WRITE, 136 MAP_PRIVATE, 137 sc->fd, 0); 138 if ((void *)sc->map_buf == MAP_FAILED) { 139 panic("vkd: cannot mmap %jd MBytes\n", 140 (intmax_t)sc->size / (1024 * 1024)); 141 } 142 kprintf("vkd%d: COW disk\n", sc->unit); 143 } 144 145 TAILQ_INIT(&sc->cotd_queue); 146 TAILQ_INIT(&sc->cotd_done); 147 sc->cotd = cothread_create(vkd_io_thread, vkd_io_intr, 148 sc, "vkd"); 149 150 bzero(&info, sizeof(info)); 151 info.d_media_blksize = DEV_BSIZE; 152 info.d_media_blocks = st.st_size / info.d_media_blksize; 153 154 info.d_nheads = 1; 155 info.d_ncylinders = 1; 156 info.d_secpertrack = info.d_media_blocks; 157 info.d_secpercyl = info.d_secpertrack * info.d_nheads; 158 159 if (dsk->serno) { 160 info.d_serialno = 161 kmalloc(SERNOLEN, M_TEMP, M_WAITOK | M_ZERO); 162 strlcpy(info.d_serialno, dsk->serno, SERNOLEN); 163 } 164 disk_setdiskinfo(&sc->disk, &info); 165 166 /* Announce disk details */ 167 kprintf("vkd%d: <Virtual disk> ", i); 168 if (info.d_serialno) 169 kprintf("Serial Number %s", info.d_serialno); 170 kprintf("\nvkd%d: %dMB (%ju %d byte sectors)\n", 171 i, (int)(st.st_size / 1024 / 1024), 172 info.d_media_blocks, info.d_media_blksize); 173 } 174 } 175 176 SYSINIT(vkdisk, SI_SUB_DRIVERS, SI_ORDER_FIRST, vkdinit, NULL); 177 178 static int 179 vkdopen(struct dev_open_args *ap) 180 { 181 struct vkd_softc *sc; 182 /* struct disk_info info; */ 183 struct stat st; 184 cdev_t dev; 185 186 dev = ap->a_head.a_dev; 187 sc = dev->si_drv1; 188 if (fstat(sc->fd, &st) < 0) 189 return(ENXIO); 190 191 /* 192 * Devices may return a st_size of 0, try to use 193 * lseek. 194 */ 195 if (st.st_size == 0) { 196 st.st_size = lseek(sc->fd, 0L, SEEK_END); 197 if (st.st_size == -1) 198 st.st_size = 0; 199 } 200 if (st.st_size == 0) 201 return(ENXIO); 202 203 /* 204 bzero(&info, sizeof(info)); 205 info.d_media_blksize = DEV_BSIZE; 206 info.d_media_blocks = st.st_size / info.d_media_blksize; 207 208 info.d_nheads = 1; 209 info.d_ncylinders = 1; 210 info.d_secpertrack = info.d_media_blocks; 211 info.d_secpercyl = info.d_secpertrack * info.d_nheads; 212 213 disk_setdiskinfo(&sc->disk, &info); */ 214 return(0); 215 } 216 217 static int 218 vkdstrategy(struct dev_strategy_args *ap) 219 { 220 struct bio *bio = ap->a_bio; 221 struct vkd_softc *sc; 222 cdev_t dev; 223 224 dev = ap->a_head.a_dev; 225 sc = dev->si_drv1; 226 227 devstat_start_transaction(&sc->stats); 228 cothread_lock(sc->cotd, 0); 229 TAILQ_INSERT_TAIL(&sc->cotd_queue, bio, bio_act); 230 cothread_signal(sc->cotd); 231 cothread_unlock(sc->cotd, 0); 232 233 return(0); 234 } 235 236 static 237 void 238 vkd_io_intr(cothread_t cotd) 239 { 240 struct vkd_softc *sc; 241 struct bio *bio; 242 TAILQ_HEAD(, bio) tmpq; 243 244 sc = cotd->arg; 245 246 /* 247 * We can't call back into the kernel while holding cothread! 248 */ 249 TAILQ_INIT(&tmpq); 250 cothread_lock(cotd, 0); 251 while ((bio = TAILQ_FIRST(&sc->cotd_done)) != NULL) { 252 TAILQ_REMOVE(&sc->cotd_done, bio, bio_act); 253 TAILQ_INSERT_TAIL(&tmpq, bio, bio_act); 254 } 255 cothread_unlock(cotd, 0); 256 257 while ((bio = TAILQ_FIRST(&tmpq)) != NULL) { 258 TAILQ_REMOVE(&tmpq, bio, bio_act); 259 devstat_end_transaction_buf(&sc->stats, bio->bio_buf); 260 biodone(bio); 261 } 262 } 263 264 /* 265 * WARNING! This runs as a cothread and has no access to mycpu nor can it 266 * make vkernel-specific calls other then cothread_*() calls. 267 * 268 * WARNING! A signal can occur and be discarded prior to our initial 269 * call to cothread_lock(). Process pending I/O before waiting. 270 */ 271 static 272 void 273 vkd_io_thread(cothread_t cotd) 274 { 275 struct bio *bio; 276 struct vkd_softc *sc = cotd->arg; 277 int count; 278 279 cothread_lock(cotd, 1); 280 for (;;) { 281 count = 0; 282 while ((bio = TAILQ_FIRST(&sc->cotd_queue)) != NULL) { 283 TAILQ_REMOVE(&sc->cotd_queue, bio, bio_act); 284 cothread_unlock(cotd, 1); 285 vkd_doio(sc, bio); 286 cothread_lock(cotd, 1); 287 TAILQ_INSERT_TAIL(&sc->cotd_done, bio, bio_act); 288 if (++count == 8) { 289 cothread_intr(cotd); 290 count = 0; 291 } 292 } 293 if (count) 294 cothread_intr(cotd); 295 cothread_wait(cotd); /* interlocks cothread lock */ 296 } 297 /* NOT REACHED */ 298 cothread_unlock(cotd, 1); 299 } 300 301 static 302 void 303 vkd_doio(struct vkd_softc *sc, struct bio *bio) 304 { 305 struct buf *bp = bio->bio_buf; 306 int n; 307 308 switch(bp->b_cmd) { 309 case BUF_CMD_READ: 310 if (sc->map_buf) { 311 bcopy(sc->map_buf + bio->bio_offset, 312 bp->b_data, 313 bp->b_bcount); 314 n = bp->b_bcount; 315 } else { 316 n = pread(sc->fd, bp->b_data, bp->b_bcount, 317 bio->bio_offset); 318 } 319 break; 320 case BUF_CMD_WRITE: 321 /* XXX HANDLE SHORT WRITE XXX */ 322 if (sc->map_buf) { 323 bcopy(bp->b_data, 324 sc->map_buf + bio->bio_offset, 325 bp->b_bcount); 326 n = bp->b_bcount; 327 } else { 328 n = pwrite(sc->fd, bp->b_data, bp->b_bcount, 329 bio->bio_offset); 330 } 331 break; 332 case BUF_CMD_FLUSH: 333 if (sc->map_buf == NULL && fsync(sc->fd) < 0) 334 n = -1; 335 else 336 n = bp->b_bcount; 337 break; 338 default: 339 panic("vkd: bad b_cmd %d", bp->b_cmd); 340 break; /* not reached */ 341 } 342 if (bp->b_bcount != n) { 343 bp->b_error = EIO; 344 bp->b_flags |= B_ERROR; 345 } 346 bp->b_resid = bp->b_bcount - n; 347 } 348