at v4.15-rc2 190 lines 4.6 kB view raw
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 */