xref: /freebsd/sys/dev/md/md.c (revision 9811e1f1a10f4fc6801ec5848aaa1ef4c286dd9b)
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 <sys/param.h>
14 #include <sys/systm.h>
15 #include <sys/buf.h>
16 #include <sys/conf.h>
17 #include <sys/devicestat.h>
18 #include <sys/disk.h>
19 #include <sys/kernel.h>
20 #include <sys/malloc.h>
21 #include <sys/module.h>
22 #include <sys/sysctl.h>
23 
24 #ifndef MDNSECT
25 #define MDNSECT (10000 * 2)
26 #endif
27 
28 MALLOC_DEFINE(M_MD, "MD disk", "Memory Disk");
29 MALLOC_DEFINE(M_MDSECT, "MD sectors", "Memory Disk Sectors");
30 
31 static int md_debug = 0;
32 SYSCTL_INT(_debug, OID_AUTO, mddebug, CTLFLAG_RW, &md_debug, 0, "");
33 
34 #define CDEV_MAJOR	95
35 #define BDEV_MAJOR	22
36 
37 static d_strategy_t mdstrategy;
38 static d_open_t mdopen;
39 static d_ioctl_t mdioctl;
40 
41 static struct cdevsw md_cdevsw = {
42         /* open */      mdopen,
43         /* close */     nullclose,
44         /* read */      physread,
45         /* write */     physwrite,
46         /* ioctl */     mdioctl,
47         /* stop */      nostop,
48         /* reset */     noreset,
49         /* devtotty */  nodevtotty,
50         /* poll */      nopoll,
51         /* mmap */      nommap,
52         /* strategy */  mdstrategy,
53         /* name */      "md",
54         /* parms */     noparms,
55         /* maj */       CDEV_MAJOR,
56         /* dump */      nodump,
57         /* psize */     nopsize,
58         /* flags */     D_DISK | D_CANFREE,
59         /* maxio */     0,
60         /* bmaj */      BDEV_MAJOR
61 };
62 static struct cdevsw mddisk_cdevsw;
63 
64 struct md_s {
65 	int unit;
66 	struct devstat stats;
67 	struct buf_queue_head buf_queue;
68 	struct disk disk;
69 	dev_t dev;
70 	unsigned nsect;
71 	unsigned nsecp;
72 	u_char **secp;
73 
74 	int busy;
75 };
76 
77 static int mdunits;
78 
79 static int
80 mdopen(dev_t dev, int flag, int fmt, struct proc *p)
81 {
82 	struct md_s *sc;
83 	struct disklabel *dl;
84 
85 	if (md_debug)
86 		printf("mdopen(%s %x %x %p)\n",
87 			devtoname(dev), flag, fmt, p);
88 
89 	sc = dev->si_drv1;
90 
91 	dl = &sc->disk.d_label;
92 	bzero(dl, sizeof(*dl));
93 	dl->d_secsize = DEV_BSIZE;
94 	dl->d_nsectors = 1024;
95 	dl->d_ntracks = 1;
96 	dl->d_secpercyl = dl->d_nsectors + dl->d_ntracks;
97 	dl->d_secperunit = sc->nsect;
98 	dl->d_ncylinders = dl->d_secperunit / dl->d_secpercyl;
99 	return (0);
100 }
101 
102 static int
103 mdioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p)
104 {
105 
106 	if (md_debug)
107 		printf("mdioctl(%s %lx %p %x %p)\n",
108 			devtoname(dev), cmd, addr, flags, p);
109 
110 	return (ENOIOCTL);
111 }
112 
113 static void
114 mdstrategy(struct buf *bp)
115 {
116 	int s, i;
117 	struct md_s *sc;
118 	devstat_trans_flags dop;
119 	u_char *secp, **secpp, *dst;
120 	unsigned secno, nsec, secval, uc;
121 
122 	if (md_debug > 1)
123 		printf("mdstrategy(%p) %s %lx, %d, %ld, %p)\n",
124 		    bp, devtoname(bp->b_dev), bp->b_flags, bp->b_blkno,
125 		    bp->b_bcount / DEV_BSIZE, bp->b_data);
126 
127 	sc = bp->b_dev->si_drv1;
128 
129 	s = splbio();
130 
131 	bufqdisksort(&sc->buf_queue, bp);
132 
133 	if (sc->busy) {
134 		splx(s);
135 		return;
136 	}
137 
138 	sc->busy++;
139 
140 	while (1) {
141 		bp = bufq_first(&sc->buf_queue);
142 		if (bp)
143 			bufq_remove(&sc->buf_queue, bp);
144 		splx(s);
145 		if (!bp)
146 			break;
147 
148 		devstat_start_transaction(&sc->stats);
149 
150 		if (bp->b_flags & B_FREEBUF)
151 			dop = DEVSTAT_NO_DATA;
152 		else if (bp->b_flags & B_READ)
153 			dop = DEVSTAT_READ;
154 		else
155 			dop = DEVSTAT_WRITE;
156 
157 		nsec = bp->b_bcount / DEV_BSIZE;
158 		secno = bp->b_pblkno;
159 		dst = bp->b_data;
160 		while (nsec--) {
161 
162 			if (secno < sc->nsecp) {
163 				secpp = &sc->secp[secno];
164 				if ((u_int)*secpp > 255) {
165 					secp = *secpp;
166 					secval = 0;
167 				} else {
168 					secp = 0;
169 					secval = (u_int) *secpp;
170 				}
171 			} else {
172 				secpp = 0;
173 				secp = 0;
174 				secval = 0;
175 			}
176 			if (md_debug > 2)
177 				printf("%lx %p %p %d\n", bp->b_flags, secpp, secp, secval);
178 
179 			if (bp->b_flags & B_FREEBUF) {
180 				if (secpp) {
181 					if (secp)
182 						FREE(secp, M_MDSECT);
183 					*secpp = 0;
184 				}
185 			} else if (bp->b_flags & B_READ) {
186 				if (secp) {
187 					bcopy(secp, dst, DEV_BSIZE);
188 				} else if (secval) {
189 					for (i = 0; i < DEV_BSIZE; i++)
190 						dst[i] = secval;
191 				} else {
192 					bzero(dst, DEV_BSIZE);
193 				}
194 			} else {
195 				uc = dst[0];
196 				for (i = 1; i < DEV_BSIZE; i++)
197 					if (dst[i] != uc)
198 						break;
199 				if (i == DEV_BSIZE && !uc) {
200 					if (secp)
201 						FREE(secp, M_MDSECT);
202 					if (secpp)
203 						*secpp = (u_char *)uc;
204 				} else {
205 					if (!secpp) {
206 						MALLOC(secpp, u_char **, (secno + nsec + 1) * sizeof(u_char *), M_MD, M_WAITOK);
207 						bzero(secpp, (secno + nsec + 1) * sizeof(u_char *));
208 						bcopy(sc->secp, secpp, sc->nsecp * sizeof(u_char *));
209 						FREE(sc->secp, M_MD);
210 						sc->secp = secpp;
211 						sc->nsecp = secno + nsec + 1;
212 						secpp = &sc->secp[secno];
213 					}
214 					if (i == DEV_BSIZE) {
215 						if (secp)
216 							FREE(secp, M_MDSECT);
217 						*secpp = (u_char *)uc;
218 					} else {
219 						if (!secp)
220 							MALLOC(secp, u_char *, DEV_BSIZE, M_MDSECT, M_WAITOK);
221 						bcopy(dst, secp, DEV_BSIZE);
222 
223 						*secpp = secp;
224 					}
225 				}
226 			}
227 			secno++;
228 			dst += DEV_BSIZE;
229 		}
230 
231 		bp->b_resid = 0;
232 		devstat_end_transaction_buf(&sc->stats, bp);
233 		biodone(bp);
234 		s = splbio();
235 	}
236 	sc->busy = 0;
237 	return;
238 }
239 
240 static dev_t
241 mdcreate(void)
242 {
243 	struct md_s *sc;
244 
245 	MALLOC(sc, struct md_s *,sizeof(*sc), M_MD, M_WAITOK);
246 	bzero(sc, sizeof(*sc));
247 	sc->unit = mdunits++;
248 
249 	bufq_init(&sc->buf_queue);
250 
251 	devstat_add_entry(&sc->stats, "md", sc->unit, DEV_BSIZE,
252 		DEVSTAT_NO_ORDERED_TAGS,
253 		DEVSTAT_TYPE_DIRECT | DEVSTAT_TYPE_IF_OTHER, 0x190);
254 
255 	sc->dev = disk_create(sc->unit, &sc->disk, 0,
256 	    &md_cdevsw, &mddisk_cdevsw);
257 
258 	sc->dev->si_drv1 = sc;
259 	sc->nsect = MDNSECT;	/* for now */
260 	MALLOC(sc->secp, u_char **, sizeof(u_char *), M_MD, M_WAITOK);
261 	bzero(sc->secp, sizeof(u_char *));
262 	sc->nsecp = 1;
263 
264 	return (0);
265 }
266 
267 static void
268 md_drvinit(void *unused)
269 {
270 
271 	mdcreate();
272 }
273 
274 SYSINIT(ptcdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR, md_drvinit,NULL)
275 
276