xref: /freebsd/sys/kern/subr_disk.c (revision 42c159fe388a3765f69860c84183700af37aca8a)
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 int
202 disk_dumpcheck(dev_t dev, u_int *count, u_int *blkno, u_int *secsize)
203 {
204 	struct disk *dp;
205 	struct disklabel *dl;
206 	u_int boff;
207 
208 	dp = dev->si_disk;
209 	if (!dp)
210 		return (ENXIO);
211 	if (!dp->d_slice)
212 		return (ENXIO);
213 	dl = dsgetlabel(dev, dp->d_slice);
214 	if (!dl)
215 		return (ENXIO);
216 	*count = Maxmem * (PAGE_SIZE / dl->d_secsize);
217 	if (dumplo <= LABELSECTOR ||
218 	    (dumplo + *count > dl->d_partitions[dkpart(dev)].p_size))
219 		return (EINVAL);
220 	boff = dl->d_partitions[dkpart(dev)].p_offset +
221 	    dp->d_slice->dss_slices[dkslice(dev)].ds_offset;
222 	*blkno = boff + dumplo;
223 	*secsize = dl->d_secsize;
224 	return (0);
225 
226 }
227 
228 void
229 disk_invalidate (struct disk *disk)
230 {
231 	if (disk->d_slice)
232 		dsgone(&disk->d_slice);
233 }
234 
235 void
236 disk_destroy(dev_t dev)
237 {
238 	LIST_REMOVE(dev->si_disk, d_list);
239 	bzero(dev->si_disk, sizeof(*dev->si_disk));
240     	dev->si_disk = NULL;
241 	destroy_dev(dev);
242 	return;
243 }
244 
245 struct disk *
246 disk_enumerate(struct disk *disk)
247 {
248 	if (!disk)
249 		return (LIST_FIRST(&disklist));
250 	else
251 		return (LIST_NEXT(disk, d_list));
252 }
253 
254 static int
255 sysctl_disks(SYSCTL_HANDLER_ARGS)
256 {
257 	struct disk *disk;
258 	int error, first;
259 
260 	disk = NULL;
261 	first = 1;
262 
263 	while ((disk = disk_enumerate(disk))) {
264 		if (!first) {
265 			error = SYSCTL_OUT(req, " ", 1);
266 			if (error)
267 				return error;
268 		} else {
269 			first = 0;
270 		}
271 		error = SYSCTL_OUT(req, disk->d_dev->si_name, strlen(disk->d_dev->si_name));
272 		if (error)
273 			return error;
274 	}
275 	error = SYSCTL_OUT(req, "", 1);
276 	return error;
277 }
278 
279 SYSCTL_PROC(_kern, OID_AUTO, disks, CTLTYPE_STRING | CTLFLAG_RD, 0, NULL,
280     sysctl_disks, "A", "names of available disks");
281 
282 /*
283  * The cdevsw functions
284  */
285 
286 static int
287 diskopen(dev_t dev, int oflags, int devtype, struct thread *td)
288 {
289 	dev_t pdev;
290 	struct disk *dp;
291 	int error;
292 
293 	error = 0;
294 	pdev = dkmodpart(dkmodslice(dev, WHOLE_DISK_SLICE), RAW_PART);
295 
296 	dp = pdev->si_disk;
297 	if (!dp)
298 		return (ENXIO);
299 
300 	while (dp->d_flags & DISKFLAG_LOCK) {
301 		dp->d_flags |= DISKFLAG_WANTED;
302 		error = tsleep(dp, PRIBIO | PCATCH, "diskopen", hz);
303 		if (error)
304 			return (error);
305 	}
306 	dp->d_flags |= DISKFLAG_LOCK;
307 
308 	if (!dsisopen(dp->d_slice)) {
309 		if (!pdev->si_iosize_max)
310 			pdev->si_iosize_max = dev->si_iosize_max;
311 		error = dp->d_devsw->d_open(pdev, oflags, devtype, td);
312 	}
313 
314 	/* Inherit properties from the whole/raw dev_t */
315 	inherit_raw(pdev, dev);
316 
317 	if (error)
318 		goto out;
319 
320 	error = dsopen(dev, devtype, dp->d_dsflags, &dp->d_slice, &dp->d_label);
321 
322 	if (!dsisopen(dp->d_slice))
323 		dp->d_devsw->d_close(pdev, oflags, devtype, td);
324 out:
325 	dp->d_flags &= ~DISKFLAG_LOCK;
326 	if (dp->d_flags & DISKFLAG_WANTED) {
327 		dp->d_flags &= ~DISKFLAG_WANTED;
328 		wakeup(dp);
329 	}
330 
331 	return(error);
332 }
333 
334 static int
335 diskclose(dev_t dev, int fflag, int devtype, struct thread *td)
336 {
337 	struct disk *dp;
338 	int error;
339 	dev_t pdev;
340 
341 	error = 0;
342 	pdev = dkmodpart(dkmodslice(dev, WHOLE_DISK_SLICE), RAW_PART);
343 	dp = pdev->si_disk;
344 	if (!dp)
345 		return (ENXIO);
346 	dsclose(dev, devtype, dp->d_slice);
347 	if (!dsisopen(dp->d_slice))
348 		error = dp->d_devsw->d_close(dp->d_dev, fflag, devtype, td);
349 	return (error);
350 }
351 
352 static void
353 diskstrategy(struct bio *bp)
354 {
355 	dev_t pdev;
356 	struct disk *dp;
357 
358 	pdev = dkmodpart(dkmodslice(bp->bio_dev, WHOLE_DISK_SLICE), RAW_PART);
359 	dp = pdev->si_disk;
360 	bp->bio_resid = bp->bio_bcount;
361 	if (dp != bp->bio_dev->si_disk)
362 		inherit_raw(pdev, bp->bio_dev);
363 
364 	if (!dp) {
365 		biofinish(bp, NULL, ENXIO);
366 		return;
367 	}
368 
369 	if (dscheck(bp, dp->d_slice) <= 0) {
370 		biodone(bp);
371 		return;
372 	}
373 
374 	if (bp->bio_bcount == 0) {
375 		biodone(bp);
376 		return;
377 	}
378 
379 	KASSERT(dp->d_devsw != NULL, ("NULL devsw"));
380 	KASSERT(dp->d_devsw->d_strategy != NULL, ("NULL d_strategy"));
381 	dp->d_devsw->d_strategy(bp);
382 	return;
383 
384 }
385 
386 static int
387 diskioctl(dev_t dev, u_long cmd, caddr_t data, int fflag, struct thread *td)
388 {
389 	struct disk *dp;
390 	int error;
391 	dev_t pdev;
392 
393 	pdev = dkmodpart(dkmodslice(dev, WHOLE_DISK_SLICE), RAW_PART);
394 	dp = pdev->si_disk;
395 	if (!dp)
396 		return (ENXIO);
397 	error = dsioctl(dev, cmd, data, fflag, &dp->d_slice);
398 	if (error == ENOIOCTL)
399 		error = dp->d_devsw->d_ioctl(dev, cmd, data, fflag, td);
400 	return (error);
401 }
402 
403 static int
404 diskpsize(dev_t dev)
405 {
406 	struct disk *dp;
407 	dev_t pdev;
408 
409 	pdev = dkmodpart(dkmodslice(dev, WHOLE_DISK_SLICE), RAW_PART);
410 	dp = pdev->si_disk;
411 	if (!dp)
412 		return (-1);
413 	if (dp != dev->si_disk) {
414 		dev->si_drv1 = pdev->si_drv1;
415 		dev->si_drv2 = pdev->si_drv2;
416 		/* XXX: don't set bp->b_dev->si_disk (?) */
417 	}
418 	return (dssize(dev, &dp->d_slice));
419 }
420 
421 SYSCTL_INT(_debug_sizeof, OID_AUTO, disklabel, CTLFLAG_RD,
422     0, sizeof(struct disklabel), "sizeof(struct disklabel)");
423 
424 SYSCTL_INT(_debug_sizeof, OID_AUTO, diskslices, CTLFLAG_RD,
425     0, sizeof(struct diskslices), "sizeof(struct diskslices)");
426 
427 SYSCTL_INT(_debug_sizeof, OID_AUTO, disk, CTLFLAG_RD,
428     0, sizeof(struct disk), "sizeof(struct disk)");
429 
430 #endif
431