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