Skip to content

Commit

Permalink
[SCSI] block: add sg buffer copy helper functions
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
fujita authored and James Bottomley committed Apr 7, 2008
1 parent 78b4b05 commit b1adaf6
Show file tree
Hide file tree
Showing 2 changed files with 107 additions and 0 deletions.
5 changes: 5 additions & 0 deletions include/linux/scatterlist.h
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,11 @@ int __sg_alloc_table(struct sg_table *, unsigned int, unsigned int, gfp_t,
sg_alloc_fn *);
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
* a list larger than this is required then chaining will be utilized.
Expand Down
102 changes: 102 additions & 0 deletions lib/scatterlist.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
*/
#include <linux/module.h>
#include <linux/scatterlist.h>
#include <linux/highmem.h>

/**
* sg_next - return the next scatterlist entry in a list
Expand Down Expand Up @@ -292,3 +293,104 @@ int sg_alloc_table(struct sg_table *table, unsigned int nents, gfp_t gfp_mask)
return ret;
}
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);

0 comments on commit b1adaf6

Please sign in to comment.