Merge tag 'hyperv-next-signed-20220528' of git://git.kernel.org/pub/scm/linux/kernel...
authorLinus Torvalds <torvalds@linux-foundation.org>
Sat, 28 May 2022 18:39:01 +0000 (11:39 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sat, 28 May 2022 18:39:01 +0000 (11:39 -0700)
Pull hyperv updates from Wei Liu:

 - Harden hv_sock driver (Andrea Parri)

 - Harden Hyper-V PCI driver (Andrea Parri)

 - Fix multi-MSI for Hyper-V PCI driver (Jeffrey Hugo)

 - Fix Hyper-V PCI to reduce boot time (Dexuan Cui)

 - Remove code for long EOL'ed Hyper-V versions (Michael Kelley, Saurabh
   Sengar)

 - Fix balloon driver error handling (Shradha Gupta)

 - Fix a typo in vmbus driver (Julia Lawall)

 - Ignore vmbus IMC device (Michael Kelley)

 - Add a new error message to Hyper-V DRM driver (Saurabh Sengar)

* tag 'hyperv-next-signed-20220528' of git://git.kernel.org/pub/scm/linux/kernel/git/hyperv/linux: (28 commits)
  hv_balloon: Fix balloon_probe() and balloon_remove() error handling
  scsi: storvsc: Removing Pre Win8 related logic
  Drivers: hv: vmbus: fix typo in comment
  PCI: hv: Fix synchronization between channel callback and hv_pci_bus_exit()
  PCI: hv: Add validation for untrusted Hyper-V values
  PCI: hv: Fix interrupt mapping for multi-MSI
  PCI: hv: Reuse existing IRTE allocation in compose_msi_msg()
  drm/hyperv: Remove support for Hyper-V 2008 and 2008R2/Win7
  video: hyperv_fb: Remove support for Hyper-V 2008 and 2008R2/Win7
  scsi: storvsc: Remove support for Hyper-V 2008 and 2008R2/Win7
  Drivers: hv: vmbus: Remove support for Hyper-V 2008 and Hyper-V 2008R2/Win7
  x86/hyperv: Disable hardlockup detector by default in Hyper-V guests
  drm/hyperv: Add error message for fb size greater than allocated
  PCI: hv: Do not set PCI_COMMAND_MEMORY to reduce VM boot time
  PCI: hv: Fix hv_arch_irq_unmask() for multi-MSI
  Drivers: hv: vmbus: Refactor the ring-buffer iterator functions
  Drivers: hv: vmbus: Accept hv_sock offers in isolated guests
  hv_sock: Add validation for untrusted Hyper-V values
  hv_sock: Copy packets sent by Hyper-V out of the ring buffer
  hv_sock: Check hv_pkt_iter_first_raw()'s return value
  ...

15 files changed:
arch/x86/kernel/cpu/mshyperv.c
drivers/gpu/drm/hyperv/hyperv_drm_modeset.c
drivers/gpu/drm/hyperv/hyperv_drm_proto.c
drivers/hv/channel.c
drivers/hv/channel_mgmt.c
drivers/hv/connection.c
drivers/hv/hv_balloon.c
drivers/hv/hyperv_vmbus.h
drivers/hv/ring_buffer.c
drivers/hv/vmbus_drv.c
drivers/pci/controller/pci-hyperv.c
drivers/scsi/storvsc_drv.c
drivers/video/fbdev/hyperv_fb.c
include/linux/hyperv.h
net/vmw_vsock/hyperv_transport.c

index 5b8f2c3..8316139 100644 (file)
@@ -457,6 +457,8 @@ static void __init ms_hyperv_init_platform(void)
         */
        if (!(ms_hyperv.features & HV_ACCESS_TSC_INVARIANT))
                mark_tsc_unstable("running on Hyper-V");
+
+       hardlockup_detector_disable();
 }
 
 static bool __init ms_hyperv_x2apic_available(void)
index e82b815..27f4fcb 100644 (file)
@@ -123,8 +123,11 @@ static int hyperv_pipe_check(struct drm_simple_display_pipe *pipe,
        if (fb->format->format != DRM_FORMAT_XRGB8888)
                return -EINVAL;
 
-       if (fb->pitches[0] * fb->height > hv->fb_size)
+       if (fb->pitches[0] * fb->height > hv->fb_size) {
+               drm_err(&hv->dev, "fb size requested by %s for %dX%d (pitch %d) greater than %ld\n",
+                       current->comm, fb->width, fb->height, fb->pitches[0], hv->fb_size);
                return -EINVAL;
+       }
 
        return 0;
 }
index c0155c6..76a182a 100644 (file)
 #define SYNTHVID_VERSION(major, minor) ((minor) << 16 | (major))
 #define SYNTHVID_VER_GET_MAJOR(ver) (ver & 0x0000ffff)
 #define SYNTHVID_VER_GET_MINOR(ver) ((ver & 0xffff0000) >> 16)
+
+/* Support for VERSION_WIN7 is removed. #define is retained for reference. */
 #define SYNTHVID_VERSION_WIN7 SYNTHVID_VERSION(3, 0)
 #define SYNTHVID_VERSION_WIN8 SYNTHVID_VERSION(3, 2)
 #define SYNTHVID_VERSION_WIN10 SYNTHVID_VERSION(3, 5)
 
-#define SYNTHVID_DEPTH_WIN7 16
 #define SYNTHVID_DEPTH_WIN8 32
-#define SYNTHVID_FB_SIZE_WIN7 (4 * 1024 * 1024)
+#define SYNTHVID_WIDTH_WIN8 1600
+#define SYNTHVID_HEIGHT_WIN8 1200
 #define SYNTHVID_FB_SIZE_WIN8 (8 * 1024 * 1024)
-#define SYNTHVID_WIDTH_MAX_WIN7 1600
-#define SYNTHVID_HEIGHT_MAX_WIN7 1200
 
 enum pipe_msg_type {
        PIPE_MSG_INVALID,
@@ -496,12 +496,6 @@ int hyperv_connect_vsp(struct hv_device *hdev)
        case VERSION_WIN8:
        case VERSION_WIN8_1:
                ret = hyperv_negotiate_version(hdev, SYNTHVID_VERSION_WIN8);
-               if (!ret)
-                       break;
-               fallthrough;
-       case VERSION_WS2008:
-       case VERSION_WIN7:
-               ret = hyperv_negotiate_version(hdev, SYNTHVID_VERSION_WIN7);
                break;
        default:
                ret = hyperv_negotiate_version(hdev, SYNTHVID_VERSION_WIN10);
@@ -513,18 +507,15 @@ int hyperv_connect_vsp(struct hv_device *hdev)
                goto error;
        }
 
-       if (hv->synthvid_version == SYNTHVID_VERSION_WIN7)
-               hv->screen_depth = SYNTHVID_DEPTH_WIN7;
-       else
-               hv->screen_depth = SYNTHVID_DEPTH_WIN8;
+       hv->screen_depth = SYNTHVID_DEPTH_WIN8;
 
        if (hyperv_version_ge(hv->synthvid_version, SYNTHVID_VERSION_WIN10)) {
                ret = hyperv_get_supported_resolution(hdev);
                if (ret)
                        drm_err(dev, "Failed to get supported resolution from host, use default\n");
        } else {
-               hv->screen_width_max = SYNTHVID_WIDTH_MAX_WIN7;
-               hv->screen_height_max = SYNTHVID_HEIGHT_MAX_WIN7;
+               hv->screen_width_max = SYNTHVID_WIDTH_WIN8;
+               hv->screen_height_max = SYNTHVID_HEIGHT_WIN8;
        }
 
        hv->mmio_megabytes = hdev->channel->offermsg.offer.mmio_megabytes;
index dc5c352..56f7e06 100644 (file)
@@ -1022,11 +1022,13 @@ void vmbus_close(struct vmbus_channel *channel)
 EXPORT_SYMBOL_GPL(vmbus_close);
 
 /**
- * vmbus_sendpacket() - Send the specified buffer on the given channel
+ * vmbus_sendpacket_getid() - Send the specified buffer on the given channel
  * @channel: Pointer to vmbus_channel structure
  * @buffer: Pointer to the buffer you want to send the data from.
  * @bufferlen: Maximum size of what the buffer holds.
  * @requestid: Identifier of the request
+ * @trans_id: Identifier of the transaction associated to this request, if
+ *            the send is successful; undefined, otherwise.
  * @type: Type of packet that is being sent e.g. negotiate, time
  *       packet etc.
  * @flags: 0 or VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED
@@ -1036,8 +1038,8 @@ EXPORT_SYMBOL_GPL(vmbus_close);
  *
  * Mainly used by Hyper-V drivers.
  */
