Author: whu
Date: Wed Apr 29 10:12:34 2015
New Revision: 282212
URL: https://svnweb.freebsd.org/changeset/base/282212

Log:
  Microsoft vmbus, storage and other related driver enhancements for HyperV.
      - Vmbus multi channel support.
      - Vector interrupt support.
      - Signal optimization.
      - Storvsc driver performance improvement.
      - Scatter and gather support for storvsc driver.
      - Minor bug fix for KVP driver.
  Thanks royger, jhb and delphij from FreeBSD community for the reviews
  and comments. Also thanks Hovy Xu from NetApp for the contributions to
  the storvsc driver.
  
  PR:     195238
  Submitted by:   whu
  Reviewed by:    royger, jhb, delphij
  Approved by:    royger
  MFC after:      2 weeks
  Relnotes:       yes
  Sponsored by:   Microsoft OSTC

Modified:
  head/sys/amd64/amd64/apic_vector.S
  head/sys/amd64/conf/GENERIC
  head/sys/amd64/conf/NOTES
  head/sys/conf/options.amd64
  head/sys/conf/options.i386
  head/sys/dev/hyperv/include/hyperv.h
  head/sys/dev/hyperv/storvsc/hv_storvsc_drv_freebsd.c
  head/sys/dev/hyperv/storvsc/hv_vstorage.h
  head/sys/dev/hyperv/utilities/hv_kvp.c
  head/sys/dev/hyperv/utilities/hv_util.c
  head/sys/dev/hyperv/vmbus/hv_channel.c
  head/sys/dev/hyperv/vmbus/hv_channel_mgmt.c   (contents, props changed)
  head/sys/dev/hyperv/vmbus/hv_connection.c   (contents, props changed)
  head/sys/dev/hyperv/vmbus/hv_hv.c
  head/sys/dev/hyperv/vmbus/hv_ring_buffer.c   (contents, props changed)
  head/sys/dev/hyperv/vmbus/hv_vmbus_drv_freebsd.c
  head/sys/dev/hyperv/vmbus/hv_vmbus_priv.h
  head/sys/i386/conf/GENERIC
  head/sys/i386/i386/apic_vector.s
  head/sys/x86/include/apicvar.h

Modified: head/sys/amd64/amd64/apic_vector.S
==============================================================================
--- head/sys/amd64/amd64/apic_vector.S  Wed Apr 29 08:56:56 2015        
(r282211)
+++ head/sys/amd64/amd64/apic_vector.S  Wed Apr 29 10:12:34 2015        
(r282212)
@@ -174,6 +174,22 @@ IDTVEC(xen_intr_upcall)
        jmp     doreti
 #endif
 
+#ifdef HYPERV
+/*
+ * This is the Hyper-V vmbus channel direct callback interrupt.
+ * Only used when it is running on Hyper-V.
+ */
+       .text
+       SUPERALIGN_TEXT
+IDTVEC(hv_vmbus_callback)
+       PUSH_FRAME
+       FAKE_MCOUNT(TF_RIP(%rsp))
+       movq    %rsp, %rdi
+       call    hv_vector_handler
+       MEXITCOUNT
+       jmp     doreti
+#endif
+
 #ifdef SMP
 /*
  * Global address space TLB shootdown.

Modified: head/sys/amd64/conf/GENERIC
==============================================================================
--- head/sys/amd64/conf/GENERIC Wed Apr 29 08:56:56 2015        (r282211)
+++ head/sys/amd64/conf/GENERIC Wed Apr 29 10:12:34 2015        (r282212)
@@ -340,7 +340,9 @@ device              virtio_blk              # VirtIO Block 
devic
 device         virtio_scsi             # VirtIO SCSI device
 device         virtio_balloon          # VirtIO Memory Balloon device
 
-# HyperV drivers
+# HyperV drivers and enchancement support
+# NOTE: HYPERV depends on hyperv.  They must be added or removed together.
+options        HYPERV                  # Hyper-V kernel infrastructure
 device         hyperv                  # HyperV drivers 
 
 # Xen HVM Guest Optimizations

Modified: head/sys/amd64/conf/NOTES
==============================================================================
--- head/sys/amd64/conf/NOTES   Wed Apr 29 08:56:56 2015        (r282211)
+++ head/sys/amd64/conf/NOTES   Wed Apr 29 10:12:34 2015        (r282212)
@@ -494,6 +494,8 @@ device              virtio_balloon  # VirtIO Memory B
 device         virtio_random   # VirtIO Entropy device
 device         virtio_console  # VirtIO Console device
 
+# Microsoft Hyper-V enchancement support
+options        HYPERV          # Hyper-V kernel infrastructure
 device                 hyperv          # HyperV drivers
 
 # Xen HVM Guest Optimizations

Modified: head/sys/conf/options.amd64
==============================================================================
--- head/sys/conf/options.amd64 Wed Apr 29 08:56:56 2015        (r282211)
+++ head/sys/conf/options.amd64 Wed Apr 29 10:12:34 2015        (r282212)
@@ -63,5 +63,7 @@ BPF_JITTER            opt_bpf.h
 
 XENHVM                 opt_global.h
 
+HYPERV                 opt_global.h
+
 # options for the Intel C600 SAS driver (isci)
 ISCI_LOGGING   opt_isci.h

Modified: head/sys/conf/options.i386
==============================================================================
--- head/sys/conf/options.i386  Wed Apr 29 08:56:56 2015        (r282211)
+++ head/sys/conf/options.i386  Wed Apr 29 10:12:34 2015        (r282212)
@@ -125,5 +125,7 @@ NATIVE                      opt_global.h
 XEN                    opt_global.h
 XENHVM                 opt_global.h
 
+HYPERV                 opt_global.h
+
 # options for the Intel C600 SAS driver (isci)
 ISCI_LOGGING   opt_isci.h

Modified: head/sys/dev/hyperv/include/hyperv.h
==============================================================================
--- head/sys/dev/hyperv/include/hyperv.h        Wed Apr 29 08:56:56 2015        
(r282211)
+++ head/sys/dev/hyperv/include/hyperv.h        Wed Apr 29 10:12:34 2015        
(r282212)
@@ -46,6 +46,7 @@
 #include <sys/systm.h>
 #include <sys/lock.h>
 #include <sys/sema.h>
+#include <sys/smp.h>
 #include <sys/mutex.h>
 #include <sys/bus.h>
 #include <vm/vm.h>
@@ -63,11 +64,22 @@ typedef uint8_t     hv_bool_uint8_t;
 #define HV_ERROR_MACHINE_LOCKED        0x800704F7
 
 /*
- * A revision number of vmbus that is used for ensuring both ends on a
- * partition are using compatible versions.
- */
+ * VMBUS version is 32 bit, upper 16 bit for major_number and lower
+ * 16 bit for minor_number.
+ *
+ * 0.13  --  Windows Server 2008
+ * 1.1   --  Windows 7
+ * 2.4   --  Windows 8
+ * 3.0   --  Windows 8.1
+ */
+#define HV_VMBUS_VERSION_WS2008                ((0 << 16) | (13))
+#define HV_VMBUS_VERSION_WIN7          ((1 << 16) | (1))
+#define HV_VMBUS_VERSION_WIN8          ((2 << 16) | (4))
+#define HV_VMBUS_VERSION_WIN8_1                ((3 << 16) | (0))
 
