[SCSI] block: add sg buffer copy helper functions
This patch adds new three helper functions to copy data between an SG list and a linear buffer. - sg_copy_from_buffer copies data from linear buffer to an SG list - sg_copy_to_buffer copies data from an SG list to a linear buffer When the APIs copy data from a linear buffer to an SG list, flush_kernel_dcache_page is called. It's not necessary for everyone but it's a no-op on most architectures and in general the API is not used in performance critical path. Signed-off-by: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp> Acked-by: Jens Axboe <jens.axboe@oracle.com> Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
This commit is contained in:
parent
78b4b05db5
commit
b1adaf65ba
|
@ -213,6 +213,11 @@ int __sg_alloc_table(struct sg_table *, unsigned int, unsigned int, gfp_t,
|
||||||
sg_alloc_fn *);
|
sg_alloc_fn *);
|
||||||
int sg_alloc_table(struct sg_table *, unsigned int, gfp_t);
|
int sg_alloc_table(struct sg_table *, unsigned int, gfp_t);
|
||||||
|
|
||||||
|
size_t sg_copy_from_buffer(struct scatterlist *sgl, unsigned int nents,
|
||||||
|
void *buf, size_t buflen);
|
||||||
|
size_t sg_copy_to_buffer(struct scatterlist *sgl, unsigned int nents,
|
||||||
|
void *buf, size_t buflen);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Maximum number of entries that will be allocated in one piece, if
|
* Maximum number of entries that will be allocated in one piece, if
|
||||||
* a list larger than this is required then chaining will be utilized.
|
* a list larger than this is required then chaining will be utilized.
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
*/
|
*/
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/scatterlist.h>
|
#include <linux/scatterlist.h>
|
||||||
|
#include <linux/highmem.h>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* sg_next - return the next scatterlist entry in a list
|
* sg_next - return the next scatterlist entry in a list
|
||||||
|
@ -292,3 +293,104 @@ int sg_alloc_table(struct sg_table *table, unsigned int nents, gfp_t gfp_mask)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(sg_alloc_table);
|
EXPORT_SYMBOL(sg_alloc_table);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sg_copy_buffer - Copy data between a linear buffer and an SG list
|
||||||
|
* @sgl: The SG list
|
||||||
|
* @nents: Number of SG entries
|
||||||
|
* @buf: Where to copy from
|
||||||
|
* @buflen: The number of bytes to copy
|
||||||
|
* @to_buffer: transfer direction (non zero == from an sg list to a
|
||||||
|
* buffer, 0 == from a buffer to an sg list
|
||||||
|
*
|
||||||
|
* Returns the number of copied bytes.
|
||||||
|
*
|
||||||
|
**/
|
||||||
|
static size_t sg_copy_buffer(struct scatterlist *sgl, unsigned int nents,
|
||||||
|
void *buf, size_t buflen, int to_buffer)
|
||||||
|
{
|
||||||
|
struct scatterlist *sg;
|
||||||
|
size_t buf_off = 0;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
WARN_ON(!irqs_disabled());
|
||||||
|
|
||||||
|
for_each_sg(sgl, sg, nents, i) {
|
||||||
|
struct page *page;
|
||||||
|
int n = 0;
|
||||||
|
unsigned int sg_off = sg->offset;
|
||||||
|
unsigned int sg_copy = sg->length;
|
||||||
|
|
||||||
|
if (sg_copy > buflen)
|
||||||
|
sg_copy = buflen;
|
||||||
|
buflen -= sg_copy;
|
||||||
|
|
||||||
|
while (sg_copy > 0) {
|
||||||
|
unsigned int page_copy;
|
||||||
|
void *p;
|
||||||
|
|
||||||
|
page_copy = PAGE_SIZE - sg_off;
|
||||||
|
if (page_copy > sg_copy)
|
||||||
|
page_copy = sg_copy;
|
||||||
|
|
||||||
|
page = nth_page(sg_page(sg), n);
|
||||||
|
p = kmap_atomic(page, KM_BIO_SRC_IRQ);
|
||||||
|
|
||||||
|
if (to_buffer)
|
||||||
|
memcpy(buf + buf_off, p + sg_off, page_copy);
|
||||||
|
else {
|
||||||
|
memcpy(p + sg_off, buf + buf_off, page_copy);
|
||||||
|
flush_kernel_dcache_page(page);
|
||||||
|
}
|
||||||
|
|
||||||
|
kunmap_atomic(p, KM_BIO_SRC_IRQ);
|
||||||
|
|
||||||
|
buf_off += page_copy;
|
||||||
|
sg_off += page_copy;
|
||||||
|
if (sg_off == PAGE_SIZE) {
|
||||||
|
sg_off = 0;
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
sg_copy -= page_copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!buflen)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf_off;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sg_copy_from_buffer - Copy from a linear buffer to an SG list
|
||||||
|
* @sgl: The SG list
|
||||||
|
* @nents: Number of SG entries
|
||||||
|
* @buf: Where to copy from
|
||||||
|
* @buflen: The number of bytes to copy
|
||||||
|
*
|
||||||
|
* Returns the number of copied bytes.
|
||||||
|
*
|
||||||
|
**/
|
||||||
|
size_t sg_copy_from_buffer(struct scatterlist *sgl, unsigned int nents,
|
||||||
|
void *buf, size_t buflen)
|
||||||
|
{
|
||||||
|
return sg_copy_buffer(sgl, nents, buf, buflen, 0);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(sg_copy_from_buffer);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sg_copy_to_buffer - Copy from an SG list to a linear buffer
|
||||||
|
* @sgl: The SG list
|
||||||
|
* @nents: Number of SG entries
|
||||||
|
* @buf: Where to copy to
|
||||||
|
* @buflen: The number of bytes to copy
|
||||||
|
*
|
||||||
|
* Returns the number of copied bytes.
|
||||||
|
*
|
||||||
|
**/
|
||||||
|
size_t sg_copy_to_buffer(struct scatterlist *sgl, unsigned int nents,
|
||||||
|
void *buf, size_t buflen)
|
||||||
|
{
|
||||||
|
return sg_copy_buffer(sgl, nents, buf, buflen, 1);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(sg_copy_to_buffer);
|
||||||
|
|
Loading…
Reference in New Issue