From: Hamza Mahfooz <hamzamahfooz@xxxxxxxxxxxxxxxxxxx> Sent: Friday, January 17, 2025 12:33 PM > > 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 d570aec0f2154 ("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 <boqun.feng@xxxxxxxxx> > Cc: Michael Kelley <mhklinux@xxxxxxxxxxx> > Cc: Wei Liu <wei.liu@xxxxxxxxxx> > Signed-off-by: Hamza Mahfooz <hamzamahfooz@xxxxxxxxxxxxxxxxxxx> > --- > v2: remove cpus_read_{un,}lock() from hv_pick_new_cpu() and add > lockdep_assert_cpus_held(). > > v3: use for_each_cpu_wrap() in hv_pick_new_cpu(). > > v4: store get_random_u32_below() in start. > > v5: style fixes, use target_cpu and set ret to -EBUSY by default. > --- > drivers/hv/hv.c | 72 ++++++++++++++++++++++++++++++++++--------------- > 1 file changed, 51 insertions(+), 21 deletions(-) > > diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c > index 36d9ba097ff5..fab0690b5c41 100644 > --- a/drivers/hv/hv.c > +++ b/drivers/hv/hv.c > @@ -433,13 +433,47 @@ static bool hv_synic_event_pending(void) > return pending; > } > > +static int hv_pick_new_cpu(struct vmbus_channel *channel) > +{ > + int ret = -EBUSY; > + int start; > + int cpu; > + > + lockdep_assert_cpus_held(); > + lockdep_assert_held(&vmbus_connection.channel_mutex); > + > + /* > + * We can't assume that the relevant interrupts will be sent before > + * the cpu is offlined on older versions of hyperv. > + */ > + if (vmbus_proto_version < VERSION_WIN10_V5_3) > + return -EBUSY; > + > + start = get_random_u32_below(nr_cpu_ids); > + > + for_each_cpu_wrap(cpu, cpu_online_mask, start) { > + if (channel->target_cpu == cpu || > + channel->target_cpu == VMBUS_CONNECT_CPU) > + continue; > + > + ret = vmbus_channel_set_cpu(channel, cpu); > + if (!ret) > + break; > + } > + > + if (ret) > + ret = vmbus_channel_set_cpu(channel, VMBUS_CONNECT_CPU); > + > + return ret; > +} > + > /* > * hv_synic_cleanup - Cleanup routine for hv_synic_init(). > */ > int hv_synic_cleanup(unsigned int cpu) > { > struct vmbus_channel *channel, *sc; > - bool channel_found = false; > + int ret = 0; > > if (vmbus_connection.conn_state != CONNECTED) > goto always_cleanup; > @@ -456,38 +490,34 @@ int hv_synic_cleanup(unsigned int cpu) > > /* > * Search for channels which are bound to the CPU we're about to > - * cleanup. In case we find one and vmbus is still connected, we > - * fail; this will effectively prevent CPU offlining. > - * > - * TODO: Re-bind the channels to different CPUs. > + * cleanup. > */ > mutex_lock(&vmbus_connection.channel_mutex); > list_for_each_entry(channel, &vmbus_connection.chn_list, listentry) { > if (channel->target_cpu == cpu) { > - channel_found = true; > - break; > + ret = hv_pick_new_cpu(channel); > + if (ret) { > + mutex_unlock(&vmbus_connection.channel_mutex); > + return ret; > + } > } > list_for_each_entry(sc, &channel->sc_list, sc_list) { > if (sc->target_cpu == cpu) { > - channel_found = true; > - break; > + ret = hv_pick_new_cpu(sc); > + if (ret) { > + > mutex_unlock(&vmbus_connection.channel_mutex); > + return ret; > + } > } > } > - if (channel_found) > - break; > } > mutex_unlock(&vmbus_connection.channel_mutex); > > - if (channel_found) > - return -EBUSY; > - > /* > - * channel_found == false means that any channels that were previously > - * assigned to the CPU have been reassigned elsewhere with a call of > - * vmbus_send_modifychannel(). Scan the event flags page looking for > - * bits that are set and waiting with a timeout for vmbus_chan_sched() > - * to process such bits. If bits are still set after this operation > - * and VMBus is connected, fail the CPU offlining operation. > + * Scan the event flags page looking for bits that are set and waiting > + * with a timeout for vmbus_chan_sched() to process such bits. If bits > + * are still set after this operation and VMBus is connected, fail the > + * CPU offlining operation. > */ > if (vmbus_proto_version >= VERSION_WIN10_V4_1 && hv_synic_event_pending()) > return -EBUSY; > @@ -497,5 +527,5 @@ int hv_synic_cleanup(unsigned int cpu) > > hv_synic_disable_regs(cpu); > > - return 0; > + return ret; > } > -- > 2.47.1 Reviewed-by: Michael Kelley <mhklinux@xxxxxxxxxxx> Tested-by: Michael Kelley <mhklinux@xxxxxxxxxxx>