Author: Yang Honggang (Joseph) <eagle.rtlinux@gmail.com>
Dslab lzu
Last Modified: 12-03-2011

NOTE: Kernel Version linux-2.6.38.8
-----------------------------------------------------------------------
This article just explain how to use scatterlist and the initializtion
of scatterlist. As to How to Use the Scatter/Gather APIs, you can
refer Documentation/DMA* for more information.

----------------------------------------------------------------------
contents:
1> structure explanation
2> How to init the scatterlist
3> Find the next scatterlist entry

===============================================
1> structure explanation

struct scatterlist {
     ...
     /* User input members */
    unsigned long   page_link;// pointer to a page, but the bit0 and bit1 have special info.[1]
    unsigned int    offset; / / Offset of data buffer in page referred by @page_link
    unsigned int    length; //Length of data
    /* Output value */
    dma_addr_t  dma_address; // this address can be used by device to do DMA
     ...
};

 [1]: Explanation of 'page_link' in struct scatterlist
 
 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.
 /*
 * 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 & 0x01)
#define sg_is_last(sg)      ((sg)->page_link & 0x02)
#define sg_chain_ptr(sg)    \
    ((struct scatterlist *) ((sg)->page_link & ~0x03))

2> How to init the scatterlist

/**
 * sg_assign_page - Assign a given page to an SG entry
 * @sg:         SG entry
 * @page:       The page
 *
 * Description:
 *   Assign page to sg entry. Also see sg_set_page(), the most commonly used
 *   variant.
 *
 **/
static inline void sg_assign_page(struct scatterlist *sg, struct page *page)
{
    unsigned long page_link = sg->page_link & 0x3;

    /*
     * In order for the low bit stealing approach to work, pages
     * must be aligned at a 32-bit boundary as a minimum.
     */
    BUG_ON((unsigned long) page & 0x03);
     ...
    sg->page_link = page_link | (unsigned long) page;
}

/**
 * sg_set_page - Set sg entry to point at given page
 * @sg:      SG entry
 * @page:    The page
 * @len:     Length of data
 * @offset:   Offset into page // Offset in page ???
 *

 * Description:
 *   Use this function to set an sg entry pointing at a page, never assign
 *   the page directly. We encode sg table information in the lower bits
 *   of the page pointer. See sg_page() for looking up the page belonging
 *   to an sg entry.
 *
 **/
static inline void sg_set_page(struct scatterlist *sg, struct page *page,
                   unsigned int len, unsigned int offset)
{
    sg_assign_page(sg, page);
    sg->offset = offset;
    sg->length = len;
}

/**
 * sg_set_buf - Set sg entry to point at given data
 * @sg:      SG entry
 * @buf:     Data
 * @buflen:  Data length
 *
 **/
static inline void sg_set_buf(struct scatterlist *sg, const void *buf,
                  unsigned int buflen)
{
    /* offset_in_page(buf) will get the offset in a page */
    sg_set_page(sg, virt_to_page(buf), buflen, offset_in_page(buf));  
}
// ~PAGE_MASK = 0xfff
#define offset_in_page(p) ((unsigned long )(p) & ~PAGE_MASK)// Get the lowest 12bits.
                                                                                                                       // That's the offset in a page.




3> Find the next scatterlist entry

/**
 * sg_next - return the next scatterlist entry in a list
 * @sg:     The current sg entry
 *
 * Description:
 *   Usually the next entry will be @sg@ + 1, but if this sg element is part
 *   of a chained scatterlist, it could jump to the start of a new
 *   scatterlist array.
 *
 **/
struct scatterlist *sg_next(struct scatterlist *sg)
{
    ...
    if (sg_is_last(sg))
        return NULL;

    sg++;
    if (unlikely(sg_is_chain(sg)))
        sg = sg_chain_ptr(sg);

    return sg;
}
EXPORT_SYMBOL(sg_next);

GitHub 加速计划 / li / linux-dash
6
1
下载
A beautiful web dashboard for Linux
最近提交(Master分支:3 个月前 )
186a802e added ecosystem file for PM2 4 年前
5def40a3 Add host customization support for the NodeJS version 4 年前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