Skip to content

Commit

Permalink
Merge tag 'hyperv-next-signed' of git://git.kernel.org/pub/scm/linux/…
Browse files Browse the repository at this point in the history
…kernel/git/hyperv/linux

Pull Hyper-V updates from Sasha Levin:

 - first round of vmbus hibernation support (Dexuan Cui)

 - remove dependencies on PAGE_SIZE (Maya Nakamura)

 - move the hyper-v tools/ code into the tools build system (Andy
   Shevchenko)

 - hyper-v balloon cleanups (Dexuan Cui)

* tag 'hyperv-next-signed' of git://git.kernel.org/pub/scm/linux/kernel/git/hyperv/linux:
  Drivers: hv: vmbus: Resume after fixing up old primary channels
  Drivers: hv: vmbus: Suspend after cleaning up hv_sock and sub channels
  Drivers: hv: vmbus: Clean up hv_sock channels by force upon suspend
  Drivers: hv: vmbus: Suspend/resume the vmbus itself for hibernation
  Drivers: hv: vmbus: Ignore the offers when resuming from hibernation
  Drivers: hv: vmbus: Implement suspend/resume for VSC drivers for hibernation
  Drivers: hv: vmbus: Add a helper function is_sub_channel()
  Drivers: hv: vmbus: Suspend/resume the synic for hibernation
  Drivers: hv: vmbus: Break out synic enable and disable operations
  HID: hv: Remove dependencies on PAGE_SIZE for ring buffer
  Tools: hv: move to tools buildsystem
  hv_balloon: Reorganize the probe function
  hv_balloon: Use a static page for the balloon_up send buffer
  • Loading branch information
torvalds committed Sep 24, 2019
2 parents 0b36c9e + d8bd2d4 commit af5a7e9
Show file tree
Hide file tree
Showing 10 changed files with 613 additions and 134 deletions.
4 changes: 2 additions & 2 deletions drivers/hid/hid-hyperv.c
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,8 @@ struct synthhid_input_report {

#pragma pack(pop)

#define INPUTVSC_SEND_RING_BUFFER_SIZE (10*PAGE_SIZE)
#define INPUTVSC_RECV_RING_BUFFER_SIZE (10*PAGE_SIZE)
#define INPUTVSC_SEND_RING_BUFFER_SIZE (40 * 1024)
#define INPUTVSC_RECV_RING_BUFFER_SIZE (40 * 1024)


enum pipe_prot_msg_type {
Expand Down
161 changes: 144 additions & 17 deletions drivers/hv/channel_mgmt.c
Original file line number Diff line number Diff line change
Expand Up @@ -407,7 +407,15 @@ void hv_process_channel_removal(struct vmbus_channel *channel)
cpumask_clear_cpu(channel->target_cpu,
&primary_channel->alloced_cpus_in_node);

vmbus_release_relid(channel->offermsg.child_relid);
/*
* Upon suspend, an in-use hv_sock channel is marked as "rescinded" and
* the relid is invalidated; after hibernation, when the user-space app
* destroys the channel, the relid is INVALID_RELID, and in this case
* it's unnecessary and unsafe to release the old relid, since the same
* relid can refer to a completely different channel now.
*/
if (channel->offermsg.child_relid != INVALID_RELID)
vmbus_release_relid(channel->offermsg.child_relid);

free_channel(channel);
}
Expand Down Expand Up @@ -545,6 +553,10 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel)

mutex_lock(&vmbus_connection.channel_mutex);

/* 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.
Expand Down Expand Up @@ -847,19 +859,126 @@ void vmbus_initiate_unload(bool crash)
vmbus_wait_for_unload();
}

static void check_ready_for_resume_event(void)
{
/*
* If all the old primary channels have been fixed up, then it's safe
* to resume.
*/
if (atomic_dec_and_test(&vmbus_connection.nr_chan_fixup_on_resume))
complete(&vmbus_connection.ready_for_resume_event);
}

static void vmbus_setup_channel_state(struct vmbus_channel *channel,
struct vmbus_channel_offer_channel *offer)
{
/*
* Setup state for signalling the host.
*/
channel->sig_event = VMBUS_EVENT_CONNECTION_ID;

if (vmbus_proto_version != VERSION_WS2008) {
channel->is_dedicated_interrupt =
(offer->is_dedicated_interrupt != 0);
channel->sig_event = offer->connection_id;
}

memcpy(&channel->offermsg, offer,
sizeof(struct vmbus_channel_offer_channel));
channel->monitor_grp = (u8)offer->monitorid / 32;
channel->monitor_bit = (u8)offer->monitorid % 32;
}