-int vmbus_sendpacket(struct vmbus_channel *channel, void *buffer,
-                          u32 bufferlen, u64 requestid,
+int vmbus_sendpacket_getid(struct vmbus_channel *channel, void *buffer,
+                          u32 bufferlen, u64 requestid, u64 *trans_id,
                           enum vmbus_packet_type type, u32 flags)
 {
        struct vmpacket_descriptor desc;
@@ -1063,7 +1065,31 @@ int vmbus_sendpacket(struct vmbus_channel *channel, void *buffer,
        bufferlist[2].iov_base = &aligned_data;
        bufferlist[2].iov_len = (packetlen_aligned - packetlen);
 
-       return hv_ringbuffer_write(channel, bufferlist, num_vecs, requestid);
+       return hv_ringbuffer_write(channel, bufferlist, num_vecs, requestid, trans_id);
+}
+EXPORT_SYMBOL(vmbus_sendpacket_getid);
+
+/**
+ * vmbus_sendpacket() - Send the specified buffer on the given channel
+ * @channel: Pointer to vmbus_channel structure
+ * @buffer: Pointer to the buffer you want to send the data from.
+ * @bufferlen: Maximum size of what the buffer holds.
+ * @requestid: Identifier of the request
+ * @type: Type of packet that is being sent e.g. negotiate, time
+ *       packet etc.
+ * @flags: 0 or VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED
+ *
+ * Sends data in @buffer directly to Hyper-V via the vmbus.
+ * This will send the data unparsed to Hyper-V.
+ *
+ * Mainly used by Hyper-V drivers.
+ */
+int vmbus_sendpacket(struct vmbus_channel *channel, void *buffer,
+                    u32 bufferlen, u64 requestid,
+                    enum vmbus_packet_type type, u32 flags)
+{
+       return vmbus_sendpacket_getid(channel, buffer, bufferlen,
+                                     requestid, NULL, type, flags);
 }
 EXPORT_SYMBOL(vmbus_sendpacket);
 
@@ -1122,7 +1148,7 @@ int vmbus_sendpacket_pagebuffer(struct vmbus_channel *channel,
        bufferlist[2].iov_base = &aligned_data;
        bufferlist[2].iov_len = (packetlen_aligned - packetlen);
 
-       return hv_ringbuffer_write(channel, bufferlist, 3, requestid);
+       return hv_ringbuffer_write(channel, bufferlist, 3, requestid, NULL);
 }
 EXPORT_SYMBOL_GPL(vmbus_sendpacket_pagebuffer);
 
@@ -1160,7 +1186,7 @@ int vmbus_sendpacket_mpb_desc(struct vmbus_channel *channel,
        bufferlist[2].iov_base = &aligned_data;
        bufferlist[2].iov_len = (packetlen_aligned - packetlen);
 
-       return hv_ringbuffer_write(channel, bufferlist, 3, requestid);
+       return hv_ringbuffer_write(channel, bufferlist, 3, requestid, NULL);
 }
 EXPORT_SYMBOL_GPL(vmbus_sendpacket_mpb_desc);
 
@@ -1226,12 +1252,12 @@ u64 vmbus_next_request_id(struct vmbus_channel *channel, u64 rqst_addr)
        if (!channel->rqstor_size)
                return VMBUS_NO_RQSTOR;
 
-       spin_lock_irqsave(&rqstor->req_lock, flags);
+       lock_requestor(channel, flags);
        current_id = rqstor->next_request_id;
 
        /* Requestor array is full */
        if (current_id >= rqstor->size) {
-               spin_unlock_irqrestore(&rqstor->req_lock, flags);
+               unlock_requestor(channel, flags);
                return VMBUS_RQST_ERROR;
        }
 
@@ -1241,27 +1267,23 @@ u64 vmbus_next_request_id(struct vmbus_channel *channel, u64 rqst_addr)
        /* The already held spin lock provides atomicity */
        bitmap_set(rqstor->req_bitmap, current_id, 1);
 
-       spin_unlock_irqrestore(&rqstor->req_lock, flags);
+       unlock_requestor(channel, flags);
 
        /*
         * Cannot return an ID of 0, which is reserved for an unsolicited
-        * message from Hyper-V.
+        * message from Hyper-V; Hyper-V does not acknowledge (respond to)
+        * VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED requests with ID of
+        * 0 sent by the guest.
         */
        return current_id + 1;
 }
 EXPORT_SYMBOL_GPL(vmbus_next_request_id);
 
-/*
- * vmbus_request_addr - Returns the memory address stored at @trans_id
- * in @rqstor. Uses a spin lock to avoid race conditions.
- * @channel: Pointer to the VMbus channel struct
- * @trans_id: Request id sent back from Hyper-V. Becomes the requestor's
- * next request id.
- */
-u64 vmbus_request_addr(struct vmbus_channel *channel, u64 trans_id)
+/* As in vmbus_request_addr_match() but without the requestor lock */
+u64 __vmbus_request_addr_match(struct vmbus_channel *channel, u64 trans_id,
+                              u64 rqst_addr)
 {
        struct vmbus_requestor *rqstor = &channel->requestor;
-       unsigned long flags;
        u64 req_addr;
 
        /* Check rqstor has been initialized */
@@ -1270,27 +1292,61 @@ u64 vmbus_request_addr(struct vmbus_channel *channel, u64 trans_id)
 
        /* Hyper-V can send an unsolicited message with ID of 0 */
        if (!trans_id)
-               return trans_id;
-
-       spin_lock_irqsave(&rqstor->req_lock, flags);
+               return VMBUS_RQST_ERROR;
 
        /* Data corresponding to trans_id is stored at trans_id - 1 */
        trans_id--;
 
        /* Invalid trans_id */
-       if (trans_id >= rqstor->size || !test_bit(trans_id, rqstor->req_bitmap)) {
-               spin_unlock_irqrestore(&rqstor->req_lock, flags);
+       if (trans_id >= rqstor->size || !test_bit(trans_id, rqstor->req_bitmap))
                return VMBUS_RQST_ERROR;
-       }
 
        req_addr = rqstor->req_arr[trans_id];
-       rqstor->req_arr[trans_id] = rqstor->next_request_id;
-       rqstor->next_request_id = trans_id;
+       if (rqst_addr == VMBUS_RQST_ADDR_ANY || req_addr == rqst_addr) {
+               rqstor->req_arr[trans_id] = rqstor->next_request_id;
+               rqstor->next_request_id = trans_id;
 
-       /* The already held spin lock provides atomicity */
-       bitmap_clear(rqstor->req_bitmap, trans_id, 1);
+               /* The already held spin lock provides atomicity */
+               bitmap_clear(rqstor->req_bitmap, trans_id, 1);
+       }
 
-       spin_unlock_irqrestore(&rqstor->req_lock, flags);
        return req_addr;
 }
+EXPORT_SYMBOL_GPL(__vmbus_request_addr_match);
+
+/*
+ * vmbus_request_addr_match - Clears/removes @trans_id from the @channel's
+ * requestor, provided the memory address stored at @trans_id equals @rqst_addr
+ * (or provided @rqst_addr matches the sentinel value VMBUS_RQST_ADDR_ANY).
+ *
+ * Returns the memory address stored at @trans_id, or VMBUS_RQST_ERROR if
+ * @trans_id is not contained in the requestor.
+ *
+ * Acquires and releases the requestor spin lock.
+ */
+u64 vmbus_request_addr_match(struct vmbus_channel *channel, u64 trans_id,
+                            u64 rqst_addr)
+{
+       unsigned long flags;
+       u64 req_addr;
+
+       lock_requestor(channel, flags);
+       req_addr = __vmbus_request_addr_match(channel, trans_id, rqst_addr);
+       unlock_requestor(channel, flags);
+
+       return req_addr;
+}
+EXPORT_SYMBOL_GPL(vmbus_request_addr_match);
+
+/*
+ * vmbus_request_addr - Returns the memory address stored at @trans_id
+ * in @rqstor. Uses a spin lock to avoid race conditions.
+ * @channel: Pointer to the VMbus channel struct
+ * @trans_id: Request id sent back from Hyper-V. Becomes the requestor's
+ * next request id.
+ */
+u64 vmbus_request_addr(struct vmbus_channel *channel, u64 trans_id)
+{
+       return vmbus_request_addr_match(channel, trans_id, VMBUS_RQST_ADDR_ANY);
+}
 EXPORT_SYMBOL_GPL(vmbus_request_addr);
index 85a2142..b60f134 100644 (file)
@@ -152,6 +152,7 @@ static const struct {
        { HV_AVMA1_GUID },
        { HV_AVMA2_GUID },
        { HV_RDV_GUID   },
+       { HV_IMC_GUID   },
 };
 
 /*
@@ -442,7 +443,7 @@ void hv_process_channel_removal(struct vmbus_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
+        * user-space application 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.
         */
@@ -713,15 +714,13 @@ static bool hv_cpuself_used(u32 cpu, struct vmbus_channel *chn)
 static int next_numa_node_id;
 
 /*
- * Starting with Win8, we can statically distribute the incoming
- * channel interrupt load by binding a channel to VCPU.
+ * We can statically distribute the incoming channel interrupt load
+ * by binding a channel to VCPU.
  *
- * For pre-win8 hosts or non-performance critical channels we assign the
- * 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.
+ * For non-performance critical channels we assign the VMBUS_CONNECT_CPU.
+ * 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)
 {
@@ -732,13 +731,10 @@ static void init_vp_index(struct vmbus_channel *channel)
        u32 target_cpu;
        int numa_node;
 
-       if ((vmbus_proto_version == VERSION_WS2008) ||
-           (vmbus_proto_version == VERSION_WIN7) || (!perf_chn) ||
+       if (!perf_chn ||
            !alloc_cpumask_var(&available_mask, GFP_KERNEL)) {
                /*
-                * Prior to win8, all channel interrupts are
-                * delivered on VMBUS_CONNECT_CPU.
-                * Also if the channel is not a performance critical
+                * If the channel is not a performance critical
                 * channel, bind it to VMBUS_CONNECT_CPU.
                 * In case alloc_cpumask_var() fails, bind it to
                 * VMBUS_CONNECT_CPU.
@@ -931,11 +927,9 @@ static void vmbus_setup_channel_state(struct vmbus_channel *channel,
         */
        channel->sig_event = VMBUS_EVENT_CONNECTION_ID;
 
-       if (vmbus_proto_version != VERSION_WS2008) {
-               channel->is_dedicated_interrupt =
-                               (offer->is_dedicated_interrupt != 0);
-               channel->sig_event = offer->connection_id;
-       }
+       channel->is_dedicated_interrupt =
+                       (offer->is_dedicated_interrupt != 0);
+       channel->sig_event = offer->connection_id;
 
        memcpy(&channel->offermsg, offer,
               sizeof(struct vmbus_channel_offer_channel));
@@ -975,13 +969,17 @@ find_primary_channel_by_offer(const struct vmbus_channel_offer_channel *offer)
        return channel;
 }
 
