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->sg_magic != SG_MAGIC);
  40        BUG_ON(sg_is_chain(sg));
  41#endif
  42        sg->page_link = page_link | (unsigned long) page;
  43}
  44
  45/**
  46 * sg_set_page - Set sg entry to point at given page
  47 * @sg:          SG entry
  48 * @page:        The page
  49 * @len:         Length of data
  50 * @offset:      Offset into page
  51 *
  52 * Description:
  53 *   Use this function to set an sg entry pointing at a page, never assign
  54 *   the page directly. We encode sg table information in the lower bits
  55 *   of the page pointer. See sg_page() for looking up the page belonging
  56 *   to an sg entry.
  57 *
  58 **/
  59static inline void sg_set_page(struct scatterlist *sg, struct page *page,
  60                               unsigned int len, unsigned int offset)
  61{
  62        sg_assign_page(sg, page);
  63        sg->offset = offset;
  64        sg->length = len;
  65}
  66
  67static inline struct page *sg_page(struct scatterlist *sg)
  68{
  69#ifdef CONFIG_DEBUG_SG
  70        BUG_ON(sg->sg_magic != SG_MAGIC);
  71        BUG_ON(sg_is_chain(sg));
  72#endif
  73        return (struct page *)((sg)->page_link & ~0x3);
  74}
  75
  76/*
  77 * Loop over each sg element, following the pointer to a new list if necessary
  78 */
  79#define for_each_sg(sglist, sg, nr, __i)        \
  80        for (__i = 0, sg = (sglist); __i < (nr); __i++, sg = sg_next(sg))
  81
  82/**
  83 * sg_chain - Chain two sglists together
  84 * @prv:        First scatterlist
  85 * @prv_nents:  Number of entries in prv
  86 * @sgl:        Second scatterlist
  87 *
  88 * Description:
  89 *   Links @prv@ and @sgl@ together, to form a longer scatterlist.
  90 *
  91 **/
  92static inline void sg_chain(struct scatterlist *prv, unsigned int prv_nents,
  93                            struct scatterlist *sgl)
  94{
  95        /*
  96         * offset and length are unused for chain entry.  Clear them.
  97         */
  98        prv[prv_nents - 1].offset = 0;
  99        prv[prv_nents - 1].length = 0;
 100
 101        /*
 102         * Set lowest bit to indicate a link pointer, and make sure to clear
 103         * the termination bit if it happens to be set.
 104         */
 105        prv[prv_nents - 1].page_link = ((unsigned long) sgl | 0x01) & ~0x02;
 106}
 107
 108/**
 109 * sg_mark_end - Mark the end of the scatterlist
 110 * @sg:          SG entryScatterlist
 111 *
 112 * Description:
 113 *   Marks the passed in sg entry as the termination point for the sg
 114 *   table. A call to sg_next() on this entry will return NULL.
 115 *
 116 **/
 117static inline void sg_mark_end(struct scatterlist *sg)
 118{
 119#ifdef CONFIG_DEBUG_SG
 120        BUG_ON(sg->sg_magic != SG_MAGIC);
 121#endif
 122        /*
 123         * Set termination bit, clear potential chain bit
 124         */
 125        sg->page_link |= 0x02;
 126        sg->page_link &= ~0x01;
 127}
 128
 129/**
 130 * sg_unmark_end - Undo setting the end of the scatterlist
 131 * @sg:          SG entryScatterlist
 132 *
 133 * Description:
 134 *   Removes the termination marker from the given entry of the scatterlist.
 135 *
 136 **/
 137static inline void sg_unmark_end(struct scatterlist *sg)
 138{
 139#ifdef CONFIG_DEBUG_SG
 140        BUG_ON(sg->sg_magic != SG_MAGIC);
 141#endif
 142        sg->page_link &= ~0x02;
 143}
 144
 145static inline struct scatterlist *sg_next(struct scatterlist *sg)
 146{
 147#ifdef CONFIG_DEBUG_SG
 148        BUG_ON(sg->sg_magic != SG_MAGIC);
 149#endif
 150        if (sg_is_last(sg))
 151                return NULL;
 152
 153        sg++;
 154        if (unlikely(sg_is_chain(sg)))
 155                sg = sg_chain_ptr(sg);
 156
 157        return sg;
 158}
 159
 160static inline void sg_init_table(struct scatterlist *sgl, unsigned int nents)
 161{
 162        memset(sgl, 0, sizeof(*sgl) * nents);
 163#ifdef CONFIG_DEBUG_SG
 164        {
 165                unsigned int i;
 166                for (i = 0; i < nents; i++)
 167                        sgl[i].sg_magic = SG_MAGIC;
 168        }
 169#endif
 170        sg_mark_end(&sgl[nents - 1]);
 171}
 172
 173static inline dma_addr_t sg_phys(struct scatterlist *sg)
 174{
 175        return page_to_phys(sg_page(sg)) + sg->offset;
 176}
 177
 178static inline void sg_set_buf(struct scatterlist *sg, const void *buf,
 179                              unsigned int buflen)
 180{
 181        sg_set_page(sg, virt_to_page(buf), buflen, offset_in_page(buf));
 182}
 183
 184static inline void sg_init_one(struct scatterlist *sg,
 185                               const void *buf, unsigned int buflen)
 186{
 187        sg_init_table(sg, 1);
 188        sg_set_buf(sg, buf, buflen);
 189}
 190#endif /* SCATTERLIST_H */
 191