in channel_mgmt.c [575:684]
static void vmbus_process_offer(struct vmbus_channel *newchannel)
{
struct vmbus_channel *channel;
struct workqueue_struct *wq;
bool fnew = true;
/*
* Synchronize vmbus_process_offer() and CPU hotplugging:
*
* CPU1 CPU2
*
* [vmbus_process_offer()] [Hot removal of the CPU]
*
* CPU_READ_LOCK CPUS_WRITE_LOCK
* LOAD cpu_online_mask SEARCH chn_list
* STORE target_cpu LOAD target_cpu
* INSERT chn_list STORE cpu_online_mask
* CPUS_READ_UNLOCK CPUS_WRITE_UNLOCK
*
* Forbids: CPU1's LOAD from *not* seing CPU2's STORE &&
* CPU2's SEARCH from *not* seeing CPU1's INSERT
*
* Forbids: CPU2's SEARCH from seeing CPU1's INSERT &&
* CPU2's LOAD from *not* seing CPU1's STORE
*/
cpus_read_lock();
/*
* Serializes the modifications of the chn_list list as well as
* the accesses to next_numa_node_id in init_vp_index().
*/
mutex_lock(&vmbus_connection.channel_mutex);
list_for_each_entry(channel, &vmbus_connection.chn_list, listentry) {
if (guid_equal(&channel->offermsg.offer.if_type,
&newchannel->offermsg.offer.if_type) &&
guid_equal(&channel->offermsg.offer.if_instance,
&newchannel->offermsg.offer.if_instance)) {
fnew = false;
newchannel->primary_channel = channel;
break;
}
}
init_vp_index(newchannel);
/* Remember the channels that should be cleaned up upon suspend. */
if (is_hvsock_channel(newchannel) || is_sub_channel(newchannel))
atomic_inc(&vmbus_connection.nr_chan_close_on_suspend);
/*
* Now that we have acquired the channel_mutex,
* we can release the potentially racing rescind thread.
*/
atomic_dec(&vmbus_connection.offer_in_progress);
if (fnew) {
list_add_tail(&newchannel->listentry,
&vmbus_connection.chn_list);
} else {
/*
* Check to see if this is a valid sub-channel.
*/
if (newchannel->offermsg.offer.sub_channel_index == 0) {
mutex_unlock(&vmbus_connection.channel_mutex);
/*
* Don't call free_channel(), because newchannel->kobj
* is not initialized yet.
*/
kfree(newchannel);
WARN_ON_ONCE(1);
return;
}
/*
* Process the sub-channel.
*/
list_add_tail(&newchannel->sc_list, &channel->sc_list);
}
vmbus_channel_map_relid(newchannel);
mutex_unlock(&vmbus_connection.channel_mutex);
cpus_read_unlock();
/*
* vmbus_process_offer() mustn't call channel->sc_creation_callback()
* directly for sub-channels, because sc_creation_callback() ->
* vmbus_open() may never get the host's response to the
* OPEN_CHANNEL message (the host may rescind a channel at any time,
* e.g. in the case of hot removing a NIC), and vmbus_onoffer_rescind()
* may not wake up the vmbus_open() as it's blocked due to a non-zero
* vmbus_connection.offer_in_progress, and finally we have a deadlock.
*
* The above is also true for primary channels, if the related device
* drivers use sync probing mode by default.
*
* And, usually the handling of primary channels and sub-channels can
* depend on each other, so we should offload them to different
* workqueues to avoid possible deadlock, e.g. in sync-probing mode,
* NIC1's netvsc_subchan_work() can race with NIC2's netvsc_probe() ->
* rtnl_lock(), and causes deadlock: the former gets the rtnl_lock
* and waits for all the sub-channels to appear, but the latter
* can't get the rtnl_lock and this blocks the handling of
* sub-channels.
*/
INIT_WORK(&newchannel->add_channel_work, vmbus_add_channel_work);
wq = fnew ? vmbus_connection.handle_primary_chan_wq :
vmbus_connection.handle_sub_chan_wq;
queue_work(wq, &newchannel->add_channel_work);
}