1 /* $NetBSD: md.c,v 1.60 2009/07/28 17:55:27 dyoung Exp $ */
4 * Copyright (c) 1995 Gordon W. Ross, Leo Weppelman.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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.
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 * This implements a general-purpose memory-disk.
30 * See md.h for notes on the config types.
32 * Note that this driver provides the same functionality
33 * as the MFS filesystem hack, but this is better because
34 * you can use this for any filesystem type you'd like!
36 * Credit for most of the kmem ramdisk code goes to:
37 * Leo Weppelman (atari) and Phil Nelson (pc532)
38 * Credit for the ideas behind the "user space memory" code goes
39 * to the authors of the MFS implementation.
42 #include <sys/cdefs.h>
43 __KERNEL_RCSID(0, "$NetBSD: md.c,v 1.60 2009/07/28 17:55:27 dyoung Exp $");
46 #include "opt_tftproot.h"
48 #include <sys/param.h>
49 #include <sys/kernel.h>
50 #include <sys/malloc.h>
51 #include <sys/systm.h>
54 #include <sys/device.h>
59 #include <sys/disklabel.h>
61 #include <uvm/uvm_extern.h>
66 * The user-space functionality is included by default.
67 * Use `options MEMORY_DISK_SERVER=0' to turn it off.
69 #ifndef MEMORY_DISK_SERVER
70 #error MEMORY_DISK_SERVER should be defined by opt_md.h
71 #endif /* MEMORY_DISK_SERVER */
74 * We should use the raw partition for ioctl.
76 #define MD_UNIT(unit) DISKUNIT(unit)
78 /* autoconfig stuff... */
81 struct disk sc_dkdev
; /* hook for generic disk handling */
83 struct bufq_state
*sc_buflist
;
85 /* shorthand for fields in sc_md: */
86 #define sc_addr sc_md.md_addr
87 #define sc_size sc_md.md_size
88 #define sc_type sc_md.md_type
92 static void md_attach(device_t
, device_t
, void *);
93 static int md_detach(device_t
, int);
95 static dev_type_open(mdopen
);
96 static dev_type_close(mdclose
);
97 static dev_type_read(mdread
);
98 static dev_type_write(mdwrite
);
99 static dev_type_ioctl(mdioctl
);
100 static dev_type_strategy(mdstrategy
);
101 static dev_type_size(mdsize
);
103 const struct bdevsw md_bdevsw
= {
104 mdopen
, mdclose
, mdstrategy
, mdioctl
, nodump
, mdsize
, D_DISK
107 const struct cdevsw md_cdevsw
= {
108 mdopen
, mdclose
, mdread
, mdwrite
, mdioctl
,
109 nostop
, notty
, nopoll
, nommap
, nokqfilter
, D_DISK
112 static struct dkdriver mddkdriver
= { mdstrategy
, NULL
};
114 extern struct cfdriver md_cd
;
115 CFATTACH_DECL3_NEW(md
, sizeof(struct md_softc
),
116 0, md_attach
, md_detach
, NULL
, NULL
, NULL
, DVF_DETACH_SHUTDOWN
);
118 extern size_t md_root_size
;
121 * This is called if we are configured as a pseudo-device
131 * Attachement of md0 must be done after md_root_setconf(),
132 * because the RAMdisk is not loaded yet.
134 if (md_root_size
== 0)
137 if (config_cfattach_attach("md", &md_ca
)) {
138 printf("md: cfattach_attach failed\n");
142 /* XXX: Are we supposed to provide a default? */
146 /* Attach as if by autoconfig. */
147 for (i
= 0; i
< n
; i
++) {
148 cf
= malloc(sizeof(*cf
), M_DEVBUF
, M_WAITOK
);
150 cf
->cf_atname
= "md";
152 cf
->cf_fstate
= FSTATE_NOTFOUND
;
153 (void)config_attach_pseudo(cf
);
158 md_attach(device_t parent
, device_t self
, void *aux
)
160 struct md_softc
*sc
= device_private(self
);
162 bufq_alloc(&sc
->sc_buflist
, "fcfs", 0);
164 /* XXX - Could accept aux info here to set the config. */
165 #ifdef MEMORY_DISK_HOOKS
167 * This external function might setup a pre-loaded disk.
168 * All it would need to do is setup the md_conf struct.
169 * See sys/dev/md_root.c for an example.
171 md_attach_hook(device_unit(self
), &sc
->sc_md
);
175 * Initialize and attach the disk structure.
177 disk_init(&sc
->sc_dkdev
, device_xname(self
), &mddkdriver
);
178 disk_attach(&sc
->sc_dkdev
);
180 if (!pmf_device_register(self
, NULL
, NULL
))
181 aprint_error_dev(self
, "couldn't establish power handler\n");
185 md_detach(device_t self
, int flags
)
187 struct md_softc
*sc
= device_private(self
);
191 mutex_enter(&sc
->sc_dkdev
.dk_openlock
);
192 if (sc
->sc_dkdev
.dk_openmask
== 0)
193 ; /* nothing to do */
194 else if ((flags
& DETACH_FORCE
) == 0)
196 mutex_exit(&sc
->sc_dkdev
.dk_openlock
);
201 pmf_device_deregister(self
);
202 disk_detach(&sc
->sc_dkdev
);
203 disk_destroy(&sc
->sc_dkdev
);
204 bufq_free(sc
->sc_buflist
);
209 * operational routines:
210 * open, close, read, write, strategy,
214 #if MEMORY_DISK_SERVER
215 static int md_server_loop(struct md_softc
*sc
);
216 static int md_ioctl_server(struct md_softc
*sc
, struct md_conf
*umd
,
218 #endif /* MEMORY_DISK_SERVER */
219 static int md_ioctl_kalloc(struct md_softc
*sc
, struct md_conf
*umd
,
227 sc
= device_lookup_private(&md_cd
, MD_UNIT(dev
));
231 if (sc
->sc_type
== MD_UNCONFIGURED
)
234 return (sc
->sc_size
>> DEV_BSHIFT
);
238 mdopen(dev_t dev
, int flag
, int fmt
, struct lwp
*l
)
241 int part
= DISKPART(dev
);
242 int pmask
= 1 << part
;
247 sc
= device_lookup_private(&md_cd
, unit
);
254 * The raw partition is used for ioctl to configure.
256 if (part
== RAW_PART
)
259 #ifdef MEMORY_DISK_HOOKS
260 /* Call the open hook to allow loading the device. */
261 md_open_hook(unit
, &sc
->sc_md
);
265 * This is a normal, "slave" device, so
266 * enforce initialized.
268 if (sc
->sc_type
== MD_UNCONFIGURED
)
272 /* XXX duplicates code in dk_open(). Call dk_open(), instead? */
273 mutex_enter(&dk
->dk_openlock
);
274 /* Mark our unit as open. */
277 dk
->dk_copenmask
|= pmask
;
280 dk
->dk_bopenmask
|= pmask
;
284 dk
->dk_openmask
= dk
->dk_copenmask
| dk
->dk_bopenmask
;
286 mutex_exit(&dk
->dk_openlock
);
291 mdclose(dev_t dev
, int flag
, int fmt
, struct lwp
*l
)
293 int part
= DISKPART(dev
);
294 int pmask
= 1 << part
;
298 sc
= device_lookup_private(&md_cd
, MD_UNIT(dev
));
304 mutex_enter(&dk
->dk_openlock
);
308 dk
->dk_copenmask
&= ~pmask
;
311 dk
->dk_bopenmask
&= ~pmask
;
314 dk
->dk_openmask
= dk
->dk_copenmask
| dk
->dk_bopenmask
;
316 mutex_exit(&dk
->dk_openlock
);
321 mdread(dev_t dev
, struct uio
*uio
, int flags
)
325 sc
= device_lookup_private(&md_cd
, MD_UNIT(dev
));
327 if (sc
->sc_type
== MD_UNCONFIGURED
)
330 return (physio(mdstrategy
, NULL
, dev
, B_READ
, minphys
, uio
));
334 mdwrite(dev_t dev
, struct uio
*uio
, int flags
)
338 sc
= device_lookup_private(&md_cd
, MD_UNIT(dev
));
340 if (sc
->sc_type
== MD_UNCONFIGURED
)
343 return (physio(mdstrategy
, NULL
, dev
, B_WRITE
, minphys
, uio
));
347 * Handle I/O requests, either directly, or
348 * by passing them to the server process.
351 mdstrategy(struct buf
*bp
)
357 sc
= device_lookup_private(&md_cd
, MD_UNIT(bp
->b_dev
));
359 if (sc
->sc_type
== MD_UNCONFIGURED
) {
364 switch (sc
->sc_type
) {
365 #if MEMORY_DISK_SERVER
367 /* Just add this job to the server's queue. */
368 bufq_put(sc
->sc_buflist
, bp
);
370 /* see md_server_loop() */
371 /* no biodone in this case */
373 #endif /* MEMORY_DISK_SERVER */
376 case MD_KMEM_ALLOCATED
:
377 /* These are in kernel space. Access directly. */
378 bp
->b_resid
= bp
->b_bcount
;
379 off
= (bp
->b_blkno
<< DEV_BSHIFT
);
380 if (off
>= sc
->sc_size
) {
381 if (bp
->b_flags
& B_READ
)
386 if (xfer
> (sc
->sc_size
- off
))
387 xfer
= (sc
->sc_size
- off
);
388 addr
= (char *)sc
->sc_addr
+ off
;
389 if (bp
->b_flags
& B_READ
)
390 memcpy(bp
->b_data
, addr
, xfer
);
392 memcpy(addr
, bp
->b_data
, xfer
);
397 bp
->b_resid
= bp
->b_bcount
;
407 mdioctl(dev_t dev
, u_long cmd
, void *data
, int flag
, struct lwp
*l
)
412 sc
= device_lookup_private(&md_cd
, MD_UNIT(dev
));
414 /* If this is not the raw partition, punt! */
415 if (DISKPART(dev
) != RAW_PART
)
418 umd
= (struct md_conf
*)data
;
425 /* Can only set it once. */
426 if (sc
->sc_type
!= MD_UNCONFIGURED
)
428 switch (umd
->md_type
) {
429 case MD_KMEM_ALLOCATED
:
430 return md_ioctl_kalloc(sc
, umd
, l
);
431 #if MEMORY_DISK_SERVER
433 return md_ioctl_server(sc
, umd
, l
);
434 #endif /* MEMORY_DISK_SERVER */
444 * Handle ioctl MD_SETCONF for (sc_type == MD_KMEM_ALLOCATED)
445 * Just allocate some kernel memory and return.
448 md_ioctl_kalloc(struct md_softc
*sc
, struct md_conf
*umd
,
454 /* Sanity check the size. */
456 addr
= uvm_km_alloc(kernel_map
, size
, 0, UVM_KMF_WIRED
|UVM_KMF_ZERO
);
460 /* This unit is now configured. */
461 sc
->sc_addr
= (void *)addr
; /* kernel space */
462 sc
->sc_size
= (size_t)size
;
463 sc
->sc_type
= MD_KMEM_ALLOCATED
;
467 #if MEMORY_DISK_SERVER
470 * Handle ioctl MD_SETCONF for (sc_type == MD_UMEM_SERVER)
471 * Set config, then become the I/O server for this unit.
474 md_ioctl_server(struct md_softc
*sc
, struct md_conf
*umd
,
480 /* Sanity check addr, size. */
481 end
= (vaddr_t
) ((char *)umd
->md_addr
+ umd
->md_size
);
483 if ((end
>= VM_MAXUSER_ADDRESS
) ||
484 (end
< ((vaddr_t
) umd
->md_addr
)) )
487 /* This unit is now configured. */
488 sc
->sc_addr
= umd
->md_addr
; /* user space */
489 sc
->sc_size
= umd
->md_size
;
490 sc
->sc_type
= MD_UMEM_SERVER
;
492 /* Become the server daemon */
493 error
= md_server_loop(sc
);
495 /* This server is now going away! */
496 sc
->sc_type
= MD_UNCONFIGURED
;
503 static int md_sleep_pri
= PWAIT
| PCATCH
;
506 md_server_loop(struct md_softc
*sc
)
509 void *addr
; /* user space address */
510 size_t off
; /* offset into "device" */
511 size_t xfer
; /* amount to transfer */
515 /* Wait for some work to arrive. */
516 while ((bp
= bufq_get(sc
->sc_buflist
)) == NULL
) {
517 error
= tsleep((void *)sc
, md_sleep_pri
, "md_idle", 0);
522 /* Do the transfer to/from user space. */
524 bp
->b_resid
= bp
->b_bcount
;
525 off
= (bp
->b_blkno
<< DEV_BSHIFT
);
526 if (off
>= sc
->sc_size
) {
527 if (bp
->b_flags
& B_READ
)
528 goto done
; /* EOF (not an error) */
533 if (xfer
> (sc
->sc_size
- off
))
534 xfer
= (sc
->sc_size
- off
);
535 addr
= (char *)sc
->sc_addr
+ off
;
536 if (bp
->b_flags
& B_READ
)
537 error
= copyin(addr
, bp
->b_data
, xfer
);
539 error
= copyout(bp
->b_data
, addr
, xfer
);
550 #endif /* MEMORY_DISK_SERVER */