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