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