diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index 7c77ada12b2e..84eb0a6a0b54 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -384,8 +384,18 @@ static void free_channel(struct vmbus_channel *channel) void vmbus_channel_map_relid(struct vmbus_channel *channel) { - if (WARN_ON(channel->offermsg.child_relid >= MAX_CHANNEL_RELIDS)) + u32 new_relid = channel->offermsg.child_relid; + + if (WARN_ON(new_relid >= MAX_CHANNEL_RELIDS)) return; + + /* + * This function is always called in the tasklet for the connect CPU. + * So updating the relid hiwater mark does not need to be atomic. + */ + if (new_relid > READ_ONCE(vmbus_connection.relid_hiwater)) + WRITE_ONCE(vmbus_connection.relid_hiwater, new_relid); + /* * The mapping of the channel's relid is visible from the CPUs that * execute vmbus_chan_sched() by the time that vmbus_chan_sched() will @@ -411,9 +421,7 @@ void vmbus_channel_map_relid(struct vmbus_channel *channel) * of the VMBus driver and vmbus_chan_sched() can not run before * vmbus_bus_resume() has completed execution (cf. resume_noirq). */ - virt_store_mb( - vmbus_connection.channels[channel->offermsg.child_relid], - channel); + virt_store_mb(vmbus_connection.channels[new_relid], channel); } void vmbus_channel_unmap_relid(struct vmbus_channel *channel) diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h index 7bd8f8486e85..2c90c81a3b0f 100644 --- a/drivers/hv/hyperv_vmbus.h +++ b/drivers/hv/hyperv_vmbus.h @@ -276,8 +276,9 @@ struct vmbus_connection { struct list_head chn_list; struct mutex channel_mutex; - /* Array of channels */ + /* Array of channel pointers, indexed by relid */ struct vmbus_channel **channels; + u32 relid_hiwater; /* * An offer message is handled first on the work_queue, and then diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index bc4fc1951ae1..3d1a58b667db 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -1258,17 +1258,12 @@ static void vmbus_chan_sched(void *event_page_addr) return; event = (union hv_synic_event_flags *)event_page_addr + VMBUS_MESSAGE_SINT; - maxbits = HV_EVENT_FLAGS_COUNT; + maxbits = READ_ONCE(vmbus_connection.relid_hiwater) + 1; recv_int_page = event->flags; if (unlikely(!recv_int_page)) return; - /* - * Suggested-by: Michael Kelley - * One possible optimization would be to keep track of the largest relID that's in use, - * and only scan up to that relID. - */ for_each_set_bit(relid, recv_int_page, maxbits) { void (*callback_fn)(void *context); struct vmbus_channel *channel;