Index: head/sys/dev/nvme/nvme_private.h =================================================================== --- head/sys/dev/nvme/nvme_private.h (revision 303125) +++ head/sys/dev/nvme/nvme_private.h (revision 303126) @@ -1,537 +1,533 @@ /*- * Copyright (C) 2012-2014 Intel Corporation * 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. * * $FreeBSD$ */ #ifndef __NVME_PRIVATE_H__ #define __NVME_PRIVATE_H__ #include #include #include #include #include #include #include #include #include #include #include #include #include "nvme.h" #define DEVICE2SOFTC(dev) ((struct nvme_controller *) device_get_softc(dev)) MALLOC_DECLARE(M_NVME); #define IDT32_PCI_ID 0x80d0111d /* 32 channel board */ #define IDT8_PCI_ID 0x80d2111d /* 8 channel board */ /* * For commands requiring more than 2 PRP entries, one PRP will be * embedded in the command (prp1), and the rest of the PRP entries * will be in a list pointed to by the command (prp2). This means * that real max number of PRP entries we support is 32+1, which * results in a max xfer size of 32*PAGE_SIZE. */ #define NVME_MAX_PRP_LIST_ENTRIES (NVME_MAX_XFER_SIZE / PAGE_SIZE) #define NVME_ADMIN_TRACKERS (16) #define NVME_ADMIN_ENTRIES (128) /* min and max are defined in admin queue attributes section of spec */ #define NVME_MIN_ADMIN_ENTRIES (2) #define NVME_MAX_ADMIN_ENTRIES (4096) /* * NVME_IO_ENTRIES defines the size of an I/O qpair's submission and completion * queues, while NVME_IO_TRACKERS defines the maximum number of I/O that we * will allow outstanding on an I/O qpair at any time. The only advantage in * having IO_ENTRIES > IO_TRACKERS is for debugging purposes - when dumping * the contents of the submission and completion queues, it will show a longer * history of data. */ #define NVME_IO_ENTRIES (256) #define NVME_IO_TRACKERS (128) #define NVME_MIN_IO_TRACKERS (4) #define NVME_MAX_IO_TRACKERS (1024) /* * NVME_MAX_IO_ENTRIES is not defined, since it is specified in CC.MQES * for each controller. */ #define NVME_INT_COAL_TIME (0) /* disabled */ #define NVME_INT_COAL_THRESHOLD (0) /* 0-based */ #define NVME_MAX_NAMESPACES (16) #define NVME_MAX_CONSUMERS (2) #define NVME_MAX_ASYNC_EVENTS (8) #define NVME_DEFAULT_TIMEOUT_PERIOD (30) /* in seconds */ #define NVME_MIN_TIMEOUT_PERIOD (5) #define NVME_MAX_TIMEOUT_PERIOD (120) #define NVME_DEFAULT_RETRY_COUNT (4) /* Maximum log page size to fetch for AERs. */ #define NVME_MAX_AER_LOG_SIZE (4096) /* * Define CACHE_LINE_SIZE here for older FreeBSD versions that do not define * it. */ #ifndef CACHE_LINE_SIZE #define CACHE_LINE_SIZE (64) #endif /* * Use presence of the BIO_UNMAPPED flag to determine whether unmapped I/O * support and the bus_dmamap_load_bio API are available on the target * kernel. This will ease porting back to earlier stable branches at a * later point. */ #ifdef BIO_UNMAPPED #define NVME_UNMAPPED_BIO_SUPPORT #endif extern uma_zone_t nvme_request_zone; extern int32_t nvme_retry_count; struct nvme_completion_poll_status { struct nvme_completion cpl; boolean_t done; }; #define NVME_REQUEST_VADDR 1 #define NVME_REQUEST_NULL 2 /* For requests with no payload. */ #define NVME_REQUEST_UIO 3 #ifdef NVME_UNMAPPED_BIO_SUPPORT #define NVME_REQUEST_BIO 4 #endif struct nvme_request { struct nvme_command cmd; struct nvme_qpair *qpair; union { void *payload; struct bio *bio; } u; uint32_t type; uint32_t payload_size; boolean_t timeout; nvme_cb_fn_t cb_fn; void *cb_arg; int32_t retries; STAILQ_ENTRY(nvme_request) stailq; }; struct nvme_async_event_request { struct nvme_controller *ctrlr; struct nvme_request *req; struct nvme_completion cpl; uint32_t log_page_id; uint32_t log_page_size; uint8_t log_page_buffer[NVME_MAX_AER_LOG_SIZE]; }; struct nvme_tracker { TAILQ_ENTRY(nvme_tracker) tailq; struct nvme_request *req; struct nvme_qpair *qpair; struct callout timer; bus_dmamap_t payload_dma_map; uint16_t cid; uint64_t prp[NVME_MAX_PRP_LIST_ENTRIES]; bus_addr_t prp_bus_addr; bus_dmamap_t prp_dma_map; }; struct nvme_qpair { struct nvme_controller *ctrlr; uint32_t id; uint32_t phase; uint16_t vector; int rid; struct resource *res; void *tag; uint32_t num_entries; uint32_t num_trackers; uint32_t sq_tdbl_off; uint32_t cq_hdbl_off; uint32_t sq_head; uint32_t sq_tail; uint32_t cq_head; int64_t num_cmds; int64_t num_intr_handler_calls; struct nvme_command *cmd; struct nvme_completion *cpl; bus_dma_tag_t dma_tag; bus_dma_tag_t dma_tag_payload; bus_dmamap_t cmd_dma_map; uint64_t cmd_bus_addr; bus_dmamap_t cpl_dma_map; uint64_t cpl_bus_addr; TAILQ_HEAD(, nvme_tracker) free_tr; TAILQ_HEAD(, nvme_tracker) outstanding_tr; STAILQ_HEAD(, nvme_request) queued_req; struct nvme_tracker **act_tr; boolean_t is_enabled; struct mtx lock __aligned(CACHE_LINE_SIZE); } __aligned(CACHE_LINE_SIZE); struct nvme_namespace { struct nvme_controller *ctrlr; struct nvme_namespace_data data; uint16_t id; uint16_t flags; struct cdev *cdev; void *cons_cookie[NVME_MAX_CONSUMERS]; uint32_t stripesize; struct mtx lock; }; /* * One of these per allocated PCI device. */ struct nvme_controller { device_t dev; struct mtx lock; - struct cam_sim *sim; - struct cam_path *path; - int cam_ref; - uint32_t ready_timeout_in_ms; bus_space_tag_t bus_tag; bus_space_handle_t bus_handle; int resource_id; struct resource *resource; /* * The NVMe spec allows for the MSI-X table to be placed in BAR 4/5, * separate from the control registers which are in BAR 0/1. These * members track the mapping of BAR 4/5 for that reason. */ int bar4_resource_id; struct resource *bar4_resource; uint32_t msix_enabled; uint32_t force_intx; uint32_t enable_aborts; uint32_t num_io_queues; uint32_t num_cpus_per_ioq; /* Fields for tracking progress during controller initialization. */ struct intr_config_hook config_hook; uint32_t ns_identified; uint32_t queues_created; struct task reset_task; struct task fail_req_task; struct taskqueue *taskqueue; /* For shared legacy interrupt. */ int rid; struct resource *res; void *tag; bus_dma_tag_t hw_desc_tag; bus_dmamap_t hw_desc_map; /** maximum i/o size in bytes */ uint32_t max_xfer_size; /** minimum page size supported by this controller in bytes */ uint32_t min_page_size; /** interrupt coalescing time period (in microseconds) */ uint32_t int_coal_time; /** interrupt coalescing threshold */ uint32_t int_coal_threshold; /** timeout period in seconds */ uint32_t timeout_period; struct nvme_qpair adminq; struct nvme_qpair *ioq; struct nvme_registers *regs; struct nvme_controller_data cdata; struct nvme_namespace ns[NVME_MAX_NAMESPACES]; struct cdev *cdev; /** bit mask of warning types currently enabled for async events */ union nvme_critical_warning_state async_event_config; uint32_t num_aers; struct nvme_async_event_request aer[NVME_MAX_ASYNC_EVENTS]; void *cons_cookie[NVME_MAX_CONSUMERS]; uint32_t is_resetting; uint32_t is_initialized; uint32_t notification_sent; boolean_t is_failed; STAILQ_HEAD(, nvme_request) fail_req; }; #define nvme_mmio_offsetof(reg) \ offsetof(struct nvme_registers, reg) #define nvme_mmio_read_4(sc, reg) \ bus_space_read_4((sc)->bus_tag, (sc)->bus_handle, \ nvme_mmio_offsetof(reg)) #define nvme_mmio_write_4(sc, reg, val) \ bus_space_write_4((sc)->bus_tag, (sc)->bus_handle, \ nvme_mmio_offsetof(reg), val) #define nvme_mmio_write_8(sc, reg, val) \ do { \ bus_space_write_4((sc)->bus_tag, (sc)->bus_handle, \ nvme_mmio_offsetof(reg), val & 0xFFFFFFFF); \ bus_space_write_4((sc)->bus_tag, (sc)->bus_handle, \ nvme_mmio_offsetof(reg)+4, \ (val & 0xFFFFFFFF00000000UL) >> 32); \ } while (0); #if __FreeBSD_version < 800054 #define wmb() __asm volatile("sfence" ::: "memory") #define mb() __asm volatile("mfence" ::: "memory") #endif #define nvme_printf(ctrlr, fmt, args...) \ device_printf(ctrlr->dev, fmt, ##args) void nvme_ns_test(struct nvme_namespace *ns, u_long cmd, caddr_t arg); void nvme_ctrlr_cmd_identify_controller(struct nvme_controller *ctrlr, void *payload, nvme_cb_fn_t cb_fn, void *cb_arg); void nvme_ctrlr_cmd_identify_namespace(struct nvme_controller *ctrlr, uint16_t nsid, void *payload, nvme_cb_fn_t cb_fn, void *cb_arg); void nvme_ctrlr_cmd_set_interrupt_coalescing(struct nvme_controller *ctrlr, uint32_t microseconds, uint32_t threshold, nvme_cb_fn_t cb_fn, void *cb_arg); void nvme_ctrlr_cmd_get_error_page(struct nvme_controller *ctrlr, struct nvme_error_information_entry *payload, uint32_t num_entries, /* 0 = max */ nvme_cb_fn_t cb_fn, void *cb_arg); void nvme_ctrlr_cmd_get_health_information_page(struct nvme_controller *ctrlr, uint32_t nsid, struct nvme_health_information_page *payload, nvme_cb_fn_t cb_fn, void *cb_arg); void nvme_ctrlr_cmd_get_firmware_page(struct nvme_controller *ctrlr, struct nvme_firmware_page *payload, nvme_cb_fn_t cb_fn, void *cb_arg); void nvme_ctrlr_cmd_create_io_cq(struct nvme_controller *ctrlr, struct nvme_qpair *io_que, uint16_t vector, nvme_cb_fn_t cb_fn, void *cb_arg); void nvme_ctrlr_cmd_create_io_sq(struct nvme_controller *ctrlr, struct nvme_qpair *io_que, nvme_cb_fn_t cb_fn, void *cb_arg); void nvme_ctrlr_cmd_delete_io_cq(struct nvme_controller *ctrlr, struct nvme_qpair *io_que, nvme_cb_fn_t cb_fn, void *cb_arg); void nvme_ctrlr_cmd_delete_io_sq(struct nvme_controller *ctrlr, struct nvme_qpair *io_que, nvme_cb_fn_t cb_fn, void *cb_arg); void nvme_ctrlr_cmd_set_num_queues(struct nvme_controller *ctrlr, uint32_t num_queues, nvme_cb_fn_t cb_fn, void *cb_arg); void nvme_ctrlr_cmd_set_async_event_config(struct nvme_controller *ctrlr, union nvme_critical_warning_state state, nvme_cb_fn_t cb_fn, void *cb_arg); void nvme_ctrlr_cmd_abort(struct nvme_controller *ctrlr, uint16_t cid, uint16_t sqid, nvme_cb_fn_t cb_fn, void *cb_arg); void nvme_completion_poll_cb(void *arg, const struct nvme_completion *cpl); int nvme_ctrlr_construct(struct nvme_controller *ctrlr, device_t dev); void nvme_ctrlr_destruct(struct nvme_controller *ctrlr, device_t dev); void nvme_ctrlr_shutdown(struct nvme_controller *ctrlr); int nvme_ctrlr_hw_reset(struct nvme_controller *ctrlr); void nvme_ctrlr_reset(struct nvme_controller *ctrlr); /* ctrlr defined as void * to allow use with config_intrhook. */ void nvme_ctrlr_start_config_hook(void *ctrlr_arg); void nvme_ctrlr_submit_admin_request(struct nvme_controller *ctrlr, struct nvme_request *req); void nvme_ctrlr_submit_io_request(struct nvme_controller *ctrlr, struct nvme_request *req); void nvme_ctrlr_post_failed_request(struct nvme_controller *ctrlr, struct nvme_request *req); void nvme_qpair_construct(struct nvme_qpair *qpair, uint32_t id, uint16_t vector, uint32_t num_entries, uint32_t num_trackers, struct nvme_controller *ctrlr); void nvme_qpair_submit_tracker(struct nvme_qpair *qpair, struct nvme_tracker *tr); void nvme_qpair_process_completions(struct nvme_qpair *qpair); void nvme_qpair_submit_request(struct nvme_qpair *qpair, struct nvme_request *req); void nvme_qpair_reset(struct nvme_qpair *qpair); void nvme_qpair_fail(struct nvme_qpair *qpair); void nvme_qpair_manual_complete_request(struct nvme_qpair *qpair, struct nvme_request *req, uint32_t sct, uint32_t sc, boolean_t print_on_error); void nvme_admin_qpair_enable(struct nvme_qpair *qpair); void nvme_admin_qpair_disable(struct nvme_qpair *qpair); void nvme_admin_qpair_destroy(struct nvme_qpair *qpair); void nvme_io_qpair_enable(struct nvme_qpair *qpair); void nvme_io_qpair_disable(struct nvme_qpair *qpair); void nvme_io_qpair_destroy(struct nvme_qpair *qpair); int nvme_ns_construct(struct nvme_namespace *ns, uint16_t id, struct nvme_controller *ctrlr); void nvme_ns_destruct(struct nvme_namespace *ns); void nvme_sysctl_initialize_ctrlr(struct nvme_controller *ctrlr); void nvme_dump_command(struct nvme_command *cmd); void nvme_dump_completion(struct nvme_completion *cpl); static __inline void nvme_single_map(void *arg, bus_dma_segment_t *seg, int nseg, int error) { uint64_t *bus_addr = (uint64_t *)arg; if (error != 0) printf("nvme_single_map err %d\n", error); *bus_addr = seg[0].ds_addr; } static __inline struct nvme_request * _nvme_allocate_request(nvme_cb_fn_t cb_fn, void *cb_arg) { struct nvme_request *req; req = uma_zalloc(nvme_request_zone, M_NOWAIT | M_ZERO); if (req != NULL) { req->cb_fn = cb_fn; req->cb_arg = cb_arg; req->timeout = TRUE; } return (req); } static __inline struct nvme_request * nvme_allocate_request_vaddr(void *payload, uint32_t payload_size, nvme_cb_fn_t cb_fn, void *cb_arg) { struct nvme_request *req; req = _nvme_allocate_request(cb_fn, cb_arg); if (req != NULL) { req->type = NVME_REQUEST_VADDR; req->u.payload = payload; req->payload_size = payload_size; } return (req); } static __inline struct nvme_request * nvme_allocate_request_null(nvme_cb_fn_t cb_fn, void *cb_arg) { struct nvme_request *req; req = _nvme_allocate_request(cb_fn, cb_arg); if (req != NULL) req->type = NVME_REQUEST_NULL; return (req); } static __inline struct nvme_request * nvme_allocate_request_bio(struct bio *bio, nvme_cb_fn_t cb_fn, void *cb_arg) { struct nvme_request *req; req = _nvme_allocate_request(cb_fn, cb_arg); if (req != NULL) { #ifdef NVME_UNMAPPED_BIO_SUPPORT req->type = NVME_REQUEST_BIO; req->u.bio = bio; #else req->type = NVME_REQUEST_VADDR; req->u.payload = bio->bio_data; req->payload_size = bio->bio_bcount; #endif } return (req); } #define nvme_free_request(req) uma_zfree(nvme_request_zone, req) void nvme_notify_async_consumers(struct nvme_controller *ctrlr, const struct nvme_completion *async_cpl, uint32_t log_page_id, void *log_page_buffer, uint32_t log_page_size); void nvme_notify_fail_consumers(struct nvme_controller *ctrlr); void nvme_notify_new_controller(struct nvme_controller *ctrlr); void nvme_ctrlr_intx_handler(void *arg); #endif /* __NVME_PRIVATE_H__ */ Index: head/sys/dev/nvme/nvme_sim.c =================================================================== --- head/sys/dev/nvme/nvme_sim.c (nonexistent) +++ head/sys/dev/nvme/nvme_sim.c (revision 303126) @@ -0,0 +1,400 @@ +/*- + * Copyright (c) 2016 Netflix, 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, + * without modification, immediately at the beginning of the file. + * 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 ``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 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. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include // Yes, this is wrong. +#include + +#include "nvme_private.h" + +#define ccb_accb_ptr spriv_ptr0 +#define ccb_ctrlr_ptr spriv_ptr1 +static void nvme_sim_action(struct cam_sim *sim, union ccb *ccb); +static void nvme_sim_poll(struct cam_sim *sim); + +#define sim2softc(sim) ((struct nvme_sim_softc *)cam_sim_softc(sim)) +#define sim2ns(sim) (sim2softc(sim)->s_ns) +#define sim2ctrlr(sim) (sim2softc(sim)->s_ctrlr) + +struct nvme_sim_softc +{ + struct nvme_controller *s_ctrlr; + struct nvme_namespace *s_ns; + struct cam_sim *s_sim; + struct cam_path *s_path; +}; + +static void +nvme_sim_nvmeio_done(void *ccb_arg, const struct nvme_completion *cpl) +{ + union ccb *ccb = (union ccb *)ccb_arg; + + /* + * Let the periph know the completion, and let it sort out what + * it means. Make our best guess, though for the status code. + */ + memcpy(&ccb->nvmeio.cpl, cpl, sizeof(*cpl)); + if (nvme_completion_is_error(cpl)) + ccb->ccb_h.status = CAM_REQ_CMP_ERR; + else + ccb->ccb_h.status = CAM_REQ_CMP; + xpt_done(ccb); +} + +static void +nvme_sim_nvmeio(struct cam_sim *sim, union ccb *ccb) +{ + struct ccb_nvmeio *nvmeio = &ccb->nvmeio; + struct nvme_request *req; + void *payload; + uint32_t size; + struct nvme_controller *ctrlr; + + ctrlr = sim2ctrlr(sim); + payload = nvmeio->data_ptr; + size = nvmeio->dxfer_len; + /* SG LIST ??? */ + if ((nvmeio->ccb_h.flags & CAM_DATA_MASK) == CAM_DATA_BIO) + req = nvme_allocate_request_bio((struct bio *)payload, + nvme_sim_nvmeio_done, ccb); + else if (payload == NULL) + req = nvme_allocate_request_null(nvme_sim_nvmeio_done, ccb); + else + req = nvme_allocate_request_vaddr(payload, size, + nvme_sim_nvmeio_done, ccb); + + if (req == NULL) { + nvmeio->ccb_h.status = CAM_RESRC_UNAVAIL; + xpt_done(ccb); + return; + } + + memcpy(&req->cmd, &ccb->nvmeio.cmd, sizeof(ccb->nvmeio.cmd)); + + nvme_ctrlr_submit_io_request(ctrlr, req); + + ccb->ccb_h.status |= CAM_SIM_QUEUED; +} + +static void +nvme_sim_action(struct cam_sim *sim, union ccb *ccb) +{ + struct nvme_controller *ctrlr; + struct nvme_namespace *ns; + + CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE, + ("nvme_sim_action: func= %#x\n", + ccb->ccb_h.func_code)); + + /* + * XXX when we support multiple namespaces in the base driver we'll need + * to revisit how all this gets stored and saved in the periph driver's + * reserved areas. Right now we store all three in the softc of the sim. + */ + ns = sim2ns(sim); + ctrlr = sim2ctrlr(sim); + + printf("Sim action: ctrlr %p ns %p\n", ctrlr, ns); + + mtx_assert(&ctrlr->lock, MA_OWNED); + + switch (ccb->ccb_h.func_code) { + case XPT_CALC_GEOMETRY: /* Calculate Geometry Totally nuts ? XXX */ + /* + * Only meaningful for old-school SCSI disks since only the SCSI + * da driver generates them. Reject all these that slip through. + */ + /*FALLTHROUGH*/ + case XPT_ABORT: /* Abort the specified CCB */ + case XPT_EN_LUN: /* Enable LUN as a target */ + case XPT_TARGET_IO: /* Execute target I/O request */ + case XPT_ACCEPT_TARGET_IO: /* Accept Host Target Mode CDB */ + case XPT_CONT_TARGET_IO: /* Continue Host Target I/O Connection*/ + /* + * Only target mode generates these, and only for SCSI. They are + * all invalid/unsupported for NVMe. + */ + ccb->ccb_h.status = CAM_REQ_INVALID; + break; + case XPT_SET_TRAN_SETTINGS: + /* + * NVMe doesn't really have different transfer settings, but + * other parts of CAM think failure here is a big deal. + */ + ccb->ccb_h.status = CAM_REQ_CMP; + break; + case XPT_PATH_INQ: /* Path routing inquiry */ + { + struct ccb_pathinq *cpi = &ccb->cpi; + + /* + * NVMe may have multiple LUNs on the same path. Current generation + * of NVMe devives support only a single name space. Multiple name + * space drives are coming, but it's unclear how we should report + * them up the stack. + */ + cpi->version_num = 1; + cpi->hba_inquiry = 0; + cpi->target_sprt = 0; + cpi->hba_misc = PIM_UNMAPPED /* | PIM_NOSCAN */; + cpi->hba_eng_cnt = 0; + cpi->max_target = 0; + cpi->max_lun = ctrlr->cdata.nn; + cpi->maxio = nvme_ns_get_max_io_xfer_size(ns); + cpi->initiator_id = 0; + cpi->bus_id = cam_sim_bus(sim); + cpi->base_transfer_speed = 4000000; /* 4 GB/s 4 lanes pcie 3 */ + strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); + strncpy(cpi->hba_vid, "NVMe", HBA_IDLEN); + strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN); + cpi->unit_number = cam_sim_unit(sim); + cpi->transport = XPORT_NVME; /* XXX XPORT_PCIE ? */ + cpi->transport_version = 1; /* XXX Get PCIe spec ? */ + cpi->protocol = PROTO_NVME; + cpi->protocol_version = NVME_REV_1; /* Groks all 1.x NVMe cards */ + cpi->xport_specific.nvme.nsid = ns->id; + cpi->ccb_h.status = CAM_REQ_CMP; + break; + } + case XPT_GET_TRAN_SETTINGS: /* Get transport settings */ + { + struct ccb_trans_settings *cts; + struct ccb_trans_settings_nvme *nvmep; + struct ccb_trans_settings_nvme *nvmex; + + cts = &ccb->cts; + nvmex = &cts->xport_specific.nvme; + nvmep = &cts->proto_specific.nvme; + + nvmex->valid = CTS_NVME_VALID_SPEC; + nvmex->spec_major = 1; /* XXX read from card */ + nvmex->spec_minor = 2; + nvmex->spec_tiny = 0; + + nvmep->valid = CTS_NVME_VALID_SPEC; + nvmep->spec_major = 1; /* XXX read from card */ + nvmep->spec_minor = 2; + nvmep->spec_tiny = 0; + cts->transport = XPORT_NVME; + cts->protocol = PROTO_NVME; + cts->ccb_h.status = CAM_REQ_CMP; + break; + } + case XPT_TERM_IO: /* Terminate the I/O process */ + /* + * every driver handles this, but nothing generates it. Assume + * it's OK to just say 'that worked'. + */ + /*FALLTHROUGH*/ + case XPT_RESET_DEV: /* Bus Device Reset the specified device */ + case XPT_RESET_BUS: /* Reset the specified bus */ + /* + * NVMe doesn't really support physically resetting the bus. It's part + * of the bus scanning dance, so return sucess to tell the process to + * proceed. + */ + ccb->ccb_h.status = CAM_REQ_CMP; + break; + case XPT_NVME_IO: /* Execute the requested I/O operation */ + nvme_sim_nvmeio(sim, ccb); + return; /* no done */ + default: + ccb->ccb_h.status = CAM_REQ_INVALID; + break; + } + xpt_done(ccb); +} + +static void +nvme_sim_poll(struct cam_sim *sim) +{ + + nvme_ctrlr_intx_handler(sim2ctrlr(sim)); +} + +static void * +nvme_sim_new_controller(struct nvme_controller *ctrlr) +{ + struct cam_devq *devq; + int max_trans; + int unit; + struct nvme_sim_softc *sc = NULL; + + max_trans = 256;/* XXX not so simple -- must match queues */ + unit = device_get_unit(ctrlr->dev); + devq = cam_simq_alloc(max_trans); + if (devq == NULL) + return NULL; + + sc = malloc(sizeof(*sc), M_NVME, M_ZERO | M_WAITOK); + + sc->s_ctrlr = ctrlr; + + sc->s_sim = cam_sim_alloc(nvme_sim_action, nvme_sim_poll, + "nvme", sc, unit, &ctrlr->lock, max_trans, max_trans, devq); + if (sc->s_sim == NULL) { + printf("Failed to allocate a sim\n"); + cam_simq_free(devq); + free(sc, M_NVME); + return NULL; + } + + return sc; +} + +static void +nvme_sim_rescan_target(struct nvme_controller *ctrlr, struct cam_path *path) +{ + union ccb *ccb; + + ccb = xpt_alloc_ccb_nowait(); + if (ccb == NULL) { + printf("unable to alloc CCB for rescan\n"); + return; + } + + if (xpt_clone_path(&ccb->ccb_h.path, path) != CAM_REQ_CMP) { + printf("unable to copy path for rescan\n"); + xpt_free_ccb(ccb); + return; + } + + xpt_rescan(ccb); +} + +static void * +nvme_sim_new_ns(struct nvme_namespace *ns, void *sc_arg) +{ + struct nvme_sim_softc *sc = sc_arg; + struct nvme_controller *ctrlr = sc->s_ctrlr; + int i; + + sc->s_ns = ns; + + printf("Our SIM's softc %p ctrlr %p ns %p\n", sc, ctrlr, ns); + + /* + * XXX this is creating one bus per ns, but it should be one + * XXX target per controller, and one LUN per namespace. + * XXX Current drives only support one NS, so there's time + * XXX to fix it later when new drives arrive. + * + * XXX I'm pretty sure the xpt_bus_register() call below is + * XXX like super lame and it really belongs in the sim_new_ctrlr + * XXX callback. Then the create_path below would be pretty close + * XXX to being right. Except we should be per-ns not per-ctrlr + * XXX data. + */ + + mtx_lock(&ctrlr->lock); +/* Create bus */ + + /* + * XXX do I need to lock ctrlr->lock ? + * XXX do I need to lock the path? + * ata and scsi seem to in their code, but their discovery is + * somewhat more asynchronous. We're only every called one at a + * time, and nothing is in parallel. + */ + + i = 0; + if (xpt_bus_register(sc->s_sim, ctrlr->dev, 0) != CAM_SUCCESS) + goto error; + i++; + if (xpt_create_path(&sc->s_path, /*periph*/NULL, cam_sim_path(sc->s_sim), + 1, ns->id) != CAM_REQ_CMP) + goto error; + i++; + + sc->s_path->device->nvme_data = nvme_ns_get_data(ns); + sc->s_path->device->nvme_cdata = nvme_ctrlr_get_data(ns->ctrlr); + +/* Scan bus */ + printf("Initiate rescan of the bus\n"); + nvme_sim_rescan_target(ctrlr, sc->s_path); + + mtx_unlock(&ctrlr->lock); + + return ns; + +error: + switch (i) { + case 2: + xpt_free_path(sc->s_path); + case 1: + xpt_bus_deregister(cam_sim_path(sc->s_sim)); + case 0: + cam_sim_free(sc->s_sim, /*free_devq*/TRUE); + } + mtx_unlock(&ctrlr->lock); + return NULL; +} + +static void +nvme_sim_controller_fail(void *ctrlr_arg) +{ + /* XXX cleanup XXX */ +} + +struct nvme_consumer *consumer_cookie; + +static void +nvme_sim_init(void) +{ + + consumer_cookie = nvme_register_consumer(nvme_sim_new_ns, + nvme_sim_new_controller, NULL, nvme_sim_controller_fail); +} + +SYSINIT(nvme_sim_register, SI_SUB_DRIVERS, SI_ORDER_ANY, + nvme_sim_init, NULL); + +static void +nvme_sim_uninit(void) +{ + /* XXX Cleanup */ + + nvme_unregister_consumer(consumer_cookie); +} + +SYSUNINIT(nvme_sim_unregister, SI_SUB_DRIVERS, SI_ORDER_ANY, + nvme_sim_uninit, NULL); Property changes on: head/sys/dev/nvme/nvme_sim.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property