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