-#define HV_VMBUS_REVISION_NUMBER       13
+#define HV_VMBUS_VERSION_INVALID       -1
+
+#define HV_VMBUS_VERSION_CURRENT       HV_VMBUS_VERSION_WIN8_1
 
 /*
  * Make maximum size of pipe payload of 16K
@@ -112,6 +124,18 @@ typedef struct hv_guid {
         unsigned char data[16];
 } __packed hv_guid;
 
+#define HV_NIC_GUID                                                    \
+       .data = {0x63, 0x51, 0x61, 0xF8, 0x3E, 0xDF, 0xc5, 0x46,        \
+               0x91, 0x3F, 0xF2, 0xD2, 0xF9, 0x65, 0xED, 0x0E}
+
+#define HV_IDE_GUID                                                    \
+       .data = {0x32, 0x26, 0x41, 0x32, 0xcb, 0x86, 0xa2, 0x44,        \
+                0x9b, 0x5c, 0x50, 0xd1, 0x41, 0x73, 0x54, 0xf5}
+
+#define HV_SCSI_GUID                                                   \
+       .data = {0xd9, 0x63, 0x61, 0xba, 0xa1, 0x04, 0x29, 0x4d,        \
+                0xb6, 0x05, 0x72, 0xe2, 0xff, 0xb1, 0xdc, 0x7f}
+
 /*
  * At the center of the Channel Management library is
  * the Channel Offer. This struct contains the
@@ -147,7 +171,11 @@ typedef struct hv_vmbus_channel_offer {
                } __packed pipe;
        } u;
 
-       uint32_t        padding;
+       /*
+        * Sub_channel_index, newly added in Win8.
+        */
+       uint16_t        sub_channel_index;
+       uint16_t        padding;
 
 } __packed hv_vmbus_channel_offer;
 
@@ -344,7 +372,25 @@ typedef struct {
        hv_vmbus_channel_offer          offer;
        uint32_t                        child_rel_id;
        uint8_t                         monitor_id;
-       hv_bool_uint8_t                 monitor_allocated;
+       /*
+        * This field has been split into a bit field on Win7
+        * and higher.
+        */
+       uint8_t                         monitor_allocated:1;
+       uint8_t                         reserved:7;
+       /*
+        * Following fields were added in win7 and higher.
+        * Make sure to check the version before accessing these fields.
+        *
+        * If "is_dedicated_interrupt" is set, we must not set the
+        * associated bit in the channel bitmap while sending the
+        * interrupt to the host.
+        *
+        * connection_id is used in signaling the host.
+        */
+       uint16_t                        is_dedicated_interrupt:1;
+       uint16_t                        reserved1:15;
+       uint32_t                        connection_id;
 } __packed hv_vmbus_channel_offer_channel;
 
 /*
@@ -394,9 +440,11 @@ typedef struct
     hv_gpadl_handle    ring_buffer_gpadl_handle;
 
     /*
-     * GPADL for the channel's server context save area.
+     * Before win8, all incoming channel interrupts are only
+     * delivered on cpu 0. Setting this value to 0 would
+     * preserve the earlier behavior.
      */
