--- a/sys/bus/firewire/fwmem.c +++ b/sys/bus/firewire/fwmem.c --- a/sys/bus/firewire/fwmem.c +++ b/sys/bus/firewire/fwmem.c @@ -48,6 +48,8 @@ #include #include #include +#include +#include #include #include @@ -70,6 +72,10 @@ MALLOC_DEFINE(M_FWMEM, "fwmem", "fwmem/FireWire"); +/* DF-5548: serialize the si_drv1/refcount lifecycle. devfs does not + * serialize open/close/ioctl/strategy of one cdev across threads. */ +static struct spinlock fwmem_lk = SPINLOCK_INITIALIZER(fwmem_lk, "fwmem_lk"); + #define MAXLEN (512 << fwmem_speed) struct fwmem_softc { @@ -263,18 +272,36 @@ cdev_t dev = ap->a_head.a_dev; struct fwmem_softc *fms; + spin_lock(&fwmem_lk); if (dev->si_drv1 != NULL) { - if ((ap->a_oflags & FWRITE) != 0) + if ((ap->a_oflags & FWRITE) != 0) { + spin_unlock(&fwmem_lk); return (EBUSY); + } fms = (struct fwmem_softc *)dev->si_drv1; fms->refcount ++; + spin_unlock(&fwmem_lk); } else { + spin_unlock(&fwmem_lk); fms = (struct fwmem_softc *)kmalloc(sizeof(struct fwmem_softc), M_FWMEM, M_WAITOK); bcopy(&fwmem_eui64, &fms->eui, sizeof(struct fw_eui64)); - dev->si_drv1 = (void *)fms; - dev->si_iosize_max = min(MAXPHYS,64*1024); fms->refcount = 1; + spin_lock(&fwmem_lk); + if (dev->si_drv1 != NULL) { + /* lost a concurrent first-open race: keep the winner */ + kfree(fms, M_FWMEM); + fms = (struct fwmem_softc *)dev->si_drv1; + if ((ap->a_oflags & FWRITE) != 0) { + spin_unlock(&fwmem_lk); + return (EBUSY); + } + fms->refcount ++; + } else { + dev->si_drv1 = (void *)fms; + dev->si_iosize_max = min(MAXPHYS,64*1024); + } + spin_unlock(&fwmem_lk); } if (fwmem_debug) kprintf("%s: refcount=%d\n", __func__, fms->refcount); @@ -288,14 +315,20 @@ cdev_t dev = ap->a_head.a_dev; struct fwmem_softc *fms; + spin_lock(&fwmem_lk); fms = (struct fwmem_softc *)dev->si_drv1; + if (fms == NULL) { + spin_unlock(&fwmem_lk); + return (0); + } fms->refcount --; if (fwmem_debug) kprintf("%s: refcount=%d\n", __func__, fms->refcount); if (fms->refcount < 1) { - kfree(dev->si_drv1, M_FW); + kfree(fms, M_FWMEM); /* DF-5549: was M_FW */ dev->si_drv1 = NULL; } + spin_unlock(&fwmem_lk); return (0); } @@ -331,6 +364,7 @@ struct fwmem_softc *fms; struct fw_device *fwdev; struct fw_xfer *xfer; + struct fw_eui64 euilocal; int unit, err=0, iolen; /* XXX check request length */ @@ -339,8 +373,16 @@ sc = devclass_get_softc(firewire_devclass, unit); crit_enter(); + spin_lock(&fwmem_lk); fms = (struct fwmem_softc *)dev->si_drv1; - fwdev = fw_noderesolve_eui64(sc->fc, &fms->eui); + if (fms == NULL) { + spin_unlock(&fwmem_lk); + err = ENXIO; + goto error; + } + euilocal = fms->eui; + spin_unlock(&fwmem_lk); + fwdev = fw_noderesolve_eui64(sc->fc, &euilocal); if (fwdev == NULL) { if (fwmem_debug) kprintf("fwmem: no such device ID:%08x%08x\n", @@ -404,7 +446,12 @@ struct fwmem_softc *fms; int err = 0; + spin_lock(&fwmem_lk); fms = (struct fwmem_softc *)dev->si_drv1; + if (fms == NULL) { + spin_unlock(&fwmem_lk); + return (ENXIO); + } switch (ap->a_cmd) { case FW_SDEUI64: bcopy(ap->a_data, &fms->eui, sizeof(struct fw_eui64)); @@ -415,6 +462,7 @@ default: err = EINVAL; } + spin_unlock(&fwmem_lk); return(err); }