본문 바로가기

리눅스 커널/블록 멀티 큐

nvme command 관련 추가 내용

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