Changeset View
Standalone View
sys/dev/vmware/pvscsi/vmw_pvscsi.c
- This file was added.
/* | |||||
* Copyright (c) 2014 EMC Corporation, Inc. All rights reserved. | |||||
*/ | |||||
emaste: Suggest adding this copyright into the existing block | |||||
/* ************************************************************************ | |||||
* Copyright 2008 VMware, Inc. All rights reserved. | |||||
* | |||||
* Redistribution and use in source and binary forms, with or without | |||||
* modification, are permitted provided that the following conditions are met: | |||||
* 1. Redistributions of source code must retain the above copyright notice, | |||||
* this list of conditions and the following disclaimer. | |||||
* 2. Redistributions in binary form must reproduce the above copyright notice, | |||||
* this list of conditions and the following disclaimer in the documentation | |||||
* and/or other materials provided with the distribution. | |||||
* | |||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY | |||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |||||
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY | |||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | |||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||||
* ************************************************************************/ | |||||
/* | |||||
* pvscsi.c -- | |||||
* | |||||
* This is a driver for the VMware PVSCSI HBA adapter. | |||||
*/ | |||||
#include "vmw_pvscsi.h" | |||||
#define PVSCSI_DEFAULT_NUM_PAGES_PER_RING 8 | |||||
#define PVSCSI_DEFAULT_NUM_PAGES_MSG_RING 1 | |||||
#define PVSCSI_DEFAULT_QUEUE_DEPTH 64 | |||||
typedef struct pvscsi_adapter pvscsinst_t; | |||||
#define MODNM pvscsi | |||||
enum { | |||||
PVSCSI_MSIX_VEC0 = 0, /* Only one MSI-X interrupt required */ | |||||
PVSCSI_NUM_MSIX | |||||
}; | |||||
MALLOC_DEFINE(M_PVSCSI, "pvscsi", "VMware's para-virtualized scsi driver"); | |||||
MALLOC_DEFINE(M_PVSCSI_PCI, "pvscsi_pci", "pvscsi's ring queues"); | |||||
MALLOC_DEFINE(M_PVSCSI_SGL, "pvscsi_sgl", "pvscsi's scatter-gather list"); | |||||
/* Command line parameters */ | |||||
static int pvscsi_ring_pages = PVSCSI_DEFAULT_NUM_PAGES_PER_RING; | |||||
static int pvscsi_msg_ring_pages = PVSCSI_DEFAULT_NUM_PAGES_MSG_RING; | |||||
static bool pvscsi_use_msg = true; | |||||
#define MAX_CMPLTNS_WO_INTRS 5 | |||||
static volatile uint64 pvscsi_intr_count; | |||||
static volatile uint64 pvscsi_to_count; | |||||
static bool pvscsi_intr_rcvd = FALSE; | |||||
#define pvscsi_dev(adapter) ((adapter)->pvs_dev) | |||||
#define pvscsi_simdev(sim) (pvscsi_dev((pvscsinst_t *)cam_sim_softc(sim))) | |||||
/* | |||||
* To share code between the Linux and Isilon ports we key off the variable | |||||
* "irq" to figure out if we need to actually lock or not | |||||
*/ | |||||
#define spin_lock_irqsave(l,f) if (irq) PVSCSILCK | |||||
#define spin_unlock_irqrestore(l,f) if (irq) PVSCSIULCK | |||||
#define SCSI_SENSE_BUFFERSIZE sizeof(csio->sense_data) | |||||
#define SIMPLE_QUEUE_TAG MSG_SIMPLE_Q_TAG | |||||
#define HEAD_OF_QUEUE_TAG MSG_HEAD_OF_Q_TAG | |||||
#define ORDERED_QUEUE_TAG MSG_ORDERED_Q_TAG | |||||
enum dma_data_direction { | |||||
DMA_BIDIRECTIONAL = 0, | |||||
DMA_TO_DEVICE = 1, | |||||
DMA_FROM_DEVICE = 2, | |||||
DMA_NONE = 3, | |||||
}; | |||||
struct scsi_cmnd { | |||||
struct ccb_scsiio *qsc_csio; | |||||
void *sense_buffer; | |||||
unsigned short cmd_len; | |||||
unsigned char *cmn; | |||||
struct scsi_device *device; | |||||
enum dma_data_direction sc_data_direction; | |||||
unsigned char tag; | |||||
unsigned char *cmnd; | |||||
pvscsinst_t *adapter; | |||||
}; | |||||
struct scsi_device { | |||||
unsigned int id, lun, channel; | |||||
bool tagged_supported; | |||||
}; | |||||
static struct pvscsi_ctx * | |||||
pvscsi_find_context(struct pvscsi_adapter *adapter, struct ccb_scsiio *cmd) | |||||
{ | |||||
struct pvscsi_ctx *ctx, *end; | |||||
end = &adapter->cmd_map[adapter->req_depth]; | |||||
for (ctx = adapter->cmd_map; ctx < end; ctx++) | |||||
if (ctx->cmd == cmd) | |||||
return ctx; | |||||
return NULL; | |||||
} | |||||
static struct pvscsi_ctx * | |||||
pvscsi_acquire_context(struct pvscsi_adapter *adapter, struct scsi_cmnd *cmd) | |||||
{ | |||||
struct pvscsi_ctx *ctx; | |||||
if (list_empty(&adapter->cmd_pool)) | |||||
return NULL; | |||||
ctx = list_entry(adapter->cmd_pool.next, struct pvscsi_ctx, list); | |||||
ctx->cmd = cmd->qsc_csio; | |||||
ctx->toed = false; | |||||
ctx->debugerr_checked = false; | |||||
list_del(&ctx->list); | |||||
return ctx; | |||||
} | |||||
static void pvscsi_release_context(struct pvscsi_adapter *adapter, | |||||
struct pvscsi_ctx *ctx) | |||||
{ | |||||
ctx->cmd = NULL; | |||||
ctx->e = NULL; | |||||
list_add(&ctx->list, &adapter->cmd_pool); | |||||
} | |||||
/* | |||||
* Map a pvscsi_ctx struct to a context ID field value; we map to a simple | |||||
* non-zero integer. | |||||
*/ | |||||
static u64 pvscsi_map_context(const struct pvscsi_adapter *adapter, | |||||
const struct pvscsi_ctx *ctx) | |||||
{ | |||||
return ctx - adapter->cmd_map + 1; | |||||
} | |||||
static struct pvscsi_ctx * | |||||
pvscsi_get_context(const struct pvscsi_adapter *adapter, u64 context) | |||||
{ | |||||
return &adapter->cmd_map[context - 1]; | |||||
} | |||||
/************************************************************** | |||||
* | |||||
* VMWARE PVSCSI Hypervisor Communication Implementation | |||||
* | |||||
* This code is largely independent of any Linux internals. | |||||
* | |||||
**************************************************************/ | |||||
static void pvscsi_reg_write(const struct pvscsi_adapter *adapter, | |||||
u32 offset, u32 val) | |||||
{ | |||||
bus_space_write_4(adapter->pvs_mmtag, adapter->pvs_mmhndl, offset, val); | |||||
} | |||||
static u32 pvscsi_reg_read(const struct pvscsi_adapter *adapter, u32 offset) | |||||
{ | |||||
return bus_space_read_4(adapter->pvs_mmtag, adapter->pvs_mmhndl, offset); | |||||
} | |||||
static u32 pvscsi_read_intr_status(const struct pvscsi_adapter *adapter) | |||||
{ | |||||
return pvscsi_reg_read(adapter, PVSCSI_REG_OFFSET_INTR_STATUS); | |||||
} | |||||
static void pvscsi_write_intr_status(const struct pvscsi_adapter *adapter, | |||||
u32 val) | |||||
{ | |||||
pvscsi_reg_write(adapter, PVSCSI_REG_OFFSET_INTR_STATUS, val); | |||||
} | |||||
static void pvscsi_unmask_intr(const struct pvscsi_adapter *adapter) | |||||
{ | |||||
u32 intr_bits; | |||||
intr_bits = PVSCSI_INTR_CMPL_MASK; | |||||
if (adapter->use_msg) { | |||||
intr_bits |= PVSCSI_INTR_MSG_MASK; | |||||
} | |||||
pvscsi_reg_write(adapter, PVSCSI_REG_OFFSET_INTR_MASK, intr_bits); | |||||
} | |||||
static void pvscsi_mask_intr(const struct pvscsi_adapter *adapter) | |||||
{ | |||||
pvscsi_reg_write(adapter, PVSCSI_REG_OFFSET_INTR_MASK, 0); | |||||
} | |||||
static void pvscsi_write_cmd_desc(const struct pvscsi_adapter *adapter, | |||||
u32 cmd, const void *desc, size_t len) | |||||
{ | |||||
const u32 *ptr = (const u32 *)desc; | |||||
unsigned i; | |||||
len /= sizeof(u32); | |||||
pvscsi_reg_write(adapter, PVSCSI_REG_OFFSET_COMMAND, cmd); | |||||
for (i = 0; i < len; i++) | |||||
pvscsi_reg_write(adapter, | |||||
PVSCSI_REG_OFFSET_COMMAND_DATA, ptr[i]); | |||||
} | |||||
static void pvscsi_abort_cmd(struct pvscsi_adapter *adapter, | |||||
struct pvscsi_ctx *ctx, | |||||
target_id_t trg) | |||||
{ | |||||
struct PVSCSICmdDescAbortCmd cmd = { 0 }; | |||||
cmd.target = trg; | |||||
cmd.context = pvscsi_map_context(adapter, ctx); | |||||
pvscsi_write_cmd_desc(adapter, PVSCSI_CMD_ABORT_CMD, &cmd, sizeof cmd); | |||||
} | |||||
static void pvscsi_kick_rw_io(const struct pvscsi_adapter *adapter) | |||||
{ | |||||
pvscsi_reg_write(adapter, PVSCSI_REG_OFFSET_KICK_RW_IO, 0); | |||||
} | |||||
static void pvscsi_process_request_ring(const struct pvscsi_adapter *adapter) | |||||
{ | |||||
pvscsi_reg_write(adapter, PVSCSI_REG_OFFSET_KICK_NON_RW_IO, 0); | |||||
} | |||||
static int scsi_is_rw(unsigned char op) | |||||
{ | |||||
return op == READ_6 || op == WRITE_6 || | |||||
op == READ_10 || op == WRITE_10 || | |||||
op == READ_12 || op == WRITE_12 || | |||||
op == READ_16 || op == WRITE_16; | |||||
} | |||||
static void pvscsi_kick_io(const struct pvscsi_adapter *adapter, | |||||
unsigned char op) | |||||
{ | |||||
if (scsi_is_rw(op)) | |||||
pvscsi_kick_rw_io(adapter); | |||||
else | |||||
pvscsi_process_request_ring(adapter); | |||||
} | |||||
static void ll_adapter_reset(const struct pvscsi_adapter *adapter) | |||||
{ | |||||
LOG(0, "Adapter Reset on %p\n", adapter); | |||||
pvscsi_write_cmd_desc(adapter, PVSCSI_CMD_ADAPTER_RESET, NULL, 0); | |||||
} | |||||
static void ll_bus_reset(const struct pvscsi_adapter *adapter) | |||||
{ | |||||
LOG(0, "Reseting bus on %p\n", adapter); | |||||
pvscsi_write_cmd_desc(adapter, PVSCSI_CMD_RESET_BUS, NULL, 0); | |||||
} | |||||
static void ll_device_reset(const struct pvscsi_adapter *adapter, u32 target) | |||||
{ | |||||
struct PVSCSICmdDescResetDevice cmd = { 0 }; | |||||
LOG(0, "Reseting device: target=%u\n", target); | |||||
device_t device = pvscsi_dev(adapter); | |||||
device_printf(device, "Resetting target %u\n", target); | |||||
cmd.target = target; | |||||
pvscsi_write_cmd_desc(adapter, PVSCSI_CMD_RESET_DEVICE, | |||||
&cmd, sizeof cmd); | |||||
device_printf(device, "Done resetting target %u\n", target); | |||||
} | |||||
/* | |||||
* Takes a list of physical segments and translates them into the VMware | |||||
* device emulation's scatter/gather format. It does not initiate the I/O. | |||||
* It reports any errors in the translation through the ctx structure. | |||||
* | |||||
* The bus_dma_segment_t pointed to by dm_segs is allocated on the stack. | |||||
*/ | |||||
static void | |||||
pvscsi_queue_io(void *arg, bus_dma_segment_t *dm_segs, int nseg, int error) | |||||
{ | |||||
struct pvscsi_ctx *ctx = (struct pvscsi_ctx *)arg; | |||||
if (error || ctx->dmamapping_errno) { | |||||
ctx->dmamapping_errno = error; | |||||
return; | |||||
} | |||||
if (nseg > PVSCSI_MAX_NUM_SG_ENTRIES_PER_SEGMENT) { | |||||
ctx->dmamapping_errno = EFBIG; | |||||
return; | |||||
} | |||||
unsigned i; | |||||
struct PVSCSISGElement *sge = &ctx->sgl->sge[0]; | |||||
struct PVSCSIRingReqDesc *e = ctx->e; | |||||
e->flags |= PVSCSI_FLAG_CMD_WITH_SG_LIST; | |||||
for (i = 0; i < nseg; i++) { | |||||
sge[i].addr = dm_segs[i].ds_addr; | |||||
sge[i].length = dm_segs[i].ds_len; | |||||
sge[i].flags = 0; | |||||
} | |||||
ctx->dmamapping_errno = 0; | |||||
e->flags |= PVSCSI_FLAG_CMD_WITH_SG_LIST; | |||||
ctx->sglPA = pci_map_single(adapter->dev, ctx->sgl, | |||||
PAGE_SIZE, PCI_DMA_TODEVICE); | |||||
e->dataAddr = ctx->sglPA; | |||||
} | |||||
#define scsi_bufflen(cmd) (cmd)->qsc_csio->dxfer_len | |||||
#define pvscsi_create_sg(a,b,c) | |||||
#define scsi_sg_count(a) 2 | |||||
#define scsi_dma_map(a) 2 | |||||
static inline dma_addr_t | |||||
sg_dma_address_fn(void) | |||||
{ | |||||
panic("This code-path shouldn't have been taken"); | |||||
return 0; | |||||
} | |||||
#define sg_dma_address(sg) sg_dma_address_fn() | |||||
#define IRQ_RETVAL(a) 0 | |||||
#define SAM_STAT_GOOD SCSI_STATUS_OK | |||||
#define SAM_STAT_CHECK_CONDITION SCSI_STATUS_CHECK_COND | |||||
#define SAM_STAT_COMMAND_TERMINATED SCSI_STATUS_CMD_TERMINATED | |||||
/* | |||||
* Map all data buffers for a command into PCI space and | |||||
* setup the scatter/gather list if needed. | |||||
*/ | |||||
static void pvscsi_map_buffers(struct pvscsi_adapter *adapter, | |||||
struct pvscsi_ctx *ctx, struct scsi_cmnd *cmd, | |||||
struct PVSCSIRingReqDesc *e) | |||||
{ | |||||
unsigned count; | |||||
unsigned bufflen = scsi_bufflen(cmd); | |||||
e->dataLen = bufflen; | |||||
e->dataAddr = 0; | |||||
if (bufflen == 0) | |||||
return; | |||||
struct ccb_scsiio *csio = cmd->qsc_csio; | |||||
ctx->e = e; | |||||
ctx->dmamapping_errno = 0; | |||||
int error; | |||||
error = bus_dmamap_load_ccb(adapter->pvs_dmat, ctx->dmap, | |||||
(union ccb *)csio, pvscsi_queue_io, ctx, BUS_DMA_NOWAIT); | |||||
scottlUnsubmitted Not Done Inline ActionsDon't use BUS_DMA_NOWAIT this with storage drivers. The rest of this function should go into the callback, and EINPROGRESS should be checked on the return. scottl: Don't use BUS_DMA_NOWAIT this with storage drivers. The rest of this function should go into… | |||||
if (error) | |||||
ctx->dmamapping_errno = error; | |||||
if (ctx->dmamapping_errno) { | |||||
if (ctx->dmamapping_errno == EFBIG) | |||||
csio->ccb_h.flags = CAM_REQ_TOO_BIG; | |||||
else | |||||
csio->ccb_h.flags = CAM_REQ_CMP_ERR; | |||||
} | |||||
/* | |||||
* Setup 'count' and 'segs' so that we choose the path that sets | |||||
* PVSCSI_FLAG_CMD_WITH_SG_LIST and uses a scatter/gather list | |||||
*/ | |||||
count = scsi_sg_count(cmd); | |||||
if (count != 0) { | |||||
int segs = 2; /* Force the more generic path below */ | |||||
if (segs > 1) { | |||||
pvscsi_create_sg(ctx, sg, segs); | |||||
e->flags |= PVSCSI_FLAG_CMD_WITH_SG_LIST; | |||||
ctx->sglPA = pci_map_single(adapter->dev, ctx->sgl, | |||||
PAGE_SIZE, PCI_DMA_TODEVICE); | |||||
e->dataAddr = ctx->sglPA; | |||||
} else | |||||
e->dataAddr = sg_dma_address(sg); | |||||
} else { | |||||
e->dataAddr = ctx->dataPA; | |||||
} | |||||
} | |||||
static void pvscsi_unmap_buffers(const struct pvscsi_adapter *adapter, | |||||
struct pvscsi_ctx *ctx) | |||||
{ | |||||
struct ccb_scsiio *csio = ctx->cmd; | |||||
if (csio->dxfer_len) | |||||
bus_dmamap_unload(adapter->pvs_dmat, ctx->dmap); | |||||
} | |||||
static int pvscsi_allocate_rings(struct pvscsi_adapter *adapter) | |||||
{ | |||||
adapter->rings_state = pci_alloc_consistent(adapter->dev, PAGE_SIZE, | |||||
&adapter->ringStatePA); | |||||
if (!adapter->rings_state) | |||||
return -ENOMEM; | |||||
adapter->req_pages = min(PVSCSI_MAX_NUM_PAGES_REQ_RING, | |||||
pvscsi_ring_pages); | |||||
adapter->req_depth = adapter->req_pages | |||||
* PVSCSI_MAX_NUM_REQ_ENTRIES_PER_PAGE; | |||||
adapter->req_ring = pci_alloc_consistent(adapter->dev, | |||||
adapter->req_pages * PAGE_SIZE, | |||||
&adapter->reqRingPA); | |||||
if (!adapter->req_ring) | |||||
return -ENOMEM; | |||||
adapter->cmp_pages = min(PVSCSI_MAX_NUM_PAGES_CMP_RING, | |||||
pvscsi_ring_pages); | |||||
adapter->cmp_ring = pci_alloc_consistent(adapter->dev, | |||||
adapter->cmp_pages * PAGE_SIZE, | |||||
&adapter->cmpRingPA); | |||||
if (!adapter->cmp_ring) | |||||
return -ENOMEM; | |||||
BUG_ON(adapter->ringStatePA & PAGE_MASK); | |||||
BUG_ON(adapter->reqRingPA & PAGE_MASK); | |||||
BUG_ON(adapter->cmpRingPA & PAGE_MASK); | |||||
if (!adapter->use_msg) | |||||
return 0; | |||||
adapter->msg_pages = min(PVSCSI_MAX_NUM_PAGES_MSG_RING, | |||||
pvscsi_msg_ring_pages); | |||||
adapter->msg_ring = pci_alloc_consistent(adapter->dev, | |||||
adapter->msg_pages * PAGE_SIZE, | |||||
&adapter->msgRingPA); | |||||
if (!adapter->msg_ring) | |||||
return -ENOMEM; | |||||
BUG_ON(adapter->msgRingPA & PAGE_MASK); | |||||
return 0; | |||||
} | |||||
static void pvscsi_setup_all_rings(const struct pvscsi_adapter *adapter) | |||||
{ | |||||
struct PVSCSICmdDescSetupRings cmd = { 0 }; | |||||
dma_addr_t base; | |||||
unsigned i; | |||||
cmd.ringsStatePPN = adapter->ringStatePA >> PAGE_SHIFT; | |||||
cmd.reqRingNumPages = adapter->req_pages; | |||||
cmd.cmpRingNumPages = adapter->cmp_pages; | |||||
base = adapter->reqRingPA; | |||||
for (i = 0; i < adapter->req_pages; i++) { | |||||
cmd.reqRingPPNs[i] = base >> PAGE_SHIFT; | |||||
base += PAGE_SIZE; | |||||
} | |||||
base = adapter->cmpRingPA; | |||||
for (i = 0; i < adapter->cmp_pages; i++) { | |||||
cmd.cmpRingPPNs[i] = base >> PAGE_SHIFT; | |||||
base += PAGE_SIZE; | |||||
} | |||||
memset(adapter->rings_state, 0, PAGE_SIZE); | |||||
memset(adapter->req_ring, 0, adapter->req_pages * PAGE_SIZE); | |||||
memset(adapter->cmp_ring, 0, adapter->cmp_pages * PAGE_SIZE); | |||||
pvscsi_write_cmd_desc(adapter, PVSCSI_CMD_SETUP_RINGS, | |||||
&cmd, sizeof cmd); | |||||
if (adapter->use_msg) { | |||||
struct PVSCSICmdDescSetupMsgRing cmd_msg = { 0 }; | |||||
cmd_msg.numPages = adapter->msg_pages; | |||||
base = adapter->msgRingPA; | |||||
for (i = 0; i < adapter->msg_pages; i++) { | |||||
cmd_msg.ringPPNs[i] = base >> PAGE_SHIFT; | |||||
base += PAGE_SIZE; | |||||
} | |||||
memset(adapter->msg_ring, 0, adapter->msg_pages * PAGE_SIZE); | |||||
pvscsi_write_cmd_desc(adapter, PVSCSI_CMD_SETUP_MSG_RING, | |||||
&cmd_msg, sizeof cmd_msg); | |||||
} | |||||
} | |||||
/* | |||||
* Pull a completion descriptor off and pass the completion back | |||||
* to the SCSI mid layer. | |||||
*/ | |||||
void pvscsi_complete_request(struct pvscsi_adapter *adapter, | |||||
const struct PVSCSIRingCmpDesc *e) | |||||
{ | |||||
struct pvscsi_ctx *ctx; | |||||
bool toed = false; | |||||
struct ccb_scsiio *cmd; | |||||
device_t device = pvscsi_dev(adapter); | |||||
int debugerr = 0; | |||||
u32 btstat = e->hostStatus; | |||||
u32 sdstat = e->scsiStatus; | |||||
u64 edataLen = e->dataLen; | |||||
mtx_assert(&adapter->pvs_camlock, MA_OWNED); | |||||
ctx = pvscsi_get_context(adapter, e->context); | |||||
cmd = ctx->cmd; | |||||
#ifdef __ISILON__ | |||||
/* | |||||
* check debugerr failpoints now so that we can do nothing if we're | |||||
* delaying the completion with a timer. Only check them once per | |||||
* command. | |||||
*/ | |||||
if (!ctx->debugerr_checked) { | |||||
ctx->debugerr_checked = true; /* For when this very routine is | |||||
* invoked from the FP callout */ | |||||
debugerr = pvscsi_debugerr_check(adapter, ctx); | |||||
if (debugerr == PVSCSI_DEBUGERR_QUEUED) { | |||||
/* | |||||
* Copy off the completion descriptor because it will | |||||
* be recycled once this routine exits. The second time | |||||
* through this routine, this copy will be used as "e" | |||||
*/ | |||||
ctx->cmpcpy = *e; | |||||
return; | |||||
} | |||||
} | |||||
#endif | |||||
ngieAuthorUnsubmitted Done Inline ActionsRemove from upstream driver. ngie: Remove from upstream driver. | |||||
callout_stop(&ctx->calloutx); /* disables ABORT or SCSI IO callout */ | |||||
toed = ctx->toed; | |||||
if (toed) { | |||||
device_printf(device, "ccb:%p marked for timeout returned with," | |||||
"ctx:%p, h:%u s:%u\n", cmd, ctx, btstat, sdstat); | |||||
} | |||||
e = NULL; | |||||
pvscsi_unmap_buffers(adapter, ctx); | |||||
pvscsi_release_context(adapter, ctx); | |||||
/* After this point there should be no more references to e */ | |||||
ngieAuthorUnsubmitted Not Done Inline ActionsSpurious newline. ngie: Spurious newline. | |||||
if (sdstat != SAM_STAT_GOOD && | |||||
(btstat == BTSTAT_SUCCESS || | |||||
btstat == BTSTAT_LINKED_COMMAND_COMPLETED || | |||||
btstat == BTSTAT_LINKED_COMMAND_COMPLETED_WITH_FLAG)) { | |||||
cmd->scsi_status = sdstat; | |||||
if (sdstat == SAM_STAT_COMMAND_TERMINATED) | |||||
cmd->ccb_h.status = CAM_SCSI_BUS_RESET; | |||||
else if (sdstat == SAM_STAT_CHECK_CONDITION) | |||||
cmd->ccb_h.status = CAM_SCSI_STATUS_ERROR | | |||||
CAM_AUTOSNS_VALID; | |||||
else | |||||
cmd->ccb_h.status = CAM_SCSI_STATUS_ERROR; | |||||
} else | |||||
switch (btstat) { | |||||
case BTSTAT_SUCCESS: | |||||
case BTSTAT_LINKED_COMMAND_COMPLETED: | |||||
case BTSTAT_LINKED_COMMAND_COMPLETED_WITH_FLAG: | |||||
/* If everything went fine, let's move on.. */ | |||||
cmd->scsi_status = sdstat; | |||||
cmd->ccb_h.status = CAM_REQ_CMP; | |||||
break; | |||||
case BTSTAT_DATARUN: | |||||
case BTSTAT_DATA_UNDERRUN: | |||||
cmd->scsi_status = sdstat; | |||||
cmd->ccb_h.status = CAM_DATA_RUN_ERR; | |||||
cmd->resid = cmd->dxfer_len - edataLen; | |||||
break; | |||||
case BTSTAT_SELTIMEO: | |||||
/* Our emulation returns this for non-connected devs */ | |||||
cmd->scsi_status = sdstat; | |||||
cmd->ccb_h.status = CAM_SEL_TIMEOUT; | |||||
break; | |||||
case BTSTAT_LUNMISMATCH: | |||||
case BTSTAT_TAGREJECT: | |||||
case BTSTAT_BADMSG: | |||||
cmd->scsi_status = sdstat; | |||||
cmd->ccb_h.status = CAM_LUN_INVALID; | |||||
break; | |||||
/* fall through */ | |||||
case BTSTAT_HAHARDWARE: | |||||
case BTSTAT_INVPHASE: | |||||
case BTSTAT_HATIMEOUT: | |||||
case BTSTAT_NORESPONSE: | |||||
case BTSTAT_DISCONNECT: | |||||
case BTSTAT_HASOFTWARE: | |||||
case BTSTAT_BUSFREE: | |||||
case BTSTAT_SENSFAILED: | |||||
cmd->scsi_status = sdstat; | |||||
cmd->ccb_h.status = CAM_REQ_CMP_ERR; | |||||
break; | |||||
case BTSTAT_SENTRST: | |||||
case BTSTAT_RECVRST: | |||||
case BTSTAT_BUSRESET: | |||||
cmd->scsi_status = sdstat; | |||||
cmd->ccb_h.status = CAM_SCSI_BUS_RESET; | |||||
break; | |||||
case BTSTAT_ABORTQUEUE: | |||||
cmd->scsi_status = sdstat; | |||||
device_printf(device, "Command %s\n", toed ? | |||||
"timedout" : "aborted"); | |||||
if(toed) { | |||||
cmd->ccb_h.status = CAM_CMD_TIMEOUT; | |||||
} else { | |||||
cmd->ccb_h.status = CAM_REQ_ABORTED; | |||||
} | |||||
break; | |||||
case BTSTAT_SCSIPARITY: | |||||
cmd->scsi_status = sdstat; | |||||
cmd->ccb_h.status = CAM_UNCOR_PARITY; | |||||
break; | |||||
default: | |||||
cmd->scsi_status = sdstat; | |||||
cmd->ccb_h.status = CAM_REQ_CMP_ERR; | |||||
device_printf(device, "Unknown completion status: " | |||||
"0x%x\n", btstat); | |||||
} | |||||
if (debugerr != 0) { | |||||
/* inject an error */ | |||||
union ccb *ccb = (union ccb *)cmd; | |||||
ccb->ccb_h.status = CAM_UNCOR_PARITY; | |||||
ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND; | |||||
} | |||||
xpt_done((union ccb *)cmd); | |||||
} | |||||
/* | |||||
* barrier usage : Since the PVSCSI device is emulated, there could be cases | |||||
* where we may want to serialize some accesses between the driver and the | |||||
* emulation layer. We use compiler barriers instead of the more expensive | |||||
* memory barriers because PVSCSI is only supported on X86 which has strong | |||||
* memory access ordering. | |||||
*/ | |||||
static void pvscsi_process_completion_ring(struct pvscsi_adapter *adapter) | |||||
{ | |||||
struct PVSCSIRingsState *s = adapter->rings_state; | |||||
struct PVSCSIRingCmpDesc *ring = adapter->cmp_ring; | |||||
u32 cmp_entries = s->cmpNumEntriesLog2; | |||||
while (s->cmpConsIdx != s->cmpProdIdx) { | |||||
struct PVSCSIRingCmpDesc *e = ring + (s->cmpConsIdx & | |||||
MASK(cmp_entries)); | |||||
/* | |||||
* This barrier() ensures that *e is not dereferenced while | |||||
* the device emulation still writes data into the slot. | |||||
* Since the device emulation advances s->cmpProdIdx only after | |||||
* updating the slot we want to check it first. | |||||
*/ | |||||
barrier(); | |||||
pvscsi_complete_request(adapter, e); | |||||
/* | |||||
* This barrier() ensures that compiler doesn't reorder write | |||||
* to s->cmpConsIdx before the read of (*e) inside | |||||
* pvscsi_complete_request. Otherwise, device emulation may | |||||
* overwrite *e before we had a chance to read it. | |||||
*/ | |||||
barrier(); | |||||
s->cmpConsIdx++; | |||||
} | |||||
} | |||||
#ifdef __ISILON__ | |||||
static inline void | |||||
PRINT_CTX(struct pvscsi_ctx *ctx) | |||||
{ | |||||
printf("pvscsi:ctx %p [0]>%lx [l]>%x %lx %lx %lx %d %p %p\n", | |||||
ctx->sgl, | |||||
ctx->sgl->sge[0].addr, | |||||
ctx->sgl->sge[0].length, | |||||
ctx->dataPA, | |||||
ctx->sensePA, | |||||
ctx->sglPA, | |||||
ctx->dmamapping_errno, | |||||
ctx->e, | |||||
ctx->dmap); | |||||
} | |||||
static inline void | |||||
PRINT_REQ(struct PVSCSIRingReqDesc *e) | |||||
{ | |||||
printf("pvscsi:req cid>%lx dat>%lx dlen>%lx sns>%lx slen>%x fl>%x " | |||||
"c0>%u cl>%u lu>%u tg>%u b>%u trg>%u cpu>%u\n", | |||||
e->context, | |||||
e->dataAddr, | |||||
e->dataLen, | |||||
e->senseAddr, | |||||
e->senseLen, | |||||
e->flags, | |||||
e->cdb[0], | |||||
e->cdbLen, | |||||
e->lun[0], | |||||
e->tag, | |||||
e->bus, | |||||
e->target, | |||||
e->vcpuHint); | |||||
} | |||||
#endif | |||||
ngieAuthorUnsubmitted Done Inline ActionsRemove from upstream driver. ngie: Remove from upstream driver. | |||||
/* | |||||
* Translate a Linux SCSI request into a request ring entry. | |||||
*/ | |||||
static int pvscsi_queue_ring(struct pvscsi_adapter *adapter, | |||||
struct pvscsi_ctx *ctx, struct scsi_cmnd *cmd) | |||||
{ | |||||
struct PVSCSIRingsState *s; | |||||
struct PVSCSIRingReqDesc *e; | |||||
struct scsi_device *sdev; | |||||
u32 req_entries; | |||||
struct ccb_scsiio *csio = cmd->qsc_csio; | |||||
s = adapter->rings_state; | |||||
sdev = cmd->device; | |||||
req_entries = s->reqNumEntriesLog2; | |||||
/* | |||||
* If this condition holds, we might have room on the request ring, but | |||||
* we might not have room on the completion ring for the response. | |||||
* However, we have already ruled out this possibility - we would not | |||||
* have successfully allocated a context if it were true, since we only | |||||
* have one context per request entry. Check for it anyway, since it | |||||
* would be a serious bug. | |||||
*/ | |||||
if (s->reqProdIdx - s->cmpConsIdx >= 1 << req_entries) { | |||||
device_printf(pvscsi_dev(adapter), "Error, ring full: reqProdIdx=%d cmpConsIdx=%d\n", | |||||
s->reqProdIdx, s->cmpConsIdx); | |||||
return CAM_RESRC_UNAVAIL; | |||||
} | |||||
e = adapter->req_ring + (s->reqProdIdx & MASK(req_entries)); | |||||
e->bus = sdev->channel; | |||||
e->target = sdev->id; | |||||
memset(e->lun, 0, sizeof e->lun); | |||||
if (cmd->sense_buffer) { | |||||
ctx->sensePA = pci_map_single(adapter->dev, cmd->sense_buffer, | |||||
SCSI_SENSE_BUFFERSIZE, | |||||
PCI_DMA_FROMDEVICE); | |||||
e->senseAddr = ctx->sensePA; | |||||
e->senseLen = SCSI_SENSE_BUFFERSIZE; | |||||
} else { | |||||
e->senseLen = 0; | |||||
e->senseAddr = 0; | |||||
} | |||||
e->cdbLen = cmd->cmd_len; | |||||
e->vcpuHint = smp_processor_id(); | |||||
memcpy(e->cdb, cmd->cmnd, e->cdbLen); | |||||
e->tag = SIMPLE_QUEUE_TAG; | |||||
if (sdev->tagged_supported && | |||||
(cmd->tag == HEAD_OF_QUEUE_TAG || | |||||
cmd->tag == ORDERED_QUEUE_TAG)) | |||||
e->tag = cmd->tag; | |||||
if (cmd->sc_data_direction == DMA_FROM_DEVICE) | |||||
e->flags = PVSCSI_FLAG_CMD_DIR_TOHOST; | |||||
else if (cmd->sc_data_direction == DMA_TO_DEVICE) | |||||
e->flags = PVSCSI_FLAG_CMD_DIR_TODEVICE; | |||||
else if (cmd->sc_data_direction == DMA_NONE) | |||||
e->flags = PVSCSI_FLAG_CMD_DIR_NONE; | |||||
else | |||||
e->flags = 0; | |||||
pvscsi_map_buffers(adapter, ctx, cmd, e); | |||||
e->context = pvscsi_map_context(adapter, ctx); | |||||
barrier(); | |||||
s->reqProdIdx++; | |||||
return 0; | |||||
} | |||||
static inline void | |||||
PRINT_RINGSTATE(pvscsinst_t *adapter) | |||||
{ | |||||
struct PVSCSIRingsState *s __unused; | |||||
s = adapter->rings_state; | |||||
LOG(0, | |||||
"req> %d %d %d cmp> %d %d %d msg> %d %d %d\n", | |||||
s->reqProdIdx, | |||||
s->reqConsIdx, | |||||
s->reqNumEntriesLog2, | |||||
s->cmpProdIdx, | |||||
s->cmpConsIdx, | |||||
s->cmpNumEntriesLog2, | |||||
s->msgProdIdx, | |||||
s->msgConsIdx, | |||||
s->msgNumEntriesLog2); | |||||
} | |||||
static void | |||||
pvscsi_abort_timeout(void *data) | |||||
{ | |||||
struct pvscsi_ctx *ctx; | |||||
pvscsinst_t *adapter; | |||||
target_id_t tid; | |||||
struct ccb_scsiio *cmd; | |||||
pvscsitarg_t *targ; | |||||
ctx = (struct pvscsi_ctx *)data; | |||||
adapter = ctx->adapter; | |||||
cmd = ctx->cmd; | |||||
if (!cmd) { | |||||
device_printf(pvscsi_dev(adapter), | |||||
"abort TIMEOUT ctx>%p with NULL cmd\n", ctx); | |||||
return; | |||||
} | |||||
tid = cmd->ccb_h.target_id; | |||||
targ = adapter->pvs_tarrg + tid; | |||||
targ->pvt_ntrs++; | |||||
pvscsi_process_request_ring(adapter); | |||||
ll_device_reset(adapter, tid); | |||||
pvscsi_process_completion_ring(adapter); | |||||
} | |||||
static void | |||||
pvscsi_scsiio_timeout(void *data) | |||||
{ | |||||
struct pvscsi_ctx *ctx; | |||||
pvscsinst_t *adapter; | |||||
pvscsitarg_t *targ; | |||||
struct timeval tv; | |||||
struct ccb_scsiio *cmd; | |||||
ctx = (struct pvscsi_ctx *)data; | |||||
adapter = ctx->adapter; | |||||
cmd = ctx->cmd; | |||||
mtx_assert(&adapter->pvs_camlock, MA_OWNED); | |||||
if (!cmd) { | |||||
device_printf(pvscsi_dev(adapter), | |||||
"SCSI IO TIMEOUT ctx>%p with NULL cmd\n", ctx); | |||||
return; | |||||
} | |||||
pvscsi_process_completion_ring(adapter); | |||||
if (ctx->cmd != cmd) { | |||||
uint64 to_count; | |||||
to_count = atomic_fetchadd_long(&pvscsi_to_count, 1); | |||||
device_printf(pvscsi_dev(adapter), "I/O timeout fired, but I/O " | |||||
"marked completed in ctx %p.\n" | |||||
"\tGlobal pvscsi_intr_count %lu, instance count %lu,\n" | |||||
"\tpvscsi_intr_rcvd %s, I/O completion w/o intr %lu\n" | |||||
"\tintr_status %u\n", | |||||
ctx, pvscsi_intr_count, adapter->pvs_intrcnt, | |||||
pvscsi_intr_rcvd == TRUE ? "TRUE" : "FALSE", to_count + 1, | |||||
pvscsi_read_intr_status(adapter)); | |||||
if ((to_count > MAX_CMPLTNS_WO_INTRS) && | |||||
(pvscsi_intr_rcvd != TRUE)) { | |||||
printf("PVSCSI: Node-wide Interrupt delivery failure.\n" | |||||
"\tRebooting\n"); | |||||
kern_reboot(RB_NOSYNC); | |||||
ngieAuthorUnsubmitted Done Inline ActionsThis behavior seems specific to how Isilon wants things (note verbiage, i.e. "Node-wide"). Also, it seems like panic(9) might be more appropriate? ngie: This behavior seems specific to how Isilon wants things (note verbiage, i.e. "Node-wide"). Also… | |||||
} | |||||
return; | |||||
} | |||||
getmicrotime(&tv); | |||||
device_printf(pvscsi_dev(adapter), "SCSI IO TIMEOUT ctx>%p ccb>%p at " | |||||
"%ld.%06ld\n", ctx, ctx->cmd, tv.tv_sec, tv.tv_usec); | |||||
/* Update the targ_t from the targ array with the TO info */ | |||||
targ = adapter->pvs_tarrg + ctx->cmd->ccb_h.target_id; | |||||
targ->pvt_ntos++; | |||||
ctx->toed = true; | |||||
/* Kick off a task abort and process completion once more */ | |||||
pvscsi_abort_cmd(adapter, ctx, cmd->ccb_h.target_id); | |||||
pvscsi_process_completion_ring(adapter); | |||||
/* If the cmd has not been aborted start a timer for the abort */ | |||||
if (ctx->cmd == cmd) { | |||||
callout_reset(&ctx->calloutx, PVSCSI_ABORT_TIMEOUT * hz, | |||||
pvscsi_abort_timeout, ctx); | |||||
} | |||||
} | |||||
static int pvscsi_queue_locked(struct scsi_cmnd *cmd, | |||||
void (*done)(struct scsi_cmnd *)) | |||||
{ | |||||
struct pvscsi_adapter *adapter = cmd->adapter; | |||||
int timeout = (cmd->qsc_csio->ccb_h.timeout * hz)/1000; | |||||
struct pvscsi_ctx *ctx; | |||||
ctx = pvscsi_acquire_context(adapter, cmd); | |||||
if (!ctx || pvscsi_queue_ring(adapter, ctx, cmd) != 0) { | |||||
if (ctx) | |||||
pvscsi_release_context(adapter, ctx); | |||||
return SCSI_MLQUEUE_HOST_BUSY; | |||||
} | |||||
ctx->debugerr_checked = false; | |||||
ctx->toed = false; | |||||
if (adapter->pvs_timeout_one_comm_targ == | |||||
cmd->qsc_csio->ccb_h.target_id) { | |||||
timeout = 1; | |||||
adapter->pvs_timeout_one_comm_targ = -1; | |||||
} | |||||
callout_reset(&ctx->calloutx, timeout, | |||||
adapter->pvs_reset_target_on_timeout ? | |||||
pvscsi_abort_timeout: pvscsi_scsiio_timeout, | |||||
ctx); | |||||
cmd->qsc_csio->ccb_h.status |= CAM_SIM_QUEUED; | |||||
ngieAuthorUnsubmitted Done Inline ActionsSpurious newline. ngie: Spurious newline. | |||||
pvscsi_kick_io(adapter, cmd->cmnd[0]); | |||||
return 0; | |||||
} | |||||
static int pvscsi_abort(pvscsinst_t *adapter, struct ccb_scsiio *cmd) | |||||
{ | |||||
struct pvscsi_ctx *ctx; | |||||
int irq = 0; | |||||
spin_lock_irqsave(&adapter->hw_lock, flags); | |||||
/* | |||||
* Poll the completion ring first - we might be trying to abort | |||||
* a command that is waiting to be dispatched in the completion ring. | |||||
*/ | |||||
pvscsi_process_completion_ring(adapter); | |||||
/* | |||||
* If there is no context for the command, it either already succeeded | |||||
* or else was never properly issued. Not our problem. | |||||
*/ | |||||
ctx = pvscsi_find_context(adapter, cmd); | |||||
if (!ctx) { | |||||
device_t device = pvscsi_dev(adapter); | |||||
device_printf(device, "Failed to abort cmd %p\n", cmd); | |||||
goto out; | |||||
} | |||||
pvscsi_abort_cmd(adapter, ctx, cmd->ccb_h.target_id); | |||||
pvscsi_process_completion_ring(adapter); | |||||
out: | |||||
spin_unlock_irqrestore(&adapter->hw_lock, flags); | |||||
return SUCCESS; | |||||
} | |||||
static int pvscsi_bus_reset(pvscsinst_t *adapter) | |||||
{ | |||||
int irq = 0; /* So that we do not lock */ | |||||
/* | |||||
* We don't want to queue new requests for this bus after | |||||
* flushing all pending requests to emulation, since new | |||||
* requests could then sneak in during this bus reset phase, | |||||
* so take the lock now. | |||||
*/ | |||||
spin_lock_irqsave(&adapter->hw_lock, flags); | |||||
pvscsi_process_request_ring(adapter); | |||||
ll_bus_reset(adapter); | |||||
pvscsi_process_completion_ring(adapter); | |||||
spin_unlock_irqrestore(&adapter->hw_lock, flags); | |||||
return SUCCESS; | |||||
} | |||||
static int pvscsi_device_reset(pvscsinst_t *adapter, target_id_t trg) | |||||
{ | |||||
int irq = 0; | |||||
/* | |||||
* We don't want to queue new requests for this device after flushing | |||||
* all pending requests to emulation, since new requests could then | |||||
* sneak in during this device reset phase, so take the lock now. | |||||
*/ | |||||
spin_lock_irqsave(&adapter->hw_lock, flags); | |||||
pvscsi_process_request_ring(adapter); | |||||
ll_device_reset(adapter, trg); | |||||
pvscsi_process_completion_ring(adapter); | |||||
spin_unlock_irqrestore(&adapter->hw_lock, flags); | |||||
return SUCCESS; | |||||
} | |||||
static void | |||||
pvscsi_device_lost_or_found(pvscsinst_t *adapter, u32 bus, u32 trg, u8 lun, | |||||
bool lost) | |||||
{ | |||||
struct cam_path *path; | |||||
struct ccb_getdev ccb = { }; | |||||
cam_status err; | |||||
#ifdef __ISILON__ | |||||
diskevt_event_t diskevent; | |||||
int eventnum = -1; | |||||
#endif | |||||
ngieAuthorUnsubmitted Done Inline ActionsRemove from upstream driver. ngie: Remove from upstream driver. | |||||
if (lun) { | |||||
device_printf(pvscsi_dev(adapter), "hotplug/removal event for " | |||||
"a non-zero LUN '%d:%d'. Ignoring event\n", trg, lun); | |||||
return; | |||||
} | |||||
PVSCSILCK; | |||||
err = xpt_create_path(&path, NULL, cam_sim_path(adapter->pvs_camsim), | |||||
trg, lun); | |||||
if (err != CAM_REQ_CMP) { | |||||
device_printf(pvscsi_dev(adapter), "hotplug/removal event " | |||||
"failed to allocate path. '%d:%d' Ignoring event\n", | |||||
trg, lun); | |||||
PVSCSIULCK; | |||||
return; | |||||
} | |||||
xpt_setup_ccb(&(ccb.ccb_h), path, /*priority*/5); | |||||
if (lost) { | |||||
device_printf(pvscsi_dev(adapter), "removing targ:lun %d:%d\n", | |||||
trg, lun); | |||||
xpt_async(AC_LOST_DEVICE, path, NULL); | |||||
} else { | |||||
device_printf(pvscsi_dev(adapter), "adding targ:lun %d:%d\n", | |||||
trg, lun); | |||||
union ccb *scan_ccb; | |||||
scan_ccb = xpt_alloc_ccb_nowait(); | |||||
if (scan_ccb == NULL) { | |||||
device_printf(pvscsi_dev(adapter), | |||||
"unable to alloc CCB for rescan\n"); | |||||
return; | |||||
} | |||||
scan_ccb->ccb_h.func_code = XPT_SCAN_TGT; | |||||
if (xpt_create_path(&scan_ccb->ccb_h.path, NULL, | |||||
cam_sim_path(adapter->pvs_camsim), trg, lun) != CAM_REQ_CMP) { | |||||
device_printf(pvscsi_dev(adapter), | |||||
"unable to create path for rescan\n"); | |||||
return; | |||||
} | |||||
xpt_rescan(scan_ccb); | |||||
} | |||||
xpt_free_path(path); | |||||
#ifdef __ISILON__ | |||||
eventnum = ISI_UNIT(IDSK_TYPE_DA, | |||||
CAM_BTL_2ISIUNIT(cam_sim_path(adapter->pvs_camsim), trg, lun)); | |||||
#endif | |||||
ngieAuthorUnsubmitted Done Inline ActionsRemove from upstream driver. ngie: Remove from upstream driver. | |||||
PVSCSIULCK; | |||||
#ifdef __ISILON__ | |||||
if (eventnum >= 0) { | |||||
bzero(&diskevent, sizeof(diskevent)); | |||||
diskevent.unitnum = eventnum; | |||||
diskevent.type = lost ? DISKEVT_DISK_ABSENT : | |||||
DISKEVT_DISK_PRESENT; | |||||
diskevt_cdev_notify_event(&diskevent); | |||||
} else { | |||||
device_printf(pvscsi_dev(adapter), | |||||
"Couldn't post drive %s DISKEVT. e:t:l %d:%d:%d\n", | |||||
lost ? "remove" : "add", eventnum, trg, lun); | |||||
} | |||||
#endif | |||||
ngieAuthorUnsubmitted Done Inline ActionsRemove from upstream driver. ngie: Remove from upstream driver. | |||||
} | |||||
static void pvscsi_process_msg(struct pvscsi_adapter *adapter, | |||||
struct PVSCSIRingMsgDesc *e) | |||||
{ | |||||
ASSERT_ON_COMPILE(PVSCSI_MSG_LAST == 2); | |||||
if (e->type == PVSCSI_MSG_DEV_ADDED) { | |||||
struct PVSCSIMsgDescDevStatusChanged *desc; | |||||
desc = (struct PVSCSIMsgDescDevStatusChanged *)e; | |||||
pvscsi_device_lost_or_found(adapter, desc->bus, desc->target, | |||||
desc->lun[1], false); | |||||
} else if (e->type == PVSCSI_MSG_DEV_REMOVED) { | |||||
struct PVSCSIMsgDescDevStatusChanged *desc; | |||||
desc = (struct PVSCSIMsgDescDevStatusChanged *)e; | |||||
pvscsi_device_lost_or_found(adapter, desc->bus, desc->target, | |||||
desc->lun[1], true); | |||||
} | |||||
} | |||||
static int pvscsi_msg_pending(const struct pvscsi_adapter *adapter) | |||||
{ | |||||
struct PVSCSIRingsState *s = adapter->rings_state; | |||||
return s->msgProdIdx != s->msgConsIdx; | |||||
} | |||||
static void pvscsi_process_msg_ring(struct pvscsi_adapter *adapter) | |||||
{ | |||||
struct PVSCSIRingsState *s = adapter->rings_state; | |||||
struct PVSCSIRingMsgDesc *ring = adapter->msg_ring; | |||||
u32 msg_entries = s->msgNumEntriesLog2; | |||||
while (pvscsi_msg_pending(adapter)) { | |||||
struct PVSCSIRingMsgDesc *e = ring + (s->msgConsIdx & | |||||
MASK(msg_entries)); | |||||
barrier(); | |||||
pvscsi_process_msg(adapter, e); | |||||
barrier(); | |||||
s->msgConsIdx++; | |||||
} | |||||
} | |||||
static void pvscsi_msg_workqueue_handler(struct work_struct *data) | |||||
{ | |||||
struct pvscsi_adapter *adapter; | |||||
adapter = COMPAT_WORK_GET_DATA(data, struct pvscsi_adapter, work); | |||||
pvscsi_process_msg_ring(adapter); | |||||
} | |||||
static int pvscsi_setup_msg_workqueue(struct pvscsi_adapter *adapter) | |||||
{ | |||||
char name[32]; | |||||
ngieAuthorUnsubmitted Done Inline ActionsMagic number ngie: Magic number | |||||
if (!pvscsi_use_msg) | |||||
return 0; | |||||
pvscsi_reg_write(adapter, PVSCSI_REG_OFFSET_COMMAND, | |||||
PVSCSI_CMD_SETUP_MSG_RING); | |||||
if (pvscsi_reg_read(adapter, PVSCSI_REG_OFFSET_COMMAND_STATUS) == -1) | |||||
return 0; | |||||
snprintf(name, sizeof name, "pvscsi_wq_%u", | |||||
device_get_unit(adapter->pvs_dev)); | |||||
adapter->workqueue = create_singlethread_workqueue(name); | |||||
if (!adapter->workqueue) { | |||||
printk(KERN_ERR "pvscsi: failed to create work queue\n"); | |||||
return 0; | |||||
} | |||||
COMPAT_INIT_WORK(&adapter->work, pvscsi_msg_workqueue_handler, adapter); | |||||
return 1; | |||||
} | |||||
static irqreturn_t pvscsi_isr(int irq, void *devp) | |||||
{ | |||||
struct pvscsi_adapter *adapter = devp; | |||||
int handled; | |||||
if (adapter->use_msi || adapter->use_msix) | |||||
handled = TRUE; | |||||
else { | |||||
/* | |||||
* N.B INTx interrupts will not work with xpt_polled_action() | |||||
* The symptom will be a swatchdog involving dashutdown() | |||||
*/ | |||||
u32 val = pvscsi_read_intr_status(adapter); | |||||
handled = (val & PVSCSI_INTR_ALL_SUPPORTED) != 0; | |||||
if (handled) | |||||
pvscsi_write_intr_status(devp, val); | |||||
} | |||||
if (handled) { | |||||
/* Bump up SIM driver interrupt count across all instances */ | |||||
if (!atomic_fetchadd_long(&pvscsi_intr_count, 1)) { | |||||
/* On first interrupt received set bool to TRUE */ | |||||
pvscsi_intr_rcvd = TRUE; | |||||
} | |||||
spin_lock_irqsave(&adapter->hw_lock, flags); | |||||
adapter->pvs_intrcnt++; /* SIM instance count bumped */ | |||||
pvscsi_process_completion_ring(adapter); | |||||
if (adapter->use_msg && pvscsi_msg_pending(adapter)) | |||||
queue_work(adapter->workqueue, &adapter->work); | |||||
spin_unlock_irqrestore(&adapter->hw_lock, flags); | |||||
} | |||||
return IRQ_RETVAL(handled); | |||||
} | |||||
static void | |||||
pvscsi_isr_freebsd(void *adapter) | |||||
{ | |||||
(void)pvscsi_isr(1, adapter); | |||||
} | |||||
static void | |||||
pvscsi_poll(struct cam_sim *sim) | |||||
{ | |||||
/* N.B. This mechanism will not work with INTx interrupts */ | |||||
pvscsinst_t *adapter = (pvscsinst_t *)cam_sim_softc(sim); | |||||
(void)pvscsi_isr(0, adapter); | |||||
} | |||||
static void pvscsi_free_sgls(const struct pvscsi_adapter *adapter) | |||||
{ | |||||
struct pvscsi_ctx *ctx = adapter->cmd_map; | |||||
unsigned i; | |||||
for (i = 0; i < adapter->req_depth; ++i, ++ctx) | |||||
free_page((unsigned long)ctx->sgl); | |||||
} | |||||
static bool | |||||
pvscsi_setup_intr(pvscsinst_t *adapter, void *isr, bool msix) | |||||
{ | |||||
int rid = 0; | |||||
struct resource *res; | |||||
int err; | |||||
device_t device = adapter->pvs_dev; | |||||
if (msix) { | |||||
int msix_vecs_needed = PVSCSI_NUM_MSIX; | |||||
rid++; /* RID 1 in the interrupt space is for MSIX interrupts */ | |||||
if (pci_msix_count(adapter->pvs_dev) < PVSCSI_NUM_MSIX) { | |||||
device_printf(device, "pci_msix_count():%d < " | |||||
"PVSCSI_NUM_MSIX\n", | |||||
pci_msix_count(adapter->pvs_dev)); | |||||
return false; | |||||
} | |||||
err = pci_alloc_msix(adapter->pvs_dev, &msix_vecs_needed); | |||||
if (err || (msix_vecs_needed < PVSCSI_NUM_MSIX)) { | |||||
device_printf(device, "retval>%d, " | |||||
"msix_vecs_needed>%d\n", | |||||
err, msix_vecs_needed); | |||||
return false; | |||||
} | |||||
} | |||||
res = bus_alloc_resource_any(adapter->pvs_dev, SYS_RES_IRQ, &rid, | |||||
RF_SHAREABLE | RF_ACTIVE); | |||||
if (!res) { | |||||
device_printf(device, "Couldn't allocate interrupt resource\n"); | |||||
if (msix) pci_release_msi(adapter->pvs_dev); | |||||
return false; | |||||
} | |||||
adapter->pvs_int_allocd = 1; | |||||
err = bus_setup_intr(adapter->pvs_dev, res, INTR_MPSAFE | INTR_TYPE_CAM, | |||||
NULL, isr, adapter, &adapter->pvs_intcookie); | |||||
if (err) { | |||||
device_printf(device, "bus_setup_intr()>%d\n", err); | |||||
bus_release_resource(adapter->pvs_dev, SYS_RES_IRQ, rid, res); | |||||
if (msix) pci_release_msi(adapter->pvs_dev); | |||||
return false; | |||||
} | |||||
adapter->pvs_int_allocd = 2; | |||||
adapter->pvs_intmsix = msix; | |||||
adapter->pvs_intres = res; | |||||
adapter->pvs_intrid = rid; | |||||
return true; | |||||
} | |||||
static void pvscsi_shutdown_intr(struct pvscsi_adapter *adapter) | |||||
{ | |||||
/* Undo the interrupt isr registration */ | |||||
if (adapter->pvs_int_allocd > 1) | |||||
bus_teardown_intr(adapter->pvs_dev, adapter->pvs_intres, | |||||
adapter->pvs_intcookie); | |||||
if (adapter->pvs_int_allocd > 0) | |||||
bus_release_resource(adapter->pvs_dev, SYS_RES_IRQ, | |||||
adapter->pvs_intrid, adapter->pvs_intres); | |||||
if (adapter->pvs_intmsix) pci_release_msi(adapter->pvs_dev); | |||||
} | |||||
static void pvscsi_release_resources(struct pvscsi_adapter *adapter) | |||||
{ | |||||
pvscsi_shutdown_intr(adapter); | |||||
if (adapter->workqueue) | |||||
destroy_workqueue(adapter->workqueue); | |||||
if (adapter->cmd_map) { | |||||
KASSERT(adapter->cmd_map_size, ("adapter->cmd_map_size is 0")); | |||||
pvscsi_free_sgls(adapter); | |||||
kfree(adapter->cmd_map, adapter->cmd_map_size); | |||||
} | |||||
if (adapter->rings_state) | |||||
pci_free_consistent(adapter->dev, PAGE_SIZE, | |||||
adapter->rings_state, adapter->ringStatePA); | |||||
if (adapter->req_ring) | |||||
pci_free_consistent(adapter->dev, | |||||
adapter->req_pages * PAGE_SIZE, | |||||
adapter->req_ring, adapter->reqRingPA); | |||||
if (adapter->cmp_ring) | |||||
pci_free_consistent(adapter->dev, | |||||
adapter->cmp_pages * PAGE_SIZE, | |||||
adapter->cmp_ring, adapter->cmpRingPA); | |||||
if (adapter->msg_ring) | |||||
pci_free_consistent(adapter->dev, | |||||
adapter->msg_pages * PAGE_SIZE, | |||||
adapter->msg_ring, adapter->msgRingPA); | |||||
if (adapter->cmd_map) { | |||||
} | |||||
ngieAuthorUnsubmitted Done Inline ActionsCopy-paste mistake? Looks like it should be deleted, but I need to look at the Linux driver first. ngie: Copy-paste mistake? Looks like it should be deleted, but I need to look at the Linux driver… | |||||
/* Undo the memory-mapped register mapping */ | |||||
bus_release_resource(adapter->pvs_dev, SYS_RES_MEMORY, | |||||
adapter->pvs_mmrid, adapter->pvs_mmres); | |||||
} | |||||
/* | |||||
* Allocate scatter gather lists. | |||||
* | |||||
* These are statically allocated. Trying to be clever was not worth it. | |||||
* | |||||
* Dynamic allocation can fail, and we can't go deeep into the memory | |||||
* allocator, since we're a SCSI driver, and trying too hard to allocate | |||||
* memory might generate disk I/O. We also don't want to fail disk I/O | |||||
* in that case because we can't get an allocation - the I/O could be | |||||
* trying to swap out data to free memory. Since that is pathological, | |||||
* just use a statically allocated scatter list. | |||||
* | |||||
*/ | |||||
static int pvscsi_allocate_sg(struct pvscsi_adapter *adapter) | |||||
{ | |||||
struct pvscsi_ctx *ctx; | |||||
int i; | |||||
ctx = adapter->cmd_map; | |||||
ASSERT_ON_COMPILE(sizeof(struct pvscsi_sg_list) <= PAGE_SIZE); | |||||
for (i = 0; i < adapter->req_depth; ++i, ++ctx) { | |||||
ctx->sgl = (void *)__get_free_page(GFP_KERNEL); | |||||
ctx->sglPA = 0; | |||||
BUG_ON(ctx->sglPA & PAGE_MASK); | |||||
if (!ctx->sgl) { | |||||
for (; i >= 0; --i, --ctx) { | |||||
free_page((unsigned long)ctx->sgl); | |||||
ctx->sgl = NULL; | |||||
} | |||||
return -ENOMEM; | |||||
} | |||||
} | |||||
return 0; | |||||
} | |||||
/* | |||||
* Query the device, fetch the config info and return the | |||||
* maximum number of targets on the adapter. In case of | |||||
* failure due to any reason return default i.e. 16. | |||||
*/ | |||||
static uint32 pvscsi_get_max_targets(struct pvscsi_adapter *adapter) | |||||
{ | |||||
PVSCSICmdDescConfigCmd cmd; | |||||
PVSCSIConfigPageHeader *header; | |||||
dma_addr_t configPagePA; | |||||
void *config_page; | |||||
uint32 numPhys; | |||||
ASSERT_ON_COMPILE(sizeof(PVSCSIConfigPageController) <= PAGE_SIZE); | |||||
numPhys = 16; | |||||
config_page = pci_alloc_consistent(adapter->dev, PAGE_SIZE, | |||||
&configPagePA); | |||||
if (!config_page) { | |||||
printk(KERN_INFO "pvscsi: failed to allocate memory for" | |||||
" config page\n"); | |||||
goto exit; | |||||
} | |||||
BUG_ON(configPagePA & PAGE_MASK); | |||||
/* Fetch config info from the device. */ | |||||
cmd.configPageAddress = QWORD(PVSCSI_CONFIG_CONTROLLER_ADDRESS, 0); | |||||
cmd.configPageNum = PVSCSI_CONFIG_PAGE_CONTROLLER; | |||||
cmd.cmpAddr = configPagePA; | |||||
cmd._pad = 0; | |||||
/* | |||||
* Mark the completion page header with error values. If the device | |||||
* completes the command successfully, it sets the status values to | |||||
* indicate success. | |||||
*/ | |||||
header = config_page; | |||||
memset(header, 0, sizeof *header); | |||||
header->hostStatus = BTSTAT_INVPARAM; | |||||
header->scsiStatus = SDSTAT_CHECK; | |||||
pvscsi_write_cmd_desc(adapter, PVSCSI_CMD_CONFIG, &cmd, sizeof cmd); | |||||
if (header->hostStatus == BTSTAT_SUCCESS && | |||||
header->scsiStatus == SDSTAT_GOOD) { | |||||
PVSCSIConfigPageController *config; | |||||
config = config_page; | |||||
numPhys = config->numPhys; | |||||
} else | |||||
printk(KERN_INFO "pvscsi: PVSCSI_CMD_CONFIG failed." | |||||
" hostStatus = 0x%x, scsiStatus = 0x%x\n", | |||||
header->hostStatus, header->scsiStatus); | |||||
pci_free_consistent(adapter->dev, PAGE_SIZE, config_page, configPagePA); | |||||
exit: | |||||
return numPhys; | |||||
} | |||||
static void | |||||
pvscsi_action(struct cam_sim *psim, union ccb *pccb) | |||||
{ | |||||
pvscsinst_t * adapter = cam_sim_softc(psim); | |||||
device_t device = pvscsi_dev(adapter); | |||||
switch (pccb->ccb_h.func_code) { | |||||
default: { | |||||
/* | |||||
device_printf(device, "%s(%d) invoked for '%u'\n", | |||||
__FUNCTION__, pccb->ccb_h.func_code, | |||||
pccb->ccb_h.target_id); | |||||
*/ | |||||
pccb->ccb_h.status = CAM_REQ_INVALID; | |||||
xpt_done(pccb); | |||||
break; | |||||
} | |||||
case XPT_SCSI_IO: { | |||||
struct ccb_scsiio *csio = &pccb->csio; | |||||
LOG(0, "Command(0x%x) for %d:%d\n", | |||||
csio->cdb_io.cdb_bytes[0], | |||||
pccb->ccb_h.target_id, | |||||
pccb->ccb_h.target_lun); | |||||
struct scsi_cmnd command = { 0 }, *cmd = &command; | |||||
struct scsi_device quasi_sdev = { 0 }; | |||||
cmd->qsc_csio = csio; | |||||
cmd->device = &quasi_sdev; | |||||
if (csio->ccb_h.target_lun) { | |||||
pccb->ccb_h.status = CAM_LUN_INVALID; | |||||
xpt_done(pccb); | |||||
break; | |||||
} | |||||
cmd->cmd_len = csio->cdb_len; | |||||
if (csio->ccb_h.flags & CAM_CDB_POINTER) | |||||
cmd->cmnd = (void *)csio->cdb_io.cdb_ptr; | |||||
else | |||||
cmd->cmnd = (void *)&csio->cdb_io.cdb_bytes; | |||||
KASSERT(!(csio->ccb_h.flags & | |||||
(CAM_SENSE_PHYS|CAM_SENSE_PTR)), ("%x", | |||||
csio->ccb_h.flags)); /* We expect a struct */ | |||||
cmd->sense_buffer = &csio->sense_data; | |||||
#define CSIODIR (csio->ccb_h.flags & CAM_DIR_MASK) | |||||
if (CSIODIR == CAM_DIR_IN) | |||||
cmd->sc_data_direction = DMA_FROM_DEVICE; | |||||
else if (CSIODIR == CAM_DIR_OUT) | |||||
cmd->sc_data_direction = DMA_TO_DEVICE; | |||||
else if (CSIODIR == CAM_DIR_NONE) | |||||
cmd->sc_data_direction = DMA_NONE; | |||||
else | |||||
cmd->sc_data_direction = 0; | |||||
quasi_sdev.channel = 0; | |||||
quasi_sdev.id = csio->ccb_h.target_id; | |||||
quasi_sdev.lun = csio->ccb_h.target_lun; | |||||
if (csio->ccb_h.flags & CAM_TAG_ACTION_VALID) { | |||||
quasi_sdev.tagged_supported = true; | |||||
cmd->tag = csio->tag_action; | |||||
} | |||||
cmd->adapter = adapter; | |||||
pvscsi_queue_locked(cmd, NULL); | |||||
break; | |||||
} | |||||
case XPT_PATH_INQ: { | |||||
struct ccb_pathinq *cpi = &pccb->cpi; | |||||
cpi->version_num = 1; | |||||
cpi->hba_inquiry = PI_TAG_ABLE; | |||||
cpi->target_sprt = 0; | |||||
cpi->hba_misc = 0; | |||||
cpi->hba_eng_cnt = 0; | |||||
cpi->max_target = adapter->pvs_max_targets - 1; | |||||
cpi->max_lun = 0; /* 7 or 0 */ | |||||
cpi->initiator_id = 7; | |||||
cpi->bus_id = cam_sim_bus(psim); | |||||
strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); | |||||
strncpy(cpi->hba_vid, "VMware", HBA_IDLEN); | |||||
strncpy(cpi->dev_name, cam_sim_name(psim), DEV_IDLEN); | |||||
cpi->unit_number = cam_sim_unit(psim); | |||||
cpi->transport = XPORT_SPI; | |||||
cpi->transport_version = 2; | |||||
cpi->protocol = PROTO_SCSI; | |||||
cpi->protocol_version = SCSI_REV_SPC2; | |||||
pccb->ccb_h.status = CAM_REQ_CMP; | |||||
xpt_done(pccb); | |||||
break; | |||||
} | |||||
case XPT_RESET_BUS: { | |||||
device_printf(device, "Bus reset initiated\n"); | |||||
pvscsi_bus_reset(adapter); | |||||
pccb->ccb_h.status = CAM_REQ_CMP; | |||||
xpt_done(pccb); | |||||
device_printf(device, "Bus reset completed\n"); | |||||
break; | |||||
} | |||||
case XPT_RESET_DEV: { | |||||
target_id_t trg = pccb->ccb_h.target_id; | |||||
if (pccb->ccb_h.target_lun) { | |||||
device_printf(device, "Non-zero LU number %lu\n", | |||||
pccb->ccb_h.target_lun); | |||||
pccb->ccb_h.status = CAM_LUN_INVALID; | |||||
xpt_done(pccb); | |||||
break; | |||||
} | |||||
device_printf(device, "target %d reset initiated\n", | |||||
trg); | |||||
pvscsi_device_reset(adapter, trg); | |||||
device_printf(device, "target %d reset completed\n", | |||||
trg); | |||||
pccb->ccb_h.status = CAM_REQ_CMP; | |||||
xpt_done(pccb); | |||||
break; | |||||
} | |||||
case XPT_ABORT: { | |||||
pvscsi_abort(adapter, | |||||
(struct ccb_scsiio *)(pccb->cab.abort_ccb)); | |||||
pccb->ccb_h.status = CAM_REQ_CMP; | |||||
xpt_done(pccb); | |||||
break; | |||||
} | |||||
case XPT_GET_TRAN_SETTINGS: { | |||||
struct ccb_trans_settings *cts = &pccb->cts; | |||||
cts->transport = XPORT_SPI; | |||||
cts->transport_version = 2; | |||||
cts->protocol = PROTO_SCSI; | |||||
cts->protocol_version = SCSI_REV_SPC2; | |||||
pccb->ccb_h.status = CAM_REQ_CMP; | |||||
xpt_done(pccb); | |||||
break; | |||||
} | |||||
} | |||||
return; | |||||
ngieAuthorUnsubmitted Not Done Inline ActionsRemove (unnecessary return;) ngie: Remove (unnecessary return;) | |||||
} | |||||
static int | |||||
pvscsi_pci_detach(device_t device) | |||||
{ | |||||
pvscsinst_t *adapter = device_get_softc(device); | |||||
PVSCSILCK; | |||||
xpt_async(AC_LOST_DEVICE, adapter->pvs_campath, NULL); | |||||
xpt_free_path(adapter->pvs_campath); | |||||
xpt_bus_deregister(cam_sim_path(adapter->pvs_camsim)); | |||||
cam_sim_free(adapter->pvs_camsim, true); | |||||
PVSCSIULCK; | |||||
mtx_destroy(&adapter->pvs_camlock); | |||||
free(adapter->pvs_tarrg, M_PVSCSI); | |||||
adapter->pvs_tarrg = NULL; | |||||
pvscsi_mask_intr(adapter); | |||||
pci_disable_io(device, SYS_RES_MEMORY); | |||||
pci_disable_busmaster(device); | |||||
pvscsi_release_resources(adapter); | |||||
return 0; | |||||
} | |||||
static int | |||||
pvscsi_pci_attach(device_t device) | |||||
{ | |||||
int retval; | |||||
struct resource *res = NULL; | |||||
pvscsinst_t *adapter = device_get_softc(device); | |||||
int rid = -1, i, error; | |||||
memset(adapter, 0, sizeof(*adapter)); | |||||
adapter->pvs_timeout_one_comm_targ = -1; | |||||
adapter->pvs_reset_target_on_timeout = 0; | |||||
adapter->pvs_int_allocd = 0; | |||||
retval = pci_enable_busmaster(device); | |||||
if(retval) { | |||||
device_printf(device, "Could not enable bus-mastering, %d", | |||||
retval); | |||||
return retval; | |||||
} | |||||
retval = pci_enable_io(device, SYS_RES_MEMORY); | |||||
if(retval) { | |||||
device_printf(device, "Could not enable memory range, %d", | |||||
retval); | |||||
pci_disable_busmaster(device); | |||||
return retval; | |||||
} | |||||
for (i = 0; i < PCIR_MAX_BAR_0; i++) { | |||||
rid = PCIR_BAR(i); | |||||
res = bus_alloc_resource_any(device, SYS_RES_MEMORY, &rid, | |||||
RF_ACTIVE); | |||||
if (res) | |||||
break; | |||||
} | |||||
if (!res) { | |||||
device_printf(device, "Could not find/activate memory range\n"); | |||||
goto out_disable_device; | |||||
} | |||||
LOG(0, "Acquired device registers at rid %d\n", rid); | |||||
adapter->pvs_mmres = res; | |||||
adapter->pvs_mmrid = rid; | |||||
adapter->pvs_mmtag = rman_get_bustag(adapter->pvs_mmres); | |||||
adapter->pvs_mmhndl = rman_get_bushandle(adapter->pvs_mmres); | |||||
adapter->pvs_dev = device; | |||||
ll_adapter_reset(adapter); | |||||
adapter->use_msg = pvscsi_setup_msg_workqueue(adapter); | |||||
error = pvscsi_allocate_rings(adapter); | |||||
if (error) { | |||||
printk(KERN_ERR "vmw_pvscsi: unable to allocate ring memory\n"); | |||||
goto out_release_resources; | |||||
} | |||||
/* | |||||
* Ask the device for max number of targets. | |||||
*/ | |||||
adapter->pvs_max_targets = pvscsi_get_max_targets(adapter); | |||||
device_printf(device, "Maximum number of targets is %u\n", | |||||
adapter->pvs_max_targets); | |||||
/* | |||||
* From this point on we should reset the adapter if anything goes | |||||
* wrong. | |||||
*/ | |||||
pvscsi_setup_all_rings(adapter); | |||||
adapter->cmd_map = kcalloc(adapter->req_depth, | |||||
sizeof(struct pvscsi_ctx), GFP_KERNEL); | |||||
if (!adapter->cmd_map) { | |||||
device_printf(device, "failed to allocate memory.\n"); | |||||
error = -ENOMEM; | |||||
goto out_reset_adapter; | |||||
} | |||||
adapter->cmd_map_size = adapter->req_depth * sizeof(struct pvscsi_ctx); | |||||
if (bus_dma_tag_create(NULL, 1, 0, BUS_SPACE_MAXADDR, | |||||
BUS_SPACE_MAXADDR, NULL, NULL, BUS_SPACE_MAXADDR, | |||||
PVSCSI_MAX_NUM_SG_ENTRIES_PER_SEGMENT, BUS_SPACE_MAXADDR, 0, | |||||
busdma_lock_mutex, &adapter->pvs_camlock, | |||||
&adapter->pvs_dmat) != 0) { | |||||
device_printf(device, "DMA tag\n"); | |||||
goto out_reset_adapter; | |||||
} | |||||
adapter->pvs_tarrg = malloc((sizeof(pvscsitarg_t)) * | |||||
adapter->pvs_max_targets, M_PVSCSI,M_WAITOK|M_ZERO); | |||||
if (!adapter->pvs_tarrg) { | |||||
goto out_reset_adapter; | |||||
} | |||||
#ifdef __ISILON__ | |||||
adapter->pvscsi_dbgfail_cnt = 0; | |||||
adapter->pvscsi_dbgfails = malloc(sizeof(struct pvscsi_dbgfail) * | |||||
IDISKFP_DBGFAILCNT, M_PVSCSI,M_WAITOK|M_ZERO); | |||||
if (!adapter->pvscsi_dbgfails) { | |||||
goto out_free_pvs_tarrg; | |||||
} | |||||
#endif | |||||
ngieAuthorUnsubmitted Done Inline ActionsRemove this from the upstream driver. ngie: Remove this from the upstream driver. | |||||
mtx_init(&adapter->pvs_camlock, "pvscsi camlock", NULL, MTX_DEF); | |||||
INIT_LIST_HEAD(&adapter->cmd_pool); | |||||
for (i = 0; i < adapter->req_depth; i++) { | |||||
struct pvscsi_ctx *ctx = adapter->cmd_map + i; | |||||
if (bus_dmamap_create(adapter->pvs_dmat, 0, &ctx->dmap) != 0) { | |||||
device_printf(device, "dmap alloc failed, %d\n", i); | |||||
goto out_delete_dmat; | |||||
} | |||||
ctx->adapter = adapter; | |||||
callout_init_mtx(&ctx->calloutx, &adapter->pvs_camlock, 0); | |||||
list_add(&ctx->list, &adapter->cmd_pool); | |||||
} | |||||
error = pvscsi_allocate_sg(adapter); | |||||
if (error) { | |||||
device_printf(device, "Unable to allocate s/g table\n"); | |||||
goto out_delete_dmat; | |||||
} | |||||
if (pvscsi_setup_intr(adapter, pvscsi_isr_freebsd, true)) { | |||||
device_printf(device, "Using MSI-X interrupts\n"); | |||||
adapter->use_msix = 1; | |||||
} else if (pvscsi_setup_intr(adapter, pvscsi_isr_freebsd, false)) { | |||||
device_printf(device, "Using INTx interrupts\n"); | |||||
adapter->use_msix = adapter->use_msi = 0; | |||||
} else { | |||||
goto out_delete_dmat; | |||||
} | |||||
SYSCTL_ADD_UINT(device_get_sysctl_ctx(device), | |||||
SYSCTL_CHILDREN(device_get_sysctl_tree(device)), OID_AUTO, | |||||
"drop_next_command_to_target", CTLFLAG_RW, | |||||
&adapter->pvs_timeout_one_comm_targ, 0U, | |||||
"Drop the next I/O to this target(for test purposes)"); | |||||
SYSCTL_ADD_UINT(device_get_sysctl_ctx(device), | |||||
SYSCTL_CHILDREN(device_get_sysctl_tree(device)), OID_AUTO, | |||||
"reset_target_on_command_timeout", CTLFLAG_RW, | |||||
&adapter->pvs_reset_target_on_timeout, 0U, | |||||
"Reset the target on I/O timing out(for test purposes)"); | |||||
#ifdef __ISILON__ | |||||
pvscsi_debugerr_add_sysctls(adapter); | |||||
#endif | |||||
ngieAuthorUnsubmitted Done Inline ActionsRemove this from the upstream driver. ngie: Remove this from the upstream driver. | |||||
/* Register with CAM as a SIM */ | |||||
adapter->pvs_camdevq = cam_simq_alloc(adapter->req_depth); | |||||
if (!adapter->pvs_camdevq) { | |||||
device_printf(device, "cam_simq_alloc(%d) failed\n", | |||||
adapter->req_depth); | |||||
goto out_delete_dmat; | |||||
} | |||||
adapter->pvs_camsim = cam_sim_alloc(pvscsi_action, pvscsi_poll, | |||||
"pvscsi", adapter, | |||||
device_get_unit(adapter->pvs_dev), | |||||
&adapter->pvs_camlock, adapter->req_depth, | |||||
adapter->req_depth, adapter->pvs_camdevq); | |||||
if (!adapter->pvs_camsim) { | |||||
device_printf(device, "cam_sim_alloc() failed\n"); | |||||
goto out_cam_simq; | |||||
} | |||||
PVSCSILCK; | |||||
if (xpt_bus_register(adapter->pvs_camsim, NULL, 0) != CAM_SUCCESS) { | |||||
PVSCSIULCK; | |||||
device_printf(device, "xpt_bus_register() failed\n"); | |||||
goto out_cam_sim; | |||||
} | |||||
if (xpt_create_path(&adapter->pvs_campath, NULL, | |||||
cam_sim_path(adapter->pvs_camsim), | |||||
CAM_TARGET_WILDCARD, | |||||
CAM_LUN_WILDCARD) != CAM_REQ_CMP) { | |||||
PVSCSIULCK; | |||||
device_printf(device, "xpt_create_path() failed\n"); | |||||
goto out_cam_busreg; | |||||
} | |||||
PVSCSIULCK; | |||||
PRINT_RINGSTATE(adapter); | |||||
device_printf(device, "softc:%p, unmasking interrupts w/intr-status " | |||||
ngieAuthorUnsubmitted Not Done Inline Actions
ngie: 1. Is there value in printing out the memory address for the adapter?
2. "w/" should be spelled… | |||||
"%d\n", adapter, pvscsi_read_intr_status(adapter)); | |||||
pvscsi_unmask_intr(adapter); | |||||
/* pvscsi_dma(adapter); */ | |||||
return 0; | |||||
out_cam_busreg: | |||||
xpt_bus_deregister(cam_sim_path(adapter->pvs_camsim)); | |||||
out_cam_sim: | |||||
cam_sim_free(adapter->pvs_camsim, false); | |||||
out_cam_simq: | |||||
cam_simq_free(adapter->pvs_camdevq); | |||||
out_delete_dmat: | |||||
bus_dma_tag_destroy(adapter->pvs_dmat); | |||||
mtx_destroy(&adapter->pvs_camlock); | |||||
#ifdef __ISILON__ | |||||
free(adapter->pvscsi_dbgfails, M_PVSCSI); | |||||
adapter->pvscsi_dbgfails = NULL; | |||||
out_free_pvs_tarrg: | |||||
#endif | |||||
ngieAuthorUnsubmitted Done Inline ActionsRemove this from the upstream driver. ngie: Remove this from the upstream driver. | |||||
free(adapter->pvs_tarrg, M_PVSCSI); | |||||
adapter->pvs_tarrg = NULL; | |||||
out_reset_adapter: | |||||
ll_adapter_reset(adapter); | |||||
out_release_resources: | |||||
pvscsi_release_resources(adapter); | |||||
out_disable_device: | |||||
pci_disable_io(device, SYS_RES_MEMORY); | |||||
pci_disable_busmaster(device); | |||||
return -ENXIO; | |||||
ngieAuthorUnsubmitted Done Inline ActionsProbably should fix this return code to work with FreeBSD (make positive instead of negative). ngie: Probably should fix this return code to work with FreeBSD (make positive instead of negative). | |||||
} | |||||
static int | |||||
pvscsi_pci_probe(device_t device) | |||||
{ | |||||
if ((pci_get_vendor(device) != PCI_VENDOR_ID_VMWARE) || | |||||
(pci_get_device(device) != PCI_DEVICE_ID_VMWARE_PVSCSI)) { | |||||
return ENXIO; | |||||
} | |||||
device_set_desc(device, "VMware para-virtual SCSI driver v1.1.2.0"); | |||||
return 0; | |||||
} | |||||
static device_method_t | |||||
pvscsi_pci_methods[] = { | |||||
/* Device interface */ | |||||
DEVMETHOD(device_probe, pvscsi_pci_probe), | |||||
DEVMETHOD(device_attach, pvscsi_pci_attach), | |||||
DEVMETHOD(device_detach, pvscsi_pci_detach), | |||||
{ 0, 0 } | |||||
ngieAuthorUnsubmitted Done Inline ActionsDEVMETHOD_END here. ngie: DEVMETHOD_END here. | |||||
}; | |||||
static driver_t pvscsi_pci_driver = { | |||||
"pvscsi", | |||||
pvscsi_pci_methods, | |||||
sizeof(pvscsinst_t), | |||||
}; | |||||
static int | |||||
pvscsi_mod(module_t modp, int modev, void *arg) | |||||
{ | |||||
switch(modev) { | |||||
case MOD_LOAD: { | |||||
/* One time module initialization here */ | |||||
break; | |||||
} | |||||
case MOD_UNLOAD: { | |||||
/* One time module uninitialization here */ | |||||
break; | |||||
} | |||||
default: | |||||
break; | |||||
} | |||||
return 0; | |||||
} | |||||
static devclass_t pvscsi_devclass; | |||||
DRIVER_MODULE(MODNM, pci, pvscsi_pci_driver, pvscsi_devclass, pvscsi_mod, NULL); | |||||
MODULE_VERSION(MODNM, 1); |
Suggest adding this copyright into the existing block