blob: 0f7641b875e923246269ac424d141bfa4513eac9 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright 2019 Google LLC
*/
#include <linux/bio.h>
#include <linux/blkdev.h>
#include <linux/slab.h>
#include <linux/keyslot-manager.h>
static int num_prealloc_crypt_ctxs = 128;
static struct kmem_cache *bio_crypt_ctx_cache;
static mempool_t *bio_crypt_ctx_pool;
int bio_crypt_ctx_init(void)
{
bio_crypt_ctx_cache = KMEM_CACHE(bio_crypt_ctx, 0);
if (!bio_crypt_ctx_cache)
return -ENOMEM;
bio_crypt_ctx_pool = mempool_create_slab_pool(
num_prealloc_crypt_ctxs,
bio_crypt_ctx_cache);
if (!bio_crypt_ctx_pool)
return -ENOMEM;
return 0;
}
struct bio_crypt_ctx *bio_crypt_alloc_ctx(gfp_t gfp_mask)
{
return mempool_alloc(bio_crypt_ctx_pool, gfp_mask);
}
EXPORT_SYMBOL(bio_crypt_alloc_ctx);
void bio_crypt_free_ctx(struct bio *bio)
{
mempool_free(bio->bi_crypt_context, bio_crypt_ctx_pool);
bio->bi_crypt_context = NULL;
}
EXPORT_SYMBOL(bio_crypt_free_ctx);
int bio_crypt_clone(struct bio *dst, struct bio *src, gfp_t gfp_mask)
{
/*
* If a bio is swhandled, then it will be decrypted when bio_endio
* is called. As we only want the data to be decrypted once, copies
* of the bio must not have have a crypt context.
*/
if (!bio_has_crypt_ctx(src) || bio_crypt_swhandled(src))
return 0;
dst->bi_crypt_context = bio_crypt_alloc_ctx(gfp_mask);
if (!dst->bi_crypt_context)
return -ENOMEM;
*dst->bi_crypt_context = *src->bi_crypt_context;
if (bio_crypt_has_keyslot(src))
keyslot_manager_get_slot(src->bi_crypt_context->processing_ksm,
src->bi_crypt_context->keyslot);
return 0;
}
EXPORT_SYMBOL(bio_crypt_clone);
bool bio_crypt_should_process(struct bio *bio, struct request_queue *q)
{
if (!bio_has_crypt_ctx(bio))
return false;
if (q->ksm != bio->bi_crypt_context->processing_ksm)
return false;
WARN_ON(!bio_crypt_has_keyslot(bio));
return true;
}
EXPORT_SYMBOL(bio_crypt_should_process);
/*
* Checks that two bio crypt contexts are compatible - i.e. that
* they are mergeable except for data_unit_num continuity.
*/
bool bio_crypt_ctx_compatible(struct bio *b_1, struct bio *b_2)
{
struct bio_crypt_ctx *bc1 = b_1->bi_crypt_context;
struct bio_crypt_ctx *bc2 = b_2->bi_crypt_context;
if (bio_has_crypt_ctx(b_1) != bio_has_crypt_ctx(b_2))
return false;
if (!bio_has_crypt_ctx(b_1))
return true;
return bc1->keyslot == bc2->keyslot &&
bc1->data_unit_size_bits == bc2->data_unit_size_bits;
}
/*
* Checks that two bio crypt contexts are compatible, and also
* that their data_unit_nums are continuous (and can hence be merged)
*/
bool bio_crypt_ctx_back_mergeable(struct bio *b_1,
unsigned int b1_sectors,
struct bio *b_2)
{
struct bio_crypt_ctx *bc1 = b_1->bi_crypt_context;
struct bio_crypt_ctx *bc2 = b_2->bi_crypt_context;
if (!bio_crypt_ctx_compatible(b_1, b_2))
return false;
return !bio_has_crypt_ctx(b_1) ||
(bc1->data_unit_num +
(b1_sectors >> (bc1->data_unit_size_bits - 9)) ==
bc2->data_unit_num);
}
void bio_crypt_ctx_release_keyslot(struct bio *bio)
{
struct bio_crypt_ctx *crypt_ctx = bio->bi_crypt_context;
keyslot_manager_put_slot(crypt_ctx->processing_ksm, crypt_ctx->keyslot);
bio->bi_crypt_context->processing_ksm = NULL;
bio->bi_crypt_context->keyslot = -1;
}
int bio_crypt_ctx_acquire_keyslot(struct bio *bio, struct keyslot_manager *ksm)
{
int slot;
enum blk_crypto_mode_num crypto_mode = bio_crypto_mode(bio);
if (!ksm)
return -ENOMEM;
slot = keyslot_manager_get_slot_for_key(ksm,
bio_crypt_raw_key(bio), crypto_mode,
1 << bio->bi_crypt_context->data_unit_size_bits);
if (slot < 0)
return slot;
bio_crypt_set_keyslot(bio, slot, ksm);
return 0;
}