mirror of
				https://github.com/torvalds/linux.git
				synced 2025-10-31 16:48:26 +02:00 
			
		
		
		
	Change table chaining layout
Change the page member of the scatterlist structure to be an unsigned long, and encode more stuff in the lower bits: - Bits 0 and 1 zero: this is a normal sg entry. Next sg entry is located at sg + 1. - Bit 0 set: this is a chain entry, the next real entry is at ->page_link with the two low bits masked off. - Bit 1 set: this is the final entry in the sg entry. sg_next() will return NULL when passed such an entry. It's thus important that sg table users use the proper accessors to get and set the page member. Signed-off-by: Jens Axboe <jens.axboe@oracle.com>
This commit is contained in:
		
							parent
							
								
									58b053e4ce
								
							
						
					
					
						commit
						18dabf473e
					
				
					 25 changed files with 79 additions and 49 deletions
				
			
		|  | @ -5,7 +5,7 @@ | |||
| #include <asm/types.h> | ||||
|    | ||||
| struct scatterlist { | ||||
| 	struct page *page; | ||||
| 	unsigned long page_link; | ||||
| 	unsigned int offset; | ||||
| 
 | ||||
| 	unsigned int length; | ||||
|  |  | |||
|  | @ -5,7 +5,7 @@ | |||
| #include <asm/types.h> | ||||
| 
 | ||||
| struct scatterlist { | ||||
| 	struct page	*page;		/* buffer page			 */ | ||||
| 	unsigned long	page_link; | ||||
| 	unsigned int	offset;		/* buffer offset		 */ | ||||
| 	dma_addr_t	dma_address;	/* dma address			 */ | ||||
| 	unsigned int	length;		/* length			 */ | ||||
|  |  | |||
|  | @ -4,7 +4,7 @@ | |||
| #include <asm/types.h> | ||||
| 
 | ||||
| struct scatterlist { | ||||
|     struct page		*page; | ||||
|     unsigned long	page_link; | ||||
|     unsigned int	offset; | ||||
|     dma_addr_t		dma_address; | ||||
|     unsigned int	length; | ||||
|  |  | |||
|  | @ -4,7 +4,7 @@ | |||
| #include <linux/mm.h> | ||||
| 
 | ||||
| struct scatterlist { | ||||
| 	struct page *page; | ||||
| 	unsigned long page_link; | ||||
| 	unsigned int offset; | ||||
| 	dma_addr_t dma_address; | ||||
| 	unsigned int length; | ||||
|  |  | |||
|  | @ -6,7 +6,7 @@ struct scatterlist { | |||
| 	unsigned int length; | ||||
| 
 | ||||
| 	/* The following is i386 highmem junk - not used by us */ | ||||
| 	struct page * page; /* Location for highmem page, if any */ | ||||
| 	unsigned long page_link; | ||||
| 	unsigned int offset;/* for highmem, page offset */ | ||||
| 
 | ||||
| }; | ||||
|  |  | |||
|  | @ -22,7 +22,7 @@ | |||
|  * and that's it. There's no excuse for not highmem enabling YOUR driver. /jens | ||||
|  */ | ||||
| struct scatterlist { | ||||
| 	struct page	*page;		/* Location for highmem page, if any */ | ||||
| 	unsigned long	page_link; | ||||
| 	unsigned int	offset;		/* for highmem, page offset */ | ||||
| 
 | ||||
| 	dma_addr_t	dma_address; | ||||
|  |  | |||
|  | @ -4,7 +4,7 @@ | |||
| #include <asm/types.h> | ||||
| 
 | ||||
| struct scatterlist { | ||||
| 	struct page	*page; | ||||
| 	unsigned long	page_link; | ||||
| 	unsigned int	offset; | ||||
| 	dma_addr_t	dma_address; | ||||
| 	unsigned int	length; | ||||
|  |  | |||
|  | @ -9,7 +9,7 @@ | |||
| #include <asm/types.h> | ||||
| 
 | ||||
| struct scatterlist { | ||||
| 	struct page *page; | ||||
| 	unsigned long page_link; | ||||
| 	unsigned int offset; | ||||
| 	unsigned int length;	/* buffer length */ | ||||
| 
 | ||||
|  |  | |||
|  | @ -6,7 +6,7 @@ | |||
| struct scatterlist { | ||||
|     char *  address;    /* Location data is to be transferred to, NULL for
 | ||||
|                          * highmem page */ | ||||
|     struct page * page; /* Location for highmem page, if any */ | ||||
|     unsigned long page_link; | ||||
|     unsigned int offset;/* for highmem, page offset */ | ||||
| 
 | ||||
|     dma_addr_t dma_address; | ||||
|  |  | |||
|  | @ -4,7 +4,7 @@ | |||
| #include <linux/types.h> | ||||
| 
 | ||||
| struct scatterlist { | ||||
| 	struct page *page; | ||||
| 	unsigned long page_link; | ||||
| 	unsigned int offset; | ||||
| 	unsigned int length; | ||||
| 
 | ||||
|  |  | |||
|  | @ -5,7 +5,7 @@ | |||
| #include <asm/types.h> | ||||
| 
 | ||||
| struct scatterlist { | ||||
| 	struct page	*page; | ||||
| 	unsigned long	page_link; | ||||
| 	unsigned int	offset; | ||||
| 	dma_addr_t	dma_address; | ||||
| 	unsigned int	length; | ||||
|  |  | |||
|  | @ -4,7 +4,7 @@ | |||
| #include <asm/types.h> | ||||
| 
 | ||||
| struct scatterlist { | ||||
| 	struct page *	page; | ||||
| 	unsigned long	page_link; | ||||
| 	unsigned int	offset; | ||||
| 	dma_addr_t	dma_address; | ||||
| 	unsigned int	length; | ||||
|  |  | |||
|  | @ -5,7 +5,7 @@ | |||
| #include <asm/types.h> | ||||
| 
 | ||||
| struct scatterlist { | ||||
| 	struct page *page; | ||||
| 	unsigned long page_link; | ||||
| 	unsigned int offset; | ||||
| 
 | ||||
| 	unsigned int length; | ||||
|  |  | |||
|  | @ -14,7 +14,7 @@ | |||
| #include <asm/dma.h> | ||||
| 
 | ||||
| struct scatterlist { | ||||
| 	struct page *page; | ||||
| 	unsigned long page_link; | ||||
| 	unsigned int offset; | ||||
| 	unsigned int length; | ||||
| 
 | ||||
|  |  | |||
|  | @ -2,7 +2,7 @@ | |||
| #define _ASMS390_SCATTERLIST_H | ||||
| 
 | ||||
| struct scatterlist { | ||||
|     struct page *page; | ||||
|     unsigned long page_link; | ||||
|     unsigned int offset; | ||||
|     unsigned int length; | ||||
| }; | ||||
|  |  | |||
|  | @ -4,7 +4,7 @@ | |||
| #include <asm/types.h> | ||||
| 
 | ||||
| struct scatterlist { | ||||
|     struct page * page; /* Location for highmem page, if any */ | ||||
|     unsigned long page_link; | ||||
|     unsigned int offset;/* for highmem, page offset */ | ||||
|     dma_addr_t dma_address; | ||||
|     unsigned int length; | ||||
|  |  | |||
|  | @ -14,7 +14,7 @@ | |||
| #include <asm/types.h> | ||||
| 
 | ||||
| struct scatterlist { | ||||
|     struct page * page; /* Location for highmem page, if any */ | ||||
|     unsigned long page_link; | ||||
|     unsigned int offset;/* for highmem, page offset */ | ||||
|     dma_addr_t dma_address; | ||||
|     unsigned int length; | ||||
|  |  | |||
|  | @ -5,7 +5,7 @@ | |||
| #include <linux/types.h> | ||||
| 
 | ||||
| struct scatterlist { | ||||
| 	struct page *page; | ||||
| 	unsigned long page_link; | ||||
| 	unsigned int offset; | ||||
| 
 | ||||
| 	unsigned int length; | ||||
|  |  | |||
|  | @ -6,7 +6,7 @@ | |||
| #include <asm/types.h> | ||||
| 
 | ||||
| struct scatterlist { | ||||
| 	struct page	*page; | ||||
| 	unsigned long	page_link; | ||||
| 	unsigned int	offset; | ||||
| 
 | ||||
| 	unsigned int	length; | ||||
|  |  | |||
|  | @ -17,7 +17,7 @@ | |||
| #include <asm/types.h> | ||||
| 
 | ||||
| struct scatterlist { | ||||
| 	struct page	*page; | ||||
| 	unsigned long	page_link; | ||||
| 	unsigned	offset; | ||||
| 	dma_addr_t	dma_address; | ||||
| 	unsigned	length; | ||||
|  |  | |||
|  | @ -45,9 +45,9 @@ dma_map_sg(struct device *dev, struct scatterlist *sglist, int nents, | |||
| 	WARN_ON(nents == 0 || sglist[0].length == 0); | ||||
| 
 | ||||
| 	for_each_sg(sglist, sg, nents, i) { | ||||
| 		BUG_ON(!sg->page); | ||||
| 		BUG_ON(!sg_page(sg)); | ||||
| 
 | ||||
| 		sg->dma_address = page_to_phys(sg->page) + sg->offset; | ||||
| 		sg->dma_address = sg_phys(sg); | ||||
| 	} | ||||
| 
 | ||||
| 	flush_write_buffers(); | ||||
|  |  | |||
|  | @ -4,7 +4,7 @@ | |||
| #include <asm/types.h> | ||||
| 
 | ||||
| struct scatterlist { | ||||
|     struct page		*page; | ||||
|     unsigned long	page_link; | ||||
|     unsigned int	offset; | ||||
|     dma_addr_t		dma_address; | ||||
|     unsigned int	length; | ||||
|  |  | |||
|  | @ -4,7 +4,7 @@ | |||
| #include <asm/types.h> | ||||
| 
 | ||||
| struct scatterlist { | ||||
|     struct page		*page; | ||||
|     unsigned long	page_link; | ||||
|     unsigned int	offset; | ||||
|     unsigned int	length; | ||||
|     dma_addr_t		dma_address; | ||||
|  |  | |||
|  | @ -14,7 +14,7 @@ | |||
| #include <asm/types.h> | ||||
| 
 | ||||
| struct scatterlist { | ||||
| 	struct page 	*page; | ||||
| 	unsigned long	page_link; | ||||
| 	unsigned int	offset; | ||||
| 	dma_addr_t	dma_address; | ||||
| 	unsigned int	length; | ||||
|  |  | |||
|  | @ -2,9 +2,26 @@ | |||
| #define _LINUX_SCATTERLIST_H | ||||
| 
 | ||||
| #include <asm/scatterlist.h> | ||||
| #include <asm/io.h> | ||||
| #include <linux/mm.h> | ||||
| #include <linux/string.h> | ||||
| #include <asm/io.h> | ||||
| 
 | ||||
| /*
 | ||||
|  * Notes on SG table design. | ||||
|  * | ||||
|  * Architectures must provide an unsigned long page_link field in the | ||||
|  * scatterlist struct. We use that to place the page pointer AND encode | ||||
|  * information about the sg table as well. The two lower bits are reserved | ||||
|  * for this information. | ||||
|  * | ||||
|  * 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. | ||||
|  * | ||||
|  * See sg_next(). | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| /**
 | ||||
|  * sg_set_page - Set sg entry to point at given page | ||||
|  | @ -20,11 +37,20 @@ | |||
|  **/ | ||||
| static inline void sg_set_page(struct scatterlist *sg, struct page *page) | ||||
| { | ||||
| 	sg->page = page; | ||||
| 	unsigned long page_link = sg->page_link & 0x3; | ||||
| 
 | ||||
| 	sg->page_link = page_link | (unsigned long) page; | ||||
| } | ||||
| 
 | ||||
| #define sg_page(sg)	((sg)->page) | ||||
| #define sg_page(sg)	((struct page *) ((sg)->page_link & ~0x3)) | ||||
| 
 | ||||
| /**
 | ||||
|  * 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) | ||||
| { | ||||
|  | @ -38,26 +64,27 @@ static inline void sg_set_buf(struct scatterlist *sg, const void *buf, | |||
|  * 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)		((unsigned long) (sg)->page & 0x01) | ||||
| #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 *) ((unsigned long) (sg)->page & ~0x01)) | ||||
| 	((struct scatterlist *) ((sg)->page_link & ~0x03)) | ||||
| 
 | ||||
| /**
 | ||||
|  * 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. | ||||
|  * | ||||
|  * Note that the caller must ensure that there are further entries after | ||||
|  * the current entry, this function will NOT return NULL for an end-of-list. | ||||
|  * | ||||
|  */ | ||||
|  **/ | ||||
| static inline struct scatterlist *sg_next(struct scatterlist *sg) | ||||
| { | ||||
| 	sg++; | ||||
| 	if (sg_is_last(sg)) | ||||
| 		return NULL; | ||||
| 
 | ||||
| 	sg++; | ||||
| 	if (unlikely(sg_is_chain(sg))) | ||||
| 		sg = sg_chain_ptr(sg); | ||||
| 
 | ||||
|  | @ -75,6 +102,7 @@ static inline struct scatterlist *sg_next(struct scatterlist *sg) | |||
|  * @sgl:	First entry in the scatterlist | ||||
|  * @nents:	Number of entries in the scatterlist | ||||
|  * | ||||
|  * Description: | ||||
|  *   Should only be used casually, it (currently) scan the entire list | ||||
|  *   to get the last entry. | ||||
|  * | ||||
|  | @ -82,7 +110,7 @@ static inline struct scatterlist *sg_next(struct scatterlist *sg) | |||
|  *   the important bit is that @nents@ denotes the number of entries that | ||||
|  *   exist from @sgl@. | ||||
|  * | ||||
|  */ | ||||
|  **/ | ||||
| static inline struct scatterlist *sg_last(struct scatterlist *sgl, | ||||
| 					  unsigned int nents) | ||||
| { | ||||
|  | @ -105,16 +133,17 @@ static inline struct scatterlist *sg_last(struct scatterlist *sgl, | |||
|  * @prv_nents:	Number of entries in prv | ||||
|  * @sgl:	Second scatterlist | ||||
|  * | ||||
|  * Description: | ||||
|  *   Links @prv@ and @sgl@ together, to form a longer scatterlist. | ||||
|  * | ||||
|  */ | ||||
|  **/ | ||||
| static inline void sg_chain(struct scatterlist *prv, unsigned int prv_nents, | ||||
| 			    struct scatterlist *sgl) | ||||
| { | ||||
| #ifndef ARCH_HAS_SG_CHAIN | ||||
| 	BUG(); | ||||
| #endif | ||||
| 	prv[prv_nents - 1].page = (struct page *) ((unsigned long) sgl | 0x01); | ||||
| 	prv[prv_nents - 1].page_link = (unsigned long) sgl | 0x01; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  | @ -128,13 +157,14 @@ static inline void sg_chain(struct scatterlist *prv, unsigned int prv_nents, | |||
|  **/ | ||||
| static inline void sg_mark_end(struct scatterlist *sgl, unsigned int nents) | ||||
| { | ||||
| 	sgl[nents - 1].page_link = 0x02; | ||||
| } | ||||
| 
 | ||||
| static inline void __sg_mark_end(struct scatterlist *sg) | ||||
| { | ||||
| 	sg->page_link |= 0x02; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /**
 | ||||
|  * sg_init_one - Initialize a single entry sg list | ||||
|  * @sg:		 SG entry | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Jens Axboe
						Jens Axboe