Simplify the ioctl handling in GEOM.

This replaces the current ioctl processing with a direct call path
from geom_dev() where the ioctl arrives (from SPECFS) to any directly
connected GEOM class.

The inverse of the above is no longer supported.  This is the
situation were you have one or more intervening GEOM classes, for
instance a BSDlabel on top of a MBR or PC98.  If you want to issue
MBR or PC98 specific ioctls, you will need to issue them on a MBR
or PC98 providers.

This paves the way for inviting CD's, FD's and other special cases
inside GEOM.
This commit is contained in:
Poul-Henning Kamp 2003-09-01 20:45:32 +00:00
parent 8778f63db2
commit 497c334767
6 changed files with 146 additions and 309 deletions

View File

@ -64,8 +64,8 @@ typedef int g_ctl_destroy_geom_t (struct gctl_req *, struct g_class *cp, struct
typedef int g_ctl_config_geom_t (struct gctl_req *, struct g_geom *gp, const char *verb);
typedef void g_init_t (struct g_class *mp);
typedef void g_fini_t (struct g_class *mp);
typedef struct g_geom * g_taste_t (struct g_class *, struct g_provider *,
int flags);
typedef struct g_geom * g_taste_t (struct g_class *, struct g_provider *, int flags);
typedef int g_ioctl_t(struct g_provider *pp, u_long cmd, void *data, struct thread *td);
#define G_TF_NORMAL 0
#define G_TF_INSIST 1
#define G_TF_TRANSPARENT 2
@ -116,6 +116,7 @@ struct g_geom {
g_dumpconf_t *dumpconf;
g_access_t *access;
g_orphan_t *orphan;
g_ioctl_t *ioctl;
void *softc;
unsigned flags;
#define G_GEOM_WITHER 1
@ -231,20 +232,6 @@ 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;
void *dev;
};
#ifdef _KERNEL
struct g_kerneldump {

View File

@ -267,98 +267,6 @@ g_bsd_writelabel(struct g_geom *gp, u_char *bootcode)
return(error);
}
/*
* Implement certain ioctls to modify disklabels with. This function
* is called by the event handler thread with topology locked as result
* of the g_post_event() in g_bsd_start(). It is not necessary to keep
* topology locked all the time but make sure to return with topology
* locked as well.
*/
static void
g_bsd_ioctl(void *arg, int flag)
{
struct bio *bp;
struct g_geom *gp;
struct g_ioctl *gio;
u_char *label;
int error;
g_topology_assert();
bp = arg;
if (flag == EV_CANCEL) {
g_io_deliver(bp, ENXIO);
return;
}
gp = bp->bio_to->geom;
gio = (struct g_ioctl *)bp->bio_data;
label = g_malloc(LABELSIZE, M_WAITOK);
/* The disklabel to set is the ioctl argument. */
bsd_disklabel_le_enc(label, gio->data);
/* Validate and modify our slice instance to match. */
error = g_bsd_modify(gp, label); /* Picks up topology lock on success. */
g_free(label);
if (error || gio->cmd == DIOCSDINFO) {
g_io_deliver(bp, error);
return;
}
KASSERT(gio->cmd == DIOCWDINFO, ("Unknown ioctl in g_bsd_ioctl"));
g_io_deliver(bp, g_bsd_writelabel(gp, NULL));
}
/*
* Rewrite the bootblock, which is BBSIZE bytes from the start of the disk.
* We punch down the disklabel where we expect it to be before writing.
*/
static int
g_bsd_diocbsdbb(dev_t dev, u_long cmd __unused, caddr_t data, int fflag __unused, struct thread *td __unused)
{
struct g_geom *gp;
struct g_slicer *gsp;
struct g_bsd_softc *ms;
struct g_consumer *cp;
u_char *buf;
void *p;
int error, i;
uint64_t sum;
/* Get hold of the interesting bits from the bio. */
gp = (void *)dev;
gsp = gp->softc;
ms = gsp->softc;
/* The disklabel to set is the ioctl argument. */
buf = g_malloc(BBSIZE, M_WAITOK);
p = *(void **)data;
error = copyin(p, buf, BBSIZE);
if (!error) {
DROP_GIANT();
g_topology_lock();
/* Validate and modify our slice instance to match. */
error = g_bsd_modify(gp, buf + ms->labeloffset);
if (!error) {
cp = LIST_FIRST(&gp->consumer);
if (ms->labeloffset == ALPHA_LABEL_OFFSET) {
sum = 0;
for (i = 0; i < 63; i++)
sum += le64dec(buf + i * 8);
le64enc(buf + 504, sum);
}
error = g_write_data(cp, 0, buf, BBSIZE);
}
g_topology_unlock();
PICKUP_GIANT();
}
g_free(buf);
return (error);
}
/*
* If the user tries to overwrite our disklabel through an open partition
* or via a magicwrite config call, we end up here and try to prevent
@ -406,6 +314,79 @@ g_bsd_hotwrite(void *arg, int flag)
* * Don't grab the topology lock.
* * Don't call biowait, g_getattr(), g_setattr() or g_read_data()
*/
static int
g_bsd_ioctl(struct g_provider *pp, u_long cmd, void * data, struct thread *td)
{
struct g_geom *gp;
struct g_bsd_softc *ms;
struct g_slicer *gsp;
u_char *label;
int error;
gp = pp->geom;
gsp = gp->softc;
ms = gsp->softc;
switch(cmd) {
case DIOCGDINFO:
/* Return a copy of the disklabel to userland. */
bsd_disklabel_le_dec(ms->label, data, MAXPARTITIONS);
return(0);
case DIOCBSDBB: {
struct g_consumer *cp;
u_char *buf;
void *p;
int error, i;
uint64_t sum;
/* The disklabel to set is the ioctl argument. */
buf = g_malloc(BBSIZE, M_WAITOK);
p = *(void **)data;
error = copyin(p, buf, BBSIZE);
if (!error) {
/* XXX: Rude, but supposedly safe */
DROP_GIANT();
g_topology_lock();
/* Validate and modify our slice instance to match. */
error = g_bsd_modify(gp, buf + ms->labeloffset);
if (!error) {
cp = LIST_FIRST(&gp->consumer);
if (ms->labeloffset == ALPHA_LABEL_OFFSET) {
sum = 0;
for (i = 0; i < 63; i++)
sum += le64dec(buf + i * 8);
le64enc(buf + 504, sum);
}
error = g_write_data(cp, 0, buf, BBSIZE);
}
g_topology_unlock();
PICKUP_GIANT();
}
g_free(buf);
return (error);
}
case DIOCSDINFO:
case DIOCWDINFO: {
label = g_malloc(LABELSIZE, M_WAITOK);
/* The disklabel to set is the ioctl argument. */
bsd_disklabel_le_enc(label, data);
DROP_GIANT();
g_topology_lock();
/* Validate and modify our slice instance to match. */
error = g_bsd_modify(gp, label);
if (error == 0 && cmd == DIOCWDINFO)
error = g_bsd_writelabel(gp, NULL);
g_topology_unlock();
PICKUP_GIANT();
g_free(label);
return(error);
}
default:
return (ENOIOCTL);
}
}
static int
g_bsd_start(struct bio *bp)
@ -413,61 +394,16 @@ g_bsd_start(struct bio *bp)
struct g_geom *gp;
struct g_bsd_softc *ms;
struct g_slicer *gsp;
struct g_ioctl *gio;
int error;
gp = bp->bio_to->geom;
gsp = gp->softc;
ms = gsp->softc;
switch(bp->bio_cmd) {
case BIO_GETATTR:
if (bp->bio_cmd == BIO_GETATTR) {
if (g_handleattr(bp, "BSD::labelsum", ms->labelsum,
sizeof(ms->labelsum)))
return (1);
break;
default:
KASSERT(0 == 1, ("Unknown bio_cmd in g_bsd_start (%d)",
bp->bio_cmd));
}
/* We only handle ioctl(2) requests of the right format. */
if (strcmp(bp->bio_attribute, "GEOM::ioctl"))
return (0);
else if (bp->bio_length != sizeof(*gio))
return (0);
/* Get hold of the ioctl parameters. */
gio = (struct g_ioctl *)bp->bio_data;
switch (gio->cmd) {
case DIOCGDINFO:
/* Return a copy of the disklabel to userland. */
bsd_disklabel_le_dec(ms->label, gio->data, MAXPARTITIONS);
g_io_deliver(bp, 0);
return (1);
case DIOCBSDBB:
gio->func = g_bsd_diocbsdbb;
gio->dev = (void *)gp;
g_io_deliver(bp, EDIRIOCTL);
return (1);
case DIOCSDINFO:
case DIOCWDINFO:
/*
* These we cannot do without the topology lock and some
* some I/O requests. Ask the event-handler to schedule
* us in a less restricted environment.
*/
error = g_post_event(g_bsd_ioctl, bp, M_NOWAIT, gp, NULL);
if (error)
g_io_deliver(bp, error);
/*
* We must return non-zero to indicate that we will deal
* with this bio, even though we have not done so yet.
*/
return (1);
default:
return (0);
}
return (0);
}
/*
@ -559,6 +495,7 @@ g_bsd_taste(struct g_class *mp, struct g_provider *pp, int flags)
* routine which the "slice" code should call at the right time
*/
gp->dumpconf = g_bsd_dumpconf;
gp->ioctl = g_bsd_ioctl;
/* Get the geom_slicer softc from the geom. */
gsp = gp->softc;

View File

@ -279,17 +279,14 @@ g_dev_ioctl(dev_t dev, u_long cmd, caddr_t data, int fflag, struct thread *td)
struct g_kerneldump kd;
int i, error;
u_int u;
struct g_ioctl *gio;
gp = dev->si_drv1;
cp = dev->si_drv2;
gio = NULL;
error = 0;
KASSERT(cp->acr || cp->acw,
("Consumer with zero access count in g_dev_ioctl"));
gio = NULL;
i = IOCPARM_LEN(cmd);
switch (cmd) {
case DIOCGSECTORSIZE:
@ -331,46 +328,14 @@ g_dev_ioctl(dev_t dev, u_long cmd, caddr_t data, int fflag, struct thread *td)
break;
default:
gio = g_malloc(sizeof *gio, M_WAITOK | M_ZERO);
gio->cmd = cmd;
gio->data = data;
gio->fflag = fflag;
gio->td = td;
i = sizeof *gio;
/*
* We always issue ioctls as getattr since the direction of data
* movement in ioctl is no indication of the ioctl being a "set"
* or "get" type ioctl or if such simplistic terms even apply
*/
error = g_io_getattr("GEOM::ioctl", cp, &i, gio);
break;
if (cp->provider->geom->ioctl != NULL) {
error = cp->provider->geom->ioctl(cp->provider, cmd, data, td);
if (error != ENOIOCTL)
return (error);
}
}
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);
}
g_waitidle();
if (gio != NULL && (error == EOPNOTSUPP || error == ENOIOCTL)) {
if (g_debugflags & G_T_TOPOLOGY) {
i = IOCGROUP(cmd);
printf("IOCTL(0x%lx) \"%s\"", cmd, gp->name);
if (i > ' ' && i <= '~')
printf(" '%c'", (int)IOCGROUP(cmd));
else
printf(" 0x%lx", IOCGROUP(cmd));
printf("/%ld ", cmd & 0xff);
if (cmd & IOC_IN)
printf("I");
if (cmd & IOC_OUT)
printf("O");
printf("(%ld) = ENOIOCTL\n", IOCPARM_LEN(cmd));
}
error = ENOTTY;
}
if (gio != NULL)
g_free(gio);
return (error);
}

