xref: /freebsd/sys/dev/md/md.c (revision 33edfabe577950bf0f9a6dabb20d0254d2985b3a)
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 
3533edfabeSPoul-Henning Kamp #ifndef MDNSECT
3633edfabeSPoul-Henning Kamp #define MDNSECT (10000 * 2)
3733edfabeSPoul-Henning Kamp #endif
3833edfabeSPoul-Henning Kamp 
3900a6a3c6SPoul-Henning Kamp MALLOC_DEFINE(M_MD, "MD disk", "Memory Disk");
4000a6a3c6SPoul-Henning Kamp MALLOC_DEFINE(M_MDSECT, "MD sectors", "Memory Disk Sectors");
4100a6a3c6SPoul-Henning Kamp 
4200a6a3c6SPoul-Henning Kamp static int md_debug = 0;
4300a6a3c6SPoul-Henning Kamp SYSCTL_INT(_debug, OID_AUTO, mddebug, CTLFLAG_RW, &md_debug, 0, "");
4400a6a3c6SPoul-Henning Kamp 
4500a6a3c6SPoul-Henning Kamp #define CDEV_MAJOR	95
4600a6a3c6SPoul-Henning Kamp #define BDEV_MAJOR	22
4700a6a3c6SPoul-Henning Kamp 
4800a6a3c6SPoul-Henning Kamp static d_strategy_t mdstrategy;
4900a6a3c6SPoul-Henning Kamp static d_open_t mdopen;
5000a6a3c6SPoul-Henning Kamp static d_ioctl_t mdioctl;
5100a6a3c6SPoul-Henning Kamp 
5200a6a3c6SPoul-Henning Kamp static struct cdevsw md_cdevsw = {
5300a6a3c6SPoul-Henning Kamp         /* open */      mdopen,
5400a6a3c6SPoul-Henning Kamp         /* close */     nullclose,
5500a6a3c6SPoul-Henning Kamp         /* read */      physread,
5600a6a3c6SPoul-Henning Kamp         /* write */     physwrite,
5700a6a3c6SPoul-Henning Kamp         /* ioctl */     mdioctl,
5800a6a3c6SPoul-Henning Kamp         /* stop */      nostop,
5900a6a3c6SPoul-Henning Kamp         /* reset */     noreset,
6000a6a3c6SPoul-Henning Kamp         /* devtotty */  nodevtotty,
6100a6a3c6SPoul-Henning Kamp         /* poll */      nopoll,
6200a6a3c6SPoul-Henning Kamp         /* mmap */      nommap,
6300a6a3c6SPoul-Henning Kamp         /* strategy */  mdstrategy,
6400a6a3c6SPoul-Henning Kamp         /* name */      "md",
6500a6a3c6SPoul-Henning Kamp         /* parms */     noparms,
6600a6a3c6SPoul-Henning Kamp         /* maj */       CDEV_MAJOR,
6700a6a3c6SPoul-Henning Kamp         /* dump */      nodump,
6800a6a3c6SPoul-Henning Kamp         /* psize */     nopsize,
6900a6a3c6SPoul-Henning Kamp         /* flags */     D_DISK | D_CANFREE,
7000a6a3c6SPoul-Henning Kamp         /* maxio */     0,
7100a6a3c6SPoul-Henning Kamp         /* bmaj */      BDEV_MAJOR
7200a6a3c6SPoul-Henning Kamp };
7300a6a3c6SPoul-Henning Kamp static struct cdevsw mddisk_cdevsw;
7400a6a3c6SPoul-Henning Kamp 
7500a6a3c6SPoul-Henning Kamp struct md_s {
7600a6a3c6SPoul-Henning Kamp 	int unit;
7700a6a3c6SPoul-Henning Kamp 	struct devstat stats;
7800a6a3c6SPoul-Henning Kamp 	struct buf_queue_head buf_queue;
7900a6a3c6SPoul-Henning Kamp 	struct disk disk;
8000a6a3c6SPoul-Henning Kamp 	dev_t dev;
8100a6a3c6SPoul-Henning Kamp 	unsigned nsect;
8200a6a3c6SPoul-Henning Kamp 	unsigned nsecp;
8300a6a3c6SPoul-Henning Kamp 	u_char **secp;
8400a6a3c6SPoul-Henning Kamp 
8500a6a3c6SPoul-Henning Kamp 	int busy;
8600a6a3c6SPoul-Henning Kamp };
8700a6a3c6SPoul-Henning Kamp 
8800a6a3c6SPoul-Henning Kamp static int mdunits;
8900a6a3c6SPoul-Henning Kamp 
9000a6a3c6SPoul-Henning Kamp static int
9100a6a3c6SPoul-Henning Kamp mdopen(dev_t dev, int flag, int fmt, struct proc *p)
9200a6a3c6SPoul-Henning Kamp {
9300a6a3c6SPoul-Henning Kamp 	struct md_s *sc;
9400a6a3c6SPoul-Henning Kamp 	struct disklabel *dl;
9500a6a3c6SPoul-Henning Kamp 
9600a6a3c6SPoul-Henning Kamp 	if (md_debug)
9700a6a3c6SPoul-Henning Kamp 		printf("mdopen(%s %x %x %p)\n",
9800a6a3c6SPoul-Henning Kamp 			devtoname(dev), flag, fmt, p);
9900a6a3c6SPoul-Henning Kamp 
10000a6a3c6SPoul-Henning Kamp 	sc = dev->si_drv1;
10100a6a3c6SPoul-Henning Kamp 
10200a6a3c6SPoul-Henning Kamp 	dl = &sc->disk.d_label;
10300a6a3c6SPoul-Henning Kamp 	bzero(dl, sizeof(*dl));
10400a6a3c6SPoul-Henning Kamp 	dl->d_secsize = DEV_BSIZE;
10500a6a3c6SPoul-Henning Kamp 	dl->d_nsectors = 1024;
10600a6a3c6SPoul-Henning Kamp 	dl->d_ntracks = 1;
10700a6a3c6SPoul-Henning Kamp 	dl->d_secpercyl = dl->d_nsectors + dl->d_ntracks;
10800a6a3c6SPoul-Henning Kamp 	dl->d_secperunit = sc->nsect;
10900a6a3c6SPoul-Henning Kamp 	dl->d_ncylinders = dl->d_secperunit / dl->d_secpercyl;
11000a6a3c6SPoul-Henning Kamp 	return (0);
11100a6a3c6SPoul-Henning Kamp }
11200a6a3c6SPoul-Henning Kamp 
11300a6a3c6SPoul-Henning Kamp static int
11400a6a3c6SPoul-Henning Kamp mdioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p)
11500a6a3c6SPoul-Henning Kamp {
11600a6a3c6SPoul-Henning Kamp 
11700a6a3c6SPoul-Henning Kamp 	if (md_debug)
11800a6a3c6SPoul-Henning Kamp 		printf("mdioctl(%s %lx %p %x %p)\n",
11900a6a3c6SPoul-Henning Kamp 			devtoname(dev), cmd, addr, flags, p);
12000a6a3c6SPoul-Henning Kamp 
12100a6a3c6SPoul-Henning Kamp 	return (ENOIOCTL);
12200a6a3c6SPoul-Henning Kamp }
12300a6a3c6SPoul-Henning Kamp 
12400a6a3c6SPoul-Henning Kamp static void
12500a6a3c6SPoul-Henning Kamp mdstrategy(struct buf *bp)
12600a6a3c6SPoul-Henning Kamp {
12700a6a3c6SPoul-Henning Kamp 	int s, i;
12800a6a3c6SPoul-Henning Kamp 	struct md_s *sc;
12900a6a3c6SPoul-Henning Kamp 	devstat_trans_flags dop;
13000a6a3c6SPoul-Henning Kamp 	u_char *secp, **secpp, *dst;
13100a6a3c6SPoul-Henning Kamp 	unsigned secno, nsec, secval, uc;
13200a6a3c6SPoul-Henning Kamp 
13300a6a3c6SPoul-Henning Kamp 	if (md_debug > 1)
13400a6a3c6SPoul-Henning Kamp 		printf("mdstrategy(%p) %s %lx, %d, %ld, %p)\n",
13500a6a3c6SPoul-Henning Kamp 		    bp, devtoname(bp->b_dev), bp->b_flags, bp->b_blkno,
13600a6a3c6SPoul-Henning Kamp 		    bp->b_bcount / DEV_BSIZE, bp->b_data);
13700a6a3c6SPoul-Henning Kamp 
13800a6a3c6SPoul-Henning Kamp 	sc = bp->b_dev->si_drv1;
13900a6a3c6SPoul-Henning Kamp 
14000a6a3c6SPoul-Henning Kamp 	s = splbio();
14100a6a3c6SPoul-Henning Kamp 
14200a6a3c6SPoul-Henning Kamp 	bufqdisksort(&sc->buf_queue, bp);
14300a6a3c6SPoul-Henning Kamp 
14400a6a3c6SPoul-Henning Kamp 	if (sc->busy) {
14500a6a3c6SPoul-Henning Kamp 		splx(s);
14600a6a3c6SPoul-Henning Kamp 		return;
14700a6a3c6SPoul-Henning Kamp 	}
14800a6a3c6SPoul-Henning Kamp 
14900a6a3c6SPoul-Henning Kamp 	sc->busy++;
15000a6a3c6SPoul-Henning Kamp 
15100a6a3c6SPoul-Henning Kamp 	while (1) {
15200a6a3c6SPoul-Henning Kamp 		bp = bufq_first(&sc->buf_queue);
15300a6a3c6SPoul-Henning Kamp 		if (bp)
15400a6a3c6SPoul-Henning Kamp 			bufq_remove(&sc->buf_queue, bp);
15500a6a3c6SPoul-Henning Kamp 		splx(s);
15600a6a3c6SPoul-Henning Kamp 		if (!bp)
15700a6a3c6SPoul-Henning Kamp 			break;
15800a6a3c6SPoul-Henning Kamp 
15900a6a3c6SPoul-Henning Kamp 		devstat_start_transaction(&sc->stats);
16000a6a3c6SPoul-Henning Kamp 
16100a6a3c6SPoul-Henning Kamp 		if (bp->b_flags & B_FREEBUF)
16200a6a3c6SPoul-Henning Kamp 			dop = DEVSTAT_NO_DATA;
16300a6a3c6SPoul-Henning Kamp 		else if (bp->b_flags & B_READ)
16400a6a3c6SPoul-Henning Kamp 			dop = DEVSTAT_READ;
16500a6a3c6SPoul-Henning Kamp 		else
16600a6a3c6SPoul-Henning Kamp 			dop = DEVSTAT_WRITE;
16700a6a3c6SPoul-Henning Kamp 
16800a6a3c6SPoul-Henning Kamp 		nsec = bp->b_bcount / DEV_BSIZE;
16900a6a3c6SPoul-Henning Kamp 		secno = bp->b_pblkno;
17000a6a3c6SPoul-Henning Kamp 		dst = bp->b_data;
17100a6a3c6SPoul-Henning Kamp 		while (nsec--) {
17200a6a3c6SPoul-Henning Kamp 
17300a6a3c6SPoul-Henning Kamp 			if (secno < sc->nsecp) {
17400a6a3c6SPoul-Henning Kamp 				secpp = &sc->secp[secno];
17533edfabeSPoul-Henning Kamp 				if ((u_int)*secpp > 255) {
17600a6a3c6SPoul-Henning Kamp 					secp = *secpp;
17700a6a3c6SPoul-Henning Kamp 					secval = 0;
17800a6a3c6SPoul-Henning Kamp 				} else {
17900a6a3c6SPoul-Henning Kamp 					secp = 0;
18033edfabeSPoul-Henning Kamp 					secval = (u_int) *secpp;
18100a6a3c6SPoul-Henning Kamp 				}
18200a6a3c6SPoul-Henning Kamp 			} else {
18300a6a3c6SPoul-Henning Kamp 				secpp = 0;
18400a6a3c6SPoul-Henning Kamp 				secp = 0;
18500a6a3c6SPoul-Henning Kamp 				secval = 0;
18600a6a3c6SPoul-Henning Kamp 			}
18733edfabeSPoul-Henning Kamp 			if (md_debug > 2)
18833edfabeSPoul-Henning Kamp 				printf("%x %p %p %d\n", bp->b_flags, secpp, secp, secval);
18900a6a3c6SPoul-Henning Kamp 
19000a6a3c6SPoul-Henning Kamp 			if (bp->b_flags & B_FREEBUF) {
19100a6a3c6SPoul-Henning Kamp 				if (secpp) {
19200a6a3c6SPoul-Henning Kamp 					if (secp)
19300a6a3c6SPoul-Henning Kamp 						FREE(secp, M_MDSECT);
19400a6a3c6SPoul-Henning Kamp 					*secpp = 0;
19500a6a3c6SPoul-Henning Kamp 				}
19600a6a3c6SPoul-Henning Kamp 			} else if (bp->b_flags & B_READ) {
19700a6a3c6SPoul-Henning Kamp 				if (secp) {
19800a6a3c6SPoul-Henning Kamp 					bcopy(secp, dst, DEV_BSIZE);
19900a6a3c6SPoul-Henning Kamp 				} else if (secval) {
20000a6a3c6SPoul-Henning Kamp 					for (i = 0; i < DEV_BSIZE; i++)
20100a6a3c6SPoul-Henning Kamp 						dst[i] = secval;
20200a6a3c6SPoul-Henning Kamp 				} else {
20300a6a3c6SPoul-Henning Kamp 					bzero(dst, DEV_BSIZE);
20400a6a3c6SPoul-Henning Kamp 				}
20500a6a3c6SPoul-Henning Kamp 			} else {
20600a6a3c6SPoul-Henning Kamp 				uc = dst[0];
20700a6a3c6SPoul-Henning Kamp 				for (i = 1; i < DEV_BSIZE; i++)
20800a6a3c6SPoul-Henning Kamp 					if (dst[i] != uc)
20900a6a3c6SPoul-Henning Kamp 						break;
21000a6a3c6SPoul-Henning Kamp 				if (i == DEV_BSIZE && !uc) {
21100a6a3c6SPoul-Henning Kamp 					if (secp)
21200a6a3c6SPoul-Henning Kamp 						FREE(secp, M_MDSECT);
21300a6a3c6SPoul-Henning Kamp 					if (secpp)
21400a6a3c6SPoul-Henning Kamp 						*secpp = (u_char *)uc;
21500a6a3c6SPoul-Henning Kamp 				} else {
21600a6a3c6SPoul-Henning Kamp 					if (!secpp) {
21700a6a3c6SPoul-Henning Kamp 						MALLOC(secpp, u_char **, (secno + nsec + 1) * sizeof(u_char *), M_MD, M_WAITOK);
21800a6a3c6SPoul-Henning Kamp 						bzero(secpp, (secno + nsec + 1) * sizeof(u_char *));
21900a6a3c6SPoul-Henning Kamp 						bcopy(sc->secp, secpp, sc->nsecp * sizeof(u_char *));
22000a6a3c6SPoul-Henning Kamp 						FREE(sc->secp, M_MD);
22100a6a3c6SPoul-Henning Kamp 						sc->secp = secpp;
22200a6a3c6SPoul-Henning Kamp 						sc->nsecp = secno + nsec + 1;
22300a6a3c6SPoul-Henning Kamp 						secpp = &sc->secp[secno];
22400a6a3c6SPoul-Henning Kamp 					}
22500a6a3c6SPoul-Henning Kamp 					if (i == DEV_BSIZE) {
22600a6a3c6SPoul-Henning Kamp 						if (secp)
22700a6a3c6SPoul-Henning Kamp 							FREE(secp, M_MDSECT);
22800a6a3c6SPoul-Henning Kamp 						*secpp = (u_char *)uc;
22900a6a3c6SPoul-Henning Kamp 					} else {
23000a6a3c6SPoul-Henning Kamp 						if (!secp)
23100a6a3c6SPoul-Henning Kamp 							MALLOC(secp, u_char *, DEV_BSIZE, M_MDSECT, M_WAITOK);
23200a6a3c6SPoul-Henning Kamp 						bcopy(dst, secp, DEV_BSIZE);
23300a6a3c6SPoul-Henning Kamp 
23400a6a3c6SPoul-Henning Kamp 						*secpp = secp;
23500a6a3c6SPoul-Henning Kamp 					}
23600a6a3c6SPoul-Henning Kamp 				}
23700a6a3c6SPoul-Henning Kamp 			}
23800a6a3c6SPoul-Henning Kamp 			secno++;
23900a6a3c6SPoul-Henning Kamp 			dst += DEV_BSIZE;
24000a6a3c6SPoul-Henning Kamp 		}
24100a6a3c6SPoul-Henning Kamp 
24200a6a3c6SPoul-Henning Kamp 		bp->b_resid = 0;
24333edfabeSPoul-Henning Kamp 		devstat_end_transaction_buf(&sc->stats, bp);
24400a6a3c6SPoul-Henning Kamp 		biodone(bp);
24500a6a3c6SPoul-Henning Kamp 		s = splbio();
24600a6a3c6SPoul-Henning Kamp 	}
24700a6a3c6SPoul-Henning Kamp 	sc->busy = 0;
24800a6a3c6SPoul-Henning Kamp 	return;
24900a6a3c6SPoul-Henning Kamp }
25000a6a3c6SPoul-Henning Kamp 
25100a6a3c6SPoul-Henning Kamp static dev_t
25200a6a3c6SPoul-Henning Kamp mdcreate(void)
25300a6a3c6SPoul-Henning Kamp {
25400a6a3c6SPoul-Henning Kamp 	struct md_s *sc;
25500a6a3c6SPoul-Henning Kamp 
25600a6a3c6SPoul-Henning Kamp 	MALLOC(sc, struct md_s *,sizeof(*sc), M_MD, M_WAITOK);
25700a6a3c6SPoul-Henning Kamp 	bzero(sc, sizeof(*sc));
25800a6a3c6SPoul-Henning Kamp 	sc->unit = mdunits++;
25900a6a3c6SPoul-Henning Kamp 
26000a6a3c6SPoul-Henning Kamp 	bufq_init(&sc->buf_queue);
26100a6a3c6SPoul-Henning Kamp 
26200a6a3c6SPoul-Henning Kamp 	devstat_add_entry(&sc->stats, "md", sc->unit, DEV_BSIZE,
26300a6a3c6SPoul-Henning Kamp 		DEVSTAT_NO_ORDERED_TAGS,
26400a6a3c6SPoul-Henning Kamp 		DEVSTAT_TYPE_DIRECT | DEVSTAT_TYPE_IF_OTHER, 0x190);
26500a6a3c6SPoul-Henning Kamp 
26600a6a3c6SPoul-Henning Kamp 	sc->dev = disk_create(sc->unit, &sc->disk, 0,
26700a6a3c6SPoul-Henning Kamp 	    &md_cdevsw, &mddisk_cdevsw);
26800a6a3c6SPoul-Henning Kamp 
26900a6a3c6SPoul-Henning Kamp 	sc->dev->si_drv1 = sc;
27033edfabeSPoul-Henning Kamp 	sc->nsect = MDNSECT;	/* for now */
27100a6a3c6SPoul-Henning Kamp 	MALLOC(sc->secp, u_char **, sizeof(u_char *), M_MD, M_WAITOK);
27200a6a3c6SPoul-Henning Kamp 	bzero(sc->secp, sizeof(u_char *));
27300a6a3c6SPoul-Henning Kamp 	sc->nsecp = 1;
27400a6a3c6SPoul-Henning Kamp 
27500a6a3c6SPoul-Henning Kamp 	return (0);
27600a6a3c6SPoul-Henning Kamp }
27700a6a3c6SPoul-Henning Kamp 
27800a6a3c6SPoul-Henning Kamp static void
27900a6a3c6SPoul-Henning Kamp md_drvinit(void *unused)
28000a6a3c6SPoul-Henning Kamp {
28100a6a3c6SPoul-Henning Kamp 
28200a6a3c6SPoul-Henning Kamp 	mdcreate();
28300a6a3c6SPoul-Henning Kamp }
28400a6a3c6SPoul-Henning Kamp 
28500a6a3c6SPoul-Henning Kamp SYSINIT(ptcdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR, md_drvinit,NULL)
28600a6a3c6SPoul-Henning Kamp 
287