scsi_alloc_sgtables函数分析

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

    推荐阅读