#ifndef _PCI_VIRTIO_SCSI_H_
#define _PCI_VIRTIO_SCSI_H_
#include "iov.h"
extern int pci_vtscsi_debug;
#define WPRINTF(msg, params...) PRINTLN("virtio-scsi: " msg, ##params)
#define DPRINTF(msg, params...) if (pci_vtscsi_debug) WPRINTF(msg, ##params)
#define VIRTIO_SCSI_MAX_CHANNEL 0
#define VIRTIO_SCSI_MAX_TARGET 255
#define VIRTIO_SCSI_MAX_LUN 16383
#define VIRTIO_SCSI_HDR_SEG 2
#define VIRTIO_SCSI_ADDL_Q 2
#define VIRTIO_SCSI_F_INOUT (1 << 0)
#define VIRTIO_SCSI_F_HOTPLUG (1 << 1)
#define VIRTIO_SCSI_F_CHANGE (1 << 2)
#define VTSCSI_DEF_RINGSZ 64
#define VTSCSI_MIN_RINGSZ 4
#define VTSCSI_MAX_RINGSZ 4096
#define VTSCSI_DEF_THR_PER_Q 16
#define VTSCSI_MIN_THR_PER_Q 1
#define VTSCSI_MAX_THR_PER_Q 256
#define VTSCSI_DEF_MAXSEG 64
#define VTSCSI_MIN_MAXSEG (VIRTIO_SCSI_HDR_SEG + 1)
#define VTSCSI_MAX_MAXSEG \
(4096 - VIRTIO_SCSI_HDR_SEG - SPLIT_IOV_ADDL_IOV)
#define VTSCSI_DEF_REQUESTQ 1
#define VTSCSI_MIN_REQUESTQ 1
#define VTSCSI_MAX_REQUESTQ (32 - VIRTIO_SCSI_ADDL_Q)
#define VTSCSI_IN_HEADER_LEN(_sc) \
(sizeof(struct pci_vtscsi_req_cmd_rd) + _sc->vss_config.cdb_size)
#define VTSCSI_OUT_HEADER_LEN(_sc) \
(sizeof(struct pci_vtscsi_req_cmd_wr) + _sc->vss_config.sense_size)
struct pci_vtscsi_config {
uint32_t num_queues;
uint32_t seg_max;
uint32_t max_sectors;
uint32_t cmd_per_lun;
uint32_t event_info_size;
uint32_t sense_size;
uint32_t cdb_size;
uint16_t max_channel;
uint16_t max_target;
uint32_t max_lun;
} __attribute__((packed));
STAILQ_HEAD(pci_vtscsi_req_queue, pci_vtscsi_request);
struct pci_vtscsi_queue {
struct pci_vtscsi_softc *vsq_sc;
struct vqueue_info *vsq_vq;
pthread_mutex_t vsq_rmtx;
pthread_mutex_t vsq_fmtx;
pthread_mutex_t vsq_qmtx;
pthread_cond_t vsq_cv;
struct pci_vtscsi_req_queue vsq_requests;
struct pci_vtscsi_req_queue vsq_free_requests;
LIST_HEAD(, pci_vtscsi_worker) vsq_workers;
};
struct pci_vtscsi_worker {
struct pci_vtscsi_queue *vsw_queue;
pthread_t vsw_thread;
bool vsw_exiting;
LIST_ENTRY(pci_vtscsi_worker) vsw_link;
};
struct pci_vtscsi_request {
struct pci_vtscsi_queue *vsr_queue;
struct iovec *vsr_iov;
struct iovec *vsr_iov_in;
struct iovec *vsr_iov_out;
struct iovec *vsr_data_iov_in;
struct iovec *vsr_data_iov_out;
struct pci_vtscsi_req_cmd_rd *vsr_cmd_rd;
struct pci_vtscsi_req_cmd_wr *vsr_cmd_wr;
void *vsr_backend;
size_t vsr_niov_in;
size_t vsr_niov_out;
size_t vsr_data_niov_in;
size_t vsr_data_niov_out;
uint32_t vsr_idx;
STAILQ_ENTRY(pci_vtscsi_request) vsr_link;
};
struct pci_vtscsi_target {
uint8_t vst_target;
int vst_fd;
int vst_max_sectors;
};
struct pci_vtscsi_softc {
struct virtio_softc vss_vs;
struct virtio_consts vss_vi_consts;
struct vqueue_info *vss_vq;
struct pci_vtscsi_queue *vss_queues;
pthread_mutex_t vss_mtx;
uint32_t vss_features;
size_t vss_num_target;
uint32_t vss_ctl_ringsz;
uint32_t vss_evt_ringsz;
uint32_t vss_req_ringsz;
uint32_t vss_thr_per_q;
struct pci_vtscsi_config vss_default_config;
struct pci_vtscsi_config vss_config;
struct pci_vtscsi_target *vss_targets;
struct pci_vtscsi_backend *vss_backend;
};
#define VIRTIO_SCSI_T_TMF 0
#define VIRTIO_SCSI_T_TMF_ABORT_TASK 0
#define VIRTIO_SCSI_T_TMF_ABORT_TASK_SET 1
#define VIRTIO_SCSI_T_TMF_CLEAR_ACA 2
#define VIRTIO_SCSI_T_TMF_CLEAR_TASK_SET 3
#define VIRTIO_SCSI_T_TMF_I_T_NEXUS_RESET 4
#define VIRTIO_SCSI_T_TMF_LOGICAL_UNIT_RESET 5
#define VIRTIO_SCSI_T_TMF_QUERY_TASK 6
#define VIRTIO_SCSI_T_TMF_QUERY_TASK_SET 7
#define VIRTIO_SCSI_T_TMF_MAX_FUNC VIRTIO_SCSI_T_TMF_QUERY_TASK_SET
#define VIRTIO_SCSI_S_FUNCTION_COMPLETE 0
#define VIRTIO_SCSI_S_FUNCTION_SUCCEEDED 10
#define VIRTIO_SCSI_S_FUNCTION_REJECTED 11
struct pci_vtscsi_ctrl_tmf {
const uint32_t type;
const uint32_t subtype;
const uint8_t lun[8];
const uint64_t id;
uint8_t response;
} __attribute__((packed));
#define VIRTIO_SCSI_T_AN_QUERY 1
#define VIRTIO_SCSI_EVT_ASYNC_OPERATIONAL_CHANGE 2
#define VIRTIO_SCSI_EVT_ASYNC_POWER_MGMT 4
#define VIRTIO_SCSI_EVT_ASYNC_EXTERNAL_REQUEST 8
#define VIRTIO_SCSI_EVT_ASYNC_MEDIA_CHANGE 16
#define VIRTIO_SCSI_EVT_ASYNC_MULTI_HOST 32
#define VIRTIO_SCSI_EVT_ASYNC_DEVICE_BUSY 64
struct pci_vtscsi_ctrl_an {
const uint32_t type;
const uint8_t lun[8];
const uint32_t event_requested;
uint32_t event_actual;
uint8_t response;
} __attribute__((packed));
#define VIRTIO_SCSI_S_OK 0
#define VIRTIO_SCSI_S_OVERRUN 1
#define VIRTIO_SCSI_S_ABORTED 2
#define VIRTIO_SCSI_S_BAD_TARGET 3
#define VIRTIO_SCSI_S_RESET 4
#define VIRTIO_SCSI_S_BUSY 5
#define VIRTIO_SCSI_S_TRANSPORT_FAILURE 6
#define VIRTIO_SCSI_S_TARGET_FAILURE 7
#define VIRTIO_SCSI_S_NEXUS_FAILURE 8
#define VIRTIO_SCSI_S_FAILURE 9
#define VIRTIO_SCSI_S_INCORRECT_LUN 12
struct pci_vtscsi_event {
uint32_t event;
uint8_t lun[8];
uint32_t reason;
} __attribute__((packed));
struct pci_vtscsi_req_cmd_rd {
const uint8_t lun[8];
const uint64_t id;
const uint8_t task_attr;
const uint8_t prio;
const uint8_t crn;
const uint8_t cdb[];
} __attribute__((packed));
#define VIRTIO_SCSI_S_SIMPLE 0
#define VIRTIO_SCSI_S_ORDERED 1
#define VIRTIO_SCSI_S_HEAD 2
#define VIRTIO_SCSI_S_ACA 3
struct pci_vtscsi_req_cmd_wr {
uint32_t sense_len;
uint32_t residual;
uint16_t status_qualifier;
uint8_t status;
uint8_t response;
uint8_t sense[];
} __attribute__((packed));
struct pci_vtscsi_backend {
const char *vsb_name;
int (*vsb_init)(struct pci_vtscsi_softc *,
struct pci_vtscsi_backend *, nvlist_t *);
int (*vsb_open)(struct pci_vtscsi_softc *, const char *,
long);
void (*vsb_reset)(struct pci_vtscsi_softc *);
void* (*vsb_req_alloc)(struct pci_vtscsi_softc *);
void (*vsb_req_clear)(void *);
void (*vsb_req_free)(void *);
void (*vsb_tmf_hdl)(struct pci_vtscsi_softc *, int,
struct pci_vtscsi_ctrl_tmf *);
void (*vsb_an_hdl)(struct pci_vtscsi_softc *, int,
struct pci_vtscsi_ctrl_an *);
int (*vsb_req_hdl)(struct pci_vtscsi_softc *, int,
struct pci_vtscsi_request *);
};
#define PCI_VTSCSI_BACKEND_SET(x) DATA_SET(pci_vtscsi_backend_set, x)
static inline bool
pci_vtscsi_check_lun(struct pci_vtscsi_softc *sc, const uint8_t *lun)
{
if (lun[0] == 0xC1)
return (false);
if (lun[0] != 0x01)
return (false);
if (lun[1] >= sc->vss_num_target)
return (false);
if (lun[1] != sc->vss_targets[lun[1]].vst_target)
return (false);
if (sc->vss_targets[lun[1]].vst_fd < 0)
return (false);
if (lun[2] != 0x00 && (lun[2] & 0xc0) != 0x40)
return (false);
if (lun[4] != 0 || lun[5] != 0 || lun[6] != 0 || lun[7] != 0)
return (false);
return (true);
}
static inline uint8_t
pci_vtscsi_get_target(struct pci_vtscsi_softc *sc, const uint8_t *lun)
{
assert(lun[0] == 0x01);
assert(lun[1] < sc->vss_num_target);
assert(lun[1] == sc->vss_targets[lun[1]].vst_target);
assert(sc->vss_targets[lun[1]].vst_fd >= 0);
assert(lun[2] == 0x00 || (lun[2] & 0xc0) == 0x40);
return (lun[1]);
}
static inline uint16_t
pci_vtscsi_get_lun(struct pci_vtscsi_softc *sc, const uint8_t *lun)
{
assert(lun[0] == 0x01);
assert(lun[1] < sc->vss_num_target);
assert(lun[1] == sc->vss_targets[lun[1]].vst_target);
assert(sc->vss_targets[lun[1]].vst_fd >= 0);
assert(lun[2] == 0x00 || (lun[2] & 0xc0) == 0x40);
return (((lun[2] << 8) | lun[3]) & 0x3fff);
}
#endif