xref: /freebsd/sys/dev/md/md.c (revision 5129159789cc9d7bc514e4546b88e3427695002d)
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"		/* 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 
31 MALLOC_DEFINE(M_MD, "MD disk", "Memory Disk");
32 MALLOC_DEFINE(M_MDSECT, "MD sectors", "Memory Disk Sectors");
33 
34 static int md_debug;
35 SYSCTL_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: */
49 static u_char mfs_root[MD_ROOT_SIZE*1024] = "MFS Filesystem goes here";
50 static u_char end_mfs_root[] __unused = "MFS Filesystem had better STOP here";
51 #endif
52 
53 static int mdrootready;
54 
55 #define CDEV_MAJOR	95
56 #define BDEV_MAJOR	22
57 
58 static d_strategy_t mdstrategy;
59 static d_strategy_t mdstrategy_preload;
60 static d_strategy_t mdstrategy_malloc;
61 static d_open_t mdopen;
62 static d_ioctl_t mdioctl;
63 
64 static 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 
81 struct md_s {
82 	int unit;
83 	struct devstat stats;
84 	struct buf_queue_head buf_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 
101 static int mdunits;
102 
103 static int
104 mdopen(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 
126 static int
127 mdioctl(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 
137 static void
138 mdstrategy(struct buf *bp)
139 {
140 	struct md_s *sc;
141 
142 	if (md_debug > 1)
143 		printf("mdstrategy(%p) %s %lx, %d, %ld, %p)\n",
144 		    bp, devtoname(bp->b_dev), bp->b_flags, bp->b_blkno,
145 		    bp->b_bcount / DEV_BSIZE, bp->b_data);
146 
147 	sc = bp->b_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 
157 static void
158 mdstrategy_malloc(struct buf *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 %lx, %d, %ld, %p)\n",
168 		    bp, devtoname(bp->b_dev), bp->b_flags, bp->b_blkno,
169 		    bp->b_bcount / DEV_BSIZE, bp->b_data);
170 
171 	sc = bp->b_dev->si_drv1;
172 
173 	s = splbio();
174 
175 	bufqdisksort(&sc->buf_queue, bp);
176 
177 	if (sc->busy) {
178 		splx(s);
179 		return;
180 	}
181 
182 	sc->busy++;
183 
184 	while (1) {
185 		bp = bufq_first(&sc->buf_queue);
186 		if (bp)
187 			bufq_remove(&sc->buf_queue, bp);
188 		splx(s);
189 		if (!bp)
190 			break;
191 
192 		devstat_start_transaction(&sc->stats);
193 
194 		if (bp->b_flags & B_FREEBUF)
195 			dop = DEVSTAT_NO_DATA;
196 		else if (bp->b_flags & B_READ)
197 			dop = DEVSTAT_READ;
198 		else
199 			dop = DEVSTAT_WRITE;
200 
201 		nsec = bp->b_bcount / DEV_BSIZE;
202 		secno = bp->b_pblkno;
203 		dst = bp->b_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("%lx %p %p %d\n", bp->b_flags, secpp, secp, secval);
222 
223 			if (bp->b_flags & B_FREEBUF) {
224 				if (secpp) {
225 					if (secp)
226 						FREE(secp, M_MDSECT);
227 					*secpp = 0;
228 				}
229 			} else if (bp->b_flags & B_READ) {
230 				if (secp) {
231 					bcopy(secp, dst, DEV_BSIZE);
232 				} else if (secval) {
233 					for (i = 0; i < DEV_BSIZE; i++)
234 						dst[i] = secval;
235 				} else {
236 					bzero(dst, DEV_BSIZE);
237 				}
238 			} else {
239 				uc = dst[0];
240 				for (i = 1; i < DEV_BSIZE; i++)
241 					if (dst[i] != uc)
242 						break;
243 				if (i == DEV_BSIZE && !uc) {
244 					if (secp)
245 						FREE(secp, M_MDSECT);
246 					if (secpp)
247 						*secpp = (u_char *)uc;
248 				} else {
249 					if (!secpp) {
250 						MALLOC(secpp, u_char **, (secno + nsec + 1) * sizeof(u_char *), M_MD, M_WAITOK);
251 						bzero(secpp, (secno + nsec + 1) * sizeof(u_char *));
252 						bcopy(sc->secp, secpp, sc->nsecp * sizeof(u_char *));
253 						FREE(sc->secp, M_MD);
254 						sc->secp = secpp;
255 						sc->nsecp = secno + nsec + 1;
256 						secpp = &sc->secp[secno];
257 					}
258 					if (i == DEV_BSIZE) {
259 						if (secp)
260 							FREE(secp, M_MDSECT);
261 						*secpp = (u_char *)uc;
262 					} else {
263 						if (!secp)
264 							MALLOC(secp, u_char *, DEV_BSIZE, M_MDSECT, M_WAITOK);
265 						bcopy(dst, secp, DEV_BSIZE);
266 
267 						*secpp = secp;
268 					}
269 				}
270 			}
271 			secno++;
272 			dst += DEV_BSIZE;
273 		}
274 		bp->b_resid = 0;
275 		devstat_end_transaction_buf(&sc->stats, bp);
276 		biodone(bp);
277 		s = splbio();
278 	}
279 	sc->busy = 0;
280 	return;
281 }
282 
283 
284 static void
285 mdstrategy_preload(struct buf *bp)
286 {
287 	int s;
288 	struct md_s *sc;
289 	devstat_trans_flags dop;
290 
291 	if (md_debug > 1)
292 		printf("mdstrategy_preload(%p) %s %lx, %d, %ld, %p)\n",
293 		    bp, devtoname(bp->b_dev), bp->b_flags, bp->b_blkno,
294 		    bp->b_bcount / DEV_BSIZE, bp->b_data);
295 
296 	sc = bp->b_dev->si_drv1;
297 
298 	s = splbio();
299 
300 	bufqdisksort(&sc->buf_queue, bp);
301 
302 	if (sc->busy) {
303 		splx(s);
304 		return;
305 	}
306 
307 	sc->busy++;
308 
309 	while (1) {
310 		bp = bufq_first(&sc->buf_queue);
311 		if (bp)
312 			bufq_remove(&sc->buf_queue, bp);
313 		splx(s);
314 		if (!bp)
315 			break;
316 
317 		devstat_start_transaction(&sc->stats);
318 
319 		if (bp->b_flags & B_FREEBUF) {
320 			dop = DEVSTAT_NO_DATA;
321 		} else if (bp->b_flags & B_READ) {
322 			dop = DEVSTAT_READ;
323 			bcopy(sc->pl_ptr + (bp->b_pblkno << DEV_BSHIFT), bp->b_data, bp->b_bcount);
324 		} else {
325 			dop = DEVSTAT_WRITE;
326 			bcopy(bp->b_data, sc->pl_ptr + (bp->b_pblkno << DEV_BSHIFT), bp->b_bcount);
327 		}
328 		bp->b_resid = 0;
329 		devstat_end_transaction_buf(&sc->stats, bp);
330 		biodone(bp);
331 		s = splbio();
332 	}
333 	sc->busy = 0;
334 	return;
335 }
336 
337 static struct md_s *
338 mdcreate(struct cdevsw *devsw)
339 {
340 	struct md_s *sc;
341 
342 	MALLOC(sc, struct md_s *,sizeof(*sc), M_MD, M_WAITOK);
343 	bzero(sc, sizeof(*sc));
344 	sc->unit = mdunits++;
345 	bufq_init(&sc->buf_queue);
346 	devstat_add_entry(&sc->stats, "md", sc->unit, DEV_BSIZE,
347 		DEVSTAT_NO_ORDERED_TAGS,
348 		DEVSTAT_TYPE_DIRECT | DEVSTAT_TYPE_IF_OTHER,
349 		DEVSTAT_PRIORITY_OTHER);
350 	sc->dev = disk_create(sc->unit, &sc->disk, 0, devsw, &sc->devsw);
351 	sc->dev->si_drv1 = sc;
352 	return (sc);
353 }
354 
355 static void
356 mdcreate_preload(u_char *image, unsigned length)
357 {
358 	struct md_s *sc;
359 
360 	sc = mdcreate(&md_cdevsw);
361 	sc->type = MD_PRELOAD;
362 	sc->nsect = length / DEV_BSIZE;
363 	sc->pl_ptr = image;
364 	sc->pl_len = length;
365 
366 	if (sc->unit == 0)
367 		mdrootready = 1;
368 }
369 
370 static void
371 mdcreate_malloc(void)
372 {
373 	struct md_s *sc;
374 
375 	sc = mdcreate(&md_cdevsw);
376 	sc->type = MD_MALLOC;
377 
378 	sc->nsect = MDNSECT;	/* for now */
379 	MALLOC(sc->secp, u_char **, sizeof(u_char *), M_MD, M_WAITOK);
380 	bzero(sc->secp, sizeof(u_char *));
381 	sc->nsecp = 1;
382 }
383 
384 static void
385 md_drvinit(void *unused)
386 {
387 
388 	caddr_t mod;
389 	caddr_t c;
390 	u_char *ptr, *name, *type;
391 	unsigned len;
392 
393 #ifdef MD_ROOT_SIZE
394 	mdcreate_preload(mfs_root, MD_ROOT_SIZE*1024);
395 #endif
396 	mod = NULL;
397 	while ((mod = preload_search_next_name(mod)) != NULL) {
398 		name = (char *)preload_search_info(mod, MODINFO_NAME);
399 		type = (char *)preload_search_info(mod, MODINFO_TYPE);
400 		if (name == NULL)
401 			continue;
402 		if (type == NULL)
403 			continue;
404 		if (strcmp(type, "md_image") && strcmp(type, "mfs_root"))
405 			continue;
406 		c = preload_search_info(mod, MODINFO_ADDR);
407 		ptr = *(u_char **)c;
408 		c = preload_search_info(mod, MODINFO_SIZE);
409 		len = *(unsigned *)c;
410 		printf("md%d: Preloaded image <%s> %d bytes at %p\n",
411 		   mdunits, name, len, ptr);
412 		mdcreate_preload(ptr, len);
413 	}
414 	printf("md%d: Malloc disk\n", mdunits);
415 	mdcreate_malloc();
416 }
417 
418 SYSINIT(mddev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR, md_drvinit,NULL)
419 
420 #ifdef MD_ROOT
421 static void
422 md_takeroot(void *junk)
423 {
424 	if (mdrootready)
425 		rootdevnames[0] = "ufs:/dev/md0c";
426 }
427 
428 SYSINIT(md_root, SI_SUB_MOUNT_ROOT, SI_ORDER_FIRST, md_takeroot, NULL);
429 #endif
430