xref: /freebsd/sys/dev/md/md.c (revision 00a6a3c65f0470e5fe9a036d5700637c3a6d3456)
100a6a3c6SPoul-Henning Kamp /*
200a6a3c6SPoul-Henning Kamp  * ----------------------------------------------------------------------------
300a6a3c6SPoul-Henning Kamp  * "THE BEER-WARE LICENSE" (Revision 42):
400a6a3c6SPoul-Henning Kamp  * <phk@FreeBSD.ORG> wrote this file.  As long as you retain this notice you
500a6a3c6SPoul-Henning Kamp  * can do whatever you want with this stuff. If we meet some day, and you think
600a6a3c6SPoul-Henning Kamp  * this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
700a6a3c6SPoul-Henning Kamp  * ----------------------------------------------------------------------------
800a6a3c6SPoul-Henning Kamp  *
900a6a3c6SPoul-Henning Kamp  * $FreeBSD$
1000a6a3c6SPoul-Henning Kamp  *
1100a6a3c6SPoul-Henning Kamp  */
1200a6a3c6SPoul-Henning Kamp 
1300a6a3c6SPoul-Henning Kamp #include <sys/param.h>
1400a6a3c6SPoul-Henning Kamp #include <sys/systm.h>
1500a6a3c6SPoul-Henning Kamp #include <sys/sysctl.h>
1600a6a3c6SPoul-Henning Kamp #include <sys/kernel.h>
1700a6a3c6SPoul-Henning Kamp #include <sys/buf.h>
1800a6a3c6SPoul-Henning Kamp #include <sys/malloc.h>
1900a6a3c6SPoul-Henning Kamp #include <sys/conf.h>
2000a6a3c6SPoul-Henning Kamp #include <sys/disk.h>
2100a6a3c6SPoul-Henning Kamp #include <sys/devicestat.h>
2200a6a3c6SPoul-Henning Kamp #include <sys/module.h>
2300a6a3c6SPoul-Henning Kamp #include <machine/bus.h>
2400a6a3c6SPoul-Henning Kamp #include <machine/clock.h>
2500a6a3c6SPoul-Henning Kamp #include <machine/resource.h>
2600a6a3c6SPoul-Henning Kamp 
2700a6a3c6SPoul-Henning Kamp #include <vm/vm.h>
2800a6a3c6SPoul-Henning Kamp #include <vm/pmap.h>
2900a6a3c6SPoul-Henning Kamp #include <vm/vm_param.h>
3000a6a3c6SPoul-Henning Kamp 
3100a6a3c6SPoul-Henning Kamp #include <sys/bus.h>
3200a6a3c6SPoul-Henning Kamp #include <isa/isareg.h>
3300a6a3c6SPoul-Henning Kamp #include <isa/isavar.h>
3400a6a3c6SPoul-Henning Kamp 
3500a6a3c6SPoul-Henning Kamp MALLOC_DEFINE(M_MD, "MD disk", "Memory Disk");
3600a6a3c6SPoul-Henning Kamp MALLOC_DEFINE(M_MDSECT, "MD sectors", "Memory Disk Sectors");
3700a6a3c6SPoul-Henning Kamp 
3800a6a3c6SPoul-Henning Kamp static int md_debug = 0;
3900a6a3c6SPoul-Henning Kamp SYSCTL_INT(_debug, OID_AUTO, mddebug, CTLFLAG_RW, &md_debug, 0, "");
4000a6a3c6SPoul-Henning Kamp 
4100a6a3c6SPoul-Henning Kamp #define CDEV_MAJOR	95
4200a6a3c6SPoul-Henning Kamp #define BDEV_MAJOR	22
4300a6a3c6SPoul-Henning Kamp 
4400a6a3c6SPoul-Henning Kamp static d_strategy_t mdstrategy;
4500a6a3c6SPoul-Henning Kamp static d_open_t mdopen;
4600a6a3c6SPoul-Henning Kamp static d_ioctl_t mdioctl;
4700a6a3c6SPoul-Henning Kamp 
4800a6a3c6SPoul-Henning Kamp static struct cdevsw md_cdevsw = {
4900a6a3c6SPoul-Henning Kamp         /* open */      mdopen,
5000a6a3c6SPoul-Henning Kamp         /* close */     nullclose,
5100a6a3c6SPoul-Henning Kamp         /* read */      physread,
5200a6a3c6SPoul-Henning Kamp         /* write */     physwrite,
5300a6a3c6SPoul-Henning Kamp         /* ioctl */     mdioctl,
5400a6a3c6SPoul-Henning Kamp         /* stop */      nostop,
5500a6a3c6SPoul-Henning Kamp         /* reset */     noreset,
5600a6a3c6SPoul-Henning Kamp         /* devtotty */  nodevtotty,
5700a6a3c6SPoul-Henning Kamp         /* poll */      nopoll,
5800a6a3c6SPoul-Henning Kamp         /* mmap */      nommap,
5900a6a3c6SPoul-Henning Kamp         /* strategy */  mdstrategy,
6000a6a3c6SPoul-Henning Kamp         /* name */      "md",
6100a6a3c6SPoul-Henning Kamp         /* parms */     noparms,
6200a6a3c6SPoul-Henning Kamp         /* maj */       CDEV_MAJOR,
6300a6a3c6SPoul-Henning Kamp         /* dump */      nodump,
6400a6a3c6SPoul-Henning Kamp         /* psize */     nopsize,
6500a6a3c6SPoul-Henning Kamp         /* flags */     D_DISK | D_CANFREE,
6600a6a3c6SPoul-Henning Kamp         /* maxio */     0,
6700a6a3c6SPoul-Henning Kamp         /* bmaj */      BDEV_MAJOR
6800a6a3c6SPoul-Henning Kamp };
6900a6a3c6SPoul-Henning Kamp static struct cdevsw mddisk_cdevsw;
7000a6a3c6SPoul-Henning Kamp 
7100a6a3c6SPoul-Henning Kamp struct md_s {
7200a6a3c6SPoul-Henning Kamp 	int unit;
7300a6a3c6SPoul-Henning Kamp 	struct devstat stats;
7400a6a3c6SPoul-Henning Kamp 	struct buf_queue_head buf_queue;
7500a6a3c6SPoul-Henning Kamp 	struct disk disk;
7600a6a3c6SPoul-Henning Kamp 	dev_t dev;
7700a6a3c6SPoul-Henning Kamp 	unsigned nsect;
7800a6a3c6SPoul-Henning Kamp 	unsigned nsecp;
7900a6a3c6SPoul-Henning Kamp 	u_char **secp;
8000a6a3c6SPoul-Henning Kamp 
8100a6a3c6SPoul-Henning Kamp 	int busy;
8200a6a3c6SPoul-Henning Kamp };
8300a6a3c6SPoul-Henning Kamp 
8400a6a3c6SPoul-Henning Kamp static int mdunits;
8500a6a3c6SPoul-Henning Kamp 
8600a6a3c6SPoul-Henning Kamp static int
8700a6a3c6SPoul-Henning Kamp mdopen(dev_t dev, int flag, int fmt, struct proc *p)
8800a6a3c6SPoul-Henning Kamp {
8900a6a3c6SPoul-Henning Kamp 	struct md_s *sc;
9000a6a3c6SPoul-Henning Kamp 	struct disklabel *dl;
9100a6a3c6SPoul-Henning Kamp 
9200a6a3c6SPoul-Henning Kamp 	if (md_debug)
9300a6a3c6SPoul-Henning Kamp 		printf("mdopen(%s %x %x %p)\n",
9400a6a3c6SPoul-Henning Kamp 			devtoname(dev), flag, fmt, p);
9500a6a3c6SPoul-Henning Kamp 
9600a6a3c6SPoul-Henning Kamp 	sc = dev->si_drv1;
9700a6a3c6SPoul-Henning Kamp 
9800a6a3c6SPoul-Henning Kamp 	dl = &sc->disk.d_label;
9900a6a3c6SPoul-Henning Kamp 	bzero(dl, sizeof(*dl));
10000a6a3c6SPoul-Henning Kamp 	dl->d_secsize = DEV_BSIZE;
10100a6a3c6SPoul-Henning Kamp 	dl->d_nsectors = 1024;
10200a6a3c6SPoul-Henning Kamp 	dl->d_ntracks = 1;
10300a6a3c6SPoul-Henning Kamp 	dl->d_secpercyl = dl->d_nsectors + dl->d_ntracks;
10400a6a3c6SPoul-Henning Kamp 	dl->d_secperunit = sc->nsect;
10500a6a3c6SPoul-Henning Kamp 	dl->d_ncylinders = dl->d_secperunit / dl->d_secpercyl;
10600a6a3c6SPoul-Henning Kamp 	return (0);
10700a6a3c6SPoul-Henning Kamp }
10800a6a3c6SPoul-Henning Kamp 
10900a6a3c6SPoul-Henning Kamp static int
11000a6a3c6SPoul-Henning Kamp mdioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p)
11100a6a3c6SPoul-Henning Kamp {
11200a6a3c6SPoul-Henning Kamp 
11300a6a3c6SPoul-Henning Kamp 	if (md_debug)
11400a6a3c6SPoul-Henning Kamp 		printf("mdioctl(%s %lx %p %x %p)\n",
11500a6a3c6SPoul-Henning Kamp 			devtoname(dev), cmd, addr, flags, p);
11600a6a3c6SPoul-Henning Kamp 
11700a6a3c6SPoul-Henning Kamp 	return (ENOIOCTL);
11800a6a3c6SPoul-Henning Kamp }
11900a6a3c6SPoul-Henning Kamp 
12000a6a3c6SPoul-Henning Kamp static void
12100a6a3c6SPoul-Henning Kamp mdstrategy(struct buf *bp)
12200a6a3c6SPoul-Henning Kamp {
12300a6a3c6SPoul-Henning Kamp 	int s, i;
12400a6a3c6SPoul-Henning Kamp 	struct md_s *sc;
12500a6a3c6SPoul-Henning Kamp 	devstat_trans_flags dop;
12600a6a3c6SPoul-Henning Kamp 	u_char *secp, **secpp, *dst;
12700a6a3c6SPoul-Henning Kamp 	unsigned secno, nsec, secval, uc;
12800a6a3c6SPoul-Henning Kamp 
12900a6a3c6SPoul-Henning Kamp 	if (md_debug > 1)
13000a6a3c6SPoul-Henning Kamp 		printf("mdstrategy(%p) %s %lx, %d, %ld, %p)\n",
13100a6a3c6SPoul-Henning Kamp 		    bp, devtoname(bp->b_dev), bp->b_flags, bp->b_blkno,
13200a6a3c6SPoul-Henning Kamp 		    bp->b_bcount / DEV_BSIZE, bp->b_data);
13300a6a3c6SPoul-Henning Kamp 
13400a6a3c6SPoul-Henning Kamp 	sc = bp->b_dev->si_drv1;
13500a6a3c6SPoul-Henning Kamp 
13600a6a3c6SPoul-Henning Kamp 	s = splbio();
13700a6a3c6SPoul-Henning Kamp 
13800a6a3c6SPoul-Henning Kamp 	bufqdisksort(&sc->buf_queue, bp);
13900a6a3c6SPoul-Henning Kamp 
14000a6a3c6SPoul-Henning Kamp 	if (sc->busy) {
14100a6a3c6SPoul-Henning Kamp 		splx(s);
14200a6a3c6SPoul-Henning Kamp 		return;
14300a6a3c6SPoul-Henning Kamp 	}
14400a6a3c6SPoul-Henning Kamp 
14500a6a3c6SPoul-Henning Kamp 	sc->busy++;
14600a6a3c6SPoul-Henning Kamp 
14700a6a3c6SPoul-Henning Kamp 	while (1) {
14800a6a3c6SPoul-Henning Kamp 		bp = bufq_first(&sc->buf_queue);
14900a6a3c6SPoul-Henning Kamp 		if (bp)
15000a6a3c6SPoul-Henning Kamp 			bufq_remove(&sc->buf_queue, bp);
15100a6a3c6SPoul-Henning Kamp 		splx(s);
15200a6a3c6SPoul-Henning Kamp 		if (!bp)
15300a6a3c6SPoul-Henning Kamp 			break;
15400a6a3c6SPoul-Henning Kamp 
15500a6a3c6SPoul-Henning Kamp 		devstat_start_transaction(&sc->stats);
15600a6a3c6SPoul-Henning Kamp 
15700a6a3c6SPoul-Henning Kamp 		if (bp->b_flags & B_FREEBUF)
15800a6a3c6SPoul-Henning Kamp 			dop = DEVSTAT_NO_DATA;
15900a6a3c6SPoul-Henning Kamp 		else if (bp->b_flags & B_READ)
16000a6a3c6SPoul-Henning Kamp 			dop = DEVSTAT_READ;
16100a6a3c6SPoul-Henning Kamp 		else
16200a6a3c6SPoul-Henning Kamp 			dop = DEVSTAT_WRITE;
16300a6a3c6SPoul-Henning Kamp 
16400a6a3c6SPoul-Henning Kamp 		nsec = bp->b_bcount / DEV_BSIZE;
16500a6a3c6SPoul-Henning Kamp 		secno = bp->b_pblkno;
16600a6a3c6SPoul-Henning Kamp 		dst = bp->b_data;
16700a6a3c6SPoul-Henning Kamp 		while (nsec--) {
16800a6a3c6SPoul-Henning Kamp 
16900a6a3c6SPoul-Henning Kamp 			if (secno < sc->nsecp) {
17000a6a3c6SPoul-Henning Kamp 				secpp = &sc->secp[secno];
17100a6a3c6SPoul-Henning Kamp 				if ((u_int)secpp > 255) {
17200a6a3c6SPoul-Henning Kamp 					secp = *secpp;
17300a6a3c6SPoul-Henning Kamp 					secval = 0;
17400a6a3c6SPoul-Henning Kamp 				} else {
17500a6a3c6SPoul-Henning Kamp 					secp = 0;
17600a6a3c6SPoul-Henning Kamp 					secval = (u_int) secpp;
17700a6a3c6SPoul-Henning Kamp 				}
17800a6a3c6SPoul-Henning Kamp 			} else {
17900a6a3c6SPoul-Henning Kamp 				secpp = 0;
18000a6a3c6SPoul-Henning Kamp 				secp = 0;
18100a6a3c6SPoul-Henning Kamp 				secval = 0;
18200a6a3c6SPoul-Henning Kamp 			}
18300a6a3c6SPoul-Henning Kamp 
18400a6a3c6SPoul-Henning Kamp 			if (bp->b_flags & B_FREEBUF) {
18500a6a3c6SPoul-Henning Kamp 				if (secpp) {
18600a6a3c6SPoul-Henning Kamp 					if (secp)
18700a6a3c6SPoul-Henning Kamp 						FREE(secp, M_MDSECT);
18800a6a3c6SPoul-Henning Kamp 					*secpp = 0;
18900a6a3c6SPoul-Henning Kamp 				}
19000a6a3c6SPoul-Henning Kamp 			} else if (bp->b_flags & B_READ) {
19100a6a3c6SPoul-Henning Kamp 				if (secp) {
19200a6a3c6SPoul-Henning Kamp 					bcopy(secp, dst, DEV_BSIZE);
19300a6a3c6SPoul-Henning Kamp 				} else if (secval) {
19400a6a3c6SPoul-Henning Kamp 					for (i = 0; i < DEV_BSIZE; i++)
19500a6a3c6SPoul-Henning Kamp 						dst[i] = secval;
19600a6a3c6SPoul-Henning Kamp 				} else {
19700a6a3c6SPoul-Henning Kamp 					bzero(dst, DEV_BSIZE);
19800a6a3c6SPoul-Henning Kamp 				}
19900a6a3c6SPoul-Henning Kamp 			} else {
20000a6a3c6SPoul-Henning Kamp 				uc = dst[0];
20100a6a3c6SPoul-Henning Kamp 				for (i = 1; i < DEV_BSIZE; i++)
20200a6a3c6SPoul-Henning Kamp 					if (dst[i] != uc)
20300a6a3c6SPoul-Henning Kamp 						break;
20400a6a3c6SPoul-Henning Kamp 				if (i == DEV_BSIZE && !uc) {
20500a6a3c6SPoul-Henning Kamp 					if (secp)
20600a6a3c6SPoul-Henning Kamp 						FREE(secp, M_MDSECT);
20700a6a3c6SPoul-Henning Kamp 					if (secpp)
20800a6a3c6SPoul-Henning Kamp 						*secpp = (u_char *)uc;
20900a6a3c6SPoul-Henning Kamp 				} else {
21000a6a3c6SPoul-Henning Kamp 					if (!secpp) {
21100a6a3c6SPoul-Henning Kamp 						MALLOC(secpp, u_char **, (secno + nsec + 1) * sizeof(u_char *), M_MD, M_WAITOK);
21200a6a3c6SPoul-Henning Kamp 						bzero(secpp, (secno + nsec + 1) * sizeof(u_char *));
21300a6a3c6SPoul-Henning Kamp 						bcopy(sc->secp, secpp, sc->nsecp * sizeof(u_char *));
21400a6a3c6SPoul-Henning Kamp 						FREE(sc->secp, M_MD);
21500a6a3c6SPoul-Henning Kamp 						sc->secp = secpp;
21600a6a3c6SPoul-Henning Kamp 						sc->nsecp = secno + nsec + 1;
21700a6a3c6SPoul-Henning Kamp 						secpp = &sc->secp[secno];
21800a6a3c6SPoul-Henning Kamp 					}
21900a6a3c6SPoul-Henning Kamp 					if (i == DEV_BSIZE) {
22000a6a3c6SPoul-Henning Kamp 						if (secp)
22100a6a3c6SPoul-Henning Kamp 							FREE(secp, M_MDSECT);
22200a6a3c6SPoul-Henning Kamp 						*secpp = (u_char *)uc;
22300a6a3c6SPoul-Henning Kamp 					} else {
22400a6a3c6SPoul-Henning Kamp 						if (!secp)
22500a6a3c6SPoul-Henning Kamp 							MALLOC(secp, u_char *, DEV_BSIZE, M_MDSECT, M_WAITOK);
22600a6a3c6SPoul-Henning Kamp 						bcopy(dst, secp, DEV_BSIZE);
22700a6a3c6SPoul-Henning Kamp 
22800a6a3c6SPoul-Henning Kamp 						*secpp = secp;
22900a6a3c6SPoul-Henning Kamp 					}
23000a6a3c6SPoul-Henning Kamp 				}
23100a6a3c6SPoul-Henning Kamp 			}
23200a6a3c6SPoul-Henning Kamp 			secno++;
23300a6a3c6SPoul-Henning Kamp 			dst += DEV_BSIZE;
23400a6a3c6SPoul-Henning Kamp 		}
23500a6a3c6SPoul-Henning Kamp 
23600a6a3c6SPoul-Henning Kamp 		bp->b_resid = 0;
23700a6a3c6SPoul-Henning Kamp 		biodone(bp);
23800a6a3c6SPoul-Henning Kamp 		devstat_end_transaction(&sc->stats, bp->b_bcount,
23900a6a3c6SPoul-Henning Kamp 		    DEVSTAT_TAG_NONE, dop);
24000a6a3c6SPoul-Henning Kamp 
24100a6a3c6SPoul-Henning Kamp 		s = splbio();
24200a6a3c6SPoul-Henning Kamp 	}
24300a6a3c6SPoul-Henning Kamp 	sc->busy = 0;
24400a6a3c6SPoul-Henning Kamp 	return;
24500a6a3c6SPoul-Henning Kamp }
24600a6a3c6SPoul-Henning Kamp 
24700a6a3c6SPoul-Henning Kamp static dev_t
24800a6a3c6SPoul-Henning Kamp mdcreate(void)
24900a6a3c6SPoul-Henning Kamp {
25000a6a3c6SPoul-Henning Kamp 	struct md_s *sc;
25100a6a3c6SPoul-Henning Kamp 
25200a6a3c6SPoul-Henning Kamp 	MALLOC(sc, struct md_s *,sizeof(*sc), M_MD, M_WAITOK);
25300a6a3c6SPoul-Henning Kamp 	bzero(sc, sizeof(*sc));
25400a6a3c6SPoul-Henning Kamp 	sc->unit = mdunits++;
25500a6a3c6SPoul-Henning Kamp 
25600a6a3c6SPoul-Henning Kamp 	bufq_init(&sc->buf_queue);
25700a6a3c6SPoul-Henning Kamp 
25800a6a3c6SPoul-Henning Kamp 	devstat_add_entry(&sc->stats, "md", sc->unit, DEV_BSIZE,
25900a6a3c6SPoul-Henning Kamp 		DEVSTAT_NO_ORDERED_TAGS,
26000a6a3c6SPoul-Henning Kamp 		DEVSTAT_TYPE_DIRECT | DEVSTAT_TYPE_IF_OTHER, 0x190);
26100a6a3c6SPoul-Henning Kamp 
26200a6a3c6SPoul-Henning Kamp 	sc->dev = disk_create(sc->unit, &sc->disk, 0,
26300a6a3c6SPoul-Henning Kamp 	    &md_cdevsw, &mddisk_cdevsw);
26400a6a3c6SPoul-Henning Kamp 
26500a6a3c6SPoul-Henning Kamp 	sc->dev->si_drv1 = sc;
26600a6a3c6SPoul-Henning Kamp 	sc->nsect = 10000 * 2;	/* for now */
26700a6a3c6SPoul-Henning Kamp 	MALLOC(sc->secp, u_char **, sizeof(u_char *), M_MD, M_WAITOK);
26800a6a3c6SPoul-Henning Kamp 	bzero(sc->secp, sizeof(u_char *));
26900a6a3c6SPoul-Henning Kamp 	sc->nsecp = 1;
27000a6a3c6SPoul-Henning Kamp 
27100a6a3c6SPoul-Henning Kamp 	return (0);
27200a6a3c6SPoul-Henning Kamp }
27300a6a3c6SPoul-Henning Kamp 
27400a6a3c6SPoul-Henning Kamp static void
27500a6a3c6SPoul-Henning Kamp md_drvinit(void *unused)
27600a6a3c6SPoul-Henning Kamp {
27700a6a3c6SPoul-Henning Kamp 
27800a6a3c6SPoul-Henning Kamp 	mdcreate();
27900a6a3c6SPoul-Henning Kamp }
28000a6a3c6SPoul-Henning Kamp 
28100a6a3c6SPoul-Henning Kamp SYSINIT(ptcdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR, md_drvinit,NULL)
28200a6a3c6SPoul-Henning Kamp 
283