-    hv_gpadl_handle    server_context_area_gpadl_handle;
+    uint32_t           target_vcpu;
 
     /*
      * The upstream ring buffer begins at offset zero in the memory described
@@ -646,14 +694,42 @@ typedef struct {
 } hv_vmbus_ring_buffer_info;
 
 typedef void (*hv_vmbus_pfn_channel_callback)(void *context);
+typedef void (*hv_vmbus_sc_creation_callback)(void *context);
 
 typedef enum {
        HV_CHANNEL_OFFER_STATE,
        HV_CHANNEL_OPENING_STATE,
        HV_CHANNEL_OPEN_STATE,
+       HV_CHANNEL_OPENED_STATE,
        HV_CHANNEL_CLOSING_NONDESTRUCTIVE_STATE,
 } hv_vmbus_channel_state;
 
+/*
+ *  Connection identifier type
+ */
+typedef union {
+       uint32_t                as_uint32_t;
+       struct {
+               uint32_t        id:24;
+               uint32_t        reserved:8;
+       } u;
+
+} __packed hv_vmbus_connection_id;
+
+/*
+ * Definition of the hv_vmbus_signal_event hypercall input structure
+ */
+typedef struct {
+       hv_vmbus_connection_id  connection_id;
+       uint16_t                flag_number;
+       uint16_t                rsvd_z;
+} __packed hv_vmbus_input_signal_event;
+
+typedef struct {
+       uint64_t                        align8;
+       hv_vmbus_input_signal_event     event;
+} __packed hv_vmbus_input_signal_event_buffer;
+
 typedef struct hv_vmbus_channel {
        TAILQ_ENTRY(hv_vmbus_channel)   list_entry;
        struct hv_device*               device;
@@ -688,8 +764,82 @@ typedef struct hv_vmbus_channel {
        hv_vmbus_pfn_channel_callback   on_channel_callback;
        void*                           channel_callback_context;
 
+       /*
+        * If batched_reading is set to "true", mask the interrupt
+        * and read until the channel is empty.
+        * If batched_reading is set to "false", the channel is not
+        * going to perform batched reading.
+        *
+        * Batched reading is enabled by default; specific
+        * drivers that don't want this behavior can turn it off.
+        */
+       boolean_t                       batched_reading;
+
+       boolean_t                       is_dedicated_interrupt;
+
+       /*
+        * Used as an input param for HV_CALL_SIGNAL_EVENT hypercall.
+        */
+       hv_vmbus_input_signal_event_buffer      signal_event_buffer;
+       /*
+        * 8-bytes aligned of the buffer above
+        */
+       hv_vmbus_input_signal_event     *signal_event_param;
+
+       /*
+        * From Win8, this field specifies the target virtual process
+        * on which to deliver the interupt from the host to guest.
+        * Before Win8, all channel interrupts would only be
+        * delivered on cpu 0. Setting this value to 0 would preserve
+        * the earlier behavior.
+        */
+       uint32_t                        target_vcpu;
+       /* The corresponding CPUID in the guest */
+       uint32_t                        target_cpu;
+
+       /*
+        * Support for multi-channels.
+        * The initial offer is considered the primary channel and this
+        * offer message will indicate if the host supports multi-channels.
+        * The guest is free to ask for multi-channels to be offerred and can
+        * open these multi-channels as a normal "primary" channel. However,
+        * all multi-channels will have the same type and instance guids as the
+        * primary channel. Requests sent on a given channel will result in a
+        * response on the same channel.
+        */
+
+       /*
+        * Multi-channel creation callback. This callback will be called in
+        * process context when a Multi-channel offer is received from the host.
+        * The guest can open the Multi-channel in the context of this callback.
+        */
+       hv_vmbus_sc_creation_callback   sc_creation_callback;
+
+       struct mtx                      sc_lock;
+
+       /*
+        * Link list of all the multi-channels if this is a primary channel
+        */
+       TAILQ_HEAD(, hv_vmbus_channel)  sc_list_anchor;
+       TAILQ_ENTRY(hv_vmbus_channel)   sc_list_entry;
+
+       /*
+        * The primary channel this sub-channle belongs to.
+        * This will be NULL for the primary channel.
+        */
+       struct hv_vmbus_channel         *primary_channel;
+       /*
+        * Support per channel state for use by vmbus drivers.
+        */
+       void                            *per_channel_state;
 } hv_vmbus_channel;
 
+static inline void
+hv_set_channel_read_state(hv_vmbus_channel* channel, boolean_t state)
+{
+       channel->batched_reading = state;
+}
+
 typedef struct hv_device {
        hv_guid             class_id;
        hv_guid             device_id;
@@ -760,6 +910,8 @@ int         hv_vmbus_channel_teardown_gpdal(
                                hv_vmbus_channel*       channel,
                                uint32_t                gpadl_handle);
 
+struct hv_vmbus_channel* vmbus_select_outgoing_channel(struct hv_vmbus_channel 
*promary);
+
 /*
  * Work abstraction defines
  */
@@ -819,6 +971,7 @@ typedef struct hv_vmbus_service {
 
 extern uint8_t* receive_buffer[];
 extern hv_vmbus_service service_table[];
+extern uint32_t hv_vmbus_protocal_version;
 
 void hv_kvp_callback(void *context);
 int hv_kvp_init(hv_vmbus_service *serv);

Modified: head/sys/dev/hyperv/storvsc/hv_storvsc_drv_freebsd.c
==============================================================================
--- head/sys/dev/hyperv/storvsc/hv_storvsc_drv_freebsd.c        Wed Apr 29 
08:56:56 2015        (r282211)
+++ head/sys/dev/hyperv/storvsc/hv_storvsc_drv_freebsd.c        Wed Apr 29 
10:12:34 2015        (r282212)
@@ -38,6 +38,7 @@ __FBSDID("$FreeBSD$");
 #include <sys/param.h>
 #include <sys/proc.h>
 #include <sys/condvar.h>
+#include <sys/time.h>
 #include <sys/systm.h>
 #include <sys/sockio.h>
 #include <sys/mbuf.h>
@@ -53,8 +54,12 @@ __FBSDID("$FreeBSD$");
 #include <sys/callout.h>
 #include <vm/vm.h>
 #include <vm/pmap.h>
+#include <vm/uma.h>
 #include <sys/lock.h>
 #include <sys/sema.h>
+#include <sys/sglist.h>
+#include <machine/bus.h>
+#include <sys/bus_dma.h>
 
 #include <cam/cam.h>
 #include <cam/cam_ccb.h>
@@ -66,7 +71,6 @@ __FBSDID("$FreeBSD$");
 #include <cam/scsi/scsi_all.h>
 #include <cam/scsi/scsi_message.h>
 
-
 #include <dev/hyperv/include/hyperv.h>
 #include "hv_vstorage.h"
 
@@ -77,8 +81,29 @@ __FBSDID("$FreeBSD$");
 #define BLKVSC_MAX_IO_REQUESTS         STORVSC_MAX_IO_REQUESTS
 #define STORVSC_MAX_TARGETS            (2)
 
+#define STORVSC_WIN7_MAJOR 4
+#define STORVSC_WIN7_MINOR 2
+
+#define STORVSC_WIN8_MAJOR 5
+#define STORVSC_WIN8_MINOR 1
+
+#define HV_ALIGN(x, a) roundup2(x, a)
+
 struct storvsc_softc;
 
+struct hv_sgl_node {
+       LIST_ENTRY(hv_sgl_node) link;
+       struct sglist *sgl_data;
+};
+
+struct hv_sgl_page_pool{
+       LIST_HEAD(, hv_sgl_node) in_use_sgl_list;
+       LIST_HEAD(, hv_sgl_node) free_sgl_list;
+       boolean_t                is_init;
+} g_hv_sgl_page_pool;
+
+#define STORVSC_MAX_SG_PAGE_CNT STORVSC_MAX_IO_REQUESTS * 
HV_MAX_MULTIPAGE_BUFFER_COUNT
+
 enum storvsc_request_type {
        WRITE_TYPE,
        READ_TYPE,
@@ -96,20 +121,24 @@ struct hv_storvsc_request {
        struct storvsc_softc *softc;
        struct callout callout;
        struct sema synch_sema; /*Synchronize the request/response if needed */
+       struct sglist *bounce_sgl;
+       unsigned int bounce_sgl_count;
+       uint64_t not_aligned_seg_bits;
 };
 
 struct storvsc_softc {
        struct hv_device                *hs_dev;
-        LIST_HEAD(, hv_storvsc_request) hs_free_list;
-        struct mtx                     hs_lock;
-        struct storvsc_driver_props     *hs_drv_props;
-        int                            hs_unit;
-        uint32_t                       hs_frozen;
-        struct cam_sim                 *hs_sim;
-        struct cam_path                *hs_path;
+       LIST_HEAD(, hv_storvsc_request) hs_free_list;
+       struct mtx                      hs_lock;
+       struct storvsc_driver_props     *hs_drv_props;
+       int                             hs_unit;
+       uint32_t                        hs_frozen;
+       struct cam_sim                  *hs_sim;
+       struct cam_path                 *hs_path;
        uint32_t                        hs_num_out_reqs;
        boolean_t                       hs_destroy;
        boolean_t                       hs_drain_notify;
+       boolean_t                       hs_open_multi_channel;
        struct sema                     hs_drain_sema;  
        struct hv_storvsc_request       hs_init_req;
        struct hv_storvsc_request       hs_reset_req;
@@ -124,7 +153,7 @@ struct storvsc_softc {
  * The first can be tested by "sg_senddiag -vv /dev/daX",
  * and the second and third can be done by
  * "sg_wr_mode -v -p 08 -c 0,1a -m 0,ff /dev/daX".
- */ 
+ */
 #define HVS_TIMEOUT_TEST 0
 
 /*
@@ -138,7 +167,7 @@ struct storvsc_driver_props {
        char            *drv_name;
        char            *drv_desc;
        uint8_t         drv_max_luns_per_target;
-       uint8_t         drv_max_ios_per_target; 
+       uint8_t         drv_max_ios_per_target;
        uint32_t        drv_ringbuffer_size;
 };
 
@@ -150,6 +179,8 @@ enum hv_storage_type {
 
 #define HS_MAX_ADAPTERS 10
 
+#define HV_STORAGE_SUPPORTS_MULTI_CHANNEL 0x1
+
 /* {ba6163d9-04a1-4d29-b605-72e2ffb1dc7f} */
 static const hv_guid gStorVscDeviceType={
        .data = {0xd9, 0x63, 0x61, 0xba, 0xa1, 0x04, 0x29, 0x4d,
@@ -171,13 +202,16 @@ static struct storvsc_driver_props g_drv
         STORVSC_RINGBUFFER_SIZE}
 };
 
+static int storvsc_current_major;
+static int storvsc_current_minor;
+
 /* static functions */
 static int storvsc_probe(device_t dev);
 static int storvsc_attach(device_t dev);
 static int storvsc_detach(device_t dev);
 static void storvsc_poll(struct cam_sim * sim);
 static void storvsc_action(struct cam_sim * sim, union ccb * ccb);
-static void create_storvsc_request(union ccb *ccb, struct hv_storvsc_request 
*reqp);
+static int create_storvsc_request(union ccb *ccb, struct hv_storvsc_request 
*reqp);
 static void storvsc_free_request(struct storvsc_softc *sc, struct 
hv_storvsc_request *reqp);
 static enum hv_storage_type storvsc_get_storage_type(device_t dev);
 static void hv_storvsc_on_channel_callback(void *context);
@@ -186,6 +220,14 @@ static void hv_storvsc_on_iocompletion( 
                                        struct hv_storvsc_request *request);
 static int hv_storvsc_connect_vsp(struct hv_device *device);
 static void storvsc_io_done(struct hv_storvsc_request *reqp);
+static void storvsc_copy_sgl_to_bounce_buf(struct sglist *bounce_sgl,
+                               bus_dma_segment_t *orig_sgl,
+                               unsigned int orig_sgl_count,
+                               uint64_t seg_bits);
+void storvsc_copy_from_bounce_buf_to_sgl(bus_dma_segment_t *dest_sgl,
+                               unsigned int dest_sgl_count,
+                               struct sglist* src_sgl,
+                               uint64_t seg_bits);
 
 static device_method_t storvsc_methods[] = {
        /* Device interface */
@@ -207,7 +249,7 @@ MODULE_DEPEND(storvsc, vmbus, 1, 1, 1);
 
 
 /**
- * The host is capable of sending messages to us that are 
+ * The host is capable of sending messages to us that are
  * completely unsolicited. So, we need to address the race
  * condition where we may be in the process of unloading the
  * driver when the host may send us an unsolicited message.
@@ -223,7 +265,7 @@ MODULE_DEPEND(storvsc, vmbus, 1, 1, 1);
  *    destroyed.
  *
  * 3. Once the device is marked as being destroyed, we only
- *    permit incoming traffic to properly account for 
+ *    permit incoming traffic to properly account for
  *    packets already sent out.
  */
 static inline struct storvsc_softc *
@@ -260,6 +302,113 @@ get_stor_device(struct hv_device *device
 }
 
 /**
+ * @brief Callback handler, will be invoked when receive mutil-channel offer
+ *
+ * @param context  new multi-channel
+ */
+static void
+storvsc_handle_sc_creation(void *context)
+{
+       hv_vmbus_channel *new_channel;
+       struct hv_device *device;
+       struct storvsc_softc *sc;
+       struct vmstor_chan_props props;
+       int ret = 0;
+
+       new_channel = (hv_vmbus_channel *)context;
+       device = new_channel->primary_channel->device;
+       sc = get_stor_device(device, TRUE);
+       if (sc == NULL)
+               return;
+
+       if (FALSE == sc->hs_open_multi_channel)
+               return;
+       
+       memset(&props, 0, sizeof(props));
+
+       ret = hv_vmbus_channel_open(new_channel,
+           sc->hs_drv_props->drv_ringbuffer_size,
+           sc->hs_drv_props->drv_ringbuffer_size,
+           (void *)&props,
+           sizeof(struct vmstor_chan_props),
+           hv_storvsc_on_channel_callback,
+           new_channel);
+
+       return;
+}
+
+/**
+ * @brief Send multi-channel creation request to host
+ *
+ * @param device  a Hyper-V device pointer
+ * @param max_chans  the max channels supported by vmbus
+ */
+static void
+storvsc_send_multichannel_request(struct hv_device *dev, int max_chans)
+{
+       struct storvsc_softc *sc;
+       struct hv_storvsc_request *request;
+       struct vstor_packet *vstor_packet;      
+       int request_channels_cnt = 0;
+       int ret;
+
+       /* get multichannels count that need to create */
+       request_channels_cnt = MIN(max_chans, mp_ncpus);
+
+       sc = get_stor_device(dev, TRUE);
+       if (sc == NULL) {
+               printf("Storvsc_error: get sc failed while send mutilchannel "
+                   "request\n");
+               return;
+       }
+
+       request = &sc->hs_init_req;
+
+       /* Establish a handler for multi-channel */
+       dev->channel->sc_creation_callback = storvsc_handle_sc_creation;
+
+       /* request the host to create multi-channel */
+       memset(request, 0, sizeof(struct hv_storvsc_request));
+       
+       sema_init(&request->synch_sema, 0, ("stor_synch_sema"));
+
+       vstor_packet = &request->vstor_packet;
+       
+       vstor_packet->operation = VSTOR_OPERATION_CREATE_MULTI_CHANNELS;
+       vstor_packet->flags = REQUEST_COMPLETION_FLAG;
+       vstor_packet->u.multi_channels_cnt = request_channels_cnt;
+
+       ret = hv_vmbus_channel_send_packet(
+           dev->channel,
+           vstor_packet,
+           sizeof(struct vstor_packet),
+           (uint64_t)(uintptr_t)request,
+           HV_VMBUS_PACKET_TYPE_DATA_IN_BAND,
+           HV_VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
+
+       /* wait for 5 seconds */
+       ret = sema_timedwait(&request->synch_sema, 5 * hz);
+       if (ret != 0) {         
+               printf("Storvsc_error: create multi-channel timeout, %d\n",
+                   ret);
+               return;
+       }
+
+       if (vstor_packet->operation != VSTOR_OPERATION_COMPLETEIO ||
+           vstor_packet->status != 0) {                
+               printf("Storvsc_error: create multi-channel invalid operation "
+                   "(%d) or statue (%u)\n",
+                   vstor_packet->operation, vstor_packet->status);
+               return;
+       }
+
+       sc->hs_open_multi_channel = TRUE;
+
+       if (bootverbose)
+               printf("Storvsc create multi-channel success!\n");
+}
+
+/**
  * @brief initialize channel connection to parent partition
  *
  * @param dev  a Hyper-V device pointer
@@ -272,11 +421,15 @@ hv_storvsc_channel_init(struct hv_device
        struct hv_storvsc_request *request;
        struct vstor_packet *vstor_packet;
        struct storvsc_softc *sc;
+       uint16_t max_chans = 0;
+       boolean_t support_multichannel = FALSE;
+
+       max_chans = 0;
+       support_multichannel = FALSE;
 
        sc = get_stor_device(dev, TRUE);
-       if (sc == NULL) {
-               return ENODEV;
-       }
+       if (sc == NULL)
+               return (ENODEV);
 
        request = &sc->hs_init_req;
        memset(request, 0, sizeof(struct hv_storvsc_request));
@@ -300,15 +453,13 @@ hv_storvsc_channel_init(struct hv_device
                        HV_VMBUS_PACKET_TYPE_DATA_IN_BAND,
                        HV_VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
 
-       if (ret != 0) {
+       if (ret != 0)
                goto cleanup;
-       }
 
-       ret = sema_timedwait(&request->synch_sema, 500); /* KYS 5 seconds */
-
-       if (ret != 0) {
+       /* wait 5 seconds */
+       ret = sema_timedwait(&request->synch_sema, 5 * hz);
+       if (ret != 0)
                goto cleanup;
-       }
 
        if (vstor_packet->operation != VSTOR_OPERATION_COMPLETEIO ||
                vstor_packet->status != 0) {
@@ -321,7 +472,8 @@ hv_storvsc_channel_init(struct hv_device
        vstor_packet->operation = VSTOR_OPERATION_QUERYPROTOCOLVERSION;
        vstor_packet->flags = REQUEST_COMPLETION_FLAG;
 
-       vstor_packet->u.version.major_minor = VMSTOR_PROTOCOL_VERSION_CURRENT;
+       vstor_packet->u.version.major_minor =
+           VMSTOR_PROTOCOL_VERSION(storvsc_current_major, 
storvsc_current_minor);
 
        /* revision is only significant for Windows guests */
        vstor_packet->u.version.revision = 0;
@@ -334,21 +486,19 @@ hv_storvsc_channel_init(struct hv_device
                        HV_VMBUS_PACKET_TYPE_DATA_IN_BAND,
                        HV_VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
 
-       if (ret != 0) {
+       if (ret != 0)
                goto cleanup;
-       }
 
-       ret = sema_timedwait(&request->synch_sema, 500); /* KYS 5 seconds */
+       /* wait 5 seconds */
+       ret = sema_timedwait(&request->synch_sema, 5 * hz);
 
-       if (ret) {
+       if (ret)
                goto cleanup;
-       }
 
        /* TODO: Check returned version */
        if (vstor_packet->operation != VSTOR_OPERATION_COMPLETEIO ||
-               vstor_packet->status != 0) {
+               vstor_packet->status != 0)
                goto cleanup;
-       }
 
        /**
         * Query channel properties
@@ -365,22 +515,30 @@ hv_storvsc_channel_init(struct hv_device
                                HV_VMBUS_PACKET_TYPE_DATA_IN_BAND,
                                HV_VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
 
-       if ( ret != 0) {
+       if ( ret != 0)
                goto cleanup;
-       }
 
-       ret = sema_timedwait(&request->synch_sema, 500); /* KYS 5 seconds */
+       /* wait 5 seconds */
+       ret = sema_timedwait(&request->synch_sema, 5 * hz);
 
-       if (ret != 0) {
+       if (ret != 0)
                goto cleanup;
-       }
 
        /* TODO: Check returned version */
        if (vstor_packet->operation != VSTOR_OPERATION_COMPLETEIO ||
-               vstor_packet->status != 0) {
+           vstor_packet->status != 0) {
                goto cleanup;
        }
 
+       /* multi-channels feature is supported by WIN8 and above version */
+       max_chans = vstor_packet->u.chan_props.max_channel_cnt;
+       if ((hv_vmbus_protocal_version != HV_VMBUS_VERSION_WIN7) &&
+           (hv_vmbus_protocal_version != HV_VMBUS_VERSION_WS2008) &&
+           (vstor_packet->u.chan_props.flags &
+            HV_STORAGE_SUPPORTS_MULTI_CHANNEL)) {
+               support_multichannel = TRUE;
+       }
+
        memset(vstor_packet, 0, sizeof(struct vstor_packet));
        vstor_packet->operation = VSTOR_OPERATION_ENDINITIALIZATION;
        vstor_packet->flags = REQUEST_COMPLETION_FLAG;
@@ -397,16 +555,22 @@ hv_storvsc_channel_init(struct hv_device
                goto cleanup;
        }
 
-       ret = sema_timedwait(&request->synch_sema, 500); /* KYS 5 seconds */
+       /* wait 5 seconds */
+       ret = sema_timedwait(&request->synch_sema, 5 * hz);
 
-       if (ret != 0) {
+       if (ret != 0)
                goto cleanup;
-       }
 
        if (vstor_packet->operation != VSTOR_OPERATION_COMPLETEIO ||
-               vstor_packet->status != 0) {
+           vstor_packet->status != 0)
                goto cleanup;
-       }
+
+       /*
+        * If multi-channel is supported, send multichannel create
+        * request to host.
+        */
+       if (support_multichannel)
+               storvsc_send_multichannel_request(dev, max_chans);
 
 cleanup:
        sema_destroy(&request->synch_sema);
@@ -443,8 +607,7 @@ hv_storvsc_connect_vsp(struct hv_device 
                (void *)&props,
                sizeof(struct vmstor_chan_props),
                hv_storvsc_on_channel_callback,
-               dev);
-
+               dev->channel);
 
        if (ret != 0) {
                return ret;
@@ -490,7 +653,7 @@ hv_storvsc_host_reset(struct hv_device *
                goto cleanup;
        }
 
-       ret = sema_timedwait(&request->synch_sema, 500); /* KYS 5 seconds */
+       ret = sema_timedwait(&request->synch_sema, 5 * hz); /* KYS 5 seconds */
 
        if (ret) {
                goto cleanup;
@@ -498,7 +661,7 @@ hv_storvsc_host_reset(struct hv_device *
 
 
        /*
-        * At this point, all outstanding requests in the adapter 
+        * At this point, all outstanding requests in the adapter
         * should have been flushed out and return to us
         */
 
@@ -521,6 +684,7 @@ hv_storvsc_io_request(struct hv_device *
 {
        struct storvsc_softc *sc;
        struct vstor_packet *vstor_packet = &request->vstor_packet;
+       struct hv_vmbus_channel* outgoing_channel = NULL;
        int ret = 0;
 
        sc = get_stor_device(device, TRUE);
@@ -539,19 +703,20 @@ hv_storvsc_io_request(struct hv_device *
 
        vstor_packet->operation = VSTOR_OPERATION_EXECUTESRB;
 
+       outgoing_channel = vmbus_select_outgoing_channel(device->channel);
 
        mtx_unlock(&request->softc->hs_lock);
        if (request->data_buf.length) {
                ret = hv_vmbus_channel_send_packet_multipagebuffer(
-                               device->channel,
+                               outgoing_channel,
                                &request->data_buf,
-                               vstor_packet, 
-                               sizeof(struct vstor_packet), 
+                               vstor_packet,
+                               sizeof(struct vstor_packet),
                                (uint64_t)(uintptr_t)request);
 
        } else {
                ret = hv_vmbus_channel_send_packet(
-                       device->channel,
+                       outgoing_channel,
                        vstor_packet,
                        sizeof(struct vstor_packet),
                        (uint64_t)(uintptr_t)request,
@@ -610,7 +775,8 @@ static void
 hv_storvsc_on_channel_callback(void *context)
 {
        int ret = 0;
-       struct hv_device *device = (struct hv_device *)context;
+       hv_vmbus_channel *channel = (hv_vmbus_channel *)context;
+       struct hv_device *device = NULL;
        struct storvsc_softc *sc;
        uint32_t bytes_recvd;
        uint64_t request_id;
@@ -618,15 +784,22 @@ hv_storvsc_on_channel_callback(void *con
        struct hv_storvsc_request *request;
        struct vstor_packet *vstor_packet;
 
+       if (channel->primary_channel != NULL){
+               device = channel->primary_channel->device;
+       } else {
+               device = channel->device;
+       }
+
+       KASSERT(device, ("device is NULL"));
+
        sc = get_stor_device(device, FALSE);
        if (sc == NULL) {
+               printf("Storvsc_error: get stor device failed.\n");
                return;
        }
 
-       KASSERT(device, ("device"));
-
        ret = hv_vmbus_channel_recv_packet(
-                       device->channel,
+                       channel,
                        packet,
                        roundup2(sizeof(struct vstor_packet), 8),
                        &bytes_recvd,
@@ -634,21 +807,28 @@ hv_storvsc_on_channel_callback(void *con
 
        while ((ret == 0) && (bytes_recvd > 0)) {
                request = (struct hv_storvsc_request *)(uintptr_t)request_id;
-               KASSERT(request, ("request"));
 
                if ((request == &sc->hs_init_req) ||
                        (request == &sc->hs_reset_req)) {
                        memcpy(&request->vstor_packet, packet,
                                   sizeof(struct vstor_packet));
-                       sema_post(&request->synch_sema); 
+                       sema_post(&request->synch_sema);
                } else {
                        vstor_packet = (struct vstor_packet *)packet;
                        switch(vstor_packet->operation) {
                        case VSTOR_OPERATION_COMPLETEIO:
+                               if (request == NULL)
+                                       panic("VMBUS: storvsc received a "
+                                           "packet with NULL request id in "
+                                           "COMPLETEIO operation.");
+
                                hv_storvsc_on_iocompletion(sc,
                                                        vstor_packet, request);
                                break;
                        case VSTOR_OPERATION_REMOVEDEVICE:
+                       case VSTOR_OPERATION_ENUMERATE_BUS:
+                               printf("VMBUS: storvsc operation %d not "
+                                   "implemented.\n", vstor_packet->operation);
                                /* TODO: implement */
                                break;
                        default:
@@ -656,7 +836,7 @@ hv_storvsc_on_channel_callback(void *con
                        }                       
                }
                ret = hv_vmbus_channel_recv_packet(
-                               device->channel,
+                               channel,
                                packet,
                                roundup2(sizeof(struct vstor_packet), 8),
                                &bytes_recvd,
@@ -680,7 +860,16 @@ storvsc_probe(device_t dev)
 {
        int ata_disk_enable = 0;
        int ret = ENXIO;
-
+       
+       if ((HV_VMBUS_VERSION_WIN8 == hv_vmbus_protocal_version) ||
+           (HV_VMBUS_VERSION_WIN8_1 == hv_vmbus_protocal_version)){
+               storvsc_current_major = STORVSC_WIN8_MAJOR;
+               storvsc_current_minor = STORVSC_WIN8_MINOR;
+       } else {
+               storvsc_current_major = STORVSC_WIN7_MAJOR;
+               storvsc_current_minor = STORVSC_WIN7_MINOR;
+       }
+       
        switch (storvsc_get_storage_type(dev)) {
        case DRIVER_BLKVSC:
                if(bootverbose)
@@ -721,9 +910,11 @@ storvsc_attach(device_t dev)
        enum hv_storage_type stor_type;
        struct storvsc_softc *sc;
        struct cam_devq *devq;
-       int ret, i;
+       int ret, i, j;
        struct hv_storvsc_request *reqp;
        struct root_hold_token *root_mount_token = NULL;
+       struct hv_sgl_node *sgl_node = NULL;
+       void *tmp_buff = NULL;
 
        /*
         * We need to serialize storvsc attach calls.
@@ -764,8 +955,41 @@ storvsc_attach(device_t dev)
                LIST_INSERT_HEAD(&sc->hs_free_list, reqp, link);
        }
 
+       /* create sg-list page pool */
+       if (FALSE == g_hv_sgl_page_pool.is_init) {
+               g_hv_sgl_page_pool.is_init = TRUE;
+               LIST_INIT(&g_hv_sgl_page_pool.in_use_sgl_list);
+               LIST_INIT(&g_hv_sgl_page_pool.free_sgl_list);
+
+               /*
+                * Pre-create SG list, each SG list with
+                * HV_MAX_MULTIPAGE_BUFFER_COUNT segments, each
+                * segment has one page buffer
+                */
+               for (i = 0; i < STORVSC_MAX_IO_REQUESTS; i++) {
+                       sgl_node = malloc(sizeof(struct hv_sgl_node),
+                           M_DEVBUF, M_WAITOK|M_ZERO);
+
+                       sgl_node->sgl_data =
+                           sglist_alloc(HV_MAX_MULTIPAGE_BUFFER_COUNT,
+                           M_WAITOK|M_ZERO);
+
+                       for (j = 0; j < HV_MAX_MULTIPAGE_BUFFER_COUNT; j++) {
+                               tmp_buff = malloc(PAGE_SIZE,
+                                   M_DEVBUF, M_WAITOK|M_ZERO);
+
+                               sgl_node->sgl_data->sg_segs[j].ss_paddr =
+                                   (vm_paddr_t)tmp_buff;
+                       }
+
+                       LIST_INSERT_HEAD(&g_hv_sgl_page_pool.free_sgl_list,
+                           sgl_node, link);
+               }
+       }
+
        sc->hs_destroy = FALSE;
        sc->hs_drain_notify = FALSE;
+       sc->hs_open_multi_channel = FALSE;
        sema_init(&sc->hs_drain_sema, 0, "Store Drain Sema");
 
        ret = hv_storvsc_connect_vsp(hv_dev);
@@ -834,6 +1058,20 @@ cleanup:
                LIST_REMOVE(reqp, link);
                free(reqp, M_DEVBUF);
        }
+
+       while (!LIST_EMPTY(&g_hv_sgl_page_pool.free_sgl_list)) {
+               sgl_node = LIST_FIRST(&g_hv_sgl_page_pool.free_sgl_list);
+               LIST_REMOVE(sgl_node, link);
+               for (j = 0; j < HV_MAX_MULTIPAGE_BUFFER_COUNT; j++) {
+                       if (NULL !=
+                           (void*)sgl_node->sgl_data->sg_segs[j].ss_paddr) {
+                               
free((void*)sgl_node->sgl_data->sg_segs[j].ss_paddr, M_DEVBUF);
+                       }
+               }
+               sglist_free(sgl_node->sgl_data);
+               free(sgl_node, M_DEVBUF);
+       }
+
        return (ret);
 }
 
@@ -853,6 +1091,8 @@ storvsc_detach(device_t dev)
        struct storvsc_softc *sc = device_get_softc(dev);
        struct hv_storvsc_request *reqp = NULL;
        struct hv_device *hv_device = vmbus_get_devctx(dev);
+       struct hv_sgl_node *sgl_node = NULL;
+       int j = 0;
 
        mtx_lock(&hv_device->channel->inbound_lock);
        sc->hs_destroy = TRUE;
@@ -884,6 +1124,20 @@ storvsc_detach(device_t dev)
                free(reqp, M_DEVBUF);
        }
        mtx_unlock(&sc->hs_lock);
+
+       while (!LIST_EMPTY(&g_hv_sgl_page_pool.free_sgl_list)) {
+               sgl_node = LIST_FIRST(&g_hv_sgl_page_pool.free_sgl_list);
+               LIST_REMOVE(sgl_node, link);
+               for (j = 0; j < HV_MAX_MULTIPAGE_BUFFER_COUNT; j++){
+                       if (NULL !=
+                           (void*)sgl_node->sgl_data->sg_segs[j].ss_paddr) {
+                               
free((void*)sgl_node->sgl_data->sg_segs[j].ss_paddr, M_DEVBUF);

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to