scsi_alloc_sgtables
函数分析
struct scatterlist {
unsigned longpage_link;
unsigned intoffset;
unsigned intlength;
dma_addr_tdma_address;
#ifdef CONFIG_NEED_SG_DMA_LENGTH
unsigned intdma_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_CHAIN0x01UL
#define SG_END0x02UL/*
* 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]);
}
【scsi_alloc_sgtables函数分析】__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;
#endifleft = 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;
}