diff options
author | phk <phk@FreeBSD.org> | 2002-10-07 06:25:26 +0000 |
---|---|---|
committer | phk <phk@FreeBSD.org> | 2002-10-07 06:25:26 +0000 |
commit | 05568a1e183e75bdc8edaa948f276ddd47ebc8cb (patch) | |
tree | 12c86e8396fed513177c4639d423b328584ee616 /sys | |
parent | d0cb388eeab801d8cb58ad70b358a55fe82b5976 (diff) | |
download | FreeBSD-src-05568a1e183e75bdc8edaa948f276ddd47ebc8cb.zip FreeBSD-src-05568a1e183e75bdc8edaa948f276ddd47ebc8cb.tar.gz |
Copyin and copyout are only possible from a process-native thread,
and therefore we need a way for ioctl handlers to run in that thread
in GEOM. Rather than invent a complicated registration system to
recognize which ioctl handler to use for a given ioctl, we still
schedule all ioctls down the tree as bio transactions but add a
special return code that means "call me directly" and have the
geom_dev layer do that.
Use this for all ioctls that make it as far as a diskdriver to
avoid any backwards compatibility problems.
Requested by: scottl
Sponsored by: DARPA & NAI Labs
Diffstat (limited to 'sys')
-rw-r--r-- | sys/geom/geom.h | 7 | ||||
-rw-r--r-- | sys/geom/geom_dev.c | 28 | ||||
-rw-r--r-- | sys/geom/geom_disk.c | 32 | ||||
-rw-r--r-- | sys/sys/errno.h | 1 |
4 files changed, 26 insertions, 42 deletions
diff --git a/sys/geom/geom.h b/sys/geom/geom.h index c687e0e..c053fcb 100644 --- a/sys/geom/geom.h +++ b/sys/geom/geom.h @@ -225,11 +225,18 @@ int g_write_data(struct g_consumer *cp, off_t offset, void *ptr, off_t length); /* geom_kern.c / geom_kernsim.c */ +#ifndef _SYS_CONF_H_ +typedef int d_ioctl_t(dev_t dev, u_long cmd, caddr_t data, + int fflag, struct thread *td); +#endif + struct g_ioctl { u_long cmd; void *data; int fflag; struct thread *td; + d_ioctl_t *func; + dev_t dev; }; #ifdef _KERNEL diff --git a/sys/geom/geom_dev.c b/sys/geom/geom_dev.c index c926e18..1b30703 100644 --- a/sys/geom/geom_dev.c +++ b/sys/geom/geom_dev.c @@ -231,14 +231,12 @@ g_dev_ioctl(dev_t dev, u_long cmd, caddr_t data, int fflag, struct thread *td) int i, error; u_int u; struct g_ioctl *gio; -#if 0 - struct sbuf *usb, *sb; -#endif gp = dev->si_drv1; cp = dev->si_drv2; pp2 = cp->provider; gp2 = pp2->geom; + gio = NULL; error = 0; DROP_GIANT(); @@ -274,21 +272,9 @@ g_dev_ioctl(dev_t dev, u_long cmd, caddr_t data, int fflag, struct thread *td) if (!error) dev->si_flags |= SI_DUMPDEV; break; -#if 0 - case GEOMGETCONF: - /* we bogusly pass cp to avoid getting any consumers listed */ - sb = g_conf_specific(gp2->class, gp2, pp2, cp); - usb = (struct sbuf *)data; - if (usb->s_size - 1 < sbuf_len(sb)) - error = ENOMEM; - else - error = copyout(sbuf_data(sb), usb->s_buf, sbuf_len(sb) + 1); - if (!error) - usb->s_len = sbuf_len(sb); - break; -#endif + default: - gio = g_malloc(sizeof *gio, M_WAITOK); + gio = g_malloc(sizeof *gio, M_WAITOK | M_ZERO); gio->cmd = cmd; gio->data = data; gio->fflag = fflag; @@ -298,11 +284,17 @@ g_dev_ioctl(dev_t dev, u_long cmd, caddr_t data, int fflag, struct thread *td) error = g_io_setattr("GEOM::ioctl", cp, i, gio); else error = g_io_getattr("GEOM::ioctl", cp, &i, gio); - g_free(gio); break; } PICKUP_GIANT(); + if (error == EDIRIOCTL) { + KASSERT(gio != NULL, ("NULL gio but EDIRIOCTL")); + KASSERT(gio->func != NULL, ("NULL function but EDIRIOCTL")); + error = (gio->func)(gio->dev, cmd, data, fflag, td); + } + if (gio != NULL) + g_free(gio); g_waitidle(); if (error == ENOIOCTL) { if (g_debugflags & G_T_TOPOLOGY) { diff --git a/sys/geom/geom_disk.c b/sys/geom/geom_disk.c index 1f1d362..be9008c 100644 --- a/sys/geom/geom_disk.c +++ b/sys/geom/geom_disk.c @@ -132,26 +132,6 @@ g_disk_done(struct bio *bp) } static void -g_disk_ioctl(void *arg) -{ - struct bio *bp; - dev_t dev; - struct disk *dp; - struct g_ioctl *gio; - int error; - - bp = arg; - dp = bp->bio_to->geom->softc; - dev = dp->d_dev; - gio = (struct g_ioctl *)bp->bio_data; - mtx_lock(&Giant); - error = devsw(dev)->d_ioctl(dev, gio->cmd, - gio->data, gio->fflag, gio->td); - mtx_unlock(&Giant); - g_io_deliver(bp, error); -} - -static void g_disk_start(struct bio *bp) { struct bio *bp2; @@ -191,16 +171,20 @@ g_disk_start(struct bio *bp) g_disk_kerneldump(bp, dp); else if (!strcmp(bp->bio_attribute, "GEOM::ioctl") && bp->bio_length == sizeof *gio) { - g_call_me(g_disk_ioctl, bp); - return; + gio = (struct g_ioctl *)bp->bio_data; + gio->func = devsw(dp->d_dev)->d_ioctl; + gio->dev = dp->d_dev; + error = EDIRIOCTL; } else error = ENOIOCTL; break; case BIO_SETATTR: if (!strcmp(bp->bio_attribute, "GEOM::ioctl") && bp->bio_length == sizeof *gio) { - g_call_me(g_disk_ioctl, bp); - return; + gio = (struct g_ioctl *)bp->bio_data; + gio->func = devsw(dp->d_dev)->d_ioctl; + gio->dev = dp->d_dev; + error = EDIRIOCTL; } else error = ENOIOCTL; break; diff --git a/sys/sys/errno.h b/sys/sys/errno.h index 70ad24a..2e93afd 100644 --- a/sys/sys/errno.h +++ b/sys/sys/errno.h @@ -180,6 +180,7 @@ __END_DECLS #define ERESTART (-1) /* restart syscall */ #define EJUSTRETURN (-2) /* don't modify regs, just return */ #define ENOIOCTL (-3) /* ioctl not handled by this layer */ +#define EDIRIOCTL (-4) /* do direct ioctl in GEOM */ #endif #endif |