/*
* find_primary_channel_by_offer - Get the channel object given the new offer.
* This is only used in the resume path of hibernation.
*/
static struct vmbus_channel *
find_primary_channel_by_offer(const struct vmbus_channel_offer_channel *offer)
{
struct vmbus_channel *channel = NULL, *iter;
const guid_t *inst1, *inst2;

/* Ignore sub-channel offers. */
if (offer->offer.sub_channel_index != 0)
return NULL;

mutex_lock(&vmbus_connection.channel_mutex);

list_for_each_entry(iter, &vmbus_connection.chn_list, listentry) {
inst1 = &iter->offermsg.offer.if_instance;
inst2 = &offer->offer.if_instance;

if (guid_equal(inst1, inst2)) {
channel = iter;
break;
}
}

mutex_unlock(&vmbus_connection.channel_mutex);

return channel;
}

/*
* vmbus_onoffer - Handler for channel offers from vmbus in parent partition.
*
*/
static void vmbus_onoffer(struct vmbus_channel_message_header *hdr)
{
struct vmbus_channel_offer_channel *offer;
struct vmbus_channel *newchannel;
struct vmbus_channel *oldchannel, *newchannel;
size_t offer_sz;

offer = (struct vmbus_channel_offer_channel *)hdr;

trace_vmbus_onoffer(offer);

oldchannel = find_primary_channel_by_offer(offer);

if (oldchannel != NULL) {
atomic_dec(&vmbus_connection.offer_in_progress);

/*
* We're resuming from hibernation: all the sub-channel and
* hv_sock channels we had before the hibernation should have
* been cleaned up, and now we must be seeing a re-offered
* primary channel that we had before the hibernation.
*/

WARN_ON(oldchannel->offermsg.child_relid != INVALID_RELID);
/* Fix up the relid. */
oldchannel->offermsg.child_relid = offer->child_relid;

offer_sz = sizeof(*offer);
if (memcmp(offer, &oldchannel->offermsg, offer_sz) == 0) {
check_ready_for_resume_event();
return;
}

/*
* This is not an error, since the host can also change the
* other field(s) of the offer, e.g. on WS RS5 (Build 17763),
* the offer->connection_id of the Mellanox VF vmbus device
* can change when the host reoffers the device upon resume.
*/
pr_debug("vmbus offer changed: relid=%d\n",
offer->child_relid);

print_hex_dump_debug("Old vmbus offer: ", DUMP_PREFIX_OFFSET,
16, 4, &oldchannel->offermsg, offer_sz,
false);
print_hex_dump_debug("New vmbus offer: ", DUMP_PREFIX_OFFSET,
16, 4, offer, offer_sz, false);

/* Fix up the old channel. */
vmbus_setup_channel_state(oldchannel, offer);

check_ready_for_resume_event();

return;
}

/* Allocate the channel object and save this offer. */
newchannel = alloc_channel();
if (!newchannel) {
Expand All @@ -869,25 +988,21 @@ static void vmbus_onoffer(struct vmbus_channel_message_header *hdr)
return;
}

/*
* Setup state for signalling the host.
*/
newchannel->sig_event = VMBUS_EVENT_CONNECTION_ID;

if (vmbus_proto_version != VERSION_WS2008) {
newchannel->is_dedicated_interrupt =
(offer->is_dedicated_interrupt != 0);
newchannel->sig_event = offer->connection_id;
}

memcpy(&newchannel->offermsg, offer,
sizeof(struct vmbus_channel_offer_channel));
newchannel->monitor_grp = (u8)offer->monitorid / 32;
newchannel->monitor_bit = (u8)offer->monitorid % 32;
vmbus_setup_channel_state(newchannel, offer);

vmbus_process_offer(newchannel);
}

static void check_ready_for_suspend_event(void)
{
/*
* If all the sub-channels or hv_sock channels have been cleaned up,
* then it's safe to suspend.
*/
if (atomic_dec_and_test(&vmbus_connection.nr_chan_close_on_suspend))
complete(&vmbus_connection.ready_for_suspend_event);
}

/*
* vmbus_onoffer_rescind - Rescind offer handler.
*
Expand All @@ -898,6 +1013,7 @@ static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr)
struct vmbus_channel_rescind_offer *rescind;
struct vmbus_channel *channel;
struct device *dev;
bool clean_up_chan_for_suspend;

rescind = (struct vmbus_channel_rescind_offer *)hdr;

Expand Down Expand Up @@ -937,6 +1053,8 @@ static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr)
return;
}

clean_up_chan_for_suspend = is_hvsock_channel(channel) ||
is_sub_channel(channel);
/*
* Before setting channel->rescind in vmbus_rescind_cleanup(), we
* should make sure the channel callback is not running any more.
Expand All @@ -962,6 +1080,10 @@ static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr)
if (channel->device_obj) {
if (channel->chn_rescind_callback) {
channel->chn_rescind_callback(channel);

if (clean_up_chan_for_suspend)
check_ready_for_suspend_event();

return;
}
/*
Expand Down Expand Up @@ -994,6 +1116,11 @@ static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr)
}
mutex_unlock(&vmbus_connection.channel_mutex);
}

/* The "channel" may have been freed. Do not access it any longer. */

