본문 바로가기

리눅스 커널/블록 멀티 큐

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, total: %d\n", 
                	nvmeq->qid, nvmeq->szs[0], nvmeq->szs[1], nvmeq->szs[0] + nvmeq->szs[1]);
        }
        __nvme_submit_cmd(nvmeq, &cmnd);
        nvme_process_cq(nvmeq);
        spin_unlock_irq(&nvmeq->q_lock);
        return BLK_STS_OK;
out_cleanup_iod:
        nvme_free_iod(dev, req);
out_free_cmd:
        nvme_cleanup_cmd(req);
        return ret;
}

 

static inline bool nvme_read_cqe(struct nvme_queue *nvmeq,
                struct nvme_completion *cqe)
{
        if (nvme_cqe_valid(nvmeq, nvmeq->cq_head, nvmeq->cq_phase)) {
                *cqe = nvmeq->cqes[nvmeq->cq_head];

                if (nvmeq->qid > 0) //io를 위한 sq에 대해서
                        nvmeq->szs[nvmeq->ops[nvmeq->cq_head]] -= 1; //dec

                if (++nvmeq->cq_head == nvmeq->q_depth) {
                        nvmeq->cq_head = 0;
                        nvmeq->cq_phase = !nvmeq->cq_phase;
                }
                return true;
        }
        return false;
}

 

sq가 원형큐라는 점을 이용했다.

현재 tail위치에 연산 정보를 기록한다. READ == 0, WRITE == 1이다.

그리고 이 값을 인덱스로 하여 read와 write 개수를 증가시킨다.

 

io가 끝나면 head의 위치부터 확인하게 된다.

그래서 이 head값을 토대로 큐잉시 저장해두었던 연산 정보를 읽어와서 해당 인덱스의 위치한 값을 하나 감소시킨다.