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