md.c revision 1.7 1 /* $NetBSD: md.c,v 1.7 1996/03/22 23:02:02 gwr Exp $ */
2
3 /*
4 * Copyright (c) 1995 Gordon W. Ross, Leo Weppelman.
5 * All rights reserved.
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 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 * 4. All advertising materials mentioning features or use of this software
18 * must display the following acknowledgement:
19 * This product includes software developed by
20 * Gordon W. Ross and Leo Weppelman.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
27 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
31 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33
34 /*
35 * This implements a general-puspose RAM-disk.
36 * See ramdisk.h for notes on the config types.
37 *
38 * Note that this driver provides the same functionality
39 * as the MFS filesystem hack, but this is better because
40 * you can use this for any filesystem type you'd like!
41 *
42 * Credit for most of the kmem ramdisk code goes to:
43 * Leo Weppelman (atari) and Phil Nelson (pc532)
44 * Credit for the ideas behind the "user space RAM" code goes
45 * to the authors of the MFS implementation.
46 */
47
48 #include <sys/param.h>
49 #include <sys/kernel.h>
50 #include <sys/malloc.h>
51 #include <sys/systm.h>
52 #include <sys/buf.h>
53 #include <sys/device.h>
54 #include <sys/disk.h>
55
56 #include <vm/vm.h>
57 #include <vm/vm_kern.h>
58 /* Don't want all those other VM headers... */
59 extern vm_offset_t kmem_alloc __P((vm_map_t, vm_size_t));
60
61 #include <dev/ramdisk.h>
62
63 /*
64 * By default, include the user-space functionality.
65 * Use: option RAMDISK_SERVER=0 to turn it off.
66 */
67 #ifndef RAMDISK_SERVER
68 #define RAMDISK_SERVER 1
69 #endif
70
71 /*
72 * XXX: the "control" unit is (base unit + 16).
73 * We should just use the cdev as the "control", but
74 * that interferes with the security stuff preventing
75 * simulatneous use of raw and block devices.
76 *
77 * XXX Assumption: 16 RAM-disks are enough!
78 */
79 #define RD_MAX_UNITS 0x10
80 #define RD_IS_CTRL(unit) (unit & 0x10)
81 #define RD_UNIT(unit) (unit & 0xF)
82
83 /* autoconfig stuff... */
84
85 struct rd_softc {
86 struct device sc_dev; /* REQUIRED first entry */
87 struct disk sc_dkdev; /* hook for generic disk handling */
88 struct rd_conf sc_rd;
89 struct buf *sc_buflist;
90 int sc_flags;
91 };
92 /* shorthand for fields in sc_rd: */
93 #define sc_addr sc_rd.rd_addr
94 #define sc_size sc_rd.rd_size
95 #define sc_type sc_rd.rd_type
96 /* flags */
97 #define RD_ISOPEN 0x01
98 #define RD_SERVED 0x02
99
100 static void rd_attach __P((struct device *, struct device *, void *));
101
102 /*
103 * Some ports (like i386) use a swapgeneric that wants to
104 * snoop around in this rd_cd structure. It is preserved
105 * (for now) to remain compatible with such practice.
106 * XXX - that practice is questionable...
107 */
108 struct cfdriver rd_cd = {
109 NULL, "rd", DV_DULL, NULL, 0
110 };
111
112 void rdstrategy __P((struct buf *bp));
113 struct dkdriver rddkdriver = { rdstrategy };
114
115 static int ramdisk_ndevs;
116 static void *ramdisk_devs[RD_MAX_UNITS];
117
118 /*
119 * This is called if we are configured as a pseudo-device
120 */
121 void
122 rdattach(n)
123 int n;
124 {
125 struct rd_softc *sc;
126 int i;
127
128 #ifdef DIAGNOSTIC
129 if (ramdisk_ndevs) {
130 printf("ramdisk: multiple attach calls?\n");
131 return;
132 }
133 #endif
134
135 /* XXX: Are we supposed to provide a default? */
136 if (n <= 1)
137 n = 1;
138 if (n > RD_MAX_UNITS)
139 n = RD_MAX_UNITS;
140 ramdisk_ndevs = n;
141
142 /* XXX: Fake-up rd_cd (see above) */
143 rd_cd.cd_ndevs = ramdisk_ndevs;
144 rd_cd.cd_devs = ramdisk_devs;
145
146 /* Attach as if by autoconfig. */
147 for (i = 0; i < n; i++) {
148
149 sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK);
150 if (!sc) {
151 printf("ramdisk: malloc for attach failed!\n");
152 return;
153 }
154 bzero((caddr_t)sc, sizeof(*sc));
155 ramdisk_devs[i] = sc;
156 sc->sc_dev.dv_unit = i;
157 sprintf(sc->sc_dev.dv_xname, "rd%d", i);
158 rd_attach(NULL, &sc->sc_dev, NULL);
159 }
160 }
161
162 static void
163 rd_attach(parent, self, aux)
164 struct device *parent, *self;
165 void *aux;
166 {
167 struct rd_softc *sc = (struct rd_softc *)self;
168
169 /* XXX - Could accept aux info here to set the config. */
170 #ifdef RAMDISK_HOOKS
171 /*
172 * This external function might setup a pre-loaded disk.
173 * All it would need to do is setup the rd_conf struct.
174 * See sys/arch/sun3/dev/rd_root.c for an example.
175 */
176 rd_attach_hook(sc->sc_dev.dv_unit, &sc->sc_rd);
177 #endif
178
179 /*
180 * Initialize and attach the disk structure.
181 */
182 sc->sc_dkdev.dk_driver = &rddkdriver;
183 sc->sc_dkdev.dk_name = sc->sc_dev.dv_xname;
184 disk_attach(&sc->sc_dkdev);
185 }
186
187 /*
188 * operational routines:
189 * open, close, read, write, strategy,
190 * ioctl, dump, size
191 */
192
193 #if RAMDISK_SERVER
194 static int rd_server_loop __P((struct rd_softc *sc));
195 static int rd_ioctl_server __P((struct rd_softc *sc,
196 struct rd_conf *urd, struct proc *proc));
197 #endif
198
199 int rddump(dev, blkno, va, size)
200 dev_t dev;
201 daddr_t blkno;
202 caddr_t va;
203 size_t size;
204 {
205 return ENODEV;
206 }
207
208 int rdsize(dev_t dev)
209 {
210 int unit;
211 struct rd_softc *sc;
212
213 /* Disallow control units. */
214 unit = minor(dev);
215 if (unit >= ramdisk_ndevs)
216 return 0;
217 sc = ramdisk_devs[unit];
218 if (sc == NULL)
219 return 0;
220
221 if (sc->sc_type == RD_UNCONFIGURED)
222 return 0;
223
224 return (sc->sc_size >> DEV_BSHIFT);
225 }
226
227 int rdopen(dev, flag, fmt, proc)
228 dev_t dev;
229 int flag, fmt;
230 struct proc *proc;
231 {
232 int md, unit;
233 struct rd_softc *sc;
234
235 md = minor(dev);
236 unit = RD_UNIT(md);
237 if (unit >= ramdisk_ndevs)
238 return ENXIO;
239 sc = ramdisk_devs[unit];
240 if (sc == NULL)
241 return ENXIO;
242
243 /*
244 * The control device is not exclusive, and can
245 * open uninitialized units (so you can setconf).
246 */
247 if (RD_IS_CTRL(md))
248 return 0;
249
250 #ifdef RAMDISK_HOOKS
251 /* Call the open hook to allow loading the device. */
252 rd_open_hook(unit, &sc->sc_rd);
253 #endif
254
255 /*
256 * This is a normal, "slave" device, so
257 * enforce initialized, exclusive open.
258 */
259 if (sc->sc_type == RD_UNCONFIGURED)
260 return ENXIO;
261 if (sc->sc_flags & RD_ISOPEN)
262 return EBUSY;
263
264 return 0;
265 }
266
267 int rdclose(dev, flag, fmt, proc)
268 dev_t dev;
269 int flag, fmt;
270 struct proc *proc;
271 {
272 int md, unit;
273 struct rd_softc *sc;
274
275 md = minor(dev);
276 unit = RD_UNIT(md);
277 sc = ramdisk_devs[unit];
278
279 if (RD_IS_CTRL(md))
280 return 0;
281
282 /* Normal device. */
283 sc->sc_flags = 0;
284
285 return 0;
286 }
287
288 int
289 rdread(dev, uio)
290 dev_t dev;
291 struct uio *uio;
292 {
293 return (physio(rdstrategy, NULL, dev, B_READ, minphys, uio));
294 }
295
296 int
297 rdwrite(dev, uio)
298 dev_t dev;
299 struct uio *uio;
300 {
301 return (physio(rdstrategy, NULL, dev, B_WRITE, minphys, uio));
302 }
303
304 /*
305 * Handle I/O requests, either directly, or
306 * by passing them to the server process.
307 */
308 void
309 rdstrategy(bp)
310 struct buf *bp;
311 {
312 int md, unit;
313 struct rd_softc *sc;
314 caddr_t addr;
315 size_t off, xfer;
316
317 md = minor(bp->b_dev);
318 unit = RD_UNIT(md);
319 sc = ramdisk_devs[unit];
320
321 switch (sc->sc_type) {
322 #if RAMDISK_SERVER
323 case RD_UMEM_SERVER:
324 /* Just add this job to the server's queue. */
325 bp->b_actf = sc->sc_buflist;
326 sc->sc_buflist = bp;
327 if (bp->b_actf == NULL) {
328 /* server queue was empty. */
329 wakeup((caddr_t)sc);
330 /* see rd_server_loop() */
331 }
332 /* no biodone in this case */
333 return;
334 #endif /* RAMDISK_SERVER */
335
336 case RD_KMEM_FIXED:
337 case RD_KMEM_ALLOCATED:
338 /* These are in kernel space. Access directly. */
339 bp->b_resid = bp->b_bcount;
340 off = (bp->b_blkno << DEV_BSHIFT);
341 if (off >= sc->sc_size) {
342 if (bp->b_flags & B_READ)
343 break; /* EOF */
344 goto set_eio;
345 }
346 xfer = bp->b_resid;
347 if (xfer > (sc->sc_size - off))
348 xfer = (sc->sc_size - off);
349 addr = sc->sc_addr + off;
350 if (bp->b_flags & B_READ)
351 bcopy(addr, bp->b_data, xfer);
352 else
353 bcopy(bp->b_data, addr, xfer);
354 bp->b_resid -= xfer;
355 break;
356
357 default:
358 bp->b_resid = bp->b_bcount;
359 set_eio:
360 bp->b_error = EIO;
361 bp->b_flags |= B_ERROR;
362 break;
363 }
364 biodone(bp);
365 }
366
367 int
368 rdioctl(dev, cmd, data, flag, proc)
369 dev_t dev;
370 u_long cmd;
371 int flag;
372 caddr_t data;
373 struct proc *proc;
374 {
375 int md, unit;
376 struct rd_softc *sc;
377 struct rd_conf *urd;
378
379 md = minor(dev);
380 unit = RD_UNIT(md);
381 sc = ramdisk_devs[unit];
382
383 /* If this is not the control device, punt! */
384 if (RD_IS_CTRL(md) == 0)
385 return ENOTTY;
386
387 urd = (struct rd_conf *)data;
388 switch (cmd) {
389 case RD_GETCONF:
390 *urd = sc->sc_rd;
391 return 0;
392
393 case RD_SETCONF:
394 /* Can only set it once. */
395 if (sc->sc_type != RD_UNCONFIGURED)
396 break;
397 switch (urd->rd_type) {
398 case RD_KMEM_ALLOCATED:
399 return rd_ioctl_kalloc(sc, urd, proc);
400 #if RAMDISK_SERVER
401 case RD_UMEM_SERVER:
402 return rd_ioctl_server(sc, urd, proc);
403 #endif
404 default:
405 break;
406 }
407 break;
408 }
409 return EINVAL;
410 }
411
412 /*
413 * Handle ioctl RD_SETCONF for (sc_type == RD_KMEM_ALLOCATED)
414 * Just allocate some kernel memory and return.
415 */
416 int
417 rd_ioctl_kalloc(sc, urd, proc)
418 struct rd_softc *sc;
419 struct rd_conf *urd;
420 struct proc *proc;
421 {
422 vm_offset_t addr;
423 vm_size_t size;
424
425 /* Sanity check the size. */
426 size = urd->rd_size;
427 addr = kmem_alloc(kernel_map, size);
428 if (!addr)
429 return ENOMEM;
430
431 /* This unit is now configured. */
432 sc->sc_addr = (caddr_t)addr; /* kernel space */
433 sc->sc_size = (size_t)size;
434 sc->sc_type = RD_KMEM_ALLOCATED;
435 return 0;
436 }
437
438 #if RAMDISK_SERVER
439
440 /*
441 * Handle ioctl RD_SETCONF for (sc_type == RD_UMEM_SERVER)
442 * Set config, then become the I/O server for this unit.
443 */
444 int
445 rd_ioctl_server(sc, urd, proc)
446 struct rd_softc *sc;
447 struct rd_conf *urd;
448 struct proc *proc;
449 {
450 vm_offset_t end;
451 int error;
452
453 /* Sanity check addr, size. */
454 end = (vm_offset_t) (urd->rd_addr + urd->rd_size);
455
456 if ((end >= VM_MAXUSER_ADDRESS) ||
457 (end < ((vm_offset_t) urd->rd_addr)) )
458 return EINVAL;
459
460 /* This unit is now configured. */
461 sc->sc_addr = urd->rd_addr; /* user space */
462 sc->sc_size = urd->rd_size;
463 sc->sc_type = RD_UMEM_SERVER;
464
465 /* Become the server daemon */
466 error = rd_server_loop(sc);
467
468 /* This server is now going away! */
469 sc->sc_type = RD_UNCONFIGURED;
470 sc->sc_addr = 0;
471 sc->sc_size = 0;
472
473 return (error);
474 }
475
476 int rd_sleep_pri = PWAIT | PCATCH;
477
478 static int
479 rd_server_loop(sc)
480 struct rd_softc *sc;
481 {
482 struct buf *bp;
483 caddr_t addr; /* user space address */
484 size_t off; /* offset into "device" */
485 size_t xfer; /* amount to transfer */
486 int error;
487
488 for (;;) {
489 /* Wait for some work to arrive. */
490 while (sc->sc_buflist == NULL) {
491 error = tsleep((caddr_t)sc, rd_sleep_pri, "rd_idle", 0);
492 if (error)
493 return error;
494 }
495
496 /* Unlink buf from head of list. */
497 bp = sc->sc_buflist;
498 sc->sc_buflist = bp->b_actf;
499 bp->b_actf = NULL;
500
501 /* Do the transfer to/from user space. */
502 error = 0;
503 bp->b_resid = bp->b_bcount;
504 off = (bp->b_blkno << DEV_BSHIFT);
505 if (off >= sc->sc_size) {
506 if (bp->b_flags & B_READ)
507 goto done; /* EOF (not an error) */
508 error = EIO;
509 goto done;
510 }
511 xfer = bp->b_resid;
512 if (xfer > (sc->sc_size - off))
513 xfer = (sc->sc_size - off);
514 addr = sc->sc_addr + off;
515 if (bp->b_flags & B_READ)
516 error = copyin(addr, bp->b_data, xfer);
517 else
518 error = copyout(bp->b_data, addr, xfer);
519 if (!error)
520 bp->b_resid -= xfer;
521
522 done:
523 if (error) {
524 bp->b_error = error;
525 bp->b_flags |= B_ERROR;
526 }
527 biodone(bp);
528 }
529 }
530
531 #endif /* RAMDISK_SERVER */
532