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