[PATCH v2 2/3] ima: introduce multi-page collect buffers

From: Dmitry Kasatkin
Date: Tue Jul 01 2014 - 16:14:55 EST


Use of multiple-page collect buffers reduces:
1) the number of block IO requests
2) the number of asynchronous hash update requests

Second is important for HW accelerated hashing, because significant
amount of time is spent for preparation of hash update operation,
which includes configuring acceleration HW, DMA engine, etc...
Thus, HW accelerators are more efficient when working on large
chunks of data.

This patch introduces usage of multi-page collect buffers. Buffer size
can be specified by providing additional option to the 'ima_ahash='
kernel parameter. 'ima_ahash=2048,16384' specifies that minimal file
size to use ahash is 2048 byes and buffer size is 16384 bytes.
Default buffer size is 4096 bytes.

Signed-off-by: Dmitry Kasatkin <d.kasatkin@xxxxxxxxxxx>
---
Documentation/kernel-parameters.txt | 3 +-
security/integrity/ima/ima_crypto.c | 85 ++++++++++++++++++++++++++++++++++---
2 files changed, 81 insertions(+), 7 deletions(-)

diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
index b406f5c..529ba58 100644
--- a/Documentation/kernel-parameters.txt
+++ b/Documentation/kernel-parameters.txt
@@ -1292,9 +1292,10 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
Set number of hash buckets for inode cache.

ima_ahash= [IMA] Asynchronous hash usage parameters
- Format: <min_file_size>
+ Format: <min_file_size>[,<bufsize>]
Set the minimal file size when use asynchronous hash.
If ima_ahash is not provided, ahash usage is disabled.
+ bufsize - hashing buffer size. 4k if not specified.

ima_appraise= [IMA] appraise integrity measurements
Format: { "off" | "enforce" | "fix" }
diff --git a/security/integrity/ima/ima_crypto.c b/security/integrity/ima/ima_crypto.c
index 5eb19b4..41f2695 100644
--- a/security/integrity/ima/ima_crypto.c
+++ b/security/integrity/ima/ima_crypto.c
@@ -25,7 +25,6 @@
#include <crypto/hash_info.h>
#include "ima.h"

-
struct ahash_completion {
struct completion completion;
int err;
@@ -36,14 +35,24 @@ static struct crypto_ahash *ima_ahash_tfm;

/* minimal file size for ahash use */
static loff_t ima_ahash_size;
+/* default is 0 - 1 page. */
+static int ima_max_order;

static int __init ima_ahash_setup(char *str)
{
- int rc;
+ int ints[3] = { 0, };
+
+ str = get_options(str, ARRAY_SIZE(ints), ints);
+ if (!ints[0])
+ return 0;
+
+ ima_ahash_size = ints[1];
+ ima_max_order = get_order(ints[2]);

- rc = kstrtoll(str, 10, &ima_ahash_size);
+ pr_info("ima_ahash: minsize=%lld, bufsize=%lu\n",
+ ima_ahash_size, (PAGE_SIZE << ima_max_order));

- return !rc;
+ return 1;
}
__setup("ima_ahash=", ima_ahash_setup);

@@ -166,6 +175,65 @@ static int ahash_wait(int err, struct ahash_completion *res)
return err;
}

+/**
+ * ima_alloc_pages() - Allocated contiguous pages.
+ * @max_size: Maximum amount of memory to allocate.
+ * @allocated_size: Returned size of actual allocation.
+ * @last_warn: Should the min_size allocation warn or not.
+ *
+ * Tries to do opportunistic allocation for memory first trying to allocate
+ * max_size amount of memory and then splitting that until zero order is
+ * reached. Allocation is tried without generating allocation warnings unless
+ * last_warn is set. Last_warn set affects only last allocation of zero order.
+ *
+ * Return pointer to allocated memory, or NULL on failure.
+ */
+static void *ima_alloc_pages(loff_t max_size, size_t *allocated_size,
+ int last_warn)
+{
+ void *ptr;
+ unsigned int order;
+ gfp_t gfp_mask = __GFP_NOWARN | __GFP_WAIT | __GFP_NORETRY;
+
+ order = min(get_order(max_size), ima_max_order);
+
+ for (; order; order--) {
+ ptr = (void *)__get_free_pages(gfp_mask, order);
+ if (ptr) {
+ *allocated_size = PAGE_SIZE << order;
+ return ptr;
+ }
+ }
+
+ /* order is zero - one page */
+
+ gfp_mask = GFP_KERNEL;
+
+ if (!last_warn)
+ gfp_mask |= __GFP_NOWARN;
+
+ ptr = (void *)__get_free_pages(gfp_mask, 0);
+ if (ptr) {
+ *allocated_size = PAGE_SIZE;
+ return ptr;
+ }
+
+ *allocated_size = 0;
+ return NULL;
+}
+
+/**
+ * ima_free_pages() - Free pages allocated by ima_alloc_pages().
+ * @ptr: Pointer to allocated pages.
+ * @size: Size of allocated buffer.
+ */
+static void ima_free_pages(void *ptr, size_t size)
+{
+ if (!ptr)
+ return;
+ free_pages((unsigned long)ptr, get_order(size));
+}
+
static int ima_calc_file_hash_atfm(struct file *file,
struct ima_digest_data *hash,
struct crypto_ahash *tfm)
@@ -176,6 +244,7 @@ static int ima_calc_file_hash_atfm(struct file *file,
struct ahash_request *req;
struct scatterlist sg[1];
struct ahash_completion res;
+ size_t rbuf_size;

hash->length = crypto_ahash_digestsize(tfm);

@@ -197,7 +266,11 @@ static int ima_calc_file_hash_atfm(struct file *file,
if (i_size == 0)
goto out2;

- rbuf = kzalloc(PAGE_SIZE, GFP_KERNEL);
+ /*
+ * Try to allocate maximum size of memory, fail if not even single
+ * page cannot be allocated.
+ */
+ rbuf = ima_alloc_pages(i_size, &rbuf_size, 1);
if (!rbuf) {
rc = -ENOMEM;
goto out1;
@@ -226,7 +299,7 @@ static int ima_calc_file_hash_atfm(struct file *file,
}
if (read)
file->f_mode &= ~FMODE_READ;
- kfree(rbuf);
+ ima_free_pages(rbuf, rbuf_size);
out2:
if (!rc) {
ahash_request_set_crypt(req, NULL, hash->digest, 0);
--
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/