4.1 Submission Queue & Completion Queue Definition

해당 장에서 얘기되는 SQ와 CQ는 NVMe over PCIe에서만 적용이 된다. NVMe over Fabrics에 관한 SQ와 CQ의 정보는 NVMe over Fabrics spec을 참조해야 한다.

SQ와 CQ는 circular queue 구조를 가지며, head와 tail을 가지고 있어 entry point를 나타낸다.  

submiiter는 현재 tail entry pointer가 가리키는 queue slot에 command를 저장한 후 tail pointer를 증가시킨다.

consumer는 현재 head entry pointer가 가리키는 slot를 가지고 간 후 head pointer를 증가시킨다.

Queue Empty 상태
• Head == Tail
Queue Full 상태
• Head == Tail + 1 mod # Of Queue Entries.

 

SQ와 CQ의 생성과 삭제는 host software에 의해 요청되어져야 한다.

생성시 CQ가 생성된 이후에 관련된 SQ가 생성된다. 삭제시 SQ가 먼저 메모리 해제된 이후 CQ가 삭제된다.

 

Host software는 새로운 command가 entry로 들어왔다는 것을 통신하기 위해 SQ tail doorbell과 CQ head doorbell에  쓰기를 수행한다. 만약 SQ tail doorbell 또는 CQ head head doorbell에 invalid value 쓰기와 asyn 이벤트 요청 command가 처리되지 않았다면, admin CQ에 invalid doorbell write value status가 놓여지게된다. 문제를 해결 하기 위해 host software에 의해 queue가 삭제 및 재생성되며, controller는 이전에 처리 중이던 command까지만 끝내고 추가적인 command에 대해서 처리하지 않는다. 해당 상황은 host software가 full 상태인 SQ에 entry를 추가하거나 빈 CQ에서 entry를 가지고 갈려는 동작에서 비롯된다.

 

Host software는 새로운 CQ entry가 놓여졌는지 확인하기 위해 CQ entry의 Phage Tag (P) bit를 확인한다. CQ tail pointer는 host에서는 알지 못하고 controller 내부에서만 사용된다. controller는 CQ 항목의 SQHD (SQ Head Pointer) 필드를 사용하여 SQHD의 새 값을 호스트에 전달한다. 

 

Queue identifier: 16 bit ID 값으로 queue가 생성되었을때 할당된다. 이것으로 queue를 식별하게된다.

 

4.2 Submission Queue Entry - Command Format

SQ는 64byte 크기로 구성되어 있다. 하지만, 추후에 spec에 의해서 command format이 변경될수 있다.

(NVMe spec 1.2와 비교해 Dword 0의 15번 bit만 사용되던 것이 spec 1.4에선 [15:14] bit로 확장되었다. 

Dword 0

- [07:00] opcode: 실행하기 위한 command의 opcode를 명시한다.

- [09:08] Fused Operation (FUSE): 간단한 command 두개를 같이 수행하는 command로 만든다. 두개의 command사이에는 어떤 command도 존재하지 않으며, atomic 하게 수행된다.

- [15:14] PRP or SGL for Data Transfer (PSDT): command와 관련된 data를 전송함에 있어 PRPs 또는 SGLs 중 어느 것을 선택할 것인지에 대한 field이다. PRPs는 NVMe over PCIe에서 admin commands를 위해 사용된다. SGLs는 NVMe over Fabrics에서 Admin command 그리고 I/O command를 위해 사용된다. 

- [31:16] Command Identifier (CID): SQ identifier와 함께 조합되어 고유의 identifier를 나타낸다.

 

Dword 1

- [31:00] Namespace Identifier (NSID): command가 적용되는 namespace를 나타낸다. 만약 command에 NSID 값이 사용되지 않는다면 0x0의 값을 가진다. 만약 0xFFFFFFFF의 값을 가진다면 broadcast value(모든 네임 스페이스를 지정하는 데 사용되는 값)이다. 만약 비활성화된(생성은 되었지만 controller에 deattached된 상태) NSID 값을 사용한다면 contoller는 abort 를 status Invalid Field를 남기고 수행한다. invalid NSID 값을 사용한다면 controller는 status Invalid Namespace or Format를 남기고 abort를 수행한다. broadcast value가 지원되지 않는 command 종류에 broadcast value를사용했다면 controller는 status Invalid Field를 남기고 abort를 수행한다.

 

Dword 4, 5

- [63:00] Metadata Pointer (MPTR): MPTR은 logical block data과 interleaved되지 않은 metadata가 command에 있을시에 유효하게 사용된다.

Dword 0의 PSDT field가 0b00인 경우, MPTR field는 metadata의 연속된 physical buffer 주소를 나타낸다. MPTR의 [01:00] 비트가 0b00의 값을 가지지 않는다면, controller는 invalid field 에러를 리포트한다. 

Dword 0의 PSDT field가 0b01인 경우, metadata의 phyiscal buffer 주소를 포함한다. 

Dword 0의 PSDT field가 0b10인 경우, 한개의 SGL descriptor를 포함하는 SGL segment 주소를 포함한다. SGL segment내에 포함된 SGL descriptor는 command를 위한 metadata의 첫번째 SGL descriptor를 나타낸다. 

 

Dword 6~9

- [127:000] Data Pointer (DPTR): command에 포함된 data를 나타낸다. 

Dword 0의 PSDT field가 0b00인 경우, PRP entry 1과 PRP entry2를 나타낸다. 

PRP1: command를 위한 PRP entry 혹은 command와 연관된 PRP list pointer를 나타낸다.

PRP2: 
1) memory page boundary를 초과하는 않는 data 전송의 경우 사용되지 않는다.