if (clean_up_chan_for_suspend)
check_ready_for_suspend_event();
}

void vmbus_hvsock_device_unregister(struct vmbus_channel *channel)
Expand Down
8 changes: 6 additions & 2 deletions drivers/hv/connection.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@
struct vmbus_connection vmbus_connection = {
.conn_state = DISCONNECTED,
.next_gpadl_handle = ATOMIC_INIT(0xE1E10),

.ready_for_suspend_event= COMPLETION_INITIALIZER(
vmbus_connection.ready_for_suspend_event),
.ready_for_resume_event = COMPLETION_INITIALIZER(
vmbus_connection.ready_for_resume_event),
};
EXPORT_SYMBOL_GPL(vmbus_connection);

Expand Down Expand Up @@ -59,8 +64,7 @@ static __u32 vmbus_get_next_version(__u32 current_version)
}
}

static int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo,
__u32 version)
int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, u32 version)
{
int ret = 0;
unsigned int cur_cpu;
Expand Down
66 changes: 37 additions & 29 deletions drivers/hv/hv.c
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ void hv_synic_free(void)
* retrieve the initialized message and event pages. Otherwise, we create and
* initialize the message and event pages.
*/
int hv_synic_init(unsigned int cpu)
void hv_synic_enable_regs(unsigned int cpu)
{
struct hv_per_cpu_context *hv_cpu
= per_cpu_ptr(hv_context.cpu_context, cpu);
Expand Down Expand Up @@ -196,6 +196,11 @@ int hv_synic_init(unsigned int cpu)
sctrl.enable = 1;

hv_set_synic_state(sctrl.as_uint64);
}

int hv_synic_init(unsigned int cpu)
{
hv_synic_enable_regs(cpu);

hv_stimer_init(cpu);

Expand All @@ -205,20 +210,45 @@ int hv_synic_init(unsigned int cpu)
/*
* hv_synic_cleanup - Cleanup routine for hv_synic_init().
*/
int hv_synic_cleanup(unsigned int cpu)
void hv_synic_disable_regs(unsigned int cpu)
{
union hv_synic_sint shared_sint;
union hv_synic_simp simp;
union hv_synic_siefp siefp;
union hv_synic_scontrol sctrl;

hv_get_synint_state(VMBUS_MESSAGE_SINT, shared_sint.as_uint64);

shared_sint.masked = 1;

/* Need to correctly cleanup in the case of SMP!!! */
/* Disable the interrupt */
hv_set_synint_state(VMBUS_MESSAGE_SINT, shared_sint.as_uint64);

hv_get_simp(simp.as_uint64);
simp.simp_enabled = 0;
simp.base_simp_gpa = 0;

hv_set_simp(simp.as_uint64);

hv_get_siefp(siefp.as_uint64);
siefp.siefp_enabled = 0;
siefp.base_siefp_gpa = 0;

hv_set_siefp(siefp.as_uint64);

/* Disable the global synic bit */
hv_get_synic_state(sctrl.as_uint64);
sctrl.enable = 0;
hv_set_synic_state(sctrl.as_uint64);
}

int hv_synic_cleanup(unsigned int cpu)
{
struct vmbus_channel *channel, *sc;
bool channel_found = false;
unsigned long flags;

hv_get_synic_state(sctrl.as_uint64);
if (sctrl.enable != 1)
return -EFAULT;

/*
* 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 need to
Expand Down Expand Up @@ -249,29 +279,7 @@ int hv_synic_cleanup(unsigned int cpu)

hv_stimer_cleanup(cpu);

hv_get_synint_state(VMBUS_MESSAGE_SINT, shared_sint.as_uint64);

shared_sint.masked = 1;

/* Need to correctly cleanup in the case of SMP!!! */
/* Disable the interrupt */
hv_set_synint_state(VMBUS_MESSAGE_SINT, shared_sint.as_uint64);

hv_get_simp(simp.as_uint64);
simp.simp_enabled = 0;
simp.base_simp_gpa = 0;

hv_set_simp(simp.as_uint64);

hv_get_siefp(siefp.as_uint64);
siefp.siefp_enabled = 0;
siefp.base_siefp_gpa = 0;

hv_set_siefp(siefp.as_uint64);

/* Disable the global synic bit */
sctrl.enable = 0;
hv_set_synic_state(sctrl.as_uint64);
hv_synic_disable_regs(cpu);

return 0;
}
Loading

0 comments on commit af5a7e9

Please sign in to comment.