Index: head/sys/dev/hyperv/include/vmbus.h =================================================================== --- head/sys/dev/hyperv/include/vmbus.h +++ head/sys/dev/hyperv/include/vmbus.h @@ -131,6 +131,7 @@ /* * vmbus_chan_open_br() + * * Return values: * 0 Succeeded. * EISCONN Failed, and the memory passed through 'br' is still @@ -139,6 +140,24 @@ * other values Failed. The memory passed through 'br' is no longer * connected. Callers are free to do anything with the * memory passed through 'br'. + * + * + * + * vmbus_chan_close_direct() + * + * NOTE: + * Callers of this function _must_ make sure to close all sub-channels before + * closing the primary channel. + * + * Return values: + * 0 Succeeded. + * EISCONN Failed, and the memory associated with the bufring + * is still connected. Callers must _not_ free the the + * memory associated with the bufring, if this error + * happens. + * other values Failed. The memory associated with the bufring is + * no longer connected. Callers are free to do anything + * with the memory associated with the bufring. */ int vmbus_chan_open(struct vmbus_channel *chan, int txbr_size, int rxbr_size, const void *udata, int udlen, @@ -147,6 +166,7 @@ const struct vmbus_chan_br *cbr, const void *udata, int udlen, vmbus_chan_callback_t cb, void *cbarg); void vmbus_chan_close(struct vmbus_channel *chan); +int vmbus_chan_close_direct(struct vmbus_channel *chan); void vmbus_chan_intr_drain(struct vmbus_channel *chan); void vmbus_chan_run_task(struct vmbus_channel *chan, struct task *task); Index: head/sys/dev/hyperv/vmbus/vmbus_chan.c =================================================================== --- head/sys/dev/hyperv/vmbus/vmbus_chan.c +++ head/sys/dev/hyperv/vmbus/vmbus_chan.c @@ -851,6 +851,41 @@ return (error); } +int +vmbus_chan_close_direct(struct vmbus_channel *chan) +{ + int error; + +#ifdef INVARIANTS + if (VMBUS_CHAN_ISPRIMARY(chan)) { + struct vmbus_channel *subchan; + + /* + * All sub-channels _must_ have been closed, or are _not_ + * opened at all. + */ + mtx_lock(&chan->ch_subchan_lock); + TAILQ_FOREACH(subchan, &chan->ch_subchans, ch_sublink) { + KASSERT( + (subchan->ch_stflags & VMBUS_CHAN_ST_OPENED) == 0, + ("chan%u: subchan%u is still opened", + chan->ch_id, subchan->ch_subidx)); + } + mtx_unlock(&chan->ch_subchan_lock); + } +#endif + + error = vmbus_chan_close_internal(chan); + if (!VMBUS_CHAN_ISPRIMARY(chan)) { + /* + * This sub-channel is referenced, when it is linked to + * the primary channel; drop that reference now. + */ + vmbus_chan_detach(chan); + } + return (error); +} + /* * Caller should make sure that all sub-channels have * been added to 'chan' and all to-be-closed channels