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