struct nvme_dev {
u32 __iomem *dbs;
struct dma_pool *prp_page_pool;
struct dma_pool *prp_small_pool;
u32 db_stride;
void __iomem *bar;
void __iomem *cmb;
pci_bus_addr_t cmb_bus_addr;
/* shadow doorbell buffer support: */
u32 *dbbuf_dbs;
dma_addr_t dbbuf_dbs_dma_addr;
u32 *dbbuf_eis;
dma_addr_t dbbuf_eis_dma_addr;
/* host memory buffer support: */
u64 host_mem_size;
u32 nr_host_mem_descs;
dma_addr_t host_mem_descs_dma;
struct nvme_host_mem_buf_desc *host_mem_descs;
void **host_mem_desc_bufs;
};
위 구조체에서 메모리 관련 멤버만 포함하였다.
아래는 각 멤버가 언제 초기화 되는지 그 코드 흐름을 정리한 것이다.
nvme_probe()
//
->nvme_dev_map(dev);
//
->nvme_remap_bar(dev, NVME_REG_DBS + 4096);
//
->dev->bar = ioremap(...);
->dev->dbs = dev->bar + NVME_REG_DBS;
->nvme_setup_prp_pools(dev);
//
->dev->prp_page_pool = dma_pool_create(...);
->dev->prp_small_pool = dma_pool_create(...);
nvme_reset_work()
//
->nvme_pci_enable()
//
->dev->dbs = dev->bar + 4096;
->nvme_pci_configure_admin_queue(dev);
//
->nvme_remap_bar();
//
->dev->bar = ioremap(...);
->dev->dbs = dev->bar + NVME_REG_DBS;
->nvme_dbbuf_dma_alloc(dev);
//
->unsigned int mem_size = nvme_dbbuf_size(dev->db_stride);
//
->return ((num_possible_cpus() + 1) * 8 * stride);
->dev->dbbuf_dbs = dma_alloc_coherent(dev->dev, mem_size, &dev->dbbuf_dbs_dma_addr, GFP_KERNEL);
->dev->dbbuf_eis = dma_alloc_coherent(dev->dev, mem_size, &dev->dbbuf_eis_dma_addr, GFP_KERNEL);
dbs란 멤버는 nvme_queue 멤버 초기화를 이용할 때 사용된다. 정확히 어떤 의미로 그 값이 초기화 되는지는 모르겠다.
struct nvme_queue {
struct nvme_command *sq_cmds;
struct nvme_command __iomem *sq_cmds_io;
volatile struct nvme_completion *cqes;
dma_addr_t sq_dma_addr;
dma_addr_t cq_dma_addr;
u32 __iomem *q_db;
u32 *dbbuf_sq_db;
u32 *dbbuf_cq_db;
u32 *dbbuf_sq_ei;
u32 *dbbuf_cq_ei;
};
역시 메모리 관련 멤버만 포함하였다.
nvme_alloc_queue()
//
->nvmeq->cqes = dma_zalloc_coherent(dev->dev, CQ_SIZE(depth), &nvmeq->cq_dma_addr, GFP_KERNEL);
->nvme_alloc_sq_cmds(dev, nvmeq, qid, depth)
//
->nvmeq->sq_cmds = dma_alloc_coherent(dev->dev, SQ_SIZE(depth), &nvmeq->sq_dma_addr, GFP_KERNEL);
->nvmeq->q_db = &dev->dbs[qid * 2 * dev->db_stride];
nvme_init_queue()
//
->nvmeq->q_db = &dev->dbs[qid * 2 * dev->db_stride];
->nvme_dbbuf_init(dev, nvmeq, qid);
//
->nvmeq->dbbuf_sq_db = &dev->dbbuf_dbs[sq_idx(qid, dev->db_stride)];
->nvmeq->dbbuf_cq_db = &dev->dbbuf_dbs[cq_idx(qid, dev->db_stride)];
->nvmeq->dbbuf_sq_ei = &dev->dbbuf_eis[sq_idx(qid, dev->db_stride)];
->nvmeq->dbbuf_cq_ei = &dev->dbbuf_eis[cq_idx(qid, dev->db_stride)];
nvme_queue 의 생성은 보통 아래와 같다.
nvme_alloc_queue() 로 nvme_queue 를 위한 메모리할당.
nvme_create_queue() 로 submission create command 를 보내 실제로 생성
nvme_init_queue() 로 마저 초기화.
그래서 cqes는 nvme_completion 구조체 배열로, dma_zalloc_coherent() 에서 depth * sizeof(struct nvme_completion) 만큼 생성된다. 또한 여기서 cq_dma_addr의 값까지 같이 초기화 해준다.
sq_cmds 도 동일하다.
q_db 는 nvme_dev 가 관리하는 dbs에서 가져온다. qid * 2 는 qid - 1 에서 두개의 배열원소가 할당되었기 때문이다.
sq_idx() 는 qid * 2 * stride; 이다. dev->db_stride는 그값이 보통 1이기 때문에 큰 신경을 쓸 필요가 없을 것 같다.
cq_idx() 는 (qid * 2 + 1) * stride 이다. sq_idx + 1 인 값이 반환된다.
'리눅스 커널 > 블록 멀티 큐' 카테고리의 다른 글
blk_mq_init_queue (0) | 2021.10.26 |
---|---|
blk_mq_alloc_tag_set 흐름 (0) | 2021.10.26 |
nvme command (0) | 2021.10.26 |
nvme_setup_io_queues 흐름 (0) | 2021.10.26 |
nvme_reset_work 흐름 (0) | 2021.10.26 |