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