xref: /freebsd/sys/kern/subr_disk.c (revision a79b71281cd63ad7a6cc43a6d5673a2510b51630)
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 <machine/md_var.h>
22 
23 MALLOC_DEFINE(M_DISK, "disk", "disk data");
24 
25 static d_strategy_t diskstrategy;
26 static d_open_t diskopen;
27 static d_close_t diskclose;
28 static d_ioctl_t diskioctl;
29 static d_psize_t diskpsize;
30 
31 static LIST_HEAD(, disk) disklist = LIST_HEAD_INITIALIZER(&disklist);
32 
33 dev_t
34 disk_create(int unit, struct disk *dp, int flags, struct cdevsw *cdevsw, struct cdevsw *proto)
35 {
36 	dev_t dev;
37 
38 	bzero(dp, sizeof(*dp));
39 
40 	dev = makedev(cdevsw->d_maj, 0);
41 	if (!devsw(dev)) {
42 		*proto = *cdevsw;
43 		proto->d_open = diskopen;
44 		proto->d_close = diskclose;
45 		proto->d_ioctl = diskioctl;
46 		proto->d_strategy = diskstrategy;
47 		proto->d_psize = diskpsize;
48 		cdevsw_add(proto);
49 	}
50 
51 	if (bootverbose)
52 		printf("Creating DISK %s%d\n", cdevsw->d_name, unit);
53 	dev = make_dev(proto, dkmakeminor(unit, WHOLE_DISK_SLICE, RAW_PART),
54 	    0, 0, 0, "%s%d", cdevsw->d_name, unit);
55 
56 	dev->si_disk = dp;
57 	dp->d_dev = dev;
58 	dp->d_dsflags = flags;
59 	dp->d_devsw = cdevsw;
60 	LIST_INSERT_HEAD(&disklist, dp, d_list);
61 	return (dev);
62 }
63 
64 int
65 disk_dumpcheck(dev_t dev, u_int *count, u_int *blkno, u_int *secsize)
66 {
67 	struct disk *dp;
68 	struct disklabel *dl;
69 	u_int boff;
70 
71 	dp = dev->si_disk;
72 	if (!dp)
73 		return (ENXIO);
74 	if (!dp->d_slice)
75 		return (ENXIO);
76 	dl = dsgetlabel(dev, dp->d_slice);
77 	if (!dl)
78 		return (ENXIO);
79 	*count = (u_long)Maxmem * PAGE_SIZE / dl->d_secsize;
80 	if (dumplo < 0 ||
81 	    (dumplo + *count > dl->d_partitions[dkpart(dev)].p_size))
82 		return (EINVAL);
83 	boff = dl->d_partitions[dkpart(dev)].p_offset +
84 	    dp->d_slice->dss_slices[dkslice(dev)].ds_offset;
85 	*blkno = boff + dumplo;
86 	*secsize = dl->d_secsize;
87 	return (0);
88 
89 }
90 
91 void
92 disk_invalidate (struct disk *disk)
93 {
94 	if (disk->d_slice)
95 		dsgone(&disk->d_slice);
96 }
97 
98 void
99 disk_destroy(dev_t dev)
100 {
101 	LIST_REMOVE(dev->si_disk, d_list);
102 	bzero(dev->si_disk, sizeof(*dev->si_disk));
103     	dev->si_disk = NULL;
104 	destroy_dev(dev);
105 	return;
106 }
107 
108 struct disk *
109 disk_enumerate(struct disk *disk)
110 {
111 	if (!disk)
112 		return (LIST_FIRST(&disklist));
113 	else
114 		return (LIST_NEXT(disk, d_list));
115 }
116 
117 /*
118  * The cdevsw functions
119  */
120 
121 static int
122 diskopen(dev_t dev, int oflags, int devtype, struct proc *p)
123 {
124 	dev_t pdev;
125 	struct disk *dp;
126 	int error;
127 
128 	error = 0;
129 	pdev = dkmodpart(dkmodslice(dev, WHOLE_DISK_SLICE), RAW_PART);
130 
131 	dp = pdev->si_disk;
132 	if (!dp)
133 		return (ENXIO);
134 
135 	while (dp->d_flags & DISKFLAG_LOCK) {
136 		dp->d_flags |= DISKFLAG_WANTED;
137 		error = tsleep(dp, PRIBIO | PCATCH, "diskopen", hz);
138 		if (error)
139 			return (error);
140 	}
141 	dp->d_flags |= DISKFLAG_LOCK;
142 
143 	if (!dsisopen(dp->d_slice)) {
144 		if (!pdev->si_iosize_max)
145 			pdev->si_iosize_max = dev->si_iosize_max;
146 		error = dp->d_devsw->d_open(pdev, oflags, devtype, p);
147 	}
148 
149 	/* Inherit properties from the whole/raw dev_t */
150 	dev->si_disk = pdev->si_disk;
151 	dev->si_drv1 = pdev->si_drv1;
152 	dev->si_drv2 = pdev->si_drv2;
153 	dev->si_iosize_max = pdev->si_iosize_max;
154 	dev->si_bsize_phys = pdev->si_bsize_phys;
155 	dev->si_bsize_best = pdev->si_bsize_best;
156 
157 	if (error)
158 		goto out;
159 
160 	error = dsopen(dev, devtype, dp->d_dsflags, &dp->d_slice, &dp->d_label);
161 
162 	if (!dsisopen(dp->d_slice))
163 		dp->d_devsw->d_close(pdev, oflags, devtype, p);
164 out:
165 	dp->d_flags &= ~DISKFLAG_LOCK;
166 	if (dp->d_flags & DISKFLAG_WANTED) {
167 		dp->d_flags &= ~DISKFLAG_WANTED;
168 		wakeup(dp);
169 	}
170 
171 	return(error);
172 }
173 
174 static int
175 diskclose(dev_t dev, int fflag, int devtype, struct proc *p)
176 {
177 	struct disk *dp;
178 	int error;
179 
180 	error = 0;
181 	dp = dev->si_disk;
182 	dsclose(dev, devtype, dp->d_slice);
183 	if (!dsisopen(dp->d_slice)) {
184 		error = dp->d_devsw->d_close(dp->d_dev, fflag, devtype, p);
185 	}
186 	return (error);
187 }
188 
189 static void
190 diskstrategy(struct bio *bp)
191 {
192 	dev_t pdev;
193 	struct disk *dp;
194 
195 	dp = bp->bio_dev->si_disk;
196 	if (!dp) {
197 		pdev = dkmodpart(dkmodslice(bp->bio_dev, WHOLE_DISK_SLICE), RAW_PART);
198 		dp = bp->bio_dev->si_disk = pdev->si_disk;
199 		bp->bio_dev->si_drv1 = pdev->si_drv1;
200 		bp->bio_dev->si_drv2 = pdev->si_drv2;
201 		bp->bio_dev->si_iosize_max = pdev->si_iosize_max;
202 		bp->bio_dev->si_bsize_phys = pdev->si_bsize_phys;
203 		bp->bio_dev->si_bsize_best = pdev->si_bsize_best;
204 	}
205 
206 	if (!dp) {
207 		bp->bio_error = ENXIO;
208 		bp->bio_flags |= BIO_ERROR;
209 		biodone(bp);
210 		return;
211 	}
212 
213 	if (dscheck(bp, dp->d_slice) <= 0) {
214 		biodone(bp);
215 		return;
216 	}
217 
218 	KASSERT(dp->d_devsw != NULL, ("NULL devsw"));
219 	KASSERT(dp->d_devsw->d_strategy != NULL, ("NULL d_strategy"));
220 	dp->d_devsw->d_strategy(bp);
221 	return;
222 
223 }
224 
225 static int
226 diskioctl(dev_t dev, u_long cmd, caddr_t data, int fflag, struct proc *p)
227 {
228 	struct disk *dp;
229 	int error;
230 
231 	dp = dev->si_disk;
232 	error = dsioctl(dev, cmd, data, fflag, &dp->d_slice);
233 	if (error == ENOIOCTL)
234 		error = dp->d_devsw->d_ioctl(dev, cmd, data, fflag, p);
235 	return (error);
236 }
237 
238 static int
239 diskpsize(dev_t dev)
240 {
241 	struct disk *dp;
242 	dev_t pdev;
243 
244 	dp = dev->si_disk;
245 	if (!dp) {
246 		pdev = dkmodpart(dkmodslice(dev, WHOLE_DISK_SLICE), RAW_PART);
247 		dp = pdev->si_disk;
248 		if (!dp)
249 			return (-1);
250 		dev->si_drv1 = pdev->si_drv1;
251 		dev->si_drv2 = pdev->si_drv2;
252 		/* XXX: don't set bp->b_dev->si_disk (?) */
253 	}
254 	return (dssize(dev, &dp->d_slice));
255 }
256 
257 SYSCTL_DECL(_debug_sizeof);
258 
259 SYSCTL_INT(_debug_sizeof, OID_AUTO, disklabel, CTLFLAG_RD,
260     0, sizeof(struct disklabel), "sizeof(struct disklabel)");
261 
262 SYSCTL_INT(_debug_sizeof, OID_AUTO, diskslices, CTLFLAG_RD,
263     0, sizeof(struct diskslices), "sizeof(struct diskslices)");
264 
265 SYSCTL_INT(_debug_sizeof, OID_AUTO, disk, CTLFLAG_RD,
266     0, sizeof(struct disk), "sizeof(struct disk)");
267