이번에는 nvme device driver 가 초기화 될 때 커널코드를 수정한 것을 그 흐름에 따라 정리해보겠다.
먼저 nvme_probe()가 디바이스 드라이버 초기화를 위해 제일 먼저 호출 된다.
static int nvme_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
int node, result = -ENOMEM;
struct nvme_dev *dev;
unsigned long quirks = id->driver_data;
node = dev_to_node(&pdev->dev);
if (node == NUMA_NO_NODE)
set_dev_node(&pdev->dev, first_memory_node);
dev = kzalloc_node(sizeof(*dev), GFP_KERNEL, node);
if (!dev)
return -ENOMEM;
dev->extra_nr = 32;
...
}
일단 nvme_queue가 소유 할 수 있는 최대 SQ의 개수를 32개로 하였다.
이 이상의 값으로 초기화 하였을 때 부팅 시 커널 패닉이 발생하는 문제가 있었다.
패닉이 발생하는 정확한 이유는 잘 모르겠지만, 일단은 되는대로 하였다.
이후 nvme_reset_work()가 호출되고 이 함수는 nvme_pci_configure_admin_queue()를 호출 한다.
그리고 이 함수는 nvme_enable_ctrl()을 호출하는데, 여기서 WRR로 스케줄링 하도록 설정 할 수 있다.
int nvme_enable_ctrl(struct nvme_ctrl *ctrl, u64 cap)
{
/*
* Default to a 4K page size, with the intention to update this
* path in the future to accomodate architectures with differing
* kernel and IO page sizes.
*/
unsigned dev_page_min = NVME_CAP_MPSMIN(cap) + 12, page_shift = 12;
int ret;
if (page_shift < dev_page_min) {
dev_err(ctrl->device,
"Minimum device page size %u too large for host (%u)\n",
1 << dev_page_min, 1 << page_shift);
return -ENODEV;
}
ctrl->page_size = 1 << page_shift;
ctrl->ctrl_config = NVME_CC_CSS_NVM;
ctrl->ctrl_config |= (page_shift - 12) << NVME_CC_MPS_SHIFT;
ctrl->ctrl_config |= NVME_CC_ARB_RR | NVME_CC_SHN_NONE;
ctrl->ctrl_config |= NVME_CC_IOSQES | NVME_CC_IOCQES;
ctrl->ctrl_config |= NVME_CC_ENABLE;
ctrl->ctrl_config |= NVME_CC_ARB_WRRU; //Use WRR sched.
ret = ctrl->ops->reg_write32(ctrl, NVME_REG_CC, ctrl->ctrl_config);
if (ret)
return ret;
return nvme_wait_ready(ctrl, cap, true);
}
EXPORT_SYMBOL_GPL(nvme_enable_ctrl);
기존은 NVME_CC_ARB_RR 즉, Round Robin으로 스케줄링을 하도록 되어있지만,
NVME_CC_ARB_WRRU, Weighted Round Robin으로 스케줄링 하도록 컨트롤러를 설정하는 것이다.
그리고 이 값을 cc에 설정하는 것이다.
그리고 이는 nvme 스펙과 관련이 있다.
그리고 해당 매크로 값은 다음과 같이 정의되어 있다.
enum {
NVME_CC_ENABLE = 1 << 0,
NVME_CC_CSS_NVM = 0 << 4,
NVME_CC_MPS_SHIFT = 7,
NVME_CC_ARB_RR = 0 << 11,
NVME_CC_ARB_WRRU = 1 << 11,
...
}
하지만 WRR을 디바이스 자체가 지원해야 하는데, 이에 대한 정보는 아래의 비트 정보를 통해 확인 할 수 있다.
테스트한 nvme ssd는 아래와 같았다.
0x33fff = 001(1) 0011 1111 1111 1111
17번째 bit가 1이므로 WRR을 지원하고 있다.
추가로 cap은 다음과 같이 디바이스로 부터 읽어 온다.
static int nvme_pci_enable(struct nvme_dev *dev)
{
...
dev->ctrl.cap = lo_hi_readq(dev->bar + NVME_REG_CAP);
dev->q_depth = min_t(int, NVME_CAP_MQES(dev->ctrl.cap) + 1,
io_queue_depth);
dev->db_stride = 1 << NVME_CAP_STRIDE(dev->ctrl.cap);
dev->dbs = dev->bar + 4096;
...
}
'리눅스 커널 > 블록 멀티 큐' 카테고리의 다른 글
NVMe device driver code tuning & comments - 4 (0) | 2021.10.26 |
---|---|
NVMe device driver code tuning & comments - 3 (0) | 2021.10.26 |
NVMe device driver code tuning & comments - 1 (0) | 2021.10.26 |
urgent queue 를 위한 tag 예약 (0) | 2021.10.26 |
blk_mq_init_queue (0) | 2021.10.26 |