2) 전송할 데이터가 하나의 memory page boundary를 초과하는 경우 두번째 memory page의 page base address를 나타낸다. 

3) 전송할 데이터가 하나의 memory page boundary를 초과하고 두개 이상의 memory pages를 가져야 하는 경우 PRP list pointer를 나타낸다. 

Dword 0의 PSDT field가 0b01 또는 0b10인 경우, SGL entry 1(SGL1)으로 사용된다.

Command를 위한 첫번째 SGL segment를 나타낸다. 

4.3 Physical Region Page Entry and List

PRP (Physical region page) entry는 pysical memory page를 가리키는 pointer이다. PRPs는 controller와 memory 사이에 데이터 전송을 위한 scatter/gather mechanism을 사용한다. 효율적인 out of order의 순서로 data를 전송하기 위해 entry는 고정된 사이즈로 사용된다. Pysical memory page는 host software에 의해 CC.MPS에 설정된다. 

위 그림은 PRP entry의 64bit field를 나타낸다. offset은 CC.MPS에 의해 결정된다. 

[64:00] Page Base Address and Offset (PBAO): 4KB의 memory page size라면 [11:00] 12bit(2^12=4KB)가 offset으로 사용된다. 만약 첫번재 PRP entry나 PRP list pointer가 아니라면, offset은 0의 값을 가진다. dword로 align되므로 [01:00] 하위 2bit (2^2=4B)는 0b00의 값으로 된다. 만약 0b00으로 초기화 되지 않으면 PRP Offset Invalid 에러를 controller가 리포트 후 0b00으로 클리어 시킨다. 

PRP List (pysical region page list)는 연속적인 메모리의 single page를 가지는 PRP entry들의 집합이다. 만약 전송해야 할 data가 PRP list pages가 필요하다면 마지막 PRP entry는 다음 PRP list의 pointer를 가리킨다. 만약 non-zero offset을 PRP entry들이 가진다면 PRP offset invalid 에러를 반환한다. 

PRP list는 다음의 수식으로 entry의 개수를 포함하게 된다.

참조:https://lwn.net/Articles/804369/

#define PRP_SIZE			sizeof(__le64)
#define PRP_PER_PAGE		(PAGE_SIZE / PRP_SIZE)

PRP_SIZE는 64bit(8B)이고, PAGE_SIZE는 host page size를 default로 보통 4KB로 고려했을때 하나의 memory page안에 총 512개의 entry가 포함될수 있다. (4096B / 8B = 512개) 또한, 하나의 entry가 physical memory page (4KB)를 가리키므로 2048KB (2MB)의 데이터를 전송할 수 있게된다. 

4.4 Scatter Gather List (SGL)

우선 Scatter Gatehr의 의미를 파악하자.

커널 영역에서 메모리 관리를 할 때는 어렵긴 하지만(오버헤드가 크지만) 물리 메모리에 일렬로 정렬해서 사용하는 것이 가능하지만 사용자 영역에서는 이렇게 할 수가 없다. 커널 영역에서 메모리를 물리 영역에 일렬로 정리해서 쓰는것이 가능하다고 했지만 여기 저기 흩어져 있는(Scatter) 메모리를 일렬로 정렬되어 있는 것처럼 사용할 수 있다면 좀더 편한 관리가 가능할 것이다. 다시 정리하면 Scatter/Gather 의 개념은 여기 저기 흩어져 있는 메모리(Scatter) 를 논리적으로 모아서(Gather) 연속된 물리적 메모리처럼 사용하는 것을 의미하며 Scatterlist 는 여기 저기 흩어져 있는 메모리(Scatter) 의 모음(array) 라고 보면 된다.
출처: https://poplinux.tistory.com/75

