void LBAOutOfRangeBare_r10b::SendCmdToHdw(SharedSQPtr sq, SharedCQPtr cq, SharedCmdPtr cmd, string qualify) { uint32_t numCE; uint32_t isrCount; uint32_t isrCountB4; string work; uint16_t uniqueId; if ((numCE = cq->ReapInquiry(isrCountB4, true)) != 0) { cq->Dump( FileSystem::PrepDumpFile(mGrpName, mTestName, "cq", "notEmpty"), "Test assumption have not been met"); throw FrmwkEx(HERE, "Require 0 CE's within CQ %d, not upheld, found %d", cq->GetQId(), numCE); } LOG_NRM("Send the cmd to hdw via SQ %d", sq->GetQId()); sq->Send(cmd, uniqueId); work = str(boost::format( "Just B4 ringing SQ %d doorbell, dump entire SQ") % sq->GetQId()); sq->Dump(FileSystem::PrepDumpFile(mGrpName, mTestName, "sq." + cmd->GetName(), qualify), work); sq->Ring(); LOG_NRM("Wait for the CE to arrive in CQ %d", cq->GetQId()); if (cq->ReapInquiryWaitSpecify(DEFAULT_CMD_WAIT_ms, 1, numCE, isrCount) == false) { work = str(boost::format( "Unable to see any CE's in CQ %d, dump entire CQ") % cq->GetQId()); cq->Dump( FileSystem::PrepDumpFile(mGrpName, mTestName, "cq." + cmd->GetName(), qualify), work); throw FrmwkEx(HERE, "Unable to see CE for issued cmd"); } else if (numCE != 1) { work = str(boost::format( "Unable to see any CE's in CQ %d, dump entire CQ") % cq->GetQId()); cq->Dump( FileSystem::PrepDumpFile(mGrpName, mTestName, "cq." + cmd->GetName(), qualify), work); throw FrmwkEx(HERE, "1 cmd caused %d CE's to arrive in CQ %d", numCE, cq->GetQId()); } work = str(boost::format("Just B4 reaping CQ %d, dump entire CQ") % cq->GetQId()); cq->Dump(FileSystem::PrepDumpFile(mGrpName, mTestName, "cq." + cmd->GetName(), qualify), work); // throws if an error occurs IO::ReapCE(cq, numCE, isrCount, mGrpName, mTestName, qualify, CESTAT_LBA_OUT_RANGE); // Single cmd submitted on empty ASQ should always yield 1 IRQ on ACQ if (gCtrlrConfig->IrqsEnabled() && cq->GetIrqEnabled() && (isrCount != (isrCountB4 + 1))) { throw FrmwkEx(HERE, "CQ using IRQ's, but IRQ count not expected (%d != %d)", isrCount, (isrCountB4 + 1)); } }
void SQ::Init(uint16_t qId, uint16_t entrySize, uint32_t numEntries, uint16_t cqId) { uint64_t work; mCqId = cqId; Queue::Init(qId, entrySize, numEntries); LOG_NRM("Create SQ: (id,cqid,entrySize,numEntries) = (%d,%d,%d,%d)", GetQId(), GetCqId(), GetEntrySize(), GetNumEntries()); LOG_NRM("Allocating contiguous SQ memory in dnvme"); if (numEntries < 2) { throw FrmwkEx(HERE, "Number elements breaches spec requirement"); } else if (gRegisters->Read(CTLSPC_CAP, work) == false) { throw FrmwkEx(HERE, "Unable to determine MQES"); } // Detect if doing something that looks suspicious/incorrect/illegal work &= CAP_MQES; work += 1; // convert to 1-based if (work < (uint64_t)numEntries) { LOG_WARN("Creating Q with %d entries, but DUT only allows %d", numEntries, (uint32_t)work); } if (GetIsAdmin()) { if (gCtrlrConfig->IsStateEnabled()) { // At best this will cause tnvme to seg fault or a kernel crash // The NVME spec states unpredictable outcomes will occur. throw FrmwkEx(HERE, "Creating an ASQ while ctrlr is enabled is a shall not"); } // We are creating a contiguous ASQ. ASQ's have a constant well known // element size and no setup is required for this type of Q. int ret; struct nvme_create_admn_q q; q.elements = GetNumEntries(); q.type = ADMIN_SQ; LOG_NRM("Init contig ASQ: (id, cqid, entrySize, numEntries) = " "(%d, %d, %d, %d)", GetQId(), GetCqId(), GetEntrySize(), GetNumEntries()); if ((ret = ioctl(mFd, NVME_IOCTL_CREATE_ADMN_Q, &q)) < 0) { throw FrmwkEx(HERE, "Q Creation failed by dnvme with error: 0x%02X", ret); } } else { // We are creating a contiguous IOSQ. struct nvme_prep_sq q; q.sq_id = GetQId(); q.cq_id = GetCqId(); q.elements = GetNumEntries(); q.contig = true; CreateIOSQ(q); } // Contiguous Q's are created in dnvme and must be mapped back to user space mContigBuf = KernelAPI::mmap(GetQSize(), GetQId(), KernelAPI::MMR_SQ); if (mContigBuf == NULL) throw FrmwkEx(HERE, "Unable to mmap contig memory to user space"); }
void UnsupportRsvdFields_r12::WriteReadVerify(SharedIOSQPtr iosq, SharedIOCQPtr iocq) { Informative::Namspc namspcData = gInformative->Get1stBareMetaE2E(); LBAFormat lbaFormat = namspcData.idCmdNamspc->GetLBAFormat(); uint64_t lbaDataSize = namspcData.idCmdNamspc->GetLBADataSize(); SharedWritePtr writeCmd = SharedWritePtr(new Write()); SharedMemBufferPtr writeMem = SharedMemBufferPtr(new MemBuffer()); SharedReadPtr readCmd = SharedReadPtr(new Read()); SharedMemBufferPtr readMem = SharedMemBufferPtr(new MemBuffer()); send_64b_bitmask prpBitmask = (send_64b_bitmask) (MASK_PRP1_PAGE | MASK_PRP2_PAGE | MASK_PRP2_LIST); switch (namspcData.type) { case Informative::NS_BARE: writeMem->Init(lbaDataSize); readMem->Init(lbaDataSize); break; case Informative::NS_METAS: writeMem->Init(lbaDataSize); readMem->Init(lbaDataSize); if (gRsrcMngr->SetMetaAllocSize(lbaFormat.MS) == false) throw FrmwkEx(HERE); writeCmd->AllocMetaBuffer(); readCmd->AllocMetaBuffer(); break; case Informative::NS_METAI: writeMem->Init(lbaDataSize + lbaFormat.MS); readMem->Init(lbaDataSize + lbaFormat.MS); break; case Informative::NS_E2ES: case Informative::NS_E2EI: throw FrmwkEx(HERE, "Deferring work to handle this case in future"); break; } writeCmd->SetPrpBuffer(prpBitmask, writeMem); writeCmd->SetNSID(namspcData.id); writeCmd->SetNLB(0); readCmd->SetPrpBuffer(prpBitmask, readMem); readCmd->SetNSID(namspcData.id); readCmd->SetNLB(0); switch (namspcData.type) { case Informative::NS_BARE: writeMem->SetDataPattern(DATAPAT_INC_32BIT); break; case Informative::NS_METAS: writeMem->SetDataPattern(DATAPAT_INC_32BIT); writeCmd->SetMetaDataPattern(DATAPAT_INC_32BIT); break; case Informative::NS_METAI: writeMem->SetDataPattern(DATAPAT_INC_32BIT); break; case Informative::NS_E2ES: case Informative::NS_E2EI: throw FrmwkEx(HERE, "Deferring work to handle this case in future"); break; } IO::SendAndReapCmd(mGrpName, mTestName, CALC_TIMEOUT_ms(1), iosq, iocq, writeCmd, "", true); IO::SendAndReapCmd(mGrpName, mTestName, CALC_TIMEOUT_ms(1), iosq, iocq, readCmd, "", true); VerifyDataPat(readCmd, writeCmd); }
void SQ::Init(uint16_t qId, uint16_t entrySize, uint32_t numEntries, const SharedMemBufferPtr memBuffer, uint16_t cqId) { uint64_t work; mCqId = cqId; Queue::Init(qId, entrySize, numEntries); LOG_NRM("Create SQ: (id,cqid,entrySize,numEntries) = (%d,%d,%d,%d)", GetQId(), GetCqId(), GetEntrySize(), GetNumEntries()); LOG_NRM("Allocating discontiguous SQ memory in tnvme"); if (numEntries < 2) { throw FrmwkEx(HERE, "Number elements breaches spec requirement"); } else if (gRegisters->Read(CTLSPC_CAP, work) == false) { throw FrmwkEx(HERE, "Unable to determine MQES"); } // Detect if doing something that looks suspicious/incorrect/illegal work &= CAP_MQES; work += 1; // convert to 1-based if (work < (uint64_t)numEntries) { LOG_WARN("Creating Q with %d entries, but DUT only allows %d", numEntries, (uint32_t)work); } if (memBuffer == MemBuffer::NullMemBufferPtr) { throw FrmwkEx(HERE, "Passing an uninitialized SharedMemBufferPtr"); } else if (GetIsAdmin()) { // There are no appropriate methods for an NVME device to report ASC/ACQ // creation errors, thus since ASC/ASQ may only be contiguous then don't // allow these problems to be injected, at best they will only succeed // to seg fault the app or crash the kernel. throw FrmwkEx(HERE, "Illegal memory alignment will corrupt"); } else if (memBuffer->GetBufSize() < GetQSize()) { LOG_ERR("Q buffer memory ambiguous to passed size params"); throw FrmwkEx(HERE, "Mem buffer size = %d, Q size = %d", memBuffer->GetBufSize(), GetQSize()); } else if (memBuffer->GetAlignment() != sysconf(_SC_PAGESIZE)) { // Nonconformance to page alignment will seg fault the app or crash // the kernel. This state is not testable since no errors can be // reported by hdw, thus disallow this attempt. throw FrmwkEx(HERE, "Q content memory shall be page aligned"); } // Zero out the content memory so the P-bit correlates to a newly alloc'd Q. // Also assuming life time ownership of this object if it wasn't created // by the RsrcMngr. mDiscontigBuf = memBuffer; mDiscontigBuf->Zero(); // We are creating a discontiguous IOSQ struct nvme_prep_sq q; q.sq_id = GetQId(); q.cq_id = GetCqId(); q.elements = GetNumEntries(); q.contig = false; CreateIOSQ(q); }
void KernelAPI::DumpPciSpaceRegs(DumpFilename filename, bool verbose) { int fd; string work; uint64_t value; const PciSpcType *pciMetrics = gRegisters->GetPciMetrics(); const vector<PciCapabilities> *pciCap = gRegisters->GetPciCapabilities(); LOG_NRM("Dump PCI regs to filename: %s", filename.c_str()); if ((fd = open(filename.c_str(), FILENAME_FLAGS, FILENAME_MODE)) == -1) throw FrmwkEx(HERE, "file=%s: %s", filename.c_str(), strerror(errno)); // Traverse the PCI header registers work = "PCI header registers\n"; write(fd, work.c_str(), work.size()); for (int j = 0; j < PCISPC_FENCE; j++) { if (pciMetrics[j].specRev != gRegisters->GetSpecRev()) continue; // All PCI hdr regs don't have an associated capability if (pciMetrics[j].cap == PCICAP_FENCE) { if (gRegisters->Read((PciSpc)j, value, verbose) == false) goto ERROR_OUT; RegToFile(fd, pciMetrics[j], value); } } // Traverse all discovered capabilities for (size_t i = 0; i < pciCap->size(); i++) { switch (pciCap->at(i)) { case PCICAP_PMCAP: work = "Capabilities: PMCAP: PCI power management\n"; break; case PCICAP_MSICAP: work = "Capabilities: MSICAP: Message signaled interrupt\n"; break; case PCICAP_MSIXCAP: work = "Capabilities: MSIXCAP: Message signaled interrupt ext'd\n"; break; case PCICAP_PXCAP: work = "Capabilities: PXCAP: Message signaled interrupt\n"; break; case PCICAP_AERCAP: work = "Capabilities: AERCAP: Advanced Error Reporting\n"; break; default: LOG_ERR("PCI space reporting an unknown capability: %d\n", pciCap->at(i)); goto ERROR_OUT; } write(fd, work.c_str(), work.size()); // Read all registers assoc with the discovered capability for (int j = 0; j < PCISPC_FENCE; j++) { if (pciMetrics[j].specRev != gRegisters->GetSpecRev()) continue; if (pciCap->at(i) == pciMetrics[j].cap) { if (pciMetrics[j].size > MAX_SUPPORTED_REG_SIZE) { bool err = false; uint8_t *buffer; buffer = new uint8_t[pciMetrics[j].size]; if (gRegisters->Read(NVMEIO_PCI_HDR, pciMetrics[j].size, pciMetrics[j].offset, buffer, verbose) == false) { err = true; } else { string work = " "; work += gRegisters->FormatRegister(NVMEIO_PCI_HDR, pciMetrics[j].size, pciMetrics[j].offset, buffer); work += "\n"; write(fd, work.c_str(), work.size()); } delete [] buffer; if (err) goto ERROR_OUT; } else if (gRegisters->Read((PciSpc)j, value, verbose) == false) { goto ERROR_OUT; } else { RegToFile(fd, pciMetrics[j], value); } } } } close(fd); return; ERROR_OUT: close(fd); throw FrmwkEx(HERE); }