Closed CkTD closed 3 years ago
Hi,
I tested you code (modified to below) and it works well (read data from host, modify in SSD, write to host again).
#define BUFFER_SIZE 4096
void Namespace::myCommand(SQEntryWrapper &req, RequestFunction &func) {
bool err = false;
CQEntryWrapper resp(req);
if (!attached) {
err = true;
resp.makeStatus(true, false, TYPE_COMMAND_SPECIFIC_STATUS,
STATUS_NAMESPACE_NOT_ATTACHED);
}
if (!err) {
DMAFunction doMyCmd = [this](uint64_t, void *context) {
DMAFunction update = [this](uint64_t, void *context) {
DMAFunction dmaDone = [this](uint64_t, void *context) {
IOContext *pContext = (IOContext *)context;
pContext->function(pContext->resp);
if (pContext->buffer) {
free(pContext->buffer);
}
delete pContext->dma;
delete pContext;
};
IOContext *pContext = (IOContext *)context;
// Print data
printf("======== H2D DMA RESULT ========\n");
for (uint64_t i = 0; i < BUFFER_SIZE; i += 32) {
for (uint64_t j = 0; j < 32; j++) {
printf("%02x ", pContext->buffer[i + j]);
}
printf("\n");
}
printf("======== H2D DMA RESULT ========\n");
// update data in pContext->buffer
memset(pContext->buffer, 0x80, BUFFER_SIZE);
pContext->dma->write(0, BUFFER_SIZE, pContext->buffer, dmaDone,
context);
};
IOContext *pContext = (IOContext *)context;
pContext->buffer = (uint8_t *)calloc(BUFFER_SIZE, 1);
pContext->dma->read(0, BUFFER_SIZE, pContext->buffer, update, context);
};
IOContext *pContext = new IOContext(func, resp);
CPUContext *pCPU =
new CPUContext(doMyCmd, pContext, CPU::NVME__NAMESPACE, CPU::READ);
if (req.useSGL) {
pContext->dma =
new SGL(cfgdata, cpuHandler, pCPU, req.entry.data1, req.entry.data2);
}
else {
pContext->dma = new PRPList(cfgdata, cpuHandler, pCPU, req.entry.data1,
req.entry.data2, (uint64_t)BUFFER_SIZE);
}
}
else {
func(resp);
}
}
I used following NVMe command:
nvme io-passthru -o 0x90 -p 3 -n 1 -l 4096 -r -s /dev/nvme0n1
The buffer is prefilled by memset(buffer, 0x03, 4096)
by nvme-cli, and gem5 shows buffer is read correctly:
1078129331000: HIL::NVMe: SQ 2 | Submission Queue Tail Doorbell | Item count in queue 0 -> 1 | head 2 | tail 2 -> 3
======== H2D DMA RESULT ========
03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03
...
03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03 03
======== H2D DMA RESULT ========
1078152096369: HIL::NVMe: INTR | MSI-X sent | vector 1
1078152950500: HIL::NVMe: CQ 2 | Completion Queue Head Doorbell | Item count in queue 1 -> 0 | head 2 -> 3 | tail 3
After command completion, nvme-cli reported that buffer is modified correctly:
root@genericarmv8:~# ./nvme io-passthru -o 0x90 -p 3 -n 1 -l 4096 -r -s /dev/nvme0n1
opcode : 90
flags : 00
rsvd1 : 0000
nsid : 00000001
cdw2 : 00000000
cdw3 : 00000000
data_len : 00001000
metadata_len : 00000000
addr : 443000
metadata : 0
cdw10 : 00000000
cdw11 : 00000000
cdw12 : 00000000
cdw13 : 00000000
cdw14 : 00000000
cdw15 : 00000000
timeout_ms : 00000000
NVMe command result:00000000
0 1 2 3 4 5 6 7 8 9 a b c d e f
0000: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 "................"
0010: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 "................"
...
0ff0: 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 "................"
root@genericarmv8:~#
I tested this on ARM architecture with AtomicSimpleCPU. If you still have same problem, please let me know.
Thanks.
Explain what you want to ask here:
Hi, I'm trying to add a new nvme IO command. I hope the data transfer direction of that command to be bidirectional. The controller will first read the data pointed by the Data Pointer in the command and update it with some new value.
Following the implementation of Namespace::read and Namespace::write, I add a new handler Namespace::myCommand. Here is my simplified code
On the host side, I use ioctl to issue this nvme command. The controller can read the data via dma, but seems that the dma write doesn't work. What is the right way to transfer data bidirectional? Thanks