FreeBSD Bugzilla – Attachment 150893 Details for
Bug 196185
emulators/hyperv-is: Update Hyper-V ports for FreeBSD 10.1
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
patch
hyperv_ports.patch (text/plain), 35.41 KB, created by
FangJun
on 2014-12-23 05:36:34 UTC
(
hide
)
Description:
patch
Filename:
MIME Type:
Creator:
FangJun
Created:
2014-12-23 05:36:34 UTC
Size:
35.41 KB
patch
obsolete
>diff -urp src_base/contrib/hv_kvp_daemon/hv_kvp_daemon.c src/contrib/hv_kvp_daemon/hv_kvp_daemon.c >--- src_base/contrib/hv_kvp_daemon/hv_kvp_daemon.c 2014-12-23 13:26:20.000000000 +0800 >+++ src/contrib/hv_kvp_daemon/hv_kvp_daemon.c 2014-12-23 13:27:48.000000000 +0800 >@@ -285,7 +285,7 @@ kvp_file_init(void) > int alloc_unit = sizeof(struct kvp_record) * ENTRIES_PER_BLOCK; > > if (mkdir("/var/db/hyperv/pool", S_IRUSR | S_IWUSR | S_IROTH) < 0 && >- errno != EISDIR) { >+ errno != EEXIST) { > KVP_LOG(LOG_ERR, " Failed to create /var/db/hyperv/pool\n"); > exit(EXIT_FAILURE); > } >diff -urp src_base/sys/dev/hyperv/netvsc/hv_net_vsc.c src/sys/dev/hyperv/netvsc/hv_net_vsc.c >--- src_base/sys/dev/hyperv/netvsc/hv_net_vsc.c 2014-11-12 04:02:59.000000000 +0800 >+++ src/sys/dev/hyperv/netvsc/hv_net_vsc.c 2014-12-18 15:58:24.000000000 +0800 >@@ -39,11 +39,12 @@ > #include <sys/socket.h> > #include <sys/lock.h> > #include <net/if.h> >+#include <net/if_var.h> > #include <net/if_arp.h> > #include <machine/bus.h> > #include <machine/atomic.h> > >-#include <dev/hyperv/include/hyperv.h> >+#include <hyperv.h> > #include "hv_net_vsc.h" > #include "hv_rndis.h" > #include "hv_rndis_filter.h" >diff -urp src_base/sys/dev/hyperv/netvsc/hv_net_vsc.h src/sys/dev/hyperv/netvsc/hv_net_vsc.h >--- src_base/sys/dev/hyperv/netvsc/hv_net_vsc.h 2014-11-12 04:02:59.000000000 +0800 >+++ src/sys/dev/hyperv/netvsc/hv_net_vsc.h 2014-12-18 09:39:08.000000000 +0800 >@@ -43,7 +43,7 @@ > #include <sys/lock.h> > #include <sys/sx.h> > >-#include <dev/hyperv/include/hyperv.h> >+#include <hyperv.h> > > > #define NVSP_INVALID_PROTOCOL_VERSION (0xFFFFFFFF) >diff -urp src_base/sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c src/sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c >--- src_base/sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c 2014-11-12 04:02:59.000000000 +0800 >+++ src/sys/dev/hyperv/netvsc/hv_netvsc_drv_freebsd.c 2014-12-23 10:52:28.000000000 +0800 >@@ -103,7 +103,7 @@ __FBSDID("$FreeBSD: releng/10.1/sys/dev/ > > #include <machine/intr_machdep.h> > >-#include <dev/hyperv/include/hyperv.h> >+#include <hyperv.h> > #include "hv_net_vsc.h" > #include "hv_rndis.h" > #include "hv_rndis_filter.h" >diff -urp src_base/sys/dev/hyperv/netvsc/hv_rndis_filter.c src/sys/dev/hyperv/netvsc/hv_rndis_filter.c >--- src_base/sys/dev/hyperv/netvsc/hv_rndis_filter.c 2014-11-12 04:02:59.000000000 +0800 >+++ src/sys/dev/hyperv/netvsc/hv_rndis_filter.c 2014-12-17 17:25:22.000000000 +0800 >@@ -43,7 +43,7 @@ __FBSDID("$FreeBSD: releng/10.1/sys/dev/ > #include <vm/vm_param.h> > #include <vm/pmap.h> > >-#include <dev/hyperv/include/hyperv.h> >+#include <hyperv.h> > #include "hv_net_vsc.h" > #include "hv_rndis.h" > #include "hv_rndis_filter.h" >diff -urp src_base/sys/dev/hyperv/storvsc/hv_storvsc_drv_freebsd.c src/sys/dev/hyperv/storvsc/hv_storvsc_drv_freebsd.c >--- src_base/sys/dev/hyperv/storvsc/hv_storvsc_drv_freebsd.c 2014-11-12 04:02:59.000000000 +0800 >+++ src/sys/dev/hyperv/storvsc/hv_storvsc_drv_freebsd.c 2014-12-23 10:18:20.000000000 +0800 >@@ -53,8 +53,12 @@ __FBSDID("$FreeBSD: releng/10.1/sys/dev/ > #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> >@@ -67,9 +71,11 @@ __FBSDID("$FreeBSD: releng/10.1/sys/dev/ > #include <cam/scsi/scsi_message.h> > > >-#include <dev/hyperv/include/hyperv.h> >+#include <hyperv.h> > #include "hv_vstorage.h" > >+#include <sys/time.h> >+ > #define STORVSC_RINGBUFFER_SIZE (20*PAGE_SIZE) > #define STORVSC_MAX_LUNS_PER_TARGET (64) > #define STORVSC_MAX_IO_REQUESTS (STORVSC_MAX_LUNS_PER_TARGET * 2) >@@ -77,8 +83,29 @@ __FBSDID("$FreeBSD: releng/10.1/sys/dev/ > #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) (((x) + ((a) - 1)) & ~((a) - 1)) >+ > 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,17 +123,20 @@ 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; >@@ -124,7 +154,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 +168,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; > }; > >@@ -171,13 +201,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 +219,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); >+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 */ >@@ -197,17 +238,17 @@ static device_method_t storvsc_methods[] > }; > > static driver_t storvsc_driver = { >- "storvsc", storvsc_methods, sizeof(struct storvsc_softc), >+ "storvsc-port", storvsc_methods, sizeof(struct storvsc_softc), > }; > > static devclass_t storvsc_devclass; >-DRIVER_MODULE(storvsc, vmbus, storvsc_driver, storvsc_devclass, 0, 0); >-MODULE_VERSION(storvsc, 1); >-MODULE_DEPEND(storvsc, vmbus, 1, 1, 1); >+DRIVER_MODULE(storvsc_port, vmbus, storvsc_driver, storvsc_devclass, 0, 0); >+MODULE_VERSION(storvsc_port, 1); >+MODULE_DEPEND(storvsc_port, 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 +264,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 * >@@ -304,7 +345,8 @@ hv_storvsc_channel_init(struct hv_device > goto cleanup; > } > >- ret = sema_timedwait(&request->synch_sema, 500); /* KYS 5 seconds */ >+ /* wait 500 ticks */ >+ ret = sema_timedwait(&request->synch_sema, 500); > > if (ret != 0) { > goto cleanup; >@@ -321,7 +363,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; >@@ -338,7 +381,8 @@ hv_storvsc_channel_init(struct hv_device > goto cleanup; > } > >- ret = sema_timedwait(&request->synch_sema, 500); /* KYS 5 seconds */ >+ /* wait 500 ticks */ >+ ret = sema_timedwait(&request->synch_sema, 500); > > if (ret) { > goto cleanup; >@@ -369,7 +413,8 @@ hv_storvsc_channel_init(struct hv_device > goto cleanup; > } > >- ret = sema_timedwait(&request->synch_sema, 500); /* KYS 5 seconds */ >+ /* wait 500 ticks */ >+ ret = sema_timedwait(&request->synch_sema, 500); > > if (ret != 0) { > goto cleanup; >@@ -377,7 +422,7 @@ hv_storvsc_channel_init(struct hv_device > > /* TODO: Check returned version */ > if (vstor_packet->operation != VSTOR_OPERATION_COMPLETEIO || >- vstor_packet->status != 0) { >+ vstor_packet->status != 0) { > goto cleanup; > } > >@@ -397,14 +442,15 @@ hv_storvsc_channel_init(struct hv_device > goto cleanup; > } > >- ret = sema_timedwait(&request->synch_sema, 500); /* KYS 5 seconds */ >+ /* wait 500 ticks */ >+ ret = sema_timedwait(&request->synch_sema, 500); > > if (ret != 0) { > goto cleanup; > } > > if (vstor_packet->operation != VSTOR_OPERATION_COMPLETEIO || >- vstor_packet->status != 0) { >+ vstor_packet->status != 0) { > goto cleanup; > } > >@@ -445,7 +491,6 @@ hv_storvsc_connect_vsp(struct hv_device > hv_storvsc_on_channel_callback, > dev); > >- > if (ret != 0) { > return ret; > } >@@ -498,7 +543,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 > */ > >@@ -545,8 +590,8 @@ hv_storvsc_io_request(struct hv_device * > ret = hv_vmbus_channel_send_packet_multipagebuffer( > device->channel, > &request->data_buf, >- vstor_packet, >- sizeof(struct vstor_packet), >+ vstor_packet, >+ sizeof(struct vstor_packet), > (uint64_t)(uintptr_t)request); > > } else { >@@ -620,11 +665,10 @@ hv_storvsc_on_channel_callback(void *con > > 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, > packet, >@@ -634,13 +678,20 @@ 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) || >+ if (request == NULL) { >+ vstor_packet = (struct vstor_packet *)packet; >+ printf("VMBUS: storvsc received a packet with an " >+ "invalid request id. Operation is %d.\n", >+ vstor_packet->operation); >+ if (vstor_packet->operation == >+ VSTOR_OPERATION_COMPLETEIO) >+ KASSERT(request, ("request")); >+ } else 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) { >@@ -680,7 +731,12 @@ storvsc_probe(device_t dev) > { > int ata_disk_enable = 0; > int ret = ENXIO; >- >+ >+ //storvsc_current_major = STORVSC_WIN8_MAJOR; >+ //storvsc_current_minor = STORVSC_WIN8_MINOR; >+ storvsc_current_major = STORVSC_WIN7_MAJOR; >+ storvsc_current_minor = STORVSC_WIN7_MINOR; >+ > switch (storvsc_get_storage_type(dev)) { > case DRIVER_BLKVSC: > if(bootverbose) >@@ -689,14 +745,14 @@ storvsc_probe(device_t dev) > if(bootverbose) > device_printf(dev, > "Enlightened ATA/IDE detected\n"); >- ret = BUS_PROBE_DEFAULT; >+ ret = BUS_PROBE_VENDOR; > } else if(bootverbose) > device_printf(dev, "Emulated ATA/IDE set (hw.ata.disk_enable set)\n"); > break; > case DRIVER_STORVSC: > if(bootverbose) > device_printf(dev, "Enlightened SCSI device detected\n"); >- ret = BUS_PROBE_DEFAULT; >+ ret = BUS_PROBE_VENDOR; > break; > default: > ret = ENXIO; >@@ -721,9 +777,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,6 +822,43 @@ 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); >+ if (NULL == sgl_node){ >+ ret = ENOMEM; >+ goto cleanup; >+ } >+ >+ sgl_node->sgl_data = sglist_alloc(HV_MAX_MULTIPAGE_BUFFER_COUNT, >+ M_WAITOK|M_ZERO); >+ if (NULL == sgl_node->sgl_data){ >+ ret = ENOMEM; >+ goto cleanup; >+ } >+ >+ for (j = 0; j < HV_MAX_MULTIPAGE_BUFFER_COUNT; j++){ >+ tmp_buff = malloc(PAGE_SIZE, >+ M_DEVBUF, M_WAITOK|M_ZERO); >+ if (NULL == tmp_buff){ >+ ret = ENOMEM; >+ goto cleanup; >+ } >+ >+ 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; > sema_init(&sc->hs_drain_sema, 0, "Store Drain Sema"); >@@ -834,6 +929,19 @@ 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 +961,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 +994,19 @@ 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); >+ } >+ } >+ sglist_free(sgl_node->sgl_data); >+ free(sgl_node, M_DEVBUF); >+ } >+ > return (0); > } > >@@ -939,7 +1062,7 @@ storvsc_timeout_test(struct hv_storvsc_r > ticks, __func__, (ret == 0)? > "IO return detected" : > "IO return not detected"); >- /* >+ /* > * Now both the timer handler and io done are running > * simultaneously. We want to confirm the io done always > * finishes after the timer handler exits. So reqp used by >@@ -1152,9 +1275,13 @@ storvsc_action(struct cam_sim *sim, unio > > bzero(reqp, sizeof(struct hv_storvsc_request)); > reqp->softc = sc; >- >- ccb->ccb_h.status |= CAM_SIM_QUEUED; >- create_storvsc_request(ccb, reqp); >+ >+ ccb->ccb_h.status |= CAM_SIM_QUEUED; >+ if ((res = create_storvsc_request(ccb, reqp)) != 0) { >+ ccb->ccb_h.status = CAM_REQ_INVALID; >+ xpt_done(ccb); >+ return; >+ } > > if (ccb->ccb_h.timeout != CAM_TIME_INFINITY) { > callout_init(&reqp->callout, CALLOUT_MPSAFE); >@@ -1195,6 +1322,207 @@ storvsc_action(struct cam_sim *sim, unio > } > > /** >+ * @brief destroy bounce buffer >+ * >+ * This function is responsible for destroy a Scatter/Gather list >+ * that create by storvsc_create_bounce_buffer() >+ * >+ * @param sgl- the Scatter/Gather need be destroy >+ * @param sg_count- page count of the SG list. >+ * >+ */ >+static void >+storvsc_destroy_bounce_buffer(struct sglist *sgl) >+{ >+ struct hv_sgl_node *sgl_node = NULL; >+ >+ sgl_node = LIST_FIRST(&g_hv_sgl_page_pool.in_use_sgl_list); >+ LIST_REMOVE(sgl_node, link); >+ if (NULL == sgl_node) { >+ printf("storvsc error: not enough in use sgl\n"); >+ return; >+ } >+ sgl_node->sgl_data = sgl; >+ LIST_INSERT_HEAD(&g_hv_sgl_page_pool.free_sgl_list, sgl_node, link); >+} >+ >+/** >+ * @brief create bounce buffer >+ * >+ * This function is responsible for create a Scatter/Gather list, >+ * which hold several pages that can be aligned with page size. >+ * >+ * @param seg_count- SG-list segments count >+ * @param write - if WRITE_TYPE, set SG list page used size to 0, >+ * otherwise set used size to page size. >+ * >+ * return NULL if create failed >+ */ >+static struct sglist * >+storvsc_create_bounce_buffer(uint16_t seg_count, int write) >+{ >+ int i = 0; >+ struct sglist *bounce_sgl = NULL; >+ unsigned int buf_len = ((write == WRITE_TYPE) ? 0 : PAGE_SIZE); >+ struct hv_sgl_node *sgl_node = NULL; >+ >+ /* get struct sglist from free_sgl_list */ >+ sgl_node = LIST_FIRST(&g_hv_sgl_page_pool.free_sgl_list); >+ LIST_REMOVE(sgl_node, link); >+ if (NULL == sgl_node) { >+ printf("storvsc error: not enough free sgl\n"); >+ return NULL; >+ } >+ bounce_sgl = sgl_node->sgl_data; >+ LIST_INSERT_HEAD(&g_hv_sgl_page_pool.in_use_sgl_list, sgl_node, link); >+ >+ bounce_sgl->sg_maxseg = seg_count; >+ if (write == WRITE_TYPE) { >+ bounce_sgl->sg_nseg = 0; >+ } else { >+ bounce_sgl->sg_nseg = seg_count; >+ } >+ >+ for (i = 0; i < seg_count; i++) { >+ bounce_sgl->sg_segs[i].ss_len = buf_len; >+ } >+ >+ return bounce_sgl; >+} >+ >+/** >+ * @brief copy data from SG list to bounce buffer >+ * >+ * This function is responsible for copy data from one SG list's segments >+ * to another SG list which used as bounce buffer. >+ * >+ * @param bounce_sgl - the destination SG list >+ * @param orig_sgl - the segment of the source SG list. >+ * @param orig_sgl_count - the count of segments. >+ * @param orig_sgl_count - indicate which segment need bounce buffer, set 1 means need. >+ * >+ */ >+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) >+{ >+ int src_sgl_idx = 0; >+ >+ for (src_sgl_idx = 0; src_sgl_idx < orig_sgl_count; src_sgl_idx++) { >+ if (seg_bits & (1 << src_sgl_idx)) { >+ memcpy((void*)bounce_sgl->sg_segs[src_sgl_idx].ss_paddr, >+ (void*)orig_sgl[src_sgl_idx].ds_addr, >+ orig_sgl[src_sgl_idx].ds_len); >+ bounce_sgl->sg_segs[src_sgl_idx].ss_len = >+ orig_sgl[src_sgl_idx].ds_len; >+ } >+ } >+} >+ >+/** >+ * @brief copy data from SG list which used as bounce to another SG list >+ * >+ * This function is responsible for copy data from one SG list with bounce >+ * buffer to another SG list's segments. >+ * >+ * @param dest_sgl - the destination SG list's segments >+ * @param dest_sgl_count - the count of destination SG list's segment. >+ * @param src_sgl - the source SG list. >+ * @param seg_bits - indicate which segment used bounce buffer of src SG-list. >+ * >+ */ >+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) >+{ >+ int sgl_idx = 0; >+ >+ for (sgl_idx = 0; sgl_idx < dest_sgl_count; sgl_idx++) { >+ if (seg_bits & (1 << sgl_idx)) { >+ memcpy((void*)(dest_sgl[sgl_idx].ds_addr), >+ (void*)(src_sgl->sg_segs[sgl_idx].ss_paddr), >+ src_sgl->sg_segs[sgl_idx].ss_len); >+ } >+ } >+} >+ >+/** >+ * @brief check SG list with bounce buffer or not >+ * >+ * This function is responsible for check if need bounce buffer for SG list. >+ * >+ * @param sgl - the SG list's segments >+ * @param sg_count - the count of SG list's segment. >+ * @param bits - segmengs number that need bounce buffer >+ * >+ * return -1 if SG list needless bounce buffer >+ */ >+static int >+storvsc_check_bounce_buffer_sgl(bus_dma_segment_t *sgl, unsigned int sg_count, uint64_t *bits) >+{ >+ int i = 0; >+ int offset = 0; >+ uint64_t phys_addr = 0; >+ uint64_t tmp_bits = 0; >+ boolean_t found_hole = FALSE; >+ boolean_t pre_aligned = TRUE; >+ >+ if (sg_count < 2){ >+ return -1; >+ } >+ >+ *bits = 0; >+ >+ phys_addr = vtophys(sgl[0].ds_addr); >+ offset = phys_addr - trunc_page(phys_addr); >+ if (offset){ >+ pre_aligned = FALSE; >+ tmp_bits |= 1; >+ } >+ >+ for (i = 1; i < sg_count; i++) { >+ phys_addr = vtophys(sgl[i].ds_addr); >+ offset = phys_addr - trunc_page(phys_addr); >+ >+ if (0 == offset) { >+ if (FALSE == pre_aligned){ >+ /* >+ * This segment is aligned, if the previous >+ * one is not aligned, find a hole >+ */ >+ found_hole = TRUE; >+ } >+ pre_aligned = TRUE; >+ } else { >+ tmp_bits |= 1 << i; >+ if (FALSE == pre_aligned) { >+ if (phys_addr != vtophys(sgl[i-1].ds_addr + >+ sgl[i-1].ds_len)) { >+ /* >+ * Check whether connect to previous >+ * segment,if not, find the hole >+ */ >+ found_hole = TRUE; >+ } >+ } else { >+ found_hole = TRUE; >+ } >+ pre_aligned = FALSE; >+ } >+ } >+ >+ if (FALSE == found_hole) { >+ return -1; >+ } else { >+ *bits = tmp_bits; >+ return 0; >+ } >+} >+ >+/** > * @brief Fill in a request structure based on a CAM control block > * > * Fills in a request structure based on the contents of a CAM control >@@ -1204,7 +1532,7 @@ storvsc_action(struct cam_sim *sim, unio > * @param ccb pointer to a CAM contorl block > * @param reqp pointer to a request structure > */ >-static void >+static int > create_storvsc_request(union ccb *ccb, struct hv_storvsc_request *reqp) > { > struct ccb_scsiio *csio = &ccb->csio; >@@ -1212,6 +1540,7 @@ create_storvsc_request(union ccb *ccb, s > uint32_t bytes_to_copy = 0; > uint32_t pfn_num = 0; > uint32_t pfn; >+ uint64_t not_aligned_seg_bits = 0; > > /* refer to struct vmscsi_req for meanings of these two fields */ > reqp->vstor_packet.u.vm_srb.port = >@@ -1232,48 +1561,156 @@ create_storvsc_request(union ccb *ccb, s > } > > switch (ccb->ccb_h.flags & CAM_DIR_MASK) { >- case CAM_DIR_OUT: >- reqp->vstor_packet.u.vm_srb.data_in = WRITE_TYPE; >- break; >- case CAM_DIR_IN: >- reqp->vstor_packet.u.vm_srb.data_in = READ_TYPE; >- break; >- case CAM_DIR_NONE: >- reqp->vstor_packet.u.vm_srb.data_in = UNKNOWN_TYPE; >- break; >- default: >- reqp->vstor_packet.u.vm_srb.data_in = UNKNOWN_TYPE; >- break; >+ case CAM_DIR_OUT: >+ reqp->vstor_packet.u.vm_srb.data_in = WRITE_TYPE; >+ break; >+ case CAM_DIR_IN: >+ reqp->vstor_packet.u.vm_srb.data_in = READ_TYPE; >+ break; >+ case CAM_DIR_NONE: >+ reqp->vstor_packet.u.vm_srb.data_in = UNKNOWN_TYPE; >+ break; >+ default: >+ reqp->vstor_packet.u.vm_srb.data_in = UNKNOWN_TYPE; >+ break; > } > > reqp->sense_data = &csio->sense_data; > reqp->sense_info_len = csio->sense_len; > > reqp->ccb = ccb; >- /* >- KASSERT((ccb->ccb_h.flags & CAM_SCATTER_VALID) == 0, >- ("ccb is scatter gather valid\n")); >- */ >- if (csio->dxfer_len != 0) { >- reqp->data_buf.length = csio->dxfer_len; >+ >+ if (0 == csio->dxfer_len) { >+ return 0; >+ } >+ >+ reqp->data_buf.length = csio->dxfer_len; >+ >+ switch (ccb->ccb_h.flags & CAM_DATA_MASK) { >+ case CAM_DATA_VADDR:{ > bytes_to_copy = csio->dxfer_len; > phys_addr = vtophys(csio->data_ptr); >- reqp->data_buf.offset = phys_addr - trunc_page(phys_addr); >+ reqp->data_buf.offset = phys_addr & PAGE_MASK; >+ >+ while (bytes_to_copy != 0) { >+ int bytes, page_offset; >+ phys_addr = >+ vtophys(&csio->data_ptr[reqp->data_buf.length - >+ bytes_to_copy]); >+ pfn = phys_addr >> PAGE_SHIFT; >+ reqp->data_buf.pfn_array[pfn_num] = pfn; >+ page_offset = phys_addr & PAGE_MASK; >+ >+ bytes = min(PAGE_SIZE - page_offset, bytes_to_copy); >+ >+ bytes_to_copy -= bytes; >+ pfn_num++; >+ } >+ break; > } >+ case CAM_DATA_SG:{ >+ int i = 0; >+ int offset = 0; >+ bus_dma_segment_t *storvsc_sglist = >+ (bus_dma_segment_t *)ccb->csio.data_ptr; >+ u_int16_t storvsc_sg_count = ccb->csio.sglist_cnt; >+ >+ printf("Storvsc: get SG I/O operation, %d\n", >+ reqp->vstor_packet.u.vm_srb.data_in); >+ >+ if (storvsc_sg_count > HV_MAX_MULTIPAGE_BUFFER_COUNT){ >+ printf("Storvsc: %d segments is too much, " >+ "only support %d segments\n", >+ storvsc_sg_count, HV_MAX_MULTIPAGE_BUFFER_COUNT); >+ return EINVAL; >+ } >+ >+ /* check if we need to create bounce buffer */ >+ if (storvsc_check_bounce_buffer_sgl( >+ storvsc_sglist, >+ storvsc_sg_count, >+ ¬_aligned_seg_bits) != -1) { >+ reqp->bounce_sgl = >+ storvsc_create_bounce_buffer(storvsc_sg_count, >+ reqp->vstor_packet.u.vm_srb.data_in); >+ if (NULL == reqp->bounce_sgl) { >+ printf("Storvsc_error: create bounce buffer failed.\n"); >+ return ENOMEM; >+ } >+ >+ reqp->bounce_sgl_count = storvsc_sg_count; >+ reqp->not_aligned_seg_bits = not_aligned_seg_bits; >+ >+ /* >+ * if it is write, we need copy the original data >+ *to bounce buffer >+ */ >+ if (WRITE_TYPE == reqp->vstor_packet.u.vm_srb.data_in) { >+ storvsc_copy_sgl_to_bounce_buf( >+ reqp->bounce_sgl, >+ storvsc_sglist, >+ storvsc_sg_count, >+ reqp->not_aligned_seg_bits); >+ } >+ >+ /* transfer virtual address to physical frame number */ >+ if (reqp->not_aligned_seg_bits & 0x1){ >+ phys_addr = >+ vtophys(reqp->bounce_sgl->sg_segs[0].ss_paddr); >+ }else{ >+ phys_addr = >+ vtophys(storvsc_sglist[0].ds_addr); >+ } >+ reqp->data_buf.offset = phys_addr & PAGE_MASK; >+ >+ pfn = phys_addr >> PAGE_SHIFT; >+ reqp->data_buf.pfn_array[0] = pfn; >+ >+ for (i = 1; i < storvsc_sg_count; i++) { >+ if (reqp->not_aligned_seg_bits & (1 << i)){ >+ phys_addr = >+ vtophys(reqp->bounce_sgl->sg_segs[i].ss_paddr); >+ } >+ else{ >+ phys_addr = >+ vtophys(storvsc_sglist[i].ds_addr); >+ } > >- while (bytes_to_copy != 0) { >- int bytes, page_offset; >- phys_addr = vtophys(&csio->data_ptr[reqp->data_buf.length - >- bytes_to_copy]); >- pfn = phys_addr >> PAGE_SHIFT; >- reqp->data_buf.pfn_array[pfn_num] = pfn; >- page_offset = phys_addr - trunc_page(phys_addr); >+ pfn = phys_addr >> PAGE_SHIFT; >+ reqp->data_buf.pfn_array[i] = pfn; >+ } >+ } >+ else { >+ phys_addr = vtophys(storvsc_sglist[0].ds_addr); >+ >+ reqp->data_buf.offset = phys_addr & PAGE_MASK; > >- bytes = min(PAGE_SIZE - page_offset, bytes_to_copy); >+ for (i = 0; i < storvsc_sg_count; i++){ >+ phys_addr = vtophys(storvsc_sglist[i].ds_addr); >+ pfn = phys_addr >> PAGE_SHIFT; >+ reqp->data_buf.pfn_array[i] = pfn; >+ } > >- bytes_to_copy -= bytes; >- pfn_num++; >+ /* check the last segment cross boundary or not */ >+ offset = phys_addr & PAGE_MASK; >+ if (offset){ >+ phys_addr = >+ vtophys(storvsc_sglist[i-1].ds_addr + >+ PAGE_SIZE - offset); >+ pfn = phys_addr >> PAGE_SHIFT; >+ reqp->data_buf.pfn_array[i] = pfn; >+ } >+ >+ reqp->bounce_sgl_count = 0; >+ } >+ break; > } >+ default: >+ printf("Unknow flags: %d\n", ccb->ccb_h.flags); >+ return EINVAL; >+ } >+ >+ return 0; > } > > /** >@@ -1292,7 +1729,29 @@ storvsc_io_done(struct hv_storvsc_reques > struct ccb_scsiio *csio = &ccb->csio; > struct storvsc_softc *sc = reqp->softc; > struct vmscsi_req *vm_srb = &reqp->vstor_packet.u.vm_srb; >- >+ bus_dma_segment_t *ori_sglist = NULL; >+ int ori_sg_count = 0; >+ >+ /* destroy bounce buffer if it is used */ >+ if (reqp->bounce_sgl_count) { >+ ori_sglist = (bus_dma_segment_t *)ccb->csio.data_ptr; >+ ori_sg_count = ccb->csio.sglist_cnt; >+ >+ /* >+ * If it is READ operation, we should copy back the data >+ * to original SG list. >+ */ >+ if (READ_TYPE == reqp->vstor_packet.u.vm_srb.data_in) { >+ storvsc_copy_from_bounce_buf_to_sgl(ori_sglist, >+ ori_sg_count, >+ reqp->bounce_sgl, >+ reqp->not_aligned_seg_bits); >+ } >+ >+ storvsc_destroy_bounce_buffer(reqp->bounce_sgl); >+ reqp->bounce_sgl_count = 0; >+ } >+ > if (reqp->retries > 0) { > mtx_lock(&sc->hs_lock); > #if HVS_TIMEOUT_TEST >@@ -1310,7 +1769,7 @@ storvsc_io_done(struct hv_storvsc_reques > mtx_unlock(&sc->hs_lock); > } > >- /* >+ /* > * callout_drain() will wait for the timer handler to finish > * if it is running. So we don't need any lock to synchronize > * between this routine and the timer handler. >diff -urp src_base/sys/dev/hyperv/storvsc/hv_vstorage.h src/sys/dev/hyperv/storvsc/hv_vstorage.h >--- src_base/sys/dev/hyperv/storvsc/hv_vstorage.h 2014-11-12 04:02:59.000000000 +0800 >+++ src/sys/dev/hyperv/storvsc/hv_vstorage.h 2014-11-14 16:18:49.000000000 +0800 >@@ -53,7 +53,7 @@ > * V1 RC > 2008/1/31 2.0 > */ > >-#define VMSTOR_PROTOCOL_VERSION_CURRENT VMSTOR_PROTOCOL_VERSION(2, 0) >+#define VMSTOR_PROTOCOL_VERSION_CURRENT VMSTOR_PROTOCOL_VERSION(5, 1) > > /** > * Packet structure ops describing virtual storage requests. >@@ -69,7 +69,10 @@ enum vstor_packet_ops { > VSTOR_OPERATION_ENDINITIALIZATION = 8, > VSTOR_OPERATION_QUERYPROTOCOLVERSION = 9, > VSTOR_OPERATION_QUERYPROPERTIES = 10, >- VSTOR_OPERATION_MAXIMUM = 10 >+ VSTOR_OPERATION_ENUMERATE_BUS = 11, >+ VSTOR_OPERATION_FCHBA_DATA = 12, >+ VSTOR_OPERATION_CREATE_MULTI_CHANNELS = 13, >+ VSTOR_OPERATION_MAXIMUM = 13 > }; > > >@@ -123,10 +126,12 @@ struct vmstor_chan_props { > uint8_t path_id; > uint8_t target_id; > >+ uint16_t max_channel_cnt; >+ > /** > * Note: port number is only really known on the client side > */ >- uint32_t port; >+ uint16_t port; > uint32_t flags; > uint32_t max_transfer_bytes; > >@@ -193,6 +198,11 @@ struct vstor_packet { > * Used during version negotiations. > */ > struct vmstor_proto_ver version; >+ >+ /** >+ * Number of multichannels to create >+ */ >+ uint16_t multi_channels_cnt; > } u; > > } __packed; >diff -urp src_base/sys/dev/hyperv/utilities/hv_kvp.c src/sys/dev/hyperv/utilities/hv_kvp.c >--- src_base/sys/dev/hyperv/utilities/hv_kvp.c 2014-11-12 04:02:59.000000000 +0800 >+++ src/sys/dev/hyperv/utilities/hv_kvp.c 2014-12-23 10:17:16.000000000 +0800 >@@ -39,7 +39,6 @@ __FBSDID("$FreeBSD: releng/10.1/sys/dev/ > #include <sys/uio.h> > #include <sys/bus.h> > #include <sys/malloc.h> >-#include <sys/mbuf.h> > #include <sys/module.h> > #include <sys/reboot.h> > #include <sys/lock.h> >@@ -55,11 +54,12 @@ __FBSDID("$FreeBSD: releng/10.1/sys/dev/ > #include <sys/_null.h> > #include <sys/signal.h> > #include <sys/syslog.h> >+#include <sys/systm.h> > #include <sys/mutex.h> > #include <net/if_arp.h> > >-#include <dev/hyperv/include/hyperv.h> >-#include <dev/hyperv/netvsc/hv_net_vsc.h> >+#include <hyperv.h> >+#include <hv_net_vsc.h> > > #include "unicode.h" > #include "hv_kvp.h" >@@ -71,17 +71,17 @@ __FBSDID("$FreeBSD: releng/10.1/sys/dev/ > #define kvp_hdr hdr.kvp_hdr > > /* hv_kvp debug control */ >-static int hv_kvp_log = 0; >-SYSCTL_INT(_dev, OID_AUTO, hv_kvp_log, CTLFLAG_RW, &hv_kvp_log, 0, >+static int hv_kvp_port_log = 0; >+SYSCTL_INT(_dev, OID_AUTO, hv_kvp_port_log, CTLFLAG_RW, &hv_kvp_port_log, 0, > "hv_kvp log"); > > #define hv_kvp_log_error(...) do { \ >- if (hv_kvp_log > 0) \ >+ if (hv_kvp_port_log > 0) \ > log(LOG_ERR, "hv_kvp: " __VA_ARGS__); \ > } while (0) > > #define hv_kvp_log_info(...) do { \ >- if (hv_kvp_log > 1) \ >+ if (hv_kvp_port_log > 1) \ > log(LOG_INFO, "hv_kvp: " __VA_ARGS__); \ > } while (0) > >@@ -232,7 +232,7 @@ hv_kvp_negotiate_version(struct hv_vmbus > */ > if ((icframe_vercnt >= 2) && (negop->icversion_data[1].major == 3)) { > icframe_vercnt = 3; >- if (icmsg_vercnt >= 2) >+ if (icmsg_vercnt > 2) > icmsg_vercnt = 4; > else > icmsg_vercnt = 3; >@@ -734,9 +734,9 @@ hv_kvp_process_request(void *context) > recvlen = 0; > ret = hv_vmbus_channel_recv_packet(channel, kvp_buf, 2 * PAGE_SIZE, > &recvlen, &requestid); >- hv_kvp_log_info("%s: read: context %p, pending_cnt %ju ret =%d, recvlen=%d\n", >- __func__, context, pending_cnt, ret, recvlen); >- } >+ hv_kvp_log_info("%s: read: context %p, pending_cnt %llu ret =%d, recvlen=%d\n", >+ __func__, context, (unsigned long long)pending_cnt, ret, recvlen); >+ } > } > > >@@ -813,9 +813,9 @@ static void > hv_kvp_dev_destroy(void) > { > >- if (daemon_task != NULL) { >+ if (daemon_task != NULL) { > PROC_LOCK(daemon_task); >- kern_psignal(daemon_task, SIGKILL); >+ kern_psignal(daemon_task, SIGKILL); > PROC_UNLOCK(daemon_task); > } > >diff -urp src_base/sys/dev/hyperv/utilities/hv_kvp.h src/sys/dev/hyperv/utilities/hv_kvp.h >--- src_base/sys/dev/hyperv/utilities/hv_kvp.h 2014-11-12 04:02:59.000000000 +0800 >+++ src/sys/dev/hyperv/utilities/hv_kvp.h 2014-11-14 16:19:15.000000000 +0800 >@@ -220,7 +220,7 @@ struct hv_kvp_register { > struct hv_kvp_msg { > union { > struct hv_kvp_hdr kvp_hdr; >- uint32_t error; >+ int error; > } hdr; > union { > struct hv_kvp_msg_get kvp_get; >diff -urp src_base/sys/dev/hyperv/utilities/hv_util.c src/sys/dev/hyperv/utilities/hv_util.c >--- src_base/sys/dev/hyperv/utilities/hv_util.c 2014-11-12 04:02:59.000000000 +0800 >+++ src/sys/dev/hyperv/utilities/hv_util.c 2014-12-22 13:44:58.000000000 +0800 >@@ -39,7 +39,7 @@ > #include <sys/timetc.h> > #include <sys/syscallsubr.h> > >-#include <dev/hyperv/include/hyperv.h> >+#include <hyperv.h> > #include "hv_kvp.h" > > /* Time Sync data */ >@@ -378,7 +378,7 @@ hv_util_probe(device_t dev) > const char *p = vmbus_get_type(dev); > if (service_table[i].enabled && !memcmp(p, &service_table[i].guid, sizeof(hv_guid))) { > device_set_softc(dev, (void *) (&service_table[i])); >- rtn_value = BUS_PROBE_DEFAULT; >+ rtn_value = BUS_PROBE_VENDOR; > } > } > >@@ -479,13 +479,13 @@ static device_method_t util_methods[] = > { 0, 0 } } > ; > >-static driver_t util_driver = { "hyperv-utils", util_methods, 0 }; >+static driver_t util_driver = { "hyperv-utils-port", util_methods, 0 }; > > static devclass_t util_devclass; > >-DRIVER_MODULE(hv_utils, vmbus, util_driver, util_devclass, hv_util_modevent, 0); >-MODULE_VERSION(hv_utils, 1); >-MODULE_DEPEND(hv_utils, vmbus, 1, 1, 1); >+DRIVER_MODULE(hv_utils_port, vmbus, util_driver, util_devclass, hv_util_modevent, 0); >+MODULE_VERSION(hv_utils_port, 1); >+MODULE_DEPEND(hv_utils_port, vmbus, 1, 1, 1); > > SYSINIT(hv_util_initx, SI_SUB_KTHREAD_IDLE, SI_ORDER_MIDDLE + 1, > hv_util_init, NULL); >diff -urp src_base/sys/dev/hyperv/vmbus/hv_vmbus_priv.h src/sys/dev/hyperv/vmbus/hv_vmbus_priv.h >--- src_base/sys/dev/hyperv/vmbus/hv_vmbus_priv.h 2014-11-12 04:02:59.000000000 +0800 >+++ src/sys/dev/hyperv/vmbus/hv_vmbus_priv.h 2014-12-19 10:31:03.000000000 +0800 >@@ -36,7 +36,7 @@ > #include <sys/mutex.h> > #include <sys/sema.h> > >-#include <dev/hyperv/include/hyperv.h> >+#include <hyperv.h> > > > /* >Only in src/sys/dev/hyperv/vmbus: hyperv_support.S
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 196185
: 150893 |
150894
|
151525