Commit 6b2591c2 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'hyperv-next-signed' of git://git.kernel.org/pub/scm/linux/kernel/git/hyperv/linux

Pull hyper-v updates from Wei Liu:

 - a series from Andrea to support channel reassignment

 - a series from Vitaly to clean up Vmbus message handling

 - a series from Michael to clean up and augment hyperv-tlfs.h

 - patches from Andy to clean up GUID usage in Hyper-V code

 - a few other misc patches

* tag 'hyperv-next-signed' of git://git.kernel.org/pub/scm/linux/kernel/git/hyperv/linux: (29 commits)
  Drivers: hv: vmbus: Resolve more races involving init_vp_index()
  Drivers: hv: vmbus: Resolve race between init_vp_index() and CPU hotplug
  vmbus: Replace zero-length array with flexible-array
  Driver: hv: vmbus: drop a no long applicable comment
  hyper-v: Switch to use UUID types directly
  hyper-v: Replace open-coded variant of %*phN specifier
  hyper-v: Supply GUID pointer to printf() like functions
  hyper-v: Use UUID API for exporting the GUID (part 2)
  asm-generic/hyperv: Add definitions for Get/SetVpRegister hypercalls
  x86/hyperv: Split hyperv-tlfs.h into arch dependent and independent files
  x86/hyperv: Remove HV_PROCESSOR_POWER_STATE #defines
  KVM: x86: hyperv: Remove duplicate definitions of Reference TSC Page
  drivers: hv: remove redundant assignment to pointer primary_channel
  scsi: storvsc: Re-init stor_chns when a channel interrupt is re-assigned
  Drivers: hv: vmbus: Introduce the CHANNELMSG_MODIFYCHANNEL message type
  Drivers: hv: vmbus: Synchronize init_vp_index() vs. CPU hotplug
  Drivers: hv: vmbus: Remove the unused HV_LOCALIZED channel affinity logic
  PCI: hv: Prepare hv_compose_msi_msg() for the VMBus-channel-interrupt-to-vCPU reassignment functionality
  Drivers: hv: vmbus: Use a spin lock for synchronizing channel scheduling vs. channel removal
  hv_utils: Always execute the fcopy and vss callbacks in a tasklet
  ...
