diff options
author | phk <phk@FreeBSD.org> | 2005-09-19 06:55:27 +0000 |
---|---|---|
committer | phk <phk@FreeBSD.org> | 2005-09-19 06:55:27 +0000 |
commit | 5376d97a97f0654a22509676652f1dd1e0b27370 (patch) | |
tree | d3bb7ebd11516215d47bd2c503786299ef5b3ba1 /sys | |
parent | d94a6a6215e73e7d38d961419dd075d70e9ed70d (diff) | |
download | FreeBSD-src-5376d97a97f0654a22509676652f1dd1e0b27370.zip FreeBSD-src-5376d97a97f0654a22509676652f1dd1e0b27370.tar.gz |
Fix configuration locking in MD.
Remove md_mtx.
Remove GIANT from the mdctl device driver and avoid DROP_GIANT,
PICKUP_GIANT and geom events since we can call into GEOM directly
now.
Pick up Giant around vn_close().
Apply an exclusive sx around mdctls ioctl and preloading to protect
lists etc..
Don't initialize our lock (md_mtx or md_sx) from a
SYSINIT when there is a perfectly good pair of _fini/_init
functions to do it from.
Prune any final fractional sector from the mediasize to
keep GEOM happy.
Cleanups:
Unify MDIOVERSION check in (x)mdctlioctl()
Add pointer to start() routine to softc to eliminate a switch{}
Inline guts of mddetach().
Always pass error pointer to mdnew(), simplify implementation.
Diffstat (limited to 'sys')
-rw-r--r-- | sys/dev/md/md.c | 196 |
1 files changed, 80 insertions, 116 deletions
diff --git a/sys/dev/md/md.c b/sys/dev/md/md.c index 230f93b..6c40191 100644 --- a/sys/dev/md/md.c +++ b/sys/dev/md/md.c @@ -68,6 +68,7 @@ #include <sys/malloc.h> #include <sys/mdioctl.h> #include <sys/mutex.h> +#include <sys/sx.h> #include <sys/namei.h> #include <sys/proc.h> #include <sys/queue.h> @@ -112,14 +113,12 @@ static g_access_t g_md_access; static int mdunits; static struct cdev *status_dev = 0; -static struct mtx md_mtx; -MTX_SYSINIT(md_mtx, &md_mtx, "md", MTX_DEF); +static struct sx md_sx; static d_ioctl_t mdctlioctl; static struct cdevsw mdctl_cdevsw = { .d_version = D_VERSION, - .d_flags = D_NEEDGIANT, .d_ioctl = mdctlioctl, .d_name = MD_NAME, }; @@ -166,6 +165,7 @@ struct md_s { struct proc *procp; struct g_geom *gp; struct g_provider *pp; + int (*start)(struct md_s *sc, struct bio *bp); /* MD_MALLOC related fields */ struct indir *indir; @@ -184,8 +184,6 @@ struct md_s { vm_object_t object; }; -static int mddestroy(struct md_s *sc, struct thread *td); - static struct indir * new_indir(u_int shift) { @@ -385,8 +383,6 @@ g_md_start(struct bio *bp) wakeup(sc); } - - static int mdstart_malloc(struct md_s *sc, struct bio *bp) { @@ -481,6 +477,7 @@ mdstart_vnode(struct md_s *sc, struct bio *bp) struct iovec aiov; struct mount *mp; + mtx_assert(&Giant, MA_OWNED); /* * VNODE I/O * @@ -652,17 +649,16 @@ md_kthread(void *arg) } for (;;) { + if (sc->flags & MD_SHUTDOWN) { + sc->procp = NULL; + wakeup(&sc->procp); + if (hasgiant) + mtx_unlock(&Giant); + kthread_exit(0); + } mtx_lock(&sc->queue_mtx); bp = bioq_takefirst(&sc->bio_queue); if (!bp) { - if (sc->flags & MD_SHUTDOWN) { - mtx_unlock(&sc->queue_mtx); - sc->procp = NULL; - wakeup(&sc->procp); - if (hasgiant) - mtx_unlock(&Giant); - kthread_exit(0); - } msleep(sc, &sc->queue_mtx, PRIBIO | PDROP, "mdwait", 0); continue; } @@ -677,25 +673,7 @@ md_kthread(void *arg) else error = EOPNOTSUPP; } else { - switch (sc->type) { - case MD_MALLOC: - error = mdstart_malloc(sc, bp); - break; - case MD_PRELOAD: - error = mdstart_preload(sc, bp); - break; - case MD_VNODE: - mtx_assert(&Giant, MA_OWNED); - error = mdstart_vnode(sc, bp); - mtx_assert(&Giant, MA_OWNED); - break; - case MD_SWAP: - error = mdstart_swap(sc, bp); - break; - default: - panic("Impossible md(type)"); - break; - } + error = sc->start(sc, bp); } if (error != -1) { @@ -710,12 +688,10 @@ mdfind(int unit) { struct md_s *sc; - mtx_lock(&md_mtx); LIST_FOREACH(sc, &md_softc_list, list) { if (sc->unit == unit) break; } - mtx_unlock(&md_mtx); return (sc); } @@ -725,40 +701,31 @@ mdnew(int unit, int *errp) struct md_s *sc, *sc2; int error, max = -1; - sc = (struct md_s *)malloc(sizeof *sc, M_MD, M_WAITOK | M_ZERO); - bioq_init(&sc->bio_queue); - mtx_init(&sc->queue_mtx, "md bio queue", NULL, MTX_DEF); - mtx_lock(&md_mtx); + *errp = 0; LIST_FOREACH(sc2, &md_softc_list, list) { - if (sc2->unit == unit) { - mtx_unlock(&md_mtx); - mtx_destroy(&sc->queue_mtx); - free(sc, M_MD); - if (errp != NULL) - *errp = EBUSY; + if (unit == sc2->unit) { + *errp = EBUSY; return (NULL); } - if (sc2->unit > max) + if (unit == -1 && sc2->unit > max) max = sc2->unit; } if (unit == -1) unit = max + 1; + sc = (struct md_s *)malloc(sizeof *sc, M_MD, M_WAITOK | M_ZERO); + bioq_init(&sc->bio_queue); + mtx_init(&sc->queue_mtx, "md bio queue", NULL, MTX_DEF); sc->unit = unit; sprintf(sc->name, "md%d", unit); LIST_INSERT_HEAD(&md_softc_list, sc, list); - mtx_unlock(&md_mtx); error = kthread_create(md_kthread, sc, &sc->procp, 0, 0,"%s", sc->name); - if (error != 0) { - mtx_lock(&md_mtx); - LIST_REMOVE(sc, list); - mtx_unlock(&md_mtx); - mtx_destroy(&sc->queue_mtx); - free(sc, M_MD); - if (errp != NULL) - *errp = error; - return (NULL); - } - return (sc); + if (error == 0) + return (sc); + LIST_REMOVE(sc, list); + mtx_destroy(&sc->queue_mtx); + free(sc, M_MD); + *errp = error; + return (NULL); } static void @@ -768,7 +735,6 @@ mdinit(struct md_s *sc) struct g_geom *gp; struct g_provider *pp; - DROP_GIANT(); g_topology_lock(); gp = g_new_geomf(&g_md_class, "md%d", sc->unit); gp->softc = sc; @@ -779,7 +745,6 @@ mdinit(struct md_s *sc) sc->pp = pp; g_error_provider(pp, 0); g_topology_unlock(); - PICKUP_GIANT(); } /* @@ -930,23 +895,16 @@ mdcreate_vnode(struct md_s *sc, struct md_ioctl *mdio, struct thread *td) return (0); } -static void -md_zapit(void *p, int cancel) -{ - if (cancel) - return; - g_wither_geom(p, ENXIO); -} - static int mddestroy(struct md_s *sc, struct thread *td) { - GIANT_REQUIRED; if (sc->gp) { sc->gp->softc = NULL; - g_waitfor_event(md_zapit, sc->gp, M_WAITOK, sc->gp, NULL); + g_topology_lock(); + g_wither_geom(sc->gp, ENXIO); + g_topology_unlock(); sc->gp = NULL; sc->pp = NULL; } @@ -955,9 +913,12 @@ mddestroy(struct md_s *sc, struct thread *td) while (sc->procp != NULL) tsleep(&sc->procp, PRIBIO, "mddestroy", hz / 10); mtx_destroy(&sc->queue_mtx); - if (sc->vnode != NULL) + if (sc->vnode != NULL) { + mtx_lock(&Giant); (void)vn_close(sc->vnode, sc->flags & MD_READONLY ? FREAD : (FREAD|FWRITE), sc->cred, td); + mtx_unlock(&Giant); + } if (sc->cred != NULL) crfree(sc->cred); if (sc->object != NULL) @@ -967,9 +928,7 @@ mddestroy(struct md_s *sc, struct thread *td) if (sc->uma) uma_zdestroy(sc->uma); - mtx_lock(&md_mtx); LIST_REMOVE(sc, list); - mtx_unlock(&md_mtx); free(sc, M_MD); return (0); } @@ -980,8 +939,6 @@ mdcreate_swap(struct md_s *sc, struct md_ioctl *mdio, struct thread *td) vm_ooffset_t npage; int error; - GIANT_REQUIRED; - /* * Range check. Disallow negative sizes or any size less then the * size of a page. Then round to a page. @@ -1020,29 +977,9 @@ mdcreate_swap(struct md_s *sc, struct md_ioctl *mdio, struct thread *td) return (error); } -static int -mddetach(int unit, struct thread *td) -{ - struct md_s *sc; - - sc = mdfind(unit); - if (sc == NULL) - return (ENOENT); - if (sc->opencount != 0 && !(sc->flags & MD_FORCE)) - return (EBUSY); - switch(sc->type) { - case MD_VNODE: - case MD_SWAP: - case MD_MALLOC: - case MD_PRELOAD: - return (mddestroy(sc, td)); - default: - return (EOPNOTSUPP); - } -} static int -mdctlioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td) +xmdctlioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td) { struct md_ioctl *mdio; struct md_s *sc; @@ -1052,6 +989,10 @@ mdctlioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread printf("mdctlioctl(%s %lx %p %x %p)\n", devtoname(dev), cmd, addr, flags, td); + mdio = (struct md_ioctl *)addr; + if (mdio->md_version != MDIOVERSION) + return (EINVAL); + /* * We assert the version number in the individual ioctl * handlers instead of out here because (a) it is possible we @@ -1059,11 +1000,9 @@ mdctlioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread * mdio, and (b) the correct return value for an unknown ioctl * is ENOIOCTL, not EINVAL. */ - mdio = (struct md_ioctl *)addr; + error = 0; switch (cmd) { case MDIOCATTACH: - if (mdio->md_version != MDIOVERSION) - return (EINVAL); switch (mdio->md_type) { case MD_MALLOC: case MD_PRELOAD: @@ -1090,15 +1029,19 @@ mdctlioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread error = EDOOFUS; switch (sc->type) { case MD_MALLOC: + sc->start = mdstart_malloc; error = mdcreate_malloc(sc, mdio); break; case MD_PRELOAD: + sc->start = mdstart_preload; error = mdcreate_preload(sc, mdio); break; case MD_VNODE: + sc->start = mdstart_vnode; error = mdcreate_vnode(sc, mdio, td); break; case MD_SWAP: + sc->start = mdstart_swap; error = mdcreate_swap(sc, mdio, td); break; } @@ -1106,17 +1049,24 @@ mdctlioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread mddestroy(sc, td); return (error); } + + /* Prune off any residual fractional sector */ + i = sc->mediasize % sc->sectorsize; + sc->mediasize -= i; + mdinit(sc); return (0); case MDIOCDETACH: - if (mdio->md_version != MDIOVERSION) - return (EINVAL); if (mdio->md_mediasize != 0 || mdio->md_options != 0) return (EINVAL); - return (mddetach(mdio->md_unit, td)); + + sc = mdfind(mdio->md_unit); + if (sc == NULL) + return (ENOENT); + if (sc->opencount != 0 && !(sc->flags & MD_FORCE)) + return (EBUSY); + return (mddestroy(sc, td)); case MDIOCQUERY: - if (mdio->md_version != MDIOVERSION) - return (EINVAL); sc = mdfind(mdio->md_unit); if (sc == NULL) return (ENOENT); @@ -1124,16 +1074,11 @@ mdctlioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread mdio->md_options = sc->flags; mdio->md_mediasize = sc->mediasize; mdio->md_sectorsize = sc->sectorsize; - if (sc->type == MD_VNODE) { + if (sc->type == MD_VNODE) error = copyout(sc->file, mdio->md_file, strlen(sc->file) + 1); - if (error != 0) - return (error); - } - return (0); + return (error); case MDIOCLIST: - if (mdio->md_version != MDIOVERSION) - return (EINVAL); i = 1; LIST_FOREACH(sc, &md_softc_list, list) { if (i == MDNPAD - 1) @@ -1146,15 +1091,26 @@ mdctlioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread default: return (ENOIOCTL); }; - return (ENOIOCTL); +} + +static int +mdctlioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td) +{ + int error; + + sx_xlock(&md_sx); + error = xmdctlioctl(dev, cmd, addr, flags, td); + sx_xunlock(&md_sx); + return (error); } static void md_preloaded(u_char *image, size_t length) { struct md_s *sc; + int error; - sc = mdnew(-1, NULL); + sc = mdnew(-1, &error); if (sc == NULL) return; sc->type = MD_PRELOAD; @@ -1162,6 +1118,7 @@ md_preloaded(u_char *image, size_t length) sc->sectorsize = DEV_BSIZE; sc->pl_ptr = image; sc->pl_len = length; + sc->start = mdstart_preload; #ifdef MD_ROOT if (sc->unit == 0) rootdevnames[0] = "ufs:/dev/md0"; @@ -1179,15 +1136,19 @@ g_md_init(struct g_class *mp __unused) unsigned len; mod = NULL; + sx_init(&md_sx, "MD config lock"); g_topology_unlock(); #ifdef MD_ROOT_SIZE - md_preloaded(mfs_root, MD_ROOT_SIZE*1024); + sx_xlock(&md_sx); + md_preloaded(mfs_root, MD_ROOT_SIZE * 1024); + sx_xunlock(&md_sx); #endif + /* XXX: are preload_* static or do they need Giant ? */ while ((mod = preload_search_next_name(mod)) != NULL) { name = (char *)preload_search_info(mod, MODINFO_NAME); - type = (char *)preload_search_info(mod, MODINFO_TYPE); if (name == NULL) continue; + type = (char *)preload_search_info(mod, MODINFO_TYPE); if (type == NULL) continue; if (strcmp(type, "md_image") && strcmp(type, "mfs_root")) @@ -1198,7 +1159,9 @@ g_md_init(struct g_class *mp __unused) len = *(size_t *)c; printf("%s%d: Preloaded image <%s> %d bytes at %p\n", MD_NAME, mdunits, name, len, ptr); + sx_xlock(&md_sx); md_preloaded(ptr, len); + sx_xunlock(&md_sx); } status_dev = make_dev(&mdctl_cdevsw, MAXMINOR, UID_ROOT, GID_WHEEL, 0600, MDCTL_NAME); @@ -1209,6 +1172,7 @@ static void g_md_fini(struct g_class *mp __unused) { + sx_destroy(&md_sx); if (status_dev != NULL) destroy_dev(status_dev); } |