The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/nvme/nvme_sim.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * Copyright (c) 2016 Netflix, Inc.
    3  *
    4  * Redistribution and use in source and binary forms, with or without
    5  * modification, are permitted provided that the following conditions
    6  * are met:
    7  * 1. Redistributions of source code must retain the above copyright
    8  *    notice, this list of conditions and the following disclaimer,
    9  *    without modification, immediately at the beginning of the file.
   10  * 2. Redistributions in binary form must reproduce the above copyright
   11  *    notice, this list of conditions and the following disclaimer in the
   12  *    documentation and/or other materials provided with the distribution.
   13  *
   14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
   15  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   16  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
   17  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
   18  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
   19  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
   20  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
   21  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
   23  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   24  */
   25 
   26 #include <sys/cdefs.h>
   27 __FBSDID("$FreeBSD$");
   28 
   29 #include <sys/param.h>
   30 #include <sys/systm.h>
   31 #include <sys/buf.h>
   32 #include <sys/bus.h>
   33 #include <sys/conf.h>
   34 #include <sys/ioccom.h>
   35 #include <sys/malloc.h>
   36 #include <sys/proc.h>
   37 #include <sys/smp.h>
   38 
   39 #include <cam/cam.h>
   40 #include <cam/cam_ccb.h>
   41 #include <cam/cam_sim.h>
   42 #include <cam/cam_xpt_sim.h>
   43 #include <cam/cam_debug.h>
   44 
   45 #include <dev/pci/pcivar.h>
   46 #include <dev/pci/pcireg.h>
   47 
   48 #include "nvme_private.h"
   49 
   50 #define ccb_accb_ptr spriv_ptr0
   51 #define ccb_ctrlr_ptr spriv_ptr1
   52 static void     nvme_sim_action(struct cam_sim *sim, union ccb *ccb);
   53 static void     nvme_sim_poll(struct cam_sim *sim);
   54 
   55 #define sim2softc(sim)  ((struct nvme_sim_softc *)cam_sim_softc(sim))
   56 #define sim2ctrlr(sim)  (sim2softc(sim)->s_ctrlr)
   57 
   58 struct nvme_sim_softc
   59 {
   60         struct nvme_controller  *s_ctrlr;
   61         struct cam_sim          *s_sim;
   62         struct cam_path         *s_path;
   63 };
   64 
   65 static void
   66 nvme_sim_nvmeio_done(void *ccb_arg, const struct nvme_completion *cpl)
   67 {
   68         union ccb *ccb = (union ccb *)ccb_arg;
   69 
   70         /*
   71          * Let the periph know the completion, and let it sort out what
   72          * it means. Make our best guess, though for the status code.
   73          */
   74         memcpy(&ccb->nvmeio.cpl, cpl, sizeof(*cpl));
   75         ccb->ccb_h.status &= ~CAM_SIM_QUEUED;
   76         if (nvme_completion_is_error(cpl)) {
   77                 ccb->ccb_h.status = CAM_REQ_CMP_ERR;
   78                 xpt_done(ccb);
   79         } else {
   80                 ccb->ccb_h.status = CAM_REQ_CMP;
   81                 xpt_done_direct(ccb);
   82         }
   83 }
   84 
   85 static void
   86 nvme_sim_nvmeio(struct cam_sim *sim, union ccb *ccb)
   87 {
   88         struct ccb_nvmeio       *nvmeio = &ccb->nvmeio;
   89         struct nvme_request     *req;
   90         void                    *payload;
   91         uint32_t                size;
   92         struct nvme_controller *ctrlr;
   93 
   94         ctrlr = sim2ctrlr(sim);
   95         payload = nvmeio->data_ptr;
   96         size = nvmeio->dxfer_len;
   97         /* SG LIST ??? */
   98         if ((nvmeio->ccb_h.flags & CAM_DATA_MASK) == CAM_DATA_BIO)
   99                 req = nvme_allocate_request_bio((struct bio *)payload,
  100                     nvme_sim_nvmeio_done, ccb);
  101         else if ((nvmeio->ccb_h.flags & CAM_DATA_SG) == CAM_DATA_SG)
  102                 req = nvme_allocate_request_ccb(ccb, nvme_sim_nvmeio_done, ccb);
  103         else if (payload == NULL)
  104                 req = nvme_allocate_request_null(nvme_sim_nvmeio_done, ccb);
  105         else
  106                 req = nvme_allocate_request_vaddr(payload, size,
  107                     nvme_sim_nvmeio_done, ccb);
  108 
  109         if (req == NULL) {
  110                 nvmeio->ccb_h.status = CAM_RESRC_UNAVAIL;
  111                 xpt_done(ccb);
  112                 return;
  113         }
  114         ccb->ccb_h.status |= CAM_SIM_QUEUED;
  115 
  116         memcpy(&req->cmd, &ccb->nvmeio.cmd, sizeof(ccb->nvmeio.cmd));
  117 
  118         if (ccb->ccb_h.func_code == XPT_NVME_IO)
  119                 nvme_ctrlr_submit_io_request(ctrlr, req);
  120         else
  121                 nvme_ctrlr_submit_admin_request(ctrlr, req);
  122 }
  123 
  124 static uint32_t
  125 nvme_link_kBps(struct nvme_controller *ctrlr)
  126 {
  127         uint32_t speed, lanes, link[] = { 1, 250000, 500000, 985000, 1970000 };
  128         uint32_t status;
  129 
  130         status = pcie_read_config(ctrlr->dev, PCIER_LINK_STA, 2);
  131         speed = status & PCIEM_LINK_STA_SPEED;
  132         lanes = (status & PCIEM_LINK_STA_WIDTH) >> 4;
  133         /*
  134          * Failsafe on link speed indicator. If it is insane report the number of
  135          * lanes as the speed. Not 100% accurate, but may be diagnostic.
  136          */
  137         if (speed >= nitems(link))
  138                 speed = 0;
  139         return link[speed] * lanes;
  140 }
  141 
  142 static void
  143 nvme_sim_action(struct cam_sim *sim, union ccb *ccb)
  144 {
  145         struct nvme_controller *ctrlr;
  146 
  147         CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE,
  148             ("nvme_sim_action: func= %#x\n",
  149                 ccb->ccb_h.func_code));
  150 
  151         ctrlr = sim2ctrlr(sim);
  152 
  153         switch (ccb->ccb_h.func_code) {
  154         case XPT_CALC_GEOMETRY:         /* Calculate Geometry Totally nuts ? XXX */
  155                 /* 
  156                  * Only meaningful for old-school SCSI disks since only the SCSI
  157                  * da driver generates them. Reject all these that slip through.
  158                  */
  159                 /*FALLTHROUGH*/
  160         case XPT_ABORT:                 /* Abort the specified CCB */
  161                 ccb->ccb_h.status = CAM_REQ_INVALID;
  162                 break;
  163         case XPT_SET_TRAN_SETTINGS:
  164                 /*
  165                  * NVMe doesn't really have different transfer settings, but
  166                  * other parts of CAM think failure here is a big deal.
  167                  */
  168                 ccb->ccb_h.status = CAM_REQ_CMP;
  169                 break;
  170         case XPT_PATH_INQ:              /* Path routing inquiry */
  171         {
  172                 struct ccb_pathinq      *cpi = &ccb->cpi;
  173                 device_t                dev = ctrlr->dev;
  174 
  175                 cpi->version_num = 1;
  176                 cpi->hba_inquiry = 0;
  177                 cpi->target_sprt = 0;
  178                 cpi->hba_misc =  PIM_UNMAPPED | PIM_NOSCAN;
  179                 cpi->hba_eng_cnt = 0;
  180                 cpi->max_target = 0;
  181                 cpi->max_lun = ctrlr->cdata.nn;
  182                 cpi->maxio = ctrlr->max_xfer_size;
  183                 cpi->initiator_id = 0;
  184                 cpi->bus_id = cam_sim_bus(sim);
  185                 cpi->base_transfer_speed = nvme_link_kBps(ctrlr);
  186                 strlcpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN);
  187                 strlcpy(cpi->hba_vid, "NVMe", HBA_IDLEN);
  188                 strlcpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN);
  189                 cpi->unit_number = cam_sim_unit(sim);
  190                 cpi->transport = XPORT_NVME;            /* XXX XPORT_PCIE ? */
  191                 cpi->transport_version = nvme_mmio_read_4(ctrlr, vs);
  192                 cpi->protocol = PROTO_NVME;
  193                 cpi->protocol_version = nvme_mmio_read_4(ctrlr, vs);
  194                 cpi->xport_specific.nvme.nsid = xpt_path_lun_id(ccb->ccb_h.path);
  195                 cpi->xport_specific.nvme.domain = pci_get_domain(dev);
  196                 cpi->xport_specific.nvme.bus = pci_get_bus(dev);
  197                 cpi->xport_specific.nvme.slot = pci_get_slot(dev);
  198                 cpi->xport_specific.nvme.function = pci_get_function(dev);
  199                 cpi->xport_specific.nvme.extra = 0;
  200                 strncpy(cpi->xport_specific.nvme.dev_name, device_get_nameunit(dev),
  201                     sizeof(cpi->xport_specific.nvme.dev_name));
  202                 cpi->hba_vendor = pci_get_vendor(dev);
  203                 cpi->hba_device = pci_get_device(dev);
  204                 cpi->hba_subvendor = pci_get_subvendor(dev);
  205                 cpi->hba_subdevice = pci_get_subdevice(dev);
  206                 cpi->ccb_h.status = CAM_REQ_CMP;
  207                 break;
  208         }
  209         case XPT_GET_TRAN_SETTINGS:     /* Get transport settings */
  210         {
  211                 struct ccb_trans_settings       *cts;
  212                 struct ccb_trans_settings_nvme  *nvmep;
  213                 struct ccb_trans_settings_nvme  *nvmex;
  214                 device_t dev;
  215                 uint32_t status, caps, flags;
  216 
  217                 dev = ctrlr->dev;
  218                 cts = &ccb->cts;
  219                 nvmex = &cts->xport_specific.nvme;
  220                 nvmep = &cts->proto_specific.nvme;
  221 
  222                 status = pcie_read_config(dev, PCIER_LINK_STA, 2);
  223                 caps = pcie_read_config(dev, PCIER_LINK_CAP, 2);
  224                 flags = pcie_read_config(dev, PCIER_FLAGS, 2);
  225                 nvmex->spec = nvme_mmio_read_4(ctrlr, vs);
  226                 nvmex->valid = CTS_NVME_VALID_SPEC;
  227                 if ((flags & PCIEM_FLAGS_TYPE) == PCIEM_TYPE_ENDPOINT) {
  228                         nvmex->valid |= CTS_NVME_VALID_LINK;
  229                         nvmex->speed = status & PCIEM_LINK_STA_SPEED;
  230                         nvmex->lanes = (status & PCIEM_LINK_STA_WIDTH) >> 4;
  231                         nvmex->max_speed = caps & PCIEM_LINK_CAP_MAX_SPEED;
  232                         nvmex->max_lanes = (caps & PCIEM_LINK_CAP_MAX_WIDTH) >> 4;
  233                 }
  234 
  235                 /* XXX these should be something else maybe ? */
  236                 nvmep->valid = 1;
  237                 nvmep->spec = nvmex->spec;
  238 
  239                 cts->transport = XPORT_NVME;
  240                 cts->protocol = PROTO_NVME;
  241                 cts->ccb_h.status = CAM_REQ_CMP;
  242                 break;
  243         }
  244         case XPT_TERM_IO:               /* Terminate the I/O process */
  245                 /*
  246                  * every driver handles this, but nothing generates it. Assume
  247                  * it's OK to just say 'that worked'.
  248                  */
  249                 /*FALLTHROUGH*/
  250         case XPT_RESET_DEV:             /* Bus Device Reset the specified device */
  251         case XPT_RESET_BUS:             /* Reset the specified bus */
  252                 /*
  253                  * NVMe doesn't really support physically resetting the bus. It's part
  254                  * of the bus scanning dance, so return sucess to tell the process to
  255                  * proceed.
  256                  */
  257                 ccb->ccb_h.status = CAM_REQ_CMP;
  258                 break;
  259         case XPT_NVME_IO:               /* Execute the requested I/O operation */
  260         case XPT_NVME_ADMIN:            /* or Admin operation */
  261                 nvme_sim_nvmeio(sim, ccb);
  262                 return;                 /* no done */
  263         default:
  264                 ccb->ccb_h.status = CAM_REQ_INVALID;
  265                 break;
  266         }
  267         xpt_done(ccb);
  268 }
  269 
  270 static void
  271 nvme_sim_poll(struct cam_sim *sim)
  272 {
  273 
  274         nvme_ctrlr_poll(sim2ctrlr(sim));
  275 }
  276 
  277 static void *
  278 nvme_sim_new_controller(struct nvme_controller *ctrlr)
  279 {
  280         struct nvme_sim_softc *sc;
  281         struct cam_devq *devq;
  282         int max_trans;
  283 
  284         max_trans = ctrlr->max_hw_pend_io;
  285         devq = cam_simq_alloc(max_trans);
  286         if (devq == NULL)
  287                 return (NULL);
  288 
  289         sc = malloc(sizeof(*sc), M_NVME, M_ZERO | M_WAITOK);
  290         sc->s_ctrlr = ctrlr;
  291 
  292         sc->s_sim = cam_sim_alloc(nvme_sim_action, nvme_sim_poll,
  293             "nvme", sc, device_get_unit(ctrlr->dev),
  294             NULL, max_trans, max_trans, devq);
  295         if (sc->s_sim == NULL) {
  296                 printf("Failed to allocate a sim\n");
  297                 cam_simq_free(devq);
  298                 goto err1;
  299         }
  300         if (xpt_bus_register(sc->s_sim, ctrlr->dev, 0) != CAM_SUCCESS) {
  301                 printf("Failed to create a bus\n");
  302                 goto err2;
  303         }
  304         if (xpt_create_path(&sc->s_path, /*periph*/NULL, cam_sim_path(sc->s_sim),
  305             CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) {
  306                 printf("Failed to create a path\n");
  307                 goto err3;
  308         }
  309 
  310         return (sc);
  311 
  312 err3:
  313         xpt_bus_deregister(cam_sim_path(sc->s_sim));
  314 err2:
  315         cam_sim_free(sc->s_sim, /*free_devq*/TRUE);
  316 err1:
  317         free(sc, M_NVME);
  318         return (NULL);
  319 }
  320 
  321 static void *
  322 nvme_sim_ns_change(struct nvme_namespace *ns, void *sc_arg)
  323 {
  324         struct nvme_sim_softc *sc = sc_arg;
  325         union ccb *ccb;
  326 
  327         ccb = xpt_alloc_ccb_nowait();
  328         if (ccb == NULL) {
  329                 printf("unable to alloc CCB for rescan\n");
  330                 return (NULL);
  331         }
  332 
  333         /*
  334          * We map the NVMe namespace idea onto the CAM unit LUN. For
  335          * each new namespace, we create a new CAM path for it. We then
  336          * rescan the path to get it to enumerate.
  337          */
  338         if (xpt_create_path(&ccb->ccb_h.path, /*periph*/NULL,
  339             cam_sim_path(sc->s_sim), 0, ns->id) != CAM_REQ_CMP) {
  340                 printf("unable to create path for rescan\n");
  341                 xpt_free_ccb(ccb);
  342                 return (NULL);
  343         }
  344         xpt_rescan(ccb);
  345 
  346         return (sc_arg);
  347 }
  348 
  349 static void
  350 nvme_sim_controller_fail(void *ctrlr_arg)
  351 {
  352         struct nvme_sim_softc *sc = ctrlr_arg;
  353 
  354         xpt_async(AC_LOST_DEVICE, sc->s_path, NULL);
  355         xpt_free_path(sc->s_path);
  356         xpt_bus_deregister(cam_sim_path(sc->s_sim));
  357         cam_sim_free(sc->s_sim, /*free_devq*/TRUE);
  358         free(sc, M_NVME);
  359 }
  360 
  361 struct nvme_consumer *consumer_cookie;
  362 
  363 static void
  364 nvme_sim_init(void)
  365 {
  366         if (nvme_use_nvd)
  367                 return;
  368 
  369         consumer_cookie = nvme_register_consumer(nvme_sim_ns_change,
  370             nvme_sim_new_controller, NULL, nvme_sim_controller_fail);
  371 }
  372 
  373 SYSINIT(nvme_sim_register, SI_SUB_DRIVERS, SI_ORDER_ANY,
  374     nvme_sim_init, NULL);
  375 
  376 static void
  377 nvme_sim_uninit(void)
  378 {
  379         if (nvme_use_nvd)
  380                 return;
  381         /* XXX Cleanup */
  382 
  383         nvme_unregister_consumer(consumer_cookie);
  384 }
  385 
  386 SYSUNINIT(nvme_sim_unregister, SI_SUB_DRIVERS, SI_ORDER_ANY,
  387     nvme_sim_uninit, NULL);

Cache object: 9daaf0240ad80355bbfdd7aa51b49a67


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.