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