Commit 3185e663 authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] remove useless highmem bounce from loop/cryptoloop

From: Ben Slusky <sluskyb@paranoiacs.org>

The attached patch changes the loop device transfer functions (including
cryptoloop transfers) to accept page/offset pairs instead of virtual
addresses, and removes the redundant kmaps in do_lo_send, do_lo_receive,
and loop_transfer_bio. Per Andrew Morton's request a while back.
parent a34c0ae9
...@@ -87,43 +87,49 @@ typedef int (*encdec_ecb_t)(struct crypto_tfm *tfm, ...@@ -87,43 +87,49 @@ typedef int (*encdec_ecb_t)(struct crypto_tfm *tfm,
static int static int
cryptoloop_transfer_ecb(struct loop_device *lo, int cmd, char *raw_buf, cryptoloop_transfer_ecb(struct loop_device *lo, int cmd,
char *loop_buf, int size, sector_t IV) struct page *raw_page, unsigned raw_off,
struct page *loop_page, unsigned loop_off,
int size, sector_t IV)
{ {
struct crypto_tfm *tfm = (struct crypto_tfm *) lo->key_data; struct crypto_tfm *tfm = (struct crypto_tfm *) lo->key_data;
struct scatterlist sg_out = { 0, }; struct scatterlist sg_out = { 0, };
struct scatterlist sg_in = { 0, }; struct scatterlist sg_in = { 0, };
encdec_ecb_t encdecfunc; encdec_ecb_t encdecfunc;
char const *in; struct page *in_page, *out_page;
char *out; unsigned in_offs, out_offs;
if (cmd == READ) { if (cmd == READ) {
in = raw_buf; in_page = raw_page;
out = loop_buf; in_offs = raw_off;
out_page = loop_page;
out_offs = loop_off;
encdecfunc = tfm->crt_u.cipher.cit_decrypt; encdecfunc = tfm->crt_u.cipher.cit_decrypt;
} else { } else {
in = loop_buf; in_page = loop_page;
out = raw_buf; in_offs = loop_off;
out_page = raw_page;
out_offs = raw_off;
encdecfunc = tfm->crt_u.cipher.cit_encrypt; encdecfunc = tfm->crt_u.cipher.cit_encrypt;
} }
while (size > 0) { while (size > 0) {
const int sz = min(size, LOOP_IV_SECTOR_SIZE); const int sz = min(size, LOOP_IV_SECTOR_SIZE);
sg_in.page = virt_to_page(in); sg_in.page = in_page;
sg_in.offset = (unsigned long)in & ~PAGE_MASK; sg_in.offset = in_offs;
sg_in.length = sz; sg_in.length = sz;
sg_out.page = virt_to_page(out); sg_out.page = out_page;
sg_out.offset = (unsigned long)out & ~PAGE_MASK; sg_out.offset = out_offs;
sg_out.length = sz; sg_out.length = sz;
encdecfunc(tfm, &sg_out, &sg_in, sz); encdecfunc(tfm, &sg_out, &sg_in, sz);
size -= sz; size -= sz;
in += sz; in_offs += sz;
out += sz; out_offs += sz;
} }
return 0; return 0;
...@@ -135,24 +141,30 @@ typedef int (*encdec_cbc_t)(struct crypto_tfm *tfm, ...@@ -135,24 +141,30 @@ typedef int (*encdec_cbc_t)(struct crypto_tfm *tfm,
unsigned int nsg, u8 *iv); unsigned int nsg, u8 *iv);
static int static int
cryptoloop_transfer_cbc(struct loop_device *lo, int cmd, char *raw_buf, cryptoloop_transfer_cbc(struct loop_device *lo, int cmd,
char *loop_buf, int size, sector_t IV) struct page *raw_page, unsigned raw_off,
struct page *loop_page, unsigned loop_off,
int size, sector_t IV)
{ {
struct crypto_tfm *tfm = (struct crypto_tfm *) lo->key_data; struct crypto_tfm *tfm = (struct crypto_tfm *) lo->key_data;
struct scatterlist sg_out = { 0, }; struct scatterlist sg_out = { 0, };
struct scatterlist sg_in = { 0, }; struct scatterlist sg_in = { 0, };
encdec_cbc_t encdecfunc; encdec_cbc_t encdecfunc;
char const *in; struct page *in_page, *out_page;
char *out; unsigned in_offs, out_offs;
if (cmd == READ) { if (cmd == READ) {
in = raw_buf; in_page = raw_page;
out = loop_buf; in_offs = raw_off;
out_page = loop_page;
out_offs = loop_off;
encdecfunc = tfm->crt_u.cipher.cit_decrypt_iv; encdecfunc = tfm->crt_u.cipher.cit_decrypt_iv;
} else { } else {
in = loop_buf; in_page = loop_page;
out = raw_buf; in_offs = loop_off;
out_page = raw_page;
out_offs = raw_off;
encdecfunc = tfm->crt_u.cipher.cit_encrypt_iv; encdecfunc = tfm->crt_u.cipher.cit_encrypt_iv;
} }
...@@ -161,39 +173,43 @@ cryptoloop_transfer_cbc(struct loop_device *lo, int cmd, char *raw_buf, ...@@ -161,39 +173,43 @@ cryptoloop_transfer_cbc(struct loop_device *lo, int cmd, char *raw_buf,
u32 iv[4] = { 0, }; u32 iv[4] = { 0, };
iv[0] = cpu_to_le32(IV & 0xffffffff); iv[0] = cpu_to_le32(IV & 0xffffffff);
sg_in.page = virt_to_page(in); sg_in.page = in_page;
sg_in.offset = offset_in_page(in); sg_in.offset = in_offs;
sg_in.length = sz; sg_in.length = sz;
sg_out.page = virt_to_page(out); sg_out.page = out_page;
sg_out.offset = offset_in_page(out); sg_out.offset = out_offs;
sg_out.length = sz; sg_out.length = sz;
encdecfunc(tfm, &sg_out, &sg_in, sz, (u8 *)iv); encdecfunc(tfm, &sg_out, &sg_in, sz, (u8 *)iv);
IV++; IV++;
size -= sz; size -= sz;
in += sz; in_offs += sz;
out += sz; out_offs += sz;
} }
return 0; return 0;
} }
static int static int
cryptoloop_transfer(struct loop_device *lo, int cmd, char *raw_buf, cryptoloop_transfer(struct loop_device *lo, int cmd,
char *loop_buf, int size, sector_t IV) struct page *raw_page, unsigned raw_off,
struct page *loop_page, unsigned loop_off,
int size, sector_t IV)
{ {
struct crypto_tfm *tfm = (struct crypto_tfm *) lo->key_data; struct crypto_tfm *tfm = (struct crypto_tfm *) lo->key_data;
if(tfm->crt_cipher.cit_mode == CRYPTO_TFM_MODE_ECB) if(tfm->crt_cipher.cit_mode == CRYPTO_TFM_MODE_ECB)
{ {
lo->transfer = cryptoloop_transfer_ecb; lo->transfer = cryptoloop_transfer_ecb;
return cryptoloop_transfer_ecb(lo, cmd, raw_buf, loop_buf, size, IV); return cryptoloop_transfer_ecb(lo, cmd, raw_page, raw_off,
loop_page, loop_off, size, IV);
} }
if(tfm->crt_cipher.cit_mode == CRYPTO_TFM_MODE_CBC) if(tfm->crt_cipher.cit_mode == CRYPTO_TFM_MODE_CBC)
{ {
lo->transfer = cryptoloop_transfer_cbc; lo->transfer = cryptoloop_transfer_cbc;
return cryptoloop_transfer_cbc(lo, cmd, raw_buf, loop_buf, size, IV); return cryptoloop_transfer_cbc(lo, cmd, raw_page, raw_off,
loop_page, loop_off, size, IV);
} }
/* This is not supposed to happen */ /* This is not supposed to happen */
......
...@@ -76,24 +76,34 @@ static struct gendisk **disks; ...@@ -76,24 +76,34 @@ static struct gendisk **disks;
/* /*
* Transfer functions * Transfer functions
*/ */
static int transfer_none(struct loop_device *lo, int cmd, char *raw_buf, static int transfer_none(struct loop_device *lo, int cmd,
char *loop_buf, int size, sector_t real_block) struct page *raw_page, unsigned raw_off,
struct page *loop_page, unsigned loop_off,
int size, sector_t real_block)
{ {
if (raw_buf != loop_buf) { char *raw_buf = kmap_atomic(raw_page, KM_USER0) + raw_off;
if (cmd == READ) char *loop_buf = kmap_atomic(loop_page, KM_USER1) + loop_off;
memcpy(loop_buf, raw_buf, size);
else if (cmd == READ)
memcpy(raw_buf, loop_buf, size); memcpy(loop_buf, raw_buf, size);
} else
memcpy(raw_buf, loop_buf, size);
kunmap_atomic(raw_buf, KM_USER0);
kunmap_atomic(loop_buf, KM_USER1);
cond_resched();
return 0; return 0;
} }
static int transfer_xor(struct loop_device *lo, int cmd, char *raw_buf, static int transfer_xor(struct loop_device *lo, int cmd,
char *loop_buf, int size, sector_t real_block) struct page *raw_page, unsigned raw_off,
struct page *loop_page, unsigned loop_off,
int size, sector_t real_block)
{ {
char *in, *out, *key; char *raw_buf = kmap_atomic(raw_page, KM_USER0) + raw_off;
int i, keysize; char *loop_buf = kmap_atomic(loop_page, KM_USER1) + loop_off;
char *in, *out, *key;
int i, keysize;
if (cmd == READ) { if (cmd == READ) {
in = raw_buf; in = raw_buf;
...@@ -107,6 +117,10 @@ static int transfer_xor(struct loop_device *lo, int cmd, char *raw_buf, ...@@ -107,6 +117,10 @@ static int transfer_xor(struct loop_device *lo, int cmd, char *raw_buf,
keysize = lo->lo_encrypt_key_size; keysize = lo->lo_encrypt_key_size;
for (i = 0; i < size; i++) for (i = 0; i < size; i++)
*out++ = *in++ ^ key[(i & 511) % keysize]; *out++ = *in++ ^ key[(i & 511) % keysize];
kunmap_atomic(raw_buf, KM_USER0);
kunmap_atomic(loop_buf, KM_USER1);
cond_resched();
return 0; return 0;
} }
...@@ -162,13 +176,15 @@ figure_loop_size(struct loop_device *lo) ...@@ -162,13 +176,15 @@ figure_loop_size(struct loop_device *lo)
} }
static inline int static inline int
lo_do_transfer(struct loop_device *lo, int cmd, char *rbuf, lo_do_transfer(struct loop_device *lo, int cmd,
char *lbuf, int size, sector_t rblock) struct page *rpage, unsigned roffs,
struct page *lpage, unsigned loffs,
int size, sector_t rblock)
{ {
if (!lo->transfer) if (!lo->transfer)
return 0; return 0;
return lo->transfer(lo, cmd, rbuf, lbuf, size, rblock); return lo->transfer(lo, cmd, rpage, roffs, lpage, loffs, size, rblock);
} }
static int static int
...@@ -178,16 +194,15 @@ do_lo_send(struct loop_device *lo, struct bio_vec *bvec, int bsize, loff_t pos) ...@@ -178,16 +194,15 @@ do_lo_send(struct loop_device *lo, struct bio_vec *bvec, int bsize, loff_t pos)
struct address_space *mapping = file->f_mapping; struct address_space *mapping = file->f_mapping;
struct address_space_operations *aops = mapping->a_ops; struct address_space_operations *aops = mapping->a_ops;
struct page *page; struct page *page;
char *kaddr, *data;
pgoff_t index; pgoff_t index;
unsigned size, offset; unsigned size, offset, bv_offs;
int len; int len;
int ret = 0; int ret = 0;
down(&mapping->host->i_sem); down(&mapping->host->i_sem);
index = pos >> PAGE_CACHE_SHIFT; index = pos >> PAGE_CACHE_SHIFT;
offset = pos & ((pgoff_t)PAGE_CACHE_SIZE - 1); offset = pos & ((pgoff_t)PAGE_CACHE_SIZE - 1);
data = kmap(bvec->bv_page) + bvec->bv_offset; bv_offs = bvec->bv_offset;
len = bvec->bv_len; len = bvec->bv_len;
while (len > 0) { while (len > 0) {
sector_t IV; sector_t IV;
...@@ -204,25 +219,28 @@ do_lo_send(struct loop_device *lo, struct bio_vec *bvec, int bsize, loff_t pos) ...@@ -204,25 +219,28 @@ do_lo_send(struct loop_device *lo, struct bio_vec *bvec, int bsize, loff_t pos)
goto fail; goto fail;
if (aops->prepare_write(file, page, offset, offset+size)) if (aops->prepare_write(file, page, offset, offset+size))
goto unlock; goto unlock;
kaddr = kmap(page); transfer_result = lo_do_transfer(lo, WRITE, page, offset,
transfer_result = lo_do_transfer(lo, WRITE, kaddr + offset, bvec->bv_page, bv_offs,
data, size, IV); size, IV);
if (transfer_result) { if (transfer_result) {
char *kaddr;
/* /*
* The transfer failed, but we still write the data to * The transfer failed, but we still write the data to
* keep prepare/commit calls balanced. * keep prepare/commit calls balanced.
*/ */
printk(KERN_ERR "loop: transfer error block %llu\n", printk(KERN_ERR "loop: transfer error block %llu\n",
(unsigned long long)index); (unsigned long long)index);
kaddr = kmap_atomic(page, KM_USER0);
memset(kaddr + offset, 0, size); memset(kaddr + offset, 0, size);
kunmap_atomic(kaddr, KM_USER0);
} }
flush_dcache_page(page); flush_dcache_page(page);
kunmap(page);
if (aops->commit_write(file, page, offset, offset+size)) if (aops->commit_write(file, page, offset, offset+size))
goto unlock; goto unlock;
if (transfer_result) if (transfer_result)
goto unlock; goto unlock;
data += size; bv_offs += size;
len -= size; len -= size;
offset = 0; offset = 0;
index++; index++;
...@@ -232,7 +250,6 @@ do_lo_send(struct loop_device *lo, struct bio_vec *bvec, int bsize, loff_t pos) ...@@ -232,7 +250,6 @@ do_lo_send(struct loop_device *lo, struct bio_vec *bvec, int bsize, loff_t pos)
} }
up(&mapping->host->i_sem); up(&mapping->host->i_sem);
out: out:
kunmap(bvec->bv_page);
return ret; return ret;
unlock: unlock:
...@@ -263,7 +280,8 @@ lo_send(struct loop_device *lo, struct bio *bio, int bsize, loff_t pos) ...@@ -263,7 +280,8 @@ lo_send(struct loop_device *lo, struct bio *bio, int bsize, loff_t pos)
struct lo_read_data { struct lo_read_data {
struct loop_device *lo; struct loop_device *lo;
char *data; struct page *page;
unsigned offset;
int bsize; int bsize;
}; };
...@@ -271,7 +289,6 @@ static int ...@@ -271,7 +289,6 @@ static int
lo_read_actor(read_descriptor_t *desc, struct page *page, lo_read_actor(read_descriptor_t *desc, struct page *page,
unsigned long offset, unsigned long size) unsigned long offset, unsigned long size)
{ {
char *kaddr;
unsigned long count = desc->count; unsigned long count = desc->count;
struct lo_read_data *p = (struct lo_read_data*)desc->buf; struct lo_read_data *p = (struct lo_read_data*)desc->buf;
struct loop_device *lo = p->lo; struct loop_device *lo = p->lo;
...@@ -282,18 +299,16 @@ lo_read_actor(read_descriptor_t *desc, struct page *page, ...@@ -282,18 +299,16 @@ lo_read_actor(read_descriptor_t *desc, struct page *page,
if (size > count) if (size > count)
size = count; size = count;
kaddr = kmap(page); if (lo_do_transfer(lo, READ, page, offset, p->page, p->offset, size, IV)) {
if (lo_do_transfer(lo, READ, kaddr + offset, p->data, size, IV)) {
size = 0; size = 0;
printk(KERN_ERR "loop: transfer error block %ld\n", printk(KERN_ERR "loop: transfer error block %ld\n",
page->index); page->index);
desc->error = -EINVAL; desc->error = -EINVAL;
} }
kunmap(page);
desc->count = count - size; desc->count = count - size;
desc->written += size; desc->written += size;
p->data += size; p->offset += size;
return size; return size;
} }
...@@ -306,12 +321,12 @@ do_lo_receive(struct loop_device *lo, ...@@ -306,12 +321,12 @@ do_lo_receive(struct loop_device *lo,
int retval; int retval;
cookie.lo = lo; cookie.lo = lo;
cookie.data = kmap(bvec->bv_page) + bvec->bv_offset; cookie.page = bvec->bv_page;
cookie.offset = bvec->bv_offset;
cookie.bsize = bsize; cookie.bsize = bsize;
file = lo->lo_backing_file; file = lo->lo_backing_file;
retval = file->f_op->sendfile(file, &pos, bvec->bv_len, retval = file->f_op->sendfile(file, &pos, bvec->bv_len,
lo_read_actor, &cookie); lo_read_actor, &cookie);
kunmap(bvec->bv_page);
return (retval < 0)? retval: 0; return (retval < 0)? retval: 0;
} }
......
...@@ -34,8 +34,9 @@ struct loop_device { ...@@ -34,8 +34,9 @@ struct loop_device {
loff_t lo_sizelimit; loff_t lo_sizelimit;
int lo_flags; int lo_flags;
int (*transfer)(struct loop_device *, int cmd, int (*transfer)(struct loop_device *, int cmd,
char *raw_buf, char *loop_buf, int size, struct page *raw_page, unsigned raw_off,
sector_t real_block); struct page *loop_page, unsigned loop_off,
int size, sector_t real_block);
char lo_file_name[LO_NAME_SIZE]; char lo_file_name[LO_NAME_SIZE];
char lo_crypt_name[LO_NAME_SIZE]; char lo_crypt_name[LO_NAME_SIZE];
char lo_encrypt_key[LO_KEY_SIZE]; char lo_encrypt_key[LO_KEY_SIZE];
...@@ -127,8 +128,10 @@ struct loop_info64 { ...@@ -127,8 +128,10 @@ struct loop_info64 {
/* Support for loadable transfer modules */ /* Support for loadable transfer modules */
struct loop_func_table { struct loop_func_table {
int number; /* filter type */ int number; /* filter type */
int (*transfer)(struct loop_device *lo, int cmd, char *raw_buf, int (*transfer)(struct loop_device *lo, int cmd,
char *loop_buf, int size, sector_t real_block); struct page *raw_page, unsigned raw_off,
struct page *loop_page, unsigned loop_off,
int size, sector_t real_block);
int (*init)(struct loop_device *, const struct loop_info64 *); int (*init)(struct loop_device *, const struct loop_info64 *);
/* release is called from loop_unregister_transfer or clr_fd */ /* release is called from loop_unregister_transfer or clr_fd */
int (*release)(struct loop_device *); int (*release)(struct loop_device *);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment