--- a/sys/dev/video/cxm/cxm.h +++ b/sys/dev/video/cxm/cxm.h @@ -37,6 +37,7 @@ */ #include +#include #include "pcidevs.h" @@ -254,6 +255,7 @@ device_t dev; /* bus attachment */ cdev_t cxm_dev_t; /* control device */ + struct lock cxm_lock; /* serializes cdev entry vs detach */ int is_opened; int mpeg; --- a/sys/dev/video/cxm/cxm.c +++ b/sys/dev/video/cxm/cxm.c @@ -1880,6 +1880,7 @@ sc->source = cxm_tuner_source; + lockinit(&sc->cxm_lock, "cxm", 0, 0); /* make the device entries */ sc->cxm_dev_t = make_dev(&cxm_ops, unit, @@ -1957,6 +1958,14 @@ dev_ops_remove_minor(&cxm_ops, /*0, */device_get_unit(dev)); /* + * Drain any in-flight cdev operations (ioctls can sleep for + * ~0.5 s in the audio/video lock waits) before tearing down + * the softc they dereference. + */ + lockmgr(&sc->cxm_lock, LK_EXCLUSIVE); + lockmgr(&sc->cxm_lock, LK_RELEASE); + + /* * Deallocate scatter / gather list and buffers. */ bus_dmamap_unload(sc->enc_sg.dmat, sc->enc_sg.dmamap); @@ -2000,6 +2009,8 @@ bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res); bus_release_resource(dev, SYS_RES_MEMORY, CXM_RID, sc->mem_res); + lockuninit(&sc->cxm_lock); + return 0; } @@ -2155,11 +2166,15 @@ return ENXIO; } - if (sc->is_opened) + lockmgr(&sc->cxm_lock, LK_EXCLUSIVE); + if (sc->is_opened) { + lockmgr(&sc->cxm_lock, LK_RELEASE); return EBUSY; + } sc->is_opened = 1; sc->mpeg = 1; + lockmgr(&sc->cxm_lock, LK_RELEASE); /* Record that the device is now busy */ device_busy(devclass_get_device(cxm_devclass, unit)); @@ -2187,8 +2202,12 @@ return ENXIO; } - if (cxm_stop_encoder(sc) < 0) + lockmgr(&sc->cxm_lock, LK_EXCLUSIVE); + + if (cxm_stop_encoder(sc) < 0) { + lockmgr(&sc->cxm_lock, LK_RELEASE); return ENXIO; + } sc->enc_pool.offset = 0; sc->enc_pool.read = 0; @@ -2201,6 +2220,8 @@ sc->is_opened = 0; + lockmgr(&sc->cxm_lock, LK_RELEASE); + return 0; } @@ -2305,7 +2326,33 @@ static int cxm_ioctl(struct dev_ioctl_args *ap) { - cdev_t dev = ap->a_head.a_dev; + int unit; + struct cxm_softc *sc; + int error; + + unit = UNIT(minor(ap->a_head.a_dev)); + + /* Get the device data */ + sc = (struct cxm_softc*)devclass_get_softc(cxm_devclass, unit); + if (sc == NULL) { + /* the device is no longer valid/functioning */ + return ENXIO; + } + + /* + * Serialize against cxm_detach: the ioctl body may sleep + * (audio/video lock waits) while dereferencing sc. + */ + lockmgr(&sc->cxm_lock, LK_EXCLUSIVE); + error = cxm_ioctl_locked(ap, sc); + lockmgr(&sc->cxm_lock, LK_RELEASE); + + return error; +} + +static int +cxm_ioctl_locked(struct dev_ioctl_args *ap, struct cxm_softc *sc) +{ int brightness; int chroma_saturation; int contrast; @@ -2313,24 +2360,13 @@ int hue; int result; int status; - int unit; unsigned int i; unsigned int sig; unsigned long freq; - struct cxm_softc *sc; enum cxm_source source; struct bktr_capture_area *cap; struct bktr_remote *remote; - unit = UNIT(minor(dev)); - - /* Get the device data */ - sc = (struct cxm_softc*)devclass_get_softc(cxm_devclass, unit); - if (sc == NULL) { - /* the device is no longer valid/functioning */ - return ENXIO; - } - switch (ap->a_cmd) { case BT848_GAUDIO: switch (cxm_msp_selected_source(sc)) {