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
This commit is contained in:
Sepherosa Ziehau 2016-07-14 07:59:01 +00:00
parent c1bea00ed1
commit e480791797

View File

@ -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);
}
/**