共计 3330 个字符,预计需要花费 9 分钟才能阅读完成。
scsi_alloc_sgtables
函数剖析
struct scatterlist {
unsigned long page_link;
unsigned int offset;
unsigned int length;
dma_addr_t dma_address;
#ifdef CONFIG_NEED_SG_DMA_LENGTH
unsigned int dma_length;
#endif
};
示意的是 sg entry 在内存中的地址和长度。
page_link 的作用:
依据 page_link 的 bit0 和 bit1 的具体值(bit0 sg 是否是链,bit1 sg 是否是最初一个)。
两种作用复用,
一是示意 sg entry 指向的内存页 page 构造体,
二是对于链起来的 sg list,示意 sg_table 指向的下一个 sg_table。
一个内存页里,对应 sg_table 外面的各 sg_entry 是内存地址间断的(数组),
如果 sg list 超过一个内存页,就得链起来。
/*
* Notes on SG table design.
*
* We use the unsigned long page_link field in the scatterlist struct to place
* the page pointer AND encode information about the sg table as well. The two
* lower bits are reserved for this information.
*
* If bit 0 is set, then the page_link contains a pointer to the next sg
* table list. Otherwise the next entry is at sg + 1.
*
* If bit 1 is set, then this sg entry is the last element in a list.
*
* See sg_next().
*
*/
#define SG_CHAIN 0x01UL
#define SG_END 0x02UL
/*
* We overload the LSB of the page pointer to indicate whether it's
* a valid sg entry, or whether it points to the start of a new scatterlist.
* Those low bits are there for everyone! (thanks mason :-)
*/
#define sg_is_chain(sg) ((sg)->page_link & SG_CHAIN)
#define sg_is_last(sg) ((sg)->page_link & SG_END)
#define sg_chain_ptr(sg) \
((struct scatterlist *) ((sg)->page_link & ~(SG_CHAIN | SG_END)))
数组的 sg_table:
/**
* sg_init_table - Initialize SG table
* @sgl: The SG table
* @nents: Number of entries in table
*
* Notes:
* If this is part of a chained sg table, sg_mark_end() should be
* used only on the last table part.
*
**/
void sg_init_table(struct scatterlist *sgl, unsigned int nents)
{memset(sgl, 0, sizeof(*sgl) * nents);
sg_init_marker(sgl, nents);
}
static inline void sg_init_marker(struct scatterlist *sgl,
unsigned int nents)
{sg_mark_end(&sgl[nents - 1]);
}
__sg_alloc_table 被调用:
/*
* Maximum number of entries that will be allocated in one piece, if
* a list larger than this is required then chaining will be utilized.
*/
#define SG_MAX_SINGLE_ALLOC (PAGE_SIZE / sizeof(struct scatterlist))
ret = __sg_alloc_table(table, nents, SG_MAX_SINGLE_ALLOC,
NULL, 0, gfp_mask, sg_kmalloc);
int __sg_alloc_table(struct sg_table *table, unsigned int nents,
unsigned int max_ents, struct scatterlist *first_chunk,
unsigned int nents_first_chunk, gfp_t gfp_mask,
sg_alloc_fn *alloc_fn)
{
struct scatterlist *sg, *prv;
unsigned int left;
// 一次调配的最大 size,如果第一个 chunk 有,就先用第一个 chunk 的。unsigned curr_max_ents = nents_first_chunk ?: max_ents;
unsigned prv_max_ents;
memset(table, 0, sizeof(*table));
if (nents == 0)
return -EINVAL;
#ifdef CONFIG_ARCH_NO_SG_CHAIN
if (WARN_ON_ONCE(nents > max_ents))
return -EINVAL;
#endif
left = nents;
prv = NULL;
do {
unsigned int sg_size, alloc_size = left;
if (alloc_size > curr_max_ents) {
alloc_size = curr_max_ents;
// 如果 alloc_size 比一次能申请的最大个数还大,那 sg list 就得链起来,最初一个 sg entry 就必须用作 sg_table 的指针,而不能当做一般的 sg entry,所以理论调配掉的 sg size 比调配的 sg entry 个数要减一。sg_size = alloc_size - 1;
} else
sg_size = alloc_size;
left -= sg_size;
if (first_chunk) {
sg = first_chunk;
first_chunk = NULL;
} else {sg = alloc_fn(alloc_size, gfp_mask);
}
if (unlikely(!sg)) {
/*
* Adjust entry count to reflect that the last
* entry of the previous table won't be used for
* linkage. Without this, sg_kfree() may get
* confused.
*/
if (prv)
table->nents = ++table->orig_nents;
return -ENOMEM;
}
// 一次调配的是内存间断的 sg_entry(数组)sg_init_table(sg, alloc_size);
table->nents = table->orig_nents += sg_size;
/*
* If this is the first mapping, assign the sg table header.
* If this is not the first mapping, chain previous part.
*/
if (prv)
sg_chain(prv, prv_max_ents, sg);
else
table->sgl = sg;
/*
* If no more entries after this one, mark the end
*/
if (!left)
sg_mark_end(&sg[sg_size - 1]);
prv = sg;
prv_max_ents = curr_max_ents;
// 如果第一个 chunk 有,用完之后,前面每次都是用的 max_ents(一个内存页能放下的最多 g_entry 的个数)curr_max_ents = max_ents;
} while (left);
return 0;
}
正文完
发表至: linux-kernel
2022-04-17