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