xref: /freebsd/sys/kern/subr_disk.c (revision 8fa113e5fc65fe6abc757f0089f477a87ee4d185)
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/kernel.h>
16 #include <sys/sysctl.h>
17 #include <sys/bio.h>
18 #include <sys/conf.h>
19 #include <sys/disk.h>
20 #include <sys/malloc.h>
21 #include <sys/sysctl.h>
22 #include <machine/md_var.h>
23 #include <sys/ctype.h>
24 
25 static MALLOC_DEFINE(M_DISK, "disk", "disk data");
26 
27 static d_strategy_t diskstrategy;
28 static d_open_t diskopen;
29 static d_close_t diskclose;
30 static d_ioctl_t diskioctl;
31 static d_psize_t diskpsize;
32 
33 static LIST_HEAD(, disk) disklist = LIST_HEAD_INITIALIZER(&disklist);
34 
35 void disk_dev_synth(dev_t dev);
36 
37 void
38 disk_dev_synth(dev_t dev)
39 {
40 	struct disk *dp;
41 	int u, s, p;
42 	dev_t pdev;
43 
44 	if (dksparebits(dev))
45 		return;
46 	LIST_FOREACH(dp, &disklist, d_list) {
47 		if (major(dev) != dp->d_devsw->d_maj)
48 			continue;
49 		u = dkunit(dev);
50 		p = RAW_PART;
51 		s = WHOLE_DISK_SLICE;
52 		pdev = makedev(dp->d_devsw->d_maj, dkmakeminor(u, s, p));
53 		if (pdev->si_devsw == NULL)
54 			return;		/* Probably a unit we don't have */
55 		s = dkslice(dev);
56 		p = dkpart(dev);
57 		if (s == WHOLE_DISK_SLICE && p == RAW_PART) {
58 			/* XXX: actually should not happen */
59 			dev = make_dev(pdev->si_devsw, dkmakeminor(u, s, p),
60 			    UID_ROOT, GID_OPERATOR, 0640, "%s%d",
61 				dp->d_devsw->d_name, u);
62 			dev_depends(pdev, dev);
63 			return;
64 		}
65 		if (s == COMPATIBILITY_SLICE) {
66 			dev = make_dev(pdev->si_devsw, dkmakeminor(u, s, p),
67 			    UID_ROOT, GID_OPERATOR, 0640, "%s%d%c",
68 				dp->d_devsw->d_name, u, 'a' + p);
69 			dev_depends(pdev, dev);
70 			return;
71 		}
72 		if (p != RAW_PART) {
73 			dev = make_dev(pdev->si_devsw, dkmakeminor(u, s, p),
74 			    UID_ROOT, GID_OPERATOR, 0640, "%s%ds%d%c",
75 				dp->d_devsw->d_name, u, s - BASE_SLICE + 1,
76 				'a' + p);
77 		} else {
78 			dev = make_dev(pdev->si_devsw, dkmakeminor(u, s, p),
79 			    UID_ROOT, GID_OPERATOR, 0640, "%s%ds%d",
80 				dp->d_devsw->d_name, u, s - BASE_SLICE + 1);
81 			make_dev_alias(dev, "%s%ds%dc",
82 			    dp->d_devsw->d_name, u, s - BASE_SLICE + 1);
83 		}
84 		dev_depends(pdev, dev);
85 		return;
86 	}
87 }
88 
89 static void
90 disk_clone(void *arg, char *name, int namelen, dev_t *dev)
91 {
92 	struct disk *dp;
93 	char const *d;
94 	int i, u, s, p;
95 	dev_t pdev;
96 
97 	if (*dev != NODEV)
98 		return;
99 
100 	LIST_FOREACH(dp, &disklist, d_list) {
101 		d = dp->d_devsw->d_name;
102 		i = strlen(d);
103 		if (bcmp(d, name, i) != 0)
104 			continue;
105 		u = 0;
106 		if (!isdigit(name[i]))
107 			continue;
108 		while (isdigit(name[i])) {
109 			u *= 10;
110 			u += name[i++] - '0';
111 		}
112 		if (u > DKMAXUNIT)
113 			continue;
114 		p = RAW_PART;
115 		s = WHOLE_DISK_SLICE;
116 		pdev = makedev(dp->d_devsw->d_maj, dkmakeminor(u, s, p));
117 		if (pdev->si_disk == NULL)
118 			continue;
119 		if (name[i] != '\0') {
120 			if (name[i] == 's') {
121 				s = 0;
122 				i++;
123 				if (!isdigit(name[i]))
124 					continue;
125 				while (isdigit(name[i])) {
126 					s *= 10;
127 					s += name[i++] - '0';
128 				}
129 				s += BASE_SLICE - 1;
130 			} else {
131 				s = COMPATIBILITY_SLICE;
132 			}
133 			if (name[i] == '\0')
134 				;
135 			else if (name[i + 1] != '\0')
136 				return;
137 			else if (name[i] < 'a' || name[i] > 'h')
138 				continue;
139 			else
140 				p = name[i] - 'a';
141 		}
142 
143 		if (s >= BASE_SLICE && p != RAW_PART) {
144 			*dev = make_dev(pdev->si_devsw, dkmakeminor(u, s, p),
145 			    UID_ROOT, GID_OPERATOR, 0640, "%s%ds%d%c",
146 			    pdev->si_devsw->d_name, u, s - BASE_SLICE + 1,
147 			    p + 'a');
148 		} else if (s >= BASE_SLICE) {
149 			*dev = make_dev(pdev->si_devsw, dkmakeminor(u, s, p),
150 			    UID_ROOT, GID_OPERATOR, 0640, "%s%ds%d",
151 			    pdev->si_devsw->d_name, u, s - BASE_SLICE + 1);
152 			make_dev_alias(*dev, "%s%ds%dc",
153 			    pdev->si_devsw->d_name, u, s - BASE_SLICE + 1);
154 		} else {
155 			*dev = make_dev(pdev->si_devsw, dkmakeminor(u, s, p),
156 			    UID_ROOT, GID_OPERATOR, 0640, name);
157 		}
158 		dev_depends(pdev, *dev);
159 		return;
160 	}
161 }
162 
163 static void
164 inherit_raw(dev_t pdev, dev_t dev)
165 {
166 	dev->si_disk = pdev->si_disk;
167 	dev->si_drv1 = pdev->si_drv1;
168 	dev->si_drv2 = pdev->si_drv2;
169 	dev->si_iosize_max = pdev->si_iosize_max;
170 	dev->si_bsize_phys = pdev->si_bsize_phys;
171 	dev->si_bsize_best = pdev->si_bsize_best;
172 }
173 
174 dev_t
175 disk_create(int unit, struct disk *dp, int flags, struct cdevsw *cdevsw, struct cdevsw *proto)
176 {
177 	static int once;
178 	dev_t dev;
179 
180 	if (!once) {
181 		EVENTHANDLER_REGISTER(dev_clone, disk_clone, 0, 1000);
182 		once++;
183 	}
184 
185 	bzero(dp, sizeof(*dp));
186 
187 	if (proto->d_open != diskopen) {
188 		*proto = *cdevsw;
189 		proto->d_open = diskopen;
190 		proto->d_close = diskclose;
191 		proto->d_ioctl = diskioctl;
192 		proto->d_strategy = diskstrategy;
193 		proto->d_psize = diskpsize;
194 	}
195 
196 	if (bootverbose)
197 		printf("Creating DISK %s%d\n", cdevsw->d_name, unit);
198 	dev = make_dev(proto, dkmakeminor(unit, WHOLE_DISK_SLICE, RAW_PART),
199 	    UID_ROOT, GID_OPERATOR, 0640, "%s%d", cdevsw->d_name, unit);
200 
201 	dev->si_disk = dp;
202 	dp->d_dev = dev;
203 	dp->d_dsflags = flags;
204 	dp->d_devsw = cdevsw;
205 	LIST_INSERT_HEAD(&disklist, dp, d_list);
206 
207 	return (dev);
208 }
209 
210 int
211 disk_dumpcheck(dev_t dev, u_int *count, u_int *blkno, u_int *secsize)
212 {
213 	struct disk *dp;
214 	struct disklabel *dl;
215 	u_int boff;
216 
217 	dp = dev->si_disk;
218 	if (!dp)
219 		return (ENXIO);
220 	if (!dp->d_slice)
221 		return (ENXIO);
222 	dl = dsgetlabel(dev, dp->d_slice);
223 	if (!dl)
224 		return (ENXIO);
225 	*count = Maxmem * (PAGE_SIZE / dl->d_secsize);
226 	if (dumplo <= LABELSECTOR ||
227 	    (dumplo + *count > dl->d_partitions[dkpart(dev)].p_size))
228 		return (EINVAL);
229 	boff = dl->d_partitions[dkpart(dev)].p_offset +
230 	    dp->d_slice->dss_slices[dkslice(dev)].ds_offset;
231 	*blkno = boff + dumplo;
232 	*secsize = dl->d_secsize;
233 	return (0);
234 
235 }
236 
237 void
238 disk_invalidate (struct disk *disk)
239 {
240 	if (disk->d_slice)
241 		dsgone(&disk->d_slice);
242 }
243 
244 void
245 disk_destroy(dev_t dev)
246 {
247 	LIST_REMOVE(dev->si_disk, d_list);
248 	bzero(dev->si_disk, sizeof(*dev->si_disk));
249     	dev->si_disk = NULL;
250 	destroy_dev(dev);
251 	return;
252 }
253 
254 struct disk *
255 disk_enumerate(struct disk *disk)
256 {
257 	if (!disk)
258 		return (LIST_FIRST(&disklist));
259 	else
260 		return (LIST_NEXT(disk, d_list));
261 }
262 
263 static int
264 sysctl_disks(SYSCTL_HANDLER_ARGS)
265 {
266 	struct disk *disk;
267 	int error, first;
268 
269 	disk = NULL;
270 	first = 1;
271 
272 	while ((disk = disk_enumerate(disk))) {
273 		if (!first) {
274 			error = SYSCTL_OUT(req, " ", 1);
275 			if (error)
276 				return error;
277 		} else {
278 			first = 0;
279 		}
280 		error = SYSCTL_OUT(req, disk->d_dev->si_name, strlen(disk->d_dev->si_name));
281 		if (error)
282 			return error;
283 	}
284 	error = SYSCTL_OUT(req, "", 1);
285 	return error;
286 }
287 
288 SYSCTL_PROC(_kern, OID_AUTO, disks, CTLTYPE_STRING | CTLFLAG_RD, 0, NULL,
289     sysctl_disks, "A", "names of available disks");
290 
291 /*
292  * The cdevsw functions
293  */
294 
295 static int
296 diskopen(dev_t dev, int oflags, int devtype, struct thread *td)
297 {
298 	dev_t pdev;
299 	struct disk *dp;
300 	int error;
301 
302 	error = 0;
303 	pdev = dkmodpart(dkmodslice(dev, WHOLE_DISK_SLICE), RAW_PART);
304 
305 	dp = pdev->si_disk;
306 	if (!dp)
307 		return (ENXIO);
308 
309 	while (dp->d_flags & DISKFLAG_LOCK) {
310 		dp->d_flags |= DISKFLAG_WANTED;
311 		error = tsleep(dp, PRIBIO | PCATCH, "diskopen", hz);
312 		if (error)
313 			return (error);
314 	}
315 	dp->d_flags |= DISKFLAG_LOCK;
316 
317 	if (!dsisopen(dp->d_slice)) {
318 		if (!pdev->si_iosize_max)
319 			pdev->si_iosize_max = dev->si_iosize_max;
320 		error = dp->d_devsw->d_open(pdev, oflags, devtype, td);
321 	}
322 
323 	/* Inherit properties from the whole/raw dev_t */
324 	inherit_raw(pdev, dev);
325 
326 	if (error)
327 		goto out;
328 
329 	error = dsopen(dev, devtype, dp->d_dsflags, &dp->d_slice, &dp->d_label);
330 
331 	if (!dsisopen(dp->d_slice))
332 		dp->d_devsw->d_close(pdev, oflags, devtype, td);
333 out:
334 	dp->d_flags &= ~DISKFLAG_LOCK;
335 	if (dp->d_flags & DISKFLAG_WANTED) {
336 		dp->d_flags &= ~DISKFLAG_WANTED;
337 		wakeup(dp);
338 	}
339 
340 	return(error);
341 }
342 
343 static int
344 diskclose(dev_t dev, int fflag, int devtype, struct thread *td)
345 {
346 	struct disk *dp;
347 	int error;
348 	dev_t pdev;
349 
350 	error = 0;
351 	pdev = dkmodpart(dkmodslice(dev, WHOLE_DISK_SLICE), RAW_PART);
352 	dp = pdev->si_disk;
353 	if (!dp)
354 		return (ENXIO);
355 	dsclose(dev, devtype, dp->d_slice);
356 	if (!dsisopen(dp->d_slice))
357 		error = dp->d_devsw->d_close(dp->d_dev, fflag, devtype, td);
358 	return (error);
359 }
360 
361 static void
362 diskstrategy(struct bio *bp)
363 {
364 	dev_t pdev;
365 	struct disk *dp;
366 
367 	pdev = dkmodpart(dkmodslice(bp->bio_dev, WHOLE_DISK_SLICE), RAW_PART);
368 	dp = pdev->si_disk;
369 	bp->bio_resid = bp->bio_bcount;
370 	if (dp != bp->bio_dev->si_disk)
371 		inherit_raw(pdev, bp->bio_dev);
372 
373 	if (!dp) {
374 		biofinish(bp, NULL, ENXIO);
375 		return;
376 	}
377 
378 	if (dscheck(bp, dp->d_slice) <= 0) {
379 		biodone(bp);
380 		return;
381 	}
382 
383 	if (bp->bio_bcount == 0) {
384 		biodone(bp);
385 		return;
386 	}
387 
388 	KASSERT(dp->d_devsw != NULL, ("NULL devsw"));
389 	KASSERT(dp->d_devsw->d_strategy != NULL, ("NULL d_strategy"));
390 	dp->d_devsw->d_strategy(bp);
391 	return;
392 
393 }
394 
395 static int
396 diskioctl(dev_t dev, u_long cmd, caddr_t data, int fflag, struct thread *td)
397 {
398 	struct disk *dp;
399 	int error;
400 	dev_t pdev;
401 
402 	pdev = dkmodpart(dkmodslice(dev, WHOLE_DISK_SLICE), RAW_PART);
403 	dp = pdev->si_disk;
404 	if (!dp)
405 		return (ENXIO);
406 	error = dsioctl(dev, cmd, data, fflag, &dp->d_slice);
407 	if (error == ENOIOCTL)
408 		error = dp->d_devsw->d_ioctl(dev, cmd, data, fflag, td);
409 	return (error);
410 }
411 
412 static int
413 diskpsize(dev_t dev)
414 {
415 	struct disk *dp;
416 	dev_t pdev;
417 
418 	pdev = dkmodpart(dkmodslice(dev, WHOLE_DISK_SLICE), RAW_PART);
419 	dp = pdev->si_disk;
420 	if (!dp)
421 		return (-1);
422 	if (dp != dev->si_disk) {
423 		dev->si_drv1 = pdev->si_drv1;
424 		dev->si_drv2 = pdev->si_drv2;
425 		/* XXX: don't set bp->b_dev->si_disk (?) */
426 	}
427 	return (dssize(dev, &dp->d_slice));
428 }
429 
430 SYSCTL_DECL(_debug_sizeof);
431 
432 SYSCTL_INT(_debug_sizeof, OID_AUTO, disklabel, CTLFLAG_RD,
433     0, sizeof(struct disklabel), "sizeof(struct disklabel)");
434 
435 SYSCTL_INT(_debug_sizeof, OID_AUTO, diskslices, CTLFLAG_RD,
436     0, sizeof(struct diskslices), "sizeof(struct diskslices)");
437 
438 SYSCTL_INT(_debug_sizeof, OID_AUTO, disk, CTLFLAG_RD,
439     0, sizeof(struct disk), "sizeof(struct disk)");
440