Changeset View
Changeset View
Standalone View
Standalone View
sys/dev/hyperv/storvsc/hv_storvsc_drv_freebsd.c
Show All 34 Lines | |||||
#include <sys/cdefs.h> | #include <sys/cdefs.h> | ||||
__FBSDID("$FreeBSD$"); | __FBSDID("$FreeBSD$"); | ||||
#include <sys/param.h> | #include <sys/param.h> | ||||
#include <sys/proc.h> | #include <sys/proc.h> | ||||
#include <sys/condvar.h> | #include <sys/condvar.h> | ||||
#include <sys/time.h> | #include <sys/time.h> | ||||
#include <sys/systm.h> | #include <sys/systm.h> | ||||
#include <sys/sysctl.h> | |||||
#include <sys/sockio.h> | #include <sys/sockio.h> | ||||
#include <sys/mbuf.h> | #include <sys/mbuf.h> | ||||
#include <sys/malloc.h> | #include <sys/malloc.h> | ||||
#include <sys/module.h> | #include <sys/module.h> | ||||
#include <sys/kernel.h> | #include <sys/kernel.h> | ||||
#include <sys/queue.h> | #include <sys/queue.h> | ||||
#include <sys/lock.h> | #include <sys/lock.h> | ||||
#include <sys/sx.h> | #include <sys/sx.h> | ||||
▲ Show 20 Lines • Show All 52 Lines • ▼ Show 20 Lines | |||||
#define STORVSC_MAX_SG_PAGE_CNT STORVSC_MAX_IO_REQUESTS * HV_MAX_MULTIPAGE_BUFFER_COUNT | #define STORVSC_MAX_SG_PAGE_CNT STORVSC_MAX_IO_REQUESTS * HV_MAX_MULTIPAGE_BUFFER_COUNT | ||||
enum storvsc_request_type { | enum storvsc_request_type { | ||||
WRITE_TYPE, | WRITE_TYPE, | ||||
READ_TYPE, | READ_TYPE, | ||||
UNKNOWN_TYPE | 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 { | struct hv_storvsc_request { | ||||
LIST_ENTRY(hv_storvsc_request) link; | LIST_ENTRY(hv_storvsc_request) link; | ||||
struct vstor_packet vstor_packet; | struct vstor_packet vstor_packet; | ||||
hv_vmbus_multipage_buffer data_buf; | hv_vmbus_multipage_buffer data_buf; | ||||
void *sense_data; | void *sense_data; | ||||
uint8_t sense_info_len; | uint8_t sense_info_len; | ||||
uint8_t retries; | uint8_t retries; | ||||
union ccb *ccb; | union ccb *ccb; | ||||
struct storvsc_softc *softc; | struct storvsc_softc *softc; | ||||
struct callout callout; | struct callout callout; | ||||
struct sema synch_sema; /*Synchronize the request/response if needed */ | struct sema synch_sema; /*Synchronize the request/response if needed */ | ||||
struct sglist *bounce_sgl; | struct sglist *bounce_sgl; | ||||
unsigned int bounce_sgl_count; | unsigned int bounce_sgl_count; | ||||
uint64_t not_aligned_seg_bits; | uint64_t not_aligned_seg_bits; | ||||
bus_dmamap_t data_dmap; | |||||
}; | }; | ||||
struct storvsc_softc { | struct storvsc_softc { | ||||
struct hv_device *hs_dev; | struct hv_device *hs_dev; | ||||
LIST_HEAD(, hv_storvsc_request) hs_free_list; | LIST_HEAD(, hv_storvsc_request) hs_free_list; | ||||
struct mtx hs_lock; | struct mtx hs_lock; | ||||
struct storvsc_driver_props *hs_drv_props; | struct storvsc_driver_props *hs_drv_props; | ||||
int hs_unit; | int hs_unit; | ||||
uint32_t hs_frozen; | uint32_t hs_frozen; | ||||
struct cam_sim *hs_sim; | struct cam_sim *hs_sim; | ||||
struct cam_path *hs_path; | struct cam_path *hs_path; | ||||
uint32_t hs_num_out_reqs; | uint32_t hs_num_out_reqs; | ||||
boolean_t hs_destroy; | boolean_t hs_destroy; | ||||
boolean_t hs_drain_notify; | boolean_t hs_drain_notify; | ||||
struct sema hs_drain_sema; | struct sema hs_drain_sema; | ||||
struct hv_storvsc_request hs_init_req; | struct hv_storvsc_request hs_init_req; | ||||
struct hv_storvsc_request hs_reset_req; | struct hv_storvsc_request hs_reset_req; | ||||
bus_dma_tag_t storvsc_req_dtag; | |||||
struct hv_storvsc_sysctl sysctl_data; | |||||
}; | }; | ||||
/** | /** | ||||
* HyperV storvsc timeout testing cases: | * HyperV storvsc timeout testing cases: | ||||
* a. IO returned after first timeout; | * a. IO returned after first timeout; | ||||
* b. IO returned after second timeout and queue freeze; | * b. IO returned after second timeout and queue freeze; | ||||
* c. IO returned while timer handler is running | * c. IO returned while timer handler is running | ||||
▲ Show 20 Lines • Show All 831 Lines • ▼ Show 20 Lines | case DRIVER_STORVSC: | ||||
ret = BUS_PROBE_DEFAULT; | ret = BUS_PROBE_DEFAULT; | ||||
break; | break; | ||||
default: | default: | ||||
ret = ENXIO; | ret = ENXIO; | ||||
} | } | ||||
return (ret); | 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); | |||||
sepherosa_gmail.com: Destroy the map; though it should be no-op in this case. And the maps could also be destroyed… | |||||
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 | * @brief StorVSC attach function | ||||
* | * | ||||
* Function responsible for allocating per-device structures, | * Function responsible for allocating per-device structures, | ||||
* setting up CAM interfaces and scanning for available LUNs to | * setting up CAM interfaces and scanning for available LUNs to | ||||
* be used for SCSI device peripherals. | * be used for SCSI device peripherals. | ||||
* | * | ||||
* @param a device | * @param a device | ||||
Show All 28 Lines | storvsc_attach(device_t dev) | ||||
/* fill in driver specific properties */ | /* fill in driver specific properties */ | ||||
sc->hs_drv_props = &g_drv_props_table[stor_type]; | sc->hs_drv_props = &g_drv_props_table[stor_type]; | ||||
/* fill in device specific properties */ | /* fill in device specific properties */ | ||||
sc->hs_unit = device_get_unit(dev); | sc->hs_unit = device_get_unit(dev); | ||||
sc->hs_dev = hv_dev; | sc->hs_dev = hv_dev; | ||||
LIST_INIT(&sc->hs_free_list); | |||||
mtx_init(&sc->hs_lock, "hvslck", NULL, MTX_DEF); | mtx_init(&sc->hs_lock, "hvslck", NULL, MTX_DEF); | ||||
for (i = 0; i < sc->hs_drv_props->drv_max_ios_per_target; ++i) { | if (storvsc_init_requests(dev)) { | ||||
reqp = malloc(sizeof(struct hv_storvsc_request), | ret = ENODEV; | ||||
M_DEVBUF, M_WAITOK|M_ZERO); | goto cleanup; | ||||
reqp->softc = sc; | |||||
LIST_INSERT_HEAD(&sc->hs_free_list, reqp, link); | |||||
} | } | ||||
/* create sg-list page pool */ | /* create sg-list page pool */ | ||||
if (FALSE == g_hv_sgl_page_pool.is_init) { | if (FALSE == g_hv_sgl_page_pool.is_init) { | ||||
g_hv_sgl_page_pool.is_init = TRUE; | 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.in_use_sgl_list); | ||||
LIST_INIT(&g_hv_sgl_page_pool.free_sgl_list); | LIST_INIT(&g_hv_sgl_page_pool.free_sgl_list); | ||||
▲ Show 20 Lines • Show All 75 Lines • ▼ Show 20 Lines | if (xpt_create_path(&sc->hs_path, /*periph*/NULL, | ||||
xpt_bus_deregister(cam_sim_path(sc->hs_sim)); | xpt_bus_deregister(cam_sim_path(sc->hs_sim)); | ||||
cam_sim_free(sc->hs_sim, /*free_devq*/TRUE); | cam_sim_free(sc->hs_sim, /*free_devq*/TRUE); | ||||
mtx_unlock(&sc->hs_lock); | mtx_unlock(&sc->hs_lock); | ||||
device_printf(dev, "Unable to create path\n"); | device_printf(dev, "Unable to create path\n"); | ||||
ret = ENXIO; | ret = ENXIO; | ||||
goto cleanup; | goto cleanup; | ||||
} | } | ||||
storvsc_sysctl(dev); | |||||
mtx_unlock(&sc->hs_lock); | mtx_unlock(&sc->hs_lock); | ||||
root_mount_rel(root_mount_token); | root_mount_rel(root_mount_token); | ||||
return (0); | return (0); | ||||
cleanup: | cleanup: | ||||
root_mount_rel(root_mount_token); | root_mount_rel(root_mount_token); | ||||
▲ Show 20 Lines • Show All 250 Lines • ▼ Show 20 Lines | storvsc_action(struct cam_sim *sim, union ccb *ccb) | ||||
switch (ccb->ccb_h.func_code) { | switch (ccb->ccb_h.func_code) { | ||||
case XPT_PATH_INQ: { | case XPT_PATH_INQ: { | ||||
struct ccb_pathinq *cpi = &ccb->cpi; | struct ccb_pathinq *cpi = &ccb->cpi; | ||||
cpi->version_num = 1; | cpi->version_num = 1; | ||||
cpi->hba_inquiry = PI_TAG_ABLE|PI_SDTR_ABLE; | cpi->hba_inquiry = PI_TAG_ABLE|PI_SDTR_ABLE; | ||||
cpi->target_sprt = 0; | cpi->target_sprt = 0; | ||||
cpi->hba_misc = PIM_NOBUSRESET; | cpi->hba_misc = PIM_NOBUSRESET; | ||||
if (hv_storvsc_use_pim_unmapped) { | |||||
cpi->hba_misc |= PIM_UNMAPPED; | |||||
} | |||||
cpi->hba_eng_cnt = 0; | cpi->hba_eng_cnt = 0; | ||||
cpi->max_target = STORVSC_MAX_TARGETS; | cpi->max_target = STORVSC_MAX_TARGETS; | ||||
cpi->max_lun = sc->hs_drv_props->drv_max_luns_per_target; | cpi->max_lun = sc->hs_drv_props->drv_max_luns_per_target; | ||||
cpi->initiator_id = cpi->max_target; | cpi->initiator_id = cpi->max_target; | ||||
cpi->bus_id = cam_sim_bus(sim); | cpi->bus_id = cam_sim_bus(sim); | ||||
cpi->base_transfer_speed = 300000; | cpi->base_transfer_speed = 300000; | ||||
cpi->transport = XPORT_SAS; | cpi->transport = XPORT_SAS; | ||||
cpi->transport_version = 0; | cpi->transport_version = 0; | ||||
▲ Show 20 Lines • Show All 332 Lines • ▼ Show 20 Lines | if (!found_hole) { | ||||
return (-1); | return (-1); | ||||
} else { | } else { | ||||
*bits = tmp_bits; | *bits = tmp_bits; | ||||
return 0; | return 0; | ||||
} | } | ||||
} | } | ||||
/** | /** | ||||
* 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 | * @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 | * Fills in a request structure based on the contents of a CAM control | ||||
* block. The request structure holds the payload information for | * block. The request structure holds the payload information for | ||||
* VSCSI protocol request. | * VSCSI protocol request. | ||||
* | * | ||||
* @param ccb pointer to a CAM contorl block | * @param ccb pointer to a CAM contorl block | ||||
* @param reqp pointer to a request structure | * @param reqp pointer to a request structure | ||||
*/ | */ | ||||
static int | static int | ||||
create_storvsc_request(union ccb *ccb, struct hv_storvsc_request *reqp) | create_storvsc_request(union ccb *ccb, struct hv_storvsc_request *reqp) | ||||
{ | { | ||||
struct ccb_scsiio *csio = &ccb->csio; | struct ccb_scsiio *csio = &ccb->csio; | ||||
uint64_t phys_addr; | uint64_t phys_addr; | ||||
uint32_t bytes_to_copy = 0; | |||||
uint32_t pfn_num = 0; | |||||
uint32_t pfn; | uint32_t pfn; | ||||
uint64_t not_aligned_seg_bits = 0; | uint64_t not_aligned_seg_bits = 0; | ||||
int error; | |||||
/* refer to struct vmscsi_req for meanings of these two fields */ | /* refer to struct vmscsi_req for meanings of these two fields */ | ||||
reqp->vstor_packet.u.vm_srb.port = | reqp->vstor_packet.u.vm_srb.port = | ||||
cam_sim_unit(xpt_path_sim(ccb->ccb_h.path)); | cam_sim_unit(xpt_path_sim(ccb->ccb_h.path)); | ||||
reqp->vstor_packet.u.vm_srb.path_id = | reqp->vstor_packet.u.vm_srb.path_id = | ||||
cam_sim_bus(xpt_path_sim(ccb->ccb_h.path)); | cam_sim_bus(xpt_path_sim(ccb->ccb_h.path)); | ||||
reqp->vstor_packet.u.vm_srb.target_id = ccb->ccb_h.target_id; | reqp->vstor_packet.u.vm_srb.target_id = ccb->ccb_h.target_id; | ||||
reqp->vstor_packet.u.vm_srb.lun = ccb->ccb_h.target_lun; | reqp->vstor_packet.u.vm_srb.lun = ccb->ccb_h.target_lun; | ||||
Show All 29 Lines | create_storvsc_request(union ccb *ccb, struct hv_storvsc_request *reqp) | ||||
if (0 == csio->dxfer_len) { | if (0 == csio->dxfer_len) { | ||||
return (0); | return (0); | ||||
} | } | ||||
reqp->data_buf.length = csio->dxfer_len; | reqp->data_buf.length = csio->dxfer_len; | ||||
switch (ccb->ccb_h.flags & CAM_DATA_MASK) { | switch (ccb->ccb_h.flags & CAM_DATA_MASK) { | ||||
case CAM_DATA_BIO: | |||||
case CAM_DATA_VADDR: | case CAM_DATA_VADDR: | ||||
{ | { | ||||
bytes_to_copy = csio->dxfer_len; | error = bus_dmamap_load_ccb(reqp->softc->storvsc_req_dtag, | ||||
Not Done Inline ActionsPass BUS_DMA_NOWAIT as the flag, since the code here assumes the direct running of the callback. And check the return value of bus_dmamap_load_ccb, even though it should not fail here, since no bounce pages will be needed; better safe than sorry. sepherosa_gmail.com: Pass BUS_DMA_NOWAIT as the flag, since the code here assumes the direct running of the callback. | |||||
phys_addr = vtophys(csio->data_ptr); | reqp->data_dmap, ccb, storvsc_xferbuf_prepare, | ||||
reqp->data_buf.offset = phys_addr & PAGE_MASK; | reqp, BUS_DMA_NOWAIT); | ||||
if (error) { | |||||
while (bytes_to_copy != 0) { | printf("Storvsc: failed in bus_dmamap_load_ccb: %d\n", error); | ||||
int bytes, page_offset; | return (error); | ||||
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++; | |||||
} | } | ||||
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; | break; | ||||
} | } | ||||
case CAM_DATA_SG: | case CAM_DATA_SG: | ||||
{ | { | ||||
int i = 0; | int i = 0; | ||||
int offset = 0; | int offset = 0; | ||||
int ret; | int ret; | ||||
bus_dma_segment_t *storvsc_sglist = | bus_dma_segment_t *storvsc_sglist = | ||||
(bus_dma_segment_t *)ccb->csio.data_ptr; | (bus_dma_segment_t *)ccb->csio.data_ptr; | ||||
▲ Show 20 Lines • Show All 93 Lines • ▼ Show 20 Lines | if (ret != -1) { | ||||
vtophys(storvsc_sglist[i-1].ds_addr + | vtophys(storvsc_sglist[i-1].ds_addr + | ||||
PAGE_SIZE - offset); | PAGE_SIZE - offset); | ||||
pfn = phys_addr >> PAGE_SHIFT; | pfn = phys_addr >> PAGE_SHIFT; | ||||
reqp->data_buf.pfn_array[i] = pfn; | reqp->data_buf.pfn_array[i] = pfn; | ||||
} | } | ||||
reqp->bounce_sgl_count = 0; | reqp->bounce_sgl_count = 0; | ||||
} | } | ||||
reqp->softc->sysctl_data.data_sg_cnt++; | |||||
break; | break; | ||||
} | } | ||||
default: | default: | ||||
printf("Unknow flags: %d\n", ccb->ccb_h.flags); | printf("Unknow flags: %d\n", ccb->ccb_h.flags); | ||||
return(EINVAL); | return(EINVAL); | ||||
} | } | ||||
return(0); | return(0); | ||||
▲ Show 20 Lines • Show All 229 Lines • Show Last 20 Lines |
Destroy the map; though it should be no-op in this case. And the maps could also be destroyed on storvsc_detach path.