Page MenuHomeFreeBSD

D4112.id10051.diff
No OneTemporary

D4112.id10051.diff

Index: sys/dev/vmware/pvscsi/compat_freebsd.h
===================================================================
--- /dev/null
+++ sys/dev/vmware/pvscsi/compat_freebsd.h
@@ -0,0 +1,273 @@
+/*
+ * Copyright (c) 2011-2013
+ * Isilon Systems, LLC. All rights reserved.
+ */
+
+/*
+ * Some of the code in here copied from src/sys/infiniband/legacy/fbsd_pci.h
+ */
+
+#ifndef _COMPAT_FREEBSD_H_
+#define _COMPAT_FREEBSD_H_
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/module.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <machine/bus.h>
+#include <sys/rman.h>
+#include <machine/resource.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/pciio.h>
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pcireg.h>
+#include <sys/sysctl.h>
+#include <sys/malloc.h>
+#include <sys/libkern.h>
+#include <sys/queue.h>
+#include <sys/taskqueue.h>
+#include <vm/vm.h>
+#include <vm/pmap.h>
+#include <vm/vm_extern.h>
+#include <sys/limits.h>
+#include <sys/cpu.h>
+
+#include <cam/cam.h>
+#include <cam/cam_ccb.h>
+#include <cam/cam_sim.h>
+#include <cam/cam_xpt_sim.h>
+#include <cam/cam_debug.h>
+
+#include <cam/scsi/scsi_all.h>
+#include <cam/scsi/scsi_message.h>
+
+#include "fbsd_list.h"
+
+#define printk printf
+
+#define KERN_WARNING "Warning:"
+#define KERN_DEBUG "Debug:"
+#define KERN_ERR "Error:"
+#define KERN_INFO "Info:"
+#define KERN_NOTICE "Notice:"
+
+typedef int8_t s8;
+typedef int16_t s16;
+typedef int32_t s32;
+typedef int64_t s64;
+
+typedef u_int8_t u8;
+typedef u_int16_t u16;
+typedef u_int32_t u32;
+typedef u_int64_t u64;
+
+typedef u_int16_t __be16;
+typedef u_int32_t __be32;
+typedef u_int64_t __be64;
+
+#define BUG_ON(x) KASSERT(!(x), ("BUG"))
+
+#define irqreturn_t int
+
+/* #define dev_dbg device_printf */
+#define LOG(d, ...)
+
+typedef bus_addr_t dma_addr_t;
+
+static inline vm_paddr_t
+virt_to_phys(void *va)
+{
+ uintptr_t v = (uintptr_t)va;
+ return (vtophys(v & ~PAGE_MASK)+(v&PAGE_MASK));
+}
+
+MALLOC_DECLARE(M_PVSCSI);
+MALLOC_DECLARE(M_PVSCSI_PCI);
+MALLOC_DECLARE(M_PVSCSI_SGL);
+
+static __inline void *
+vmalloc(size_t size, int flags)
+{
+ return malloc(size, M_PVSCSI, flags);
+}
+
+static __inline void
+vfree(void *addr)
+{
+ return free(addr, M_PVSCSI);
+}
+
+/*
+ * pci_alloc_consistent returns two values: the virtual address which
+ * you can use to access it from the CPU and dma_handle which you pass
+ * to the card.
+ */
+static inline void *
+pci_alloc_consistent_fn(unsigned int size, dma_addr_t *phys)
+{
+ void *r;
+
+ /*
+ * Note that BSD does not guarantee physically contiguous
+ * memory from malloc(), but contigmalloc is very slow.
+ */
+ r = contigmalloc(size, M_PVSCSI_PCI, M_WAITOK, 0ul, ~0ul, PAGE_SIZE, 0);
+ if (r)
+ *phys = virt_to_phys(r);
+
+ return(r);
+}
+
+#define pci_alloc_consistent(d,s,p) pci_alloc_consistent_fn(s,p)
+
+static inline dma_addr_t
+pci_map_single_fn(void *va, size_t len)
+{
+ bus_addr_t retval, lastb;
+
+ retval = virt_to_phys(va);
+ if (len) {
+ lastb = virt_to_phys((char *)va + len - 1);
+ KASSERT((lastb & ~PAGE_MASK) == (retval & ~PAGE_MASK),
+ ("%lx %lx %p %zu", lastb, retval, va, len));
+ }
+
+ return retval;
+}
+
+#define pci_map_single(dev,va,len,dir) pci_map_single_fn(va,len)
+
+static inline void *
+kcalloc_fn(size_t n, size_t size)
+{
+ if (size != 0 && n > ULONG_MAX / size)
+ return NULL;
+ return contigmalloc(n * size, M_PVSCSI_PCI, M_WAITOK|M_ZERO, 0ul,
+ ~0ul, PAGE_SIZE, 0);
+}
+
+#define kcalloc(n,s,f) kcalloc_fn(n,s)
+
+#define kfree(a,s) contigfree(a, s, M_PVSCSI_PCI);
+#define pci_free_consistent(d,s,a,h) contigfree(a, s, M_PVSCSI_PCI);
+
+
+static __inline void *__get_free_page_fn(void)
+{
+ return contigmalloc(PAGE_SIZE, M_PVSCSI_SGL, M_WAITOK,
+ 0ul, ~0ul, PAGE_SIZE, 0);
+}
+#define __get_free_page(f) __get_free_page_fn()
+
+static __inline void free_page(unsigned long p)
+{
+ contigfree((void *)p, PAGE_SIZE, M_PVSCSI_SGL);
+}
+
+struct workqueue_struct {
+ struct taskqueue *taskqueue;
+};
+
+struct work_struct {
+ struct task work_task;
+ struct taskqueue *taskqueue;
+ void (*fn)(struct work_struct *);
+};
+
+struct delayed_work {
+ struct work_struct work;
+ struct callout timer;
+};
+
+static inline struct workqueue_struct *
+_create_workqueue_common(char *name, int cpus)
+{
+ struct workqueue_struct *wq;
+
+ wq = vmalloc(sizeof(*wq), M_WAITOK);
+ wq->taskqueue = taskqueue_create((name), M_WAITOK,
+ taskqueue_thread_enqueue, &wq->taskqueue);
+ taskqueue_start_threads(&wq->taskqueue, cpus, PWAIT, "pvscsi_wq");
+
+ return (wq);
+}
+
+
+#define create_singlethread_workqueue(name) \
+ _create_workqueue_common(name, 1)
+
+static inline void
+_work_fn(void *context, int pending)
+{
+ struct work_struct *work;
+
+ work = context;
+ work->fn(work);
+}
+
+#define COMPAT_INIT_WORK(work, func, dud) \
+do { \
+ (work)->fn = (func); \
+ (work)->taskqueue = NULL; \
+ TASK_INIT(&(work)->work_task, 0, _work_fn, (work)); \
+} while (0)
+
+static inline void
+destroy_workqueue(struct workqueue_struct *wq)
+{
+ taskqueue_free(wq->taskqueue);
+ vfree(wq);
+}
+
+#define queue_work(q, work) \
+do { \
+ (work)->taskqueue = (q)->taskqueue; \
+ taskqueue_enqueue((q)->taskqueue, &(work)->work_task); \
+} while (0)
+
+
+/* Optimization barrier */
+/* The "volatile" is due to gcc bugs */
+#define barrier() __asm__ __volatile__("": : :"memory")
+
+#define __devinit
+
+#define IS_ALIGNED(x, a) (((x) & ((typeof(x))(a) - 1)) == 0)
+
+#define typeof __typeof
+
+#define list_first_entry(h, t, m) list_entry((h)->next, t, m)
+
+#define smp_processor_id() PCPU_GET(cpuid)
+#define SCSI_MLQUEUE_HOST_BUSY CAM_RESRC_UNAVAIL
+#define SUCCESS 0
+
+#define ASSERT_ON_COMPILE CTASSERT
+
+typedef uint8_t uint8;
+typedef uint16_t uint16;
+typedef uint32_t uint32;
+typedef uint64_t uint64;
+typedef uint64_t PA;
+
+#define QWORD(_hi, _lo) ((((uint64)(_hi)) << 32) | ((uint32)(_lo)))
+
+#define COMPAT_WORK_GET_DATA container_of
+
+#ifndef ISILON
+#define __container_of(entry, type, field, uniq) ({ \
+ const __typeof(((type *)NULL)->field) *uniq = (entry); \
+ \
+ (uniq == NULL) ? NULL : \
+ (type *)((uintptr_t)uniq - __offsetof(type, field)); \
+})
+/* Create a unique variable name to allow nested calls without shadowing */
+#define __UNIQ __CONCAT(__uniq, __COUNTER__)
+#ifndef container_of
+#define container_of(entry, type, field) \
+ __container_of(entry, type, field, __UNIQ)
+#endif
+#endif
+#endif /* _COMPAT_FREEBSD_H_ */
Index: sys/dev/vmware/pvscsi/fbsd_list.h
===================================================================
--- /dev/null
+++ sys/dev/vmware/pvscsi/fbsd_list.h
@@ -0,0 +1,24 @@
+#ifndef _FBSD_LIST_H_
+#define _FBSD_LIST_H_
+
+struct list_head {
+ struct list_head *next,*last;
+};
+#define INIT_LIST_HEAD(a) {(a)->next=(a)->last=(a);}
+#define list_entry(p,t,m) container_of(p, t, m)
+
+#define list_empty(a) ((a)->next==(a))
+
+#define list_del(a)\
+ {(a)->last->next=(a)->next;\
+ (a)->next->last=(a)->last;}
+
+#define list_del_rcu(a)\
+ {atomic_store_rel_ptr(&(a)->last->next, (a)->next);\
+ (a)->next->last=(a)->last;}
+
+#define list_add(a,b)\
+ {(a)->last=(b); (a)->next=(b)->next ;\
+ (a)->next->last=(a); (b)->next=(a);}
+
+#endif /* !_FBSD_LIST_H_ */
Index: sys/dev/vmware/pvscsi/vmw_pvscsi.h
===================================================================
--- /dev/null
+++ sys/dev/vmware/pvscsi/vmw_pvscsi.h
@@ -0,0 +1,603 @@
+/* ************************************************************************
+ * 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.
+ * ************************************************************************/
+
+#ifndef _VMW_PVSCSI_H_
+#define _VMW_PVSCSI_H_
+
+#include "compat_freebsd.h"
+#ifdef ISILON
+#include <geom/isi_disk.h>
+#include <geom/isi_disk_if.h>
+#include <geom/isi_diskevt.h>
+#endif
+#include <sys/reboot.h>
+
+#define PVSCSI_DRIVER_VERSION_STRING "1.0.1.0-k"
+
+#define PVSCSI_MAX_NUM_SG_ENTRIES_PER_SEGMENT 128
+
+#define MASK(n) ((1 << (n)) - 1) /* make an n-bit mask */
+
+#define PCI_VENDOR_ID_VMWARE 0x15AD
+#define PCI_DEVICE_ID_VMWARE_PVSCSI 0x07C0
+
+/*
+ * host adapter status/error codes
+ */
+enum HostBusAdapterStatus {
+ BTSTAT_SUCCESS = 0x00, // CCB complete normally with no errors
+ BTSTAT_LINKED_COMMAND_COMPLETED = 0x0a,
+ BTSTAT_LINKED_COMMAND_COMPLETED_WITH_FLAG = 0x0b,
+ BTSTAT_DATA_UNDERRUN = 0x0c,
+ BTSTAT_SELTIMEO = 0x11, // SCSI selection timeout
+ BTSTAT_DATARUN = 0x12, // data overrun/underrun
+ BTSTAT_BUSFREE = 0x13, // unexpected bus free
+ BTSTAT_INVPHASE = 0x14, // invalid bus phase or sequence requested by target
+ BTSTAT_INVCODE = 0x15, // invalid action code in outgoing mailbox
+ BTSTAT_INVOPCODE = 0x16, // invalid operation code in CCB
+ BTSTAT_LUNMISMATCH = 0x17, // linked CCB has different LUN from first CCB
+ BTSTAT_INVPARAM = 0x1a, // invalid parameter in CCB or segment list
+ BTSTAT_SENSFAILED = 0x1b, // auto request sense failed
+ BTSTAT_TAGREJECT = 0x1c, // SCSI II tagged queueing message rejected by target
+ BTSTAT_BADMSG = 0x1d, // unsupported message received by the host adapter
+ BTSTAT_HAHARDWARE = 0x20, // host adapter hardware failed
+ BTSTAT_NORESPONSE = 0x21, // target did not respond to SCSI ATN, sent a SCSI RST
+ BTSTAT_SENTRST = 0x22, // host adapter asserted a SCSI RST
+ BTSTAT_RECVRST = 0x23, // other SCSI devices asserted a SCSI RST
+ BTSTAT_DISCONNECT = 0x24, // target device reconnected improperly (w/o tag)
+ BTSTAT_BUSRESET = 0x25, // host adapter issued BUS device reset
+ BTSTAT_ABORTQUEUE = 0x26, // abort queue generated
+ BTSTAT_HASOFTWARE = 0x27, // host adapter software error
+ BTSTAT_HATIMEOUT = 0x30, // host adapter hardware timeout error
+ BTSTAT_SCSIPARITY = 0x34, // SCSI parity error detected
+};
+
+// scsi device status values
+typedef enum {
+ SDSTAT_GOOD = 0x00, // no errors
+ SDSTAT_CHECK = 0x02, // check condition
+ SDSTAT_CONDITION_MET = 0x04, // condition met
+ SDSTAT_BUSY = 0x08, // device busy
+ SDSTAT_INTERMEDIATE = 0x10,
+ SDSTAT_INTERMEDIATE_CONDITION = 0x14,
+ SDSTAT_RESERVATION_CONFLICT = 0x18, // device reserved by another host
+ SDSTAT_COMMAND_TERMINATED = 0x22,
+ SDSTAT_TASK_SET_FULL = 0x28,
+ SDSTAT_ACA_ACTIVE = 0x30,
+ SDSTAT_TASK_ABORTED = 0x40,
+} SCSIDeviceStatus;
+
+/*
+ * Register offsets.
+ *
+ * These registers are accessible both via i/o space and mm i/o.
+ */
+
+enum PVSCSIRegOffset {
+ PVSCSI_REG_OFFSET_COMMAND = 0x0,
+ PVSCSI_REG_OFFSET_COMMAND_DATA = 0x4,
+ PVSCSI_REG_OFFSET_COMMAND_STATUS = 0x8,
+ PVSCSI_REG_OFFSET_LAST_STS_0 = 0x100,
+ PVSCSI_REG_OFFSET_LAST_STS_1 = 0x104,
+ PVSCSI_REG_OFFSET_LAST_STS_2 = 0x108,
+ PVSCSI_REG_OFFSET_LAST_STS_3 = 0x10c,
+ PVSCSI_REG_OFFSET_INTR_STATUS = 0x100c,
+ PVSCSI_REG_OFFSET_INTR_MASK = 0x2010,
+ PVSCSI_REG_OFFSET_KICK_NON_RW_IO = 0x3014,
+ PVSCSI_REG_OFFSET_DEBUG = 0x3018,
+ PVSCSI_REG_OFFSET_KICK_RW_IO = 0x4018,
+};
+
+
+/*
+ * Configuration pages. Structure sizes are 4 byte multiples.
+ */
+
+enum PVSCSIConfigPageType {
+ PVSCSI_CONFIG_PAGE_CONTROLLER = 0x1958,
+ PVSCSI_CONFIG_PAGE_PHY = 0x1959,
+ PVSCSI_CONFIG_PAGE_DEVICE = 0x195a,
+};
+
+
+enum PVSCSIConfigPageAddressType {
+ PVSCSI_CONFIG_CONTROLLER_ADDRESS = 0x2120,
+ PVSCSI_CONFIG_BUSTARGET_ADDRESS = 0x2121,
+ PVSCSI_CONFIG_PHY_ADDRESS = 0x2122,
+};
+
+struct PVSCSIConfigPageHeader {
+ uint32 pageNum;
+ uint16 numDwords; /* Including the header. */
+ uint16 hostStatus;
+ uint16 scsiStatus;
+ uint16 reserved[3];
+} __packed;
+typedef struct PVSCSIConfigPageHeader PVSCSIConfigPageHeader;
+
+
+struct PVSCSIConfigPageDevice {
+ PVSCSIConfigPageHeader header;
+ uint64 deviceWWN;
+ uint64 phyWWN;
+ uint32 phyNum;
+ uint8 target;
+ uint8 bus;
+ uint8 reserved[2];
+} __packed;
+typedef struct PVSCSIConfigPageDevice PVSCSIConfigPageDevice;
+
+
+struct PVSCSIConfigPageController {
+ PVSCSIConfigPageHeader header;
+ uint64 nodeWWN; /* Device name as defined in the SAS spec. */
+ uint16 manufacturer[64];
+ uint16 serialNumber[64];
+ uint16 opromVersion[32];
+ uint16 hwVersion[32];
+ uint16 firmwareVersion[32];
+ uint32 numPhys;
+ uint8 useConsecutivePhyWWNs;
+ uint8 reserved[3];
+} __packed;
+typedef struct PVSCSIConfigPageController PVSCSIConfigPageController;
+
+
+/*
+ * Virtual h/w commands.
+ */
+
+enum PVSCSICommands {
+ PVSCSI_CMD_FIRST = 0, /* has to be first */
+
+ PVSCSI_CMD_ADAPTER_RESET = 1,
+ PVSCSI_CMD_ISSUE_SCSI = 2,
+ PVSCSI_CMD_SETUP_RINGS = 3,
+ PVSCSI_CMD_RESET_BUS = 4,
+ PVSCSI_CMD_RESET_DEVICE = 5,
+ PVSCSI_CMD_ABORT_CMD = 6,
+ PVSCSI_CMD_CONFIG = 7,
+ PVSCSI_CMD_SETUP_MSG_RING = 8,
+ PVSCSI_CMD_DEVICE_UNPLUG = 9,
+
+ PVSCSI_CMD_LAST = 10 /* has to be last */
+};
+
+/*
+ * Command descriptor for PVSCSI_CMD_RESET_DEVICE --
+ */
+
+struct PVSCSICmdDescResetDevice {
+ u32 target;
+ u8 lun[8];
+} __packed;
+
+/*
+ * Command descriptor for PVSCSI_CMD_ABORT_CMD --
+ *
+ * - currently does not support specifying the LUN.
+ * - _pad should be 0.
+ */
+
+struct PVSCSICmdDescAbortCmd {
+ u64 context;
+ u32 target;
+ u32 _pad;
+} __packed;
+
+/*
+ * Command descriptor for PVSCSI_CMD_SETUP_RINGS --
+ *
+ * Notes:
+ * - reqRingNumPages and cmpRingNumPages need to be power of two.
+ * - reqRingNumPages and cmpRingNumPages need to be different from 0,
+ * - reqRingNumPages and cmpRingNumPages need to be inferior to
+ * PVSCSI_SETUP_RINGS_MAX_NUM_PAGES.
+ */
+
+#define PVSCSI_SETUP_RINGS_MAX_NUM_PAGES 32
+struct PVSCSICmdDescSetupRings {
+ u32 reqRingNumPages;
+ u32 cmpRingNumPages;
+ u64 ringsStatePPN;
+ u64 reqRingPPNs[PVSCSI_SETUP_RINGS_MAX_NUM_PAGES];
+ u64 cmpRingPPNs[PVSCSI_SETUP_RINGS_MAX_NUM_PAGES];
+} __packed;
+
+
+/*
+ * Command descriptor for PVSCSI_CMD_CONFIG --
+ */
+
+struct PVSCSICmdDescConfigCmd {
+ PA cmpAddr;
+ uint64 configPageAddress;
+ uint32 configPageNum;
+ uint32 _pad;
+} __packed;
+typedef struct PVSCSICmdDescConfigCmd PVSCSICmdDescConfigCmd;
+
+
+/*
+ * Command descriptor for PVSCSI_CMD_SETUP_MSG_RING --
+ *
+ * Notes:
+ * - this command was not supported in the initial revision of the h/w
+ * interface. Before using it, you need to check that it is supported by
+ * writing PVSCSI_CMD_SETUP_MSG_RING to the 'command' register, then
+ * immediately after read the 'command status' register:
+ * * a value of -1 means that the cmd is NOT supported,
+ * * a value != -1 means that the cmd IS supported.
+ * If it's supported the 'command status' register should return:
+ * sizeof(PVSCSICmdDescSetupMsgRing) / sizeof(u32).
+ * - this command should be issued _after_ the usual SETUP_RINGS so that the
+ * RingsState page is already setup. If not, the command is a nop.
+ * - numPages needs to be a power of two,
+ * - numPages needs to be different from 0,
+ * - _pad should be zero.
+ */
+
+#define PVSCSI_SETUP_MSG_RING_MAX_NUM_PAGES 16
+
+struct PVSCSICmdDescSetupMsgRing {
+ u32 numPages;
+ u32 _pad;
+ u64 ringPPNs[PVSCSI_SETUP_MSG_RING_MAX_NUM_PAGES];
+} __packed;
+
+enum PVSCSIMsgType {
+ PVSCSI_MSG_DEV_ADDED = 0,
+ PVSCSI_MSG_DEV_REMOVED = 1,
+ PVSCSI_MSG_LAST = 2,
+};
+
+/*
+ * Msg descriptor.
+ *
+ * sizeof(struct PVSCSIRingMsgDesc) == 128.
+ *
+ * - type is of type enum PVSCSIMsgType.
+ * - the content of args depend on the type of event being delivered.
+ */
+
+struct PVSCSIRingMsgDesc {
+ u32 type;
+ u32 args[31];
+} __packed;
+
+struct PVSCSIMsgDescDevStatusChanged {
+ u32 type; /* PVSCSI_MSG_DEV _ADDED / _REMOVED */
+ u32 bus;
+ u32 target;
+ u8 lun[8];
+ u32 pad[27];
+} __packed;
+
+/*
+ * Rings state.
+ *
+ * - the fields:
+ * . msgProdIdx,
+ * . msgConsIdx,
+ * . msgNumEntriesLog2,
+ * .. are only used once the SETUP_MSG_RING cmd has been issued.
+ * - '_pad' helps to ensure that the msg related fields are on their own
+ * cache-line.
+ */
+
+struct PVSCSIRingsState {
+ u32 reqProdIdx;
+ u32 reqConsIdx;
+ u32 reqNumEntriesLog2;
+
+ u32 cmpProdIdx;
+ u32 cmpConsIdx;
+ u32 cmpNumEntriesLog2;
+
+ u8 _pad[104];
+
+ u32 msgProdIdx;
+ u32 msgConsIdx;
+ u32 msgNumEntriesLog2;
+} __packed;
+
+/*
+ * Request descriptor.
+ *
+ * sizeof(RingReqDesc) = 128
+ *
+ * - context: is a unique identifier of a command. It could normally be any
+ * 64bit value, however we currently store it in the serialNumber variable
+ * of struct SCSI_Command, so we have the following restrictions due to the
+ * way this field is handled in the vmkernel storage stack:
+ * * this value can't be 0,
+ * * the upper 32bit need to be 0 since serialNumber is as a u32.
+ * Currently tracked as PR 292060.
+ * - dataLen: contains the total number of bytes that need to be transferred.
+ * - dataAddr:
+ * * if PVSCSI_FLAG_CMD_WITH_SG_LIST is set: dataAddr is the PA of the first
+ * s/g table segment, each s/g segment is entirely contained on a single
+ * page of physical memory,
+ * * if PVSCSI_FLAG_CMD_WITH_SG_LIST is NOT set, then dataAddr is the PA of
+ * the buffer used for the DMA transfer,
+ * - flags:
+ * * PVSCSI_FLAG_CMD_WITH_SG_LIST: see dataAddr above,
+ * * PVSCSI_FLAG_CMD_DIR_NONE: no DMA involved,
+ * * PVSCSI_FLAG_CMD_DIR_TOHOST: transfer from device to main memory,
+ * * PVSCSI_FLAG_CMD_DIR_TODEVICE: transfer from main memory to device,
+ * * PVSCSI_FLAG_CMD_OUT_OF_BAND_CDB: reserved to handle CDBs larger than
+ * 16bytes. To be specified.
+ * - vcpuHint: vcpuId of the processor that will be most likely waiting for the
+ * completion of the i/o. For guest OSes that use lowest priority message
+ * delivery mode (such as windows), we use this "hint" to deliver the
+ * completion action to the proper vcpu. For now, we can use the vcpuId of
+ * the processor that initiated the i/o as a likely candidate for the vcpu
+ * that will be waiting for the completion..
+ * - bus should be 0: we currently only support bus 0 for now.
+ * - unused should be zero'd.
+ */
+
+#define PVSCSI_FLAG_CMD_WITH_SG_LIST (1 << 0)
+#define PVSCSI_FLAG_CMD_OUT_OF_BAND_CDB (1 << 1)
+#define PVSCSI_FLAG_CMD_DIR_NONE (1 << 2)
+#define PVSCSI_FLAG_CMD_DIR_TOHOST (1 << 3)
+#define PVSCSI_FLAG_CMD_DIR_TODEVICE (1 << 4)
+
+struct PVSCSIRingReqDesc {
+ u64 context;
+ u64 dataAddr;
+ u64 dataLen;
+ u64 senseAddr;
+ u32 senseLen;
+ u32 flags;
+ u8 cdb[16];
+ u8 cdbLen;
+ u8 lun[8];
+ u8 tag;
+ u8 bus;
+ u8 target;
+ u8 vcpuHint;
+ u8 unused[59];
+} __packed;
+
+/*
+ * Scatter-gather list management.
+ *
+ * As described above, when PVSCSI_FLAG_CMD_WITH_SG_LIST is set in the
+ * RingReqDesc.flags, then RingReqDesc.dataAddr is the PA of the first s/g
+ * table segment.
+ *
+ * - each segment of the s/g table contain a succession of struct
+ * PVSCSISGElement.
+ * - each segment is entirely contained on a single physical page of memory.
+ * - a "chain" s/g element has the flag PVSCSI_SGE_FLAG_CHAIN_ELEMENT set in
+ * PVSCSISGElement.flags and in this case:
+ * * addr is the PA of the next s/g segment,
+ * * length is undefined, assumed to be 0.
+ */
+
+struct PVSCSISGElement {
+ u64 addr;
+ u32 length;
+ u32 flags;
+} __packed;
+
+/*
+ * Completion descriptor.
+ *
+ * sizeof(RingCmpDesc) = 32
+ *
+ * - context: identifier of the command. The same thing that was specified
+ * under "context" as part of struct RingReqDesc at initiation time,
+ * - dataLen: number of bytes transferred for the actual i/o operation,
+ * - senseLen: number of bytes written into the sense buffer,
+ * - hostStatus: adapter status,
+ * - scsiStatus: device status,
+ * - _pad should be zero.
+ */
+
+struct PVSCSIRingCmpDesc {
+ u64 context;
+ u64 dataLen;
+ u32 senseLen;
+ u16 hostStatus;
+ u16 scsiStatus;
+ u32 _pad[2];
+} __packed;
+
+/*
+ * Interrupt status / IRQ bits.
+ */
+
+#define PVSCSI_INTR_CMPL_0 (1 << 0)
+#define PVSCSI_INTR_CMPL_1 (1 << 1)
+#define PVSCSI_INTR_CMPL_MASK MASK(2)
+
+#define PVSCSI_INTR_MSG_0 (1 << 2)
+#define PVSCSI_INTR_MSG_1 (1 << 3)
+#define PVSCSI_INTR_MSG_MASK (MASK(2) << 2)
+
+#define PVSCSI_INTR_ALL_SUPPORTED MASK(4)
+
+/*
+ * Number of MSI-X vectors supported.
+ */
+#define PVSCSI_MAX_INTRS 24
+
+/*
+ * Enumeration of supported MSI-X vectors
+ */
+#define PVSCSI_VECTOR_COMPLETION 0
+
+/*
+ * Misc constants for the rings.
+ */
+
+#define PVSCSI_MAX_NUM_PAGES_REQ_RING PVSCSI_SETUP_RINGS_MAX_NUM_PAGES
+#define PVSCSI_MAX_NUM_PAGES_CMP_RING PVSCSI_SETUP_RINGS_MAX_NUM_PAGES
+#define PVSCSI_MAX_NUM_PAGES_MSG_RING PVSCSI_SETUP_MSG_RING_MAX_NUM_PAGES
+
+#define PVSCSI_MAX_NUM_REQ_ENTRIES_PER_PAGE \
+ (PAGE_SIZE / sizeof(struct PVSCSIRingReqDesc))
+
+#define PVSCSI_MAX_REQ_QUEUE_DEPTH \
+ (PVSCSI_MAX_NUM_PAGES_REQ_RING * PVSCSI_MAX_NUM_REQ_ENTRIES_PER_PAGE)
+
+#define PVSCSI_MEM_SPACE_COMMAND_NUM_PAGES 1
+#define PVSCSI_MEM_SPACE_INTR_STATUS_NUM_PAGES 1
+#define PVSCSI_MEM_SPACE_MISC_NUM_PAGES 2
+#define PVSCSI_MEM_SPACE_KICK_IO_NUM_PAGES 2
+#define PVSCSI_MEM_SPACE_MSIX_NUM_PAGES 2
+
+enum PVSCSIMemSpace {
+ PVSCSI_MEM_SPACE_COMMAND_PAGE = 0,
+ PVSCSI_MEM_SPACE_INTR_STATUS_PAGE = 1,
+ PVSCSI_MEM_SPACE_MISC_PAGE = 2,
+ PVSCSI_MEM_SPACE_KICK_IO_PAGE = 4,
+ PVSCSI_MEM_SPACE_MSIX_TABLE_PAGE = 6,
+ PVSCSI_MEM_SPACE_MSIX_PBA_PAGE = 7,
+};
+
+#define PVSCSI_MEM_SPACE_NUM_PAGES \
+ (PVSCSI_MEM_SPACE_COMMAND_NUM_PAGES + \
+ PVSCSI_MEM_SPACE_INTR_STATUS_NUM_PAGES + \
+ PVSCSI_MEM_SPACE_MISC_NUM_PAGES + \
+ PVSCSI_MEM_SPACE_KICK_IO_NUM_PAGES + \
+ PVSCSI_MEM_SPACE_MSIX_NUM_PAGES)
+
+#define PVSCSI_MEM_SPACE_SIZE (PVSCSI_MEM_SPACE_NUM_PAGES * PAGE_SIZE)
+
+struct pvscsi_sg_list {
+ struct PVSCSISGElement sge[PVSCSI_MAX_NUM_SG_ENTRIES_PER_SEGMENT];
+};
+
+#define PVSCSI_ABORT_TIMEOUT 5
+struct pvscsi_adapter;
+
+struct pvscsi_ctx {
+ /*
+ * The index of the context in cmd_map serves as the context ID for a
+ * 1-to-1 mapping completions back to requests.
+ */
+ struct pvscsi_sg_list *sgl;
+ struct list_head list;
+ dma_addr_t dataPA;
+ dma_addr_t sensePA;
+ dma_addr_t sglPA;
+ struct ccb_scsiio *cmd;
+ int dmamapping_errno;
+ struct PVSCSIRingReqDesc *e;
+ bus_dmamap_t dmap;
+ struct callout calloutx;
+ bool toed;
+ bool debugerr_checked;
+ struct PVSCSIRingCmpDesc cmpcpy;
+ struct pvscsi_adapter *adapter;
+};
+
+typedef struct pvscsitarg {
+ uint16_t pvt_ntos; /* number of timeouts since last reset */
+ uint16_t pvt_ntrs; /* number of target resets */
+} pvscsitarg_t;
+
+#include <sys/fail.h>
+
+struct pvscsi_dbgfail {
+ uint32_t ctrl; /* See sys/geom/isi_disk.h */
+ uint32_t unit;
+ uint64_t lba;
+ struct fail_point rate;
+};
+
+/* Expects a pvscsinst_t *adapter has been initialized */
+#define PVSCSILCK mtx_lock(&((adapter)->pvs_camlock))
+#define PVSCSIULCK mtx_unlock(&((adapter)->pvs_camlock))
+
+
+struct pvscsi_adapter {
+ char *mmioBase;
+ unsigned int irq;
+ u8 rev;
+ char use_msi;
+ char use_msix;
+ char use_msg;
+
+ struct mtx pvs_camlock;
+
+ struct workqueue_struct *workqueue;
+ struct work_struct work;
+
+ struct PVSCSIRingReqDesc *req_ring;
+ unsigned req_pages;
+ unsigned req_depth;
+ dma_addr_t reqRingPA;
+
+ struct PVSCSIRingCmpDesc *cmp_ring;
+ unsigned cmp_pages;
+ dma_addr_t cmpRingPA;
+
+ struct PVSCSIRingMsgDesc *msg_ring;
+ unsigned msg_pages;
+ dma_addr_t msgRingPA;
+
+ struct PVSCSIRingsState *rings_state;
+ dma_addr_t ringStatePA;
+
+
+ struct list_head cmd_pool;
+ struct pvscsi_ctx *cmd_map;
+ unsigned long cmd_map_size;
+ device_t pvs_dev;
+
+ struct resource *pvs_mmres;
+ int pvs_mmrid;
+ bus_space_handle_t pvs_mmhndl;
+ bus_space_tag_t pvs_mmtag;
+
+ bool pvs_intmsix;
+ struct resource *pvs_intres;
+ int pvs_intrid;
+ void *pvs_intcookie;
+ struct cam_devq *pvs_camdevq;
+ struct cam_sim *pvs_camsim;
+ struct cam_path *pvs_campath;
+ bus_dma_tag_t pvs_dmat;
+ int pvs_int_allocd;
+
+ uint32 pvs_max_targets;
+ pvscsitarg_t *pvs_tarrg;
+ target_id_t pvs_timeout_one_comm_targ;
+ uint32 pvs_reset_target_on_timeout;
+ uint64 pvs_intrcnt;
+
+ struct pvscsi_dbgfail *pvscsi_dbgfails;
+ int pvscsi_dbgfail_cnt;
+};
+
+void pvscsi_complete_request(struct pvscsi_adapter *adapter,
+ const struct PVSCSIRingCmpDesc *e);
+
+#ifdef ISILON
+#include "isln_pvscsi.h"
+#endif
+#endif /* _VMW_PVSCSI_H_ */
Index: sys/dev/vmware/pvscsi/vmw_pvscsi.c
===================================================================
--- /dev/null
+++ sys/dev/vmware/pvscsi/vmw_pvscsi.c
@@ -0,0 +1,1894 @@
+/*
+ * Copyright (c) 2014 EMC Corporation, Inc. All rights reserved.
+ */
+
+/* ************************************************************************
+ * 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);
+ 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
+
+ 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 */
+
+
+ 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
+
+/*
+ * 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);
+ }
+
+ 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;
+
+
+ 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
+
+ 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
+ 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),
+ "Could'nt post drive %s DISKEVT. e:t:l %d:%d:%d\n",
+ lost ? "remove" : "add", eventnum, trg, lun);
+ }
+#endif
+}
+
+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];
+
+ 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) {
+ }
+
+ /* 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;
+}
+
+
+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
+ 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
+
+ /* 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 "
+ "%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
+ 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;
+}
+
+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 }
+};
+
+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);
Index: sys/modules/vmware/Makefile
===================================================================
--- sys/modules/vmware/Makefile
+++ sys/modules/vmware/Makefile
@@ -23,6 +23,7 @@
# SUCH DAMAGE.
#
-SUBDIR= vmxnet3
+SUBDIR= vmxnet3
+SUBDIR+= pvscsi
.include <bsd.subdir.mk>
Index: sys/modules/vmware/pvscsi/Makefile
===================================================================
--- /dev/null
+++ sys/modules/vmware/pvscsi/Makefile
@@ -0,0 +1,10 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../../dev/vmware/pvscsi
+DEBUG_FLAGS = -g
+
+KMOD= vmw_pvscsi
+SRCS= vmw_pvscsi.c
+SRCS+= opt_cam.h bus_if.h device_if.h pci_if.h
+
+.include <bsd.kmod.mk>

File Metadata

Mime Type
text/plain
Expires
Sun, Feb 15, 10:40 PM (16 h, 30 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
28758515
Default Alt Text
D4112.id10051.diff (78 KB)

Event Timeline