diff options
Diffstat (limited to 'crypto/scatterwalk.c')
| -rw-r--r-- | crypto/scatterwalk.c | 157 |
1 files changed, 95 insertions, 62 deletions
diff --git a/crypto/scatterwalk.c b/crypto/scatterwalk.c index 47ac90e615f..79ca2278c2a 100644 --- a/crypto/scatterwalk.c +++ b/crypto/scatterwalk.c @@ -13,103 +13,136 @@ * any later version. * */ + +#include <crypto/scatterwalk.h> #include <linux/kernel.h> #include <linux/mm.h> +#include <linux/module.h> #include <linux/pagemap.h> #include <linux/highmem.h> -#include <asm/bug.h> -#include <asm/scatterlist.h> -#include "internal.h" -#include "scatterwalk.h" - -enum km_type crypto_km_types[] = { - KM_USER0, - KM_USER1, - KM_SOFTIRQ0, - KM_SOFTIRQ1, -}; - -static void memcpy_dir(void *buf, void *sgdata, size_t nbytes, int out) +#include <linux/scatterlist.h> + +static inline void memcpy_dir(void *buf, void *sgdata, size_t nbytes, int out) { - if (out) - memcpy(sgdata, buf, nbytes); - else - memcpy(buf, sgdata, nbytes); + void *src = out ? buf : sgdata; + void *dst = out ? sgdata : buf; + + memcpy(dst, src, nbytes); } void scatterwalk_start(struct scatter_walk *walk, struct scatterlist *sg) { - unsigned int rest_of_page; - walk->sg = sg; - walk->page = sg->page; - walk->len_this_segment = sg->length; - BUG_ON(!sg->length); - rest_of_page = PAGE_CACHE_SIZE - (sg->offset & (PAGE_CACHE_SIZE - 1)); - walk->len_this_page = min(sg->length, rest_of_page); walk->offset = sg->offset; } +EXPORT_SYMBOL_GPL(scatterwalk_start); -void scatterwalk_map(struct scatter_walk *walk, int out) -{ - walk->data = crypto_kmap(walk->page, out) + walk->offset; -} - -static inline void scatterwalk_unmap(struct scatter_walk *walk, int out) +void *scatterwalk_map(struct scatter_walk *walk) { - /* walk->data may be pointing the first byte of the next page; - however, we know we transfered at least one byte. So, - walk->data - 1 will be a virtual address in the mapped page. */ - crypto_kunmap(walk->data - 1, out); + return kmap_atomic(scatterwalk_page(walk)) + + offset_in_page(walk->offset); } +EXPORT_SYMBOL_GPL(scatterwalk_map); static void scatterwalk_pagedone(struct scatter_walk *walk, int out, unsigned int more) { - if (out) - flush_dcache_page(walk->page); + if (out) { + struct page *page; + + page = sg_page(walk->sg) + ((walk->offset - 1) >> PAGE_SHIFT); + if (!PageSlab(page)) + flush_dcache_page(page); + } if (more) { - walk->len_this_segment -= walk->len_this_page; - - if (walk->len_this_segment) { - walk->page++; - walk->len_this_page = min(walk->len_this_segment, - (unsigned)PAGE_CACHE_SIZE); - walk->offset = 0; - } - else - scatterwalk_start(walk, sg_next(walk->sg)); + walk->offset += PAGE_SIZE - 1; + walk->offset &= PAGE_MASK; + if (walk->offset >= walk->sg->offset + walk->sg->length) + scatterwalk_start(walk, scatterwalk_sg_next(walk->sg)); } } void scatterwalk_done(struct scatter_walk *walk, int out, int more) { - scatterwalk_unmap(walk, out); - if (walk->len_this_page == 0 || !more) + if (!(scatterwalk_pagelen(walk) & (PAGE_SIZE - 1)) || !more) scatterwalk_pagedone(walk, out, more); } +EXPORT_SYMBOL_GPL(scatterwalk_done); -/* - * Do not call this unless the total length of all of the fragments - * has been verified as multiple of the block size. - */ -int scatterwalk_copychunks(void *buf, struct scatter_walk *walk, - size_t nbytes, int out) +void scatterwalk_copychunks(void *buf, struct scatter_walk *walk, + size_t nbytes, int out) { - while (nbytes > walk->len_this_page) { - memcpy_dir(buf, walk->data, walk->len_this_page, out); - buf += walk->len_this_page; - nbytes -= walk->len_this_page; + for (;;) { + unsigned int len_this_page = scatterwalk_pagelen(walk); + u8 *vaddr; + + if (len_this_page > nbytes) + len_this_page = nbytes; + + vaddr = scatterwalk_map(walk); + memcpy_dir(buf, vaddr, len_this_page, out); + scatterwalk_unmap(vaddr); + + scatterwalk_advance(walk, len_this_page); + + if (nbytes == len_this_page) + break; + + buf += len_this_page; + nbytes -= len_this_page; - scatterwalk_unmap(walk, out); scatterwalk_pagedone(walk, out, 1); - scatterwalk_map(walk, out); } +} +EXPORT_SYMBOL_GPL(scatterwalk_copychunks); + +void scatterwalk_map_and_copy(void *buf, struct scatterlist *sg, + unsigned int start, unsigned int nbytes, int out) +{ + struct scatter_walk walk; + unsigned int offset = 0; + + if (!nbytes) + return; + + for (;;) { + scatterwalk_start(&walk, sg); + + if (start < offset + sg->length) + break; + + offset += sg->length; + sg = scatterwalk_sg_next(sg); + } + + scatterwalk_advance(&walk, start - offset); + scatterwalk_copychunks(buf, &walk, nbytes, out); + scatterwalk_done(&walk, out, 0); +} +EXPORT_SYMBOL_GPL(scatterwalk_map_and_copy); + +int scatterwalk_bytes_sglen(struct scatterlist *sg, int num_bytes) +{ + int offset = 0, n = 0; + + /* num_bytes is too small */ + if (num_bytes < sg->length) + return -1; + + do { + offset += sg->length; + n++; + sg = scatterwalk_sg_next(sg); + + /* num_bytes is too large */ + if (unlikely(!sg && (num_bytes < offset))) + return -1; + } while (sg && (num_bytes > offset)); - memcpy_dir(buf, walk->data, nbytes, out); - return nbytes; + return n; } +EXPORT_SYMBOL_GPL(scatterwalk_bytes_sglen); |
