From e48079179763ea8cf6ae3790cb1ab07094fa5717 Mon Sep 17 00:00:00 2001 From: Sepherosa Ziehau Date: Thu, 14 Jul 2016 07:59:01 +0000 Subject: [PATCH] hyperv/vmbus: Fix the racy channel close. It is not safe to iterate the sub-channel list w/o lock on the close path, while it's even more difficult to hold the lock and iterate the sub-channel list. We leverage the vmbua_{get,rel}_subchan() functions to solve this dilemma. MFC after: 1 week Sponsored by: Microsoft OSTC Differential Revision: https://reviews.freebsd.org/D7112 --- sys/dev/hyperv/vmbus/hv_channel.c | 39 +++++++++++++++++-------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/sys/dev/hyperv/vmbus/hv_channel.c b/sys/dev/hyperv/vmbus/hv_channel.c index d3da0e998ba..692fbc3a614 100644 --- a/sys/dev/hyperv/vmbus/hv_channel.c +++ b/sys/dev/hyperv/vmbus/hv_channel.c @@ -542,35 +542,40 @@ hv_vmbus_channel_close_internal(hv_vmbus_channel *channel) M_DEVBUF); } -/** - * @brief Close the specified channel +/* + * Caller should make sure that all sub-channels have + * been added to 'chan' and all to-be-closed channels + * are not being opened. */ void -hv_vmbus_channel_close(hv_vmbus_channel *channel) +hv_vmbus_channel_close(struct hv_vmbus_channel *chan) { - hv_vmbus_channel* sub_channel; + int subchan_cnt; - if (channel->primary_channel != NULL) { + if (!VMBUS_CHAN_ISPRIMARY(chan)) { /* - * We only close multi-channels when the primary is - * closed. + * Sub-channel is closed when its primary channel + * is closed; done. */ return; } /* - * Close all multi-channels first. + * Close all sub-channels, if any. */ - TAILQ_FOREACH(sub_channel, &channel->sc_list_anchor, - sc_list_entry) { - if ((sub_channel->ch_stflags & VMBUS_CHAN_ST_OPENED) == 0) - continue; - hv_vmbus_channel_close_internal(sub_channel); + subchan_cnt = chan->subchan_cnt; + if (subchan_cnt > 0) { + struct hv_vmbus_channel **subchan; + int i; + + subchan = vmbus_get_subchan(chan, subchan_cnt); + for (i = 0; i < subchan_cnt; ++i) + hv_vmbus_channel_close_internal(subchan[i]); + vmbus_rel_subchan(subchan, subchan_cnt); } - /* - * Then close the primary channel. - */ - hv_vmbus_channel_close_internal(channel); + + /* Then close the primary channel. */ + hv_vmbus_channel_close_internal(chan); } /**