Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Drivers: hv: vmbus: Limit channel interrupt scan to relid high water mark

When checking for VMBus channel interrupts, current code always scans the
full SynIC receive interrupt bit array to get the relid of the
interrupting channels. The array has HV_EVENT_FLAGS_COUNT (2048) bits.
But VMs rarely have more than 100 channels, and the relid is typically
a small integer that is densely assigned by the Hyper-V host. It's
wasteful to scan 2048 bits when it is highly unlikely that anything will
be found past bit 100. The waste is double with Confidential VMBus because
there are two receive interrupt arrays that must be scanned: one for the
hypervisor SynIC and one for the paravisor SynIC.

Improve the scanning by tracking the largest relid that has been offered
by the Hyper-V host. Then when checking for VMBus channel interrupts, only
scan up to this high water mark.

When channels are rescinded, it's not worth the complexity to recalculate
the high water mark. Hyper-V tends to reuse the rescinded relids for any
new channels that are subsequently added, and the performance benefit of
exactly tracking the high water mark would be minimal.

Signed-off-by: Michael Kelley <mhklinux@outlook.com>
Tested-by: Roman Kisel <vdso@mailbox.org>
Reviewed-by: Roman Kisel <vdso@mailbox.org>
Signed-off-by: Wei Liu <wei.liu@kernel.org>

authored by

Michael Kelley and committed by
Wei Liu
1c80dd81 028ef9c9

+15 -11
+12 -4
drivers/hv/channel_mgmt.c
··· 384 384 385 385 void vmbus_channel_map_relid(struct vmbus_channel *channel) 386 386 { 387 - if (WARN_ON(channel->offermsg.child_relid >= MAX_CHANNEL_RELIDS)) 387 + u32 new_relid = channel->offermsg.child_relid; 388 + 389 + if (WARN_ON(new_relid >= MAX_CHANNEL_RELIDS)) 388 390 return; 391 + 392 + /* 393 + * This function is always called in the tasklet for the connect CPU. 394 + * So updating the relid hiwater mark does not need to be atomic. 395 + */ 396 + if (new_relid > READ_ONCE(vmbus_connection.relid_hiwater)) 397 + WRITE_ONCE(vmbus_connection.relid_hiwater, new_relid); 398 + 389 399 /* 390 400 * The mapping of the channel's relid is visible from the CPUs that 391 401 * execute vmbus_chan_sched() by the time that vmbus_chan_sched() will ··· 421 411 * of the VMBus driver and vmbus_chan_sched() can not run before 422 412 * vmbus_bus_resume() has completed execution (cf. resume_noirq). 423 413 */ 424 - virt_store_mb( 425 - vmbus_connection.channels[channel->offermsg.child_relid], 426 - channel); 414 + virt_store_mb(vmbus_connection.channels[new_relid], channel); 427 415 } 428 416 429 417 void vmbus_channel_unmap_relid(struct vmbus_channel *channel)
+2 -1
drivers/hv/hyperv_vmbus.h
··· 276 276 struct list_head chn_list; 277 277 struct mutex channel_mutex; 278 278 279 - /* Array of channels */ 279 + /* Array of channel pointers, indexed by relid */ 280 280 struct vmbus_channel **channels; 281 + u32 relid_hiwater; 281 282 282 283 /* 283 284 * An offer message is handled first on the work_queue, and then
+1 -6
drivers/hv/vmbus_drv.c
··· 1258 1258 return; 1259 1259 event = (union hv_synic_event_flags *)event_page_addr + VMBUS_MESSAGE_SINT; 1260 1260 1261 - maxbits = HV_EVENT_FLAGS_COUNT; 1261 + maxbits = READ_ONCE(vmbus_connection.relid_hiwater) + 1; 1262 1262 recv_int_page = event->flags; 1263 1263 1264 1264 if (unlikely(!recv_int_page)) 1265 1265 return; 1266 1266 1267 - /* 1268 - * Suggested-by: Michael Kelley <mhklinux@outlook.com> 1269 - * One possible optimization would be to keep track of the largest relID that's in use, 1270 - * and only scan up to that relID. 1271 - */ 1272 1267 for_each_set_bit(relid, recv_int_page, maxbits) { 1273 1268 void (*callback_fn)(void *context); 1274 1269 struct vmbus_channel *channel;