xref: /freebsd/sys/kern/subr_disk.c (revision 1d66272a85cde1c8a69c58f4b5dd649babd6eca6)
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 	dsclose(dev, devtype, dp->d_slice);
284 	if (!dsisopen(dp->d_slice)) {
285 		error = dp->d_devsw->d_close(dp->d_dev, fflag, devtype, p);
286 	}
287 	return (error);
288 }
289 
290 static void
291 diskstrategy(struct bio *bp)
292 {
293 	dev_t pdev;
294 	struct disk *dp;
295 
296 	pdev = dkmodpart(dkmodslice(bp->bio_dev, WHOLE_DISK_SLICE), RAW_PART);
297 	dp = pdev->si_disk;
298 	if (dp != bp->bio_dev->si_disk)
299 		inherit_raw(pdev, bp->bio_dev);
300 
301 	if (!dp) {
302 		bp->bio_error = ENXIO;
303 		bp->bio_flags |= BIO_ERROR;
304 		biodone(bp);
305 		return;
306 	}
307 
308 	if (dscheck(bp, dp->d_slice) <= 0) {
309 		biodone(bp);
310 		return;
311 	}
312 
313 	KASSERT(dp->d_devsw != NULL, ("NULL devsw"));
314 	KASSERT(dp->d_devsw->d_strategy != NULL, ("NULL d_strategy"));
315 	dp->d_devsw->d_strategy(bp);
316 	return;
317 
318 }
319 
320 static int
321 diskioctl(dev_t dev, u_long cmd, caddr_t data, int fflag, struct proc *p)
322 {
323 	struct disk *dp;
324 	int error;
325 	dev_t pdev;
326 
327 	pdev = dkmodpart(dkmodslice(dev, WHOLE_DISK_SLICE), RAW_PART);
328 	dp = pdev->si_disk;
329 	error = dsioctl(dev, cmd, data, fflag, &dp->d_slice);
330 	if (error == ENOIOCTL)
331 		error = dp->d_devsw->d_ioctl(dev, cmd, data, fflag, p);
332 	return (error);
333 }
334 
335 static int
336 diskpsize(dev_t dev)
337 {
338 	struct disk *dp;
339 	dev_t pdev;
340 
341 	pdev = dkmodpart(dkmodslice(dev, WHOLE_DISK_SLICE), RAW_PART);
342 	dp = pdev->si_disk;
343 	if (!dp)
344 		return (-1);
345 	if (dp != dev->si_disk) {
346 		dev->si_drv1 = pdev->si_drv1;
347 		dev->si_drv2 = pdev->si_drv2;
348 		/* XXX: don't set bp->b_dev->si_disk (?) */
349 	}
350 	return (dssize(dev, &dp->d_slice));
351 }
352 
353 SYSCTL_DECL(_debug_sizeof);
354 
355 SYSCTL_INT(_debug_sizeof, OID_AUTO, disklabel, CTLFLAG_RD,
356     0, sizeof(struct disklabel), "sizeof(struct disklabel)");
357 
358 SYSCTL_INT(_debug_sizeof, OID_AUTO, diskslices, CTLFLAG_RD,
359     0, sizeof(struct diskslices), "sizeof(struct diskslices)");
360 
361 SYSCTL_INT(_debug_sizeof, OID_AUTO, disk, CTLFLAG_RD,
362     0, sizeof(struct disk), "sizeof(struct disk)");
363