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