diff -urNp freebsd/src/sbin/mdconfig/mdconfig.8 growfs/sbin/mdconfig/mdconfig.8 --- freebsd/src/sbin/mdconfig/mdconfig.8 2012-06-14 07:15:51.000000000 +0200 +++ growfs/sbin/mdconfig/mdconfig.8 2012-06-13 14:03:42.000000000 +0200 @@ -64,6 +64,11 @@ .Fl u Ar unit .Op Fl o Oo Cm no Oc Ns Ar force .Nm +.Fl r +.Fl u Ar unit +.Fl s Ar size +.Op Fl o Oo Cm no Oc Ns Ar force +.Nm .Fl l .Op Fl n .Op Fl v @@ -85,6 +90,8 @@ This will configure and attach a memory parameters specified and attach it to the system. .It Fl d Detach a memory disk from the system and release all resources. +.It Fl r +Resize a memory disk. .It Fl t Ar type Select the type of the memory disk. .Bl -tag -width "malloc" diff -urNp freebsd/src/sbin/mdconfig/mdconfig.c growfs/sbin/mdconfig/mdconfig.c --- freebsd/src/sbin/mdconfig/mdconfig.c 2012-06-14 07:15:51.000000000 +0200 +++ growfs/sbin/mdconfig/mdconfig.c 2012-06-13 14:03:44.000000000 +0200 @@ -27,7 +27,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sbin/mdconfig/mdconfig.c,v 1.68 2012/03/14 10:10:15 trasz Exp $ + * $FreeBSD: head/sbin/mdconfig/mdconfig.c 230612 2012-01-27 11:48:44Z trasz $ */ #include @@ -54,7 +54,7 @@ #include static struct md_ioctl mdio; -static enum {UNSET, ATTACH, DETACH, LIST} action = UNSET; +static enum {UNSET, ATTACH, DETACH, RESIZE, LIST} action = UNSET; static int nflag; static void usage(void); @@ -81,6 +81,7 @@ usage(void) " [-s size] [-S sectorsize] [-u unit]\n" " [-x sectors/track] [-y heads/cylinder]\n" " mdconfig -d -u unit [-o [no]force]\n" +" mdconfig -r -u unit -s size [-o [no]force]\n" " mdconfig -l [-v] [-n] [-u unit]\n" " mdconfig file\n"); fprintf(stderr, "\t\ttype = {malloc, preload, vnode, swap}\n"); @@ -96,7 +97,7 @@ main(int argc, char **argv) { int ch, fd, i, vflag; char *p; - char *fflag = NULL, *tflag = NULL, *uflag = NULL; + char *fflag = NULL, *sflag = NULL, *tflag = NULL, *uflag = NULL; bzero(&mdio, sizeof(mdio)); mdio.md_file = malloc(PATH_MAX); @@ -108,25 +109,32 @@ main(int argc, char **argv) if (argc == 1) usage(); - while ((ch = getopt(argc, argv, "ab:df:lno:s:S:t:u:vx:y:")) != -1) { + while ((ch = getopt(argc, argv, "ab:df:lno:rs:S:t:u:vx:y:")) != -1) { switch (ch) { case 'a': if (action != UNSET && action != ATTACH) - errx(1, - "-a is mutually exclusive with -d and -l"); + errx(1, "-a is mutually exclusive " + "with -d, -r, and -l"); action = ATTACH; break; case 'd': if (action != UNSET && action != DETACH) - errx(1, - "-d is mutually exclusive with -a and -l"); + errx(1, "-d is mutually exclusive " + "with -a, -r, and -l"); action = DETACH; mdio.md_options |= MD_AUTOUNIT; break; + case 'r': + if (action != UNSET && action != RESIZE) + errx(1, "-r is mutually exclusive " + "with -a, -d, and -l"); + action = RESIZE; + mdio.md_options |= MD_AUTOUNIT; + break; case 'l': if (action != UNSET && action != LIST) - errx(1, - "-l is mutually exclusive with -a and -d"); + errx(1, "-l is mutually exclusive " + "with -a, -r, and -d"); action = LIST; mdio.md_options |= MD_AUTOUNIT; break; @@ -188,6 +196,9 @@ main(int argc, char **argv) mdio.md_sectorsize = strtoul(optarg, &p, 0); break; case 's': + if (sflag != NULL) + errx(1, "-s can be passed only once"); + sflag = optarg; mdio.md_mediasize = (off_t)strtoumax(optarg, &p, 0); if (p == NULL || *p == '\0') mdio.md_mediasize *= DEV_BSIZE; @@ -242,7 +253,7 @@ main(int argc, char **argv) mdio.md_type = MD_VNODE; mdio.md_options |= MD_CLUSTER | MD_AUTOUNIT | MD_COMPRESS; - } else if (mdio.md_mediasize != 0) { + } else if (sflag != NULL) { /* Imply ``-t swap'' */ mdio.md_type = MD_SWAP; mdio.md_options |= MD_CLUSTER | MD_AUTOUNIT | @@ -276,15 +287,15 @@ main(int argc, char **argv) } if ((mdio.md_type == MD_MALLOC || mdio.md_type == MD_SWAP) && - mdio.md_mediasize == 0) + sflag == NULL) errx(1, "must specify -s for -t malloc or -t swap"); if (mdio.md_type == MD_VNODE && mdio.md_file[0] == '\0') errx(1, "must specify -f for -t vnode"); } else { if (mdio.md_sectorsize != 0) errx(1, "-S can only be used with -a"); - if (mdio.md_mediasize != 0) - errx(1, "-s can only be used with -a"); + if (action != RESIZE && sflag != NULL) + errx(1, "-s can only be used with -a and -r"); if (mdio.md_fwsectors != 0) errx(1, "-x can only be used with -a"); if (mdio.md_fwheads != 0) @@ -295,13 +306,20 @@ main(int argc, char **argv) errx(1, "-t can only be used with -a"); if (argc > 0) errx(1, "file can only be used with -a"); - if (action != DETACH && (mdio.md_options & ~MD_AUTOUNIT) != 0) - errx(1, "-o can only be used with -a and -d"); + if ((action != DETACH && action != RESIZE) && + (mdio.md_options & ~MD_AUTOUNIT) != 0) + errx(1, "-o can only be used with -a, -d, and -r"); if (action == DETACH && (mdio.md_options & ~(MD_FORCE | MD_AUTOUNIT)) != 0) errx(1, "only -o [no]force can be used with -d"); + if (action == RESIZE && + (mdio.md_options & ~(MD_FORCE | MD_RESERVE | MD_AUTOUNIT)) != 0) + errx(1, "only -o [no]force and -o [no]reserve can be used with -r"); } + if (action == RESIZE && sflag == NULL) + errx(1, "must specify -s for -r"); + if (action != LIST && vflag == OPT_VERBOSE) errx(1, "-v can only be used with -l"); @@ -333,6 +351,12 @@ main(int argc, char **argv) i = ioctl(fd, MDIOCDETACH, &mdio); if (i < 0) err(1, "ioctl(/dev/%s)", MDCTL_NAME); + } else if (action == RESIZE) { + if (mdio.md_options & MD_AUTOUNIT) + errx(1, "-r requires -u"); + i = ioctl(fd, MDIOCRESIZE, &mdio); + if (i < 0) + err(1, "ioctl(/dev/%s)", MDCTL_NAME); } else if (action == LIST) { if (mdio.md_options & MD_AUTOUNIT) { /* @@ -342,7 +366,6 @@ main(int argc, char **argv) md_list(NULL, OPT_LIST | vflag); } else return (md_query(uflag)); - } else usage(); close(fd); diff -urNp freebsd/src/sys/cam/scsi/scsi_da.c growfs/sys/cam/scsi/scsi_da.c --- freebsd/src/sys/cam/scsi/scsi_da.c 2012-06-24 05:59:55.000000000 +0200 +++ growfs/sys/cam/scsi/scsi_da.c 2012-06-24 06:36:09.000000000 +0200 @@ -2644,6 +2644,8 @@ dasetgeom(struct cam_periph *periph, uin softc->disk->d_flags |= DISKFLAG_CANDELETE; else softc->disk->d_flags &= ~DISKFLAG_CANDELETE; + + disk_resize(softc->disk); } static void diff -urNp freebsd/src/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa.c growfs/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa.c --- freebsd/src/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa.c 2012-06-14 07:22:23.000000000 +0200 +++ growfs/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa.c 2012-06-14 09:48:39.000000000 +0200 @@ -5466,6 +5466,13 @@ spa_async_autoexpand(spa_t *spa, vdev_t if (!vd->vdev_ops->vdev_op_leaf || vd->vdev_physpath == NULL) return; + /* XXX - L2ARC 1.0 does not support expansion */ + if (vd->vdev_aux) + return; + + vdev_reopen(vd); + spa_async_request(spa, SPA_ASYNC_CONFIG_UPDATE); + physpath = kmem_zalloc(MAXPATHLEN, KM_SLEEP); (void) snprintf(physpath, MAXPATHLEN, "/devices%s", vd->vdev_physpath); diff -urNp freebsd/src/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_geom.c growfs/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_geom.c --- freebsd/src/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_geom.c 2012-06-14 07:22:31.000000000 +0200 +++ growfs/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/vdev_geom.c 2012-06-14 09:48:40.000000000 +0200 @@ -86,6 +86,18 @@ vdev_geom_orphan(struct g_consumer *cp) spa_async_request(vd->vdev_spa, SPA_ASYNC_REMOVE); } +static void +vdev_geom_resize(struct g_consumer *cp) +{ + vdev_t *vd; + + g_topology_assert(); + + vd = cp->private; + + spa_async_request(vd->vdev_spa, SPA_ASYNC_AUTOEXPAND); +} + static struct g_consumer * vdev_geom_attach(struct g_provider *pp) { @@ -106,6 +118,7 @@ vdev_geom_attach(struct g_provider *pp) if (gp == NULL) { gp = g_new_geomf(&zfs_vdev_class, "zfs::vdev"); gp->orphan = vdev_geom_orphan; + gp->resize = vdev_geom_resize; cp = g_new_consumer(gp); if (g_attach(cp, pp) != 0) { g_wither_geom(gp, ENXIO); diff -urNp freebsd/src/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zvol.c growfs/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zvol.c --- freebsd/src/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zvol.c 2012-06-14 07:22:39.000000000 +0200 +++ growfs/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zvol.c 2012-06-13 14:20:03.000000000 +0200 @@ -177,15 +177,7 @@ zvol_size_changed(zvol_state_t *zv) pp = zv->zv_provider; if (pp == NULL) return; - if (zv->zv_volsize == pp->mediasize) - return; - /* - * Changing provider size is not really supported by GEOM, but it - * should be safe when provider is closed. - */ - if (zv->zv_total_opens > 0) - return; - pp->mediasize = zv->zv_volsize; + g_resize_provider(pp, zv->zv_volsize); #endif /* !sun */ } diff -urNp freebsd/src/sys/dev/md/md.c growfs/sys/dev/md/md.c --- freebsd/src/sys/dev/md/md.c 2012-06-14 07:42:14.000000000 +0200 +++ growfs/sys/dev/md/md.c 2012-06-13 14:48:51.000000000 +0200 @@ -6,7 +6,7 @@ * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp * ---------------------------------------------------------------------------- * - * $FreeBSD: src/sys/dev/md/md.c,v 1.203 2011/12/13 00:38:50 eadler Exp $ + * $FreeBSD: head/sys/dev/md/md.c 228449 2011-12-13 00:38:50Z eadler $ * */ @@ -1081,6 +1081,64 @@ mddestroy(struct md_s *sc, struct thread } static int +mdresize(struct md_s *sc, struct md_ioctl *mdio) +{ + int error, res; + vm_pindex_t oldpages, newpages; + + switch (sc->type) { + case MD_VNODE: + break; + case MD_SWAP: + if (mdio->md_mediasize == 0 || + (mdio->md_mediasize % PAGE_SIZE) != 0) + return (EDOM); + oldpages = OFF_TO_IDX(round_page(sc->mediasize)); + newpages = OFF_TO_IDX(round_page(mdio->md_mediasize)); + if (newpages < oldpages) { + VM_OBJECT_LOCK(sc->object); + vm_object_page_remove(sc->object, newpages, 0, 0); + swap_pager_freespace(sc->object, newpages, + oldpages - newpages); + swap_release_by_cred(IDX_TO_OFF(oldpages - + newpages), sc->cred); + sc->object->charge = IDX_TO_OFF(newpages); + sc->object->size = newpages; + VM_OBJECT_UNLOCK(sc->object); + } else if (newpages > oldpages) { + res = swap_reserve_by_cred(IDX_TO_OFF(newpages - + oldpages), sc->cred); + if (!res) + return (ENOMEM); + if ((mdio->md_options & MD_RESERVE) || + (sc->flags & MD_RESERVE)) { + error = swap_pager_reserve(sc->object, + oldpages, newpages - oldpages); + if (error < 0) { + swap_release_by_cred( + IDX_TO_OFF(newpages - oldpages), + sc->cred); + return (EDOM); + } + } + VM_OBJECT_LOCK(sc->object); + sc->object->charge = IDX_TO_OFF(newpages); + sc->object->size = newpages; + VM_OBJECT_UNLOCK(sc->object); + } + break; + default: + return (EOPNOTSUPP); + } + + sc->mediasize = mdio->md_mediasize; + g_topology_lock(); + g_resize_provider(sc->pp, sc->mediasize); + g_topology_unlock(); + return (0); +} + +static int mdcreate_swap(struct md_s *sc, struct md_ioctl *mdio, struct thread *td) { vm_ooffset_t npage; @@ -1108,7 +1166,7 @@ mdcreate_swap(struct md_s *sc, struct md VM_PROT_DEFAULT, 0, td->td_ucred); if (sc->object == NULL) return (ENOMEM); - sc->flags = mdio->md_options & MD_FORCE; + sc->flags = mdio->md_options & (MD_FORCE | MD_RESERVE); if (mdio->md_options & MD_RESERVE) { if (swap_pager_reserve(sc->object, 0, npage) < 0) { error = EDOM; @@ -1217,6 +1275,18 @@ xmdctlioctl(struct cdev *dev, u_long cmd !(mdio->md_options & MD_FORCE)) return (EBUSY); return (mddestroy(sc, td)); + case MDIOCRESIZE: + if ((mdio->md_options & ~(MD_FORCE | MD_RESERVE)) != 0) + return (EINVAL); + + sc = mdfind(mdio->md_unit); + if (sc == NULL) + return (ENOENT); + if (mdio->md_mediasize < sc->mediasize && + !(sc->flags & MD_FORCE) && + !(mdio->md_options & MD_FORCE)) + return (EBUSY); + return (mdresize(sc, mdio)); case MDIOCQUERY: sc = mdfind(mdio->md_unit); if (sc == NULL) diff -urNp freebsd/src/sys/geom/bde/g_bde.c growfs/sys/geom/bde/g_bde.c --- freebsd/src/sys/geom/bde/g_bde.c 2012-06-14 07:48:48.000000000 +0200 +++ growfs/sys/geom/bde/g_bde.c 2012-06-13 15:00:07.000000000 +0200 @@ -77,19 +77,15 @@ g_bde_orphan(struct g_consumer *cp) struct g_geom *gp; struct g_provider *pp; struct g_bde_softc *sc; - int error; g_trace(G_T_TOPOLOGY, "g_bde_orphan(%p/%s)", cp, cp->provider->name); g_topology_assert(); - KASSERT(cp->provider->error != 0, - ("g_bde_orphan with error == 0")); gp = cp->geom; sc = gp->softc; gp->flags |= G_GEOM_WITHER; - error = cp->provider->error; LIST_FOREACH(pp, &gp->provider, provider) - g_orphan_provider(pp, error); + g_orphan_provider(pp, ENXIO); bzero(sc, sizeof(struct g_bde_softc)); /* destroy evidence */ return; } diff -urNp freebsd/src/sys/geom/geom.h growfs/sys/geom/geom.h --- freebsd/src/sys/geom/geom.h 2012-06-24 06:01:31.000000000 +0200 +++ growfs/sys/geom/geom.h 2012-06-24 06:38:35.000000000 +0200 @@ -79,6 +79,7 @@ typedef void g_attrchanged_t (struct g_c typedef void g_provgone_t (struct g_provider *); typedef void g_dumpconf_t (struct sbuf *, const char *indent, struct g_geom *, struct g_consumer *, struct g_provider *); +typedef void g_resize_t(struct g_consumer *cp); /* * The g_class structure describes a transformation class. In other words @@ -108,8 +109,8 @@ struct g_class { g_access_t *access; g_orphan_t *orphan; g_ioctl_t *ioctl; + g_resize_t *resize; void *spare1; - void *spare2; /* * The remaining elements are private */ @@ -140,7 +141,7 @@ struct g_geom { g_access_t *access; g_orphan_t *orphan; g_ioctl_t *ioctl; - void *spare0; + g_resize_t *resize; void *spare1; void *softc; unsigned flags; @@ -267,6 +268,7 @@ int g_handleattr_str(struct bio *bp, con struct g_consumer * g_new_consumer(struct g_geom *gp); struct g_geom * g_new_geomf(struct g_class *mp, const char *fmt, ...); struct g_provider * g_new_providerf(struct g_geom *gp, const char *fmt, ...); +void g_resize_provider(struct g_provider *pp, off_t size); int g_retaste(struct g_class *mp); void g_spoil(struct g_provider *pp, struct g_consumer *cp); int g_std_access(struct g_provider *pp, int dr, int dw, int de); diff -urNp freebsd/src/sys/geom/geom_aes.c growfs/sys/geom/geom_aes.c --- freebsd/src/sys/geom/geom_aes.c 2012-06-14 07:48:51.000000000 +0200 +++ growfs/sys/geom/geom_aes.c 2012-06-13 15:00:12.000000000 +0200 @@ -241,12 +241,10 @@ g_aes_orphan(struct g_consumer *cp) g_trace(G_T_TOPOLOGY, "g_aes_orphan(%p/%s)", cp, cp->provider->name); g_topology_assert(); - KASSERT(cp->provider->error != 0, - ("g_aes_orphan with error == 0")); gp = cp->geom; sc = gp->softc; - g_wither_geom(gp, cp->provider->error); + g_wither_geom(gp, ENXIO); bzero(sc, sizeof(struct g_aes_softc)); /* destroy evidence */ g_free(sc); return; diff -urNp freebsd/src/sys/geom/geom_dev.c growfs/sys/geom/geom_dev.c --- freebsd/src/sys/geom/geom_dev.c 2012-06-14 07:48:51.000000000 +0200 +++ growfs/sys/geom/geom_dev.c 2012-06-13 15:00:17.000000000 +0200 @@ -34,7 +34,7 @@ */ #include -__FBSDID("$FreeBSD: src/sys/geom/geom_dev.c,v 1.113 2011/11/14 19:32:05 mav Exp $"); +__FBSDID("$FreeBSD: head/sys/geom/geom_dev.c 227510 2011-11-14 19:32:05Z mav $"); #include #include @@ -456,7 +456,8 @@ g_dev_strategy(struct bio *bp) KASSERT(bp->bio_cmd == BIO_READ || bp->bio_cmd == BIO_WRITE || - bp->bio_cmd == BIO_DELETE, + bp->bio_cmd == BIO_DELETE || + bp->bio_cmd == BIO_FLUSH, ("Wrong bio_cmd bio=%p cmd=%d", bp, bp->bio_cmd)); dev = bp->bio_dev; cp = dev->si_drv2; @@ -520,7 +521,7 @@ g_dev_orphan(struct g_consumer *cp) if (dev->si_flags & SI_DUMPDEV) set_dumper(NULL); - /* Destroy the struct cdev *so we get no more requests */ + /* Destroy the struct cdev, so we get no more requests */ destroy_dev(dev); /* Wait for the cows to come home */ diff -urNp freebsd/src/sys/geom/geom_disk.c growfs/sys/geom/geom_disk.c --- freebsd/src/sys/geom/geom_disk.c 2012-06-24 06:01:31.000000000 +0200 +++ growfs/sys/geom/geom_disk.c 2012-06-24 06:38:35.000000000 +0200 @@ -48,6 +48,7 @@ __FBSDID("$FreeBSD: src/sys/geom/geom_di #include #include #include +#include #include #include @@ -65,6 +66,7 @@ struct g_disk_softc { struct sysctl_oid *sysctl_tree; char led[64]; uint32_t state; + struct task resize_task; }; static struct mtx g_disk_done_mtx; @@ -445,6 +447,27 @@ g_disk_dumpconf(struct sbuf *sb, const c } static void +g_disk_resize_task(void *context, int pending) +{ + struct g_geom *gp; + struct g_provider *pp; + struct disk *dp; + struct g_disk_softc *sc; + + sc = (struct g_disk_softc *)context; + dp = sc->dp; + gp = dp->d_geom; + + LIST_FOREACH(pp, &gp->provider, provider) { + if (pp->sectorsize != 0 && + pp->sectorsize != dp->d_sectorsize) + g_wither_provider(pp, ENXIO); + else + g_resize_provider(pp, dp->d_mediasize); + } +} + +static void g_disk_create(void *arg, int flag) { struct g_geom *gp; @@ -484,6 +507,7 @@ g_disk_create(void *arg, int flag) CTLFLAG_RW | CTLFLAG_TUN, sc->led, sizeof(sc->led), "LED name"); } + TASK_INIT(&sc->resize_task, 0, g_disk_resize_task, sc); pp->private = sc; dp->d_geom = gp; g_error_provider(pp, 0); @@ -627,6 +651,24 @@ disk_attr_changed(struct disk *dp, const (void)g_attr_changed(pp, attr, flag); } +void +disk_resize(struct disk *dp) +{ + struct g_geom *gp; + struct g_disk_softc *sc; + int error; + + gp = dp->d_geom; + + if (gp == NULL) + return; + + sc = gp->softc; + + error = taskqueue_enqueue(taskqueue_thread, &sc->resize_task); + KASSERT(error == 0, ("taskqueue_enqueue(9) failed.")); +} + static void g_kern_disks(void *p, int flag __unused) { diff -urNp freebsd/src/sys/geom/geom_disk.h growfs/sys/geom/geom_disk.h --- freebsd/src/sys/geom/geom_disk.h 2012-06-24 06:01:32.000000000 +0200 +++ growfs/sys/geom/geom_disk.h 2012-06-24 06:38:35.000000000 +0200 @@ -109,6 +109,7 @@ void disk_create(struct disk *disk, int void disk_destroy(struct disk *disk); void disk_gone(struct disk *disk); void disk_attr_changed(struct disk *dp, const char *attr, int flag); +void disk_resize(struct disk *dp); #define DISK_VERSION_00 0x58561059 #define DISK_VERSION_01 0x5856105a diff -urNp freebsd/src/sys/geom/geom_slice.c growfs/sys/geom/geom_slice.c --- freebsd/src/sys/geom/geom_slice.c 2012-06-14 07:48:52.000000000 +0200 +++ growfs/sys/geom/geom_slice.c 2012-06-13 15:00:20.000000000 +0200 @@ -522,10 +522,8 @@ g_slice_orphan(struct g_consumer *cp) g_trace(G_T_TOPOLOGY, "g_slice_orphan(%p/%s)", cp, cp->provider->name); g_topology_assert(); - KASSERT(cp->provider->error != 0, - ("g_slice_orphan with error == 0")); /* XXX: Not good enough we leak the softc and its suballocations */ g_slice_free(cp->geom->softc); - g_wither_geom(cp->geom, cp->provider->error); + g_wither_geom(cp->geom, ENXIO); } diff -urNp freebsd/src/sys/geom/geom_subr.c growfs/sys/geom/geom_subr.c --- freebsd/src/sys/geom/geom_subr.c 2012-06-24 06:01:32.000000000 +0200 +++ growfs/sys/geom/geom_subr.c 2012-06-24 06:38:35.000000000 +0200 @@ -68,9 +68,11 @@ static struct g_tailq_head geoms = TAILQ char *g_wait_event, *g_wait_up, *g_wait_down, *g_wait_sim; struct g_hh00 { - struct g_class *mp; - int error; - int post; + struct g_class *mp; + struct g_provider *pp; + off_t size; + int error; + int post; }; /* @@ -356,6 +358,7 @@ g_new_geomf(struct g_class *mp, const ch gp->access = mp->access; gp->orphan = mp->orphan; gp->ioctl = mp->ioctl; + gp->resize = mp->resize; return (gp); } @@ -555,7 +558,6 @@ g_new_provider_event(void *arg, int flag } } - struct g_provider * g_new_providerf(struct g_geom *gp, const char *fmt, ...) { @@ -601,6 +603,80 @@ g_error_provider(struct g_provider *pp, pp->error = error; } +static void +g_resize_provider_event(void *arg, int flag) +{ + struct g_hh00 *hh; + struct g_class *mp; + struct g_geom *gp; + struct g_provider *pp; + struct g_consumer *cp, *cp2; + off_t size; + + g_topology_assert(); + if (flag == EV_CANCEL) + return; + if (g_shutdown) + return; + + hh = arg; + pp = hh->pp; + size = hh->size; +#if 0 /* XXX */ + g_free(hh); + hh = NULL; +#endif + + G_VALID_PROVIDER(pp); + g_trace(G_T_TOPOLOGY, "g_resize_provider_event(%p)", pp); + + LIST_FOREACH_SAFE(cp, &pp->consumers, consumers, cp2) { + gp = cp->geom; + if (gp->resize == NULL && size < pp->mediasize) + cp->geom->orphan(cp); + } + + pp->mediasize = size; + + LIST_FOREACH_SAFE(cp, &pp->consumers, consumers, cp2) { + gp = cp->geom; + if (gp->resize != NULL) + gp->resize(cp); + } + + /* + * After resizing, the previously invalid GEOM class metadata + * might become valid. This means we should retaste. + */ + LIST_FOREACH(mp, &g_classes, class) { + if (mp->taste == NULL) + continue; + LIST_FOREACH(cp, &pp->consumers, consumers) + if (cp->geom->class == mp) + break; + if (cp != NULL) + continue; + mp->taste(mp, pp, 0); + g_topology_assert(); + } +} + +void +g_resize_provider(struct g_provider *pp, off_t size) +{ + struct g_hh00 *hh; + + G_VALID_PROVIDER(pp); + + if (size == pp->mediasize) + return; + + hh = g_malloc(sizeof *hh, M_WAITOK | M_ZERO); + hh->pp = pp; + hh->size = size; + g_post_event(g_resize_provider_event, hh, M_WAITOK, NULL); +} + struct g_provider * g_provider_by_name(char const *arg) { diff -urNp freebsd/src/sys/geom/mountver/g_mountver.c growfs/sys/geom/mountver/g_mountver.c --- freebsd/src/sys/geom/mountver/g_mountver.c 2012-06-14 07:48:55.000000000 +0200 +++ growfs/sys/geom/mountver/g_mountver.c 2012-06-13 15:00:23.000000000 +0200 @@ -59,6 +59,7 @@ static eventhandler_tag g_mountver_pre_s static void g_mountver_queue(struct bio *bp); static void g_mountver_orphan(struct g_consumer *cp); +static void g_mountver_resize(struct g_consumer *cp); static int g_mountver_destroy(struct g_geom *gp, boolean_t force); static g_taste_t g_mountver_taste; static int g_mountver_destroy_geom(struct gctl_req *req, struct g_class *mp, @@ -257,6 +258,7 @@ g_mountver_create(struct gctl_req *req, gp->softc = sc; gp->start = g_mountver_start; gp->orphan = g_mountver_orphan; + gp->resize = g_mountver_resize; gp->access = g_mountver_access; gp->dumpconf = g_mountver_dumpconf; @@ -457,6 +459,18 @@ g_mountver_orphan(struct g_consumer *cp) G_MOUNTVER_DEBUG(0, "%s is offline. Mount verification in progress.", sc->sc_provider_name); } +static void +g_mountver_resize(struct g_consumer *cp) +{ + struct g_geom *gp; + struct g_provider *pp; + + gp = cp->geom; + + LIST_FOREACH(pp, &gp->provider, provider) + g_resize_provider(pp, cp->provider->mediasize); +} + static int g_mountver_ident_matches(struct g_geom *gp) { diff -urNp freebsd/src/sys/geom/nop/g_nop.c growfs/sys/geom/nop/g_nop.c --- freebsd/src/sys/geom/nop/g_nop.c 2012-06-14 07:48:59.000000000 +0200 +++ growfs/sys/geom/nop/g_nop.c 2012-06-13 15:00:24.000000000 +0200 @@ -72,6 +72,30 @@ g_nop_orphan(struct g_consumer *cp) } static void +g_nop_resize(struct g_consumer *cp) +{ + struct g_nop_softc *sc; + struct g_geom *gp; + struct g_provider *pp; + off_t size; + + g_topology_assert(); + + gp = cp->geom; + sc = gp->softc; + + if (sc->sc_explicitsize != 0) + return; + if (cp->provider->mediasize < sc->sc_offset) { + g_nop_destroy(gp, 1); + return; + } + size = cp->provider->mediasize - sc->sc_offset; + LIST_FOREACH(pp, &gp->provider, provider) + g_resize_provider(pp, size); +} + +static void g_nop_start(struct bio *bp) { struct g_nop_softc *sc; @@ -146,6 +170,7 @@ g_nop_create(struct gctl_req *req, struc struct g_consumer *cp; char name[64]; int error; + off_t explicitsize; g_topology_assert(); @@ -165,6 +190,7 @@ g_nop_create(struct gctl_req *req, struc gctl_error(req, "Invalid offset for provider %s.", pp->name); return (EINVAL); } + explicitsize = size; if (size == 0) size = pp->mediasize - offset; if (offset + size > pp->mediasize) { @@ -192,6 +218,7 @@ g_nop_create(struct gctl_req *req, struc gp = g_new_geomf(mp, name); sc = g_malloc(sizeof(*sc), M_WAITOK); sc->sc_offset = offset; + sc->sc_explicitsize = explicitsize; sc->sc_error = ioerror; sc->sc_rfailprob = rfailprob; sc->sc_wfailprob = wfailprob; @@ -202,6 +229,7 @@ g_nop_create(struct gctl_req *req, struc gp->softc = sc; gp->start = g_nop_start; gp->orphan = g_nop_orphan; + gp->resize = g_nop_resize; gp->access = g_nop_access; gp->dumpconf = g_nop_dumpconf; diff -urNp freebsd/src/sys/geom/nop/g_nop.h growfs/sys/geom/nop/g_nop.h --- freebsd/src/sys/geom/nop/g_nop.h 2012-06-14 07:49:00.000000000 +0200 +++ growfs/sys/geom/nop/g_nop.h 2012-06-13 15:00:24.000000000 +0200 @@ -57,6 +57,7 @@ struct g_nop_softc { int sc_error; off_t sc_offset; + off_t sc_explicitsize; u_int sc_rfailprob; u_int sc_wfailprob; uintmax_t sc_reads; diff -urNp freebsd/src/sys/geom/part/g_part.c growfs/sys/geom/part/g_part.c --- freebsd/src/sys/geom/part/g_part.c 2012-06-14 07:49:01.000000000 +0200 +++ growfs/sys/geom/part/g_part.c 2012-06-14 09:50:37.000000000 +0200 @@ -130,6 +130,7 @@ static g_taste_t g_part_taste; static g_access_t g_part_access; static g_dumpconf_t g_part_dumpconf; static g_orphan_t g_part_orphan; +static g_resize_t g_part_resize; static g_spoiled_t g_part_spoiled; static g_start_t g_part_start; @@ -146,6 +147,7 @@ static struct g_class g_part_class = { .access = g_part_access, .dumpconf = g_part_dumpconf, .orphan = g_part_orphan, + .resize = g_part_resize, .spoiled = g_part_spoiled, .start = g_part_start, }; @@ -1257,6 +1259,7 @@ g_part_ctl_resize(struct gctl_req *req, struct sbuf *sb; quad_t end; int error; + off_t mediasize; gp = gpp->gpp_geom; G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name)); @@ -1301,8 +1304,11 @@ g_part_ctl_resize(struct gctl_req *req, pp = entry->gpe_pp; if ((g_debugflags & 16) == 0 && (pp->acr > 0 || pp->acw > 0 || pp->ace > 0)) { - gctl_error(req, "%d", EBUSY); - return (EBUSY); + if (entry->gpe_end - entry->gpe_start > gpp->gpp_size) { + /* Deny shrinking an opened partition. */ + gctl_error(req, "%d", EBUSY); + return (EBUSY); + } } error = G_PART_RESIZE(table, entry, gpp); @@ -1315,8 +1321,9 @@ g_part_ctl_resize(struct gctl_req *req, entry->gpe_modified = 1; /* update mediasize of changed provider */ - pp->mediasize = (entry->gpe_end - entry->gpe_start + 1) * + mediasize = (entry->gpe_end - entry->gpe_start + 1) * pp->sectorsize; + g_resize_provider(pp, mediasize); /* Provide feedback if so requested. */ if (gpp->gpp_parms & G_PART_PARM_OUTPUT) { @@ -2043,6 +2050,106 @@ g_part_orphan(struct g_consumer *cp) g_part_wither(cp->geom, pp->error); } +/* + * Reread a partition table and compare with the previous one. + * If the new table is invalid or differs from the previous one, + * orphan. Otherwise replace the previous one. We cannot just + * destroy the old table and read the new one, as it would orphan + * all providers. + */ +static void +g_part_resize(struct g_consumer *cp) +{ + struct g_part_entry *oldentry, *entry; + struct g_part_table *oldtable, *table; + struct g_geom *gp; + struct g_provider *pp; + int error; + + G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, cp->provider->name)); + g_topology_assert(); + + gp = cp->geom; + pp = cp->provider; + + /* + * Nothing to do if there was no table in the first place. + */ + if (gp->softc == NULL) + return; + + error = g_access(cp, 1, 0, 0); + if (error) { + g_part_wither(gp, ENXIO); + return; + } + + oldtable = gp->softc; + gp->softc = NULL; + + g_topology_unlock(); + + error = g_part_probe(gp, cp, oldtable->gpt_depth); + if (error) + goto fail_lock; + table = gp->softc; + g_part_geometry(table, cp, pp->mediasize / pp->sectorsize); + error = G_PART_READ(table, cp); + if (error) + goto fail_lock; + error = g_part_check_integrity(table, cp); + if (error) + goto fail_lock; + + g_topology_lock(); + + /* + * Compare the tables. These checks could probably be + * relaxed somewhat; their purpose is to make sure we don't + * assign providers to wrong entries. + */ + oldentry = LIST_FIRST(&oldtable->gpt_entry); + LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) { + if (oldentry == NULL || + oldentry->gpe_start != entry->gpe_start || + oldentry->gpe_end != entry->gpe_end || + oldentry->gpe_index != entry->gpe_index) + goto fail; + oldentry = LIST_NEXT(oldentry, gpe_entry); + } + + /* + * Fix up pointers. + */ + oldentry = LIST_FIRST(&oldtable->gpt_entry); + LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) { + entry->gpe_offset = oldentry->gpe_offset; + entry->gpe_pp = oldentry->gpe_pp; + oldentry->gpe_pp = NULL; + entry->gpe_pp->private = entry; + oldentry = LIST_NEXT(oldentry, gpe_entry); + } + + /* + * Free the old table. + */ + while ((oldentry = LIST_FIRST(&oldtable->gpt_entry)) != NULL) { + LIST_REMOVE(oldentry, gpe_entry); + g_free(oldentry); + } + + g_access(cp, -1, 0, 0); + + return; +fail_lock: + g_topology_lock(); +fail: + g_access(cp, -1, 0, 0); + g_part_wither(gp, ENXIO); + gp->softc = oldtable; + g_part_wither(gp, ENXIO); +} + static void g_part_spoiled(struct g_consumer *cp) { diff -urNp freebsd/src/sys/geom/uncompress/g_uncompress.c growfs/sys/geom/uncompress/g_uncompress.c --- freebsd/src/sys/geom/uncompress/g_uncompress.c 2012-06-14 07:49:10.000000000 +0200 +++ growfs/sys/geom/uncompress/g_uncompress.c 2012-06-13 15:00:50.000000000 +0200 @@ -406,13 +406,11 @@ g_uncompress_orphan(struct g_consumer *c g_trace(G_T_TOPOLOGY, "%s(%p/%s)", __func__, cp, cp->provider->name); g_topology_assert(); - KASSERT(cp->provider->error != 0, - ("g_uncompress_orphan with error == 0")); gp = cp->geom; g_uncompress_softc_free(gp->softc, gp); gp->softc = NULL; - g_wither_geom(gp, cp->provider->error); + g_wither_geom(gp, ENXIO); } static int diff -urNp freebsd/src/sys/geom/uzip/g_uzip.c growfs/sys/geom/uzip/g_uzip.c --- freebsd/src/sys/geom/uzip/g_uzip.c 2012-06-14 07:49:10.000000000 +0200 +++ growfs/sys/geom/uzip/g_uzip.c 2012-06-13 15:00:51.000000000 +0200 @@ -313,13 +313,11 @@ g_uzip_orphan(struct g_consumer *cp) g_trace(G_T_TOPOLOGY, "g_uzip_orphan(%p/%s)", cp, cp->provider->name); g_topology_assert(); - KASSERT(cp->provider->error != 0, - ("g_uzip_orphan with error == 0")); gp = cp->geom; g_uzip_softc_free(gp->softc, gp); gp->softc = NULL; - g_wither_geom(gp, cp->provider->error); + g_wither_geom(gp, ENXIO); } static int diff -urNp freebsd/src/sys/sys/mdioctl.h growfs/sys/sys/mdioctl.h --- freebsd/src/sys/sys/mdioctl.h 2012-06-14 07:58:58.000000000 +0200 +++ growfs/sys/sys/mdioctl.h 2012-06-13 15:14:52.000000000 +0200 @@ -37,7 +37,7 @@ * * From: src/sys/sys/vnioctl.h,v 1.4 * - * $FreeBSD: src/sys/sys/mdioctl.h,v 1.22 2012/01/25 11:28:18 trasz Exp $ + * $FreeBSD: head/sys/sys/mdioctl.h 230536 2012-01-25 11:28:18Z trasz $ */ #ifndef _SYS_MDIOCTL_H_ @@ -79,6 +79,7 @@ struct md_ioctl { #define MDIOCDETACH _IOWR('m', 1, struct md_ioctl) /* detach disk */ #define MDIOCQUERY _IOWR('m', 2, struct md_ioctl) /* query status */ #define MDIOCLIST _IOWR('m', 3, struct md_ioctl) /* query status */ +#define MDIOCRESIZE _IOWR('m', 4, struct md_ioctl) /* resize disk */ #define MD_CLUSTER 0x01 /* Don't cluster */ #define MD_RESERVE 0x02 /* Pre-reserve swap */