-static bool vmbus_is_valid_device(const guid_t *guid)
+static bool vmbus_is_valid_offer(const struct vmbus_channel_offer_channel *offer)
 {
+       const guid_t *guid = &offer->offer.if_type;
        u16 i;
 
        if (!hv_is_isolation_supported())
                return true;
 
+       if (is_hvsock_offer(offer))
+               return true;
+
        for (i = 0; i < ARRAY_SIZE(vmbus_devs); i++) {
                if (guid_equal(guid, &vmbus_devs[i].guid))
                        return vmbus_devs[i].allowed_in_isolated;
@@ -1003,7 +1001,7 @@ static void vmbus_onoffer(struct vmbus_channel_message_header *hdr)
 
        trace_vmbus_onoffer(offer);
 
-       if (!vmbus_is_valid_device(&offer->offer.if_type)) {
+       if (!vmbus_is_valid_offer(offer)) {
                pr_err_ratelimited("Invalid offer %d from the host supporting isolation\n",
                                   offer->child_relid);
                atomic_dec(&vmbus_connection.offer_in_progress);
index a3d8be8..6218bbf 100644 (file)
@@ -47,6 +47,8 @@ EXPORT_SYMBOL_GPL(vmbus_proto_version);
 
 /*
  * Table of VMBus versions listed from newest to oldest.
+ * VERSION_WIN7 and VERSION_WS2008 are no longer supported in
+ * Linux guests and are not listed.
  */
 static __u32 vmbus_versions[] = {
        VERSION_WIN10_V5_3,
@@ -56,9 +58,7 @@ static __u32 vmbus_versions[] = {
        VERSION_WIN10_V4_1,
        VERSION_WIN10,
        VERSION_WIN8_1,
-       VERSION_WIN8,
-       VERSION_WIN7,
-       VERSION_WS2008
+       VERSION_WIN8
 };
 
 /*
index 3248b48..91e8a72 100644 (file)
@@ -1842,7 +1842,7 @@ static int balloon_probe(struct hv_device *dev,
 
        ret = balloon_connect_vsp(dev);
        if (ret != 0)
-               return ret;
+               goto connect_error;
 
        enable_page_reporting();
        dm_device.state = DM_INITIALIZED;
@@ -1861,6 +1861,7 @@ probe_error:
        dm_device.thread  = NULL;
        disable_page_reporting();
        vmbus_close(dev->channel);
+connect_error:
 #ifdef CONFIG_MEMORY_HOTPLUG
        unregister_memory_notifier(&hv_memory_nb);
        restore_online_page_callback(&hv_online_page);
@@ -1882,12 +1883,21 @@ static int balloon_remove(struct hv_device *dev)
        cancel_work_sync(&dm->ha_wrk.wrk);
 
        kthread_stop(dm->thread);
-       disable_page_reporting();
-       vmbus_close(dev->channel);
+
+       /*
+        * This is to handle the case when balloon_resume()
+        * call has failed and some cleanup has been done as
+        * a part of the error handling.
+        */
+       if (dm_device.state != DM_INIT_ERROR) {
+               disable_page_reporting();
+               vmbus_close(dev->channel);
 #ifdef CONFIG_MEMORY_HOTPLUG
-       unregister_memory_notifier(&hv_memory_nb);
-       restore_online_page_callback(&hv_online_page);
+               unregister_memory_notifier(&hv_memory_nb);
+               restore_online_page_callback(&hv_online_page);
 #endif
+       }
+
        spin_lock_irqsave(&dm_device.ha_lock, flags);
        list_for_each_entry_safe(has, tmp, &dm->ha_region_list, list) {
                list_for_each_entry_safe(gap, tmp_gap, &has->gap_list, list) {
@@ -1948,6 +1958,7 @@ close_channel:
        vmbus_close(dev->channel);
 out:
        dm_device.state = DM_INIT_ERROR;
+       disable_page_reporting();
 #ifdef CONFIG_MEMORY_HOTPLUG
        unregister_memory_notifier(&hv_memory_nb);
        restore_online_page_callback(&hv_online_page);
index 6b45c22..4f5b824 100644 (file)
@@ -181,7 +181,7 @@ void hv_ringbuffer_cleanup(struct hv_ring_buffer_info *ring_info);
 
 int hv_ringbuffer_write(struct vmbus_channel *channel,
                        const struct kvec *kv_list, u32 kv_count,
-                       u64 requestid);
+                       u64 requestid, u64 *trans_id);
 
 int hv_ringbuffer_read(struct vmbus_channel *channel,
                       void *buffer, u32 buflen, u32 *buffer_actual_len,
index 3d215d9..59a4aa8 100644 (file)
@@ -283,7 +283,7 @@ void hv_ringbuffer_cleanup(struct hv_ring_buffer_info *ring_info)
 /* Write to the ring buffer. */
 int hv_ringbuffer_write(struct vmbus_channel *channel,
                        const struct kvec *kv_list, u32 kv_count,
-                       u64 requestid)
+                       u64 requestid, u64 *trans_id)
 {
        int i;
        u32 bytes_avail_towrite;
@@ -294,7 +294,7 @@ int hv_ringbuffer_write(struct vmbus_channel *channel,
        unsigned long flags;
        struct hv_ring_buffer_info *outring_info = &channel->outbound;
        struct vmpacket_descriptor *desc = kv_list[0].iov_base;
-       u64 rqst_id = VMBUS_NO_RQSTOR;
+       u64 __trans_id, rqst_id = VMBUS_NO_RQSTOR;
 
        if (channel->rescind)
                return -ENODEV;
@@ -353,7 +353,15 @@ int hv_ringbuffer_write(struct vmbus_channel *channel,
                }
        }
        desc = hv_get_ring_buffer(outring_info) + old_write;
-       desc->trans_id = (rqst_id == VMBUS_NO_RQSTOR) ? requestid : rqst_id;
+       __trans_id = (rqst_id == VMBUS_NO_RQSTOR) ? requestid : rqst_id;
+       /*
+        * Ensure the compiler doesn't generate code that reads the value of
+        * the transaction ID from the ring buffer, which is shared with the
+        * Hyper-V host and subject to being changed at any time.
+        */
+       WRITE_ONCE(desc->trans_id, __trans_id);
+       if (trans_id)
+               *trans_id = __trans_id;
 
        /* Set previous packet start */
        prev_indices = hv_get_ring_bufferindices(outring_info);
@@ -421,7 +429,7 @@ int hv_ringbuffer_read(struct vmbus_channel *channel,
        memcpy(buffer, (const char *)desc + offset, packetlen);
 
        /* Advance ring index to next packet descriptor */
-       __hv_pkt_iter_next(channel, desc, true);
+       __hv_pkt_iter_next(channel, desc);
 
        /* Notify host of update */
        hv_pkt_iter_close(channel);
@@ -457,22 +465,6 @@ static u32 hv_pkt_iter_avail(const struct hv_ring_buffer_info *rbi)
 }
 
 /*
- * Get first vmbus packet without copying it out of the ring buffer
- */
-struct vmpacket_descriptor *hv_pkt_iter_first_raw(struct vmbus_channel *channel)
-{
-       struct hv_ring_buffer_info *rbi = &channel->inbound;
-
-       hv_debug_delay_test(channel, MESSAGE_DELAY);
-
-       if (hv_pkt_iter_avail(rbi) < sizeof(struct vmpacket_descriptor))
-               return NULL;
-
-       return (struct vmpacket_descriptor *)(hv_get_ring_buffer(rbi) + rbi->priv_read_index);
-}
-EXPORT_SYMBOL_GPL(hv_pkt_iter_first_raw);
-
-/*
  * Get first vmbus packet from ring buffer after read_index
  *
  * If ring buffer is empty, returns NULL and no other action needed.
@@ -483,11 +475,14 @@ struct vmpacket_descriptor *hv_pkt_iter_first(struct vmbus_channel *channel)
        struct vmpacket_descriptor *desc, *desc_copy;
        u32 bytes_avail, pkt_len, pkt_offset;
 
-       desc = hv_pkt_iter_first_raw(channel);
-       if (!desc)
+       hv_debug_delay_test(channel, MESSAGE_DELAY);
+
+       bytes_avail = hv_pkt_iter_avail(rbi);
+       if (bytes_avail < sizeof(struct vmpacket_descriptor))
                return NULL;
+       bytes_avail = min(rbi->pkt_buffer_size, bytes_avail);
 
-       bytes_avail = min(rbi->pkt_buffer_size, hv_pkt_iter_avail(rbi));
+       desc = (struct vmpacket_descriptor *)(hv_get_ring_buffer(rbi) + rbi->priv_read_index);
 
        /*
         * Ensure the compiler does not use references to incoming Hyper-V values (which
@@ -534,8 +529,7 @@ EXPORT_SYMBOL_GPL(hv_pkt_iter_first);
  */
 struct vmpacket_descriptor *
 __hv_pkt_iter_next(struct vmbus_channel *channel,
-                  const struct vmpacket_descriptor *desc,
-                  bool copy)
+                  const struct vmpacket_descriptor *desc)
 {
        struct hv_ring_buffer_info *rbi = &channel->inbound;
        u32 packetlen = desc->len8 << 3;
@@ -548,7 +542,7 @@ __hv_pkt_iter_next(struct vmbus_channel *channel,
                rbi->priv_read_index -= dsize;
 
        /* more data? */
-       return copy ? hv_pkt_iter_first(channel) : hv_pkt_iter_first_raw(channel);
+       return hv_pkt_iter_first(channel);
 }
 EXPORT_SYMBOL_GPL(__hv_pkt_iter_next);
 
index 14de170..9c1b362 100644 (file)
@@ -1263,23 +1263,17 @@ static void vmbus_chan_sched(struct hv_per_cpu_context *hv_cpu)
        unsigned long *recv_int_page;
        u32 maxbits, relid;
 
-       if (vmbus_proto_version < VERSION_WIN8) {
-               maxbits = MAX_NUM_CHANNELS_SUPPORTED;
-               recv_int_page = vmbus_connection.recv_int_page;
-       } else {
-               /*
-                * When the host is win8 and beyond, the event page
-                * can be directly checked to get the id of the channel
-                * that has the interrupt pending.
-                */
-               void *page_addr = hv_cpu->synic_event_page;
-               union hv_synic_event_flags *event
-                       = (union hv_synic_event_flags *)page_addr +
-                                                VMBUS_MESSAGE_SINT;
+       /*
+        * The event page can be directly checked to get the id of
+        * the channel that has the interrupt pending.
+        */
+       void *page_addr = hv_cpu->synic_event_page;
+       union hv_synic_event_flags *event
+               = (union hv_synic_event_flags *)page_addr +
+                                        VMBUS_MESSAGE_SINT;
 
-               maxbits = HV_EVENT_FLAGS_COUNT;
-               recv_int_page = event->flags;
-       }
+       maxbits = HV_EVENT_FLAGS_COUNT;
+       recv_int_page = event->flags;
 
        if (unlikely(!recv_int_page))
                return;
@@ -1351,40 +1345,10 @@ static void vmbus_isr(void)
 {
        struct hv_per_cpu_context *hv_cpu
                = this_cpu_ptr(hv_context.cpu_context);
-       void *page_addr = hv_cpu->synic_event_page;
+       void *page_addr;
        struct hv_message *msg;
-       union hv_synic_event_flags *event;
-       bool handled = false;
-
-       if (unlikely(page_addr == NULL))
-               return;
-
-       event = (union hv_synic_event_flags *)page_addr +
-                                        VMBUS_MESSAGE_SINT;
-       /*
-        * Check for events before checking for messages. This is the order
-        * in which events and messages are checked in Windows guests on
-        * Hyper-V, and the Windows team suggested we do the same.
-        */
-
-       if ((vmbus_proto_version == VERSION_WS2008) ||
-               (vmbus_proto_version == VERSION_WIN7)) {
-
-               /* Since we are a child, we only need to check bit 0 */
-               if (sync_test_and_clear_bit(0, event->flags))
-                       handled = true;
-       } else {
-               /*
-                * Our host is win8 or above. The signaling mechanism
-                * has changed and we can directly look at the event page.
-                * If bit n is set then we have an interrup on the channel
-                * whose id is n.
-                */
-               handled = true;
-       }
 
-       if (handled)
-               vmbus_chan_sched(hv_cpu);
+       vmbus_chan_sched(hv_cpu);
 
        page_addr = hv_cpu->synic_message_page;
        msg = (struct hv_message *)page_addr + VMBUS_MESSAGE_SINT;
index d270a20..db814f7 100644 (file)
@@ -92,6 +92,13 @@ static enum pci_protocol_version_t pci_protocol_versions[] = {
 #define SLOT_NAME_SIZE 11
 
 /*
+ * Size of requestor for VMbus; the value is based on the observation
+ * that having more than one request outstanding is 'rare', and so 64
+ * should be generous in ensuring that we don't ever run out.
+ */
+#define HV_PCI_RQSTOR_SIZE 64
+
+/*
  * Message Types
  */
 
@@ -604,17 +611,19 @@ static unsigned int hv_msi_get_int_vector(struct irq_data *data)
        return cfg->vector;
 }
 
-static void hv_set_msi_entry_from_desc(union hv_msi_entry *msi_entry,
-                                      struct msi_desc *msi_desc)
-{
-       msi_entry->address.as_uint32 = msi_desc->msg.address_lo;
-       msi_entry->data.as_uint32 = msi_desc->msg.data;
-}
-
 static int hv_msi_prepare(struct irq_domain *domain, struct device *dev,
                          int nvec, msi_alloc_info_t *info)
 {
-       return pci_msi_prepare(domain, dev, nvec, info);
+       int ret = pci_msi_prepare(domain, dev, nvec, info);
+
+       /*
+        * By using the interrupt remapper in the hypervisor IOMMU, contiguous
+        * CPU vectors is not needed for multi-MSI
+        */
+       if (info->type == X86_IRQ_ALLOC_TYPE_PCI_MSI)
+               info->flags &= ~X86_IRQ_ALLOC_CONTIGUOUS_VECTORS;
+
+       return ret;
 }
 
 /**
@@ -631,6 +640,7 @@ static void hv_arch_irq_unmask(struct irq_data *data)
 {
        struct msi_desc *msi_desc = irq_data_get_msi_desc(data);
        struct hv_retarget_device_interrupt *params;
+       struct tran_int_desc *int_desc;
        struct hv_pcibus_device *hbus;
        struct cpumask *dest;
        cpumask_var_t tmp;
@@ -645,6 +655,7 @@ static void hv_arch_irq_unmask(struct irq_data *data)
        pdev = msi_desc_to_pci_dev(msi_desc);
        pbus = pdev->bus;
        hbus = container_of(pbus->sysdata, struct hv_pcibus_device, sysdata);
+       int_desc = data->chip_data;
 
        spin_lock_irqsave(&hbus->retarget_msi_interrupt_lock, flags);
 
@@ -652,7 +663,8 @@ static void hv_arch_irq_unmask(struct irq_data *data)
        memset(params, 0, sizeof(*params));
        params->partition_id = HV_PARTITION_ID_SELF;
        params->int_entry.source = HV_INTERRUPT_SOURCE_MSI;
-       hv_set_msi_entry_from_desc(&params->int_entry.msi_entry, msi_desc);
+       params->int_entry.msi_entry.address.as_uint32 = int_desc->address & 0xffffffff;
+       params->int_entry.msi_entry.data.as_uint32 = int_desc->data;
        params->device_id = (hbus->hdev->dev_instance.b[5] << 24) |
                           (hbus->hdev->dev_instance.b[4] << 16) |
                           (hbus->hdev->dev_instance.b[7] << 8) |
@@ -969,11 +981,7 @@ static void hv_pci_generic_compl(void *context, struct pci_response *resp,
 {
        struct hv_pci_compl *comp_pkt = context;
 
-       if (resp_packet_size >= offsetofend(struct pci_response, status))
-               comp_pkt->completion_status = resp->status;
-       else
-               comp_pkt->completion_status = -1;
-
+       comp_pkt->completion_status = resp->status;
        complete(&comp_pkt->host_event);
 }
 
@@ -1513,6 +1521,10 @@ static void hv_int_desc_free(struct hv_pci_dev *hpdev,
                u8 buffer[sizeof(struct pci_delete_interrupt)];
        } ctxt;
 
+       if (!int_desc->vector_count) {
+               kfree(int_desc);
+               return;
+       }
        memset(&ctxt, 0, sizeof(ctxt));
        int_pkt = (struct pci_delete_interrupt *)&ctxt.pkt.message;
        int_pkt->message_type.type =
@@ -1520,7 +1532,7 @@ static void hv_int_desc_free(struct hv_pci_dev *hpdev,
        int_pkt->wslot.slot = hpdev->desc.win_slot.slot;
        int_pkt->int_desc = *int_desc;
        vmbus_sendpacket(hpdev->hbus->hdev->channel, int_pkt, sizeof(*int_pkt),
-                        (unsigned long)&ctxt.pkt, VM_PKT_DATA_INBAND, 0);
+                        0, VM_PKT_DATA_INBAND, 0);
        kfree(int_desc);
 }
 
@@ -1590,19 +1602,24 @@ static void hv_pci_compose_compl(void *context, struct pci_response *resp,
        struct pci_create_int_response *int_resp =
                (struct pci_create_int_response *)resp;
 
+       if (resp_packet_size < sizeof(*int_resp)) {
+               comp_pkt->comp_pkt.completion_status = -1;
+               goto out;
+       }
        comp_pkt->comp_pkt.completion_status = resp->status;
        comp_pkt->int_desc = int_resp->int_desc;
+out:
        complete(&comp_pkt->comp_pkt.host_event);
 }
 
 static u32 hv_compose_msi_req_v1(
        struct pci_create_interrupt *int_pkt, struct cpumask *affinity,
-       u32 slot, u8 vector)
+       u32 slot, u8 vector, u8 vector_count)
 {
        int_pkt->message_type.type = PCI_CREATE_INTERRUPT_MESSAGE;
        int_pkt->wslot.slot = slot;
        int_pkt->int_desc.vector = vector;
-       int_pkt->int_desc.vector_count = 1;
+       int_pkt->int_desc.vector_count = vector_count;
        int_pkt->int_desc.delivery_mode = DELIVERY_MODE;
 
        /*
@@ -1625,14 +1642,14 @@ static int hv_compose_msi_req_get_cpu(struct cpumask *affinity)
 
 static u32 hv_compose_msi_req_v2(
        struct pci_create_interrupt2 *int_pkt, struct cpumask *affinity,
-       u32 slot, u8 vector)
+       u32 slot, u8 vector, u8 vector_count)
 {
        int cpu;
 
        int_pkt->message_type.type = PCI_CREATE_INTERRUPT_MESSAGE2;
        int_pkt->wslot.slot = slot;
        int_pkt->int_desc.vector = vector;
-       int_pkt->int_desc.vector_count = 1;
+       int_pkt->int_desc.vector_count = vector_count;
        int_pkt->int_desc.delivery_mode = DELIVERY_MODE;
        cpu = hv_compose_msi_req_get_cpu(affinity);
        int_pkt->int_desc.processor_array[0] =
@@ -1644,7 +1661,7 @@ static u32 hv_compose_msi_req_v2(
 
 static u32 hv_compose_msi_req_v3(
        struct pci_create_interrupt3 *int_pkt, struct cpumask *affinity,
-       u32 slot, u32 vector)
+       u32 slot, u32 vector, u8 vector_count)
 {
        int cpu;
 
@@ -1652,7 +1669,7 @@ static u32 hv_compose_msi_req_v3(
        int_pkt->wslot.slot = slot;
        int_pkt->int_desc.vector = vector;
        int_pkt->int_desc.reserved = 0;
-       int_pkt->int_desc.vector_count = 1;
+       int_pkt->int_desc.vector_count = vector_count;
        int_pkt->int_desc.delivery_mode = DELIVERY_MODE;
        cpu = hv_compose_msi_req_get_cpu(affinity);
        int_pkt->int_desc.processor_array[0] =
@@ -1683,6 +1700,8 @@ static void hv_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
        struct cpumask *dest;
        struct compose_comp_ctxt comp;
        struct tran_int_desc *int_desc;
+       struct msi_desc *msi_desc;
+       u8 vector, vector_count;
        struct {
                struct pci_packet pci_pkt;
                union {
@@ -1691,11 +1710,21 @@ static void hv_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
                        struct pci_create_interrupt3 v3;
                } int_pkts;
        } __packed ctxt;
-
+       u64 trans_id;
        u32 size;
        int ret;
 
-       pdev = msi_desc_to_pci_dev(irq_data_get_msi_desc(data));
+       /* Reuse the previous allocation */
+       if (data->chip_data) {
+               int_desc = data->chip_data;
+               msg->address_hi = int_desc->address >> 32;
+               msg->address_lo = int_desc->address & 0xffffffff;
+               msg->data = int_desc->data;
+               return;
+       }
+
+       msi_desc  = irq_data_get_msi_desc(data);
+       pdev = msi_desc_to_pci_dev(msi_desc);
        dest = irq_data_get_effective_affinity_mask(data);
        pbus = pdev->bus;
        hbus = container_of(pbus->sysdata, struct hv_pcibus_device, sysdata);
@@ -1704,17 +1733,40 @@ static void hv_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
        if (!hpdev)
                goto return_null_message;
 
-       /* Free any previous message that might have already been composed. */
-       if (data->chip_data) {
-               int_desc = data->chip_data;
-               data->chip_data = NULL;
-               hv_int_desc_free(hpdev, int_desc);
-       }
-
        int_desc = kzalloc(sizeof(*int_desc), GFP_ATOMIC);
        if (!int_desc)
                goto drop_reference;
 
+       if (!msi_desc->pci.msi_attrib.is_msix && msi_desc->nvec_used > 1) {
+               /*
+                * If this is not the first MSI of Multi MSI, we already have
+                * a mapping.  Can exit early.
+                */
+               if (msi_desc->irq != data->irq) {
+                       data->chip_data = int_desc;
+                       int_desc->address = msi_desc->msg.address_lo |
+                                           (u64)msi_desc->msg.address_hi << 32;
+                       int_desc->data = msi_desc->msg.data +
+                                        (data->irq - msi_desc->irq);
+                       msg->address_hi = msi_desc->msg.address_hi;
+                       msg->address_lo = msi_desc->msg.address_lo;
+                       msg->data = int_desc->data;
+                       put_pcichild(hpdev);
+                       return;
+               }
+               /*
+                * The vector we select here is a dummy value.  The correct
+                * value gets sent to the hypervisor in unmask().  This needs
+                * to be aligned with the count, and also not zero.  Multi-msi
+                * is powers of 2 up to 32, so 32 will always work here.
+                */
+               vector = 32;
+               vector_count = msi_desc->nvec_used;
+       } else {
+               vector = hv_msi_get_int_vector(data);
+               vector_count = 1;
+       }
+
        memset(&ctxt, 0, sizeof(ctxt));
        init_completion(&comp.comp_pkt.host_event);
        ctxt.pci_pkt.completion_func = hv_pci_compose_compl;
@@ -1725,7 +1777,8 @@ static void hv_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
                size = hv_compose_msi_req_v1(&ctxt.int_pkts.v1,
                                        dest,
                                        hpdev->desc.win_slot.slot,
-                                       hv_msi_get_int_vector(data));
+                                       vector,
+                                       vector_count);
                break;
 
        case PCI_PROTOCOL_VERSION_1_2:
@@ -1733,14 +1786,16 @@ static void hv_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
                size = hv_compose_msi_req_v2(&ctxt.int_pkts.v2,
                                        dest,
                                        hpdev->desc.win_slot.slot,
-                                       hv_msi_get_int_vector(data));
+                                       vector,
+                                       vector_count);
                break;
 
        case PCI_PROTOCOL_VERSION_1_4:
                size = hv_compose_msi_req_v3(&ctxt.int_pkts.v3,
                                        dest,
                                        hpdev->desc.win_slot.slot,
-                                       hv_msi_get_int_vector(data));
+                                       vector,
+                                       vector_count);
                break;
 
        default:
@@ -1753,10 +1808,10 @@ static void hv_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
                goto free_int_desc;
        }
 
-       ret = vmbus_sendpacket(hpdev->hbus->hdev->channel, &ctxt.int_pkts,
-                              size, (unsigned long)&ctxt.pci_pkt,
-                              VM_PKT_DATA_INBAND,
-                              VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
+       ret = vmbus_sendpacket_getid(hpdev->hbus->hdev->channel, &ctxt.int_pkts,
+                                    size, (unsigned long)&ctxt.pci_pkt,
+                                    &trans_id, VM_PKT_DATA_INBAND,
+                                    VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
        if (ret) {
                dev_err(&hbus->hdev->device,
                        "Sending request for interrupt failed: 0x%x",
@@ -1835,6 +1890,15 @@ static void hv_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
 
 enable_tasklet:
        tasklet_enable(&channel->callback_event);
+       /*
+        * The completion packet on the stack becomes invalid after 'return';
+        * remove the ID from the VMbus requestor if the identifier is still
+        * mapped to/associated with the packet.  (The identifier could have
+        * been 're-used', i.e., already removed and (re-)mapped.)
+        *
+        * Cf. hv_pci_onchannelcallback().
+        */
+       vmbus_request_addr_match(channel, trans_id, (unsigned long)&ctxt.pci_pkt);
 free_int_desc:
        kfree(int_desc);
 drop_reference:
@@ -2082,12 +2146,17 @@ static void prepopulate_bars(struct hv_pcibus_device *hbus)
                                }
                        }
                        if (high_size <= 1 && low_size <= 1) {
-                               /* Set the memory enable bit. */
-                               _hv_pcifront_read_config(hpdev, PCI_COMMAND, 2,
-                                                        &command);
-                               command |= PCI_COMMAND_MEMORY;
-                               _hv_pcifront_write_config(hpdev, PCI_COMMAND, 2,
-                                                         command);
+                               /*
+                                * No need to set the PCI_COMMAND_MEMORY bit as
+                                * the core PCI driver doesn't require the bit
+                                * to be pre-set. Actually here we intentionally
+                                * keep the bit off so that the PCI BAR probing
+                                * in the core PCI driver doesn't cause Hyper-V
+                                * to unnecessarily unmap/map the virtual BARs
+                                * from/to the physical BARs multiple times.
+                                * This reduces the VM boot time significantly
+                                * if the BAR sizes are huge.
+                                */
                                break;
                        }
                }
@@ -2223,12 +2292,14 @@ static void q_resource_requirements(void *context, struct pci_response *resp,
        struct q_res_req_compl *completion = context;
        struct pci_q_res_req_response *q_res_req =
                (struct pci_q_res_req_response *)resp;
+       s32 status;
        int i;
 
-       if (resp->status < 0) {
+       status = (resp_packet_size < sizeof(*q_res_req)) ? -1 : resp->status;
+       if (status < 0) {
                dev_err(&completion->hpdev->hbus->hdev->device,
                        "query resource requirements failed: %x\n",
-                       resp->status);
+                       status);
        } else {
                for (i = 0; i < PCI_STD_NUM_BARS; i++) {
                        completion->hpdev->probed_bar[i] =
@@ -2652,7 +2723,7 @@ static void hv_eject_device_work(struct work_struct *work)
        ejct_pkt->message_type.type = PCI_EJECTION_COMPLETE;
        ejct_pkt->wslot.slot = hpdev->desc.win_slot.slot;
        vmbus_sendpacket(hbus->hdev->channel, ejct_pkt,
-                        sizeof(*ejct_pkt), (unsigned long)&ctxt.pkt,
+                        sizeof(*ejct_pkt), 0,
                         VM_PKT_DATA_INBAND, 0);
 
        /* For the get_pcichild() in hv_pci_eject_device() */
@@ -2699,8 +2770,9 @@ static void hv_pci_onchannelcallback(void *context)
        const int packet_size = 0x100;
        int ret;
        struct hv_pcibus_device *hbus = context;
+       struct vmbus_channel *chan = hbus->hdev->channel;
        u32 bytes_recvd;
-       u64 req_id;
+       u64 req_id, req_addr;
        struct vmpacket_descriptor *desc;
        unsigned char *buffer;
        int bufferlen = packet_size;
@@ -2712,14 +2784,15 @@ static void hv_pci_onchannelcallback(void *context)
        struct pci_dev_inval_block *inval;
        struct pci_dev_incoming *dev_message;
        struct hv_pci_dev *hpdev;
+       unsigned long flags;
 
        buffer = kmalloc(bufferlen, GFP_ATOMIC);
        if (!buffer)
                return;
 
        while (1) {
-               ret = vmbus_recvpacket_raw(hbus->hdev->channel, buffer,
-                                          bufferlen, &bytes_recvd, &req_id);
+               ret = vmbus_recvpacket_raw(chan, buffer, bufferlen,
+                                          &bytes_recvd, &req_id);
 
                if (ret == -ENOBUFS) {
                        kfree(buffer);
@@ -2746,15 +2819,29 @@ static void hv_pci_onchannelcallback(void *context)
                switch (desc->type) {
                case VM_PKT_COMP:
 
+                       lock_requestor(chan, flags);
+                       req_addr = __vmbus_request_addr_match(chan, req_id,
+                                                             VMBUS_RQST_ADDR_ANY);
+                       if (req_addr == VMBUS_RQST_ERROR) {
+                               unlock_requestor(chan, flags);
+                               dev_err(&hbus->hdev->device,
+                                       "Invalid transaction ID %llx\n",
+                                       req_id);
+                               break;
+                       }
+                       comp_packet = (struct pci_packet *)req_addr;
+                       response = (struct pci_response *)buffer;
                        /*
-                        * The host is trusted, and thus it's safe to interpret
-                        * this transaction ID as a pointer.
+                        * Call ->completion_func() within the critical section to make
+                        * sure that the packet pointer is still valid during the call:
+                        * here 'valid' means that there's a task still waiting for the
+                        * completion, and that the packet data is still on the waiting
+                        * task's stack.  Cf. hv_compose_msi_msg().
                         */
-                       comp_packet = (struct pci_packet *)req_id;
-                       response = (struct pci_response *)buffer;
                        comp_packet->completion_func(comp_packet->compl_ctxt,
                                                     response,
                                                     bytes_recvd);
+                       unlock_requestor(chan, flags);
                        break;
 
                case VM_PKT_DATA_INBAND:
@@ -2764,7 +2851,8 @@ static void hv_pci_onchannelcallback(void *context)
                        case PCI_BUS_RELATIONS:
 
                                bus_rel = (struct pci_bus_relations *)buffer;
-                               if (bytes_recvd <
+                               if (bytes_recvd < sizeof(*bus_rel) ||
+                                   bytes_recvd <
                                        struct_size(bus_rel, func,
                                                    bus_rel->device_count)) {
                                        dev_err(&hbus->hdev->device,
@@ -2778,7 +2866,8 @@ static void hv_pci_onchannelcallback(void *context)
                        case PCI_BUS_RELATIONS2:
 
                                bus_rel2 = (struct pci_bus_relations2 *)buffer;
-                               if (bytes_recvd <
+                               if (bytes_recvd < sizeof(*bus_rel2) ||
+                                   bytes_recvd <
                                        struct_size(bus_rel2, func,
                                                    bus_rel2->device_count)) {
                                        dev_err(&hbus->hdev->device,
@@ -2792,6 +2881,11 @@ static void hv_pci_onchannelcallback(void *context)
                        case PCI_EJECT:
 
                                dev_message = (struct pci_dev_incoming *)buffer;
+                               if (bytes_recvd < sizeof(*dev_message)) {
+                                       dev_err(&hbus->hdev->device,
+                                               "eject message too small\n");
+                                       break;
+                               }
                                hpdev = get_pcichild_wslot(hbus,
                                                      dev_message->wslot.slot);
                                if (hpdev) {
@@ -2803,6 +2897,11 @@ static void hv_pci_onchannelcallback(void *context)
                        case PCI_INVALIDATE_BLOCK:
 
                                inval = (struct pci_dev_inval_block *)buffer;
+                               if (bytes_recvd < sizeof(*inval)) {
+                                       dev_err(&hbus->hdev->device,
+                                               "invalidate message too small\n");
+                                       break;
+                               }
                                hpdev = get_pcichild_wslot(hbus,
                                                           inval->wslot.slot);
                                if (hpdev) {
@@ -3431,6 +3530,10 @@ static int hv_pci_probe(struct hv_device *hdev,
                goto free_dom;
        }
 
+       hdev->channel->next_request_id_callback = vmbus_next_request_id;
+       hdev->channel->request_addr_callback = vmbus_request_addr;
+       hdev->channel->rqstor_size = HV_PCI_RQSTOR_SIZE;
+
        ret = vmbus_open(hdev->channel, pci_ring_size, pci_ring_size, NULL, 0,
                         hv_pci_onchannelcallback, hbus);
        if (ret)
@@ -3561,6 +3664,7 @@ free_bus:
 static int hv_pci_bus_exit(struct hv_device *hdev, bool keep_devs)
 {
        struct hv_pcibus_device *hbus = hv_get_drvdata(hdev);
+       struct vmbus_channel *chan = hdev->channel;
        struct {
                struct pci_packet teardown_packet;
                u8 buffer[sizeof(struct pci_message)];
@@ -3568,13 +3672,14 @@ static int hv_pci_bus_exit(struct hv_device *hdev, bool keep_devs)
        struct hv_pci_compl comp_pkt;
        struct hv_pci_dev *hpdev, *tmp;
        unsigned long flags;
+       u64 trans_id;
        int ret;
 
        /*
         * After the host sends the RESCIND_CHANNEL message, it doesn't
         * access the per-channel ringbuffer any longer.
         */
-       if (hdev->channel->rescind)
+       if (chan->rescind)
                return 0;
 
        if (!keep_devs) {
@@ -3611,16 +3716,26 @@ static int hv_pci_bus_exit(struct hv_device *hdev, bool keep_devs)
        pkt.teardown_packet.compl_ctxt = &comp_pkt;
        pkt.teardown_packet.message[0].type = PCI_BUS_D0EXIT;
 
-       ret = vmbus_sendpacket(hdev->channel, &pkt.teardown_packet.message,
-                              sizeof(struct pci_message),
-                              (unsigned long)&pkt.teardown_packet,
-                              VM_PKT_DATA_INBAND,
-                              VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
+       ret = vmbus_sendpacket_getid(chan, &pkt.teardown_packet.message,
+                                    sizeof(struct pci_message),
+                                    (unsigned long)&pkt.teardown_packet,
+                                    &trans_id, VM_PKT_DATA_INBAND,
+                                    VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
        if (ret)
                return ret;
 
-       if (wait_for_completion_timeout(&comp_pkt.host_event, 10 * HZ) == 0)
+       if (wait_for_completion_timeout(&comp_pkt.host_event, 10 * HZ) == 0) {
+               /*
+                * The completion packet on the stack becomes invalid after
+                * 'return'; remove the ID from the VMbus requestor if the
+                * identifier is still mapped to/associated with the packet.
+                *
+                * Cf. hv_pci_onchannelcallback().
+                */
+               vmbus_request_addr_match(chan, trans_id,
+                                        (unsigned long)&pkt.teardown_packet);
                return -ETIMEDOUT;
+       }
 
        return 0;
 }
@@ -3761,6 +3876,10 @@ static int hv_pci_resume(struct hv_device *hdev)
 
        hbus->state = hv_pcibus_init;
 
+       hdev->channel->next_request_id_callback = vmbus_next_request_id;
+       hdev->channel->request_addr_callback = vmbus_request_addr;
+       hdev->channel->rqstor_size = HV_PCI_RQSTOR_SIZE;
+
        ret = vmbus_open(hdev->channel, pci_ring_size, pci_ring_size, NULL, 0,
                         hv_pci_onchannelcallback, hbus);
        if (ret)
index 9a0bba5..08ed059 100644 (file)
@@ -54,7 +54,6 @@
 
 #define VMSTOR_PROTO_VERSION(MAJOR_, MINOR_)   ((((MAJOR_) & 0xff) << 8) | \
                                                (((MINOR_) & 0xff)))
-
 #define VMSTOR_PROTO_VERSION_WIN6      VMSTOR_PROTO_VERSION(2, 0)
 #define VMSTOR_PROTO_VERSION_WIN7      VMSTOR_PROTO_VERSION(4, 2)
 #define VMSTOR_PROTO_VERSION_WIN8      VMSTOR_PROTO_VERSION(5, 1)
@@ -136,21 +135,11 @@ struct hv_fc_wwn_packet {
  */
 #define STORVSC_MAX_CMD_LEN                    0x10
 
-#define POST_WIN7_STORVSC_SENSE_BUFFER_SIZE    0x14
-#define PRE_WIN8_STORVSC_SENSE_BUFFER_SIZE     0x12
-
+/* Sense buffer size is the same for all versions since Windows 8 */
 #define STORVSC_SENSE_BUFFER_SIZE              0x14
 #define STORVSC_MAX_BUF_LEN_WITH_PADDING       0x14
 
 /*
- * Sense buffer size changed in win8; have a run-time
- * variable to track the size we should use.  This value will
- * likely change during protocol negotiation but it is valid
- * to start by assuming pre-Win8.
- */
-static int sense_buffer_size = PRE_WIN8_STORVSC_SENSE_BUFFER_SIZE;
-
-/*
  * The storage protocol version is determined during the
  * initial exchange with the host.  It will indicate which
  * storage functionality is available in the host.
@@ -177,18 +166,6 @@ do {                                                               \
                dev_warn(&(dev)->device, fmt, ##__VA_ARGS__);   \
 } while (0)
 
-struct vmscsi_win8_extension {
-       /*
-        * The following were added in Windows 8
-        */
-       u16 reserve;
-       u8  queue_tag;
-       u8  queue_action;
-       u32 srb_flags;
-       u32 time_out_value;
-       u32 queue_sort_ey;
-} __packed;
-
 struct vmscsi_request {
        u16 length;
        u8 srb_status;
@@ -214,46 +191,23 @@ struct vmscsi_request {
        /*
         * The following was added in win8.
         */
-       struct vmscsi_win8_extension win8_extension;
+       u16 reserve;
+       u8  queue_tag;
+       u8  queue_action;
+       u32 srb_flags;
+       u32 time_out_value;
+       u32 queue_sort_ey;
 
 } __attribute((packed));
 
 /*
- * The list of storage protocols in order of preference.
+ * The list of windows version in order of preference.
  */
-struct vmstor_protocol {
-       int protocol_version;
-       int sense_buffer_size;
-       int vmscsi_size_delta;
-};
-
 
-static const struct vmstor_protocol vmstor_protocols[] = {
-       {
+static const int protocol_version[] = {
                VMSTOR_PROTO_VERSION_WIN10,
-               POST_WIN7_STORVSC_SENSE_BUFFER_SIZE,
-               0
-       },
-       {
                VMSTOR_PROTO_VERSION_WIN8_1,
-               POST_WIN7_STORVSC_SENSE_BUFFER_SIZE,
-               0
-       },
-       {
                VMSTOR_PROTO_VERSION_WIN8,
-               POST_WIN7_STORVSC_SENSE_BUFFER_SIZE,
-               0
-       },
-       {
-               VMSTOR_PROTO_VERSION_WIN7,
-               PRE_WIN8_STORVSC_SENSE_BUFFER_SIZE,
-               sizeof(struct vmscsi_win8_extension),
-       },
-       {
-               VMSTOR_PROTO_VERSION_WIN6,
-               PRE_WIN8_STORVSC_SENSE_BUFFER_SIZE,
-               sizeof(struct vmscsi_win8_extension),
-       }
 };
 
 
@@ -409,9 +363,7 @@ static void storvsc_on_channel_callback(void *context);
 #define STORVSC_IDE_MAX_CHANNELS                       1
 
 /*
- * Upper bound on the size of a storvsc packet. vmscsi_size_delta is not
- * included in the calculation because it is set after STORVSC_MAX_PKT_SIZE
- * is used in storvsc_connect_to_vsp
+ * Upper bound on the size of a storvsc packet.
  */
 #define STORVSC_MAX_PKT_SIZE (sizeof(struct vmpacket_descriptor) +\
                              sizeof(struct vstor_packet))
@@ -453,17 +405,6 @@ struct storvsc_device {
        unsigned char target_id;
 
        /*
-        * The size of the vmscsi_request has changed in win8. The
-        * additional size is because of new elements added to the
-        * structure. These elements are valid only when we are talking
-        * to a win8 host.
-        * Track the correction to size we need to apply. This value
-        * will likely change during protocol negotiation but it is
-        * valid to start by assuming pre-Win8.
-        */
-       int vmscsi_size_delta;
-
-       /*
         * Max I/O, the device can support.
         */
        u32   max_transfer_bytes;
@@ -795,8 +736,7 @@ static void  handle_multichannel_storage(struct hv_device *device, int max_chns)
        vstor_packet->sub_channel_count = num_sc;
 
        ret = vmbus_sendpacket(device->channel, vstor_packet,
-                              (sizeof(struct vstor_packet) -
-                              stor_device->vmscsi_size_delta),
+                              sizeof(struct vstor_packet),
                               VMBUS_RQST_INIT,
                               VM_PKT_DATA_INBAND,
                               VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
@@ -864,8 +804,7 @@ static int storvsc_execute_vstor_op(struct hv_device *device,
        vstor_packet->flags = REQUEST_COMPLETION_FLAG;
 
        ret = vmbus_sendpacket(device->channel, vstor_packet,
-                              (sizeof(struct vstor_packet) -
-                              stor_device->vmscsi_size_delta),
+                              sizeof(struct vstor_packet),
                               VMBUS_RQST_INIT,
                               VM_PKT_DATA_INBAND,
                               VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
@@ -915,14 +854,13 @@ static int storvsc_channel_init(struct hv_device *device, bool is_fc)
         * Query host supported protocol version.
         */
 
-       for (i = 0; i < ARRAY_SIZE(vmstor_protocols); i++) {
+       for (i = 0; i < ARRAY_SIZE(protocol_version); i++) {
                /* reuse the packet for version range supported */
                memset(vstor_packet, 0, sizeof(struct vstor_packet));
                vstor_packet->operation =
                        VSTOR_OPERATION_QUERY_PROTOCOL_VERSION;
 
-               vstor_packet->version.major_minor =
-                       vmstor_protocols[i].protocol_version;
+               vstor_packet->version.major_minor = protocol_version[i];
 
                /*
                 * The revision number is only used in Windows; set it to 0.
@@ -936,21 +874,16 @@ static int storvsc_channel_init(struct hv_device *device, bool is_fc)
                        return -EINVAL;
 
                if (vstor_packet->status == 0) {
-                       vmstor_proto_version =
-                               vmstor_protocols[i].protocol_version;
-
-                       sense_buffer_size =
-                               vmstor_protocols[i].sense_buffer_size;
-
-                       stor_device->vmscsi_size_delta =
-                               vmstor_protocols[i].vmscsi_size_delta;
+                       vmstor_proto_version = protocol_version[i];
 
                        break;
                }
        }
 
-       if (vstor_packet->status != 0)
+       if (vstor_packet->status != 0) {
+               dev_err(&device->device, "Obsolete Hyper-V version\n");
                return -EINVAL;
+       }
 
 
        memset(vstor_packet, 0, sizeof(struct vstor_packet));
@@ -986,11 +919,10 @@ static int storvsc_channel_init(struct hv_device *device, bool is_fc)
        cpumask_set_cpu(device->channel->target_cpu,
                        &stor_device->alloced_cpus);
 
-       if (vmstor_proto_version >= VMSTOR_PROTO_VERSION_WIN8) {
-               if (vstor_packet->storage_channel_properties.flags &
-                   STORAGE_CHANNEL_SUPPORTS_MULTI_CHANNEL)
-                       process_sub_channels = true;
-       }
+       if (vstor_packet->storage_channel_properties.flags &
+           STORAGE_CHANNEL_SUPPORTS_MULTI_CHANNEL)
+               process_sub_channels = true;
+
        stor_device->max_transfer_bytes =
                vstor_packet->storage_channel_properties.max_transfer_bytes;
 
@@ -1197,7 +1129,7 @@ static void storvsc_on_io_completion(struct storvsc_device *stor_device,
         * Copy over the sense_info_length, but limit to the known max
         * size if Hyper-V returns a bad value.
         */
-       stor_pkt->vm_srb.sense_info_length = min_t(u8, sense_buffer_size,
+       stor_pkt->vm_srb.sense_info_length = min_t(u8, STORVSC_SENSE_BUFFER_SIZE,
                vstor_packet->vm_srb.sense_info_length);
 
        if (vstor_packet->vm_srb.scsi_status != 0 ||
@@ -1289,8 +1221,8 @@ static void storvsc_on_channel_callback(void *context)
                struct storvsc_cmd_request *request = NULL;
                u32 pktlen = hv_pkt_datalen(desc);
                u64 rqst_id = desc->trans_id;
-               u32 minlen = rqst_id ? sizeof(struct vstor_packet) -
-                       stor_device->vmscsi_size_delta : sizeof(enum vstor_packet_operation);
+               u32 minlen = rqst_id ? sizeof(struct vstor_packet) :
+                       sizeof(enum vstor_packet_operation);
 
                if (pktlen < minlen) {
                        dev_err(&device->device,
@@ -1346,7 +1278,7 @@ static void storvsc_on_channel_callback(void *context)
                }
 
                memcpy(&request->vstor_packet, packet,
-                      (sizeof(struct vstor_packet) - stor_device->vmscsi_size_delta));
+                      sizeof(struct vstor_packet));
                complete(&request->wait_event);
        }
 }
@@ -1557,11 +1489,10 @@ static int storvsc_do_io(struct hv_device *device,
 found_channel:
        vstor_packet->flags |= REQUEST_COMPLETION_FLAG;
 
-       vstor_packet->vm_srb.length = (sizeof(struct vmscsi_request) -
-                                       stor_device->vmscsi_size_delta);
+       vstor_packet->vm_srb.length = sizeof(struct vmscsi_request);
 
 
-       vstor_packet->vm_srb.sense_info_length = sense_buffer_size;
+       vstor_packet->vm_srb.sense_info_length = STORVSC_SENSE_BUFFER_SIZE;
 
 
        vstor_packet->vm_srb.data_transfer_length =
@@ -1574,13 +1505,11 @@ found_channel:
                ret = vmbus_sendpacket_mpb_desc(outgoing_channel,
                                request->payload, request->payload_sz,
                                vstor_packet,
-                               (sizeof(struct vstor_packet) -
-                               stor_device->vmscsi_size_delta),
+                               sizeof(struct vstor_packet),
                                (unsigned long)request);
        } else {
                ret = vmbus_sendpacket(outgoing_channel, vstor_packet,
-                              (sizeof(struct vstor_packet) -
-                               stor_device->vmscsi_size_delta),
+                              sizeof(struct vstor_packet),
                               (unsigned long)request,
                               VM_PKT_DATA_INBAND,
                               VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
@@ -1684,8 +1613,7 @@ static int storvsc_host_reset_handler(struct scsi_cmnd *scmnd)
        vstor_packet->vm_srb.path_id = stor_device->path_id;
 
        ret = vmbus_sendpacket(device->channel, vstor_packet,
-                              (sizeof(struct vstor_packet) -
-                               stor_device->vmscsi_size_delta),
+                              sizeof(struct vstor_packet),
                               VMBUS_RQST_RESET,
                               VM_PKT_DATA_INBAND,
                               VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
@@ -1778,31 +1706,31 @@ static int storvsc_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *scmnd)
 
        memset(&cmd_request->vstor_packet, 0, sizeof(struct vstor_packet));
        vm_srb = &cmd_request->vstor_packet.vm_srb;
-       vm_srb->win8_extension.time_out_value = 60;
+       vm_srb->time_out_value = 60;
 
-       vm_srb->win8_extension.srb_flags |=
+       vm_srb->srb_flags |=
                SRB_FLAGS_DISABLE_SYNCH_TRANSFER;
 
        if (scmnd->device->tagged_supported) {
-               vm_srb->win8_extension.srb_flags |=
+               vm_srb->srb_flags |=
                (SRB_FLAGS_QUEUE_ACTION_ENABLE | SRB_FLAGS_NO_QUEUE_FREEZE);
-               vm_srb->win8_extension.queue_tag = SP_UNTAGGED;
-               vm_srb->win8_extension.queue_action = SRB_SIMPLE_TAG_REQUEST;
+               vm_srb->queue_tag = SP_UNTAGGED;
+               vm_srb->queue_action = SRB_SIMPLE_TAG_REQUEST;
        }
 
        /* Build the SRB */
        switch (scmnd->sc_data_direction) {
        case DMA_TO_DEVICE:
                vm_srb->data_in = WRITE_TYPE;
-               vm_srb->win8_extension.srb_flags |= SRB_FLAGS_DATA_OUT;
+               vm_srb->srb_flags |= SRB_FLAGS_DATA_OUT;
                break;
        case DMA_FROM_DEVICE:
                vm_srb->data_in = READ_TYPE;
-               vm_srb->win8_extension.srb_flags |= SRB_FLAGS_DATA_IN;
+               vm_srb->srb_flags |= SRB_FLAGS_DATA_IN;
                break;
        case DMA_NONE:
                vm_srb->data_in = UNKNOWN_TYPE;
-               vm_srb->win8_extension.srb_flags |= SRB_FLAGS_NO_DATA_TRANSFER;
+               vm_srb->srb_flags |= SRB_FLAGS_NO_DATA_TRANSFER;
                break;
        default:
                /*
@@ -1966,34 +1894,16 @@ static int storvsc_probe(struct hv_device *device,
        bool is_fc = ((dev_id->driver_data == SFC_GUID) ? true : false);
        int target = 0;
        struct storvsc_device *stor_device;
-       int max_luns_per_target;
-       int max_targets;
-       int max_channels;
        int max_sub_channels = 0;
 
        /*
-        * Based on the windows host we are running on,
-        * set state to properly communicate with the host.
+        * We support sub-channels for storage on SCSI and FC controllers.
+        * The number of sub-channels offerred is based on the number of
+        * VCPUs in the guest.
         */
-
-       if (vmbus_proto_version < VERSION_WIN8) {
-               max_luns_per_target = STORVSC_IDE_MAX_LUNS_PER_TARGET;
-               max_targets = STORVSC_IDE_MAX_TARGETS;
-               max_channels = STORVSC_IDE_MAX_CHANNELS;
-       } else {
-               max_luns_per_target = STORVSC_MAX_LUNS_PER_TARGET;
-               max_targets = STORVSC_MAX_TARGETS;
-               max_channels = STORVSC_MAX_CHANNELS;
-               /*
-                * On Windows8 and above, we support sub-channels for storage
-                * on SCSI and FC controllers.
-                * The number of sub-channels offerred is based on the number of
-                * VCPUs in the guest.
-                */
-               if (!dev_is_ide)
-                       max_sub_channels =
-                               (num_cpus - 1) / storvsc_vcpus_per_sub_channel;
-       }
+       if (!dev_is_ide)
+               max_sub_channels =
+                       (num_cpus - 1) / storvsc_vcpus_per_sub_channel;
 
        scsi_driver.can_queue = max_outstanding_req_per_channel *
                                (max_sub_channels + 1) *
@@ -2022,7 +1932,6 @@ static int storvsc_probe(struct hv_device *device,
        init_waitqueue_head(&stor_device->waiting_to_drain);
        stor_device->device = device;
        stor_device->host = host;
-       stor_device->vmscsi_size_delta = sizeof(struct vmscsi_win8_extension);
        spin_lock_init(&stor_device->lock);
        hv_set_drvdata(device, stor_device);
        dma_set_min_align_mask(&device->device, HV_HYP_PAGE_SIZE - 1);
@@ -2046,9 +1955,9 @@ static int storvsc_probe(struct hv_device *device,
                break;
 
        case SCSI_GUID:
-               host->max_lun = max_luns_per_target;
-               host->max_id = max_targets;
-               host->max_channel = max_channels - 1;
+               host->max_lun = STORVSC_MAX_LUNS_PER_TARGET;
+               host->max_id = STORVSC_MAX_TARGETS;
+               host->max_channel = STORVSC_MAX_CHANNELS - 1;
                break;
 
        default:
@@ -2235,10 +2144,6 @@ static int __init storvsc_drv_init(void)
         * than the ring buffer size since that page is reserved for
         * the ring buffer indices) by the max request size (which is
         * vmbus_channel_packet_multipage_buffer + struct vstor_packet + u64)
-        *
-        * The computation underestimates max_outstanding_req_per_channel
-        * for Win7 and older hosts because it does not take into account
-        * the vmscsi_size_delta correction to the max request size.
         */
        max_outstanding_req_per_channel =
                ((storvsc_ringbuffer_size - PAGE_SIZE) /
index 8359a51..18dc2fc 100644 (file)
@@ -63,6 +63,7 @@
 #define MAX_VMBUS_PKT_SIZE 0x4000
 
 #define SYNTHVID_VERSION(major, minor) ((minor) << 16 | (major))
+/* Support for VERSION_WIN7 is removed. #define is retained for reference. */
 #define SYNTHVID_VERSION_WIN7 SYNTHVID_VERSION(3, 0)
 #define SYNTHVID_VERSION_WIN8 SYNTHVID_VERSION(3, 2)
 #define SYNTHVID_VERSION_WIN10 SYNTHVID_VERSION(3, 5)
 #define SYNTHVID_VER_GET_MAJOR(ver) (ver & 0x0000ffff)
 #define SYNTHVID_VER_GET_MINOR(ver) ((ver & 0xffff0000) >> 16)
 
-#define SYNTHVID_DEPTH_WIN7 16
 #define SYNTHVID_DEPTH_WIN8 32
-
-#define SYNTHVID_FB_SIZE_WIN7 (4 * 1024 * 1024)
-#define SYNTHVID_WIDTH_MAX_WIN7 1600
-#define SYNTHVID_HEIGHT_MAX_WIN7 1200
-
 #define SYNTHVID_FB_SIZE_WIN8 (8 * 1024 * 1024)
 
 #define PCI_VENDOR_ID_MICROSOFT 0x1414
@@ -643,12 +638,6 @@ static int synthvid_connect_vsp(struct hv_device *hdev)
        case VERSION_WIN8:
        case VERSION_WIN8_1:
                ret = synthvid_negotiate_ver(hdev, SYNTHVID_VERSION_WIN8);
-               if (!ret)
-                       break;
-               fallthrough;
-       case VERSION_WS2008:
-       case VERSION_WIN7:
-               ret = synthvid_negotiate_ver(hdev, SYNTHVID_VERSION_WIN7);
                break;
        default:
                ret = synthvid_negotiate_ver(hdev, SYNTHVID_VERSION_WIN10);
@@ -660,11 +649,7 @@ static int synthvid_connect_vsp(struct hv_device *hdev)
                goto error;
        }
 
-       if (par->synthvid_version == SYNTHVID_VERSION_WIN7)
-               screen_depth = SYNTHVID_DEPTH_WIN7;
-       else
-               screen_depth = SYNTHVID_DEPTH_WIN8;
-
+       screen_depth = SYNTHVID_DEPTH_WIN8;
        if (synthvid_ver_ge(par->synthvid_version, SYNTHVID_VERSION_WIN10)) {
                ret = synthvid_get_supported_resolution(hdev);
                if (ret)
@@ -933,9 +918,7 @@ static void hvfb_get_option(struct fb_info *info)
            (synthvid_ver_ge(par->synthvid_version, SYNTHVID_VERSION_WIN10) &&
            (x * y * screen_depth / 8 > screen_fb_size)) ||
            (par->synthvid_version == SYNTHVID_VERSION_WIN8 &&
-            x * y * screen_depth / 8 > SYNTHVID_FB_SIZE_WIN8) ||
-           (par->synthvid_version == SYNTHVID_VERSION_WIN7 &&
-            (x > SYNTHVID_WIDTH_MAX_WIN7 || y > SYNTHVID_HEIGHT_MAX_WIN7))) {
+            x * y * screen_depth / 8 > SYNTHVID_FB_SIZE_WIN8)) {
                pr_err("Screen resolution option is out of range: skipped\n");
                return;
        }
index fe2e017..a246429 100644 (file)
@@ -230,15 +230,19 @@ static inline u32 hv_get_avail_to_write_percent(
  * two 16 bit quantities: major_number. minor_number.
  *
  * 0 . 13 (Windows Server 2008)
- * 1 . 1  (Windows 7)
- * 2 . 4  (Windows 8)
- * 3 . 0  (Windows 8 R2)
+ * 1 . 1  (Windows 7, WS2008 R2)
+ * 2 . 4  (Windows 8, WS2012)
+ * 3 . 0  (Windows 8.1, WS2012 R2)
  * 4 . 0  (Windows 10)
  * 4 . 1  (Windows 10 RS3)
  * 5 . 0  (Newer Windows 10)
  * 5 . 1  (Windows 10 RS4)
  * 5 . 2  (Windows Server 2019, RS5)
  * 5 . 3  (Windows Server 2022)
+ *
+ * The WS2008 and WIN7 versions are listed here for
+ * completeness but are no longer supported in the
+ * Linux kernel.
  */
 
 #define VERSION_WS2008  ((0 << 16) | (13))
@@ -788,6 +792,7 @@ struct vmbus_requestor {
 
 #define VMBUS_NO_RQSTOR U64_MAX
 #define VMBUS_RQST_ERROR (U64_MAX - 1)
+#define VMBUS_RQST_ADDR_ANY U64_MAX
 /* NetVSC-specific */
 #define VMBUS_RQST_ID_NO_RESPONSE (U64_MAX - 2)
 /* StorVSC-specific */
@@ -1041,13 +1046,36 @@ struct vmbus_channel {
        u32 max_pkt_size;
 };
 
+#define lock_requestor(channel, flags)                                 \
+do {                                                                   \
+       struct vmbus_requestor *rqstor = &(channel)->requestor;         \
+                                                                       \
+       spin_lock_irqsave(&rqstor->req_lock, flags);                    \
+} while (0)
+
+static __always_inline void unlock_requestor(struct vmbus_channel *channel,
+                                            unsigned long flags)
+{
+       struct vmbus_requestor *rqstor = &channel->requestor;
+
+       spin_unlock_irqrestore(&rqstor->req_lock, flags);
+}
+
 u64 vmbus_next_request_id(struct vmbus_channel *channel, u64 rqst_addr);
+u64 __vmbus_request_addr_match(struct vmbus_channel *channel, u64 trans_id,
+                              u64 rqst_addr);
+u64 vmbus_request_addr_match(struct vmbus_channel *channel, u64 trans_id,
+                            u64 rqst_addr);
 u64 vmbus_request_addr(struct vmbus_channel *channel, u64 trans_id);
 
+static inline bool is_hvsock_offer(const struct vmbus_channel_offer_channel *o)
+{
+       return !!(o->offer.chn_flags & VMBUS_CHANNEL_TLNPI_PROVIDER_OFFER);
+}
+
 static inline bool is_hvsock_channel(const struct vmbus_channel *c)
 {
-       return !!(c->offermsg.offer.chn_flags &
-                 VMBUS_CHANNEL_TLNPI_PROVIDER_OFFER);
+       return is_hvsock_offer(&c->offermsg);
 }
 
 static inline bool is_sub_channel(const struct vmbus_channel *c)
@@ -1161,6 +1189,13 @@ extern int vmbus_open(struct vmbus_channel *channel,
 
 extern void vmbus_close(struct vmbus_channel *channel);
 
+extern int vmbus_sendpacket_getid(struct vmbus_channel *channel,
+                                 void *buffer,
+                                 u32 bufferLen,
+                                 u64 requestid,
+                                 u64 *trans_id,
+                                 enum vmbus_packet_type type,
+                                 u32 flags);
 extern int vmbus_sendpacket(struct vmbus_channel *channel,
                                  void *buffer,
                                  u32 bufferLen,
@@ -1451,12 +1486,14 @@ void vmbus_free_mmio(resource_size_t start, resource_size_t size);
                          0x80, 0x2e, 0x27, 0xed, 0xe1, 0x9f)
 
 /*
- * Linux doesn't support the 3 devices: the first two are for
- * Automatic Virtual Machine Activation, and the third is for
- * Remote Desktop Virtualization.
+ * Linux doesn't support these 4 devices: the first two are for
+ * Automatic Virtual Machine Activation, the third is for
+ * Remote Desktop Virtualization, and the fourth is Initial
+ * Machine Configuration (IMC) used only by Windows guests.
  * {f8e65716-3cb3-4a06-9a60-1889c5cccab5}
  * {3375baf4-9e15-4b30-b765-67acb10d607b}
  * {276aacf4-ac15-426c-98dd-7521ad3f01fe}
+ * {c376c1c3-d276-48d2-90a9-c04748072c60}
  */
 
 #define HV_AVMA1_GUID \
@@ -1471,6 +1508,10 @@ void vmbus_free_mmio(resource_size_t start, resource_size_t size);
        .guid = GUID_INIT(0x276aacf4, 0xac15, 0x426c, 0x98, 0xdd, \
                          0x75, 0x21, 0xad, 0x3f, 0x01, 0xfe)
 
+#define HV_IMC_GUID \
+       .guid = GUID_INIT(0xc376c1c3, 0xd276, 0x48d2, 0x90, 0xa9, \
+                         0xc0, 0x47, 0x48, 0x07, 0x2c, 0x60)
+
 /*
  * Common header for Hyper-V ICs
  */
@@ -1663,56 +1704,34 @@ static inline u32 hv_pkt_datalen(const struct vmpacket_descriptor *desc)
        return (desc->len8 << 3) - (desc->offset8 << 3);
 }
 
-
-struct vmpacket_descriptor *
-hv_pkt_iter_first_raw(struct vmbus_channel *channel);
+/* Get packet length associated with descriptor */
+static inline u32 hv_pkt_len(const struct vmpacket_descriptor *desc)
+{
+       return desc->len8 << 3;
+}
 
 struct vmpacket_descriptor *
 hv_pkt_iter_first(struct vmbus_channel *channel);
 
 struct vmpacket_descriptor *
 __hv_pkt_iter_next(struct vmbus_channel *channel,
-                  const struct vmpacket_descriptor *pkt,
-                  bool copy);
+                  const struct vmpacket_descriptor *pkt);
 
 void hv_pkt_iter_close(struct vmbus_channel *channel);
 
 static inline struct vmpacket_descriptor *
-hv_pkt_iter_next_pkt(struct vmbus_channel *channel,
-                    const struct vmpacket_descriptor *pkt,
-                    bool copy)
+hv_pkt_iter_next(struct vmbus_channel *channel,
+                const struct vmpacket_descriptor *pkt)
 {
        struct vmpacket_descriptor *nxt;
 
-       nxt = __hv_pkt_iter_next(channel, pkt, copy);
+       nxt = __hv_pkt_iter_next(channel, pkt);
        if (!nxt)
                hv_pkt_iter_close(channel);
 
        return nxt;
 }
 
-/*
- * Get next packet descriptor without copying it out of the ring buffer
- * If at end of list, return NULL and update host.
- */
-static inline struct vmpacket_descriptor *
-hv_pkt_iter_next_raw(struct vmbus_channel *channel,
-                    const struct vmpacket_descriptor *pkt)
-{
-       return hv_pkt_iter_next_pkt(channel, pkt, false);
-}
-
-/*
- * Get next packet descriptor from iterator
- * If at end of list, return NULL and update host.
- */
-static inline struct vmpacket_descriptor *
-hv_pkt_iter_next(struct vmbus_channel *channel,
-                const struct vmpacket_descriptor *pkt)
-{
-       return hv_pkt_iter_next_pkt(channel, pkt, true);
-}
-
 #define foreach_vmbus_pkt(pkt, channel) \
        for (pkt = hv_pkt_iter_first(channel); pkt; \
            pkt = hv_pkt_iter_next(channel, pkt))
index e111e13..fd98229 100644 (file)
@@ -78,6 +78,9 @@ struct hvs_send_buf {
                                         ALIGN((payload_len), 8) + \
                                         VMBUS_PKT_TRAILER_SIZE)
 
+/* Upper bound on the size of a VMbus packet for hv_sock */
+#define HVS_MAX_PKT_SIZE       HVS_PKT_LEN(HVS_MTU_SIZE)
+
 union hvs_service_id {
        guid_t  srv_id;
 
@@ -378,6 +381,8 @@ static void hvs_open_connection(struct vmbus_channel *chan)
                rcvbuf = ALIGN(rcvbuf, HV_HYP_PAGE_SIZE);
        }
 
+       chan->max_pkt_size = HVS_MAX_PKT_SIZE;
+
        ret = vmbus_open(chan, sndbuf, rcvbuf, NULL, 0, hvs_channel_cb,
                         conn_from_host ? new : sk);
        if (ret != 0) {
@@ -572,12 +577,18 @@ static bool hvs_dgram_allow(u32 cid, u32 port)
 static int hvs_update_recv_data(struct hvsock *hvs)
 {
        struct hvs_recv_buf *recv_buf;
-       u32 payload_len;
+       u32 pkt_len, payload_len;
+
+       pkt_len = hv_pkt_len(hvs->recv_desc);
+
+       if (pkt_len < HVS_HEADER_LEN)
+               return -EIO;
 
        recv_buf = (struct hvs_recv_buf *)(hvs->recv_desc + 1);
        payload_len = recv_buf->hdr.data_size;
 
-       if (payload_len > HVS_MTU_SIZE)
+       if (payload_len > pkt_len - HVS_HEADER_LEN ||
+           payload_len > HVS_MTU_SIZE)
                return -EIO;
 
        if (payload_len == 0)
@@ -602,7 +613,9 @@ static ssize_t hvs_stream_dequeue(struct vsock_sock *vsk, struct msghdr *msg,
                return -EOPNOTSUPP;
 
        if (need_refill) {
-               hvs->recv_desc = hv_pkt_iter_first_raw(hvs->chan);
+               hvs->recv_desc = hv_pkt_iter_first(hvs->chan);
+               if (!hvs->recv_desc)
+                       return -ENOBUFS;
                ret = hvs_update_recv_data(hvs);
                if (ret)
                        return ret;
@@ -616,7 +629,7 @@ static ssize_t hvs_stream_dequeue(struct vsock_sock *vsk, struct msghdr *msg,
 
        hvs->recv_data_len -= to_read;
        if (hvs->recv_data_len == 0) {
-               hvs->recv_desc = hv_pkt_iter_next_raw(hvs->chan, hvs->recv_desc);
+               hvs->recv_desc = hv_pkt_iter_next(hvs->chan, hvs->recv_desc);
                if (hvs->recv_desc) {
                        ret = hvs_update_recv_data(hvs);
                        if (ret)