Index: head/sys/dev/hyperv/storvsc/hv_storvsc_drv_freebsd.c =================================================================== --- head/sys/dev/hyperv/storvsc/hv_storvsc_drv_freebsd.c +++ head/sys/dev/hyperv/storvsc/hv_storvsc_drv_freebsd.c @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -87,7 +88,10 @@ #define VSTOR_PKT_SIZE (sizeof(struct vstor_packet) - vmscsi_size_delta) -#define HV_ALIGN(x, a) roundup2(x, a) +#define STORVSC_DATA_SEGCNT_MAX VMBUS_CHAN_PRPLIST_MAX +#define STORVSC_DATA_SEGSZ_MAX PAGE_SIZE +#define STORVSC_DATA_SIZE_MAX \ + (STORVSC_DATA_SEGCNT_MAX * STORVSC_DATA_SEGSZ_MAX) struct storvsc_softc; @@ -102,7 +106,7 @@ boolean_t is_init; } g_hv_sgl_page_pool; -#define STORVSC_MAX_SG_PAGE_CNT STORVSC_MAX_IO_REQUESTS * VMBUS_CHAN_PRPLIST_MAX +#define STORVSC_MAX_SG_PAGE_CNT STORVSC_MAX_IO_REQUESTS * STORVSC_DATA_SEGCNT_MAX enum storvsc_request_type { WRITE_TYPE, @@ -110,26 +114,41 @@ UNKNOWN_TYPE }; -struct hvs_gpa_range { +SYSCTL_NODE(_hw, OID_AUTO, storvsc, CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, + "Hyper-V storage interface"); + +static u_int hv_storvsc_use_pim_unmapped = 1; +SYSCTL_INT(_hw_storvsc, OID_AUTO, use_pim_unmapped, CTLFLAG_RDTUN, + &hv_storvsc_use_pim_unmapped, 0, + "Optimize storvsc by using unmapped I/O"); + +struct hv_storvsc_sysctl { + u_long data_bio_cnt; + u_long data_vaddr_cnt; + u_long data_sg_cnt; +}; + +struct storvsc_gpa_range { struct vmbus_gpa_range gpa_range; - uint64_t gpa_page[VMBUS_CHAN_PRPLIST_MAX]; + uint64_t gpa_page[STORVSC_DATA_SEGCNT_MAX]; } __packed; struct hv_storvsc_request { - LIST_ENTRY(hv_storvsc_request) link; - struct vstor_packet vstor_packet; - int prp_cnt; - struct hvs_gpa_range prp_list; - void *sense_data; - uint8_t sense_info_len; - uint8_t retries; - union ccb *ccb; - 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; + LIST_ENTRY(hv_storvsc_request) link; + struct vstor_packet vstor_packet; + int prp_cnt; + struct storvsc_gpa_range prp_list; + void *sense_data; + uint8_t sense_info_len; + uint8_t retries; + union ccb *ccb; + 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; + bus_dmamap_t data_dmap; }; struct storvsc_softc { @@ -148,6 +167,8 @@ struct hv_storvsc_request hs_init_req; struct hv_storvsc_request hs_reset_req; device_t hs_dev; + bus_dma_tag_t storvsc_req_dtag; + struct hv_storvsc_sysctl sysctl_data; struct vmbus_channel *hs_cpu2chan[MAXCPU]; }; @@ -882,6 +903,77 @@ } } +static int +storvsc_init_requests(device_t dev) +{ + struct storvsc_softc *sc = device_get_softc(dev); + struct hv_storvsc_request *reqp; + int error, i; + + LIST_INIT(&sc->hs_free_list); + + error = bus_dma_tag_create( + bus_get_dma_tag(dev), /* parent */ + 1, /* alignment */ + PAGE_SIZE, /* boundary */ + BUS_SPACE_MAXADDR, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + STORVSC_DATA_SIZE_MAX, /* maxsize */ + STORVSC_DATA_SEGCNT_MAX, /* nsegments */ + STORVSC_DATA_SEGSZ_MAX, /* maxsegsize */ + 0, /* flags */ + NULL, /* lockfunc */ + NULL, /* lockfuncarg */ + &sc->storvsc_req_dtag); + if (error) { + device_printf(dev, "failed to create storvsc dma tag\n"); + return (error); + } + + for (i = 0; i < sc->hs_drv_props->drv_max_ios_per_target; ++i) { + reqp = malloc(sizeof(struct hv_storvsc_request), + M_DEVBUF, M_WAITOK|M_ZERO); + reqp->softc = sc; + error = bus_dmamap_create(sc->storvsc_req_dtag, 0, + &reqp->data_dmap); + if (error) { + device_printf(dev, "failed to allocate storvsc " + "data dmamap\n"); + goto cleanup; + } + LIST_INSERT_HEAD(&sc->hs_free_list, reqp, link); + } + return (0); + +cleanup: + while ((reqp = LIST_FIRST(&sc->hs_free_list)) != NULL) { + LIST_REMOVE(reqp, link); + bus_dmamap_destroy(sc->storvsc_req_dtag, reqp->data_dmap); + free(reqp, M_DEVBUF); + } + return (error); +} + +static void +storvsc_sysctl(device_t dev) +{ + struct sysctl_oid_list *child; + struct sysctl_ctx_list *ctx; + struct storvsc_softc *sc; + + sc = device_get_softc(dev); + ctx = device_get_sysctl_ctx(dev); + child = SYSCTL_CHILDREN(device_get_sysctl_tree(dev)); + + SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "data_bio_cnt", CTLFLAG_RW, + &sc->sysctl_data.data_bio_cnt, "# of bio data block"); + SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "data_vaddr_cnt", CTLFLAG_RW, + &sc->sysctl_data.data_vaddr_cnt, "# of vaddr data block"); + SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "data_sg_cnt", CTLFLAG_RW, + &sc->sysctl_data.data_sg_cnt, "# of sg data block"); +} + /** * @brief StorVSC attach function * @@ -926,16 +1018,11 @@ sc->hs_unit = device_get_unit(dev); sc->hs_dev = dev; - LIST_INIT(&sc->hs_free_list); mtx_init(&sc->hs_lock, "hvslck", NULL, MTX_DEF); - for (i = 0; i < sc->hs_drv_props->drv_max_ios_per_target; ++i) { - reqp = malloc(sizeof(struct hv_storvsc_request), - M_DEVBUF, M_WAITOK|M_ZERO); - reqp->softc = sc; - - LIST_INSERT_HEAD(&sc->hs_free_list, reqp, link); - } + ret = storvsc_init_requests(dev); + if (ret != 0) + goto cleanup; /* create sg-list page pool */ if (FALSE == g_hv_sgl_page_pool.is_init) { @@ -945,7 +1032,7 @@ /* * Pre-create SG list, each SG list with - * VMBUS_CHAN_PRPLIST_MAX segments, each + * STORVSC_DATA_SEGCNT_MAX segments, each * segment has one page buffer */ for (i = 0; i < STORVSC_MAX_IO_REQUESTS; i++) { @@ -953,10 +1040,10 @@ M_DEVBUF, M_WAITOK|M_ZERO); sgl_node->sgl_data = - sglist_alloc(VMBUS_CHAN_PRPLIST_MAX, + sglist_alloc(STORVSC_DATA_SEGCNT_MAX, M_WAITOK|M_ZERO); - for (j = 0; j < VMBUS_CHAN_PRPLIST_MAX; j++) { + for (j = 0; j < STORVSC_DATA_SEGCNT_MAX; j++) { tmp_buff = malloc(PAGE_SIZE, M_DEVBUF, M_WAITOK|M_ZERO); @@ -1031,6 +1118,8 @@ mtx_unlock(&sc->hs_lock); + storvsc_sysctl(dev); + root_mount_rel(root_mount_token); return (0); @@ -1040,13 +1129,14 @@ while (!LIST_EMPTY(&sc->hs_free_list)) { reqp = LIST_FIRST(&sc->hs_free_list); LIST_REMOVE(reqp, link); + bus_dmamap_destroy(sc->storvsc_req_dtag, reqp->data_dmap); 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 < VMBUS_CHAN_PRPLIST_MAX; j++) { + for (j = 0; j < STORVSC_DATA_SEGCNT_MAX; 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); @@ -1101,7 +1191,7 @@ while (!LIST_EMPTY(&sc->hs_free_list)) { reqp = LIST_FIRST(&sc->hs_free_list); LIST_REMOVE(reqp, link); - + bus_dmamap_destroy(sc->storvsc_req_dtag, reqp->data_dmap); free(reqp, M_DEVBUF); } mtx_unlock(&sc->hs_lock); @@ -1109,7 +1199,7 @@ 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 < VMBUS_CHAN_PRPLIST_MAX; j++){ + for (j = 0; j < STORVSC_DATA_SEGCNT_MAX; 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); @@ -1294,6 +1384,8 @@ cpi->hba_inquiry = PI_TAG_ABLE|PI_SDTR_ABLE; cpi->target_sprt = 0; cpi->hba_misc = PIM_NOBUSRESET; + if (hv_storvsc_use_pim_unmapped) + cpi->hba_misc |= PIM_UNMAPPED; cpi->hba_eng_cnt = 0; cpi->max_target = STORVSC_MAX_TARGETS; cpi->max_lun = sc->hs_drv_props->drv_max_luns_per_target; @@ -1368,6 +1460,7 @@ case XPT_SCSI_IO: case XPT_IMMED_NOTIFY: { struct hv_storvsc_request *reqp = NULL; + bus_dmamap_t dmap_saved; if (ccb->csio.cdb_len == 0) { panic("cdl_len is 0\n"); @@ -1386,7 +1479,14 @@ reqp = LIST_FIRST(&sc->hs_free_list); LIST_REMOVE(reqp, link); + /* Save the data_dmap before reset request */ + dmap_saved = reqp->data_dmap; + + /* XXX this is ugly */ bzero(reqp, sizeof(struct hv_storvsc_request)); + + /* Restore necessary bits */ + reqp->data_dmap = dmap_saved; reqp->softc = sc; ccb->ccb_h.status |= CAM_SIM_QUEUED; @@ -1642,6 +1742,35 @@ } /** + * Copy bus_dma segments to multiple page buffer, which requires + * the pages are compact composed except for the 1st and last pages. + */ +static void +storvsc_xferbuf_prepare(void *arg, bus_dma_segment_t *segs, int nsegs, int error) +{ + struct hv_storvsc_request *reqp = arg; + union ccb *ccb = reqp->ccb; + struct ccb_scsiio *csio = &ccb->csio; + struct storvsc_gpa_range *prplist; + int i; + + prplist = &reqp->prp_list; + prplist->gpa_range.gpa_len = csio->dxfer_len; + prplist->gpa_range.gpa_ofs = segs[0].ds_addr & PAGE_MASK; + + for (i = 0; i < nsegs; i++) { + prplist->gpa_page[i] = atop(segs[i].ds_addr); +#ifdef INVARIANTS + if (i != 0 && i != nsegs - 1) { + KASSERT((segs[i].ds_addr & PAGE_MASK) == 0 && + segs[i].ds_len == PAGE_SIZE, ("not a full page")); + } +#endif + } + reqp->prp_cnt = nsegs; +} + +/** * @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 @@ -1656,11 +1785,9 @@ { struct ccb_scsiio *csio = &ccb->csio; uint64_t phys_addr; - uint32_t bytes_to_copy = 0; - uint32_t pfn_num = 0; uint32_t pfn; uint64_t not_aligned_seg_bits = 0; - struct hvs_gpa_range *prplist; + int error; /* refer to struct vmscsi_req for meanings of these two fields */ reqp->vstor_packet.u.vm_srb.port = @@ -1704,36 +1831,26 @@ return (0); } - prplist = &reqp->prp_list; - prplist->gpa_range.gpa_len = csio->dxfer_len; - switch (ccb->ccb_h.flags & CAM_DATA_MASK) { + case CAM_DATA_BIO: case CAM_DATA_VADDR: - { - bytes_to_copy = csio->dxfer_len; - phys_addr = vtophys(csio->data_ptr); - prplist->gpa_range.gpa_ofs = phys_addr & PAGE_MASK; - - while (bytes_to_copy != 0) { - int bytes, page_offset; - phys_addr = - vtophys(&csio->data_ptr[prplist->gpa_range.gpa_len - - bytes_to_copy]); - pfn = phys_addr >> PAGE_SHIFT; - prplist->gpa_page[pfn_num] = pfn; - page_offset = phys_addr & PAGE_MASK; - - bytes = min(PAGE_SIZE - page_offset, bytes_to_copy); - - bytes_to_copy -= bytes; - pfn_num++; + error = bus_dmamap_load_ccb(reqp->softc->storvsc_req_dtag, + reqp->data_dmap, ccb, storvsc_xferbuf_prepare, reqp, + BUS_DMA_NOWAIT); + if (error) { + xpt_print(ccb->ccb_h.path, + "bus_dmamap_load_ccb failed: %d\n", error); + return (error); } - reqp->prp_cnt = pfn_num; + if ((ccb->ccb_h.flags & CAM_DATA_MASK) == CAM_DATA_BIO) + reqp->softc->sysctl_data.data_bio_cnt++; + else + reqp->softc->sysctl_data.data_vaddr_cnt++; break; - } case CAM_DATA_SG: { + struct storvsc_gpa_range *prplist; int i = 0; int offset = 0; int ret; @@ -1742,13 +1859,16 @@ (bus_dma_segment_t *)ccb->csio.data_ptr; u_int16_t storvsc_sg_count = ccb->csio.sglist_cnt; + prplist = &reqp->prp_list; + prplist->gpa_range.gpa_len = csio->dxfer_len; + printf("Storvsc: get SG I/O operation, %d\n", reqp->vstor_packet.u.vm_srb.data_in); - if (storvsc_sg_count > VMBUS_CHAN_PRPLIST_MAX){ + if (storvsc_sg_count > STORVSC_DATA_SEGCNT_MAX){ printf("Storvsc: %d segments is too much, " "only support %d segments\n", - storvsc_sg_count, VMBUS_CHAN_PRPLIST_MAX); + storvsc_sg_count, STORVSC_DATA_SEGCNT_MAX); return (EINVAL); } @@ -1845,6 +1965,7 @@ reqp->bounce_sgl_count = 0; } + reqp->softc->sysctl_data.data_sg_cnt++; break; } default: