Deleted Added
sdiff udiff text old ( 110700 ) new ( 110710 )
full compact
1/*-
2 * Copyright (c) 2002 Poul-Henning Kamp
3 * Copyright (c) 2002 Networks Associates Technology, Inc.
4 * All rights reserved.
5 *
6 * This software was developed for the FreeBSD Project by Poul-Henning Kamp
7 * and NAI Labs, the Security Research Division of Network Associates, Inc.
8 * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the
9 * DARPA CHATS research program.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. The names of the authors may not be used to endorse or promote
20 * products derived from this software without specific prior written
21 * permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 *
35 * $FreeBSD: head/sys/geom/geom_dev.c 110700 2003-02-11 12:49:58Z phk $
36 */
37
38#include <sys/param.h>
39#include <sys/stdint.h>
40#include <sys/systm.h>
41#include <sys/malloc.h>
42#include <sys/kernel.h>
43#include <sys/conf.h>
44#include <sys/bio.h>
45#include <sys/lock.h>
46#include <sys/mutex.h>
47#include <sys/errno.h>
48#include <sys/time.h>
49#include <sys/disk.h>
50#include <sys/fcntl.h>
51#include <geom/geom.h>
52#include <geom/geom_int.h>
53#include <geom/geom_stats.h>
54#include <machine/limits.h>
55
56static d_open_t g_dev_open;
57static d_close_t g_dev_close;
58static d_strategy_t g_dev_strategy;
59static d_ioctl_t g_dev_ioctl;
60static d_psize_t g_dev_psize;
61
62static struct cdevsw g_dev_cdevsw = {
63 /* open */ g_dev_open,
64 /* close */ g_dev_close,
65 /* read */ physread,
66 /* write */ physwrite,
67 /* ioctl */ g_dev_ioctl,
68 /* poll */ nopoll,
69 /* mmap */ nommap,
70 /* strategy */ g_dev_strategy,
71 /* name */ "g_dev",
72 /* maj */ GEOM_MAJOR,
73 /* dump */ nodump,
74 /* psize */ g_dev_psize,
75 /* flags */ D_DISK | D_TRACKCLOSE,
76 /* kqfilter */ nokqfilter
77};
78
79static g_taste_t g_dev_taste;
80static g_orphan_t g_dev_orphan;
81
82static struct g_class g_dev_class = {
83 "DEV",
84 g_dev_taste,
85 NULL,
86 G_CLASS_INITIALIZER
87};
88
89int
90g_dev_print(void)
91{
92 struct g_geom *gp;
93
94 if (LIST_EMPTY(&g_dev_class.geom))
95 return (0);
96 printf("List of GEOM disk devices:\n ");
97 LIST_FOREACH(gp, &g_dev_class.geom, geom)
98 printf(" %s", gp->name);
99 printf("\n");
100 return (1);
101}
102
103/*
104 * XXX: This is disgusting and wrong in every way imaginable: The only reason
105 * XXX: we have a clone function is because of the root-mount hack we currently
106 * XXX: employ. An improvment would be to unregister this cloner once we know
107 * XXX: we no longer need it. Ideally, root-fs would be mounted through DEVFS
108 * XXX: eliminating the need for this hack.
109 */
110static void
111g_dev_clone(void *arg __unused, char *name, int namelen __unused, dev_t *dev)
112{
113 struct g_geom *gp;
114
115 if (*dev != NODEV)
116 return;
117
118 g_waitidle();
119
120 /* g_topology_lock(); */
121 LIST_FOREACH(gp, &g_dev_class.geom, geom) {
122 if (strcmp(gp->name, name))
123 continue;
124 *dev = gp->softc;
125 g_trace(G_T_TOPOLOGY, "g_dev_clone(%s) = %p", name, *dev);
126 return;
127 }
128 /* g_topology_unlock(); */
129 return;
130}
131
132static void
133g_dev_register_cloner(void *foo __unused)
134{
135 static int once;
136
137 /* XXX: why would this happen more than once ?? */
138 if (!once) {
139 EVENTHANDLER_REGISTER(dev_clone, g_dev_clone, 0, 1000);
140 once++;
141 }
142}
143
144SYSINIT(geomdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE,g_dev_register_cloner,NULL);
145
146static struct g_geom *
147g_dev_taste(struct g_class *mp, struct g_provider *pp, int insist __unused)
148{
149 struct g_geom *gp;
150 struct g_consumer *cp;
151 static int unit = GEOM_MINOR_PROVIDERS;
152 int error;
153 dev_t dev;
154
155 g_trace(G_T_TOPOLOGY, "dev_taste(%s,%s)", mp->name, pp->name);
156 g_topology_assert();
157 LIST_FOREACH(cp, &pp->consumers, consumers)
158 if (cp->geom->class == mp)
159 return (NULL);
160 gp = g_new_geomf(mp, pp->name);
161 gp->orphan = g_dev_orphan;
162 cp = g_new_consumer(gp);
163 error = g_attach(cp, pp);
164 KASSERT(error == 0,
165 ("g_dev_taste(%s) failed to g_attach, err=%d", pp->name, error));
166 /*
167 * XXX: I'm not 100% sure we can call make_dev(9) without Giant
168 * yet. Once we can, we don't need to drop topology here either.
169 */
170 g_topology_unlock();
171 mtx_lock(&Giant);
172 dev = make_dev(&g_dev_cdevsw, unit2minor(unit++),
173 UID_ROOT, GID_OPERATOR, 0640, gp->name);
174 if (pp->flags & G_PF_CANDELETE)
175 dev->si_flags |= SI_CANDELETE;
176 mtx_unlock(&Giant);
177 g_topology_lock();
178
179 gp->softc = dev;
180 dev->si_drv1 = gp;
181 dev->si_drv2 = cp;
182 return (gp);
183}
184
185static int
186g_dev_open(dev_t dev, int flags, int fmt, struct thread *td)
187{
188 struct g_geom *gp;
189 struct g_consumer *cp;
190 int error, r, w, e;
191
192 gp = dev->si_drv1;
193 cp = dev->si_drv2;
194 if (gp == NULL || cp == NULL)
195 return(ENXIO);
196 g_trace(G_T_ACCESS, "g_dev_open(%s, %d, %d, %p)",
197 gp->name, flags, fmt, td);
198 DROP_GIANT();
199 g_topology_lock();
200 r = flags & FREAD ? 1 : 0;
201 w = flags & FWRITE ? 1 : 0;
202#ifdef notyet
203 e = flags & O_EXCL ? 1 : 0;
204#else
205 e = 0;
206#endif
207 error = g_access_rel(cp, r, w, e);
208 g_topology_unlock();
209 PICKUP_GIANT();
210 g_waitidle();
211 dev->si_bsize_phys = cp->provider->sectorsize;
212 return(error);
213}
214
215static int
216g_dev_close(dev_t dev, int flags, int fmt, struct thread *td)
217{
218 struct g_geom *gp;
219 struct g_consumer *cp;
220 int error, r, w, e;
221
222 gp = dev->si_drv1;
223 cp = dev->si_drv2;
224 if (gp == NULL || cp == NULL)
225 return(ENXIO);
226 g_trace(G_T_ACCESS, "g_dev_close(%s, %d, %d, %p)",
227 gp->name, flags, fmt, td);
228 DROP_GIANT();
229 g_topology_lock();
230 r = flags & FREAD ? -1 : 0;
231 w = flags & FWRITE ? -1 : 0;
232#ifdef notyet
233 e = flags & O_EXCL ? -1 : 0;
234#else
235 e = 0;
236#endif
237 error = g_access_rel(cp, r, w, e);
238 g_topology_unlock();
239 PICKUP_GIANT();
240 g_waitidle();
241 return (error);
242}
243
244static int
245g_dev_ioctl(dev_t dev, u_long cmd, caddr_t data, int fflag, struct thread *td)
246{
247 struct g_geom *gp, *gp2;
248 struct g_consumer *cp;
249 struct g_provider *pp2;
250 struct g_kerneldump kd;
251 int i, error;
252 u_int u;
253 struct g_ioctl *gio;
254
255 gp = dev->si_drv1;
256 cp = dev->si_drv2;
257 pp2 = cp->provider;
258 gp2 = pp2->geom;
259 gio = NULL;
260
261 error = 0;
262 DROP_GIANT();
263
264 gio = NULL;
265 i = IOCPARM_LEN(cmd);
266 switch (cmd) {
267 case DIOCGSECTORSIZE:
268 *(u_int *)data = cp->provider->sectorsize;
269 if (*(u_int *)data == 0)
270 error = ENOENT;
271 break;
272 case DIOCGMEDIASIZE:
273 *(off_t *)data = cp->provider->mediasize;
274 if (*(off_t *)data == 0)
275 error = ENOENT;
276 break;
277 case DIOCGFWSECTORS:
278 error = g_io_getattr("GEOM::fwsectors", cp, &i, data);
279 if (error == 0 && *(u_int *)data == 0)
280 error = ENOENT;
281 break;
282 case DIOCGFWHEADS:
283 error = g_io_getattr("GEOM::fwheads", cp, &i, data);
284 if (error == 0 && *(u_int *)data == 0)
285 error = ENOENT;
286 break;
287 case DIOCGFRONTSTUFF:
288 error = g_io_getattr("GEOM::frontstuff", cp, &i, data);
289 break;
290 case DIOCSKERNELDUMP:
291 u = *((u_int *)data);
292 if (!u) {
293 set_dumper(NULL);
294 error = 0;
295 break;
296 }
297 kd.offset = 0;
298 kd.length = OFF_MAX;
299 i = sizeof kd;
300 error = g_io_getattr("GEOM::kerneldump", cp, &i, &kd);
301 if (!error)
302 dev->si_flags |= SI_DUMPDEV;
303 break;
304
305 default:
306 gio = g_malloc(sizeof *gio, M_ZERO);
307 gio->cmd = cmd;
308 gio->data = data;
309 gio->fflag = fflag;
310 gio->td = td;
311 i = sizeof *gio;
312 /*
313 * We always issue ioctls as getattr since the direction of data
314 * movement in ioctl is no indication of the ioctl being a "set"
315 * or "get" type ioctl or if such simplistic terms even apply
316 */
317 error = g_io_getattr("GEOM::ioctl", cp, &i, gio);
318 break;
319 }
320
321 PICKUP_GIANT();
322 if (error == EDIRIOCTL) {
323 KASSERT(gio != NULL, ("NULL gio but EDIRIOCTL"));
324 KASSERT(gio->func != NULL, ("NULL function but EDIRIOCTL"));
325 error = (gio->func)(gio->dev, cmd, data, fflag, td);
326 }
327 g_waitidle();
328 if (gio != NULL && (error == EOPNOTSUPP || error == ENOIOCTL)) {
329 if (g_debugflags & G_T_TOPOLOGY) {
330 i = IOCGROUP(cmd);
331 printf("IOCTL(0x%lx) \"%s\"", cmd, gp->name);
332 if (i > ' ' && i <= '~')
333 printf(" '%c'", (int)IOCGROUP(cmd));
334 else
335 printf(" 0x%lx", IOCGROUP(cmd));
336 printf("/%ld ", cmd & 0xff);
337 if (cmd & IOC_IN)
338 printf("I");
339 if (cmd & IOC_OUT)
340 printf("O");
341 printf("(%ld) = ENOIOCTL\n", IOCPARM_LEN(cmd));
342 }
343 error = ENOTTY;
344 }
345 if (gio != NULL)
346 g_free(gio);
347 return (error);
348}
349
350static int
351g_dev_psize(dev_t dev)
352{
353 struct g_consumer *cp;
354 off_t mediasize;
355
356 cp = dev->si_drv2;
357
358 mediasize = cp->provider->mediasize;
359 return (mediasize >> DEV_BSHIFT);
360}
361
362static void
363g_dev_done(struct bio *bp2)
364{
365 struct bio *bp;
366
367 bp = bp2->bio_parent;
368 bp->bio_error = bp2->bio_error;
369 if (bp->bio_error != 0) {
370 g_trace(G_T_BIO, "g_dev_done(%p) had error %d",
371 bp2, bp->bio_error);
372 bp->bio_flags |= BIO_ERROR;
373 } else {
374 g_trace(G_T_BIO, "g_dev_done(%p/%p) resid %ld completed %jd",
375 bp2, bp, bp->bio_resid, (intmax_t)bp2->bio_completed);
376 }
377 bp->bio_resid = bp->bio_bcount - bp2->bio_completed;
378 g_destroy_bio(bp2);
379 mtx_lock(&Giant);
380 biodone(bp);
381 mtx_unlock(&Giant);
382}
383
384static void
385g_dev_strategy(struct bio *bp)
386{
387 struct g_geom *gp;
388 struct g_consumer *cp;
389 struct bio *bp2;
390 dev_t dev;
391
392 KASSERT(bp->bio_cmd == BIO_READ ||
393 bp->bio_cmd == BIO_WRITE ||
394 bp->bio_cmd == BIO_DELETE,
395 ("Wrong bio_cmd bio=%p cmd=%d", bp, bp->bio_cmd));
396 dev = bp->bio_dev;
397 gp = dev->si_drv1;
398 cp = dev->si_drv2;
399 bp2 = g_clone_bio(bp);
400 KASSERT(bp2 != NULL, ("XXX: ENOMEM in a bad place"));
401 bp2->bio_offset = (off_t)bp->bio_blkno << DEV_BSHIFT;
402 KASSERT(bp2->bio_offset >= 0,
403 ("Negative bio_offset (%jd) on bio %p",
404 (intmax_t)bp2->bio_offset, bp));
405 bp2->bio_length = (off_t)bp->bio_bcount;
406 bp2->bio_done = g_dev_done;
407 g_trace(G_T_BIO,
408 "g_dev_strategy(%p/%p) offset %jd length %jd data %p cmd %d",
409 bp, bp2, (intmax_t)bp->bio_offset, (intmax_t)bp2->bio_length,
410 bp2->bio_data, bp2->bio_cmd);
411 g_io_request(bp2, cp);
412}
413
414/*
415 * g_dev_orphan()
416 *
417 * Called from below when the provider orphaned us. It is our responsibility
418 * to get the access counts back to zero, until we do so the stack below will
419 * not unravel. We must clear the kernel-dump settings, if this is the
420 * current dumpdev. We call destroy_dev(9) to send our dev_t the way of
421 * punched cards and if we have non-zero access counts, we call down with
422 * them negated before we detattch and selfdestruct.
423 */
424
425static void
426g_dev_orphan(struct g_consumer *cp)
427{
428 struct g_geom *gp;
429 dev_t dev;
430
431 gp = cp->geom;
432 g_trace(G_T_TOPOLOGY, "g_dev_orphan(%p(%s))", cp, gp->name);
433 g_topology_assert();
434 if (cp->stat->nop != cp->stat->nend) /* XXX ? */
435 return;
436 dev = gp->softc;
437 if (dev->si_flags & SI_DUMPDEV)
438 set_dumper(NULL);
439 /* XXX: we may need Giant for now */
440 destroy_dev(dev);
441 if (cp->acr > 0 || cp->acw > 0 || cp->ace > 0)
442 g_access_rel(cp, -cp->acr, -cp->acw, -cp->ace);
443 g_detach(cp);
444 g_destroy_consumer(cp);
445 g_destroy_geom(gp);
446}
447
448DECLARE_GEOM_CLASS(g_dev_class, g_dev);