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
vkdinit(void * dummy __unused)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
vkdopen(struct dev_open_args * ap)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
vkdstrategy(struct dev_strategy_args * ap)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
vkd_io_intr(cothread_t cotd)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
vkd_io_thread(cothread_t cotd)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
vkd_doio(struct vkd_softc * sc,struct bio * bio)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