diff options
author | Robert Love <robert.w.love@intel.com> | 2009-11-03 20:47:28 +0100 |
---|---|---|
committer | James Bottomley <James.Bottomley@suse.de> | 2009-12-04 19:01:05 +0100 |
commit | 5868287460b0fc243e828a0b856cd53d8bf45739 (patch) | |
tree | 81b18b5e3a104b3202b8e2583d9bd3ffc5546895 /drivers/scsi/libfc/fc_libfc.c | |
parent | [SCSI] libfc: Export FC headers (diff) | |
download | linux-5868287460b0fc243e828a0b856cd53d8bf45739.tar.xz linux-5868287460b0fc243e828a0b856cd53d8bf45739.zip |
[SCSI] libfc: Add routine to copy data from a buffer to a SG list
When handling the multi-frame responses of fc pass-thru requests,
a code segment similar to fc_fcp_recv_data (routine to receive
inbound SCSI data) is used in the response handler. This patch
is to add a routine, called fc_copy_buffer_to_sglist(), to handle
the common function of copying data from a buffer to a scatter-
gather list in order to avoid code duplication.
Signed-off-by: Robert Love <robert.w.love@intel.com>
Signed-off-by: James Bottomley <James.Bottomley@suse.de>
Diffstat (limited to 'drivers/scsi/libfc/fc_libfc.c')
-rw-r--r-- | drivers/scsi/libfc/fc_libfc.c | 60 |
1 files changed, 60 insertions, 0 deletions
diff --git a/drivers/scsi/libfc/fc_libfc.c b/drivers/scsi/libfc/fc_libfc.c index 01418ae8cb84..295eafb0316f 100644 --- a/drivers/scsi/libfc/fc_libfc.c +++ b/drivers/scsi/libfc/fc_libfc.c @@ -72,3 +72,63 @@ static void __exit libfc_exit(void) fc_destroy_rport(); } module_exit(libfc_exit); + +/** + * fc_copy_buffer_to_sglist() - This routine copies the data of a buffer + * into a scatter-gather list (SG list). + * + * @buf: pointer to the data buffer. + * @len: the byte-length of the data buffer. + * @sg: pointer to the pointer of the SG list. + * @nents: pointer to the remaining number of entries in the SG list. + * @offset: pointer to the current offset in the SG list. + * @km_type: dedicated page table slot type for kmap_atomic. + * @crc: pointer to the 32-bit crc value. + * If crc is NULL, CRC is not calculated. + */ +u32 fc_copy_buffer_to_sglist(void *buf, size_t len, + struct scatterlist *sg, + u32 *nents, size_t *offset, + enum km_type km_type, u32 *crc) +{ + size_t remaining = len; + u32 copy_len = 0; + + while (remaining > 0 && sg) { + size_t off, sg_bytes; + void *page_addr; + + if (*offset >= sg->length) { + /* + * Check for end and drop resources + * from the last iteration. + */ + if (!(*nents)) + break; + --(*nents); + *offset -= sg->length; + sg = sg_next(sg); + continue; + } + sg_bytes = min(remaining, sg->length - *offset); + + /* + * The scatterlist item may be bigger than PAGE_SIZE, + * but we are limited to mapping PAGE_SIZE at a time. + */ + off = *offset + sg->offset; + sg_bytes = min(sg_bytes, + (size_t)(PAGE_SIZE - (off & ~PAGE_MASK))); + page_addr = kmap_atomic(sg_page(sg) + (off >> PAGE_SHIFT), + km_type); + if (crc) + *crc = crc32(*crc, buf, sg_bytes); + memcpy((char *)page_addr + (off & ~PAGE_MASK), buf, sg_bytes); + kunmap_atomic(page_addr, km_type); + buf += sg_bytes; + *offset += sg_bytes; + remaining -= sg_bytes; + copy_len += sg_bytes; + } + return copy_len; +} |