summaryrefslogtreecommitdiff
path: root/lib/scatterlist.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2008-04-18 11:25:31 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2008-04-18 11:25:31 -0700
commit2cca775baecbfede2fec20c99add709232311fe7 (patch)
treeb0eefe80881d263ba7976174144ae4e9cf238425 /lib/scatterlist.c
parenteddeb0e2d863e3941d8768e70cb50c6120e61fa0 (diff)
parent94795b61e84994a3b058f92d041d1fb3d869c7d5 (diff)
Merge git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi-misc-2.6
* git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi-misc-2.6: (137 commits) [SCSI] iscsi: bidi support for iscsi_tcp [SCSI] iscsi: bidi support at the generic libiscsi level [SCSI] iscsi: extended cdb support [SCSI] zfcp: Fix error handling for blocked unit for send FCP command [SCSI] zfcp: Remove zfcp_erp_wait from slave destory handler to fix deadlock [SCSI] zfcp: fix 31 bit compile warnings [SCSI] bsg: no need to set BSG_F_BLOCK bit in bsg_complete_all_commands [SCSI] bsg: remove minor in struct bsg_device [SCSI] bsg: use better helper list functions [SCSI] bsg: replace kobject_get with blk_get_queue [SCSI] bsg: takes a ref to struct device in fops->open [SCSI] qla1280: remove version check [SCSI] libsas: fix endianness bug in sas_ata [SCSI] zfcp: fix compiler warning caused by poking inside new semaphore (linux-next) [SCSI] aacraid: Do not describe check_reset parameter with its value [SCSI] aacraid: Fix down_interruptible() to check the return value [SCSI] sun3_scsi_vme: add MODULE_LICENSE [SCSI] st: rename flush_write_buffer() [SCSI] tgt: use KMEM_CACHE macro [SCSI] initio: fix big endian problems for auto request sense ...
Diffstat (limited to 'lib/scatterlist.c')
-rw-r--r--lib/scatterlist.c102
1 files changed, 102 insertions, 0 deletions
diff --git a/lib/scatterlist.c b/lib/scatterlist.c
index acca4901046c..b80c21100d78 100644
--- a/lib/scatterlist.c
+++ b/lib/scatterlist.c
@@ -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
@@ -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);