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 breserved_tags;
struct request **rqs;
struct request **static_rqs;
struct list_head page_list;
int q_size[3]; //한 코어당 가질 수 있는 최대 sq개수로 설정, write only, read only, urgent
};
static struct request *blk_mq_get_request(struct request_queue *q,
struct bio *bio, unsigned int op,
struct blk_mq_alloc_data *data)
{
...
data->op = op; //연산종류 저장
tag = blk_mq_get_tag(data);
...
}
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;
//원래 커널이 설정한 io_queue_depth = 1024 로 설정,
if (current->prio < 120 && data->hctx->tags->q_size[2] >= 1024)
return -1;
//REQ_OP_MASK를 통하여 READ, WRITE를 추출해야함,
else if ((data->op & REQ_OP_MASK) == WRITE && data->hctx->tags->q_size[0] >= 1024)
return -1;
else if ((data->op & REQ_OP_MASK) == READ && data->hctx->tags->q_size[1] >= 1024)
return -1;
if (data->shallow_depth)
return __sbitmap_queue_get_shallow(bt, data->shallow_depth);
else
return __sbitmap_queue_get(bt);
}
static void __nvme_submit_cmd(struct nvme_queue *nvmeq,
struct nvme_command *cmd)
{
...
if (nvmeq->qid)
{
nvmeq->size += 1;
(*(nvmeq->tags))->q_size[0] += 1; //블록레이어에서 접근 하기 위함,
}
nvmeq->sq_tail = tail;
}
static void __nvme_submit_cmd_extra(struct nvme_queue *nvmeq,
struct nvme_command *cmd)
{
struct extra_queue * sq = nvmeq->extra_list[nvmeq->extra_cursor];
int sq_idx;
if (current->prio < 120)
sq_idx = 1; //sq = nvmeq->extra_list[1];
else
sq_idx = 0; //sq = nvmeq->extra_list[0];
sq = nvmeq->extra_list[sq_idx];
sq->size += 1;
(*(nvmeq->tags))->q_size[sq_idx + 1] += 1; //블록레이어에서 접근 하기 위함
//printk("log, qid_extra: %d, size: %d\n", sq->qid_extra, (*(nvmeq->tags))->q_size[sq_idx + 1]);
//if (++nvmeq->extra_cursor >= nvmeq->extra_cnt)
//nvmeq->extra_cursor = 0;
sq->sq_tail_extra = tail;
}
static inline void nvme_queue_size_update(struct nvme_queue * nvmeq, u16 sq_id)
{
int max = nvmeq->dev->online_queues - 1;
sq_id -= 1;
if (sq_id < max)
{
nvmeq->size -= 1;
(*(nvmeq->tags))->q_size[0] -= 1;
}
else
{
nvmeq->extra_list[(sq_id / max) - 1]->size -= 1;
(*(nvmeq->tags))->q_size[(sq_id / max)] -= 1;
}
}
struct blk_mq_tags 가 갖고 있는 request pool의 길이를 늘리기 위해, io_queue_depth를 늘려야 한다.
이 과정에서 각 sq의 depth도 같이 늘어난 상태로 생성된다.
그래서 각 sq의 depth를 의도적으로 1024로 제한하기 위해 커널 코드를 수정했다.
핵심은 blk_mq_get_request()에서 tag 획득을 지연 시키는 것이다.
큐잉할 sq의 현재 사이즈가 1024, 즉 원래 커널이 제한하고 있는 값 이라면
-1을 반환하여 tag획득을 저지하여 request획득을 저지하여 SQ에 큐잉하지 못하도록 하는 것이다.
'리눅스 커널 > 블록 멀티 큐' 카테고리의 다른 글
nvme 디바이스 레이어에서 read/write 연산 횟수 계산 방법 (0) | 2021.10.27 |
---|---|
NVMe device driver code tuning & comments - 5 (0) | 2021.10.26 |
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 - 2 (0) | 2021.10.26 |