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;}