struct scatterlist{
  struct page *page;
  unsigned int offset;
  dma_addr_t dma_address;
  unsigned int length;
}

왜 NVMe over PCIe에서는 SGL을 안쓰고 PRP를 쓰는지에 대한 이유이다. 결론은 결국 PRP가 SGL보다 간단하다이다. 하지만, 많은 양의 데이터를 전송할땐 SGL을 사용하는 것이 더 빠르다고 한다.

http://everylwn.blogspot.com/2016/06/sglscatter-gather-list-support-in-nvme_2.html

 

SGL은 데이터 버퍼를 표현하기 위한 memory 주소 공간의 자료구조이다. controller는 'Identify Controller data stucture'에 지원하는 SGL types를 가리킨다. data buffer는 source buffer이거나 destination buffer이다. SGL은 하나 이상의 SGL segment를 포함하고 있다. SGL 내에 Data Block과 Bit Bucket descriptor의 총 길이가 전송할 logical block의 수와 같거나 초과할것이다. 마지막 SGL segment는 SGL segment descriptor 또는 SGL last segment descriptor를 포함하지 않는다. 

SGL segment는 하나 이상의 SGL descriptor를 가진다. 

SGL descriptor의 format이다. 

15B: SGL identifier

[03:00] SGL descriptor sub type: 만약 reserved value이거나 unsupported value인 경우, descriptor는 SGL descriptor type error로 처리된다.

[07:04] SGL descriptor type: 만약 reserved이거나 unsupported value인 경우 SGL descriptor는 SGL descriptor type error로 처리된다. 

SGL data block descriptor인 경우

[07:00] Address:

- SGL descriptor sub type field가 0x0인 경우: address field는 data block의 64bit memory 시작 주소를 나타낸다. 

- SGL descriptor sub type field가 0x1인 경우: 전송되어야 할 memory 주소의 offset을 나타낸다. 

[11:08] Length: data block의 B단위 길이를 나타낸다. 0x0인 경우 전송할 데이터가 존재하지 않는다. 

[15:15] SGL identifier:

- [03:00] SGL descriptor sub type

- [07:04] SGL descriptor type: 0x0

 

SGL bit bucket descriptor인 경우

[11:08] Length: discard되는 source data의 길이를 나타낸다. 만약 destination data buffer인 경우, controller에 의해 discard되는 source data의 길이를 말한다.  만약 source data buffer인 경우 0이다. 

[15:15] SGL identifier:

- [03:00] SGL descriptor sub type

- [07:04] SGL descriptor type: 0x0

4.6 Completion Queue Entry

CQ의 entry는 최소 16B 크기를 가진다. 추후에 I/O coomand set의 사용에 따라 크기가 달라질 수 있다.

Dword 2

[31:16] SQ identifier (SQID): issue된 command와 관련되어 SQ를 구분하기 위한 식별자로 사용된다.

[15:00] SQ Head Pointer (SQHD): command를 가지고온 SQ의 현재 SQ head pointer를 가리킨다. 어디까지 entry가 처리되었는지를 나타낸다.

 

Dword 3

[31:17] Status Field (SF): 처리 완료된 command의 상태를 나타낸다. 

- no fatal or non-fatal error conditions 없이 성공적으로 끝났을 경우 0x0의 값을 가진다. 

- [31] Do Not Retry (DNR): 

--- set to 1: 같은 command가 다시 submmit된 command는 fail된다. 

--- set to 0: 같은 command가 다시 submiit된 command는 성공한다. 

- [30] More (M): 

---set to 1: Get log pag command를 통해 받은 에러 정보보다 더 많은 status 정보를 가지고 있다.

---set to 0: 추가적은 status 정보가 없다.

- [29:28] Command Retry Delay (CRD):

--- set to 0: ?????????????

- [27:25] Status Code Type (SCT): status code type of the completion queue entry

- [24:17] Status Code (SC): status code identifying any error or status information

 

[16:16] Phase Tag (P): CQ entry가 새로운 entry인지 판단하는데 사용된다. CC.EN이 1의 값이 되기전 host software에 의해 0으로 초기화 된다. controller가 CQ에 처리 끝난 entry를 놓은경우, host software가 새로운 entry를 식별할수 있도록 controller는 P field를 invert시킨다.

[15:00] Command Identifier (CID): 처리 완료된 command의 식별자를 가리킨다. 해당 식별자는 host software에 의해 SQ에 command가 놓여질때 할당된다. SQ identifier와 Command identifier의 조합으로 command가 완료되었다는 것을 판단할 수 있다.

4.7 Controller Memory Buffer

CQ

+ Recent posts