md.c revision 59249
1/*
2 * ----------------------------------------------------------------------------
3 * "THE BEER-WARE LICENSE" (Revision 42):
4 * <phk@FreeBSD.ORG> wrote this file.  As long as you retain this notice you
5 * can do whatever you want with this stuff. If we meet some day, and you think
6 * this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
7 * ----------------------------------------------------------------------------
8 *
9 * $FreeBSD: head/sys/dev/md/md.c 59249 2000-04-15 05:54:02Z phk $
10 *
11 */
12
13#include "opt_mfs.h"		/* We have adopted some tasks from MFS */
14#include "opt_md.h"		/* We have adopted some tasks from MFS */
15
16#include <sys/param.h>
17#include <sys/systm.h>
18#include <sys/buf.h>
19#include <sys/conf.h>
20#include <sys/devicestat.h>
21#include <sys/disk.h>
22#include <sys/kernel.h>
23#include <sys/malloc.h>
24#include <sys/sysctl.h>
25#include <sys/linker.h>
26
27#ifndef MDNSECT
28#define MDNSECT (10000 * 2)
29#endif
30
31MALLOC_DEFINE(M_MD, "MD disk", "Memory Disk");
32MALLOC_DEFINE(M_MDSECT, "MD sectors", "Memory Disk Sectors");
33
34static int md_debug;
35SYSCTL_INT(_debug, OID_AUTO, mddebug, CTLFLAG_RW, &md_debug, 0, "");
36
37#if defined(MFS_ROOT) && !defined(MD_ROOT)
38#define MD_ROOT MFS_ROOT
39#warning "option MFS_ROOT has been superceeded by MD_ROOT"
40#endif
41
42#if defined(MFS_ROOT_SIZE) && !defined(MD_ROOT_SIZE)
43#define MD_ROOT_SIZE MFS_ROOT_SIZE
44#warning "option MFS_ROOT_SIZE has been superceeded by MD_ROOT_SIZE"
45#endif
46
47#if defined(MD_ROOT) && defined(MD_ROOT_SIZE)
48/* Image gets put here: */
49static u_char mfs_root[MD_ROOT_SIZE*1024] = "MFS Filesystem goes here";
50static u_char end_mfs_root[] __unused = "MFS Filesystem had better STOP here";
51#endif
52
53static int mdrootready;
54
55#define CDEV_MAJOR	95
56#define BDEV_MAJOR	22
57
58static d_strategy_t mdstrategy;
59static d_strategy_t mdstrategy_preload;
60static d_strategy_t mdstrategy_malloc;
61static d_open_t mdopen;
62static d_ioctl_t mdioctl;
63
64static struct cdevsw md_cdevsw = {
65        /* open */      mdopen,
66        /* close */     nullclose,
67        /* read */      physread,
68        /* write */     physwrite,
69        /* ioctl */     mdioctl,
70        /* poll */      nopoll,
71        /* mmap */      nommap,
72        /* strategy */  mdstrategy,
73        /* name */      "md",
74        /* maj */       CDEV_MAJOR,
75        /* dump */      nodump,
76        /* psize */     nopsize,
77        /* flags */     D_DISK | D_CANFREE | D_MEMDISK,
78        /* bmaj */      BDEV_MAJOR
79};
80
81struct md_s {
82	int unit;
83	struct devstat stats;
84	struct bio_queue_head bio_queue;
85	struct disk disk;
86	dev_t dev;
87	int busy;
88	enum {MD_MALLOC, MD_PRELOAD} type;
89	unsigned nsect;
90	struct cdevsw devsw;
91
92	/* MD_MALLOC related fields */
93	unsigned nsecp;
94	u_char **secp;
95
96	/* MD_PRELOAD related fields */
97	u_char *pl_ptr;
98	unsigned pl_len;
99};
100
101static int mdunits;
102
103static int
104mdopen(dev_t dev, int flag, int fmt, struct proc *p)
105{
106	struct md_s *sc;
107	struct disklabel *dl;
108
109	if (md_debug)
110		printf("mdopen(%s %x %x %p)\n",
111			devtoname(dev), flag, fmt, p);
112
113	sc = dev->si_drv1;
114
115	dl = &sc->disk.d_label;
116	bzero(dl, sizeof(*dl));
117	dl->d_secsize = DEV_BSIZE;
118	dl->d_nsectors = 1024;
119	dl->d_ntracks = 1;
120	dl->d_secpercyl = dl->d_nsectors * dl->d_ntracks;
121	dl->d_secperunit = sc->nsect;
122	dl->d_ncylinders = dl->d_secperunit / dl->d_secpercyl;
123	return (0);
124}
125
126static int
127mdioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p)
128{
129
130	if (md_debug)
131		printf("mdioctl(%s %lx %p %x %p)\n",
132			devtoname(dev), cmd, addr, flags, p);
133
134	return (ENOIOCTL);
135}
136
137static void
138mdstrategy(struct bio *bp)
139{
140	struct md_s *sc;
141
142	if (md_debug > 1)
143		printf("mdstrategy(%p) %s %x, %d, %ld, %p)\n",
144		    bp, devtoname(bp->bio_dev), bp->bio_flags, bp->bio_blkno,
145		    bp->bio_bcount / DEV_BSIZE, bp->bio_data);
146
147	sc = bp->bio_dev->si_drv1;
148	if (sc->type == MD_MALLOC) {
149		mdstrategy_malloc(bp);
150	} else {
151		mdstrategy_preload(bp);
152	}
153	return;
154}
155
156
157static void
158mdstrategy_malloc(struct bio *bp)
159{
160	int s, i;
161	struct md_s *sc;
162	devstat_trans_flags dop;
163	u_char *secp, **secpp, *dst;
164	unsigned secno, nsec, secval, uc;
165
166	if (md_debug > 1)
167		printf("mdstrategy_malloc(%p) %s %x, %d, %ld, %p)\n",
168		    bp, devtoname(bp->bio_dev), bp->bio_flags, bp->bio_blkno,
169		    bp->bio_bcount / DEV_BSIZE, bp->bio_data);
170
171	sc = bp->bio_dev->si_drv1;
172
173	s = splbio();
174
175	bioqdisksort(&sc->bio_queue, bp);
176
177	if (sc->busy) {
178		splx(s);
179		return;
180	}
181
182	sc->busy++;
183
184	while (1) {
185		bp = bioq_first(&sc->bio_queue);
186		if (bp)
187			bioq_remove(&sc->bio_queue, bp);
188		splx(s);
189		if (!bp)
190			break;
191
192		devstat_start_transaction(&sc->stats);
193
194		if (bp->bio_cmd == BIO_DELETE)
195			dop = DEVSTAT_NO_DATA;
196		else if (bp->bio_cmd == BIO_READ)
197			dop = DEVSTAT_READ;
198		else
199			dop = DEVSTAT_WRITE;
200
201		nsec = bp->bio_bcount / DEV_BSIZE;
202		secno = bp->bio_pblkno;
203		dst = bp->bio_data;
204		while (nsec--) {
205
206			if (secno < sc->nsecp) {
207				secpp = &sc->secp[secno];
208				if ((u_int)*secpp > 255) {
209					secp = *secpp;
210					secval = 0;
211				} else {
212					secp = 0;
213					secval = (u_int) *secpp;
214				}
215			} else {
216				secpp = 0;
217				secp = 0;
218				secval = 0;
219			}
220			if (md_debug > 2)
221				printf("%x %p %p %d\n",
222				    bp->bio_flags, secpp, secp, secval);
223
224			if (bp->bio_cmd == BIO_DELETE) {
225				if (secpp) {
226					if (secp)
227						FREE(secp, M_MDSECT);
228					*secpp = 0;
229				}
230			} else if (bp->bio_cmd == BIO_READ) {
231				if (secp) {
232					bcopy(secp, dst, DEV_BSIZE);
233				} else if (secval) {
234					for (i = 0; i < DEV_BSIZE; i++)
235						dst[i] = secval;
236				} else {
237					bzero(dst, DEV_BSIZE);
238				}
239			} else {
240				uc = dst[0];
241				for (i = 1; i < DEV_BSIZE; i++)
242					if (dst[i] != uc)
243						break;
244				if (i == DEV_BSIZE && !uc) {
245					if (secp)
246						FREE(secp, M_MDSECT);
247					if (secpp)
248						*secpp = (u_char *)uc;
249				} else {
250					if (!secpp) {
251						MALLOC(secpp, u_char **, (secno + nsec + 1) * sizeof(u_char *), M_MD, M_WAITOK);
252						bzero(secpp, (secno + nsec + 1) * sizeof(u_char *));
253						bcopy(sc->secp, secpp, sc->nsecp * sizeof(u_char *));
254						FREE(sc->secp, M_MD);
255						sc->secp = secpp;
256						sc->nsecp = secno + nsec + 1;
257						secpp = &sc->secp[secno];
258					}
259					if (i == DEV_BSIZE) {
260						if (secp)
261							FREE(secp, M_MDSECT);
262						*secpp = (u_char *)uc;
263					} else {
264						if (!secp)
265							MALLOC(secp, u_char *, DEV_BSIZE, M_MDSECT, M_WAITOK);
266						bcopy(dst, secp, DEV_BSIZE);
267
268						*secpp = secp;
269					}
270				}
271			}
272			secno++;
273			dst += DEV_BSIZE;
274		}
275		bp->bio_resid = 0;
276		devstat_end_transaction_bio(&sc->stats, bp);
277		biodone(bp);
278		s = splbio();
279	}
280	sc->busy = 0;
281	return;
282}
283
284
285static void
286mdstrategy_preload(struct bio *bp)
287{
288	int s;
289	struct md_s *sc;
290	devstat_trans_flags dop;
291
292	if (md_debug > 1)
293		printf("mdstrategy_preload(%p) %s %x, %d, %ld, %p)\n",
294		    bp, devtoname(bp->bio_dev), bp->bio_flags, bp->bio_blkno,
295		    bp->bio_bcount / DEV_BSIZE, bp->bio_data);
296
297	sc = bp->bio_dev->si_drv1;
298
299	s = splbio();
300
301	bioqdisksort(&sc->bio_queue, bp);
302
303	if (sc->busy) {
304		splx(s);
305		return;
306	}
307
308	sc->busy++;
309
310	while (1) {
311		bp = bioq_first(&sc->bio_queue);
312		if (bp)
313			bioq_remove(&sc->bio_queue, bp);
314		splx(s);
315		if (!bp)
316			break;
317
318		devstat_start_transaction(&sc->stats);
319
320		if (bp->bio_cmd == BIO_DELETE) {
321			dop = DEVSTAT_NO_DATA;
322		} else if (bp->bio_cmd == BIO_READ) {
323			dop = DEVSTAT_READ;
324			bcopy(sc->pl_ptr + (bp->bio_pblkno << DEV_BSHIFT), bp->bio_data, bp->bio_bcount);
325		} else {
326			dop = DEVSTAT_WRITE;
327			bcopy(bp->bio_data, sc->pl_ptr + (bp->bio_pblkno << DEV_BSHIFT), bp->bio_bcount);
328		}
329		bp->bio_resid = 0;
330		devstat_end_transaction_bio(&sc->stats, bp);
331		biodone(bp);
332		s = splbio();
333	}
334	sc->busy = 0;
335	return;
336}
337
338static struct md_s *
339mdcreate(struct cdevsw *devsw)
340{
341	struct md_s *sc;
342
343	MALLOC(sc, struct md_s *,sizeof(*sc), M_MD, M_WAITOK);
344	bzero(sc, sizeof(*sc));
345	sc->unit = mdunits++;
346	bioq_init(&sc->bio_queue);
347	devstat_add_entry(&sc->stats, "md", sc->unit, DEV_BSIZE,
348		DEVSTAT_NO_ORDERED_TAGS,
349		DEVSTAT_TYPE_DIRECT | DEVSTAT_TYPE_IF_OTHER,
350		DEVSTAT_PRIORITY_OTHER);
351	sc->dev = disk_create(sc->unit, &sc->disk, 0, devsw, &sc->devsw);
352	sc->dev->si_drv1 = sc;
353	return (sc);
354}
355
356static void
357mdcreate_preload(u_char *image, unsigned length)
358{
359	struct md_s *sc;
360
361	sc = mdcreate(&md_cdevsw);
362	sc->type = MD_PRELOAD;
363	sc->nsect = length / DEV_BSIZE;
364	sc->pl_ptr = image;
365	sc->pl_len = length;
366
367	if (sc->unit == 0)
368		mdrootready = 1;
369}
370
371static void
372mdcreate_malloc(void)
373{
374	struct md_s *sc;
375
376	sc = mdcreate(&md_cdevsw);
377	sc->type = MD_MALLOC;
378
379	sc->nsect = MDNSECT;	/* for now */
380	MALLOC(sc->secp, u_char **, sizeof(u_char *), M_MD, M_WAITOK);
381	bzero(sc->secp, sizeof(u_char *));
382	sc->nsecp = 1;
383}
384
385static void
386md_drvinit(void *unused)
387{
388
389	caddr_t mod;
390	caddr_t c;
391	u_char *ptr, *name, *type;
392	unsigned len;
393
394#ifdef MD_ROOT_SIZE
395	mdcreate_preload(mfs_root, MD_ROOT_SIZE*1024);
396#endif
397	mod = NULL;
398	while ((mod = preload_search_next_name(mod)) != NULL) {
399		name = (char *)preload_search_info(mod, MODINFO_NAME);
400		type = (char *)preload_search_info(mod, MODINFO_TYPE);
401		if (name == NULL)
402			continue;
403		if (type == NULL)
404			continue;
405		if (strcmp(type, "md_image") && strcmp(type, "mfs_root"))
406			continue;
407		c = preload_search_info(mod, MODINFO_ADDR);
408		ptr = *(u_char **)c;
409		c = preload_search_info(mod, MODINFO_SIZE);
410		len = *(unsigned *)c;
411		printf("md%d: Preloaded image <%s> %d bytes at %p\n",
412		   mdunits, name, len, ptr);
413		mdcreate_preload(ptr, len);
414	}
415	printf("md%d: Malloc disk\n", mdunits);
416	mdcreate_malloc();
417}
418
419SYSINIT(mddev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR, md_drvinit,NULL)
420
421#ifdef MD_ROOT
422static void
423md_takeroot(void *junk)
424{
425	if (mdrootready)
426		rootdevnames[0] = "ufs:/dev/md0c";
427}
428
429SYSINIT(md_root, SI_SUB_MOUNT_ROOT, SI_ORDER_FIRST, md_takeroot, NULL);
430#endif
431