c46f2334c8
sg_mark_end() overwrites the page_link information, but all users want __sg_mark_end() behaviour where we just set the end bit. That is the most natural way to use the sg list, since you'll fill it in and then mark the end point. So change sg_mark_end() to only set the termination bit. Add a sg_magic debug check as well, and clear a chain pointer if it is set. Signed-off-by: Jens Axboe <jens.axboe@oracle.com>
282 lines
6.9 KiB
C
282 lines
6.9 KiB
C
#ifndef _LINUX_SCATTERLIST_H
|
|
#define _LINUX_SCATTERLIST_H
|
|
|
|
#include <asm/types.h>
|
|
#include <asm/scatterlist.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().
|
|
*
|
|
*/
|
|
|
|
#define SG_MAGIC 0x87654321
|
|
|
|
/**
|
|
* sg_assign_page - Assign a given page to an SG entry
|
|
* @sg: SG entry
|
|
* @page: The page
|
|
*
|
|
* Description:
|
|
* Assign page to sg entry. Also see sg_set_page(), the most commonly used
|
|
* variant.
|
|
*
|
|
**/
|
|
static inline void sg_assign_page(struct scatterlist *sg, struct page *page)
|
|
{
|
|
unsigned long page_link = sg->page_link & 0x3;
|
|
|
|
/*
|
|
* In order for the low bit stealing approach to work, pages
|
|
* must be aligned at a 32-bit boundary as a minimum.
|
|
*/
|
|
BUG_ON((unsigned long) page & 0x03);
|
|
#ifdef CONFIG_DEBUG_SG
|
|
BUG_ON(sg->sg_magic != SG_MAGIC);
|
|
#endif
|
|
sg->page_link = page_link | (unsigned long) page;
|
|
}
|
|
|
|
/**
|
|
* sg_set_page - Set sg entry to point at given page
|
|
* @sg: SG entry
|
|
* @page: The page
|
|
* @len: Length of data
|
|
* @offset: Offset into page
|
|
*
|
|
* Description:
|
|
* Use this function to set an sg entry pointing at a page, never assign
|
|
* the page directly. We encode sg table information in the lower bits
|
|
* of the page pointer. See sg_page() for looking up the page belonging
|
|
* to an sg entry.
|
|
*
|
|
**/
|
|
static inline void sg_set_page(struct scatterlist *sg, struct page *page,
|
|
unsigned int len, unsigned int offset)
|
|
{
|
|
sg_assign_page(sg, page);
|
|
sg->offset = offset;
|
|
sg->length = len;
|
|
}
|
|
|
|
#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)
|
|
{
|
|
sg_set_page(sg, virt_to_page(buf), buflen, offset_in_page(buf));
|
|
}
|
|
|
|
/*
|
|
* We overload the LSB of the page pointer to indicate whether it's
|
|
* 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) ((sg)->page_link & 0x01)
|
|
#define sg_is_last(sg) ((sg)->page_link & 0x02)
|
|
#define sg_chain_ptr(sg) \
|
|
((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.
|
|
*
|
|
**/
|
|
static inline struct scatterlist *sg_next(struct scatterlist *sg)
|
|
{
|
|
#ifdef CONFIG_DEBUG_SG
|
|
BUG_ON(sg->sg_magic != SG_MAGIC);
|
|
#endif
|
|
if (sg_is_last(sg))
|
|
return NULL;
|
|
|
|
sg++;
|
|
if (unlikely(sg_is_chain(sg)))
|
|
sg = sg_chain_ptr(sg);
|
|
|
|
return sg;
|
|
}
|
|
|
|
/*
|
|
* Loop over each sg element, following the pointer to a new list if necessary
|
|
*/
|
|
#define for_each_sg(sglist, sg, nr, __i) \
|
|
for (__i = 0, sg = (sglist); __i < (nr); __i++, sg = sg_next(sg))
|
|
|
|
/**
|
|
* sg_last - return the last scatterlist entry in a list
|
|
* @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.
|
|
*
|
|
* Note that the @sgl@ pointer passed in need not be the first one,
|
|
* 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)
|
|
{
|
|
#ifndef ARCH_HAS_SG_CHAIN
|
|
struct scatterlist *ret = &sgl[nents - 1];
|
|
#else
|
|
struct scatterlist *sg, *ret = NULL;
|
|
unsigned int i;
|
|
|
|
for_each_sg(sgl, sg, nents, i)
|
|
ret = sg;
|
|
|
|
#endif
|
|
#ifdef CONFIG_DEBUG_SG
|
|
BUG_ON(sgl[0].sg_magic != SG_MAGIC);
|
|
BUG_ON(!sg_is_last(ret));
|
|
#endif
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* sg_chain - Chain two sglists together
|
|
* @prv: First scatterlist
|
|
* @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
|
|
/*
|
|
* Set lowest bit to indicate a link pointer, and make sure to clear
|
|
* the termination bit if it happens to be set.
|
|
*/
|
|
prv[prv_nents - 1].page_link = ((unsigned long) sgl | 0x01) & ~0x02;
|
|
}
|
|
|
|
/**
|
|
* sg_mark_end - Mark the end of the scatterlist
|
|
* @sg: SG entryScatterlist
|
|
*
|
|
* Description:
|
|
* Marks the passed in sg entry as the termination point for the sg
|
|
* table. A call to sg_next() on this entry will return NULL.
|
|
*
|
|
**/
|
|
static inline void sg_mark_end(struct scatterlist *sg)
|
|
{
|
|
#ifdef CONFIG_DEBUG_SG
|
|
BUG_ON(sg->sg_magic != SG_MAGIC);
|
|
#endif
|
|
/*
|
|
* Set termination bit, clear potential chain bit
|
|
*/
|
|
sg->page_link |= 0x02;
|
|
sg->page_link &= ~0x01;
|
|
}
|
|
|
|
/**
|
|
* sg_init_table - Initialize SG table
|
|
* @sgl: The SG table
|
|
* @nents: Number of entries in table
|
|
*
|
|
* Notes:
|
|
* If this is part of a chained sg table, sg_mark_end() should be
|
|
* used only on the last table part.
|
|
*
|
|
**/
|
|
static inline void sg_init_table(struct scatterlist *sgl, unsigned int nents)
|
|
{
|
|
memset(sgl, 0, sizeof(*sgl) * nents);
|
|
#ifdef CONFIG_DEBUG_SG
|
|
{
|
|
unsigned int i;
|
|
for (i = 0; i < nents; i++)
|
|
sgl[i].sg_magic = SG_MAGIC;
|
|
}
|
|
#endif
|
|
sg_mark_end(&sgl[nents - 1]);
|
|
}
|
|
|
|
/**
|
|
* sg_init_one - Initialize a single entry sg list
|
|
* @sg: SG entry
|
|
* @buf: Virtual address for IO
|
|
* @buflen: IO length
|
|
*
|
|
* Notes:
|
|
* This should not be used on a single entry that is part of a larger
|
|
* table. Use sg_init_table() for that.
|
|
*
|
|
**/
|
|
static inline void sg_init_one(struct scatterlist *sg, const void *buf,
|
|
unsigned int buflen)
|
|
{
|
|
sg_init_table(sg, 1);
|
|
sg_set_buf(sg, buf, buflen);
|
|
}
|
|
|
|
/**
|
|
* sg_phys - Return physical address of an sg entry
|
|
* @sg: SG entry
|
|
*
|
|
* Description:
|
|
* This calls page_to_phys() on the page in this sg entry, and adds the
|
|
* sg offset. The caller must know that it is legal to call page_to_phys()
|
|
* on the sg page.
|
|
*
|
|
**/
|
|
static inline dma_addr_t sg_phys(struct scatterlist *sg)
|
|
{
|
|
return page_to_phys(sg_page(sg)) + sg->offset;
|
|
}
|
|
|
|
/**
|
|
* sg_virt - Return virtual address of an sg entry
|
|
* @sg: SG entry
|
|
*
|
|
* Description:
|
|
* This calls page_address() on the page in this sg entry, and adds the
|
|
* sg offset. The caller must know that the sg page has a valid virtual
|
|
* mapping.
|
|
*
|
|
**/
|
|
static inline void *sg_virt(struct scatterlist *sg)
|
|
{
|
|
return page_address(sg_page(sg)) + sg->offset;
|
|
}
|
|
|
|
#endif /* _LINUX_SCATTERLIST_H */
|