subr_disk.c revision 61953
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 61953 2000-06-22 11:44:43Z nbm $
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
24MALLOC_DEFINE(M_DISK, "disk", "disk data");
25
26static d_strategy_t diskstrategy;
27static d_open_t diskopen;
28static d_close_t diskclose;
29static d_ioctl_t diskioctl;
30static d_psize_t diskpsize;
31
32static LIST_HEAD(, disk) disklist = LIST_HEAD_INITIALIZER(&disklist);
33
34dev_t
35disk_create(int unit, struct disk *dp, int flags, struct cdevsw *cdevsw, struct cdevsw *proto)
36{
37	dev_t dev;
38
39	bzero(dp, sizeof(*dp));
40
41	dev = makedev(cdevsw->d_maj, 0);
42	if (!devsw(dev)) {
43		*proto = *cdevsw;
44		proto->d_open = diskopen;
45		proto->d_close = diskclose;
46		proto->d_ioctl = diskioctl;
47		proto->d_strategy = diskstrategy;
48		proto->d_psize = diskpsize;
49		cdevsw_add(proto);
50	}
51
52	if (bootverbose)
53		printf("Creating DISK %s%d\n", cdevsw->d_name, unit);
54	dev = make_dev(proto, dkmakeminor(unit, WHOLE_DISK_SLICE, RAW_PART),
55	    0, 0, 0, "%s%d", cdevsw->d_name, unit);
56
57	dev->si_disk = dp;
58	dp->d_dev = dev;
59	dp->d_dsflags = flags;
60	dp->d_devsw = cdevsw;
61	LIST_INSERT_HEAD(&disklist, dp, d_list);
62	return (dev);
63}
64
65int
66disk_dumpcheck(dev_t dev, u_int *count, u_int *blkno, u_int *secsize)
67{
68	struct disk *dp;
69	struct disklabel *dl;
70	u_int boff;
71
72	dp = dev->si_disk;
73	if (!dp)
74		return (ENXIO);
75	if (!dp->d_slice)
76		return (ENXIO);
77	dl = dsgetlabel(dev, dp->d_slice);
78	if (!dl)
79		return (ENXIO);
80	*count = (u_long)Maxmem * PAGE_SIZE / dl->d_secsize;
81	if (dumplo < 0 ||
82	    (dumplo + *count > dl->d_partitions[dkpart(dev)].p_size))
83		return (EINVAL);
84	boff = dl->d_partitions[dkpart(dev)].p_offset +
85	    dp->d_slice->dss_slices[dkslice(dev)].ds_offset;
86	*blkno = boff + dumplo;
87	*secsize = dl->d_secsize;
88	return (0);
89
90}
91
92void
93disk_invalidate (struct disk *disk)
94{
95	if (disk->d_slice)
96		dsgone(&disk->d_slice);
97}
98
99void
100disk_destroy(dev_t dev)
101{
102	LIST_REMOVE(dev->si_disk, d_list);
103	bzero(dev->si_disk, sizeof(*dev->si_disk));
104    	dev->si_disk = NULL;
105	destroy_dev(dev);
106	return;
107}
108
109struct disk *
110disk_enumerate(struct disk *disk)
111{
112	if (!disk)
113		return (LIST_FIRST(&disklist));
114	else
115		return (LIST_NEXT(disk, d_list));
116}
117
118static int
119sysctl_disks SYSCTL_HANDLER_ARGS
120{
121	struct disk *disk;
122	int error, first;
123
124	disk = NULL;
125	first = 1;
126
127	while ((disk = disk_enumerate(disk))) {
128		if (!first) {
129			error = SYSCTL_OUT(req, " ", 1);
130			if (error)
131				return error;
132		} else {
133			first = 0;
134		}
135		error = SYSCTL_OUT(req, disk->d_dev->si_name, strlen(disk->d_dev->si_name));
136		if (error)
137			return error;
138	}
139	error = SYSCTL_OUT(req, "", 1);
140	return error;
141}
142
143SYSCTL_PROC(_kern, OID_AUTO, disks, CTLTYPE_STRING | CTLFLAG_RD, 0, NULL,
144    sysctl_disks, "A", "names of available disks");
145
146/*
147 * The cdevsw functions
148 */
149
150static int
151diskopen(dev_t dev, int oflags, int devtype, struct proc *p)
152{
153	dev_t pdev;
154	struct disk *dp;
155	int error;
156
157	error = 0;
158	pdev = dkmodpart(dkmodslice(dev, WHOLE_DISK_SLICE), RAW_PART);
159
160	dp = pdev->si_disk;
161	if (!dp)
162		return (ENXIO);
163
164	while (dp->d_flags & DISKFLAG_LOCK) {
165		dp->d_flags |= DISKFLAG_WANTED;
166		error = tsleep(dp, PRIBIO | PCATCH, "diskopen", hz);
167		if (error)
168			return (error);
169	}
170	dp->d_flags |= DISKFLAG_LOCK;
171
172	if (!dsisopen(dp->d_slice)) {
173		if (!pdev->si_iosize_max)
174			pdev->si_iosize_max = dev->si_iosize_max;
175		error = dp->d_devsw->d_open(pdev, oflags, devtype, p);
176	}
177
178	/* Inherit properties from the whole/raw dev_t */
179	dev->si_disk = pdev->si_disk;
180	dev->si_drv1 = pdev->si_drv1;
181	dev->si_drv2 = pdev->si_drv2;
182	dev->si_iosize_max = pdev->si_iosize_max;
183	dev->si_bsize_phys = pdev->si_bsize_phys;
184	dev->si_bsize_best = pdev->si_bsize_best;
185
186	if (error)
187		goto out;
188
189	error = dsopen(dev, devtype, dp->d_dsflags, &dp->d_slice, &dp->d_label);
190
191	if (!dsisopen(dp->d_slice))
192		dp->d_devsw->d_close(pdev, oflags, devtype, p);
193out:
194	dp->d_flags &= ~DISKFLAG_LOCK;
195	if (dp->d_flags & DISKFLAG_WANTED) {
196		dp->d_flags &= ~DISKFLAG_WANTED;
197		wakeup(dp);
198	}
199
200	return(error);
201}
202
203static int
204diskclose(dev_t dev, int fflag, int devtype, struct proc *p)
205{
206	struct disk *dp;
207	int error;
208
209	error = 0;
210	dp = dev->si_disk;
211	dsclose(dev, devtype, dp->d_slice);
212	if (!dsisopen(dp->d_slice)) {
213		error = dp->d_devsw->d_close(dp->d_dev, fflag, devtype, p);
214	}
215	return (error);
216}
217
218static void
219diskstrategy(struct bio *bp)
220{
221	dev_t pdev;
222	struct disk *dp;
223
224	dp = bp->bio_dev->si_disk;
225	if (!dp) {
226		pdev = dkmodpart(dkmodslice(bp->bio_dev, WHOLE_DISK_SLICE), RAW_PART);
227		dp = bp->bio_dev->si_disk = pdev->si_disk;
228		bp->bio_dev->si_drv1 = pdev->si_drv1;
229		bp->bio_dev->si_drv2 = pdev->si_drv2;
230		bp->bio_dev->si_iosize_max = pdev->si_iosize_max;
231		bp->bio_dev->si_bsize_phys = pdev->si_bsize_phys;
232		bp->bio_dev->si_bsize_best = pdev->si_bsize_best;
233	}
234
235	if (!dp) {
236		bp->bio_error = ENXIO;
237		bp->bio_flags |= BIO_ERROR;
238		biodone(bp);
239		return;
240	}
241
242	if (dscheck(bp, dp->d_slice) <= 0) {
243		biodone(bp);
244		return;
245	}
246
247	KASSERT(dp->d_devsw != NULL, ("NULL devsw"));
248	KASSERT(dp->d_devsw->d_strategy != NULL, ("NULL d_strategy"));
249	dp->d_devsw->d_strategy(bp);
250	return;
251
252}
253
254static int
255diskioctl(dev_t dev, u_long cmd, caddr_t data, int fflag, struct proc *p)
256{
257	struct disk *dp;
258	int error;
259
260	dp = dev->si_disk;
261	error = dsioctl(dev, cmd, data, fflag, &dp->d_slice);
262	if (error == ENOIOCTL)
263		error = dp->d_devsw->d_ioctl(dev, cmd, data, fflag, p);
264	return (error);
265}
266
267static int
268diskpsize(dev_t dev)
269{
270	struct disk *dp;
271	dev_t pdev;
272
273	dp = dev->si_disk;
274	if (!dp) {
275		pdev = dkmodpart(dkmodslice(dev, WHOLE_DISK_SLICE), RAW_PART);
276		dp = pdev->si_disk;
277		if (!dp)
278			return (-1);
279		dev->si_drv1 = pdev->si_drv1;
280		dev->si_drv2 = pdev->si_drv2;
281		/* XXX: don't set bp->b_dev->si_disk (?) */
282	}
283	return (dssize(dev, &dp->d_slice));
284}
285
286SYSCTL_DECL(_debug_sizeof);
287
288SYSCTL_INT(_debug_sizeof, OID_AUTO, disklabel, CTLFLAG_RD,
289    0, sizeof(struct disklabel), "sizeof(struct disklabel)");
290
291SYSCTL_INT(_debug_sizeof, OID_AUTO, diskslices, CTLFLAG_RD,
292    0, sizeof(struct diskslices), "sizeof(struct diskslices)");
293
294SYSCTL_INT(_debug_sizeof, OID_AUTO, disk, CTLFLAG_RD,
295    0, sizeof(struct disk), "sizeof(struct disk)");
296