/** * Setup admin queue. * @param ses session */ static void unvme_adminq_create(unvme_session_t* ses) { DEBUG_FN("%x: qs=%d", unvme_dev.vfiodev->pci, ses->qsize); unvme_queue_t* adminq = ses->queues; adminq->ses = ses; adminq->sqdma = vfio_dma_alloc(unvme_dev.vfiodev, ses->qsize * sizeof(nvme_sq_entry_t)); if (!adminq->sqdma) FATAL("vfio_dma_alloc"); adminq->cqdma = vfio_dma_alloc(unvme_dev.vfiodev, ses->qsize * sizeof(nvme_cq_entry_t)); if (!adminq->cqdma) FATAL("vfio_dma_alloc"); adminq->nvq = nvme_setup_adminq(unvme_dev.nvmedev, ses->qsize, adminq->sqdma->buf, adminq->sqdma->addr, adminq->cqdma->buf, adminq->cqdma->addr); if (!adminq->nvq) FATAL("nvme_setup_adminq"); }
/** * Create an I/O queue. * @param ses session * @param sqi session queue id */ static void unvme_ioq_create(unvme_session_t* ses, int sqi) { unvme_queue_t* ioq = &ses->queues[sqi]; ioq->ses = ses; if (sqi == 0) { ses->id = ses->prev->queues[ses->prev->qcount-1].id + 1; ses->ns.sid = ses->id; } ioq->id = ses->id + sqi; DEBUG_FN("%x: q=%d qs=%d", unvme_dev.vfiodev->pci, ioq->id, ses->qsize); int i; for (i = 0; i < 16; i++) unvme_get_desc(ioq); ioq->descfree = ioq->desclist; ioq->desclist = NULL; ioq->desccount = 0; ioq->cidmask = zalloc(ses->masksize); // assume maxppio fits 1 PRP list page ioq->prpsize = ses->ns.pagesize; ioq->prplist = vfio_dma_alloc(unvme_dev.vfiodev, ioq->prpsize * ses->qsize); if (!ioq->prplist) FATAL("vfio_dma_alloc"); ioq->sqdma = vfio_dma_alloc(unvme_dev.vfiodev, ses->qsize * sizeof(nvme_sq_entry_t)); if (!ioq->sqdma) FATAL("vfio_dma_alloc"); ioq->cqdma = vfio_dma_alloc(unvme_dev.vfiodev, ses->qsize * sizeof(nvme_cq_entry_t)); if (!ioq->cqdma) FATAL("vfio_dma_alloc"); ioq->nvq = nvme_create_ioq(unvme_dev.nvmedev, ioq->id, ses->qsize, ioq->sqdma->buf, ioq->sqdma->addr, ioq->cqdma->buf, ioq->cqdma->addr); if (!ioq->nvq) FATAL("nvme_create_ioq"); unvme_dev.numioqs++; INFO_FN("%x: q=%d qc=%d qs=%d db=%#04lx", unvme_dev.vfiodev->pci, ioq->nvq->id, unvme_dev.numioqs, ioq->nvq->size, (u64)ioq->nvq->sq_doorbell - (u64)unvme_dev.nvmedev->reg); }
/** * Create a namespace object. * @param ses session * @param nsid namespace id */ static void unvme_ns_init(unvme_session_t* ses, int nsid) { unvme_ns_t* ns = &ses->ns; ns->maxqsize = unvme_dev.nvmedev->maxqsize; ns->pageshift = unvme_dev.nvmedev->pageshift; ns->pagesize = 1 << ns->pageshift; vfio_dma_t* dma = vfio_dma_alloc(unvme_dev.vfiodev, ns->pagesize << 1); if (!dma) FATAL("vfio_dma_alloc"); if (nvme_acmd_identify(unvme_dev.nvmedev, nsid, dma->addr, dma->addr + ns->pagesize)) FATAL("nvme_acmd_identify"); if (nsid == 0) { int i; nvme_identify_ctlr_t* idc = (nvme_identify_ctlr_t*)dma->buf; ns->vid = idc->vid; memcpy(ns->sn, idc->sn, sizeof(ns->sn)); for (i = sizeof(ns->sn) - 1; i > 0 && ns->sn[i] == ' '; i--) ns->sn[i] = 0; memcpy(ns->mn, idc->mn, sizeof(ns->mn)); for (i = sizeof(ns->mn) - 1; i > 0 && ns->mn[i] == ' '; i--) ns->mn[i] = 0; memcpy(ns->fr, idc->fr, sizeof(ns->fr)); for (i = sizeof(ns->fr) - 1; i > 0 && ns->fr[i] == ' '; i--) ns->fr[i] = 0; ns->maxppio = ns->pagesize / sizeof(u64); // limit to 1 PRP list page if (idc->mdts) { int maxp = 2; for (i = 1; i < idc->mdts; i++) maxp *= 2; if (ns->maxppio > maxp) ns->maxppio = maxp; } } else { memcpy(ns, &unvme_dev.ses->ns, sizeof(unvme_ns_t)); nvme_identify_ns_t* idns = (nvme_identify_ns_t*)dma->buf; ns->blockcount = idns->ncap; ns->blockshift = idns->lbaf[idns->flbas & 0xF].lbads; ns->blocksize = 1 << ns->blockshift; if (ns->blocksize > ns->pagesize || ns->blockcount < 8) { FATAL("ps=%d bs=%d bc=%ld", ns->pagesize, ns->blocksize, ns->blockcount); } ns->nbpp = ns->pagesize / ns->blocksize; ns->maxbpio = ns->maxppio * ns->nbpp; ns->maxiopq = ses->qsize - 1; } ns->id = nsid; ns->ses = ses; ns->qcount = ses->qcount; ns->qsize = ses->qsize; if (vfio_dma_free(dma)) FATAL("vfio_dma_free"); }
/** * Allocate an I/O buffer associated with a session. * @param ses session * @param size buffer size * @return the allocated buffer or NULL if failure. */ void* unvme_do_alloc(unvme_session_t* ses, u64 size) { void* buf = NULL; pthread_spin_lock(&ses->iomem.lock); vfio_dma_t* dma = vfio_dma_alloc(unvme_dev.vfiodev, size); if (dma) { unvme_iomem_t* iomem = &ses->iomem; if (iomem->count >= iomem->size) { iomem->size += 256; iomem->map = realloc(iomem->map, iomem->size * sizeof (vfio_dma_t*)); } iomem->map[iomem->count++] = dma; buf = dma->buf; } pthread_spin_unlock(&ses->iomem.lock); return buf; }
/** * Main program. */ int main(int argc, char* argv[]) { const char* usage = "Usage: %s pciname nsid log_page_id\n\ where\n\ log_page_id 1 = error information\n\ log_page_id 2 = SMART / Health information\n\ log_page_id 3 = firmware slot information\n"; error_print_progname = no_progname; if (argc != 4) error(1, 0, usage, argv[0]); char* s = argv[2]; int nsid = strtol(s, &s, 0); if (*s || nsid <= 0) error(1, 0, usage, argv[0]); int lid = strtol(argv[3], &s, 0); if (*s || (lid < 1 || lid > 3)) error(1, 0, usage, argv[0]); nvme_setup(argv[1], 8); vfio_dma_t* dma = vfio_dma_alloc(vfiodev, 2 << PAGESHIFT); if (!dma) error(1, 0, "vfio_dma_alloc"); int numd = dma->size / sizeof(u32) - 1; u64 prp1 = dma->addr; u64 prp2 = dma->addr + (1 << PAGESHIFT); int err = nvme_acmd_get_log_page(nvmedev, nsid, lid, numd, prp1, prp2); if (err) error(1, 0, "nvme_acmd_get_log_page"); switch (lid) { case 1: print_error_info(dma->buf); break; case 2: print_smart_health(dma->buf); break; case 3: print_firmware_slot(dma->buf); break; } nvme_cleanup(); return 0; }