linux/tools/virtio/linux/scatterlist.h
<<
>>
Prefs
   1/* SPDX-License-Identifier: GPL-2.0 */
   2#ifndef SCATTERLIST_H
   3#define SCATTERLIST_H
   4#include <linux/kernel.h>
   5
   6struct scatterlist {
   7        unsigned long   page_link;
   8        unsigned int    offset;
   9        unsigned int    length;
  10        dma_addr_t      dma_address;
  11};
  12
  13/* Scatterlist helpers, stolen from linux/scatterlist.h */
  14#define sg_is_chain(sg)         ((sg)->page_link & 0x01)
  15#define sg_is_last(sg)          ((sg)->page_link & 0x02)
  16#define sg_chain_ptr(sg)        \
  17        ((struct scatterlist *) ((sg)->page_link & ~0x03))
  18
  19/**
  20 * sg_assign_page - Assign a given page to an SG entry
  21 * @sg:             SG entry
  22 * @page:           The page
  23 *
  24 * Description:
  25 *   Assign page to sg entry. Also see sg_set_page(), the most commonly used
  26 *   variant.
  27 *
  28 **/
  29static inline void sg_assign_page(struct scatterlist *sg, struct page *page)
  30{
  31        unsigned long page_link = sg->page_link & 0x3;
  32
  33        /*
  34         * In order for the low bit stealing approach to work, pages
  35         * must be aligned at a 32-bit boundary as a minimum.
  36         */
  37        BUG_ON((unsigned long) page & 0x03);
  38#ifdef CONFIG_DEBUG_SG
  39        BUG_ON(sg_is_chain(sg));
  40#endif
  41        sg->page_link = page_link | (unsigned long) page;
  42}
  43
  44/**
  45 * sg_set_page - Set sg entry to point at given page
  46 * @sg:          SG entry
  47 * @page:        The page
  48 * @len:         Length of data
  49 * @offset:      Offset into page
  50 *
  51 * Description:
  52 *   Use this function to set an sg entry pointing at a page, never assign
  53 *   the page directly. We encode sg table information in the lower bits
  54 *   of the page pointer. See sg_page() for looking up the page belonging
  55 *   to an sg entry.
  56 *
  57 **/
  58static inline void sg_set_page(struct scatterlist *sg, struct page *page,
  59                               unsigned int len, unsigned int offset)
  60{
  61        sg_assign_page(sg, page);
  62        sg->offset = offset;
  63        sg->length = len;
  64}
  65
  66static inline struct page *sg_page(struct scatterlist *sg)
  67{
  68#ifdef CONFIG_DEBUG_SG
  69        BUG_ON(sg_is_chain(sg));
  70#endif
  71        return (struct page *)((sg)->page_link & ~0x3);
  72}
  73
  74/*
  75 * Loop over each sg element, following the pointer to a new list if necessary
  76 */
  77#define for_each_sg(sglist, sg, nr, __i)        \
  78        for (__i = 0, sg = (sglist); __i < (nr); __i++, sg = sg_next(sg))
  79
  80/**
  81 * sg_chain - Chain two sglists together
  82 * @prv:        First scatterlist
  83 * @prv_nents:  Number of entries in prv
  84 * @sgl:        Second scatterlist
  85 *
  86 * Description:
  87 *   Links @prv@ and @sgl@ together, to form a longer scatterlist.
  88 *
  89 **/
  90static inline void sg_chain(struct scatterlist *prv, unsigned int prv_nents,
  91                            struct scatterlist *sgl)
  92{
  93        /*
  94         * offset and length are unused for chain entry.  Clear them.
  95         */
  96        prv[prv_nents - 1].offset = 0;
  97        prv[prv_nents - 1].length = 0;
  98
  99        /*
 100         * Set lowest bit to indicate a link pointer, and make sure to clear
 101         * the termination bit if it happens to be set.
 102         */
 103        prv[prv_nents - 1].page_link = ((unsigned long) sgl | 0x01) & ~0x02;
 104}
 105
 106/**
 107 * sg_mark_end - Mark the end of the scatterlist
 108 * @sg:          SG entryScatterlist
 109 *
 110 * Description:
 111 *   Marks the passed in sg entry as the termination point for the sg
 112 *   table. A call to sg_next() on this entry will return NULL.
 113 *
 114 **/
 115static inline void sg_mark_end(struct scatterlist *sg)
 116{
 117        /*
 118         * Set termination bit, clear potential chain bit
 119         */
 120        sg->page_link |= 0x02;
 121        sg->page_link &= ~0x01;
 122}
 123
 124/**
 125 * sg_unmark_end - Undo setting the end of the scatterlist
 126 * @sg:          SG entryScatterlist
 127 *
 128 * Description:
 129 *   Removes the termination marker from the given entry of the scatterlist.
 130 *
 131 **/
 132static inline void sg_unmark_end(struct scatterlist *sg)
 133{
 134        sg->page_link &= ~0x02;
 135}
 136
 137static inline struct scatterlist *sg_next(struct scatterlist *sg)
 138{
 139        if (sg_is_last(sg))
 140                return NULL;
 141
 142        sg++;
 143        if (unlikely(sg_is_chain(sg)))
 144                sg = sg_chain_ptr(sg);
 145
 146        return sg;
 147}
 148
 149static inline void sg_init_table(struct scatterlist *sgl, unsigned int nents)
 150{
 151        memset(sgl, 0, sizeof(*sgl) * nents);
 152        sg_mark_end(&sgl[nents - 1]);
 153}
 154
 155static inline dma_addr_t sg_phys(struct scatterlist *sg)
 156{
 157        return page_to_phys(sg_page(sg)) + sg->offset;
 158}
 159
 160static inline void sg_set_buf(struct scatterlist *sg, const void *buf,
 161                              unsigned int buflen)
 162{
 163        sg_set_page(sg, virt_to_page(buf), buflen, offset_in_page(buf));
 164}
 165
 166static inline void sg_init_one(struct scatterlist *sg,
 167                               const void *buf, unsigned int buflen)
 168{
 169        sg_init_table(sg, 1);
 170        sg_set_buf(sg, buf, buflen);
 171}
 172#endif /* SCATTERLIST_H */
 173