DF-5548 / fix.diff
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 | --- 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 <sys/signal.h> #include <sys/mman.h> #include <sys/fcntl.h> +#include <sys/spinlock.h> +#include <sys/spinlock2.h> #include <sys/thread2.h> #include <bus/firewire/firewire.h> @@ -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); } |