본문 바로가기

리눅스 커널/블록 멀티 큐

(16)
nvme 디바이스 레이어에서 read/write 연산 횟수 계산 방법 struct nvme_queue { ... //int ssize; int szs[2]; //READ: 0, WRITE: 1 int ops[1024]; //sq의 depth에 맞게 설정. 포인터로 선언해서 동적할당하기를 권장 }; static blk_status_t nvme_queue_rq(struct blk_mq_hw_ctx *hctx, const struct blk_mq_queue_data *bd) { ... if (nvmeq->qid > 0) //io를 위한 sq에 대해서, { nvmeq->ops[nvmeq->sq_tail] = rq_data_dir(req); nvmeq->szs[rq_data_dir(req)] += 1; printk("log, qid: %d, read: %d, write: %d, t..
Limited sq entry implementation struct blk_mq_alloc_data { /* input parameter */ struct request_queue *q; unsigned int flags; unsigned int shallow_depth; /* input & output parameter */ struct blk_mq_ctx *ctx; struct blk_mq_hw_ctx *hctx; unsigned int op; //read, write 확인을 위해 }; struct blk_mq_tags { unsigned int nr_tags; unsigned int nr_reserved_tags; atomic_t active_queues; struct sbitmap_queue bitmap_tags; struct sbitmap_queue..
NVMe device driver code tuning & comments - 5 작성한 커널 코드를 정리하던 중에 디버깅용으로 작성한 코드 중 SQ의 쌓인 entry 개수를 측정하는 함수가 있었다. 여기서는 이와 관련된 것을 정리하겠다. SQ의 entry가 증가하는 경우는 __nvme_submit_cmd()와 __nvme_submit_cmd_extra()를 참조하면 된다. 해당 함수에서 size를 1 증가시키는 코드의 주석을 해제하면 된다. static void __nvme_submit_cmd(struct nvme_queue *nvmeq, struct nvme_command *cmd) { u16 tail = nvmeq->sq_tail; if (nvmeq->sq_cmds_io) memcpy_toio(&nvmeq->sq_cmds_io[tail], cmd, sizeof(*cmd)); el..
NVMe device driver code tuning & comments - 4 디바이스 드라이버 초기화에 이어서, 이번에는 IO를 각 SQ에 어떻게 큐잉하는지에 대한 내용이다. 파일 시스템부터 디바이스 드라이버 계층 까지의 코드 흐름은 일단 생략하고, nvme_queue_rq() 함수에 대해 소개하겠다. 이 함수에 대해 간단히 설명하면 블록 멀티 큐에서 nvme device driver 로 넘어갈 때 호출되는 함수이다. 그래서 IO type에 따라 알맞는 SQ로 큐잉하기 위해 다음과 같이 함수 일부를 수정하였다. static blk_status_t nvme_queue_rq(struct blk_mq_hw_ctx *hctx, const struct blk_mq_queue_data *bd) { ... op = rq_data_dir(req); if (nvmeq->qid == 0) __nv..
NVMe device driver code tuning & comments - 3 이번에는 nvme_queue 구조체 안에 여러개의 SQ를 만들 차례이다. nvme_reset_work() -> nvme_setup_io_queues() -> nvme_create_io_queues() 먼저 모든 nvme_queue는 원래대로 SQ를 각각 하나 씩 생성하게 한다. 그리고 이것이 성공하면 다음과 같이 모든 코어에 추가로 SQ를 생성하게 한다. static int nvme_create_io_queues(struct nvme_dev *dev) { ... if (!ret) //all nvme queue was successfully created { int result = 0, extra_sq_cnt = 0, qid = 1; while (!(result = nvme_create_extra_queu..
NVMe device driver code tuning & comments - 2 이번에는 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_n..
NVMe device driver code tuning & comments - 1 구현한 NVMe device driver 구조. nvme device driver 관련된 자료구조중 nvme_queue 구조체가 3개의 SQ를 갖게한다. 3개의 SQ는 아래와 같은데, 1. Read Only SQ 2. Write Only SQ 3. Urgent SQ NVMe SSD가 WRR을 지원하면, 생성할 SQ의 Weight를 설정하여 IO의 차별적인 서비스 제공이 가능해진다. 여기서는 좀 극단적으로 Write에는 2, Read에는 8이라는 weight를 주어 응답성과 관련된 read연산의 성능을 향상 시키는 것을 목표로 하였고, 더 나아가 Urgent SQ를 두어 우선순위가 높은 프로세스의 IO는 이보다 더 빨리 처리되도록 구현하였다. 이는 아래와 같은 스케줄링을 NVMe SSD가 하기 때문이다. ..
urgent queue 를 위한 tag 예약 1. request_queue 구조체 안에 int adm; 추가 ->adm = 0; //일반 IO 큐로 다룬다. ->adm = 1; //관리자 큐로 다룬다. 2. request 구조체 안에 int urg; 추가 3. blk_mq_tags 구조체 안에 int nr_allocated; 추가 ->read/write를 위해 할당한 태그 개수를 저장한다. 4. __blk_mq_get_tag() 수정 static int __blk_mq_get_tag(struct blk_mq_alloc_data *data, struct sbitmap_queue *bt) { if (!(data->flags & BLK_MQ_REQ_INTERNAL) && !hctx_may_queue(data->hctx, bt)) return -1; if..
blk_mq_init_queue 블록 레이어에서 사용할 request_queue 를 할당하기 위해 호출되는 함수이다. 관리자 큐를 위해 nvme_alloc_admin_tagset() 에서 호출되는 것과 io 큐를 위해 nvme_alloc_ns() 에서 호출된다. 아래 흐름은 io 큐를 위해 호출된 상황이므로 set 은 dev.tagset 이다. 관리자 용 tagset이 아닌 것이다. blk_mq_init_queue(struct blk_mq_tag_set * set) from nvme_alloc_ns // ->uninit_q = blk_alloc_queue_node(GFP_KERNEL, set->numa_node); ->blk_mq_init_allocated_queue(set, uninit_q); // ->q->mq_ops = set->..
blk_mq_alloc_tag_set 흐름 이 함수는 nvme_reset_work() 에서 nvme_alloc_admin_tags() 에서도 호출된다. 그러나 이 때에는 관리자 큐를 위한 tagset 및 tags를 할당하기 위한 것이나 그 흐름은 거의 유사하다. nvme_dev_add(struct nvme_dev * dev) from nvme_reset_work ->dev->tagset.ops = &nvme_mq_ops; ->dev->tagset.nr_hw_queues = dev->online_queues - 1; ->dev->tagset.queue_depth = min_t(int, dev->q_depth, BLK_MQ_MAX_DEPTH) - 1; ->dev->tagset.driver_data = dev; ->blk_mq_alloc_tag_set..