View File

@ -194,12 +194,29 @@ g_disk_done(struct bio *bp)
mtx_unlock(&g_disk_done_mtx);
}
static int
g_disk_ioctl(struct g_provider *pp, u_long cmd, void * data, struct thread *td)
{
struct g_geom *gp;
struct disk *dp;
int error;
gp = pp->geom;
dp = gp->softc;
if (dp->d_ioctl == NULL)
return (ENOIOCTL);
g_disk_lock_giant(dp);
error = dp->d_ioctl(dp, cmd, data, 0, td);
g_disk_unlock_giant(dp);
return(error);
}
static void
g_disk_start(struct bio *bp)
{
struct bio *bp2, *bp3;
struct disk *dp;
struct g_ioctl *gio;
int error;
off_t off;
@ -264,15 +281,7 @@ g_disk_start(struct bio *bp)
break;
else if (!strcmp(bp->bio_attribute, "GEOM::kerneldump"))
g_disk_kerneldump(bp, dp);
else if ((g_debugflags & G_F_DISKIOCTL) &&
(dp->d_ioctl != NULL) &&
!strcmp(bp->bio_attribute, "GEOM::ioctl") &&
bp->bio_length == sizeof *gio) {
gio = (struct g_ioctl *)bp->bio_data;
gio->dev = dp;
gio->func = (d_ioctl_t *)(dp->d_ioctl);
error = EDIRIOCTL;
} else
else
error = ENOIOCTL;
break;
default:
@ -317,6 +326,7 @@ g_disk_create(void *arg, int flag)
gp = g_new_geomf(&g_disk_class, "%s%d", dp->d_name, dp->d_unit);
gp->start = g_disk_start;
gp->access = g_disk_access;
gp->ioctl = g_disk_ioctl;
gp->softc = dp;
gp->dumpconf = g_disk_dumpconf;
pp = g_new_providerf(gp, "%s", gp->name);

View File

@ -152,42 +152,36 @@ g_mbr_modify(struct g_geom *gp, struct g_mbr_softc *ms, u_char *sec0)
return (0);
}
static void
g_mbr_ioctl(void *arg, int flag)
static int
g_mbr_ioctl(struct g_provider *pp, u_long cmd, void *data, struct thread *td)
{
struct bio *bp;
struct g_geom *gp;
struct g_slicer *gsp;
struct g_mbr_softc *ms;
struct g_ioctl *gio;
struct g_slicer *gsp;
struct g_consumer *cp;
u_char *sec0;
int error;
bp = arg;
if (flag == EV_CANCEL) {
g_io_deliver(bp, ENXIO);
return;
}
gp = bp->bio_to->geom;
gp = pp->geom;
gsp = gp->softc;
ms = gsp->softc;
gio = (struct g_ioctl *)bp->bio_data;
/* The disklabel to set is the ioctl argument. */
sec0 = gio->data;
error = g_mbr_modify(gp, ms, sec0);
if (error) {
g_io_deliver(bp, error);
return;
switch(cmd) {
case DIOCSMBR: {
DROP_GIANT();
g_topology_lock();
/* Validate and modify our slicer instance to match. */
error = g_mbr_modify(gp, ms, data);
cp = LIST_FIRST(&gp->consumer);
error = g_write_data(cp, 0, data, 512);
g_topology_unlock();
PICKUP_GIANT();
return(error);
}
default:
return (ENOIOCTL);
}
cp = LIST_FIRST(&gp->consumer);
error = g_write_data(cp, 0, sec0, 512);
g_io_deliver(bp, error);
}
static int
g_mbr_start(struct bio *bp)
{
@ -195,8 +189,7 @@ g_mbr_start(struct bio *bp)
struct g_geom *gp;
struct g_mbr_softc *mp;
struct g_slicer *gsp;
struct g_ioctl *gio;
int idx, error;
int idx;
pp = bp->bio_to;
idx = pp->index;
@ -211,33 +204,7 @@ g_mbr_start(struct bio *bp)
return (1);
}
/* We only handle ioctl(2) requests of the right format. */
if (strcmp(bp->bio_attribute, "GEOM::ioctl"))
return (0);
else if (bp->bio_length != sizeof(*gio))
return (0);
/* Get hold of the ioctl parameters. */
gio = (struct g_ioctl *)bp->bio_data;
switch (gio->cmd) {
case DIOCSMBR:
/*
* These we cannot do without the topology lock and some
* some I/O requests. Ask the event-handler to schedule
* us in a less restricted environment.
*/
error = g_post_event(g_mbr_ioctl, bp, M_NOWAIT, gp, NULL);
if (error)
g_io_deliver(bp, error);
/*
* We must return non-zero to indicate that we will deal
* with this bio, even though we have not done so yet.
*/
return (1);
default:
return (0);
}
return (0);
}
static void
@ -275,6 +242,7 @@ g_mbr_taste(struct g_class *mp, struct g_provider *pp, int insist)
return (NULL);
g_topology_unlock();
gp->dumpconf = g_mbr_dumpconf;
gp->ioctl = g_mbr_ioctl;
do {
if (gp->rank != 2 && insist == 0)
break;

View File

@ -134,39 +134,34 @@ g_pc98_modify(struct g_geom *gp, struct g_pc98_softc *ms, u_char *sec)
return (0);
}
static void
g_pc98_ioctl(void *arg, int flag)
static int
g_pc98_ioctl(struct g_provider *pp, u_long cmd, void *data, struct thread *td)
{
struct bio *bp;
struct g_geom *gp;
struct g_slicer *gsp;
struct g_pc98_softc *ms;
struct g_ioctl *gio;
struct g_slicer *gsp;
struct g_consumer *cp;
u_char *sec;
int error;
bp = arg;
if (flag == EV_CANCEL) {
g_io_deliver(bp, ENXIO);
return;
}
gp = bp->bio_to->geom;
gp = pp->geom;
gsp = gp->softc;
ms = gsp->softc;
gio = (struct g_ioctl *)bp->bio_data;
/* The disklabel to set is the ioctl argument. */
sec = gio->data;
error = g_pc98_modify(gp, ms, sec);
if (error) {
g_io_deliver(bp, error);
return;
switch(cmd) {
case DIOCSPC98: {
DROP_GIANT();
g_topology_lock();
/* Validate and modify our slicer instance to match. */
error = g_pc98_modify(gp, ms, data);
cp = LIST_FIRST(&gp->consumer);
error = g_write_data(cp, 0, data, 8192);
g_topology_unlock();
PICKUP_GIANT();
return(error);
}
default:
return (ENOIOCTL);
}
cp = LIST_FIRST(&gp->consumer);
error = g_write_data(cp, 0, sec, 8192);
g_io_deliver(bp, error);
}
static int
@ -176,8 +171,7 @@ g_pc98_start(struct bio *bp)
struct g_geom *gp;
struct g_pc98_softc *mp;
struct g_slicer *gsp;
struct g_ioctl *gio;
int idx, error;
int idx;
pp = bp->bio_to;
idx = pp->index;
@ -192,32 +186,7 @@ g_pc98_start(struct bio *bp)
return (1);
}
/* We only handle ioctl(2) requests of the right format. */
if (strcmp(bp->bio_attribute, "GEOM::ioctl"))
return (0);
else if (bp->bio_length != sizeof(*gio))
return (0);
/* Get hold of the ioctl parameters. */
gio = (struct g_ioctl *)bp->bio_data;
switch (gio->cmd) {
case DIOCSPC98:
/*
* These we cannot do without the topology lock and some
* some I/O requests. Ask the event-handler to schedule
* us in a less restricted environment.
*/
error = g_post_event(g_pc98_ioctl, bp, M_NOWAIT, gp, NULL);
if (error)
g_io_deliver(bp, error);
/*
* We must return non-zero to indicate that we will deal
* with this bio, even though we have not done so yet.
*/
return (1);
default:
return (0);
}
return (0);
}
static void
@ -270,6 +239,7 @@ g_pc98_taste(struct g_class *mp, struct g_provider *pp, int flags)
return (NULL);
g_topology_unlock();
gp->dumpconf = g_pc98_dumpconf;
gp->ioctl = g_pc98_ioctl;
do {
if (gp->rank != 2 && flags == G_TF_NORMAL)
break;