Changeset View
Changeset View
Standalone View
Standalone View
head/sys/dev/hyperv/storvsc/hv_storvsc_drv_freebsd.c
Show First 20 Lines • Show All 69 Lines • ▼ Show 20 Lines | |||||
#include <cam/cam_xpt_sim.h> | #include <cam/cam_xpt_sim.h> | ||||
#include <cam/cam_xpt_internal.h> | #include <cam/cam_xpt_internal.h> | ||||
#include <cam/cam_debug.h> | #include <cam/cam_debug.h> | ||||
#include <cam/scsi/scsi_all.h> | #include <cam/scsi/scsi_all.h> | ||||
#include <cam/scsi/scsi_message.h> | #include <cam/scsi/scsi_message.h> | ||||
#include <dev/hyperv/include/hyperv.h> | #include <dev/hyperv/include/hyperv.h> | ||||
#include <dev/hyperv/include/vmbus.h> | #include <dev/hyperv/include/vmbus.h> | ||||
#include "hv_vstorage.h" | #include "hv_vstorage.h" | ||||
#include "vmbus_if.h" | #include "vmbus_if.h" | ||||
#define STORVSC_RINGBUFFER_SIZE (20*PAGE_SIZE) | |||||
#define STORVSC_MAX_LUNS_PER_TARGET (64) | #define STORVSC_MAX_LUNS_PER_TARGET (64) | ||||
#define STORVSC_MAX_IO_REQUESTS (STORVSC_MAX_LUNS_PER_TARGET * 2) | #define STORVSC_MAX_IO_REQUESTS (STORVSC_MAX_LUNS_PER_TARGET * 2) | ||||
#define BLKVSC_MAX_IDE_DISKS_PER_TARGET (1) | #define BLKVSC_MAX_IDE_DISKS_PER_TARGET (1) | ||||
#define BLKVSC_MAX_IO_REQUESTS STORVSC_MAX_IO_REQUESTS | #define BLKVSC_MAX_IO_REQUESTS STORVSC_MAX_IO_REQUESTS | ||||
#define STORVSC_MAX_TARGETS (2) | #define STORVSC_MAX_TARGETS (2) | ||||
#define VSTOR_PKT_SIZE (sizeof(struct vstor_packet) - vmscsi_size_delta) | #define VSTOR_PKT_SIZE (sizeof(struct vstor_packet) - vmscsi_size_delta) | ||||
Show All 25 Lines | |||||
}; | }; | ||||
struct hv_sgl_page_pool{ | struct hv_sgl_page_pool{ | ||||
LIST_HEAD(, hv_sgl_node) in_use_sgl_list; | LIST_HEAD(, hv_sgl_node) in_use_sgl_list; | ||||
LIST_HEAD(, hv_sgl_node) free_sgl_list; | LIST_HEAD(, hv_sgl_node) free_sgl_list; | ||||
boolean_t is_init; | boolean_t is_init; | ||||
} g_hv_sgl_page_pool; | } g_hv_sgl_page_pool; | ||||
#define STORVSC_MAX_SG_PAGE_CNT STORVSC_MAX_IO_REQUESTS * STORVSC_DATA_SEGCNT_MAX | |||||
enum storvsc_request_type { | enum storvsc_request_type { | ||||
WRITE_TYPE, | WRITE_TYPE, | ||||
READ_TYPE, | READ_TYPE, | ||||
UNKNOWN_TYPE | UNKNOWN_TYPE | ||||
}; | }; | ||||
SYSCTL_NODE(_hw, OID_AUTO, storvsc, CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, | SYSCTL_NODE(_hw, OID_AUTO, storvsc, CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, | ||||
"Hyper-V storage interface"); | "Hyper-V storage interface"); | ||||
static u_int hv_storvsc_use_win8ext_flags = 1; | |||||
SYSCTL_UINT(_hw_storvsc, OID_AUTO, use_win8ext_flags, CTLFLAG_RW, | |||||
&hv_storvsc_use_win8ext_flags, 0, | |||||
"Use win8 extension flags or not"); | |||||
static u_int hv_storvsc_use_pim_unmapped = 1; | static u_int hv_storvsc_use_pim_unmapped = 1; | ||||
SYSCTL_INT(_hw_storvsc, OID_AUTO, use_pim_unmapped, CTLFLAG_RDTUN, | SYSCTL_UINT(_hw_storvsc, OID_AUTO, use_pim_unmapped, CTLFLAG_RDTUN, | ||||
&hv_storvsc_use_pim_unmapped, 0, | &hv_storvsc_use_pim_unmapped, 0, | ||||
"Optimize storvsc by using unmapped I/O"); | "Optimize storvsc by using unmapped I/O"); | ||||
static u_int hv_storvsc_ringbuffer_size = (64 * PAGE_SIZE); | |||||
SYSCTL_UINT(_hw_storvsc, OID_AUTO, ringbuffer_size, CTLFLAG_RDTUN, | |||||
&hv_storvsc_ringbuffer_size, 0, "Hyper-V storage ringbuffer size"); | |||||
static u_int hv_storvsc_max_io = 512; | |||||
SYSCTL_UINT(_hw_storvsc, OID_AUTO, max_io, CTLFLAG_RDTUN, | |||||
&hv_storvsc_max_io, 0, "Hyper-V storage max io limit"); | |||||
#define STORVSC_MAX_IO \ | |||||
vmbus_chan_prplist_nelem(hv_storvsc_ringbuffer_size, \ | |||||
STORVSC_DATA_SEGCNT_MAX, VSTOR_PKT_SIZE) | |||||
struct hv_storvsc_sysctl { | struct hv_storvsc_sysctl { | ||||
u_long data_bio_cnt; | u_long data_bio_cnt; | ||||
u_long data_vaddr_cnt; | u_long data_vaddr_cnt; | ||||
u_long data_sg_cnt; | u_long data_sg_cnt; | ||||
u_long chan_send_cnt[MAXCPU]; | |||||
}; | }; | ||||
struct storvsc_gpa_range { | struct storvsc_gpa_range { | ||||
struct vmbus_gpa_range gpa_range; | struct vmbus_gpa_range gpa_range; | ||||
uint64_t gpa_page[STORVSC_DATA_SEGCNT_MAX]; | uint64_t gpa_page[STORVSC_DATA_SEGCNT_MAX]; | ||||
} __packed; | } __packed; | ||||
struct hv_storvsc_request { | struct hv_storvsc_request { | ||||
Show All 27 Lines | struct storvsc_softc { | ||||
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; | ||||
device_t hs_dev; | device_t hs_dev; | ||||
bus_dma_tag_t storvsc_req_dtag; | bus_dma_tag_t storvsc_req_dtag; | ||||
struct hv_storvsc_sysctl sysctl_data; | struct hv_storvsc_sysctl sysctl_data; | ||||
uint32_t hs_nchan; | |||||
struct vmbus_channel *hs_cpu2chan[MAXCPU]; | struct vmbus_channel *hs_sel_chan[MAXCPU]; | ||||
}; | }; | ||||
/* | |||||
* The size of the vmscsi_request has changed in win8. The | |||||
* additional size is for the newly added elements in the | |||||
* structure. These elements are valid only when we are talking | |||||
* to a win8 host. | |||||
* Track the correct size we need to apply. | |||||
*/ | |||||
static int vmscsi_size_delta = sizeof(struct vmscsi_win8_extension); | |||||
/** | /** | ||||
* 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 | ||||
* The first can be tested by "sg_senddiag -vv /dev/daX", | * The first can be tested by "sg_senddiag -vv /dev/daX", | ||||
* and the second and third can be done by | * and the second and third can be done by | ||||
* "sg_wr_mode -v -p 08 -c 0,1a -m 0,ff /dev/daX". | * "sg_wr_mode -v -p 08 -c 0,1a -m 0,ff /dev/daX". | ||||
*/ | */ | ||||
#define HVS_TIMEOUT_TEST 0 | #define HVS_TIMEOUT_TEST 0 | ||||
/* | /* | ||||
* Bus/adapter reset functionality on the Hyper-V host is | * Bus/adapter reset functionality on the Hyper-V host is | ||||
* buggy and it will be disabled until | * buggy and it will be disabled until | ||||
* it can be further tested. | * it can be further tested. | ||||
*/ | */ | ||||
#define HVS_HOST_RESET 0 | #define HVS_HOST_RESET 0 | ||||
struct storvsc_driver_props { | struct storvsc_driver_props { | ||||
char *drv_name; | char *drv_name; | ||||
char *drv_desc; | char *drv_desc; | ||||
uint8_t drv_max_luns_per_target; | uint8_t drv_max_luns_per_target; | ||||
uint8_t drv_max_ios_per_target; | uint32_t drv_max_ios_per_target; | ||||
uint32_t drv_ringbuffer_size; | uint32_t drv_ringbuffer_size; | ||||
}; | }; | ||||
enum hv_storage_type { | enum hv_storage_type { | ||||
DRIVER_BLKVSC, | DRIVER_BLKVSC, | ||||
DRIVER_STORVSC, | DRIVER_STORVSC, | ||||
DRIVER_UNKNOWN | DRIVER_UNKNOWN | ||||
}; | }; | ||||
Show All 12 Lines | |||||
static const struct hyperv_guid gBlkVscDeviceType={ | static const struct hyperv_guid gBlkVscDeviceType={ | ||||
.hv_guid = {0x32, 0x26, 0x41, 0x32, 0xcb, 0x86, 0xa2, 0x44, | .hv_guid = {0x32, 0x26, 0x41, 0x32, 0xcb, 0x86, 0xa2, 0x44, | ||||
0x9b, 0x5c, 0x50, 0xd1, 0x41, 0x73, 0x54, 0xf5} | 0x9b, 0x5c, 0x50, 0xd1, 0x41, 0x73, 0x54, 0xf5} | ||||
}; | }; | ||||
static struct storvsc_driver_props g_drv_props_table[] = { | static struct storvsc_driver_props g_drv_props_table[] = { | ||||
{"blkvsc", "Hyper-V IDE Storage Interface", | {"blkvsc", "Hyper-V IDE Storage Interface", | ||||
BLKVSC_MAX_IDE_DISKS_PER_TARGET, BLKVSC_MAX_IO_REQUESTS, | BLKVSC_MAX_IDE_DISKS_PER_TARGET, BLKVSC_MAX_IO_REQUESTS, | ||||
STORVSC_RINGBUFFER_SIZE}, | 20*PAGE_SIZE}, | ||||
{"storvsc", "Hyper-V SCSI Storage Interface", | {"storvsc", "Hyper-V SCSI Storage Interface", | ||||
STORVSC_MAX_LUNS_PER_TARGET, STORVSC_MAX_IO_REQUESTS, | STORVSC_MAX_LUNS_PER_TARGET, STORVSC_MAX_IO_REQUESTS, | ||||
STORVSC_RINGBUFFER_SIZE} | 20*PAGE_SIZE} | ||||
}; | }; | ||||
/* | /* | ||||
* Sense buffer size changed in win8; have a run-time | * Sense buffer size changed in win8; have a run-time | ||||
* variable to track the size we should use. | * variable to track the size we should use. | ||||
*/ | */ | ||||
static int sense_buffer_size = PRE_WIN8_STORVSC_SENSE_BUFFER_SIZE; | static int sense_buffer_size = PRE_WIN8_STORVSC_SENSE_BUFFER_SIZE; | ||||
/* | /* | ||||
* The size of the vmscsi_request has changed in win8. The | |||||
* additional size is for the newly added elements in the | |||||
* structure. These elements are valid only when we are talking | |||||
* to a win8 host. | |||||
* Track the correct size we need to apply. | |||||
*/ | |||||
static int vmscsi_size_delta; | |||||
/* | |||||
* The storage protocol version is determined during the | * The storage protocol version is determined during the | ||||
* initial exchange with the host. It will indicate which | * initial exchange with the host. It will indicate which | ||||
* storage functionality is available in the host. | * storage functionality is available in the host. | ||||
*/ | */ | ||||
static int vmstor_proto_version; | static int vmstor_proto_version; | ||||
struct vmstor_proto { | struct vmstor_proto { | ||||
int proto_version; | int proto_version; | ||||
▲ Show 20 Lines • Show All 136 Lines • ▼ Show 20 Lines | storvsc_send_multichannel_request(struct storvsc_softc *sc, int max_chans) | ||||
if (vstor_packet->operation != VSTOR_OPERATION_COMPLETEIO || | if (vstor_packet->operation != VSTOR_OPERATION_COMPLETEIO || | ||||
vstor_packet->status != 0) { | vstor_packet->status != 0) { | ||||
printf("Storvsc_error: create multi-channel invalid operation " | printf("Storvsc_error: create multi-channel invalid operation " | ||||
"(%d) or statue (%u)\n", | "(%d) or statue (%u)\n", | ||||
vstor_packet->operation, vstor_packet->status); | vstor_packet->operation, vstor_packet->status); | ||||
return; | return; | ||||
} | } | ||||
/* Update channel count */ | |||||
sc->hs_nchan = request_channels_cnt + 1; | |||||
/* Wait for sub-channels setup to complete. */ | /* Wait for sub-channels setup to complete. */ | ||||
subchan = vmbus_subchan_get(sc->hs_chan, request_channels_cnt); | subchan = vmbus_subchan_get(sc->hs_chan, request_channels_cnt); | ||||
/* Attach the sub-channels. */ | /* Attach the sub-channels. */ | ||||
for (i = 0; i < request_channels_cnt; ++i) | for (i = 0; i < request_channels_cnt; ++i) | ||||
storvsc_subchan_attach(sc, subchan[i]); | storvsc_subchan_attach(sc, subchan[i]); | ||||
/* Release the sub-channels. */ | /* Release the sub-channels. */ | ||||
▲ Show 20 Lines • Show All 156 Lines • ▼ Show 20 Lines | if (vstor_packet->operation != VSTOR_OPERATION_COMPLETEIO || | ||||
goto cleanup; | goto cleanup; | ||||
/* | /* | ||||
* If multi-channel is supported, send multichannel create | * If multi-channel is supported, send multichannel create | ||||
* request to host. | * request to host. | ||||
*/ | */ | ||||
if (support_multichannel) | if (support_multichannel) | ||||
storvsc_send_multichannel_request(sc, max_chans); | storvsc_send_multichannel_request(sc, max_chans); | ||||
cleanup: | cleanup: | ||||
sema_destroy(&request->synch_sema); | sema_destroy(&request->synch_sema); | ||||
return (ret); | return (ret); | ||||
} | } | ||||
/** | /** | ||||
* @brief Open channel connection to paraent partition StorVSP driver | * @brief Open channel connection to paraent partition StorVSP driver | ||||
* | * | ||||
Show All 22 Lines | ret = vmbus_chan_open( | ||||
sizeof(struct vmstor_chan_props), | sizeof(struct vmstor_chan_props), | ||||
hv_storvsc_on_channel_callback, sc); | hv_storvsc_on_channel_callback, sc); | ||||
if (ret != 0) { | if (ret != 0) { | ||||
return ret; | return ret; | ||||
} | } | ||||
ret = hv_storvsc_channel_init(sc); | ret = hv_storvsc_channel_init(sc); | ||||
return (ret); | return (ret); | ||||
} | } | ||||
#if HVS_HOST_RESET | #if HVS_HOST_RESET | ||||
static int | static int | ||||
hv_storvsc_host_reset(struct storvsc_softc *sc) | hv_storvsc_host_reset(struct storvsc_softc *sc) | ||||
{ | { | ||||
int ret = 0; | int ret = 0; | ||||
▲ Show 20 Lines • Show All 45 Lines • ▼ Show 20 Lines | |||||
* @returns 0 on success, non-zero error on failure | * @returns 0 on success, non-zero error on failure | ||||
*/ | */ | ||||
static int | static int | ||||
hv_storvsc_io_request(struct storvsc_softc *sc, | hv_storvsc_io_request(struct storvsc_softc *sc, | ||||
struct hv_storvsc_request *request) | struct hv_storvsc_request *request) | ||||
{ | { | ||||
struct vstor_packet *vstor_packet = &request->vstor_packet; | struct vstor_packet *vstor_packet = &request->vstor_packet; | ||||
struct vmbus_channel* outgoing_channel = NULL; | struct vmbus_channel* outgoing_channel = NULL; | ||||
int ret = 0; | int ret = 0, ch_sel; | ||||
vstor_packet->flags |= REQUEST_COMPLETION_FLAG; | vstor_packet->flags |= REQUEST_COMPLETION_FLAG; | ||||
vstor_packet->u.vm_srb.length = VSTOR_PKT_SIZE; | vstor_packet->u.vm_srb.length = VSTOR_PKT_SIZE; | ||||
vstor_packet->u.vm_srb.sense_info_len = sense_buffer_size; | vstor_packet->u.vm_srb.sense_info_len = sense_buffer_size; | ||||
vstor_packet->u.vm_srb.transfer_len = | vstor_packet->u.vm_srb.transfer_len = | ||||
request->prp_list.gpa_range.gpa_len; | request->prp_list.gpa_range.gpa_len; | ||||
vstor_packet->operation = VSTOR_OPERATION_EXECUTESRB; | vstor_packet->operation = VSTOR_OPERATION_EXECUTESRB; | ||||
outgoing_channel = sc->hs_cpu2chan[curcpu]; | ch_sel = (vstor_packet->u.vm_srb.lun + curcpu) % sc->hs_nchan; | ||||
outgoing_channel = sc->hs_sel_chan[ch_sel]; | |||||
mtx_unlock(&request->softc->hs_lock); | mtx_unlock(&request->softc->hs_lock); | ||||
if (request->prp_list.gpa_range.gpa_len) { | if (request->prp_list.gpa_range.gpa_len) { | ||||
ret = vmbus_chan_send_prplist(outgoing_channel, | ret = vmbus_chan_send_prplist(outgoing_channel, | ||||
&request->prp_list.gpa_range, request->prp_cnt, | &request->prp_list.gpa_range, request->prp_cnt, | ||||
vstor_packet, VSTOR_PKT_SIZE, (uint64_t)(uintptr_t)request); | vstor_packet, VSTOR_PKT_SIZE, (uint64_t)(uintptr_t)request); | ||||
} else { | } else { | ||||
ret = vmbus_chan_send(outgoing_channel, | ret = vmbus_chan_send(outgoing_channel, | ||||
VMBUS_CHANPKT_TYPE_INBAND, VMBUS_CHANPKT_FLAG_RC, | VMBUS_CHANPKT_TYPE_INBAND, VMBUS_CHANPKT_FLAG_RC, | ||||
vstor_packet, VSTOR_PKT_SIZE, (uint64_t)(uintptr_t)request); | vstor_packet, VSTOR_PKT_SIZE, (uint64_t)(uintptr_t)request); | ||||
} | } | ||||
/* statistic for successful request sending on each channel */ | |||||
if (!ret) { | |||||
sc->sysctl_data.chan_send_cnt[ch_sel]++; | |||||
} | |||||
mtx_lock(&request->softc->hs_lock); | mtx_lock(&request->softc->hs_lock); | ||||
if (ret != 0) { | if (ret != 0) { | ||||
printf("Unable to send packet %p ret %d", vstor_packet, ret); | printf("Unable to send packet %p ret %d", vstor_packet, ret); | ||||
} else { | } else { | ||||
atomic_add_int(&sc->hs_num_out_reqs, 1); | atomic_add_int(&sc->hs_num_out_reqs, 1); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 179 Lines • ▼ Show 20 Lines | case DRIVER_STORVSC: | ||||
break; | break; | ||||
default: | default: | ||||
ret = ENXIO; | ret = ENXIO; | ||||
} | } | ||||
return (ret); | return (ret); | ||||
} | } | ||||
static void | static void | ||||
storvsc_create_cpu2chan(struct storvsc_softc *sc) | storvsc_create_chan_sel(struct storvsc_softc *sc) | ||||
{ | { | ||||
int cpu; | struct vmbus_channel **subch; | ||||
int i, nsubch; | |||||
CPU_FOREACH(cpu) { | sc->hs_sel_chan[0] = sc->hs_chan; | ||||
sc->hs_cpu2chan[cpu] = vmbus_chan_cpu2chan(sc->hs_chan, cpu); | nsubch = sc->hs_nchan - 1; | ||||
if (bootverbose) { | if (nsubch == 0) | ||||
device_printf(sc->hs_dev, "cpu%d -> chan%u\n", | return; | ||||
cpu, vmbus_chan_id(sc->hs_cpu2chan[cpu])); | |||||
subch = vmbus_subchan_get(sc->hs_chan, nsubch); | |||||
for (i = 0; i < nsubch; i++) | |||||
sc->hs_sel_chan[i + 1] = subch[i]; | |||||
vmbus_subchan_rel(subch, nsubch); | |||||
} | } | ||||
} | |||||
} | |||||
static int | static int | ||||
storvsc_init_requests(device_t dev) | storvsc_init_requests(device_t dev) | ||||
{ | { | ||||
struct storvsc_softc *sc = device_get_softc(dev); | struct storvsc_softc *sc = device_get_softc(dev); | ||||
struct hv_storvsc_request *reqp; | struct hv_storvsc_request *reqp; | ||||
int error, i; | int error, i; | ||||
▲ Show 20 Lines • Show All 42 Lines • ▼ Show 20 Lines | cleanup: | ||||
return (error); | return (error); | ||||
} | } | ||||
static void | static void | ||||
storvsc_sysctl(device_t dev) | storvsc_sysctl(device_t dev) | ||||
{ | { | ||||
struct sysctl_oid_list *child; | struct sysctl_oid_list *child; | ||||
struct sysctl_ctx_list *ctx; | struct sysctl_ctx_list *ctx; | ||||
struct sysctl_oid *ch_tree, *chid_tree; | |||||
struct storvsc_softc *sc; | struct storvsc_softc *sc; | ||||
char name[16]; | |||||
int i; | |||||
sc = device_get_softc(dev); | sc = device_get_softc(dev); | ||||
ctx = device_get_sysctl_ctx(dev); | ctx = device_get_sysctl_ctx(dev); | ||||
child = SYSCTL_CHILDREN(device_get_sysctl_tree(dev)); | child = SYSCTL_CHILDREN(device_get_sysctl_tree(dev)); | ||||
SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "data_bio_cnt", CTLFLAG_RW, | SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "data_bio_cnt", CTLFLAG_RW, | ||||
&sc->sysctl_data.data_bio_cnt, "# of bio data block"); | &sc->sysctl_data.data_bio_cnt, "# of bio data block"); | ||||
SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "data_vaddr_cnt", CTLFLAG_RW, | SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "data_vaddr_cnt", CTLFLAG_RW, | ||||
&sc->sysctl_data.data_vaddr_cnt, "# of vaddr data block"); | &sc->sysctl_data.data_vaddr_cnt, "# of vaddr data block"); | ||||
SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "data_sg_cnt", CTLFLAG_RW, | SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "data_sg_cnt", CTLFLAG_RW, | ||||
&sc->sysctl_data.data_sg_cnt, "# of sg data block"); | &sc->sysctl_data.data_sg_cnt, "# of sg data block"); | ||||
/* dev.storvsc.UNIT.channel */ | |||||
ch_tree = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "channel", | |||||
CTLFLAG_RD | CTLFLAG_MPSAFE, 0, ""); | |||||
if (ch_tree == NULL) | |||||
return; | |||||
for (i = 0; i < sc->hs_nchan; i++) { | |||||
uint32_t ch_id; | |||||
ch_id = vmbus_chan_id(sc->hs_sel_chan[i]); | |||||
snprintf(name, sizeof(name), "%d", ch_id); | |||||
/* dev.storvsc.UNIT.channel.CHID */ | |||||
chid_tree = SYSCTL_ADD_NODE(ctx, SYSCTL_CHILDREN(ch_tree), | |||||
OID_AUTO, name, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, ""); | |||||
if (chid_tree == NULL) | |||||
return; | |||||
/* dev.storvsc.UNIT.channel.CHID.send_req */ | |||||
SYSCTL_ADD_ULONG(ctx, SYSCTL_CHILDREN(chid_tree), OID_AUTO, | |||||
"send_req", CTLFLAG_RD, &sc->sysctl_data.chan_send_cnt[i], | |||||
"# of request sending from this channel"); | |||||
} | } | ||||
} | |||||
/** | /** | ||||
* @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. | ||||
* | * | ||||
Show All 13 Lines | storvsc_attach(device_t dev) | ||||
void *tmp_buff = NULL; | void *tmp_buff = NULL; | ||||
/* | /* | ||||
* We need to serialize storvsc attach calls. | * We need to serialize storvsc attach calls. | ||||
*/ | */ | ||||
root_mount_token = root_mount_hold("storvsc"); | root_mount_token = root_mount_hold("storvsc"); | ||||
sc = device_get_softc(dev); | sc = device_get_softc(dev); | ||||
sc->hs_nchan = 1; | |||||
sc->hs_chan = vmbus_get_channel(dev); | sc->hs_chan = vmbus_get_channel(dev); | ||||
stor_type = storvsc_get_storage_type(dev); | stor_type = storvsc_get_storage_type(dev); | ||||
if (stor_type == DRIVER_UNKNOWN) { | if (stor_type == DRIVER_UNKNOWN) { | ||||
ret = ENODEV; | ret = ENODEV; | ||||
goto cleanup; | goto cleanup; | ||||
} | } | ||||
/* 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]; | ||||
sc->hs_drv_props->drv_ringbuffer_size = hv_storvsc_ringbuffer_size; | |||||
sc->hs_drv_props->drv_max_ios_per_target = | |||||
MIN(STORVSC_MAX_IO, hv_storvsc_max_io); | |||||
if (bootverbose) { | |||||
printf("storvsc ringbuffer size: %d, max_io: %d\n", | |||||
sc->hs_drv_props->drv_ringbuffer_size, | |||||
sc->hs_drv_props->drv_max_ios_per_target); | |||||
} | |||||
/* 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 = dev; | sc->hs_dev = dev; | ||||
mtx_init(&sc->hs_lock, "hvslck", NULL, MTX_DEF); | mtx_init(&sc->hs_lock, "hvslck", NULL, MTX_DEF); | ||||
ret = storvsc_init_requests(dev); | ret = storvsc_init_requests(dev); | ||||
if (ret != 0) | if (ret != 0) | ||||
goto cleanup; | goto cleanup; | ||||
/* 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); | ||||
/* | /* | ||||
* Pre-create SG list, each SG list with | * Pre-create SG list, each SG list with | ||||
* STORVSC_DATA_SEGCNT_MAX segments, each | * STORVSC_DATA_SEGCNT_MAX segments, each | ||||
* segment has one page buffer | * segment has one page buffer | ||||
*/ | */ | ||||
for (i = 0; i < STORVSC_MAX_IO_REQUESTS; i++) { | for (i = 0; i < sc->hs_drv_props->drv_max_ios_per_target; i++) { | ||||
sgl_node = malloc(sizeof(struct hv_sgl_node), | sgl_node = malloc(sizeof(struct hv_sgl_node), | ||||
M_DEVBUF, M_WAITOK|M_ZERO); | M_DEVBUF, M_WAITOK|M_ZERO); | ||||
sgl_node->sgl_data = | sgl_node->sgl_data = | ||||
sglist_alloc(STORVSC_DATA_SEGCNT_MAX, | sglist_alloc(STORVSC_DATA_SEGCNT_MAX, | ||||
M_WAITOK|M_ZERO); | M_WAITOK|M_ZERO); | ||||
for (j = 0; j < STORVSC_DATA_SEGCNT_MAX; j++) { | for (j = 0; j < STORVSC_DATA_SEGCNT_MAX; j++) { | ||||
Show All 14 Lines | storvsc_attach(device_t dev) | ||||
sema_init(&sc->hs_drain_sema, 0, "Store Drain Sema"); | sema_init(&sc->hs_drain_sema, 0, "Store Drain Sema"); | ||||
ret = hv_storvsc_connect_vsp(sc); | ret = hv_storvsc_connect_vsp(sc); | ||||
if (ret != 0) { | if (ret != 0) { | ||||
goto cleanup; | goto cleanup; | ||||
} | } | ||||
/* Construct cpu to channel mapping */ | /* Construct cpu to channel mapping */ | ||||
storvsc_create_cpu2chan(sc); | storvsc_create_chan_sel(sc); | ||||
/* | /* | ||||
* Create the device queue. | * Create the device queue. | ||||
* Hyper-V maps each target to one SCSI HBA | * Hyper-V maps each target to one SCSI HBA | ||||
*/ | */ | ||||
devq = cam_simq_alloc(sc->hs_drv_props->drv_max_ios_per_target); | devq = cam_simq_alloc(sc->hs_drv_props->drv_max_ios_per_target); | ||||
if (devq == NULL) { | if (devq == NULL) { | ||||
device_printf(dev, "Failed to alloc device queue\n"); | device_printf(dev, "Failed to alloc device queue\n"); | ||||
▲ Show 20 Lines • Show All 740 Lines • ▼ Show 20 Lines | create_storvsc_request(union ccb *ccb, struct hv_storvsc_request *reqp) | ||||
if(ccb->ccb_h.flags & CAM_CDB_POINTER) { | if(ccb->ccb_h.flags & CAM_CDB_POINTER) { | ||||
memcpy(&reqp->vstor_packet.u.vm_srb.u.cdb, csio->cdb_io.cdb_ptr, | memcpy(&reqp->vstor_packet.u.vm_srb.u.cdb, csio->cdb_io.cdb_ptr, | ||||
csio->cdb_len); | csio->cdb_len); | ||||
} else { | } else { | ||||
memcpy(&reqp->vstor_packet.u.vm_srb.u.cdb, csio->cdb_io.cdb_bytes, | memcpy(&reqp->vstor_packet.u.vm_srb.u.cdb, csio->cdb_io.cdb_bytes, | ||||
csio->cdb_len); | csio->cdb_len); | ||||
} | } | ||||
if (hv_storvsc_use_win8ext_flags) { | |||||
reqp->vstor_packet.u.vm_srb.win8_extension.time_out_value = 60; | |||||
reqp->vstor_packet.u.vm_srb.win8_extension.srb_flags |= | |||||
SRB_FLAGS_DISABLE_SYNCH_TRANSFER; | |||||
} | |||||
switch (ccb->ccb_h.flags & CAM_DIR_MASK) { | switch (ccb->ccb_h.flags & CAM_DIR_MASK) { | ||||
case CAM_DIR_OUT: | case CAM_DIR_OUT: | ||||
reqp->vstor_packet.u.vm_srb.data_in = WRITE_TYPE; | reqp->vstor_packet.u.vm_srb.data_in = WRITE_TYPE; | ||||
if (hv_storvsc_use_win8ext_flags) { | |||||
reqp->vstor_packet.u.vm_srb.win8_extension.srb_flags |= | |||||
SRB_FLAGS_DATA_OUT; | |||||
} | |||||
break; | break; | ||||
case CAM_DIR_IN: | case CAM_DIR_IN: | ||||
reqp->vstor_packet.u.vm_srb.data_in = READ_TYPE; | reqp->vstor_packet.u.vm_srb.data_in = READ_TYPE; | ||||
if (hv_storvsc_use_win8ext_flags) { | |||||
reqp->vstor_packet.u.vm_srb.win8_extension.srb_flags |= | |||||
SRB_FLAGS_DATA_IN; | |||||
} | |||||
break; | break; | ||||
case CAM_DIR_NONE: | case CAM_DIR_NONE: | ||||
reqp->vstor_packet.u.vm_srb.data_in = UNKNOWN_TYPE; | reqp->vstor_packet.u.vm_srb.data_in = UNKNOWN_TYPE; | ||||
if (hv_storvsc_use_win8ext_flags) { | |||||
reqp->vstor_packet.u.vm_srb.win8_extension.srb_flags |= | |||||
SRB_FLAGS_NO_DATA_TRANSFER; | |||||
} | |||||
break; | break; | ||||
default: | default: | ||||
reqp->vstor_packet.u.vm_srb.data_in = UNKNOWN_TYPE; | printf("Error: unexpected data direction: 0x%x\n", | ||||
break; | ccb->ccb_h.flags & CAM_DIR_MASK); | ||||
return (EINVAL); | |||||
} | } | ||||
reqp->sense_data = &csio->sense_data; | reqp->sense_data = &csio->sense_data; | ||||
reqp->sense_info_len = csio->sense_len; | reqp->sense_info_len = csio->sense_len; | ||||
reqp->ccb = ccb; | reqp->ccb = ccb; | ||||
if (0 == csio->dxfer_len) { | if (0 == csio->dxfer_len) { | ||||
▲ Show 20 Lines • Show All 352 Lines • Show Last 20 Lines |