parents f1e45535 afaa33da
......@@ -7924,6 +7924,7 @@ F: drivers/pci/controller/pci-hyperv.c
F: drivers/scsi/storvsc_drv.c
F: drivers/uio/uio_hv_generic.c
F: drivers/video/fbdev/hyperv_fb.c
F: include/asm-generic/hyperv-tlfs.h
F: include/asm-generic/mshyperv.h
F: include/clocksource/hyperv_timer.h
F: include/linux/hyperv.h
......
......@@ -11,17 +11,6 @@
#include <linux/types.h>
#include <asm/page.h>
/*
* While not explicitly listed in the TLFS, Hyper-V always runs with a page size
* of 4096. These definitions are used when communicating with Hyper-V using
* guest physical pages and guest physical page addresses, since the guest page
* size may not be 4096 on all architectures.
*/
#define HV_HYP_PAGE_SHIFT 12
#define HV_HYP_PAGE_SIZE BIT(HV_HYP_PAGE_SHIFT)
#define HV_HYP_PAGE_MASK (~(HV_HYP_PAGE_SIZE - 1))
/*
* The below CPUID leaves are present if VersionAndFeatures.HypervisorPresent
* is set by CPUID(HvCpuIdFunctionVersionAndFeatures).
......@@ -39,78 +28,41 @@
#define HYPERV_CPUID_MAX 0x4000ffff
/*
* Feature identification. EAX indicates which features are available
* to the partition based upon the current partition privileges.
* These are HYPERV_CPUID_FEATURES.EAX bits.
* Aliases for Group A features that have X64 in the name.
* On x86/x64 these are HYPERV_CPUID_FEATURES.EAX bits.
*/
/* VP Runtime (HV_X64_MSR_VP_RUNTIME) available */
#define HV_X64_MSR_VP_RUNTIME_AVAILABLE BIT(0)
/* Partition Reference Counter (HV_X64_MSR_TIME_REF_COUNT) available*/
#define HV_MSR_TIME_REF_COUNT_AVAILABLE BIT(1)
/*
* Basic SynIC MSRs (HV_X64_MSR_SCONTROL through HV_X64_MSR_EOM
* and HV_X64_MSR_SINT0 through HV_X64_MSR_SINT15) available
*/
#define HV_X64_MSR_SYNIC_AVAILABLE BIT(2)
/*
* Synthetic Timer MSRs (HV_X64_MSR_STIMER0_CONFIG through
* HV_X64_MSR_STIMER3_COUNT) available
*/
#define HV_MSR_SYNTIMER_AVAILABLE BIT(3)
/*
* APIC access MSRs (HV_X64_MSR_EOI, HV_X64_MSR_ICR and HV_X64_MSR_TPR)
* are available
*/
#define HV_X64_MSR_APIC_ACCESS_AVAILABLE BIT(4)
/* Hypercall MSRs (HV_X64_MSR_GUEST_OS_ID and HV_X64_MSR_HYPERCALL) available*/
#define HV_X64_MSR_HYPERCALL_AVAILABLE BIT(5)
/* Access virtual processor index MSR (HV_X64_MSR_VP_INDEX) available*/
#define HV_X64_MSR_VP_INDEX_AVAILABLE BIT(6)
/* Virtual system reset MSR (HV_X64_MSR_RESET) is available*/
#define HV_X64_MSR_RESET_AVAILABLE BIT(7)
/*
* Access statistics pages MSRs (HV_X64_MSR_STATS_PARTITION_RETAIL_PAGE,
* HV_X64_MSR_STATS_PARTITION_INTERNAL_PAGE, HV_X64_MSR_STATS_VP_RETAIL_PAGE,
* HV_X64_MSR_STATS_VP_INTERNAL_PAGE) available
*/
#define HV_X64_MSR_STAT_PAGES_AVAILABLE BIT(8)
/* Partition reference TSC MSR is available */
#define HV_MSR_REFERENCE_TSC_AVAILABLE BIT(9)
/* Partition Guest IDLE MSR is available */
#define HV_X64_MSR_GUEST_IDLE_AVAILABLE BIT(10)
/*
* There is a single feature flag that signifies if the partition has access
* to MSRs with local APIC and TSC frequencies.
*/
#define HV_X64_ACCESS_FREQUENCY_MSRS BIT(11)
/* AccessReenlightenmentControls privilege */
#define HV_X64_ACCESS_REENLIGHTENMENT BIT(13)
/* AccessTscInvariantControls privilege */
#define HV_X64_ACCESS_TSC_INVARIANT BIT(15)
#define HV_X64_MSR_VP_RUNTIME_AVAILABLE \
HV_MSR_VP_RUNTIME_AVAILABLE
#define HV_X64_MSR_SYNIC_AVAILABLE \
HV_MSR_SYNIC_AVAILABLE
#define HV_X64_MSR_APIC_ACCESS_AVAILABLE \
HV_MSR_APIC_ACCESS_AVAILABLE
#define HV_X64_MSR_HYPERCALL_AVAILABLE \
HV_MSR_HYPERCALL_AVAILABLE
#define HV_X64_MSR_VP_INDEX_AVAILABLE \
HV_MSR_VP_INDEX_AVAILABLE
#define HV_X64_MSR_RESET_AVAILABLE \
HV_MSR_RESET_AVAILABLE
#define HV_X64_MSR_GUEST_IDLE_AVAILABLE \
HV_MSR_GUEST_IDLE_AVAILABLE
#define HV_X64_ACCESS_FREQUENCY_MSRS \
HV_ACCESS_FREQUENCY_MSRS
#define HV_X64_ACCESS_REENLIGHTENMENT \
HV_ACCESS_REENLIGHTENMENT
#define HV_X64_ACCESS_TSC_INVARIANT \
HV_ACCESS_TSC_INVARIANT
/*
* Feature identification: indicates which flags were specified at partition
* creation. The format is the same as the partition creation flag structure
* defined in section Partition Creation Flags.
* These are HYPERV_CPUID_FEATURES.EBX bits.
* Aliases for Group B features that have X64 in the name.
* On x86/x64 these are HYPERV_CPUID_FEATURES.EBX bits.
*/
#define HV_X64_CREATE_PARTITIONS BIT(0)
#define HV_X64_ACCESS_PARTITION_ID BIT(1)
#define HV_X64_ACCESS_MEMORY_POOL BIT(2)
#define HV_X64_ADJUST_MESSAGE_BUFFERS BIT(3)
#define HV_X64_POST_MESSAGES BIT(4)
#define HV_X64_SIGNAL_EVENTS BIT(5)
#define HV_X64_CREATE_PORT BIT(6)
#define HV_X64_CONNECT_PORT BIT(7)
#define HV_X64_ACCESS_STATS BIT(8)
#define HV_X64_DEBUGGING BIT(11)
#define HV_X64_CPU_POWER_MANAGEMENT BIT(12)
#define HV_X64_POST_MESSAGES HV_POST_MESSAGES
#define HV_X64_SIGNAL_EVENTS HV_SIGNAL_EVENTS
/*
* Feature identification. EDX indicates which miscellaneous features
* are available to the partition.
* These are HYPERV_CPUID_FEATURES.EDX bits.
* Group D Features. The bit assignments are custom to each architecture.
* On x86/x64 these are HYPERV_CPUID_FEATURES.EDX bits.
*/
/* The MWAIT instruction is available (per section MONITOR / MWAIT) */
#define HV_X64_MWAIT_AVAILABLE BIT(0)
......@@ -187,7 +139,7 @@
* processor, except for virtual processors that are reported as sibling SMT
* threads.
*/
#define HV_X64_NO_NONARCH_CORESHARING BIT(18)
#define HV_X64_NO_NONARCH_CORESHARING BIT(18)
/* Nested features. These are HYPERV_CPUID_NESTED_FEATURES.EAX bits. */
#define HV_X64_NESTED_DIRECT_FLUSH BIT(17)
......@@ -295,43 +247,6 @@ union hv_x64_msr_hypercall_contents {
} __packed;
};
/*
* TSC page layout.
*/
struct ms_hyperv_tsc_page {
volatile u32 tsc_sequence;
u32 reserved1;
volatile u64 tsc_scale;
volatile s64 tsc_offset;
u64 reserved2[509];
} __packed;
/*
* The guest OS needs to register the guest ID with the hypervisor.
* The guest ID is a 64 bit entity and the structure of this ID is
* specified in the Hyper-V specification:
*
* msdn.microsoft.com/en-us/library/windows/hardware/ff542653%28v=vs.85%29.aspx
*
* While the current guideline does not specify how Linux guest ID(s)
* need to be generated, our plan is to publish the guidelines for
* Linux and other guest operating systems that currently are hosted
* on Hyper-V. The implementation here conforms to this yet
* unpublished guidelines.
*
*
* Bit(s)
* 63 - Indicates if the OS is Open Source or not; 1 is Open Source
* 62:56 - Os Type; Linux is 0x100
* 55:48 - Distro specific identification
* 47:16 - Linux kernel version number
* 15:0 - Distro specific identification
*
*
*/
#define HV_LINUX_VENDOR_ID 0x8100
struct hv_reenlightenment_control {
__u64 vector:8;
__u64 reserved1:8;
......@@ -355,31 +270,12 @@ struct hv_tsc_emulation_status {
#define HV_X64_MSR_HYPERCALL_PAGE_ADDRESS_MASK \
(~((1ull << HV_X64_MSR_HYPERCALL_PAGE_ADDRESS_SHIFT) - 1))
/*
* Crash notification (HV_X64_MSR_CRASH_CTL) flags.
*/
#define HV_CRASH_CTL_CRASH_NOTIFY_MSG BIT_ULL(62)
#define HV_CRASH_CTL_CRASH_NOTIFY BIT_ULL(63)
#define HV_X64_MSR_CRASH_PARAMS \
(1 + (HV_X64_MSR_CRASH_P4 - HV_X64_MSR_CRASH_P0))
#define HV_IPI_LOW_VECTOR 0x10
#define HV_IPI_HIGH_VECTOR 0xff
/* Declare the various hypercall operations. */
#define HVCALL_FLUSH_VIRTUAL_ADDRESS_SPACE 0x0002
#define HVCALL_FLUSH_VIRTUAL_ADDRESS_LIST 0x0003
#define HVCALL_NOTIFY_LONG_SPIN_WAIT 0x0008
#define HVCALL_SEND_IPI 0x000b
#define HVCALL_FLUSH_VIRTUAL_ADDRESS_SPACE_EX 0x0013
#define HVCALL_FLUSH_VIRTUAL_ADDRESS_LIST_EX 0x0014
#define HVCALL_SEND_IPI_EX 0x0015
#define HVCALL_POST_MESSAGE 0x005c
#define HVCALL_SIGNAL_EVENT 0x005d
#define HVCALL_RETARGET_INTERRUPT 0x007e
#define HVCALL_FLUSH_GUEST_PHYSICAL_ADDRESS_SPACE 0x00af
#define HVCALL_FLUSH_GUEST_PHYSICAL_ADDRESS_LIST 0x00b0
#define HV_X64_MSR_VP_ASSIST_PAGE_ENABLE 0x00000001
#define HV_X64_MSR_VP_ASSIST_PAGE_ADDRESS_SHIFT 12
#define HV_X64_MSR_VP_ASSIST_PAGE_ADDRESS_MASK \
......@@ -391,75 +287,6 @@ struct hv_tsc_emulation_status {
#define HV_X64_MSR_TSC_REFERENCE_ENABLE 0x00000001
#define HV_X64_MSR_TSC_REFERENCE_ADDRESS_SHIFT 12
#define HV_PROCESSOR_POWER_STATE_C0 0
#define HV_PROCESSOR_POWER_STATE_C1 1
#define HV_PROCESSOR_POWER_STATE_C2 2
#define HV_PROCESSOR_POWER_STATE_C3 3
#define HV_FLUSH_ALL_PROCESSORS BIT(0)
#define HV_FLUSH_ALL_VIRTUAL_ADDRESS_SPACES BIT(1)
#define HV_FLUSH_NON_GLOBAL_MAPPINGS_ONLY BIT(2)
#define HV_FLUSH_USE_EXTENDED_RANGE_FORMAT BIT(3)
enum HV_GENERIC_SET_FORMAT {
HV_GENERIC_SET_SPARSE_4K,
HV_GENERIC_SET_ALL,
};
#define HV_PARTITION_ID_SELF ((u64)-1)
#define HV_HYPERCALL_RESULT_MASK GENMASK_ULL(15, 0)
#define HV_HYPERCALL_FAST_BIT BIT(16)
#define HV_HYPERCALL_VARHEAD_OFFSET 17
#define HV_HYPERCALL_REP_COMP_OFFSET 32
#define HV_HYPERCALL_REP_COMP_MASK GENMASK_ULL(43, 32)
#define HV_HYPERCALL_REP_START_OFFSET 48
#define HV_HYPERCALL_REP_START_MASK GENMASK_ULL(59, 48)
/* hypercall status code */
#define HV_STATUS_SUCCESS 0
#define HV_STATUS_INVALID_HYPERCALL_CODE 2
#define HV_STATUS_INVALID_HYPERCALL_INPUT 3
#define HV_STATUS_INVALID_ALIGNMENT 4
#define HV_STATUS_INVALID_PARAMETER 5
#define HV_STATUS_INSUFFICIENT_MEMORY 11
#define HV_STATUS_INVALID_PORT_ID 17
#define HV_STATUS_INVALID_CONNECTION_ID 18
#define HV_STATUS_INSUFFICIENT_BUFFERS 19
/*
* The Hyper-V TimeRefCount register and the TSC
* page provide a guest VM clock with 100ns tick rate
*/
#define HV_CLOCK_HZ (NSEC_PER_SEC/100)
typedef struct _HV_REFERENCE_TSC_PAGE {
__u32 tsc_sequence;
__u32 res1;
__u64 tsc_scale;
__s64 tsc_offset;
} __packed HV_REFERENCE_TSC_PAGE, *PHV_REFERENCE_TSC_PAGE;
/* Define the number of synthetic interrupt sources. */
#define HV_SYNIC_SINT_COUNT (16)
/* Define the expected SynIC version. */
#define HV_SYNIC_VERSION_1 (0x1)
/* Valid SynIC vectors are 16-255. */
#define HV_SYNIC_FIRST_VALID_VECTOR (16)
#define HV_SYNIC_CONTROL_ENABLE (1ULL << 0)
#define HV_SYNIC_SIMP_ENABLE (1ULL << 0)
#define HV_SYNIC_SIEFP_ENABLE (1ULL << 0)
#define HV_SYNIC_SINT_MASKED (1ULL << 16)
#define HV_SYNIC_SINT_AUTO_EOI (1ULL << 17)
#define HV_SYNIC_SINT_VECTOR_MASK (0xFF)
#define HV_SYNIC_STIMER_COUNT (4)
/* Define synthetic interrupt controller message constants. */
#define HV_MESSAGE_SIZE (256)
#define HV_MESSAGE_PAYLOAD_BYTE_COUNT (240)
#define HV_MESSAGE_PAYLOAD_QWORD_COUNT (30)
/* Define hypervisor message types. */
enum hv_message_type {
......@@ -470,76 +297,25 @@ enum hv_message_type {
HVMSG_GPA_INTERCEPT = 0x80000001,
/* Timer notification messages. */
HVMSG_TIMER_EXPIRED = 0x80000010,
HVMSG_TIMER_EXPIRED = 0x80000010,
/* Error messages. */
HVMSG_INVALID_VP_REGISTER_VALUE = 0x80000020,
HVMSG_UNRECOVERABLE_EXCEPTION = 0x80000021,
HVMSG_UNSUPPORTED_FEATURE = 0x80000022,
HVMSG_UNSUPPORTED_FEATURE = 0x80000022,
/* Trace buffer complete messages. */
HVMSG_EVENTLOG_BUFFERCOMPLETE = 0x80000040,
/* Platform-specific processor intercept messages. */
HVMSG_X64_IOPORT_INTERCEPT = 0x80010000,
HVMSG_X64_IOPORT_INTERCEPT = 0x80010000,
HVMSG_X64_MSR_INTERCEPT = 0x80010001,
HVMSG_X64_CPUID_INTERCEPT = 0x80010002,
HVMSG_X64_CPUID_INTERCEPT = 0x80010002,
HVMSG_X64_EXCEPTION_INTERCEPT = 0x80010003,
HVMSG_X64_APIC_EOI = 0x80010004,
HVMSG_X64_LEGACY_FP_ERROR = 0x80010005
};
/* Define synthetic interrupt controller message flags. */
union hv_message_flags {
__u8 asu8;
struct {
__u8 msg_pending:1;
__u8 reserved:7;
} __packed;
};
/* Define port identifier type. */
union hv_port_id {
__u32 asu32;
struct {
__u32 id:24;
__u32 reserved:8;
} __packed u;
HVMSG_X64_APIC_EOI = 0x80010004,
HVMSG_X64_LEGACY_FP_ERROR = 0x80010005
};
/* Define synthetic interrupt controller message header. */
struct hv_message_header {
__u32 message_type;
__u8 payload_size;
union hv_message_flags message_flags;
__u8 reserved[2];
union {
__u64 sender;
union hv_port_id port;
};
} __packed;
/* Define synthetic interrupt controller message format. */
struct hv_message {
struct hv_message_header header;
union {
__u64 payload[HV_MESSAGE_PAYLOAD_QWORD_COUNT];
} u;
} __packed;
/* Define the synthetic interrupt message page layout. */
struct hv_message_page {
struct hv_message sint_message[HV_SYNIC_SINT_COUNT];
} __packed;
/* Define timer message payload structure. */
struct hv_timer_message_payload {
__u32 timer_index;
__u32 reserved;
__u64 expiration_time; /* When the timer expired */
__u64 delivery_time; /* When the message was delivered */
} __packed;
struct hv_nested_enlightenments_control {
struct {
__u32 directhypercall:1;
......@@ -767,187 +543,11 @@ struct hv_enlightened_vmcs {
#define HV_VMX_ENLIGHTENED_CLEAN_FIELD_ALL 0xFFFF
/* Define synthetic interrupt controller flag constants. */
#define HV_EVENT_FLAGS_COUNT (256 * 8)
#define HV_EVENT_FLAGS_LONG_COUNT (256 / sizeof(unsigned long))
/*
* Synthetic timer configuration.
*/
union hv_stimer_config {
u64 as_uint64;
struct {
u64 enable:1;
u64 periodic:1;
u64 lazy:1;
u64 auto_enable:1;
u64 apic_vector:8;
u64 direct_mode:1;
u64 reserved_z0:3;
u64 sintx:4;
u64 reserved_z1:44;
} __packed;
};
/* Define the synthetic interrupt controller event flags format. */
union hv_synic_event_flags {
unsigned long flags[HV_EVENT_FLAGS_LONG_COUNT];
};
/* Define SynIC control register. */
union hv_synic_scontrol {
u64 as_uint64;
struct {
u64 enable:1;
u64 reserved:63;
} __packed;
};
/* Define synthetic interrupt source. */
union hv_synic_sint {
u64 as_uint64;
struct {
u64 vector:8;
u64 reserved1:8;
u64 masked:1;
u64 auto_eoi:1;
u64 polling:1;
u64 reserved2:45;
} __packed;
};
/* Define the format of the SIMP register */
union hv_synic_simp {
u64 as_uint64;
struct {
u64 simp_enabled:1;
u64 preserved:11;
u64 base_simp_gpa:52;
} __packed;
};
/* Define the format of the SIEFP register */
union hv_synic_siefp {
u64 as_uint64;
struct {
u64 siefp_enabled:1;
u64 preserved:11;
u64 base_siefp_gpa:52;
} __packed;
};
struct hv_vpset {
u64 format;
u64 valid_bank_mask;
u64 bank_contents[];
} __packed;
/* HvCallSendSyntheticClusterIpi hypercall */
struct hv_send_ipi {
u32 vector;
u32 reserved;
u64 cpu_mask;
} __packed;
/* HvCallSendSyntheticClusterIpiEx hypercall */
struct hv_send_ipi_ex {
u32 vector;
u32 reserved;
struct hv_vpset vp_set;
} __packed;
/* HvFlushGuestPhysicalAddressSpace hypercalls */
struct hv_guest_mapping_flush {
u64 address_space;
u64 flags;
} __packed;
/*
* HV_MAX_FLUSH_PAGES = "additional_pages" + 1. It's limited
* by the bitwidth of "additional_pages" in union hv_gpa_page_range.
*/
#define HV_MAX_FLUSH_PAGES (2048)
/* HvFlushGuestPhysicalAddressList hypercall */
union hv_gpa_page_range {
u64 address_space;
struct {
u64 additional_pages:11;
u64 largepage:1;
u64 basepfn:52;
} page;
};
/*
* All input flush parameters should be in single page. The max flush
* count is equal with how many entries of union hv_gpa_page_range can
* be populated into the input parameter page.
*/
#define HV_MAX_FLUSH_REP_COUNT ((HV_HYP_PAGE_SIZE - 2 * sizeof(u64)) / \
sizeof(union hv_gpa_page_range))
struct hv_guest_mapping_flush_list {
u64 address_space;
u64 flags;
union hv_gpa_page_range gpa_list[HV_MAX_FLUSH_REP_COUNT];
};
/* HvFlushVirtualAddressSpace, HvFlushVirtualAddressList hypercalls */
struct hv_tlb_flush {
u64 address_space;
u64 flags;
u64 processor_mask;
u64 gva_list[];
} __packed;
/* HvFlushVirtualAddressSpaceEx, HvFlushVirtualAddressListEx hypercalls */
struct hv_tlb_flush_ex {
u64 address_space;
u64 flags;
struct hv_vpset hv_vp_set;
u64 gva_list[];
} __packed;
struct hv_partition_assist_pg {
u32 tlb_lock_count;
};
union hv_msi_entry {
u64 as_uint64;
struct {
u32 address;
u32 data;
} __packed;
};
struct hv_interrupt_entry {
u32 source; /* 1 for MSI(-X) */
u32 reserved1;
union hv_msi_entry msi_entry;
} __packed;
/*
* flags for hv_device_interrupt_target.flags
*/
#define HV_DEVICE_INTERRUPT_TARGET_MULTICAST 1
#define HV_DEVICE_INTERRUPT_TARGET_PROCESSOR_SET 2
struct hv_device_interrupt_target {
u32 vector;
u32 flags;
union {
u64 vp_mask;
struct hv_vpset vp_set;
};
} __packed;
#include <asm-generic/hyperv-tlfs.h>
/* HvRetargetDeviceInterrupt hypercall */
struct hv_retarget_device_interrupt {
u64 partition_id; /* use "self" */
u64 device_id;
struct hv_interrupt_entry int_entry;
u64 reserved2;
struct hv_device_interrupt_target int_target;
} __packed __aligned(8);
#endif
......@@ -866,7 +866,7 @@ struct kvm_hv {
u64 hv_crash_param[HV_X64_MSR_CRASH_PARAMS];
u64 hv_crash_ctl;
HV_REFERENCE_TSC_PAGE tsc_ref;
struct ms_hyperv_tsc_page tsc_ref;
struct idr conn_to_evt;
......
......@@ -900,7 +900,7 @@ static int kvm_hv_msr_set_crash_data(struct kvm_vcpu *vcpu,
* These two equivalencies are implemented in this function.
*/
static bool compute_tsc_page_parameters(struct pvclock_vcpu_time_info *hv_clock,
HV_REFERENCE_TSC_PAGE *tsc_ref)
struct ms_hyperv_tsc_page *tsc_ref)
{
u64 max_mul;
......@@ -941,7 +941,7 @@ void kvm_hv_setup_tsc_page(struct kvm *kvm,
u64 gfn;
BUILD_BUG_ON(sizeof(tsc_seq) != sizeof(hv->tsc_ref.tsc_sequence));
BUILD_BUG_ON(offsetof(HV_REFERENCE_TSC_PAGE, tsc_sequence) != 0);
BUILD_BUG_ON(offsetof(struct ms_hyperv_tsc_page, tsc_sequence) != 0);
if (!(hv->hv_tsc_page & HV_X64_MSR_TSC_REFERENCE_ENABLE))
return;
......
......@@ -289,6 +289,34 @@ int vmbus_send_tl_connect_request(const guid_t *shv_guest_servie_id,
}
EXPORT_SYMBOL_GPL(vmbus_send_tl_connect_request);
/*
* Set/change the vCPU (@target_vp) the channel (@child_relid) will interrupt.
*
* CHANNELMSG_MODIFYCHANNEL messages are aynchronous. Also, Hyper-V does not
* ACK such messages. IOW we can't know when the host will stop interrupting
* the "old" vCPU and start interrupting the "new" vCPU for the given channel.
*
* The CHANNELMSG_MODIFYCHANNEL message type is supported since VMBus version
* VERSION_WIN10_V4_1.
*/
int vmbus_send_modifychannel(u32 child_relid, u32 target_vp)
{
struct vmbus_channel_modifychannel conn_msg;
int ret;
memset(&conn_msg, 0, sizeof(conn_msg));
conn_msg.header.msgtype = CHANNELMSG_MODIFYCHANNEL;
conn_msg.child_relid = child_relid;
conn_msg.target_vp = target_vp;
ret = vmbus_post_msg(&conn_msg, sizeof(conn_msg), true);
trace_vmbus_send_modifychannel(&conn_msg, ret);
return ret;
}
EXPORT_SYMBOL_GPL(vmbus_send_modifychannel);
/*
* create_gpadl_header - Creates a gpadl for the specified buffer
*/
......@@ -594,35 +622,31 @@ int vmbus_teardown_gpadl(struct vmbus_channel *channel, u32 gpadl_handle)
}
EXPORT_SYMBOL_GPL(vmbus_teardown_gpadl);
static void reset_channel_cb(void *arg)
{
struct vmbus_channel *channel = arg;
channel->onchannel_callback = NULL;
}
void vmbus_reset_channel_cb(struct vmbus_channel *channel)
{
unsigned long flags;
/*
* vmbus_on_event(), running in the per-channel tasklet, can race
* with vmbus_close_internal() in the case of SMP guest, e.g., when
* the former is accessing channel->inbound.ring_buffer, the latter
* could be freeing the ring_buffer pages, so here we must stop it
* first.
*
* vmbus_chan_sched() might call the netvsc driver callback function
* that ends up scheduling NAPI work that accesses the ring buffer.
* At this point, we have to ensure that any such work is completed
* and that the channel ring buffer is no longer being accessed, cf.
* the calls to napi_disable() in netvsc_device_remove().
*/
tasklet_disable(&channel->callback_event);
channel->sc_creation_callback = NULL;
/* See the inline comments in vmbus_chan_sched(). */
spin_lock_irqsave(&channel->sched_lock, flags);
channel->onchannel_callback = NULL;
spin_unlock_irqrestore(&channel->sched_lock, flags);
/* Stop the callback asap */
if (channel->target_cpu != get_cpu()) {
put_cpu();
smp_call_function_single(channel->target_cpu, reset_channel_cb,
channel, true);
} else {
reset_channel_cb(channel);
put_cpu();
}
channel->sc_creation_callback = NULL;
/* Re-enable tasklet for use on re-open */
tasklet_enable(&channel->callback_event);
......
......@@ -18,14 +18,15 @@
#include <linux/module.h>
#include <linux/completion.h>
#include <linux/delay.h>
#include <linux/cpu.h>
#include <linux/hyperv.h>
#include <asm/mshyperv.h>
#include "hyperv_vmbus.h"
static void init_vp_index(struct vmbus_channel *channel, u16 dev_type);
static void init_vp_index(struct vmbus_channel *channel);
static const struct vmbus_device vmbus_devs[] = {
const struct vmbus_device vmbus_devs[] = {
/* IDE */
{ .dev_type = HV_IDE,
HV_IDE_GUID,
......@@ -315,11 +316,11 @@ static struct vmbus_channel *alloc_channel(void)
if (!channel)
return NULL;
spin_lock_init(&channel->sched_lock);
spin_lock_init(&channel->lock);
init_completion(&channel->rescind_event);
INIT_LIST_HEAD(&channel->sc_list);
INIT_LIST_HEAD(&channel->percpu_list);
tasklet_init(&channel->callback_event,
vmbus_on_event, (unsigned long)channel);
......@@ -340,23 +341,49 @@ static void free_channel(struct vmbus_channel *channel)
kobject_put(&channel->kobj);
}
static void percpu_channel_enq(void *arg)
void vmbus_channel_map_relid(struct vmbus_channel *channel)
{
struct vmbus_channel *channel = arg;
struct hv_per_cpu_context *hv_cpu
= this_cpu_ptr(hv_context.cpu_context);
list_add_tail_rcu(&channel->percpu_list, &hv_cpu->chan_list);
if (WARN_ON(channel->offermsg.child_relid >= MAX_CHANNEL_RELIDS))
return;
/*
* 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
* execute:
*
* (a) In the "normal (i.e., not resuming from hibernation)" path,
* the full barrier in smp_store_mb() guarantees that the store
* is propagated to all CPUs before the add_channel_work work
* is queued. In turn, add_channel_work is queued before the
* channel's ring buffer is allocated/initialized and the
* OPENCHANNEL message for the channel is sent in vmbus_open().
* Hyper-V won't start sending the interrupts for the channel
* before the OPENCHANNEL message is acked. The memory barrier
* in vmbus_chan_sched() -> sync_test_and_clear_bit() ensures
* that vmbus_chan_sched() must find the channel's relid in
* recv_int_page before retrieving the channel pointer from the
* array of channels.
*
* (b) In the "resuming from hibernation" path, the smp_store_mb()
* guarantees that the store is propagated to all CPUs before
* the VMBus connection is marked as ready for the resume event
* (cf. check_ready_for_resume_event()). The interrupt handler
* of the VMBus driver and vmbus_chan_sched() can not run before
* vmbus_bus_resume() has completed execution (cf. resume_noirq).
*/
smp_store_mb(
vmbus_connection.channels[channel->offermsg.child_relid],
channel);
}
static void percpu_channel_deq(void *arg)
void vmbus_channel_unmap_relid(struct vmbus_channel *channel)
{
struct vmbus_channel *channel = arg;
list_del_rcu(&channel->percpu_list);
if (WARN_ON(channel->offermsg.child_relid >= MAX_CHANNEL_RELIDS))
return;
WRITE_ONCE(
vmbus_connection.channels[channel->offermsg.child_relid],
NULL);
}
static void vmbus_release_relid(u32 relid)
{
struct vmbus_channel_relid_released msg;
......@@ -373,39 +400,43 @@ static void vmbus_release_relid(u32 relid)
void hv_process_channel_removal(struct vmbus_channel *channel)
{
struct vmbus_channel *primary_channel;
unsigned long flags;
BUG_ON(!mutex_is_locked(&vmbus_connection.channel_mutex));
lockdep_assert_held(&vmbus_connection.channel_mutex);
BUG_ON(!channel->rescind);
if (channel->target_cpu != get_cpu()) {
put_cpu();
smp_call_function_single(channel->target_cpu,
percpu_channel_deq, channel, true);
} else {
percpu_channel_deq(channel);
put_cpu();
}
/*
* hv_process_channel_removal() could find INVALID_RELID only for
* hv_sock channels. See the inline comments in vmbus_onoffer().
*/
WARN_ON(channel->offermsg.child_relid == INVALID_RELID &&
!is_hvsock_channel(channel));
/*
* Upon suspend, an in-use hv_sock channel is removed from the array of
* channels and the relid is invalidated. After hibernation, when the
* user-space appplication destroys the channel, it's unnecessary and
* unsafe to remove the channel from the array of channels. See also
* the inline comments before the call of vmbus_release_relid() below.
*/
if (channel->offermsg.child_relid != INVALID_RELID)
vmbus_channel_unmap_relid(channel);
if (channel->primary_channel == NULL) {
list_del(&channel->listentry);
primary_channel = channel;
} else {
primary_channel = channel->primary_channel;
struct vmbus_channel *primary_channel = channel->primary_channel;
spin_lock_irqsave(&primary_channel->lock, flags);
list_del(&channel->sc_list);
spin_unlock_irqrestore(&primary_channel->lock, flags);
}
/*
* We need to free the bit for init_vp_index() to work in the case
* of sub-channel, when we reload drivers like hv_netvsc.
* If this is a "perf" channel, updates the hv_numa_map[] masks so that
* init_vp_index() can (re-)use the CPU.
*/
if (channel->affinity_policy == HV_LOCALIZED)
cpumask_clear_cpu(channel->target_cpu,
&primary_channel->alloced_cpus_in_node);
if (hv_is_perf_channel(channel))
hv_clear_alloced_cpu(channel->target_cpu);
/*
* Upon suspend, an in-use hv_sock channel is marked as "rescinded" and
......@@ -440,23 +471,8 @@ static void vmbus_add_channel_work(struct work_struct *work)
container_of(work, struct vmbus_channel, add_channel_work);
struct vmbus_channel *primary_channel = newchannel->primary_channel;
unsigned long flags;
u16 dev_type;
int ret;
dev_type = hv_get_dev_type(newchannel);
init_vp_index(newchannel, dev_type);
if (newchannel->target_cpu != get_cpu()) {
put_cpu();
smp_call_function_single(newchannel->target_cpu,
percpu_channel_enq,
newchannel, true);
} else {
percpu_channel_enq(newchannel);
put_cpu();
}
/*
* This state is used to indicate a successful open
* so that when we do close the channel normally, we
......@@ -488,7 +504,7 @@ static void vmbus_add_channel_work(struct work_struct *work)
if (!newchannel->device_obj)
goto err_deq_chan;
newchannel->device_obj->device_id = dev_type;
newchannel->device_obj->device_id = newchannel->device_id;
/*
* Add the new device to the bus. This will kick off device-driver
* binding which eventually invokes the device driver's AddDevice()
......@@ -523,17 +539,10 @@ static void vmbus_add_channel_work(struct work_struct *work)
spin_unlock_irqrestore(&primary_channel->lock, flags);
}
mutex_unlock(&vmbus_connection.channel_mutex);
/* vmbus_process_offer() has mapped the channel. */
vmbus_channel_unmap_relid(newchannel);
if (newchannel->target_cpu != get_cpu()) {
put_cpu();
smp_call_function_single(newchannel->target_cpu,
percpu_channel_deq,
newchannel, true);
} else {
percpu_channel_deq(newchannel);
put_cpu();
}
mutex_unlock(&vmbus_connection.channel_mutex);
vmbus_release_relid(newchannel->offermsg.child_relid);
......@@ -551,8 +560,35 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel)
unsigned long flags;
bool fnew = true;
/*
* Synchronize vmbus_process_offer() and CPU hotplugging:
*
* CPU1 CPU2
*
* [vmbus_process_offer()] [Hot removal of the CPU]
*
* CPU_READ_LOCK CPUS_WRITE_LOCK
* LOAD cpu_online_mask SEARCH chn_list
* STORE target_cpu LOAD target_cpu
* INSERT chn_list STORE cpu_online_mask
* CPUS_READ_UNLOCK CPUS_WRITE_UNLOCK
*
* Forbids: CPU1's LOAD from *not* seing CPU2's STORE &&
* CPU2's SEARCH from *not* seeing CPU1's INSERT
*
* Forbids: CPU2's SEARCH from seeing CPU1's INSERT &&
* CPU2's LOAD from *not* seing CPU1's STORE
*/
cpus_read_lock();
/*
* Serializes the modifications of the chn_list list as well as
* the accesses to next_numa_node_id in init_vp_index().
*/
mutex_lock(&vmbus_connection.channel_mutex);
init_vp_index(newchannel);
/* 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);
......@@ -599,7 +635,10 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel)
spin_unlock_irqrestore(&channel->lock, flags);
}
vmbus_channel_map_relid(newchannel);
mutex_unlock(&vmbus_connection.channel_mutex);
cpus_read_unlock();
/*
* vmbus_process_offer() mustn't call channel->sc_creation_callback()
......@@ -632,73 +671,61 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel)
* We use this state to statically distribute the channel interrupt load.
*/
static int next_numa_node_id;
/*
* init_vp_index() accesses global variables like next_numa_node_id, and
* it can run concurrently for primary channels and sub-channels: see
* vmbus_process_offer(), so we need the lock to protect the global
* variables.
*/
static DEFINE_SPINLOCK(bind_channel_to_cpu_lock);
/*
* Starting with Win8, we can statically distribute the incoming
* channel interrupt load by binding a channel to VCPU.
* We distribute the interrupt loads to one or more NUMA nodes based on
* the channel's affinity_policy.
*
* For pre-win8 hosts or non-performance critical channels we assign the
* first CPU in the first NUMA node.
* VMBUS_CONNECT_CPU.
*
* Starting with win8, performance critical channels will be distributed
* evenly among all the available NUMA nodes. Once the node is assigned,
* we will assign the CPU based on a simple round robin scheme.
*/
static void init_vp_index(struct vmbus_channel *channel, u16 dev_type)
static void init_vp_index(struct vmbus_channel *channel)
{
u32 cur_cpu;
bool perf_chn = vmbus_devs[dev_type].perf_device;
struct vmbus_channel *primary = channel->primary_channel;
int next_node;
bool perf_chn = hv_is_perf_channel(channel);
cpumask_var_t available_mask;
struct cpumask *alloced_mask;
u32 target_cpu;
int numa_node;
if ((vmbus_proto_version == VERSION_WS2008) ||
(vmbus_proto_version == VERSION_WIN7) || (!perf_chn) ||
!alloc_cpumask_var(&available_mask, GFP_KERNEL)) {
/*
* Prior to win8, all channel interrupts are
* delivered on cpu 0.
* delivered on VMBUS_CONNECT_CPU.
* Also if the channel is not a performance critical
* channel, bind it to cpu 0.
* In case alloc_cpumask_var() fails, bind it to cpu 0.
* channel, bind it to VMBUS_CONNECT_CPU.
* In case alloc_cpumask_var() fails, bind it to
* VMBUS_CONNECT_CPU.
*/
channel->numa_node = 0;
channel->target_cpu = 0;
channel->target_vp = hv_cpu_number_to_vp_number(0);
channel->numa_node = cpu_to_node(VMBUS_CONNECT_CPU);
channel->target_cpu = VMBUS_CONNECT_CPU;
channel->target_vp =
hv_cpu_number_to_vp_number(VMBUS_CONNECT_CPU);
if (perf_chn)
hv_set_alloced_cpu(VMBUS_CONNECT_CPU);
return;
}
spin_lock(&bind_channel_to_cpu_lock);
/*
* Based on the channel affinity policy, we will assign the NUMA
* nodes.
*/
if ((channel->affinity_policy == HV_BALANCED) || (!primary)) {
while (true) {
next_node = next_numa_node_id++;
if (next_node == nr_node_ids) {
next_node = next_numa_node_id = 0;
continue;
}
if (cpumask_empty(cpumask_of_node(next_node)))
continue;
break;
while (true) {
numa_node = next_numa_node_id++;
if (numa_node == nr_node_ids) {
next_numa_node_id = 0;
continue;
}
channel->numa_node = next_node;
primary = channel;
if (cpumask_empty(cpumask_of_node(numa_node)))
continue;
break;
}
alloced_mask = &hv_context.hv_numa_map[primary->numa_node];
channel->numa_node = numa_node;
alloced_mask = &hv_context.hv_numa_map[numa_node];
if (cpumask_weight(alloced_mask) ==
cpumask_weight(cpumask_of_node(primary->numa_node))) {
cpumask_weight(cpumask_of_node(numa_node))) {
/*
* We have cycled through all the CPUs in the node;
* reset the alloced map.
......@@ -706,59 +733,13 @@ static void init_vp_index(struct vmbus_channel *channel, u16 dev_type)
cpumask_clear(alloced_mask);
}
cpumask_xor(available_mask, alloced_mask,
cpumask_of_node(primary->numa_node));
cur_cpu = -1;
if (primary->affinity_policy == HV_LOCALIZED) {
/*
* Normally Hyper-V host doesn't create more subchannels
* than there are VCPUs on the node but it is possible when not
* all present VCPUs on the node are initialized by guest.
* Clear the alloced_cpus_in_node to start over.
*/
if (cpumask_equal(&primary->alloced_cpus_in_node,
cpumask_of_node(primary->numa_node)))
cpumask_clear(&primary->alloced_cpus_in_node);
}
while (true) {
cur_cpu = cpumask_next(cur_cpu, available_mask);
if (cur_cpu >= nr_cpu_ids) {
cur_cpu = -1;
cpumask_copy(available_mask,
cpumask_of_node(primary->numa_node));
continue;
}
if (primary->affinity_policy == HV_LOCALIZED) {
/*
* NOTE: in the case of sub-channel, we clear the
* sub-channel related bit(s) in
* primary->alloced_cpus_in_node in
* hv_process_channel_removal(), so when we
* reload drivers like hv_netvsc in SMP guest, here
* we're able to re-allocate
* bit from primary->alloced_cpus_in_node.
*/
if (!cpumask_test_cpu(cur_cpu,
&primary->alloced_cpus_in_node)) {
cpumask_set_cpu(cur_cpu,
&primary->alloced_cpus_in_node);
cpumask_set_cpu(cur_cpu, alloced_mask);
break;
}
} else {
cpumask_set_cpu(cur_cpu, alloced_mask);
break;
}
}
cpumask_xor(available_mask, alloced_mask, cpumask_of_node(numa_node));
channel->target_cpu = cur_cpu;
channel->target_vp = hv_cpu_number_to_vp_number(cur_cpu);
target_cpu = cpumask_first(available_mask);
cpumask_set_cpu(target_cpu, alloced_mask);
spin_unlock(&bind_channel_to_cpu_lock);
channel->target_cpu = target_cpu;
channel->target_vp = hv_cpu_number_to_vp_number(target_cpu);
free_cpumask_var(available_mask);
}
......@@ -890,6 +871,7 @@ static void vmbus_setup_channel_state(struct vmbus_channel *channel,
sizeof(struct vmbus_channel_offer_channel));
channel->monitor_grp = (u8)offer->monitorid / 32;
channel->monitor_bit = (u8)offer->monitorid % 32;
channel->device_id = hv_get_dev_type(channel);
}
/*
......@@ -940,8 +922,6 @@ static void vmbus_onoffer(struct vmbus_channel_message_header *hdr)
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
......@@ -949,36 +929,65 @@ static void vmbus_onoffer(struct vmbus_channel_message_header *hdr)
* primary channel that we had before the hibernation.
*/
/*
* { Initially: channel relid = INVALID_RELID,
* channels[valid_relid] = NULL }
*
* CPU1 CPU2
*
* [vmbus_onoffer()] [vmbus_device_release()]
*
* LOCK channel_mutex LOCK channel_mutex
* STORE channel relid = valid_relid LOAD r1 = channel relid
* MAP_RELID channel if (r1 != INVALID_RELID)
* UNLOCK channel_mutex UNMAP_RELID channel
* UNLOCK channel_mutex
*
* Forbids: r1 == valid_relid &&
* channels[valid_relid] == channel
*
* Note. r1 can be INVALID_RELID only for an hv_sock channel.
* None of the hv_sock channels which were present before the
* suspend are re-offered upon the resume. See the WARN_ON()
* in hv_process_channel_removal().
*/
mutex_lock(&vmbus_connection.channel_mutex);
atomic_dec(&vmbus_connection.offer_in_progress);
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;
if (memcmp(offer, &oldchannel->offermsg, offer_sz) != 0) {
/*
* 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);
}
/*
* 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);
/* Add the channel back to the array of channels. */
vmbus_channel_map_relid(oldchannel);
check_ready_for_resume_event();
mutex_unlock(&vmbus_connection.channel_mutex);
return;
}
......@@ -1028,11 +1037,22 @@ static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr)
* offer comes in first and then the rescind.
* Since we process these events in work elements,
* and with preemption, we may end up processing
* the events out of order. Given that we handle these
* work elements on the same CPU, this is possible only
* in the case of preemption. In any case wait here
* until the offer processing has moved beyond the
* point where the channel is discoverable.
* the events out of order. We rely on the synchronization
* provided by offer_in_progress and by channel_mutex for
* ordering these events:
*
* { Initially: offer_in_progress = 1 }
*
* CPU1 CPU2
*
* [vmbus_onoffer()] [vmbus_onoffer_rescind()]
*
* LOCK channel_mutex WAIT_ON offer_in_progress == 0
* DECREMENT offer_in_progress LOCK channel_mutex
* STORE channels[] LOAD channels[]
* UNLOCK channel_mutex UNLOCK channel_mutex
*
* Forbids: CPU2's LOAD from *not* seeing CPU1's STORE
*/
while (atomic_read(&vmbus_connection.offer_in_progress) != 0) {
......@@ -1332,30 +1352,36 @@ static void vmbus_onversion_response(
/* Channel message dispatch table */
const struct vmbus_channel_message_table_entry
channel_message_table[CHANNELMSG_COUNT] = {
{ CHANNELMSG_INVALID, 0, NULL },
{ CHANNELMSG_OFFERCHANNEL, 0, vmbus_onoffer },
{ CHANNELMSG_RESCIND_CHANNELOFFER, 0, vmbus_onoffer_rescind },
{ CHANNELMSG_REQUESTOFFERS, 0, NULL },
{ CHANNELMSG_ALLOFFERS_DELIVERED, 1, vmbus_onoffers_delivered },
{ CHANNELMSG_OPENCHANNEL, 0, NULL },
{ CHANNELMSG_OPENCHANNEL_RESULT, 1, vmbus_onopen_result },
{ CHANNELMSG_CLOSECHANNEL, 0, NULL },
{ CHANNELMSG_GPADL_HEADER, 0, NULL },
{ CHANNELMSG_GPADL_BODY, 0, NULL },
{ CHANNELMSG_GPADL_CREATED, 1, vmbus_ongpadl_created },
{ CHANNELMSG_GPADL_TEARDOWN, 0, NULL },
{ CHANNELMSG_GPADL_TORNDOWN, 1, vmbus_ongpadl_torndown },
{ CHANNELMSG_RELID_RELEASED, 0, NULL },
{ CHANNELMSG_INITIATE_CONTACT, 0, NULL },
{ CHANNELMSG_VERSION_RESPONSE, 1, vmbus_onversion_response },
{ CHANNELMSG_UNLOAD, 0, NULL },
{ CHANNELMSG_UNLOAD_RESPONSE, 1, vmbus_unload_response },
{ CHANNELMSG_18, 0, NULL },
{ CHANNELMSG_19, 0, NULL },
{ CHANNELMSG_20, 0, NULL },
{ CHANNELMSG_TL_CONNECT_REQUEST, 0, NULL },
{ CHANNELMSG_22, 0, NULL },
{ CHANNELMSG_TL_CONNECT_RESULT, 0, NULL },
{ CHANNELMSG_INVALID, 0, NULL, 0},
{ CHANNELMSG_OFFERCHANNEL, 0, vmbus_onoffer,
sizeof(struct vmbus_channel_offer_channel)},
{ CHANNELMSG_RESCIND_CHANNELOFFER, 0, vmbus_onoffer_rescind,
sizeof(struct vmbus_channel_rescind_offer) },
{ CHANNELMSG_REQUESTOFFERS, 0, NULL, 0},
{ CHANNELMSG_ALLOFFERS_DELIVERED, 1, vmbus_onoffers_delivered, 0},
{ CHANNELMSG_OPENCHANNEL, 0, NULL, 0},
{ CHANNELMSG_OPENCHANNEL_RESULT, 1, vmbus_onopen_result,
sizeof(struct vmbus_channel_open_result)},
{ CHANNELMSG_CLOSECHANNEL, 0, NULL, 0},
{ CHANNELMSG_GPADL_HEADER, 0, NULL, 0},
{ CHANNELMSG_GPADL_BODY, 0, NULL, 0},
{ CHANNELMSG_GPADL_CREATED, 1, vmbus_ongpadl_created,
sizeof(struct vmbus_channel_gpadl_created)},
{ CHANNELMSG_GPADL_TEARDOWN, 0, NULL, 0},
{ CHANNELMSG_GPADL_TORNDOWN, 1, vmbus_ongpadl_torndown,
sizeof(struct vmbus_channel_gpadl_torndown) },
{ CHANNELMSG_RELID_RELEASED, 0, NULL, 0},
{ CHANNELMSG_INITIATE_CONTACT, 0, NULL, 0},
{ CHANNELMSG_VERSION_RESPONSE, 1, vmbus_onversion_response,
sizeof(struct vmbus_channel_version_response)},
{ CHANNELMSG_UNLOAD, 0, NULL, 0},
{ CHANNELMSG_UNLOAD_RESPONSE, 1, vmbus_unload_response, 0},
{ CHANNELMSG_18, 0, NULL, 0},
{ CHANNELMSG_19, 0, NULL, 0},
{ CHANNELMSG_20, 0, NULL, 0},
{ CHANNELMSG_TL_CONNECT_REQUEST, 0, NULL, 0},
{ CHANNELMSG_MODIFYCHANNEL, 0, NULL, 0},
{ CHANNELMSG_TL_CONNECT_RESULT, 0, NULL, 0},
};
/*
......@@ -1363,13 +1389,8 @@ channel_message_table[CHANNELMSG_COUNT] = {
*
* This is invoked in the vmbus worker thread context.
*/
void vmbus_onmessage(void *context)
void vmbus_onmessage(struct vmbus_channel_message_header *hdr)
{
struct hv_message *msg = context;
struct vmbus_channel_message_header *hdr;
hdr = (struct vmbus_channel_message_header *)msg->u.payload;
trace_vmbus_on_message(hdr);
/*
......
......@@ -69,7 +69,6 @@ MODULE_PARM_DESC(max_version,
int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, u32 version)
{
int ret = 0;
unsigned int cur_cpu;
struct vmbus_channel_initiate_contact *msg;
unsigned long flags;
......@@ -102,24 +101,7 @@ int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, u32 version)
msg->monitor_page1 = virt_to_phys(vmbus_connection.monitor_pages[0]);
msg->monitor_page2 = virt_to_phys(vmbus_connection.monitor_pages[1]);
/*
* We want all channel messages to be delivered on CPU 0.
* This has been the behavior pre-win8. This is not
* perf issue and having all channel messages delivered on CPU 0
* would be ok.
* For post win8 hosts, we support receiving channel messagges on
* all the CPUs. This is needed for kexec to work correctly where
* the CPU attempting to connect may not be CPU 0.
*/
if (version >= VERSION_WIN8_1) {
cur_cpu = get_cpu();
msg->target_vcpu = hv_cpu_number_to_vp_number(cur_cpu);
vmbus_connection.connect_cpu = cur_cpu;
put_cpu();
} else {
msg->target_vcpu = 0;
vmbus_connection.connect_cpu = 0;
}
msg->target_vcpu = hv_cpu_number_to_vp_number(VMBUS_CONNECT_CPU);
/*
* Add to list before we send the request since we may
......@@ -266,6 +248,14 @@ int vmbus_connect(void)
pr_info("Vmbus version:%d.%d\n",
version >> 16, version & 0xFFFF);
vmbus_connection.channels = kcalloc(MAX_CHANNEL_RELIDS,
sizeof(struct vmbus_channel *),
GFP_KERNEL);
if (vmbus_connection.channels == NULL) {
ret = -ENOMEM;
goto cleanup;
}
kfree(msginfo);
return 0;
......@@ -313,33 +303,9 @@ void vmbus_disconnect(void)
*/
struct vmbus_channel *relid2channel(u32 relid)
{
struct vmbus_channel *channel;
struct vmbus_channel *found_channel = NULL;
struct list_head *cur, *tmp;
struct vmbus_channel *cur_sc;
BUG_ON(!mutex_is_locked(&vmbus_connection.channel_mutex));
list_for_each_entry(channel, &vmbus_connection.chn_list, listentry) {
if (channel->offermsg.child_relid == relid) {
found_channel = channel;
break;
} else if (!list_empty(&channel->sc_list)) {
/*
* Deal with sub-channels.
*/
list_for_each_safe(cur, tmp, &channel->sc_list) {
cur_sc = list_entry(cur, struct vmbus_channel,
sc_list);
if (cur_sc->offermsg.child_relid == relid) {
found_channel = cur_sc;
break;
}
}
}
}
return found_channel;
if (WARN_ON(relid >= MAX_CHANNEL_RELIDS))
return NULL;
return READ_ONCE(vmbus_connection.channels[relid]);
}
/*
......
......@@ -117,8 +117,6 @@ int hv_synic_alloc(void)
pr_err("Unable to allocate post msg page\n");
goto err;
}
INIT_LIST_HEAD(&hv_cpu->chan_list);
}
return 0;
......@@ -245,11 +243,19 @@ int hv_synic_cleanup(unsigned int cpu)
bool channel_found = false;
unsigned long flags;
/*
* Hyper-V does not provide a way to change the connect CPU once
* it is set; we must prevent the connect CPU from going offline.
*/
if (cpu == VMBUS_CONNECT_CPU)
return -EBUSY;
/*
* 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
* fail, this will effectively prevent CPU offlining. There is no way
* we can re-bind channels to different CPUs for now.
* 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.
*/
mutex_lock(&vmbus_connection.channel_mutex);
list_for_each_entry(channel, &vmbus_connection.chn_list, listentry) {
......
......@@ -71,7 +71,7 @@ static void fcopy_poll_wrapper(void *channel)
{
/* Transaction is finished, reset the state here to avoid races. */
fcopy_transaction.state = HVUTIL_READY;
hv_fcopy_onchannelcallback(channel);
tasklet_schedule(&((struct vmbus_channel *)channel)->callback_event);
}
static void fcopy_timeout_func(struct work_struct *dummy)
......
......@@ -80,7 +80,7 @@ static void vss_poll_wrapper(void *channel)
{
/* Transaction is finished, reset the state here to avoid races. */
vss_transaction.state = HVUTIL_READY;
hv_vss_onchannelcallback(channel);
tasklet_schedule(&((struct vmbus_channel *)channel)->callback_event);
}
/*
......
......@@ -44,10 +44,8 @@ TRACE_EVENT(vmbus_onoffer,
__entry->monitorid = offer->monitorid;
__entry->is_ddc_int = offer->is_dedicated_interrupt;
__entry->connection_id = offer->connection_id;
memcpy(__entry->if_type,
&offer->offer.if_type.b, 16);
memcpy(__entry->if_instance,
&offer->offer.if_instance.b, 16);
export_guid(__entry->if_type, &offer->offer.if_type);
export_guid(__entry->if_instance, &offer->offer.if_instance);
__entry->chn_flags = offer->offer.chn_flags;
__entry->mmio_mb = offer->offer.mmio_megabytes;
__entry->sub_idx = offer->offer.sub_channel_index;
......@@ -296,6 +294,25 @@ TRACE_EVENT(vmbus_send_tl_connect_request,
)
);
TRACE_EVENT(vmbus_send_modifychannel,
TP_PROTO(const struct vmbus_channel_modifychannel *msg,
int ret),
TP_ARGS(msg, ret),
TP_STRUCT__entry(
__field(u32, child_relid)
__field(u32, target_vp)
__field(int, ret)
),
TP_fast_assign(
__entry->child_relid = msg->child_relid;
__entry->target_vp = msg->target_vp;
__entry->ret = ret;
),
TP_printk("binding child_relid 0x%x to target_vp 0x%x, ret %d",
__entry->child_relid, __entry->target_vp, __entry->ret
)
);
DECLARE_EVENT_CLASS(vmbus_channel,
TP_PROTO(const struct vmbus_channel *channel),
TP_ARGS(channel),
......
......@@ -132,12 +132,6 @@ struct hv_per_cpu_context {
* basis.
*/
struct tasklet_struct msg_dpc;
/*
* To optimize the mapping of relid to channel, maintain
* per-cpu list of the channels based on their CPU affinity.
*/
struct list_head chan_list;
};
struct hv_context {
......@@ -202,6 +196,8 @@ int hv_ringbuffer_read(struct vmbus_channel *channel,
/* TODO: Need to make this configurable */
#define MAX_NUM_CHANNELS_SUPPORTED 256
#define MAX_CHANNEL_RELIDS \
max(MAX_NUM_CHANNELS_SUPPORTED, HV_EVENT_FLAGS_COUNT)
enum vmbus_connect_state {
DISCONNECTED,
......@@ -212,12 +208,13 @@ enum vmbus_connect_state {
#define MAX_SIZE_CHANNEL_MESSAGE HV_MESSAGE_PAYLOAD_BYTE_COUNT
struct vmbus_connection {
/*
* CPU on which the initial host contact was made.
*/
int connect_cpu;
/*
* The CPU that Hyper-V will interrupt for VMBUS messages, such as
* CHANNELMSG_OFFERCHANNEL and CHANNELMSG_RESCIND_CHANNELOFFER.
*/
#define VMBUS_CONNECT_CPU 0
struct vmbus_connection {
u32 msg_conn_id;
atomic_t offer_in_progress;
......@@ -250,6 +247,9 @@ struct vmbus_connection {
struct list_head chn_list;
struct mutex channel_mutex;
/* Array of channels */
struct vmbus_channel **channels;
/*
* An offer message is handled first on the work_queue, and then
* is further handled on handle_primary_chan_wq or
......@@ -317,6 +317,7 @@ struct vmbus_channel_message_table_entry {
enum vmbus_channel_message_type message_type;
enum vmbus_message_handler_type handler_type;
void (*message_handler)(struct vmbus_channel_message_header *msg);
u32 min_payload_len;
};
extern const struct vmbus_channel_message_table_entry
......@@ -336,6 +337,9 @@ int vmbus_add_channel_kobj(struct hv_device *device_obj,
void vmbus_remove_channel_attr_group(struct vmbus_channel *channel);
void vmbus_channel_map_relid(struct vmbus_channel *channel);
void vmbus_channel_unmap_relid(struct vmbus_channel *channel);
struct vmbus_channel *relid2channel(u32 relid);
void vmbus_free_channels(void);
......@@ -374,12 +378,7 @@ static inline void hv_poll_channel(struct vmbus_channel *channel,
{
if (!channel)
return;
if (in_interrupt() && (channel->target_cpu == smp_processor_id())) {
cb(channel);
return;
}
smp_call_function_single(channel->target_cpu, cb, channel, true);
cb(channel);
}
enum hvutil_device_state {
......@@ -396,6 +395,54 @@ enum delay {
MESSAGE_DELAY = 1,
};
extern const struct vmbus_device vmbus_devs[];
static inline bool hv_is_perf_channel(struct vmbus_channel *channel)
{
return vmbus_devs[channel->device_id].perf_device;
}
static inline bool hv_is_alloced_cpu(unsigned int cpu)
{
struct vmbus_channel *channel, *sc;
lockdep_assert_held(&vmbus_connection.channel_mutex);
/*
* List additions/deletions as well as updates of the target CPUs are
* protected by channel_mutex.
*/
list_for_each_entry(channel, &vmbus_connection.chn_list, listentry) {
if (!hv_is_perf_channel(channel))
continue;
if (channel->target_cpu == cpu)
return true;
list_for_each_entry(sc, &channel->sc_list, sc_list) {
if (sc->target_cpu == cpu)
return true;
}
}
return false;
}
static inline void hv_set_alloced_cpu(unsigned int cpu)
{
cpumask_set_cpu(cpu, &hv_context.hv_numa_map[cpu_to_node(cpu)]);
}
static inline void hv_clear_alloced_cpu(unsigned int cpu)
{
if (hv_is_alloced_cpu(cpu))
return;
cpumask_clear_cpu(cpu, &hv_context.hv_numa_map[cpu_to_node(cpu)]);
}
static inline void hv_update_alloced_cpus(unsigned int old_cpu,
unsigned int new_cpu)
{
hv_set_alloced_cpu(new_cpu);
hv_clear_alloced_cpu(old_cpu);
}
#ifdef CONFIG_HYPERV_TESTING
int hv_debug_add_dev_dir(struct hv_device *dev);
......
......@@ -117,14 +117,6 @@ static int vmbus_exists(void)
return 0;
}
#define VMBUS_ALIAS_LEN ((sizeof((struct hv_vmbus_device_id *)0)->guid) * 2)
static void print_alias_name(struct hv_device *hv_dev, char *alias_name)
{
int i;
for (i = 0; i < VMBUS_ALIAS_LEN; i += 2)
sprintf(&alias_name[i], "%02x", hv_dev->dev_type.b[i/2]);
}
static u8 channel_monitor_group(const struct vmbus_channel *channel)
{
return (u8)channel->offermsg.monitorid / 32;
......@@ -201,7 +193,7 @@ static ssize_t class_id_show(struct device *dev,
if (!hv_dev->channel)
return -ENODEV;
return sprintf(buf, "{%pUl}\n",
hv_dev->channel->offermsg.offer.if_type.b);
&hv_dev->channel->offermsg.offer.if_type);
}
static DEVICE_ATTR_RO(class_id);
......@@ -213,7 +205,7 @@ static ssize_t device_id_show(struct device *dev,
if (!hv_dev->channel)
return -ENODEV;
return sprintf(buf, "{%pUl}\n",
hv_dev->channel->offermsg.offer.if_instance.b);
&hv_dev->channel->offermsg.offer.if_instance);
}
static DEVICE_ATTR_RO(device_id);
......@@ -221,10 +213,8 @@ static ssize_t modalias_show(struct device *dev,
struct device_attribute *dev_attr, char *buf)
{
struct hv_device *hv_dev = device_to_hv_device(dev);
char alias_name[VMBUS_ALIAS_LEN + 1];
print_alias_name(hv_dev, alias_name);
return sprintf(buf, "vmbus:%s\n", alias_name);
return sprintf(buf, "vmbus:%*phN\n", UUID_SIZE, &hv_dev->dev_type);
}
static DEVICE_ATTR_RO(modalias);
......@@ -693,12 +683,9 @@ __ATTRIBUTE_GROUPS(vmbus_dev);
static int vmbus_uevent(struct device *device, struct kobj_uevent_env *env)
{
struct hv_device *dev = device_to_hv_device(device);
int ret;
char alias_name[VMBUS_ALIAS_LEN + 1];
const char *format = "MODALIAS=vmbus:%*phN";
print_alias_name(dev, alias_name);
ret = add_uevent_var(env, "MODALIAS=vmbus:%s", alias_name);
return ret;
return add_uevent_var(env, format, UUID_SIZE, &dev->dev_type);
}
static const struct hv_vmbus_device_id *
......@@ -1033,7 +1020,10 @@ static struct bus_type hv_bus = {
struct onmessage_work_context {
struct work_struct work;
struct hv_message msg;
struct {
struct hv_message_header header;
u8 payload[];
} msg;
};
static void vmbus_onmessage_work(struct work_struct *work)
......@@ -1046,7 +1036,8 @@ static void vmbus_onmessage_work(struct work_struct *work)
ctx = container_of(work, struct onmessage_work_context,
work);
vmbus_onmessage(&ctx->msg);
vmbus_onmessage((struct vmbus_channel_message_header *)
&ctx->msg.payload);
kfree(ctx);
}
......@@ -1061,6 +1052,13 @@ void vmbus_on_msg_dpc(unsigned long data)
struct onmessage_work_context *ctx;
u32 message_type = msg->header.message_type;
/*
* 'enum vmbus_channel_message_type' is supposed to always be 'u32' as
* it is being used in 'struct vmbus_channel_message_header' definition
* which is supposed to match hypervisor ABI.
*/
BUILD_BUG_ON(sizeof(enum vmbus_channel_message_type) != sizeof(u32));
if (message_type == HVMSG_NONE)
/* no msg */
return;
......@@ -1074,41 +1072,88 @@ void vmbus_on_msg_dpc(unsigned long data)
goto msg_handled;
}
if (msg->header.payload_size > HV_MESSAGE_PAYLOAD_BYTE_COUNT) {
WARN_ONCE(1, "payload size is too large (%d)\n",
msg->header.payload_size);
goto msg_handled;
}
entry = &channel_message_table[hdr->msgtype];
if (!entry->message_handler)
goto msg_handled;
if (msg->header.payload_size < entry->min_payload_len) {
WARN_ONCE(1, "message too short: msgtype=%d len=%d\n",
hdr->msgtype, msg->header.payload_size);
goto msg_handled;
}
if (entry->handler_type == VMHT_BLOCKING) {
ctx = kmalloc(sizeof(*ctx), GFP_ATOMIC);
ctx = kmalloc(sizeof(*ctx) + msg->header.payload_size,
GFP_ATOMIC);
if (ctx == NULL)
return;
INIT_WORK(&ctx->work, vmbus_onmessage_work);
memcpy(&ctx->msg, msg, sizeof(*msg));
memcpy(&ctx->msg, msg, sizeof(msg->header) +
msg->header.payload_size);
/*
* The host can generate a rescind message while we
* may still be handling the original offer. We deal with
* this condition by ensuring the processing is done on the
* same CPU.
* this condition by relying on the synchronization provided
* by offer_in_progress and by channel_mutex. See also the
* inline comments in vmbus_onoffer_rescind().
*/
switch (hdr->msgtype) {
case CHANNELMSG_RESCIND_CHANNELOFFER:
/*
* If we are handling the rescind message;
* schedule the work on the global work queue.
*
* The OFFER message and the RESCIND message should
* not be handled by the same serialized work queue,
* because the OFFER handler may call vmbus_open(),
* which tries to open the channel by sending an
* OPEN_CHANNEL message to the host and waits for
* the host's response; however, if the host has
* rescinded the channel before it receives the
* OPEN_CHANNEL message, the host just silently
* ignores the OPEN_CHANNEL message; as a result,
* the guest's OFFER handler hangs for ever, if we
* handle the RESCIND message in the same serialized
* work queue: the RESCIND handler can not start to
* run before the OFFER handler finishes.
*/
schedule_work_on(vmbus_connection.connect_cpu,
&ctx->work);
schedule_work(&ctx->work);
break;
case CHANNELMSG_OFFERCHANNEL:
/*
* The host sends the offer message of a given channel
* before sending the rescind message of the same
* channel. These messages are sent to the guest's
* connect CPU; the guest then starts processing them
* in the tasklet handler on this CPU:
*
* VMBUS_CONNECT_CPU
*
* [vmbus_on_msg_dpc()]
* atomic_inc() // CHANNELMSG_OFFERCHANNEL
* queue_work()
* ...
* [vmbus_on_msg_dpc()]
* schedule_work() // CHANNELMSG_RESCIND_CHANNELOFFER
*
* We rely on the memory-ordering properties of the
* queue_work() and schedule_work() primitives, which
* guarantee that the atomic increment will be visible
* to the CPUs which will execute the offer & rescind
* works by the time these works will start execution.
*/
atomic_inc(&vmbus_connection.offer_in_progress);
queue_work_on(vmbus_connection.connect_cpu,
vmbus_connection.work_queue,
&ctx->work);
break;
fallthrough;
default:
queue_work(vmbus_connection.work_queue, &ctx->work);
......@@ -1133,10 +1178,11 @@ static void vmbus_force_channel_rescinded(struct vmbus_channel *channel)
WARN_ON(!is_hvsock_channel(channel));
/*
* sizeof(*ctx) is small and the allocation should really not fail,
* Allocation size is small and the allocation should really not fail,
* otherwise the state of the hv_sock connections ends up in limbo.
*/
ctx = kzalloc(sizeof(*ctx), GFP_KERNEL | __GFP_NOFAIL);
ctx = kzalloc(sizeof(*ctx) + sizeof(*rescind),
GFP_KERNEL | __GFP_NOFAIL);
/*
* So far, these are not really used by Linux. Just set them to the
......@@ -1146,30 +1192,16 @@ static void vmbus_force_channel_rescinded(struct vmbus_channel *channel)
ctx->msg.header.payload_size = sizeof(*rescind);
/* These values are actually used by Linux. */
rescind = (struct vmbus_channel_rescind_offer *)ctx->msg.u.payload;
rescind = (struct vmbus_channel_rescind_offer *)ctx->msg.payload;
rescind->header.msgtype = CHANNELMSG_RESCIND_CHANNELOFFER;
rescind->child_relid = channel->offermsg.child_relid;
INIT_WORK(&ctx->work, vmbus_onmessage_work);
queue_work_on(vmbus_connection.connect_cpu,
vmbus_connection.work_queue,
&ctx->work);
queue_work(vmbus_connection.work_queue, &ctx->work);
}
#endif /* CONFIG_PM_SLEEP */
/*
* Direct callback for channels using other deferred processing
*/
static void vmbus_channel_isr(struct vmbus_channel *channel)
{
void (*callback_fn)(void *);
callback_fn = READ_ONCE(channel->onchannel_callback);
if (likely(callback_fn != NULL))
(*callback_fn)(channel->channel_callback_context);
}
/*
* Schedule all channels with events pending
*/
......@@ -1200,6 +1232,7 @@ static void vmbus_chan_sched(struct hv_per_cpu_context *hv_cpu)
return;
for_each_set_bit(relid, recv_int_page, maxbits) {
void (*callback_fn)(void *context);
struct vmbus_channel *channel;
if (!sync_test_and_clear_bit(relid, recv_int_page))
......@@ -1209,33 +1242,54 @@ static void vmbus_chan_sched(struct hv_per_cpu_context *hv_cpu)
if (relid == 0)
continue;
/*
* Pairs with the kfree_rcu() in vmbus_chan_release().
* Guarantees that the channel data structure doesn't
* get freed while the channel pointer below is being
* dereferenced.
*/
rcu_read_lock();
/* Find channel based on relid */
list_for_each_entry_rcu(channel, &hv_cpu->chan_list, percpu_list) {
if (channel->offermsg.child_relid != relid)
continue;
channel = relid2channel(relid);
if (channel == NULL)
goto sched_unlock_rcu;
if (channel->rescind)
continue;
if (channel->rescind)
goto sched_unlock_rcu;
trace_vmbus_chan_sched(channel);
/*
* Make sure that the ring buffer data structure doesn't get
* freed while we dereference the ring buffer pointer. Test
* for the channel's onchannel_callback being NULL within a
* sched_lock critical section. See also the inline comments
* in vmbus_reset_channel_cb().
*/
spin_lock(&channel->sched_lock);
++channel->interrupts;
callback_fn = channel->onchannel_callback;
if (unlikely(callback_fn == NULL))
goto sched_unlock;
switch (channel->callback_mode) {
case HV_CALL_ISR:
vmbus_channel_isr(channel);
break;
trace_vmbus_chan_sched(channel);
case HV_CALL_BATCHED:
hv_begin_read(&channel->inbound);
/* fallthrough */
case HV_CALL_DIRECT:
tasklet_schedule(&channel->callback_event);
}
++channel->interrupts;
switch (channel->callback_mode) {
case HV_CALL_ISR:
(*callback_fn)(channel->channel_callback_context);
break;
case HV_CALL_BATCHED:
hv_begin_read(&channel->inbound);
fallthrough;
case HV_CALL_DIRECT:
tasklet_schedule(&channel->callback_event);
}
sched_unlock:
spin_unlock(&channel->sched_lock);
sched_unlock_rcu:
rcu_read_unlock();
}
}
......@@ -1364,7 +1418,6 @@ static int vmbus_bus_init(void)
{
int ret;
/* Hypervisor initialization...setup hypercall page..etc */
ret = hv_init();
if (ret != 0) {
pr_err("Unable to initialize the hypervisor - 0x%x\n", ret);
......@@ -1553,8 +1606,24 @@ static ssize_t vmbus_chan_attr_show(struct kobject *kobj,
return attribute->show(chan, buf);
}
static ssize_t vmbus_chan_attr_store(struct kobject *kobj,
struct attribute *attr, const char *buf,
size_t count)
{
const struct vmbus_chan_attribute *attribute
= container_of(attr, struct vmbus_chan_attribute, attr);
struct vmbus_channel *chan
= container_of(kobj, struct vmbus_channel, kobj);
if (!attribute->store)
return -EIO;
return attribute->store(chan, buf, count);
}
static const struct sysfs_ops vmbus_chan_sysfs_ops = {
.show = vmbus_chan_attr_show,
.store = vmbus_chan_attr_store,
};
static ssize_t out_mask_show(struct vmbus_channel *channel, char *buf)
......@@ -1625,11 +1694,110 @@ static ssize_t write_avail_show(struct vmbus_channel *channel, char *buf)
}
static VMBUS_CHAN_ATTR_RO(write_avail);
static ssize_t show_target_cpu(struct vmbus_channel *channel, char *buf)
static ssize_t target_cpu_show(struct vmbus_channel *channel, char *buf)
{
return sprintf(buf, "%u\n", channel->target_cpu);
}
static VMBUS_CHAN_ATTR(cpu, S_IRUGO, show_target_cpu, NULL);
static ssize_t target_cpu_store(struct vmbus_channel *channel,
const char *buf, size_t count)
{
u32 target_cpu, origin_cpu;
ssize_t ret = count;
if (vmbus_proto_version < VERSION_WIN10_V4_1)
return -EIO;
if (sscanf(buf, "%uu", &target_cpu) != 1)
return -EIO;
/* Validate target_cpu for the cpumask_test_cpu() operation below. */
if (target_cpu >= nr_cpumask_bits)
return -EINVAL;
/* No CPUs should come up or down during this. */
cpus_read_lock();
if (!cpumask_test_cpu(target_cpu, cpu_online_mask)) {
cpus_read_unlock();
return -EINVAL;
}
/*
* Synchronizes target_cpu_store() and channel closure:
*
* { Initially: state = CHANNEL_OPENED }
*
* CPU1 CPU2
*
* [target_cpu_store()] [vmbus_disconnect_ring()]
*
* LOCK channel_mutex LOCK channel_mutex
* LOAD r1 = state LOAD r2 = state
* IF (r1 == CHANNEL_OPENED) IF (r2 == CHANNEL_OPENED)
* SEND MODIFYCHANNEL STORE state = CHANNEL_OPEN
* [...] SEND CLOSECHANNEL
* UNLOCK channel_mutex UNLOCK channel_mutex
*
* Forbids: r1 == r2 == CHANNEL_OPENED (i.e., CPU1's LOCK precedes
* CPU2's LOCK) && CPU2's SEND precedes CPU1's SEND
*
* Note. The host processes the channel messages "sequentially", in
* the order in which they are received on a per-partition basis.
*/
mutex_lock(&vmbus_connection.channel_mutex);
/*
* Hyper-V will ignore MODIFYCHANNEL messages for "non-open" channels;
* avoid sending the message and fail here for such channels.
*/
if (channel->state != CHANNEL_OPENED_STATE) {
ret = -EIO;
goto cpu_store_unlock;
}
origin_cpu = channel->target_cpu;
if (target_cpu == origin_cpu)
goto cpu_store_unlock;
if (vmbus_send_modifychannel(channel->offermsg.child_relid,
hv_cpu_number_to_vp_number(target_cpu))) {
ret = -EIO;
goto cpu_store_unlock;
}
/*
* Warning. At this point, there is *no* guarantee that the host will
* have successfully processed the vmbus_send_modifychannel() request.
* See the header comment of vmbus_send_modifychannel() for more info.
*
* Lags in the processing of the above vmbus_send_modifychannel() can
* result in missed interrupts if the "old" target CPU is taken offline
* before Hyper-V starts sending interrupts to the "new" target CPU.
* But apart from this offlining scenario, the code tolerates such
* lags. It will function correctly even if a channel interrupt comes
* in on a CPU that is different from the channel target_cpu value.
*/
channel->target_cpu = target_cpu;
channel->target_vp = hv_cpu_number_to_vp_number(target_cpu);
channel->numa_node = cpu_to_node(target_cpu);
/* See init_vp_index(). */
if (hv_is_perf_channel(channel))
hv_update_alloced_cpus(origin_cpu, target_cpu);
/* Currently set only for storvsc channels. */
if (channel->change_target_cpu_callback) {
(*channel->change_target_cpu_callback)(channel,
origin_cpu, target_cpu);
}
cpu_store_unlock:
mutex_unlock(&vmbus_connection.channel_mutex);
cpus_read_unlock();
return ret;
}
static VMBUS_CHAN_ATTR(cpu, 0644, target_cpu_show, target_cpu_store);
static ssize_t channel_pending_show(struct vmbus_channel *channel,
char *buf)
......@@ -1830,7 +1998,7 @@ int vmbus_device_register(struct hv_device *child_device_obj)
int ret;
dev_set_name(&child_device_obj->device, "%pUl",
child_device_obj->channel->offermsg.offer.if_instance.b);
&child_device_obj->channel->offermsg.offer.if_instance);
child_device_obj->device.bus = &hv_bus;
child_device_obj->device.parent = &hv_acpi_dev->dev;
......@@ -2221,9 +2389,12 @@ static int vmbus_bus_suspend(struct device *dev)
list_for_each_entry(channel, &vmbus_connection.chn_list, listentry) {
/*
* Invalidate the field. Upon resume, vmbus_onoffer() will fix
* up the field, and the other fields (if necessary).
* Remove the channel from the array of channels and invalidate
* the channel's relid. Upon resume, vmbus_onoffer() will fix
* up the relid (and other fields, if necessary) and add the
* channel back to the array.
*/
vmbus_channel_unmap_relid(channel);
channel->offermsg.child_relid = INVALID_RELID;
if (is_hvsock_channel(channel)) {
......@@ -2470,6 +2641,7 @@ static void __exit vmbus_exit(void)
hv_debug_rm_all_dir();
vmbus_free_channels();
kfree(vmbus_connection.channels);
if (ms_hyperv.misc_features & HV_FEATURE_GUEST_CRASH_MSR_AVAILABLE) {
kmsg_dump_unregister(&hv_kmsg_dumper);
......
......@@ -636,9 +636,12 @@ void netvsc_device_remove(struct hv_device *device)
RCU_INIT_POINTER(net_device_ctx->nvdev, NULL);
/* And disassociate NAPI context from device */
for (i = 0; i < net_device->num_chn; i++)
/* Disable NAPI and disassociate its context from the device. */
for (i = 0; i < net_device->num_chn; i++) {
/* See also vmbus_reset_channel_cb(). */
napi_disable(&net_device->chan_table[i].napi);
netif_napi_del(&net_device->chan_table[i].napi);
}
/*
* At this point, no one should be accessing net_device
......
......@@ -1356,11 +1356,11 @@ static void hv_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
{
struct irq_cfg *cfg = irqd_cfg(data);
struct hv_pcibus_device *hbus;
struct vmbus_channel *channel;
struct hv_pci_dev *hpdev;
struct pci_bus *pbus;
struct pci_dev *pdev;
struct cpumask *dest;
unsigned long flags;
struct compose_comp_ctxt comp;
struct tran_int_desc *int_desc;
struct {
......@@ -1378,6 +1378,7 @@ static void hv_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
dest = irq_data_get_effective_affinity_mask(data);
pbus = pdev->bus;
hbus = container_of(pbus->sysdata, struct hv_pcibus_device, sysdata);
channel = hbus->hdev->channel;
hpdev = get_pcichild_wslot(hbus, devfn_to_wslot(pdev->devfn));
if (!hpdev)
goto return_null_message;
......@@ -1435,43 +1436,52 @@ static void hv_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
goto free_int_desc;
}
/*
* Prevents hv_pci_onchannelcallback() from running concurrently
* in the tasklet.
*/
tasklet_disable(&channel->callback_event);
/*
* Since this function is called with IRQ locks held, can't
* do normal wait for completion; instead poll.
*/
while (!try_wait_for_completion(&comp.comp_pkt.host_event)) {
unsigned long flags;
/* 0xFFFF means an invalid PCI VENDOR ID. */
if (hv_pcifront_get_vendor_id(hpdev) == 0xFFFF) {
dev_err_once(&hbus->hdev->device,
"the device has gone\n");
goto free_int_desc;
goto enable_tasklet;
}
/*
* When the higher level interrupt code calls us with
* interrupt disabled, we must poll the channel by calling
* the channel callback directly when channel->target_cpu is
* the current CPU. When the higher level interrupt code
* calls us with interrupt enabled, let's add the
* local_irq_save()/restore() to avoid race:
* hv_pci_onchannelcallback() can also run in tasklet.
* Make sure that the ring buffer data structure doesn't get
* freed while we dereference the ring buffer pointer. Test
* for the channel's onchannel_callback being NULL within a
* sched_lock critical section. See also the inline comments
* in vmbus_reset_channel_cb().
*/
local_irq_save(flags);
if (hbus->hdev->channel->target_cpu == smp_processor_id())
hv_pci_onchannelcallback(hbus);
local_irq_restore(flags);
spin_lock_irqsave(&channel->sched_lock, flags);
if (unlikely(channel->onchannel_callback == NULL)) {
spin_unlock_irqrestore(&channel->sched_lock, flags);
goto enable_tasklet;
}
hv_pci_onchannelcallback(hbus);
spin_unlock_irqrestore(&channel->sched_lock, flags);
if (hpdev->state == hv_pcichild_ejecting) {
dev_err_once(&hbus->hdev->device,
"the device is being ejected\n");
goto free_int_desc;
goto enable_tasklet;
}
udelay(100);
}
tasklet_enable(&channel->callback_event);
if (comp.comp_pkt.completion_status < 0) {
dev_err(&hbus->hdev->device,
"Request for interrupt failed: 0x%x",
......@@ -1495,6 +1505,8 @@ static void hv_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
put_pcichild(hpdev);
return;
enable_tasklet:
tasklet_enable(&channel->callback_event);
free_int_desc:
kfree(int_desc);
drop_reference:
......
......@@ -621,6 +621,64 @@ static inline struct storvsc_device *get_in_stor_device(
}
static void storvsc_change_target_cpu(struct vmbus_channel *channel, u32 old,
u32 new)
{
struct storvsc_device *stor_device;
struct vmbus_channel *cur_chn;
bool old_is_alloced = false;
struct hv_device *device;
unsigned long flags;
int cpu;
device = channel->primary_channel ?
channel->primary_channel->device_obj
: channel->device_obj;
stor_device = get_out_stor_device(device);
if (!stor_device)
return;
/* See storvsc_do_io() -> get_og_chn(). */
spin_lock_irqsave(&device->channel->lock, flags);
/*
* Determines if the storvsc device has other channels assigned to
* the "old" CPU to update the alloced_cpus mask and the stor_chns
* array.
*/
if (device->channel != channel && device->channel->target_cpu == old) {
cur_chn = device->channel;
old_is_alloced = true;
goto old_is_alloced;
}
list_for_each_entry(cur_chn, &device->channel->sc_list, sc_list) {
if (cur_chn == channel)
continue;
if (cur_chn->target_cpu == old) {
old_is_alloced = true;
goto old_is_alloced;
}
}
old_is_alloced:
if (old_is_alloced)
WRITE_ONCE(stor_device->stor_chns[old], cur_chn);
else
cpumask_clear_cpu(old, &stor_device->alloced_cpus);
/* "Flush" the stor_chns array. */
for_each_possible_cpu(cpu) {
if (stor_device->stor_chns[cpu] && !cpumask_test_cpu(
cpu, &stor_device->alloced_cpus))
WRITE_ONCE(stor_device->stor_chns[cpu], NULL);
}
WRITE_ONCE(stor_device->stor_chns[new], channel);
cpumask_set_cpu(new, &stor_device->alloced_cpus);
spin_unlock_irqrestore(&device->channel->lock, flags);
}
static void handle_sc_creation(struct vmbus_channel *new_sc)
{
struct hv_device *device = new_sc->primary_channel->device_obj;
......@@ -648,6 +706,8 @@ static void handle_sc_creation(struct vmbus_channel *new_sc)
return;
}
new_sc->change_target_cpu_callback = storvsc_change_target_cpu;
/* Add the sub-channel to the array of available channels. */
stor_device->stor_chns[new_sc->target_cpu] = new_sc;
cpumask_set_cpu(new_sc->target_cpu, &stor_device->alloced_cpus);
......@@ -876,6 +936,8 @@ static int storvsc_channel_init(struct hv_device *device, bool is_fc)
if (stor_device->stor_chns == NULL)
return -ENOMEM;
device->channel->change_target_cpu_callback = storvsc_change_target_cpu;
stor_device->stor_chns[device->channel->target_cpu] = device->channel;
cpumask_set_cpu(device->channel->target_cpu,
&stor_device->alloced_cpus);
......@@ -1248,8 +1310,10 @@ static struct vmbus_channel *get_og_chn(struct storvsc_device *stor_device,
const struct cpumask *node_mask;
int num_channels, tgt_cpu;
if (stor_device->num_sc == 0)
if (stor_device->num_sc == 0) {
stor_device->stor_chns[q_num] = stor_device->device->channel;
return stor_device->device->channel;
}
/*
* Our channel array is sparsley populated and we
......@@ -1258,7 +1322,6 @@ static struct vmbus_channel *get_og_chn(struct storvsc_device *stor_device,
* The strategy is simple:
* I. Ensure NUMA locality
* II. Distribute evenly (best effort)
* III. Mapping is persistent.
*/
node_mask = cpumask_of_node(cpu_to_node(q_num));
......@@ -1268,8 +1331,10 @@ static struct vmbus_channel *get_og_chn(struct storvsc_device *stor_device,
if (cpumask_test_cpu(tgt_cpu, node_mask))
num_channels++;
}
if (num_channels == 0)
if (num_channels == 0) {
stor_device->stor_chns[q_num] = stor_device->device->channel;
return stor_device->device->channel;
}
hash_qnum = q_num;
while (hash_qnum >= num_channels)
......@@ -1295,6 +1360,7 @@ static int storvsc_do_io(struct hv_device *device,
struct storvsc_device *stor_device;
struct vstor_packet *vstor_packet;
struct vmbus_channel *outgoing_channel, *channel;
unsigned long flags;
int ret = 0;
const struct cpumask *node_mask;
int tgt_cpu;
......@@ -1308,10 +1374,11 @@ static int storvsc_do_io(struct hv_device *device,
request->device = device;
/*
* Select an an appropriate channel to send the request out.
* Select an appropriate channel to send the request out.
*/
if (stor_device->stor_chns[q_num] != NULL) {
outgoing_channel = stor_device->stor_chns[q_num];
/* See storvsc_change_target_cpu(). */
outgoing_channel = READ_ONCE(stor_device->stor_chns[q_num]);
if (outgoing_channel != NULL) {
if (outgoing_channel->target_cpu == q_num) {
/*
* Ideally, we want to pick a different channel if
......@@ -1324,7 +1391,10 @@ static int storvsc_do_io(struct hv_device *device,
continue;
if (tgt_cpu == q_num)
continue;
channel = stor_device->stor_chns[tgt_cpu];
channel = READ_ONCE(
stor_device->stor_chns[tgt_cpu]);
if (channel == NULL)
continue;
if (hv_get_avail_to_write_percent(
&channel->outbound)
> ring_avail_percent_lowater) {
......@@ -1350,7 +1420,10 @@ static int storvsc_do_io(struct hv_device *device,
for_each_cpu(tgt_cpu, &stor_device->alloced_cpus) {
if (cpumask_test_cpu(tgt_cpu, node_mask))
continue;
channel = stor_device->stor_chns[tgt_cpu];
channel = READ_ONCE(
stor_device->stor_chns[tgt_cpu]);
if (channel == NULL)
continue;
if (hv_get_avail_to_write_percent(
&channel->outbound)
> ring_avail_percent_lowater) {
......@@ -1360,7 +1433,14 @@ static int storvsc_do_io(struct hv_device *device,
}
}
} else {
spin_lock_irqsave(&device->channel->lock, flags);
outgoing_channel = stor_device->stor_chns[q_num];
if (outgoing_channel != NULL) {
spin_unlock_irqrestore(&device->channel->lock, flags);
goto found_channel;
}
outgoing_channel = get_og_chn(stor_device, q_num);
spin_unlock_irqrestore(&device->channel->lock, flags);
}
found_channel:
......
/* SPDX-License-Identifier: GPL-2.0 */
/*
* This file contains definitions from Hyper-V Hypervisor Top-Level Functional
* Specification (TLFS):
* https://docs.microsoft.com/en-us/virtualization/hyper-v-on-windows/reference/tlfs
*/
#ifndef _ASM_GENERIC_HYPERV_TLFS_H
#define _ASM_GENERIC_HYPERV_TLFS_H
#include <linux/types.h>
#include <linux/bits.h>
#include <linux/time64.h>
/*
* While not explicitly listed in the TLFS, Hyper-V always runs with a page size
* of 4096. These definitions are used when communicating with Hyper-V using
* guest physical pages and guest physical page addresses, since the guest page
* size may not be 4096 on all architectures.
*/
#define HV_HYP_PAGE_SHIFT 12
#define HV_HYP_PAGE_SIZE BIT(HV_HYP_PAGE_SHIFT)
#define HV_HYP_PAGE_MASK (~(HV_HYP_PAGE_SIZE - 1))
/*
* Hyper-V provides two categories of flags relevant to guest VMs. The
* "Features" category indicates specific functionality that is available
* to guests on this particular instance of Hyper-V. The "Features"
* are presented in four groups, each of which is 32 bits. The group A
* and B definitions are common across architectures and are listed here.
* However, not all flags are relevant on all architectures.
*
* Groups C and D vary across architectures and are listed in the
* architecture specific portion of hyperv-tlfs.h. Some of these flags exist
* on multiple architectures, but the bit positions are different so they
* cannot appear in the generic portion of hyperv-tlfs.h.
*
* The "Enlightenments" category provides recommendations on whether to use
* specific enlightenments that are available. The Enlighenments are a single
* group of 32 bits, but they vary across architectures and are listed in
* the architecture specific portion of hyperv-tlfs.h.
*/
/*
* Group A Features.
*/
/* VP Runtime register available */
#define HV_MSR_VP_RUNTIME_AVAILABLE BIT(0)
/* Partition Reference Counter available*/
#define HV_MSR_TIME_REF_COUNT_AVAILABLE BIT(1)
/* Basic SynIC register available */
#define HV_MSR_SYNIC_AVAILABLE BIT(2)
/* Synthetic Timer registers available */
#define HV_MSR_SYNTIMER_AVAILABLE BIT(3)
/* Virtual APIC assist and VP assist page registers available */
#define HV_MSR_APIC_ACCESS_AVAILABLE BIT(4)
/* Hypercall and Guest OS ID registers available*/
#define HV_MSR_HYPERCALL_AVAILABLE BIT(5)
/* Access virtual processor index register available*/
#define HV_MSR_VP_INDEX_AVAILABLE BIT(6)
/* Virtual system reset register available*/
#define HV_MSR_RESET_AVAILABLE BIT(7)
/* Access statistics page registers available */
#define HV_MSR_STAT_PAGES_AVAILABLE BIT(8)
/* Partition reference TSC register is available */
#define HV_MSR_REFERENCE_TSC_AVAILABLE BIT(9)
/* Partition Guest IDLE register is available */
#define HV_MSR_GUEST_IDLE_AVAILABLE BIT(10)
/* Partition local APIC and TSC frequency registers available */
#define HV_ACCESS_FREQUENCY_MSRS BIT(11)
/* AccessReenlightenmentControls privilege */
#define HV_ACCESS_REENLIGHTENMENT BIT(13)
/* AccessTscInvariantControls privilege */
#define HV_ACCESS_TSC_INVARIANT BIT(15)
/*
* Group B features.
*/
#define HV_CREATE_PARTITIONS BIT(0)
#define HV_ACCESS_PARTITION_ID BIT(1)
#define HV_ACCESS_MEMORY_POOL BIT(2)
#define HV_ADJUST_MESSAGE_BUFFERS BIT(3)
#define HV_POST_MESSAGES BIT(4)
#define HV_SIGNAL_EVENTS BIT(5)
#define HV_CREATE_PORT BIT(6)
#define HV_CONNECT_PORT BIT(7)
#define HV_ACCESS_STATS BIT(8)
#define HV_DEBUGGING BIT(11)
#define HV_CPU_POWER_MANAGEMENT BIT(12)
/*
* TSC page layout.
*/
struct ms_hyperv_tsc_page {
volatile u32 tsc_sequence;
u32 reserved1;
volatile u64 tsc_scale;
volatile s64 tsc_offset;
} __packed;
/*
* The guest OS needs to register the guest ID with the hypervisor.
* The guest ID is a 64 bit entity and the structure of this ID is
* specified in the Hyper-V specification:
*
* msdn.microsoft.com/en-us/library/windows/hardware/ff542653%28v=vs.85%29.aspx
*
* While the current guideline does not specify how Linux guest ID(s)
* need to be generated, our plan is to publish the guidelines for
* Linux and other guest operating systems that currently are hosted
* on Hyper-V. The implementation here conforms to this yet
* unpublished guidelines.
*
*
* Bit(s)
* 63 - Indicates if the OS is Open Source or not; 1 is Open Source
* 62:56 - Os Type; Linux is 0x100
* 55:48 - Distro specific identification
* 47:16 - Linux kernel version number
* 15:0 - Distro specific identification
*
*
*/
#define HV_LINUX_VENDOR_ID 0x8100
/*
* Crash notification flags.
*/
#define HV_CRASH_CTL_CRASH_NOTIFY_MSG BIT_ULL(62)
#define HV_CRASH_CTL_CRASH_NOTIFY BIT_ULL(63)
/* Declare the various hypercall operations. */
#define HVCALL_FLUSH_VIRTUAL_ADDRESS_SPACE 0x0002
#define HVCALL_FLUSH_VIRTUAL_ADDRESS_LIST 0x0003
#define HVCALL_NOTIFY_LONG_SPIN_WAIT 0x0008
#define HVCALL_SEND_IPI 0x000b
#define HVCALL_FLUSH_VIRTUAL_ADDRESS_SPACE_EX 0x0013
#define HVCALL_FLUSH_VIRTUAL_ADDRESS_LIST_EX 0x0014
#define HVCALL_SEND_IPI_EX 0x0015
#define HVCALL_GET_VP_REGISTERS 0x0050
#define HVCALL_SET_VP_REGISTERS 0x0051
#define HVCALL_POST_MESSAGE 0x005c
#define HVCALL_SIGNAL_EVENT 0x005d
#define HVCALL_RETARGET_INTERRUPT 0x007e
#define HVCALL_FLUSH_GUEST_PHYSICAL_ADDRESS_SPACE 0x00af
#define HVCALL_FLUSH_GUEST_PHYSICAL_ADDRESS_LIST 0x00b0
#define HV_FLUSH_ALL_PROCESSORS BIT(0)
#define HV_FLUSH_ALL_VIRTUAL_ADDRESS_SPACES BIT(1)
#define HV_FLUSH_NON_GLOBAL_MAPPINGS_ONLY BIT(2)
#define HV_FLUSH_USE_EXTENDED_RANGE_FORMAT BIT(3)
enum HV_GENERIC_SET_FORMAT {
HV_GENERIC_SET_SPARSE_4K,
HV_GENERIC_SET_ALL,
};
#define HV_PARTITION_ID_SELF ((u64)-1)
#define HV_VP_INDEX_SELF ((u32)-2)
#define HV_HYPERCALL_RESULT_MASK GENMASK_ULL(15, 0)
#define HV_HYPERCALL_FAST_BIT BIT(16)
#define HV_HYPERCALL_VARHEAD_OFFSET 17
#define HV_HYPERCALL_REP_COMP_OFFSET 32
#define HV_HYPERCALL_REP_COMP_1 BIT_ULL(32)
#define HV_HYPERCALL_REP_COMP_MASK GENMASK_ULL(43, 32)
#define HV_HYPERCALL_REP_START_OFFSET 48
#define HV_HYPERCALL_REP_START_MASK GENMASK_ULL(59, 48)
/* hypercall status code */
#define HV_STATUS_SUCCESS 0
#define HV_STATUS_INVALID_HYPERCALL_CODE 2
#define HV_STATUS_INVALID_HYPERCALL_INPUT 3
#define HV_STATUS_INVALID_ALIGNMENT 4
#define HV_STATUS_INVALID_PARAMETER 5
#define HV_STATUS_INSUFFICIENT_MEMORY 11
#define HV_STATUS_INVALID_PORT_ID 17
#define HV_STATUS_INVALID_CONNECTION_ID 18
#define HV_STATUS_INSUFFICIENT_BUFFERS 19
/*
* The Hyper-V TimeRefCount register and the TSC
* page provide a guest VM clock with 100ns tick rate
*/
#define HV_CLOCK_HZ (NSEC_PER_SEC/100)
/* Define the number of synthetic interrupt sources. */
#define HV_SYNIC_SINT_COUNT (16)
/* Define the expected SynIC version. */
#define HV_SYNIC_VERSION_1 (0x1)
/* Valid SynIC vectors are 16-255. */
#define HV_SYNIC_FIRST_VALID_VECTOR (16)
#define HV_SYNIC_CONTROL_ENABLE (1ULL << 0)
#define HV_SYNIC_SIMP_ENABLE (1ULL << 0)
#define HV_SYNIC_SIEFP_ENABLE (1ULL << 0)
#define HV_SYNIC_SINT_MASKED (1ULL << 16)
#define HV_SYNIC_SINT_AUTO_EOI (1ULL << 17)
#define HV_SYNIC_SINT_VECTOR_MASK (0xFF)
#define HV_SYNIC_STIMER_COUNT (4)
/* Define synthetic interrupt controller message constants. */
#define HV_MESSAGE_SIZE (256)
#define HV_MESSAGE_PAYLOAD_BYTE_COUNT (240)
#define HV_MESSAGE_PAYLOAD_QWORD_COUNT (30)
/* Define synthetic interrupt controller message flags. */
union hv_message_flags {
__u8 asu8;
struct {
__u8 msg_pending:1;
__u8 reserved:7;
} __packed;
};
/* Define port identifier type. */
union hv_port_id {
__u32 asu32;
struct {
__u32 id:24;
__u32 reserved:8;
} __packed u;
};
/* Define synthetic interrupt controller message header. */
struct hv_message_header {
__u32 message_type;
__u8 payload_size;
union hv_message_flags message_flags;
__u8 reserved[2];
union {
__u64 sender;
union hv_port_id port;
};
} __packed;
/* Define synthetic interrupt controller message format. */
struct hv_message {
struct hv_message_header header;
union {
__u64 payload[HV_MESSAGE_PAYLOAD_QWORD_COUNT];
} u;
} __packed;
/* Define the synthetic interrupt message page layout. */
struct hv_message_page {
struct hv_message sint_message[HV_SYNIC_SINT_COUNT];
} __packed;
/* Define timer message payload structure. */
struct hv_timer_message_payload {
__u32 timer_index;
__u32 reserved;
__u64 expiration_time; /* When the timer expired */
__u64 delivery_time; /* When the message was delivered */
} __packed;
/* Define synthetic interrupt controller flag constants. */
#define HV_EVENT_FLAGS_COUNT (256 * 8)
#define HV_EVENT_FLAGS_LONG_COUNT (256 / sizeof(unsigned long))
/*
* Synthetic timer configuration.
*/
union hv_stimer_config {
u64 as_uint64;
struct {
u64 enable:1;
u64 periodic:1;
u64 lazy:1;
u64 auto_enable:1;
u64 apic_vector:8;
u64 direct_mode:1;
u64 reserved_z0:3;
u64 sintx:4;
u64 reserved_z1:44;
} __packed;
};
/* Define the synthetic interrupt controller event flags format. */
union hv_synic_event_flags {
unsigned long flags[HV_EVENT_FLAGS_LONG_COUNT];
};
/* Define SynIC control register. */
union hv_synic_scontrol {
u64 as_uint64;
struct {
u64 enable:1;
u64 reserved:63;
} __packed;
};
/* Define synthetic interrupt source. */
union hv_synic_sint {
u64 as_uint64;
struct {
u64 vector:8;
u64 reserved1:8;
u64 masked:1;
u64 auto_eoi:1;
u64 polling:1;
u64 reserved2:45;
} __packed;
};
/* Define the format of the SIMP register */
union hv_synic_simp {
u64 as_uint64;
struct {
u64 simp_enabled:1;
u64 preserved:11;
u64 base_simp_gpa:52;
} __packed;
};
/* Define the format of the SIEFP register */
union hv_synic_siefp {
u64 as_uint64;
struct {
u64 siefp_enabled:1;
u64 preserved:11;
u64 base_siefp_gpa:52;
} __packed;
};
struct hv_vpset {
u64 format;
u64 valid_bank_mask;
u64 bank_contents[];
} __packed;
/* HvCallSendSyntheticClusterIpi hypercall */
struct hv_send_ipi {
u32 vector;
u32 reserved;
u64 cpu_mask;
} __packed;
/* HvCallSendSyntheticClusterIpiEx hypercall */
struct hv_send_ipi_ex {
u32 vector;
u32 reserved;
struct hv_vpset vp_set;
} __packed;
/* HvFlushGuestPhysicalAddressSpace hypercalls */
struct hv_guest_mapping_flush {
u64 address_space;
u64 flags;
} __packed;
/*
* HV_MAX_FLUSH_PAGES = "additional_pages" + 1. It's limited
* by the bitwidth of "additional_pages" in union hv_gpa_page_range.
*/
#define HV_MAX_FLUSH_PAGES (2048)
/* HvFlushGuestPhysicalAddressList hypercall */
union hv_gpa_page_range {
u64 address_space;
struct {
u64 additional_pages:11;
u64 largepage:1;
u64 basepfn:52;
} page;
};
/*
* All input flush parameters should be in single page. The max flush
* count is equal with how many entries of union hv_gpa_page_range can
* be populated into the input parameter page.
*/
#define HV_MAX_FLUSH_REP_COUNT ((HV_HYP_PAGE_SIZE - 2 * sizeof(u64)) / \
sizeof(union hv_gpa_page_range))
struct hv_guest_mapping_flush_list {
u64 address_space;
u64 flags;
union hv_gpa_page_range gpa_list[HV_MAX_FLUSH_REP_COUNT];
};
/* HvFlushVirtualAddressSpace, HvFlushVirtualAddressList hypercalls */
struct hv_tlb_flush {
u64 address_space;
u64 flags;
u64 processor_mask;
u64 gva_list[];
} __packed;
/* HvFlushVirtualAddressSpaceEx, HvFlushVirtualAddressListEx hypercalls */
struct hv_tlb_flush_ex {
u64 address_space;
u64 flags;
struct hv_vpset hv_vp_set;
u64 gva_list[];
} __packed;
/* HvRetargetDeviceInterrupt hypercall */
union hv_msi_entry {
u64 as_uint64;
struct {
u32 address;
u32 data;
} __packed;
};
struct hv_interrupt_entry {
u32 source; /* 1 for MSI(-X) */
u32 reserved1;
union hv_msi_entry msi_entry;
} __packed;
/*
* flags for hv_device_interrupt_target.flags
*/
#define HV_DEVICE_INTERRUPT_TARGET_MULTICAST 1
#define HV_DEVICE_INTERRUPT_TARGET_PROCESSOR_SET 2
struct hv_device_interrupt_target {
u32 vector;
u32 flags;
union {
u64 vp_mask;
struct hv_vpset vp_set;
};
} __packed;
struct hv_retarget_device_interrupt {
u64 partition_id; /* use "self" */
u64 device_id;
struct hv_interrupt_entry int_entry;
u64 reserved2;
struct hv_device_interrupt_target int_target;
} __packed __aligned(8);
/* HvGetVpRegisters hypercall input with variable size reg name list*/
struct hv_get_vp_registers_input {
struct {
u64 partitionid;
u32 vpindex;
u8 inputvtl;
u8 padding[3];
} header;
struct input {
u32 name0;
u32 name1;
} element[];
} __packed;
/* HvGetVpRegisters returns an array of these output elements */
struct hv_get_vp_registers_output {
union {
struct {
u32 a;
u32 b;
u32 c;
u32 d;
} as32 __packed;
struct {
u64 low;
u64 high;
} as64 __packed;
};
};
/* HvSetVpRegisters hypercall with variable size reg name/value list*/
struct hv_set_vp_registers_input {
struct {
u64 partitionid;
u32 vpindex;
u8 inputvtl;
u8 padding[3];
} header;
struct {
u32 name;
u32 padding1;
u64 padding2;
u64 valuelow;
u64 valuehigh;
} element[];
} __packed;
#endif
......@@ -117,7 +117,7 @@ struct hv_ring_buffer {
* Ring data starts here + RingDataStartOffset
* !!! DO NOT place any fields below this !!!
*/
u8 buffer[0];
u8 buffer[];
} __packed;
struct hv_ring_buffer_info {
......@@ -313,7 +313,7 @@ struct vmadd_remove_transfer_page_set {
struct gpa_range {
u32 byte_count;
u32 byte_offset;
u64 pfn_array[0];
u64 pfn_array[];
};
/*
......@@ -425,7 +425,7 @@ enum vmbus_channel_message_type {
CHANNELMSG_19 = 19,
CHANNELMSG_20 = 20,
CHANNELMSG_TL_CONNECT_REQUEST = 21,
CHANNELMSG_22 = 22,
CHANNELMSG_MODIFYCHANNEL = 22,
CHANNELMSG_TL_CONNECT_RESULT = 23,
CHANNELMSG_COUNT
};
......@@ -563,7 +563,7 @@ struct vmbus_channel_gpadl_header {
u32 gpadl;
u16 range_buflen;
u16 rangecount;
struct gpa_range range[0];
struct gpa_range range[];
} __packed;
/* This is the followup packet that contains more PFNs. */
......@@ -571,7 +571,7 @@ struct vmbus_channel_gpadl_body {
struct vmbus_channel_message_header header;
u32 msgnumber;
u32 gpadl;
u64 pfn[0];
u64 pfn[];
} __packed;
struct vmbus_channel_gpadl_created {
......@@ -620,6 +620,13 @@ struct vmbus_channel_tl_connect_request {
guid_t host_service_id;
} __packed;
/* Modify Channel parameters, cf. vmbus_send_modifychannel() */
struct vmbus_channel_modifychannel {
struct vmbus_channel_message_header header;
u32 child_relid;
u32 target_vp;
} __packed;
struct vmbus_channel_version_response {
struct vmbus_channel_message_header header;
u8 version_supported;
......@@ -672,7 +679,7 @@ struct vmbus_channel_msginfo {
* The channel message that goes out on the "wire".
* It will contain at minimum the VMBUS_CHANNEL_MESSAGE_HEADER header
*/
unsigned char msg[0];
unsigned char msg[];
};
struct vmbus_close_msg {
......@@ -689,11 +696,6 @@ union hv_connection_id {
} u;
};
enum hv_numa_policy {
HV_BALANCED = 0,
HV_LOCALIZED,
};
enum vmbus_device_type {
HV_IDE = 0,
HV_SCSI,
......@@ -771,6 +773,15 @@ struct vmbus_channel {
void (*onchannel_callback)(void *context);
void *channel_callback_context;
void (*change_target_cpu_callback)(struct vmbus_channel *channel,
u32 old, u32 new);
/*
* Synchronize channel scheduling and channel removal; see the inline
* comments in vmbus_chan_sched() and vmbus_reset_channel_cb().
*/
spinlock_t sched_lock;
/*
* A channel can be marked for one of three modes of reading:
* BATCHED - callback called from taslket and should read
......@@ -802,10 +813,6 @@ struct vmbus_channel {
u32 target_vp;
/* The corresponding CPUID in the guest */
u32 target_cpu;
/*
* State to manage the CPU affiliation of channels.
*/
struct cpumask alloced_cpus_in_node;
int numa_node;
/*
* Support for sub-channels. For high performance devices,
......@@ -854,11 +861,6 @@ struct vmbus_channel {
* Support per-channel state for use by vmbus drivers.
*/
void *per_channel_state;
/*
* To support per-cpu lookup mapping of relid to channel,
* link up channels based on their CPU affinity.
*/
struct list_head percpu_list;
/*
* Defer freeing channel until after all cpu's have
......@@ -897,19 +899,14 @@ struct vmbus_channel {
*/
bool low_latency;
bool probe_done;
/*
* NUMA distribution policy:
* We support two policies:
* 1) Balanced: Here all performance critical channels are
* distributed evenly amongst all the NUMA nodes.
* This policy will be the default policy.
* 2) Localized: All channels of a given instance of a
* performance critical service will be assigned CPUs
* within a selected NUMA node.
* Cache the device ID here for easy access; this is useful, in
* particular, in situations where the channel's device_obj has
* not been allocated/initialized yet.
*/
enum hv_numa_policy affinity_policy;
bool probe_done;
u16 device_id;
/*
* We must offload the handling of the primary/sub channels
......@@ -964,12 +961,6 @@ static inline bool is_sub_channel(const struct vmbus_channel *c)
return c->offermsg.offer.sub_channel_index != 0;
}
static inline void set_channel_affinity_state(struct vmbus_channel *c,
enum hv_numa_policy policy)
{
c->affinity_policy = policy;
}
static inline void set_channel_read_mode(struct vmbus_channel *c,
enum hv_callback_mode mode)
{
......@@ -1017,7 +1008,7 @@ static inline void clear_low_latency_mode(struct vmbus_channel *c)
c->low_latency = false;
}
void vmbus_onmessage(void *context);
void vmbus_onmessage(struct vmbus_channel_message_header *hdr);
int vmbus_request_offers(void);
......@@ -1531,6 +1522,7 @@ extern __u32 vmbus_proto_version;
int vmbus_send_tl_connect_request(const guid_t *shv_guest_servie_id,
const guid_t *shv_host_servie_id);
int vmbus_send_modifychannel(u32 child_relid, u32 target_vp);
void vmbus_set_event(struct vmbus_channel *channel);
/* Get the start of the ring buffer. */
......
......@@ -434,7 +434,7 @@ struct virtio_device_id {
* For Hyper-V devices we use the device guid as the id.
*/
struct hv_vmbus_device_id {
uuid_le guid;
guid_t guid;
kernel_ulong_t driver_data; /* Data private to the driver */
};
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment