kernel-ark/drivers/staging/pohmelfs/crypto.c
Jesper Juhl 391a169e99 pohmelfs: remove unneeded conditionals before calls to crypto_destroy_tfm wrappers.
Hi,

crypto_free_hash() and crypto_free_ablkcipher() are just wrappers around
crypto_free_tfm() which is itself just a wrapper around
crypto_destroy_tfm().
Passing crypto_destroy_tfm() a NULL pointer is valid, so there's no reason
to check for NULL first.

Removing the unneeded conditionals (which is what the patch does) brings
us the benefit of having to execute a few fewer test/branch instructions
and also reduces object code size slightly:

before:
   text    data     bss     dec     hex filename
   8630     112    3312   12054    2f16 drivers/staging/pohmelfs/crypto.o

0000000000000cbe <pohmelfs_crypto_engine_exit>:
     cbe:       55                      push   %rbp
     cbf:       48 89 e5                mov    %rsp,%rbp
     cc2:       53                      push   %rbx
     cc3:       48 83 ec 08             sub    $0x8,%rsp
     cc7:       e8 00 00 00 00          callq  ccc <pohmelfs_crypto_engine_exit+0xe>
     ccc:       48 ff 05 00 00 00 00    incq   0x0(%rip)        # cd3 <pohmelfs_crypto_engine_exit+0x15>
     cd3:       48 89 fb                mov    %rdi,%rbx
     cd6:       48 8b 7f 20             mov    0x20(%rdi),%rdi
     cda:       48 85 ff                test   %rdi,%rdi
     cdd:       74 0c                   je     ceb <pohmelfs_crypto_engine_exit+0x2d>
     cdf:       48 ff 05 00 00 00 00    incq   0x0(%rip)        # ce6 <pohmelfs_crypto_engine_exit+0x28>
     ce6:       e8 58 fa ff ff          callq  743 <crypto_free_hash>
     ceb:       48 8b 7b 28             mov    0x28(%rbx),%rdi
     cef:       48 85 ff                test   %rdi,%rdi
     cf2:       75 09                   jne    cfd <pohmelfs_crypto_engine_exit+0x3f>
     cf4:       48 ff 05 00 00 00 00    incq   0x0(%rip)        # cfb <pohmelfs_crypto_engine_exit+0x3d>
     cfb:       eb 16                   jmp    d13 <pohmelfs_crypto_engine_exit+0x55>
     cfd:       48 89 fe                mov    %rdi,%rsi
     d00:       48 ff 05 00 00 00 00    incq   0x0(%rip)        # d07 <pohmelfs_crypto_engine_exit+0x49>
     d07:       e8 00 00 00 00          callq  d0c <pohmelfs_crypto_engine_exit+0x4e>
     d0c:       48 ff 05 00 00 00 00    incq   0x0(%rip)        # d13 <pohmelfs_crypto_engine_exit+0x55>
     d13:       48 8b 7b 18             mov    0x18(%rbx),%rdi
     d17:       e8 00 00 00 00          callq  d1c <pohmelfs_crypto_engine_exit+0x5e>
     d1c:       48 ff 05 00 00 00 00    incq   0x0(%rip)        # d23 <pohmelfs_crypto_engine_exit+0x65>
     d23:       5e                      pop    %rsi
     d24:       5b                      pop    %rbx
     d25:       c9                      leaveq
     d26:       c3                      retq

after:
   text    data     bss     dec     hex filename
   8604     112    3296   12012    2eec drivers/staging/pohmelfs/crypto.o

0000000000000cbe <pohmelfs_crypto_engine_exit>:
     cbe:       55                      push   %rbp
     cbf:       48 89 e5                mov    %rsp,%rbp
     cc2:       53                      push   %rbx
     cc3:       48 83 ec 08             sub    $0x8,%rsp
     cc7:       e8 00 00 00 00          callq  ccc <pohmelfs_crypto_engine_exit+0xe>
     ccc:       48 ff 05 00 00 00 00    incq   0x0(%rip)        # cd3 <pohmelfs_crypto_engine_exit+0x15>
     cd3:       48 89 fb                mov    %rdi,%rbx
     cd6:       48 8b 7f 20             mov    0x20(%rdi),%rdi
     cda:       e8 64 fa ff ff          callq  743 <crypto_free_hash>
     cdf:       48 8b 7b 28             mov    0x28(%rbx),%rdi
     ce3:       48 ff 05 00 00 00 00    incq   0x0(%rip)        # cea <pohmelfs_crypto_engine_exit+0x2c>
     cea:       48 89 fe                mov    %rdi,%rsi
     ced:       e8 00 00 00 00          callq  cf2 <pohmelfs_crypto_engine_exit+0x34>
     cf2:       48 8b 7b 18             mov    0x18(%rbx),%rdi
     cf6:       48 ff 05 00 00 00 00    incq   0x0(%rip)        # cfd <pohmelfs_crypto_engine_exit+0x3f>
     cfd:       e8 00 00 00 00          callq  d02 <pohmelfs_crypto_engine_exit+0x44>
     d02:       48 ff 05 00 00 00 00    incq   0x0(%rip)        # d09 <pohmelfs_crypto_engine_exit+0x4b>
     d09:       5e                      pop    %rsi
     d0a:       5b                      pop    %rbx
     d0b:       c9                      leaveq
     d0c:       c3                      retq

Signed-off-by: Jesper Juhl <jj@chaosbits.net>
Acked-by: Evgeniy Polyakov <zbr@ioremap.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2010-11-09 16:07:08 -08:00

878 lines
20 KiB
C

/*
* 2007+ Copyright (c) Evgeniy Polyakov <zbr@ioremap.net>
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/crypto.h>
#include <linux/highmem.h>
#include <linux/kthread.h>
#include <linux/pagemap.h>
#include <linux/slab.h>
#include "netfs.h"
static struct crypto_hash *pohmelfs_init_hash(struct pohmelfs_sb *psb)
{
int err;
struct crypto_hash *hash;
hash = crypto_alloc_hash(psb->hash_string, 0, CRYPTO_ALG_ASYNC);
if (IS_ERR(hash)) {
err = PTR_ERR(hash);
dprintk("%s: idx: %u: failed to allocate hash '%s', err: %d.\n",
__func__, psb->idx, psb->hash_string, err);
goto err_out_exit;
}
psb->crypto_attached_size = crypto_hash_digestsize(hash);
if (!psb->hash_keysize)
return hash;
err = crypto_hash_setkey(hash, psb->hash_key, psb->hash_keysize);
if (err) {
dprintk("%s: idx: %u: failed to set key for hash '%s', err: %d.\n",
__func__, psb->idx, psb->hash_string, err);
goto err_out_free;
}
return hash;
err_out_free:
crypto_free_hash(hash);
err_out_exit:
return ERR_PTR(err);
}
static struct crypto_ablkcipher *pohmelfs_init_cipher(struct pohmelfs_sb *psb)
{
int err = -EINVAL;
struct crypto_ablkcipher *cipher;
if (!psb->cipher_keysize)
goto err_out_exit;
cipher = crypto_alloc_ablkcipher(psb->cipher_string, 0, 0);
if (IS_ERR(cipher)) {
err = PTR_ERR(cipher);
dprintk("%s: idx: %u: failed to allocate cipher '%s', err: %d.\n",
__func__, psb->idx, psb->cipher_string, err);
goto err_out_exit;
}
crypto_ablkcipher_clear_flags(cipher, ~0);
err = crypto_ablkcipher_setkey(cipher, psb->cipher_key, psb->cipher_keysize);
if (err) {
dprintk("%s: idx: %u: failed to set key for cipher '%s', err: %d.\n",
__func__, psb->idx, psb->cipher_string, err);
goto err_out_free;
}
return cipher;
err_out_free:
crypto_free_ablkcipher(cipher);
err_out_exit:
return ERR_PTR(err);
}
int pohmelfs_crypto_engine_init(struct pohmelfs_crypto_engine *e, struct pohmelfs_sb *psb)
{
int err;
e->page_num = 0;
e->size = PAGE_SIZE;
e->data = kmalloc(e->size, GFP_KERNEL);
if (!e->data) {
err = -ENOMEM;
goto err_out_exit;
}
if (psb->hash_string) {
e->hash = pohmelfs_init_hash(psb);
if (IS_ERR(e->hash)) {
err = PTR_ERR(e->hash);
e->hash = NULL;
goto err_out_free;
}
}
if (psb->cipher_string) {
e->cipher = pohmelfs_init_cipher(psb);
if (IS_ERR(e->cipher)) {
err = PTR_ERR(e->cipher);
e->cipher = NULL;
goto err_out_free_hash;
}
}
return 0;
err_out_free_hash:
crypto_free_hash(e->hash);
err_out_free:
kfree(e->data);
err_out_exit:
return err;
}
void pohmelfs_crypto_engine_exit(struct pohmelfs_crypto_engine *e)
{
crypto_free_hash(e->hash);
crypto_free_ablkcipher(e->cipher);
kfree(e->data);
}
static void pohmelfs_crypto_complete(struct crypto_async_request *req, int err)
{
struct pohmelfs_crypto_completion *c = req->data;
if (err == -EINPROGRESS)
return;
dprintk("%s: req: %p, err: %d.\n", __func__, req, err);
c->error = err;
complete(&c->complete);
}
static int pohmelfs_crypto_process(struct ablkcipher_request *req,
struct scatterlist *sg_dst, struct scatterlist *sg_src,
void *iv, int enc, unsigned long timeout)
{
struct pohmelfs_crypto_completion complete;
int err;
init_completion(&complete.complete);
complete.error = -EINPROGRESS;
ablkcipher_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
pohmelfs_crypto_complete, &complete);
ablkcipher_request_set_crypt(req, sg_src, sg_dst, sg_src->length, iv);
if (enc)
err = crypto_ablkcipher_encrypt(req);
else
err = crypto_ablkcipher_decrypt(req);
switch (err) {
case -EINPROGRESS:
case -EBUSY:
err = wait_for_completion_interruptible_timeout(&complete.complete,
timeout);
if (!err)
err = -ETIMEDOUT;
else if (err > 0)
err = complete.error;
break;
default:
break;
}
return err;
}
int pohmelfs_crypto_process_input_data(struct pohmelfs_crypto_engine *e, u64 cmd_iv,
void *data, struct page *page, unsigned int size)
{
int err;
struct scatterlist sg;
if (!e->cipher && !e->hash)
return 0;
dprintk("%s: eng: %p, iv: %llx, data: %p, page: %p/%lu, size: %u.\n",
__func__, e, cmd_iv, data, page, (page) ? page->index : 0, size);
if (data) {
sg_init_one(&sg, data, size);
} else {
sg_init_table(&sg, 1);
sg_set_page(&sg, page, size, 0);
}
if (e->cipher) {
struct ablkcipher_request *req = e->data + crypto_hash_digestsize(e->hash);
u8 iv[32];
memset(iv, 0, sizeof(iv));
memcpy(iv, &cmd_iv, sizeof(cmd_iv));
ablkcipher_request_set_tfm(req, e->cipher);
err = pohmelfs_crypto_process(req, &sg, &sg, iv, 0, e->timeout);
if (err)
goto err_out_exit;
}
if (e->hash) {
struct hash_desc desc;
void *dst = e->data + e->size/2;
desc.tfm = e->hash;
desc.flags = 0;
err = crypto_hash_init(&desc);
if (err)
goto err_out_exit;
err = crypto_hash_update(&desc, &sg, size);
if (err)
goto err_out_exit;
err = crypto_hash_final(&desc, dst);
if (err)
goto err_out_exit;
err = !!memcmp(dst, e->data, crypto_hash_digestsize(e->hash));
if (err) {
#ifdef CONFIG_POHMELFS_DEBUG
unsigned int i;
unsigned char *recv = e->data, *calc = dst;
dprintk("%s: eng: %p, hash: %p, cipher: %p: iv : %llx, hash mismatch (recv/calc): ",
__func__, e, e->hash, e->cipher, cmd_iv);
for (i = 0; i < crypto_hash_digestsize(e->hash); ++i) {
#if 0
dprintka("%02x ", recv[i]);
if (recv[i] != calc[i]) {
dprintka("| calc byte: %02x.\n", calc[i]);
break;
}
#else
dprintka("%02x/%02x ", recv[i], calc[i]);
#endif
}
dprintk("\n");
#endif
goto err_out_exit;
} else {
dprintk("%s: eng: %p, hash: %p, cipher: %p: hashes matched.\n",
__func__, e, e->hash, e->cipher);
}
}
dprintk("%s: eng: %p, size: %u, hash: %p, cipher: %p: completed.\n",
__func__, e, e->size, e->hash, e->cipher);
return 0;
err_out_exit:
dprintk("%s: eng: %p, hash: %p, cipher: %p: err: %d.\n",
__func__, e, e->hash, e->cipher, err);
return err;
}
static int pohmelfs_trans_iter(struct netfs_trans *t, struct pohmelfs_crypto_engine *e,
int (*iterator) (struct pohmelfs_crypto_engine *e,
struct scatterlist *dst,
struct scatterlist *src))
{
void *data = t->iovec.iov_base + sizeof(struct netfs_cmd) + t->psb->crypto_attached_size;
unsigned int size = t->iovec.iov_len - sizeof(struct netfs_cmd) - t->psb->crypto_attached_size;
struct netfs_cmd *cmd = data;
unsigned int sz, pages = t->attached_pages, i, csize, cmd_cmd, dpage_idx;
struct scatterlist sg_src, sg_dst;
int err;
while (size) {
cmd = data;
cmd_cmd = __be16_to_cpu(cmd->cmd);
csize = __be32_to_cpu(cmd->size);
cmd->iv = __cpu_to_be64(e->iv);
if (cmd_cmd == NETFS_READ_PAGES || cmd_cmd == NETFS_READ_PAGE)
csize = __be16_to_cpu(cmd->ext);
sz = csize + __be16_to_cpu(cmd->cpad) + sizeof(struct netfs_cmd);
dprintk("%s: size: %u, sz: %u, cmd_size: %u, cmd_cpad: %u.\n",
__func__, size, sz, __be32_to_cpu(cmd->size), __be16_to_cpu(cmd->cpad));
data += sz;
size -= sz;
sg_init_one(&sg_src, cmd->data, sz - sizeof(struct netfs_cmd));
sg_init_one(&sg_dst, cmd->data, sz - sizeof(struct netfs_cmd));
err = iterator(e, &sg_dst, &sg_src);
if (err)
return err;
}
if (!pages)
return 0;
dpage_idx = 0;
for (i = 0; i < t->page_num; ++i) {
struct page *page = t->pages[i];
struct page *dpage = e->pages[dpage_idx];
if (!page)
continue;
sg_init_table(&sg_src, 1);
sg_init_table(&sg_dst, 1);
sg_set_page(&sg_src, page, page_private(page), 0);
sg_set_page(&sg_dst, dpage, page_private(page), 0);
err = iterator(e, &sg_dst, &sg_src);
if (err)
return err;
pages--;
if (!pages)
break;
dpage_idx++;
}
return 0;
}
static int pohmelfs_encrypt_iterator(struct pohmelfs_crypto_engine *e,
struct scatterlist *sg_dst, struct scatterlist *sg_src)
{
struct ablkcipher_request *req = e->data;
u8 iv[32];
memset(iv, 0, sizeof(iv));
memcpy(iv, &e->iv, sizeof(e->iv));
return pohmelfs_crypto_process(req, sg_dst, sg_src, iv, 1, e->timeout);
}
static int pohmelfs_encrypt(struct pohmelfs_crypto_thread *tc)
{
struct netfs_trans *t = tc->trans;
struct pohmelfs_crypto_engine *e = &tc->eng;
struct ablkcipher_request *req = e->data;
memset(req, 0, sizeof(struct ablkcipher_request));
ablkcipher_request_set_tfm(req, e->cipher);
e->iv = pohmelfs_gen_iv(t);
return pohmelfs_trans_iter(t, e, pohmelfs_encrypt_iterator);
}
static int pohmelfs_hash_iterator(struct pohmelfs_crypto_engine *e,
struct scatterlist *sg_dst, struct scatterlist *sg_src)
{
return crypto_hash_update(e->data, sg_src, sg_src->length);
}
static int pohmelfs_hash(struct pohmelfs_crypto_thread *tc)
{
struct pohmelfs_crypto_engine *e = &tc->eng;
struct hash_desc *desc = e->data;
unsigned char *dst = tc->trans->iovec.iov_base + sizeof(struct netfs_cmd);
int err;
desc->tfm = e->hash;
desc->flags = 0;
err = crypto_hash_init(desc);
if (err)
return err;
err = pohmelfs_trans_iter(tc->trans, e, pohmelfs_hash_iterator);
if (err)
return err;
err = crypto_hash_final(desc, dst);
if (err)
return err;
{
unsigned int i;
dprintk("%s: ", __func__);
for (i = 0; i < tc->psb->crypto_attached_size; ++i)
dprintka("%02x ", dst[i]);
dprintka("\n");
}
return 0;
}
static void pohmelfs_crypto_pages_free(struct pohmelfs_crypto_engine *e)
{
unsigned int i;
for (i = 0; i < e->page_num; ++i)
__free_page(e->pages[i]);
kfree(e->pages);
}
static int pohmelfs_crypto_pages_alloc(struct pohmelfs_crypto_engine *e, struct pohmelfs_sb *psb)
{
unsigned int i;
e->pages = kmalloc(psb->trans_max_pages * sizeof(struct page *), GFP_KERNEL);
if (!e->pages)
return -ENOMEM;
for (i = 0; i < psb->trans_max_pages; ++i) {
e->pages[i] = alloc_page(GFP_KERNEL);
if (!e->pages[i])
break;
}
e->page_num = i;
if (!e->page_num)
goto err_out_free;
return 0;
err_out_free:
kfree(e->pages);
return -ENOMEM;
}
static void pohmelfs_sys_crypto_exit_one(struct pohmelfs_crypto_thread *t)
{
struct pohmelfs_sb *psb = t->psb;
if (t->thread)
kthread_stop(t->thread);
mutex_lock(&psb->crypto_thread_lock);
list_del(&t->thread_entry);
psb->crypto_thread_num--;
mutex_unlock(&psb->crypto_thread_lock);
pohmelfs_crypto_engine_exit(&t->eng);
pohmelfs_crypto_pages_free(&t->eng);
kfree(t);
}
static int pohmelfs_crypto_finish(struct netfs_trans *t, struct pohmelfs_sb *psb, int err)
{
struct netfs_cmd *cmd = t->iovec.iov_base;
netfs_convert_cmd(cmd);
if (likely(!err))
err = netfs_trans_finish_send(t, psb);
t->result = err;
netfs_trans_put(t);
return err;
}
void pohmelfs_crypto_thread_make_ready(struct pohmelfs_crypto_thread *th)
{
struct pohmelfs_sb *psb = th->psb;
th->page = NULL;
th->trans = NULL;
mutex_lock(&psb->crypto_thread_lock);
list_move_tail(&th->thread_entry, &psb->crypto_ready_list);
mutex_unlock(&psb->crypto_thread_lock);
wake_up(&psb->wait);
}
static int pohmelfs_crypto_thread_trans(struct pohmelfs_crypto_thread *t)
{
struct netfs_trans *trans;
int err = 0;
trans = t->trans;
trans->eng = NULL;
if (t->eng.hash) {
err = pohmelfs_hash(t);
if (err)
goto out_complete;
}
if (t->eng.cipher) {
err = pohmelfs_encrypt(t);
if (err)
goto out_complete;
trans->eng = &t->eng;
}
out_complete:
t->page = NULL;
t->trans = NULL;
if (!trans->eng)
pohmelfs_crypto_thread_make_ready(t);
pohmelfs_crypto_finish(trans, t->psb, err);
return err;
}
static int pohmelfs_crypto_thread_page(struct pohmelfs_crypto_thread *t)
{
struct pohmelfs_crypto_engine *e = &t->eng;
struct page *page = t->page;
int err;
WARN_ON(!PageChecked(page));
err = pohmelfs_crypto_process_input_data(e, e->iv, NULL, page, t->size);
if (!err)
SetPageUptodate(page);
else
SetPageError(page);
unlock_page(page);
page_cache_release(page);
pohmelfs_crypto_thread_make_ready(t);
return err;
}
static int pohmelfs_crypto_thread_func(void *data)
{
struct pohmelfs_crypto_thread *t = data;
while (!kthread_should_stop()) {
wait_event_interruptible(t->wait, kthread_should_stop() ||
t->trans || t->page);
if (kthread_should_stop())
break;
if (!t->trans && !t->page)
continue;
dprintk("%s: thread: %p, trans: %p, page: %p.\n",
__func__, t, t->trans, t->page);
if (t->trans)
pohmelfs_crypto_thread_trans(t);
else if (t->page)
pohmelfs_crypto_thread_page(t);
}
return 0;
}
static void pohmelfs_crypto_flush(struct pohmelfs_sb *psb, struct list_head *head)
{
while (!list_empty(head)) {
struct pohmelfs_crypto_thread *t = NULL;
mutex_lock(&psb->crypto_thread_lock);
if (!list_empty(head)) {
t = list_first_entry(head, struct pohmelfs_crypto_thread, thread_entry);
list_del_init(&t->thread_entry);
}
mutex_unlock(&psb->crypto_thread_lock);
if (t)
pohmelfs_sys_crypto_exit_one(t);
}
}
static void pohmelfs_sys_crypto_exit(struct pohmelfs_sb *psb)
{
while (!list_empty(&psb->crypto_active_list) || !list_empty(&psb->crypto_ready_list)) {
dprintk("%s: crypto_thread_num: %u.\n", __func__, psb->crypto_thread_num);
pohmelfs_crypto_flush(psb, &psb->crypto_active_list);
pohmelfs_crypto_flush(psb, &psb->crypto_ready_list);
}
}
static int pohmelfs_sys_crypto_init(struct pohmelfs_sb *psb)
{
unsigned int i;
struct pohmelfs_crypto_thread *t;
struct pohmelfs_config *c;
struct netfs_state *st;
int err;
list_for_each_entry(c, &psb->state_list, config_entry) {
st = &c->state;
err = pohmelfs_crypto_engine_init(&st->eng, psb);
if (err)
goto err_out_exit;
dprintk("%s: st: %p, eng: %p, hash: %p, cipher: %p.\n",
__func__, st, &st->eng, &st->eng.hash, &st->eng.cipher);
}
for (i = 0; i < psb->crypto_thread_num; ++i) {
err = -ENOMEM;
t = kzalloc(sizeof(struct pohmelfs_crypto_thread), GFP_KERNEL);
if (!t)
goto err_out_free_state_engines;
init_waitqueue_head(&t->wait);
t->psb = psb;
t->trans = NULL;
t->eng.thread = t;
err = pohmelfs_crypto_engine_init(&t->eng, psb);
if (err)
goto err_out_free_state_engines;
err = pohmelfs_crypto_pages_alloc(&t->eng, psb);
if (err)
goto err_out_free;
t->thread = kthread_run(pohmelfs_crypto_thread_func, t,
"pohmelfs-crypto-%d-%d", psb->idx, i);
if (IS_ERR(t->thread)) {
err = PTR_ERR(t->thread);
t->thread = NULL;
goto err_out_free;
}
if (t->eng.cipher)
psb->crypto_align_size = crypto_ablkcipher_blocksize(t->eng.cipher);
mutex_lock(&psb->crypto_thread_lock);
list_add_tail(&t->thread_entry, &psb->crypto_ready_list);
mutex_unlock(&psb->crypto_thread_lock);
}
psb->crypto_thread_num = i;
return 0;
err_out_free:
pohmelfs_sys_crypto_exit_one(t);
err_out_free_state_engines:
list_for_each_entry(c, &psb->state_list, config_entry) {
st = &c->state;
pohmelfs_crypto_engine_exit(&st->eng);
}
err_out_exit:
pohmelfs_sys_crypto_exit(psb);
return err;
}
void pohmelfs_crypto_exit(struct pohmelfs_sb *psb)
{
pohmelfs_sys_crypto_exit(psb);
kfree(psb->hash_string);
kfree(psb->cipher_string);
}
static int pohmelfs_crypt_init_complete(struct page **pages, unsigned int page_num,
void *private, int err)
{
struct pohmelfs_sb *psb = private;
psb->flags = -err;
dprintk("%s: err: %d.\n", __func__, err);
wake_up(&psb->wait);
return err;
}
static int pohmelfs_crypto_init_handshake(struct pohmelfs_sb *psb)
{
struct netfs_trans *t;
struct netfs_crypto_capabilities *cap;
struct netfs_cmd *cmd;
char *str;
int err = -ENOMEM, size;
size = sizeof(struct netfs_crypto_capabilities) +
psb->cipher_strlen + psb->hash_strlen + 2; /* 0 bytes */
t = netfs_trans_alloc(psb, size, 0, 0);
if (!t)
goto err_out_exit;
t->complete = pohmelfs_crypt_init_complete;
t->private = psb;
cmd = netfs_trans_current(t);
cap = (struct netfs_crypto_capabilities *)(cmd + 1);
str = (char *)(cap + 1);
cmd->cmd = NETFS_CAPABILITIES;
cmd->id = POHMELFS_CRYPTO_CAPABILITIES;
cmd->size = size;
cmd->start = 0;
cmd->ext = 0;
cmd->csize = 0;
netfs_convert_cmd(cmd);
netfs_trans_update(cmd, t, size);
cap->hash_strlen = psb->hash_strlen;
if (cap->hash_strlen) {
sprintf(str, "%s", psb->hash_string);
str += cap->hash_strlen;
}
cap->cipher_strlen = psb->cipher_strlen;
cap->cipher_keysize = psb->cipher_keysize;
if (cap->cipher_strlen)
sprintf(str, "%s", psb->cipher_string);
netfs_convert_crypto_capabilities(cap);
psb->flags = ~0;
err = netfs_trans_finish(t, psb);
if (err)
goto err_out_exit;
err = wait_event_interruptible_timeout(psb->wait, (psb->flags != ~0),
psb->wait_on_page_timeout);
if (!err)
err = -ETIMEDOUT;
else if (err > 0)
err = -psb->flags;
if (!err)
psb->perform_crypto = 1;
psb->flags = 0;
/*
* At this point NETFS_CAPABILITIES response command
* should setup superblock in a way, which is acceptible
* for both client and server, so if server refuses connection,
* it will send error in transaction response.
*/
if (err)
goto err_out_exit;
return 0;
err_out_exit:
return err;
}
int pohmelfs_crypto_init(struct pohmelfs_sb *psb)
{
int err;
if (!psb->cipher_string && !psb->hash_string)
return 0;
err = pohmelfs_crypto_init_handshake(psb);
if (err)
return err;
err = pohmelfs_sys_crypto_init(psb);
if (err)
return err;
return 0;
}
static int pohmelfs_crypto_thread_get(struct pohmelfs_sb *psb,
int (*action)(struct pohmelfs_crypto_thread *t, void *data), void *data)
{
struct pohmelfs_crypto_thread *t = NULL;
int err;
while (!t) {
err = wait_event_interruptible_timeout(psb->wait,
!list_empty(&psb->crypto_ready_list),
psb->wait_on_page_timeout);
t = NULL;
err = 0;
mutex_lock(&psb->crypto_thread_lock);
if (!list_empty(&psb->crypto_ready_list)) {
t = list_entry(psb->crypto_ready_list.prev,
struct pohmelfs_crypto_thread,
thread_entry);
list_move_tail(&t->thread_entry,
&psb->crypto_active_list);
action(t, data);
wake_up(&t->wait);
}
mutex_unlock(&psb->crypto_thread_lock);
}
return err;
}
static int pohmelfs_trans_crypt_action(struct pohmelfs_crypto_thread *t, void *data)
{
struct netfs_trans *trans = data;
netfs_trans_get(trans);
t->trans = trans;
dprintk("%s: t: %p, gen: %u, thread: %p.\n", __func__, trans, trans->gen, t);
return 0;
}
int pohmelfs_trans_crypt(struct netfs_trans *trans, struct pohmelfs_sb *psb)
{
if ((!psb->hash_string && !psb->cipher_string) || !psb->perform_crypto) {
netfs_trans_get(trans);
return pohmelfs_crypto_finish(trans, psb, 0);
}
return pohmelfs_crypto_thread_get(psb, pohmelfs_trans_crypt_action, trans);
}
struct pohmelfs_crypto_input_action_data {
struct page *page;
struct pohmelfs_crypto_engine *e;
u64 iv;
unsigned int size;
};
static int pohmelfs_crypt_input_page_action(struct pohmelfs_crypto_thread *t, void *data)
{
struct pohmelfs_crypto_input_action_data *act = data;
memcpy(t->eng.data, act->e->data, t->psb->crypto_attached_size);
t->size = act->size;
t->eng.iv = act->iv;
t->page = act->page;
return 0;
}
int pohmelfs_crypto_process_input_page(struct pohmelfs_crypto_engine *e,
struct page *page, unsigned int size, u64 iv)
{
struct inode *inode = page->mapping->host;
struct pohmelfs_crypto_input_action_data act;
int err = -ENOENT;
act.page = page;
act.e = e;
act.size = size;
act.iv = iv;
err = pohmelfs_crypto_thread_get(POHMELFS_SB(inode->i_sb),
pohmelfs_crypt_input_page_action, &act);
if (err)
goto err_out_exit;
return 0;
err_out_exit:
SetPageUptodate(page);
page_cache_release(page);
return err;
}