Index: sys/dev/hyperv/storvsc/hv_storvsc_drv_freebsd.c =================================================================== --- sys/dev/hyperv/storvsc/hv_storvsc_drv_freebsd.c +++ sys/dev/hyperv/storvsc/hv_storvsc_drv_freebsd.c @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -108,37 +109,55 @@ UNKNOWN_TYPE }; +#define STORVSC_DATA_SEGCNT_MAX (32) +SYSCTL_NODE(_hw, OID_AUTO, storvsc, CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, + "Hyper-V storage interface"); + +static u_int hv_storvsc_use_pim_unmapped = 1; +TUNABLE_INT("hw.storvsc.use_pim_unmapped", &hv_storvsc_use_pim_unmapped); +SYSCTL_INT(_hw_storvsc, OID_AUTO, use_pim_unmapped, CTLFLAG_RDTUN, + &hv_storvsc_use_pim_unmapped, 0, "Optimize storvsc by using unmapped memory"); + +struct hv_storvsc_sysctl { + u_long data_bio_cnt; + u_long data_vaddr_cnt; + u_long data_sg_cnt; +}; + struct hv_storvsc_request { - LIST_ENTRY(hv_storvsc_request) link; - struct vstor_packet vstor_packet; - hv_vmbus_multipage_buffer data_buf; - 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; + hv_vmbus_multipage_buffer data_buf; + 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 { - 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; - uint32_t hs_num_out_reqs; - boolean_t hs_destroy; - boolean_t hs_drain_notify; - struct sema hs_drain_sema; - struct hv_storvsc_request hs_init_req; - struct hv_storvsc_request hs_reset_req; + 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; + uint32_t hs_num_out_reqs; + boolean_t hs_destroy; + boolean_t hs_drain_notify; + struct sema hs_drain_sema; + struct hv_storvsc_request hs_init_req; + struct hv_storvsc_request hs_reset_req; + bus_dma_tag_t storvsc_req_dtag; + struct hv_storvsc_sysctl sysctl_data; }; @@ -986,6 +1005,76 @@ return (ret); } +static int storvsc_init_requests(device_t dev) +{ + struct storvsc_softc *sc; + struct hv_storvsc_request *reqp; + int error, i; + + sc = device_get_softc(dev); + 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_SEGCNT_MAX*PAGE_SIZE, /* maxsize */ + STORVSC_DATA_SEGCNT_MAX, /* nsegments */ + PAGE_SIZE, /* maxsegsize */ + 0, /* flags */ + NULL, /* lockfunc */ + NULL, /* lockfuncarg */ + &sc->storvsc_req_dtag); + if (error) { + device_printf(sc->hs_dev->device, "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(sc->hs_dev->device, "failed to allocate storvsc data dmamap\n"); + goto cleanup; + } + LIST_INSERT_HEAD(&sc->hs_free_list, reqp, link); + } + return (0); + +cleanup: + 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); + } + return (0); +} + +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 * @@ -1030,15 +1119,11 @@ sc->hs_unit = device_get_unit(dev); sc->hs_dev = hv_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); + if (storvsc_init_requests(dev)) { + ret = ENODEV; + goto cleanup; } /* create sg-list page pool */ @@ -1130,6 +1215,8 @@ goto cleanup; } + storvsc_sysctl(dev); + mtx_unlock(&sc->hs_lock); root_mount_rel(root_mount_token); @@ -1141,6 +1228,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); } @@ -1203,7 +1291,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); @@ -1396,6 +1484,9 @@ 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; @@ -1487,8 +1578,11 @@ reqp = LIST_FIRST(&sc->hs_free_list); LIST_REMOVE(reqp, link); - + // backup the bus_dmamap value before reset request + bus_dmamap_t dmap_back = reqp->data_dmap; bzero(reqp, sizeof(struct hv_storvsc_request)); + reqp->data_dmap = dmap_back; + reqp->softc = sc; ccb->ccb_h.status |= CAM_SIM_QUEUED; @@ -1744,6 +1838,31 @@ } /** + * 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; + int i; + + reqp->data_buf.offset = segs[0].ds_addr & PAGE_MASK; + reqp->data_buf.length = csio->dxfer_len; + + for (i = 0; i < nsegs; i++) { + reqp->data_buf.pfn_array[i] = atop(segs[i].ds_addr); + if (i != 0 && i != nsegs - 1) { + KASSERT((segs[i].ds_addr & PAGE_MASK) == 0 && + segs[i].ds_len == PAGE_SIZE, + ("bus_dma page segment is not a full page")); + } + } +} + +/** * @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 @@ -1758,11 +1877,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; - + int error; /* refer to struct vmscsi_req for meanings of these two fields */ reqp->vstor_packet.u.vm_srb.port = cam_sim_unit(xpt_path_sim(ccb->ccb_h.path)); @@ -1808,29 +1925,23 @@ reqp->data_buf.length = 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); - 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++; + error = bus_dmamap_load_ccb(reqp->softc->storvsc_req_dtag, + reqp->data_dmap, ccb, storvsc_xferbuf_prepare, + reqp, BUS_DMA_NOWAIT); + if (error) { + printf("Storvsc: failed in bus_dmamap_load_ccb: %d\n", error); + return (error); + } + 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: { int i = 0; @@ -1940,6 +2051,7 @@ reqp->bounce_sgl_count = 0; } + reqp->softc->sysctl_data.data_sg_cnt++; break; } default: