DF-3547 / 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 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 | --- a/sys/netgraph7/ng_device.c +++ b/sys/netgraph7/ng_device.c @@ -88,6 +88,10 @@ struct ngd_private { struct cdev *ngddev; struct mtx ngd_mtx; int unit; + int refs; /* 1 base (node) + one per active cdevsw call */ + int dying; /* teardown began; refuse new callers */ uint16_t flags; #define NGDF_OPEN 0x0001 #define NGDF_RWAIT 0x0002 @@ -190,6 +194,8 @@ ng_device_constructor(node_p node) mtx_init(&priv->ngd_mtx, "ng_device", NULL, MTX_DEF); mtx_init(&priv->readq.ifq_mtx, "ng_device queue", NULL, MTX_DEF); IFQ_SET_MAXLEN(&priv->readq, ifqmaxlen); + priv->refs = 1; /* node's base reference */ + priv->dying = 0; /* Link everything together */ NG_NODE_SET_PRIVATE(node, priv); @@ -292,10 +298,54 @@ ng_device_disconnect(hook_p hook) priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); + int last; DBG; + /* + * DragonFly's destroy_dev() is asynchronous: it only queues work to + * the devfs core thread and does NOT drain in-flight (or blocked) + * cdevsw method invocations, and an open fd keeps dispatching through + * si_drv1 afterwards. Therefore priv may only be freed when the last + * device method has returned. + */ + mtx_lock(&priv->ngd_mtx); + priv->dying = 1; + if (priv->ngddev != NULL) + priv->ngddev->si_drv1 = NULL; /* no new callers can acquire */ + last = (--priv->refs == 0); + mtx_unlock(&priv->ngd_mtx); + destroy_dev(priv->ngddev); - mtx_destroy(&priv->ngd_mtx); - IF_DRAIN(&priv->readq); - mtx_destroy(&(priv)->readq.ifq_mtx); + if (last) { + mtx_destroy(&priv->ngd_mtx); + IF_DRAIN(&priv->readq); + mtx_destroy(&(priv)->readq.ifq_mtx); - free_unr(ngd_unit, priv->unit); + free_unr(ngd_unit, priv->unit); - kfree(priv, M_NETGRAPH); + kfree(priv, M_NETGRAPH); + } ng_rmnode_self(NG_HOOK_NODE(hook)); @@ -299,6 +349,34 @@ ng_device_disconnect(hook_p hook) return(0); } + +/* + * Acquire/release the private structure from a cdevsw method. Acquire + * serializes with ng_device_disconnect() on ngd_mtx so that priv cannot be + * freed while a method is running, and refuses to hand out dying privs. + */ +static priv_p +ngd_acquire(struct cdev *dev) +{ + priv_p priv = dev->si_drv1; + + if (priv == NULL) + return (NULL); + mtx_lock(&priv->ngd_mtx); + if (priv->dying) { + mtx_unlock(&priv->ngd_mtx); + return (NULL); + } + priv->refs++; + mtx_unlock(&priv->ngd_mtx); + return (priv); +} + +static void +ngd_release(priv_p priv) +{ + int last; + + mtx_lock(&priv->ngd_mtx); + last = (--priv->refs == 0); + mtx_unlock(&priv->ngd_mtx); + if (last) { + IF_DRAIN(&priv->readq); + mtx_destroy(&(priv)->readq.ifq_mtx); + mtx_destroy(&priv->ngd_mtx); + free_unr(ngd_unit, priv->unit); + kfree(priv, M_NETGRAPH); + } +} /****************************************************************************** * Device methods @@ -332,6 +410,9 @@ ngdopen(struct cdev *dev, int flag, int mode, struct thread *td) priv_p priv = (priv_p )dev->si_drv1; DBG; + + if ((priv = ngd_acquire(dev)) == NULL) + return (ENXIO); mtx_lock(&priv->ngd_mtx); priv->flags |= NGDF_OPEN; @@ -340,6 +421,8 @@ ngdopen(struct cdev *dev, int flag, int mode, struct thread *td) return(0); } @@ -349,6 +432,9 @@ ngdclose(struct cdev *dev, int flag, int mode, struct thread *td) priv_p priv = (priv_p )dev->si_drv1; DBG; + + if ((priv = ngd_acquire(dev)) == NULL) + return (ENXIO); + mtx_lock(&priv->ngd_mtx); priv->flags &= ~NGDF_OPEN; mtx_unlock(&priv->ngd_mtx); @@ -409,6 +495,10 @@ ngdread(struct cdev *dev, struct uio *uio, int flag) int len, error = 0; DBG; + + if ((priv = ngd_acquire(dev)) == NULL) + return (ENXIO); /* get an mbuf */ do { @@ -431,6 +521,7 @@ ngdread(struct cdev *dev, struct uio *uio, int flag) if (m) m_freem(m); + ngd_release(priv); return (error); } @@ -452,6 +543,10 @@ ngdwrite(struct cdev *dev, struct uio *uio, int flag) int error = 0; DBG; + + if ((priv = ngd_acquire(dev)) == NULL) + return (ENXIO); if (uio->uio_resid == 0) return (0); @@ -467,6 +562,7 @@ ngdwrite(struct cdev *dev, struct uio *uio, int flag) NG_SEND_DATA_ONLY(error, priv->hook, m); + ngd_release(priv); return (error); } @@ -479,6 +575,10 @@ ngdpoll(struct cdev *dev, int events, struct thread *td) int revents = 0; + if ((priv = ngd_acquire(dev)) == NULL) + return (0); + if (events & (POLLIN | POLLRDNORM) && !IFQ_IS_EMPTY(&priv->readq)) revents |= events & (POLLIN | POLLRDNORM); + ngd_release(priv); return (revents); } |