Skip to content

Commit 3a7f778

Browse files
effective-lightliuw
authored andcommitted
drivers/hv: add CPU offlining support
Currently, it is tedious to offline CPUs in a Hyper-V VM since CPUs may have VMBus channels attached to them that a user would have to manually rebind elsewhere. So, as made mention of in commit d570aec ("Drivers: hv: vmbus: Synchronize init_vp_index() vs. CPU hotplug"), rebind channels associated with CPUs that a user is trying to offline to a new "randomly" selected CPU. Cc: Boqun Feng <[email protected]> Cc: Michael Kelley <[email protected]> Cc: Wei Liu <[email protected]> Signed-off-by: Hamza Mahfooz <[email protected]> Reviewed-by: Michael Kelley <[email protected]> Tested-by: Michael Kelley <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Wei Liu <[email protected]> Message-ID: <[email protected]>
1 parent 5e4304f commit 3a7f778

File tree

1 file changed

+51
-21
lines changed

1 file changed

+51
-21
lines changed

drivers/hv/hv.c

Lines changed: 51 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -433,13 +433,47 @@ static bool hv_synic_event_pending(void)
433433
return pending;
434434
}
435435

436+
static int hv_pick_new_cpu(struct vmbus_channel *channel)
437+
{
438+
int ret = -EBUSY;
439+
int start;
440+
int cpu;
441+
442+
lockdep_assert_cpus_held();
443+
lockdep_assert_held(&vmbus_connection.channel_mutex);
444+
445+
/*
446+
* We can't assume that the relevant interrupts will be sent before
447+
* the cpu is offlined on older versions of hyperv.
448+
*/
449+
if (vmbus_proto_version < VERSION_WIN10_V5_3)
450+
return -EBUSY;
451+
452+
start = get_random_u32_below(nr_cpu_ids);
453+
454+
for_each_cpu_wrap(cpu, cpu_online_mask, start) {
455+
if (channel->target_cpu == cpu ||
456+
channel->target_cpu == VMBUS_CONNECT_CPU)
457+
continue;
458+
459+
ret = vmbus_channel_set_cpu(channel, cpu);
460+
if (!ret)
461+
break;
462+
}
463+
464+
if (ret)
465+
ret = vmbus_channel_set_cpu(channel, VMBUS_CONNECT_CPU);
466+
467+
return ret;
468+
}
469+
436470
/*
437471
* hv_synic_cleanup - Cleanup routine for hv_synic_init().
438472
*/
439473
int hv_synic_cleanup(unsigned int cpu)
440474
{
441475
struct vmbus_channel *channel, *sc;
442-
bool channel_found = false;
476+
int ret = 0;
443477

444478
if (vmbus_connection.conn_state != CONNECTED)
445479
goto always_cleanup;
@@ -456,38 +490,34 @@ int hv_synic_cleanup(unsigned int cpu)
456490

457491
/*
458492
* Search for channels which are bound to the CPU we're about to
459-
* cleanup. In case we find one and vmbus is still connected, we
460-
* fail; this will effectively prevent CPU offlining.
461-
*
462-
* TODO: Re-bind the channels to different CPUs.
493+
* cleanup.
463494
*/
464495
mutex_lock(&vmbus_connection.channel_mutex);
465496
list_for_each_entry(channel, &vmbus_connection.chn_list, listentry) {
466497
if (channel->target_cpu == cpu) {
467-
channel_found = true;
468-
break;
498+
ret = hv_pick_new_cpu(channel);
499+
if (ret) {
500+
mutex_unlock(&vmbus_connection.channel_mutex);
501+
return ret;
502+
}
469503
}
470504
list_for_each_entry(sc, &channel->sc_list, sc_list) {
471505
if (sc->target_cpu == cpu) {
472-
channel_found = true;
473-
break;
506+
ret = hv_pick_new_cpu(sc);
507+
if (ret) {
508+
mutex_unlock(&vmbus_connection.channel_mutex);
509+
return ret;
510+
}
474511
}
475512
}
476-
if (channel_found)
477-
break;
478513
}
479514
mutex_unlock(&vmbus_connection.channel_mutex);
480515

481-
if (channel_found)
482-
return -EBUSY;
483-
484516
/*
485-
* channel_found == false means that any channels that were previously
486-
* assigned to the CPU have been reassigned elsewhere with a call of
487-
* vmbus_send_modifychannel(). Scan the event flags page looking for
488-
* bits that are set and waiting with a timeout for vmbus_chan_sched()
489-
* to process such bits. If bits are still set after this operation
490-
* and VMBus is connected, fail the CPU offlining operation.
517+
* Scan the event flags page looking for bits that are set and waiting
518+
* with a timeout for vmbus_chan_sched() to process such bits. If bits
519+
* are still set after this operation and VMBus is connected, fail the
520+
* CPU offlining operation.
491521
*/
492522
if (vmbus_proto_version >= VERSION_WIN10_V4_1 && hv_synic_event_pending())
493523
return -EBUSY;
@@ -497,5 +527,5 @@ int hv_synic_cleanup(unsigned int cpu)
497527

498528
hv_synic_disable_regs(cpu);
499529

500-
return 0;
530+
return ret;
501531
}

0 commit comments

Comments
 (0)