19ed84223SSascha Wildner /*
254ea00b8SMatthew Dillon * Copyright (c) 2006,2016 The DragonFly Project. All rights reserved.
39ed84223SSascha Wildner *
49ed84223SSascha Wildner * This code is derived from software contributed to The DragonFly Project
59ed84223SSascha Wildner * by Matthew Dillon <dillon@backplane.com>
69ed84223SSascha Wildner *
79ed84223SSascha Wildner * Redistribution and use in source and binary forms, with or without
89ed84223SSascha Wildner * modification, are permitted provided that the following conditions
99ed84223SSascha Wildner * are met:
109ed84223SSascha Wildner *
119ed84223SSascha Wildner * 1. Redistributions of source code must retain the above copyright
129ed84223SSascha Wildner * notice, this list of conditions and the following disclaimer.
139ed84223SSascha Wildner * 2. Redistributions in binary form must reproduce the above copyright
149ed84223SSascha Wildner * notice, this list of conditions and the following disclaimer in
159ed84223SSascha Wildner * the documentation and/or other materials provided with the
169ed84223SSascha Wildner * distribution.
179ed84223SSascha Wildner * 3. Neither the name of The DragonFly Project nor the names of its
189ed84223SSascha Wildner * contributors may be used to endorse or promote products derived
199ed84223SSascha Wildner * from this software without specific, prior written permission.
209ed84223SSascha Wildner *
219ed84223SSascha Wildner * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
229ed84223SSascha Wildner * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
239ed84223SSascha Wildner * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
249ed84223SSascha Wildner * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
259ed84223SSascha Wildner * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
269ed84223SSascha Wildner * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
279ed84223SSascha Wildner * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
289ed84223SSascha Wildner * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
299ed84223SSascha Wildner * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
309ed84223SSascha Wildner * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
319ed84223SSascha Wildner * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
329ed84223SSascha Wildner * SUCH DAMAGE.
339ed84223SSascha Wildner *
349ed84223SSascha Wildner */
359ed84223SSascha Wildner
369ed84223SSascha Wildner /*
379ed84223SSascha Wildner * Virtual disk driver
389ed84223SSascha Wildner */
399ed84223SSascha Wildner #include <sys/types.h>
409ed84223SSascha Wildner #include <sys/param.h>
419ed84223SSascha Wildner #include <sys/systm.h>
429ed84223SSascha Wildner #include <sys/kernel.h>
439ed84223SSascha Wildner #include <sys/malloc.h>
449ed84223SSascha Wildner #include <sys/conf.h>
459ed84223SSascha Wildner #include <sys/bus.h>
469ed84223SSascha Wildner #include <sys/buf.h>
479ed84223SSascha Wildner #include <sys/devicestat.h>
489ed84223SSascha Wildner #include <sys/disk.h>
499ed84223SSascha Wildner #include <machine/cothread.h>
509ed84223SSascha Wildner #include <machine/md_var.h>
519ed84223SSascha Wildner
529ed84223SSascha Wildner #include <sys/buf2.h>
539ed84223SSascha Wildner
549ed84223SSascha Wildner #include <sys/stat.h>
559ed84223SSascha Wildner #include <unistd.h>
569ed84223SSascha Wildner
579ed84223SSascha Wildner struct vkd_softc {
589ed84223SSascha Wildner struct bio_queue_head bio_queue;
599ed84223SSascha Wildner struct devstat stats;
609ed84223SSascha Wildner struct disk disk;
619ed84223SSascha Wildner cothread_t cotd;
629ed84223SSascha Wildner TAILQ_HEAD(, bio) cotd_queue;
639ed84223SSascha Wildner TAILQ_HEAD(, bio) cotd_done;
649ed84223SSascha Wildner cdev_t dev;
659ed84223SSascha Wildner int unit;
669ed84223SSascha Wildner int fd;
6754ea00b8SMatthew Dillon int flags;
6854ea00b8SMatthew Dillon off_t size; /* in bytes */
6954ea00b8SMatthew Dillon char *map_buf; /* COW mode only */
709ed84223SSascha Wildner };
719ed84223SSascha Wildner
729ed84223SSascha Wildner static void vkd_io_thread(cothread_t cotd);
739ed84223SSascha Wildner static void vkd_io_intr(cothread_t cotd);
749ed84223SSascha Wildner static void vkd_doio(struct vkd_softc *sc, struct bio *bio);
759ed84223SSascha Wildner
769ed84223SSascha Wildner static d_strategy_t vkdstrategy;
779ed84223SSascha Wildner static d_open_t vkdopen;
789ed84223SSascha Wildner
799ed84223SSascha Wildner static struct dev_ops vkd_ops = {
809ed84223SSascha Wildner { "vkd", 0, D_DISK },
819ed84223SSascha Wildner .d_open = vkdopen,
829ed84223SSascha Wildner .d_close = nullclose,
839ed84223SSascha Wildner .d_read = physread,
849ed84223SSascha Wildner .d_write = physwrite,
859ed84223SSascha Wildner .d_strategy = vkdstrategy,
869ed84223SSascha Wildner };
879ed84223SSascha Wildner
889ed84223SSascha Wildner static void
vkdinit(void * dummy __unused)899ed84223SSascha Wildner vkdinit(void *dummy __unused)
909ed84223SSascha Wildner {
919ed84223SSascha Wildner struct vkdisk_info *dsk;
929ed84223SSascha Wildner struct vkd_softc *sc;
939ed84223SSascha Wildner struct disk_info info;
949ed84223SSascha Wildner struct stat st;
959ed84223SSascha Wildner int i;
969ed84223SSascha Wildner
979ed84223SSascha Wildner for (i = 0; i < DiskNum; i++) {
989ed84223SSascha Wildner /* check that the 'bus device' has been initialized */
999ed84223SSascha Wildner dsk = &DiskInfo[i];
1009ed84223SSascha Wildner if (dsk == NULL || dsk->type != VKD_DISK)
1019ed84223SSascha Wildner continue;
1029ed84223SSascha Wildner if (dsk->fd < 0 || fstat(dsk->fd, &st) < 0)
1039ed84223SSascha Wildner continue;
1049ed84223SSascha Wildner
1059ed84223SSascha Wildner /*
1069ed84223SSascha Wildner * Devices may return a st_size of 0, try to use
1079ed84223SSascha Wildner * lseek.
1089ed84223SSascha Wildner */
1099ed84223SSascha Wildner if (st.st_size == 0) {
1109ed84223SSascha Wildner st.st_size = lseek(dsk->fd, 0L, SEEK_END);
1119ed84223SSascha Wildner if (st.st_size == -1)
1129ed84223SSascha Wildner st.st_size = 0;
1139ed84223SSascha Wildner }
1149ed84223SSascha Wildner
1159ed84223SSascha Wildner /* and create a new device */
1169ed84223SSascha Wildner sc = kmalloc(sizeof(*sc), M_DEVBUF, M_WAITOK | M_ZERO);
1179ed84223SSascha Wildner sc->unit = dsk->unit;
1189ed84223SSascha Wildner sc->fd = dsk->fd;
11954ea00b8SMatthew Dillon sc->size = st.st_size;
12054ea00b8SMatthew Dillon sc->flags = dsk->flags;
1219ed84223SSascha Wildner bioq_init(&sc->bio_queue);
1229ed84223SSascha Wildner devstat_add_entry(&sc->stats, "vkd", sc->unit, DEV_BSIZE,
1239ed84223SSascha Wildner DEVSTAT_NO_ORDERED_TAGS,
1249ed84223SSascha Wildner DEVSTAT_TYPE_DIRECT | DEVSTAT_TYPE_IF_OTHER,
1259ed84223SSascha Wildner DEVSTAT_PRIORITY_DISK);
1269ed84223SSascha Wildner sc->dev = disk_create(sc->unit, &sc->disk, &vkd_ops);
1279ed84223SSascha Wildner sc->dev->si_drv1 = sc;
128db11ac4fSFrançois Tigeot sc->dev->si_iosize_max = min(MAXPHYS,256*1024);
1299ed84223SSascha Wildner
13054ea00b8SMatthew Dillon /*
13154ea00b8SMatthew Dillon * Use a private mmap if COW mode is requested.
13254ea00b8SMatthew Dillon */
13354ea00b8SMatthew Dillon if (sc->flags & 1) {
13454ea00b8SMatthew Dillon sc->map_buf = mmap(NULL, sc->size,
13554ea00b8SMatthew Dillon PROT_READ|PROT_WRITE,
13654ea00b8SMatthew Dillon MAP_PRIVATE,
13754ea00b8SMatthew Dillon sc->fd, 0);
13854ea00b8SMatthew Dillon if ((void *)sc->map_buf == MAP_FAILED) {
139*2c7fc62cSMatthew Dillon panic("vkd: cannot mmap %jd MBytes\n",
140*2c7fc62cSMatthew Dillon (intmax_t)sc->size / (1024 * 1024));
14154ea00b8SMatthew Dillon }
14254ea00b8SMatthew Dillon kprintf("vkd%d: COW disk\n", sc->unit);
14354ea00b8SMatthew Dillon }
14454ea00b8SMatthew Dillon
1459ed84223SSascha Wildner TAILQ_INIT(&sc->cotd_queue);
1469ed84223SSascha Wildner TAILQ_INIT(&sc->cotd_done);
14754ea00b8SMatthew Dillon sc->cotd = cothread_create(vkd_io_thread, vkd_io_intr,
14854ea00b8SMatthew Dillon sc, "vkd");
1499ed84223SSascha Wildner
1509ed84223SSascha Wildner bzero(&info, sizeof(info));
1519ed84223SSascha Wildner info.d_media_blksize = DEV_BSIZE;
1529ed84223SSascha Wildner info.d_media_blocks = st.st_size / info.d_media_blksize;
1539ed84223SSascha Wildner
1549ed84223SSascha Wildner info.d_nheads = 1;
1559ed84223SSascha Wildner info.d_ncylinders = 1;
1569ed84223SSascha Wildner info.d_secpertrack = info.d_media_blocks;
1579ed84223SSascha Wildner info.d_secpercyl = info.d_secpertrack * info.d_nheads;
1589ed84223SSascha Wildner
1592a4f37b8SAntonio Huete Jimenez if (dsk->serno) {
16054ea00b8SMatthew Dillon info.d_serialno =
16154ea00b8SMatthew Dillon kmalloc(SERNOLEN, M_TEMP, M_WAITOK | M_ZERO);
1622a4f37b8SAntonio Huete Jimenez strlcpy(info.d_serialno, dsk->serno, SERNOLEN);
1632a4f37b8SAntonio Huete Jimenez }
1649ed84223SSascha Wildner disk_setdiskinfo(&sc->disk, &info);
1657eb18c8eSAntonio Huete Jimenez
1667eb18c8eSAntonio Huete Jimenez /* Announce disk details */
1677eb18c8eSAntonio Huete Jimenez kprintf("vkd%d: <Virtual disk> ", i);
1687eb18c8eSAntonio Huete Jimenez if (info.d_serialno)
1697eb18c8eSAntonio Huete Jimenez kprintf("Serial Number %s", info.d_serialno);
1707eb18c8eSAntonio Huete Jimenez kprintf("\nvkd%d: %dMB (%ju %d byte sectors)\n",
17154ea00b8SMatthew Dillon i, (int)(st.st_size / 1024 / 1024),
17254ea00b8SMatthew Dillon info.d_media_blocks, info.d_media_blksize);
1739ed84223SSascha Wildner }
1749ed84223SSascha Wildner }
1759ed84223SSascha Wildner
1769ed84223SSascha Wildner SYSINIT(vkdisk, SI_SUB_DRIVERS, SI_ORDER_FIRST, vkdinit, NULL);
1779ed84223SSascha Wildner
1789ed84223SSascha Wildner static int
vkdopen(struct dev_open_args * ap)1799ed84223SSascha Wildner vkdopen(struct dev_open_args *ap)
1809ed84223SSascha Wildner {
1819ed84223SSascha Wildner struct vkd_softc *sc;
1829ed84223SSascha Wildner /* struct disk_info info; */
1839ed84223SSascha Wildner struct stat st;
1849ed84223SSascha Wildner cdev_t dev;
1859ed84223SSascha Wildner
1869ed84223SSascha Wildner dev = ap->a_head.a_dev;
1879ed84223SSascha Wildner sc = dev->si_drv1;
1889ed84223SSascha Wildner if (fstat(sc->fd, &st) < 0)
1899ed84223SSascha Wildner return(ENXIO);
1909ed84223SSascha Wildner
1919ed84223SSascha Wildner /*
1929ed84223SSascha Wildner * Devices may return a st_size of 0, try to use
1939ed84223SSascha Wildner * lseek.
1949ed84223SSascha Wildner */
1959ed84223SSascha Wildner if (st.st_size == 0) {
1969ed84223SSascha Wildner st.st_size = lseek(sc->fd, 0L, SEEK_END);
1979ed84223SSascha Wildner if (st.st_size == -1)
1989ed84223SSascha Wildner st.st_size = 0;
1999ed84223SSascha Wildner }
2009ed84223SSascha Wildner if (st.st_size == 0)
2019ed84223SSascha Wildner return(ENXIO);
2029ed84223SSascha Wildner
2039ed84223SSascha Wildner /*
2049ed84223SSascha Wildner bzero(&info, sizeof(info));
2059ed84223SSascha Wildner info.d_media_blksize = DEV_BSIZE;
2069ed84223SSascha Wildner info.d_media_blocks = st.st_size / info.d_media_blksize;
2079ed84223SSascha Wildner
2089ed84223SSascha Wildner info.d_nheads = 1;
2099ed84223SSascha Wildner info.d_ncylinders = 1;
2109ed84223SSascha Wildner info.d_secpertrack = info.d_media_blocks;
2119ed84223SSascha Wildner info.d_secpercyl = info.d_secpertrack * info.d_nheads;
2129ed84223SSascha Wildner
2139ed84223SSascha Wildner disk_setdiskinfo(&sc->disk, &info); */
2149ed84223SSascha Wildner return(0);
2159ed84223SSascha Wildner }
2169ed84223SSascha Wildner
2179ed84223SSascha Wildner static int
vkdstrategy(struct dev_strategy_args * ap)2189ed84223SSascha Wildner vkdstrategy(struct dev_strategy_args *ap)
2199ed84223SSascha Wildner {
2209ed84223SSascha Wildner struct bio *bio = ap->a_bio;
2219ed84223SSascha Wildner struct vkd_softc *sc;
2229ed84223SSascha Wildner cdev_t dev;
2239ed84223SSascha Wildner
2249ed84223SSascha Wildner dev = ap->a_head.a_dev;
2259ed84223SSascha Wildner sc = dev->si_drv1;
2269ed84223SSascha Wildner
2279ed84223SSascha Wildner devstat_start_transaction(&sc->stats);
2289ed84223SSascha Wildner cothread_lock(sc->cotd, 0);
2299ed84223SSascha Wildner TAILQ_INSERT_TAIL(&sc->cotd_queue, bio, bio_act);
2309ed84223SSascha Wildner cothread_signal(sc->cotd);
2319ed84223SSascha Wildner cothread_unlock(sc->cotd, 0);
2329ed84223SSascha Wildner
2339ed84223SSascha Wildner return(0);
2349ed84223SSascha Wildner }
2359ed84223SSascha Wildner
2369ed84223SSascha Wildner static
2379ed84223SSascha Wildner void
vkd_io_intr(cothread_t cotd)2389ed84223SSascha Wildner vkd_io_intr(cothread_t cotd)
2399ed84223SSascha Wildner {
2409ed84223SSascha Wildner struct vkd_softc *sc;
2419ed84223SSascha Wildner struct bio *bio;
2429ed84223SSascha Wildner TAILQ_HEAD(, bio) tmpq;
2439ed84223SSascha Wildner
2449ed84223SSascha Wildner sc = cotd->arg;
2459ed84223SSascha Wildner
2469ed84223SSascha Wildner /*
2479ed84223SSascha Wildner * We can't call back into the kernel while holding cothread!
2489ed84223SSascha Wildner */
2499ed84223SSascha Wildner TAILQ_INIT(&tmpq);
2509ed84223SSascha Wildner cothread_lock(cotd, 0);
2519ed84223SSascha Wildner while ((bio = TAILQ_FIRST(&sc->cotd_done)) != NULL) {
2529ed84223SSascha Wildner TAILQ_REMOVE(&sc->cotd_done, bio, bio_act);
2539ed84223SSascha Wildner TAILQ_INSERT_TAIL(&tmpq, bio, bio_act);
2549ed84223SSascha Wildner }
2559ed84223SSascha Wildner cothread_unlock(cotd, 0);
2569ed84223SSascha Wildner
2579ed84223SSascha Wildner while ((bio = TAILQ_FIRST(&tmpq)) != NULL) {
2589ed84223SSascha Wildner TAILQ_REMOVE(&tmpq, bio, bio_act);
2599ed84223SSascha Wildner devstat_end_transaction_buf(&sc->stats, bio->bio_buf);
2609ed84223SSascha Wildner biodone(bio);
2619ed84223SSascha Wildner }
2629ed84223SSascha Wildner }
2639ed84223SSascha Wildner
2649ed84223SSascha Wildner /*
2659ed84223SSascha Wildner * WARNING! This runs as a cothread and has no access to mycpu nor can it
2669ed84223SSascha Wildner * make vkernel-specific calls other then cothread_*() calls.
2679ed84223SSascha Wildner *
2689ed84223SSascha Wildner * WARNING! A signal can occur and be discarded prior to our initial
2699ed84223SSascha Wildner * call to cothread_lock(). Process pending I/O before waiting.
2709ed84223SSascha Wildner */
2719ed84223SSascha Wildner static
2729ed84223SSascha Wildner void
vkd_io_thread(cothread_t cotd)2739ed84223SSascha Wildner vkd_io_thread(cothread_t cotd)
2749ed84223SSascha Wildner {
2759ed84223SSascha Wildner struct bio *bio;
2769ed84223SSascha Wildner struct vkd_softc *sc = cotd->arg;
2779ed84223SSascha Wildner int count;
2789ed84223SSascha Wildner
2799ed84223SSascha Wildner cothread_lock(cotd, 1);
2809ed84223SSascha Wildner for (;;) {
2819ed84223SSascha Wildner count = 0;
2829ed84223SSascha Wildner while ((bio = TAILQ_FIRST(&sc->cotd_queue)) != NULL) {
2839ed84223SSascha Wildner TAILQ_REMOVE(&sc->cotd_queue, bio, bio_act);
2849ed84223SSascha Wildner cothread_unlock(cotd, 1);
2859ed84223SSascha Wildner vkd_doio(sc, bio);
2869ed84223SSascha Wildner cothread_lock(cotd, 1);
2879ed84223SSascha Wildner TAILQ_INSERT_TAIL(&sc->cotd_done, bio, bio_act);
2889ed84223SSascha Wildner if (++count == 8) {
2899ed84223SSascha Wildner cothread_intr(cotd);
2909ed84223SSascha Wildner count = 0;
2919ed84223SSascha Wildner }
2929ed84223SSascha Wildner }
2939ed84223SSascha Wildner if (count)
2949ed84223SSascha Wildner cothread_intr(cotd);
2959ed84223SSascha Wildner cothread_wait(cotd); /* interlocks cothread lock */
2969ed84223SSascha Wildner }
2979ed84223SSascha Wildner /* NOT REACHED */
2989ed84223SSascha Wildner cothread_unlock(cotd, 1);
2999ed84223SSascha Wildner }
3009ed84223SSascha Wildner
3019ed84223SSascha Wildner static
3029ed84223SSascha Wildner void
vkd_doio(struct vkd_softc * sc,struct bio * bio)3039ed84223SSascha Wildner vkd_doio(struct vkd_softc *sc, struct bio *bio)
3049ed84223SSascha Wildner {
3059ed84223SSascha Wildner struct buf *bp = bio->bio_buf;
3069ed84223SSascha Wildner int n;
3079ed84223SSascha Wildner
3089ed84223SSascha Wildner switch(bp->b_cmd) {
3099ed84223SSascha Wildner case BUF_CMD_READ:
31054ea00b8SMatthew Dillon if (sc->map_buf) {
31154ea00b8SMatthew Dillon bcopy(sc->map_buf + bio->bio_offset,
31254ea00b8SMatthew Dillon bp->b_data,
31354ea00b8SMatthew Dillon bp->b_bcount);
31454ea00b8SMatthew Dillon n = bp->b_bcount;
31554ea00b8SMatthew Dillon } else {
31654ea00b8SMatthew Dillon n = pread(sc->fd, bp->b_data, bp->b_bcount,
31754ea00b8SMatthew Dillon bio->bio_offset);
31854ea00b8SMatthew Dillon }
3199ed84223SSascha Wildner break;
3209ed84223SSascha Wildner case BUF_CMD_WRITE:
3219ed84223SSascha Wildner /* XXX HANDLE SHORT WRITE XXX */
32254ea00b8SMatthew Dillon if (sc->map_buf) {
32354ea00b8SMatthew Dillon bcopy(bp->b_data,
32454ea00b8SMatthew Dillon sc->map_buf + bio->bio_offset,
32554ea00b8SMatthew Dillon bp->b_bcount);
32654ea00b8SMatthew Dillon n = bp->b_bcount;
32754ea00b8SMatthew Dillon } else {
32854ea00b8SMatthew Dillon n = pwrite(sc->fd, bp->b_data, bp->b_bcount,
32954ea00b8SMatthew Dillon bio->bio_offset);
33054ea00b8SMatthew Dillon }
3319ed84223SSascha Wildner break;
3329ed84223SSascha Wildner case BUF_CMD_FLUSH:
33354ea00b8SMatthew Dillon if (sc->map_buf == NULL && fsync(sc->fd) < 0)
3349ed84223SSascha Wildner n = -1;
3359ed84223SSascha Wildner else
3369ed84223SSascha Wildner n = bp->b_bcount;
3379ed84223SSascha Wildner break;
3389ed84223SSascha Wildner default:
3399ed84223SSascha Wildner panic("vkd: bad b_cmd %d", bp->b_cmd);
3409ed84223SSascha Wildner break; /* not reached */
3419ed84223SSascha Wildner }
34254ea00b8SMatthew Dillon if (bp->b_bcount != n) {
3439ed84223SSascha Wildner bp->b_error = EIO;
3449ed84223SSascha Wildner bp->b_flags |= B_ERROR;
3459ed84223SSascha Wildner }
3469ed84223SSascha Wildner bp->b_resid = bp->b_bcount - n;
3479ed84223SSascha Wildner }
348