参考:
1:https://developer.aliyun.com/article/784610
2:https://developer.aliyun.com/article/770780
内核版本:4.19.1
结构体定义如下:
struct queue_limits {
unsigned long bounce_pfn;
unsigned long seg_boundary_mask;
unsigned long virt_boundary_mask;
unsigned int max_hw_sectors;
unsigned int max_dev_sectors;
unsigned int chunk_sectors;
unsigned int max_sectors;
unsigned int max_write_same_sectors;
unsigned int max_write_zeroes_sectors;
unsigned short max_segments;
unsigned int max_segment_size;
unsigned int physical_block_size;
unsigned short logical_block_size;
unsigned int io_min;
unsigned int io_opt;
unsigned int alignment_offset;
unsigned int max_discard_sectors;
unsigned int max_hw_discard_sectors;
unsigned int discard_granularity;
unsigned int discard_alignment;
unsigned short max_discard_segments;
unsigned char discard_misaligned;
unsigned short max_integrity_segments;
unsigned char misaligned;
unsigned char cluster;
unsigned char raid_partial_stripes_expensive;
enum blk_zoned_model zoned;
};
内核提供了相关的API接口去设置这些相关的参数,
在blk-settings.c文件。
该参数我的理解是 这个参数的描述的地址是一个dma地址,也就是给控制器用的,我猜测应该是通过dma_xx这系列函数将内核态的虚拟地址映射为dma地址以后,这个dma地址不能超过seg_boundary_mask 的值。
这个参数起作用的地方应该主要是在bio合并的时候调用了这个宏,该宏会对给seg_boundary_mask 的值做一些相关的限制操作。
include/linux/bio.h
BIOVEC_SEG_BOUNDARY
/**
* blk_queue_segment_boundary - set boundary rules for segment merging
* @q: the request queue for the device
* @mask: the memory boundary mask
**/
void blk_queue_segment_boundary(struct request_queue *q, unsigned long mask)
{
if (mask < PAGE_SIZE - 1) {
mask = PAGE_SIZE - 1;
printk(KERN_INFO "%s: set to minimum %lx\n", __func__, mask);
}
q->limits.seg_boundary_mask = mask;
}
EXPORT_SYMBOL(blk_queue_segment_boundary);
这个参数我的理解是最后生成的dma地址,必须要满足nvme 的prp模型。
在bio生成的时候也会做类型的限制,比如在bio_add_pc_page函数当中调用的
bvec_gap_to_prev函数,就是做类似的操作的。
block/bio.c
/**
* blk_queue_virt_boundary - set boundary rules for bio merging
* @q: the request queue for the device
* @mask: the memory boundary mask
**/
void blk_queue_virt_boundary(struct request_queue *q, unsigned long mask)
{
q->limits.virt_boundary_mask = mask;
}
EXPORT_SYMBOL(blk_queue_virt_boundary);
这两个参数比较容易理解,但是个人感觉加上physical字段会更好,比如
段这个概念是在bio里的bvec去表示的,它可能是page的一部分,也可能等于page,但是在驱动里描述的时候,用的是内核态的虚拟地址,而且不同的bvec表示的page在虚拟地址当中可能不是连续的,但是在物理上可能是连续的,比如bvec[0]和bvec[1],是连续的,那么这个时候对于当前io请求来说,它的段的个数是1,这个1会和max_segments值进行比较。
同理,max_segment_size值就是bvec[0]和bvec[1]的地址长度大小了。
max_segments:
/**
* blk_queue_max_segments - set max hw segments for a request for this queue
* @q: the request queue for the device
* @max_segments: max number of segments
*
* Description:
* Enables a low level driver to set an upper limit on the number of
* hw data segments in a request.
**/
void blk_queue_max_segments(struct request_queue *q, unsigned short max_segments)
{
if (!max_segments) {
max_segments = 1;
printk(KERN_INFO "%s: set to minimum %d\n", __func__, max_segments);
}
q->limits.max_segments = max_segments;
}
EXPORT_SYMBOL(blk_queue_max_segments);
max_segment_size:
/**
* blk_queue_max_segment_size - set max segment size for blk_rq_map_sg
* @q: the request queue for the device
* @max_size: max size of segment in bytes
*
* Description:
* Enables a low level driver to set an upper limit on the size of a
* coalesced segment
**/
void blk_queue_max_segment_size(struct request_queue *q, unsigned int max_size)
{
if (max_size < PAGE_SIZE) {
max_size = PAGE_SIZE;
printk(KERN_INFO "%s: set to minimum %d\n", __func__, max_size);
}
q->limits.max_segment_size = max_size;
}
EXPORT_SYMBOL(blk_queue_max_segment_size);
查看blk-setting.c里面的源码,发现并没有设置max_dev_sectors和max_sectors4参数值的api, 而只有设置max_hw_sectors和chunk_sectors的。
max_hw_sectors参数的话,在发起io请求时,req->bio->bi_iter.bi_size的值不会超过max_hw_sectors(最小值是8).
而chunk_sectors值的是在连续两次调用queue_rq回调函数时,blk_rq_pos(req)得到的扇区差值不会超过chunk_sectors。
另外两个参数没有相关api设置的话就不研究了,而且在设置max_hw_sectors值的时候,也会考虑max_dev_sectors和max_sectors4的值,,这一块我写demo,加上打印,确实是这样子。
这3个参数的值和LBA值的形成有关系,也就是和struct queue结构体里的__sector值有关系,
通过打印blk_rq_cur_sectors的值可以分析出,直接的影响就是bio里面每一个bvec的大小,比如设置为4096,
那么打印blk_rq_cur_sectors的值就为4096/512,,而physical_block_size参数和io_min的设置倒没有发现那个值会有改变或者规律,通常驱动代码里,physical_block_size和io_min是和logical_block_size一块设置的。(比如nvme驱动)。
也就是说__sector的值是和logical_block_size的值是对齐的,比如get_max_io_size函数就是做类似对齐的功能的。