Changeset View
Changeset View
Standalone View
Standalone View
sys/dev/hyperv/storvsc/hv_storvsc_drv_freebsd.c
Show First 20 Lines • Show All 54 Lines • ▼ Show 20 Lines | |||||
#include <sys/callout.h> | #include <sys/callout.h> | ||||
#include <sys/smp.h> | #include <sys/smp.h> | ||||
#include <vm/vm.h> | #include <vm/vm.h> | ||||
#include <vm/pmap.h> | #include <vm/pmap.h> | ||||
#include <vm/uma.h> | #include <vm/uma.h> | ||||
#include <sys/lock.h> | #include <sys/lock.h> | ||||
#include <sys/sema.h> | #include <sys/sema.h> | ||||
#include <sys/sglist.h> | #include <sys/sglist.h> | ||||
#include <sys/eventhandler.h> | |||||
#include <machine/bus.h> | #include <machine/bus.h> | ||||
#include <sys/bus_dma.h> | #include <sys/bus_dma.h> | ||||
#include <cam/cam.h> | #include <cam/cam.h> | ||||
#include <cam/cam_ccb.h> | #include <cam/cam_ccb.h> | ||||
#include <cam/cam_periph.h> | #include <cam/cam_periph.h> | ||||
#include <cam/cam_sim.h> | #include <cam/cam_sim.h> | ||||
#include <cam/cam_xpt_sim.h> | #include <cam/cam_xpt_sim.h> | ||||
▲ Show 20 Lines • Show All 126 Lines • ▼ Show 20 Lines | struct storvsc_softc { | ||||
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; | uint32_t hs_nchan; | ||||
struct vmbus_channel *hs_sel_chan[MAXCPU]; | struct vmbus_channel *hs_sel_chan[MAXCPU]; | ||||
}; | }; | ||||
static eventhandler_tag storvsc_handler_tag; | |||||
/* | /* | ||||
* The size of the vmscsi_request has changed in win8. The | * The size of the vmscsi_request has changed in win8. The | ||||
* additional size is for the newly added elements in the | * additional size is for the newly added elements in the | ||||
* structure. These elements are valid only when we are talking | * structure. These elements are valid only when we are talking | ||||
* to a win8 host. | * to a win8 host. | ||||
* Track the correct size we need to apply. | * Track the correct size we need to apply. | ||||
*/ | */ | ||||
static int vmscsi_size_delta = sizeof(struct vmscsi_win8_extension); | static int vmscsi_size_delta = sizeof(struct vmscsi_win8_extension); | ||||
▲ Show 20 Lines • Show All 679 Lines • ▼ Show 20 Lines | |||||
* favor of the emulated ATA/IDE device, return ENXIO. | * favor of the emulated ATA/IDE device, return ENXIO. | ||||
* | * | ||||
* @param a device | * @param a device | ||||
* @returns 0 on success, ENXIO if not a matcing StorVSC device | * @returns 0 on success, ENXIO if not a matcing StorVSC device | ||||
*/ | */ | ||||
static int | static int | ||||
storvsc_probe(device_t dev) | storvsc_probe(device_t dev) | ||||
{ | { | ||||
int ata_disk_enable = 0; | |||||
int ret = ENXIO; | int ret = ENXIO; | ||||
switch (storvsc_get_storage_type(dev)) { | switch (storvsc_get_storage_type(dev)) { | ||||
case DRIVER_BLKVSC: | case DRIVER_BLKVSC: | ||||
if(bootverbose) | if(bootverbose) | ||||
device_printf(dev, "DRIVER_BLKVSC-Emulated ATA/IDE probe\n"); | |||||
if (!getenv_int("hw.ata.disk_enable", &ata_disk_enable)) { | |||||
if(bootverbose) | |||||
device_printf(dev, | device_printf(dev, | ||||
"Enlightened ATA/IDE detected\n"); | "Enlightened ATA/IDE detected\n"); | ||||
device_set_desc(dev, g_drv_props_table[DRIVER_BLKVSC].drv_desc); | device_set_desc(dev, g_drv_props_table[DRIVER_BLKVSC].drv_desc); | ||||
ret = BUS_PROBE_DEFAULT; | ret = BUS_PROBE_DEFAULT; | ||||
} else if(bootverbose) | |||||
device_printf(dev, "Emulated ATA/IDE set (hw.ata.disk_enable set)\n"); | |||||
break; | break; | ||||
case DRIVER_STORVSC: | case DRIVER_STORVSC: | ||||
if(bootverbose) | if(bootverbose) | ||||
device_printf(dev, "Enlightened SCSI device detected\n"); | device_printf(dev, "Enlightened SCSI device detected\n"); | ||||
device_set_desc(dev, g_drv_props_table[DRIVER_STORVSC].drv_desc); | device_set_desc(dev, g_drv_props_table[DRIVER_STORVSC].drv_desc); | ||||
ret = BUS_PROBE_DEFAULT; | ret = BUS_PROBE_DEFAULT; | ||||
break; | break; | ||||
default: | default: | ||||
▲ Show 20 Lines • Show All 1,226 Lines • ▼ Show 20 Lines | if (ccb->ccb_h.timeout != CAM_TIME_INFINITY) { | ||||
callout_drain(&reqp->callout); | callout_drain(&reqp->callout); | ||||
} | } | ||||
#endif | #endif | ||||
ccb->ccb_h.status &= ~CAM_SIM_QUEUED; | ccb->ccb_h.status &= ~CAM_SIM_QUEUED; | ||||
ccb->ccb_h.status &= ~CAM_STATUS_MASK; | ccb->ccb_h.status &= ~CAM_STATUS_MASK; | ||||
if (vm_srb->scsi_status == SCSI_STATUS_OK) { | if (vm_srb->scsi_status == SCSI_STATUS_OK) { | ||||
const struct scsi_generic *cmd; | const struct scsi_generic *cmd; | ||||
cmd = (const struct scsi_generic *) | |||||
((ccb->ccb_h.flags & CAM_CDB_POINTER) ? | |||||
csio->cdb_io.cdb_ptr : csio->cdb_io.cdb_bytes); | |||||
if (vm_srb->srb_status != SRB_STATUS_SUCCESS) { | if (vm_srb->srb_status != SRB_STATUS_SUCCESS) { | ||||
/* | |||||
* If there are errors, for example, invalid LUN, | |||||
* host will inform VM through SRB status. | |||||
*/ | |||||
if (bootverbose) { | |||||
mtx_lock(&sc->hs_lock); | |||||
if (vm_srb->srb_status == SRB_STATUS_INVALID_LUN) { | if (vm_srb->srb_status == SRB_STATUS_INVALID_LUN) { | ||||
xpt_print(ccb->ccb_h.path, "invalid LUN %d\n", | xpt_print(ccb->ccb_h.path, | ||||
vm_srb->lun); | "invalid LUN %d for op: %s\n", | ||||
vm_srb->lun, | |||||
scsi_op_desc(cmd->opcode, NULL)); | |||||
} else { | } else { | ||||
xpt_print(ccb->ccb_h.path, "Unknown SRB flag: %d\n", | xpt_print(ccb->ccb_h.path, | ||||
vm_srb->srb_status); | "Unknown SRB flag: %d for op: %s\n", | ||||
vm_srb->srb_status, | |||||
scsi_op_desc(cmd->opcode, NULL)); | |||||
} | } | ||||
mtx_unlock(&sc->hs_lock); | |||||
} | |||||
/* | /* | ||||
* If there are errors, for example, invalid LUN, | * XXX For a selection timeout, all of the LUNs | ||||
* host will inform VM through SRB status. | * on the target will be gone. It works for SCSI | ||||
* disks, but does not work for IDE disks. | |||||
* | |||||
* For CAM_DEV_NOT_THERE, CAM will only get | |||||
* rid of the device(s) specified by the path. | |||||
*/ | */ | ||||
if (storvsc_get_storage_type(sc->hs_dev) == | |||||
DRIVER_STORVSC) { | |||||
ccb->ccb_h.status |= CAM_SEL_TIMEOUT; | ccb->ccb_h.status |= CAM_SEL_TIMEOUT; | ||||
} else { | } else { | ||||
ccb->ccb_h.status |= CAM_DEV_NOT_THERE; | |||||
} | |||||
} else { | |||||
ccb->ccb_h.status |= CAM_REQ_CMP; | ccb->ccb_h.status |= CAM_REQ_CMP; | ||||
} | } | ||||
cmd = (const struct scsi_generic *) | |||||
((ccb->ccb_h.flags & CAM_CDB_POINTER) ? | |||||
csio->cdb_io.cdb_ptr : csio->cdb_io.cdb_bytes); | |||||
if (cmd->opcode == INQUIRY) { | if (cmd->opcode == INQUIRY) { | ||||
struct scsi_inquiry_data *inq_data = | struct scsi_inquiry_data *inq_data = | ||||
(struct scsi_inquiry_data *)csio->data_ptr; | (struct scsi_inquiry_data *)csio->data_ptr; | ||||
uint8_t *resp_buf = (uint8_t *)csio->data_ptr; | uint8_t *resp_buf = (uint8_t *)csio->data_ptr; | ||||
int resp_xfer_len, resp_buf_len, data_len; | int resp_xfer_len, resp_buf_len, data_len; | ||||
/* Get the buffer length reported by host */ | /* Get the buffer length reported by host */ | ||||
resp_xfer_len = vm_srb->transfer_len; | resp_xfer_len = vm_srb->transfer_len; | ||||
/* Get the available buffer length */ | /* Get the available buffer length */ | ||||
resp_buf_len = resp_xfer_len >= 5 ? resp_buf[4] + 5 : 0; | resp_buf_len = resp_xfer_len >= 5 ? resp_buf[4] + 5 : 0; | ||||
data_len = (resp_buf_len < resp_xfer_len) ? | data_len = (resp_buf_len < resp_xfer_len) ? | ||||
resp_buf_len : resp_xfer_len; | resp_buf_len : resp_xfer_len; | ||||
if (bootverbose && data_len >= 5) { | if (bootverbose && data_len >= 5) { | ||||
mtx_lock(&sc->hs_lock); | |||||
sepherosa_gmail.com: This lock is actually not useful for xpt_print() | |||||
xpt_print(ccb->ccb_h.path, "storvsc inquiry " | xpt_print(ccb->ccb_h.path, "storvsc inquiry " | ||||
"(%d) [%x %x %x %x %x ... ]\n", data_len, | "(%d) [%x %x %x %x %x ... ]\n", data_len, | ||||
resp_buf[0], resp_buf[1], resp_buf[2], | resp_buf[0], resp_buf[1], resp_buf[2], | ||||
resp_buf[3], resp_buf[4]); | resp_buf[3], resp_buf[4]); | ||||
mtx_unlock(&sc->hs_lock); | |||||
} | } | ||||
if (vm_srb->srb_status == SRB_STATUS_SUCCESS && | if (vm_srb->srb_status == SRB_STATUS_SUCCESS && | ||||
data_len > SHORT_INQUIRY_LENGTH) { | data_len > SHORT_INQUIRY_LENGTH) { | ||||
char vendor[16]; | char vendor[16]; | ||||
cam_strvis(vendor, inq_data->vendor, | cam_strvis(vendor, inq_data->vendor, | ||||
sizeof(inq_data->vendor), sizeof(vendor)); | sizeof(inq_data->vendor), sizeof(vendor)); | ||||
/* | /* | ||||
* XXX: Upgrade SPC2 to SPC3 if host is WIN8 or | * XXX: Upgrade SPC2 to SPC3 if host is WIN8 or | ||||
* WIN2012 R2 in order to support UNMAP feature. | * WIN2012 R2 in order to support UNMAP feature. | ||||
*/ | */ | ||||
if (!strncmp(vendor, "Msft", 4) && | if (!strncmp(vendor, "Msft", 4) && | ||||
SID_ANSI_REV(inq_data) == SCSI_REV_SPC2 && | SID_ANSI_REV(inq_data) == SCSI_REV_SPC2 && | ||||
(vmstor_proto_version == | (vmstor_proto_version == | ||||
VMSTOR_PROTOCOL_VERSION_WIN8_1 || | VMSTOR_PROTOCOL_VERSION_WIN8_1 || | ||||
vmstor_proto_version == | vmstor_proto_version == | ||||
VMSTOR_PROTOCOL_VERSION_WIN8)) { | VMSTOR_PROTOCOL_VERSION_WIN8)) { | ||||
inq_data->version = SCSI_REV_SPC3; | inq_data->version = SCSI_REV_SPC3; | ||||
if (bootverbose) { | if (bootverbose) { | ||||
mtx_lock(&sc->hs_lock); | |||||
xpt_print(ccb->ccb_h.path, | xpt_print(ccb->ccb_h.path, | ||||
"storvsc upgrades " | "storvsc upgrades " | ||||
"SPC2 to SPC3\n"); | "SPC2 to SPC3\n"); | ||||
mtx_unlock(&sc->hs_lock); | |||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} else { | } else { | ||||
mtx_lock(&sc->hs_lock); | mtx_lock(&sc->hs_lock); | ||||
xpt_print(ccb->ccb_h.path, | xpt_print(ccb->ccb_h.path, | ||||
"storvsc scsi_status = %d\n", | "storvsc scsi_status = %d\n", | ||||
▲ Show 20 Lines • Show All 54 Lines • ▼ Show 20 Lines | storvsc_get_storage_type(device_t dev) | ||||
device_t parent = device_get_parent(dev); | device_t parent = device_get_parent(dev); | ||||
if (VMBUS_PROBE_GUID(parent, dev, &gBlkVscDeviceType) == 0) | if (VMBUS_PROBE_GUID(parent, dev, &gBlkVscDeviceType) == 0) | ||||
return DRIVER_BLKVSC; | return DRIVER_BLKVSC; | ||||
if (VMBUS_PROBE_GUID(parent, dev, &gStorVscDeviceType) == 0) | if (VMBUS_PROBE_GUID(parent, dev, &gStorVscDeviceType) == 0) | ||||
return DRIVER_STORVSC; | return DRIVER_STORVSC; | ||||
return DRIVER_UNKNOWN; | return DRIVER_UNKNOWN; | ||||
} | } | ||||
#define PCI_VENDOR_INTEL 0x8086 | |||||
#define PCI_PRODUCT_PIIX4 0x7111 | |||||
static void | |||||
storvsc_ada_probe_veto(void *arg __unused, struct cam_path *path, | |||||
struct ata_params *ident_buf __unused, int *veto) | |||||
{ | |||||
/* | |||||
* XXX: VM for Hyper-V should ignore ATA | |||||
*/ | |||||
if (path->device->protocol == PROTO_ATA) { | |||||
struct ccb_pathinq cpi; | |||||
bzero(&cpi, sizeof(cpi)); | |||||
xpt_setup_ccb(&cpi.ccb_h, path, CAM_PRIORITY_NONE); | |||||
cpi.ccb_h.func_code = XPT_PATH_INQ; | |||||
xpt_action((union ccb *)&cpi); | |||||
if (cpi.ccb_h.status == CAM_REQ_CMP && | |||||
cpi.hba_vendor == PCI_VENDOR_INTEL && | |||||
cpi.hba_device == PCI_PRODUCT_PIIX4) { | |||||
(*veto)++; | |||||
xpt_print(path, | |||||
"Disable ATA for vendor: %x, device: %x\n", | |||||
cpi.hba_vendor, cpi.hba_device); | |||||
} | |||||
} | |||||
} | |||||
static void | |||||
storvsc_sysinit(void *arg __unused) | |||||
{ | |||||
if (vm_guest == VM_GUEST_HV) { | |||||
storvsc_handler_tag = EVENTHANDLER_REGISTER(ada_probe_veto, | |||||
storvsc_ada_probe_veto, NULL, EVENTHANDLER_PRI_ANY); | |||||
} | |||||
} | |||||
SYSINIT(storvsc_sys_init, SI_SUB_DRIVERS, SI_ORDER_SECOND, storvsc_sysinit, | |||||
NULL); | |||||
static void | |||||
storvsc_sysuninit(void *arg __unused) | |||||
{ | |||||
if (storvsc_handler_tag != NULL) { | |||||
EVENTHANDLER_DEREGISTER(ada_probe_veto, storvsc_handler_tag); | |||||
} | |||||
} | |||||
SYSUNINIT(storvsc_sys_uninit, SI_SUB_DRIVERS, SI_ORDER_SECOND, | |||||
storvsc_sysuninit, NULL); |
This lock is actually not useful for xpt_print()