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