diff options
author | trasz <trasz@FreeBSD.org> | 2012-07-07 20:32:21 +0000 |
---|---|---|
committer | trasz <trasz@FreeBSD.org> | 2012-07-07 20:32:21 +0000 |
commit | 1a80449f25e6980accddd15147c01f7c51e59731 (patch) | |
tree | 43f177b6e61b4e88e7b97088b0b3392ef38588c2 /sys/dev/md | |
parent | b5336ab352c11dcd18cdec71746a53518ab2f96f (diff) | |
download | FreeBSD-src-1a80449f25e6980accddd15147c01f7c51e59731.zip FreeBSD-src-1a80449f25e6980accddd15147c01f7c51e59731.tar.gz |
Make it possible to resize md(4) devices.
Reviewed by: kib
Sponsored by: FreeBSD Foundation
Diffstat (limited to 'sys/dev/md')
-rw-r--r-- | sys/dev/md/md.c | 72 |
1 files changed, 71 insertions, 1 deletions
diff --git a/sys/dev/md/md.c b/sys/dev/md/md.c index 8ff52a9..4d4c22c 100644 --- a/sys/dev/md/md.c +++ b/sys/dev/md/md.c @@ -1081,6 +1081,64 @@ mddestroy(struct md_s *sc, struct thread *td) } 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_ioctl *mdio, struct thread *td) 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, caddr_t addr, int flags, struct thread !(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) |