ANDROID: Incremental fs: Cache successful hash calculations
Bug: 155996534 Signed-off-by: Paul Lawrence <paullawrence@google.com> Change-Id: Ic508e6fa07c90decb29e07647dd3b0fc4d243ce8 (cherry picked from commit 21e6d932da379416bab8ea8d78f14525e7bf564d) Git-commit: 3585748a58362588795792778878c2f6e340d670 Git-repo: https://android.googlesource.com/kernel/common/ Signed-off-by: Sayali Lokhande <sayalil@codeaurora.org>
This commit is contained in:
committed by
Blagovest Kolenichev
parent
5409507f89
commit
31ee9d8fb7
@@ -2,15 +2,16 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2019 Google LLC
|
* Copyright 2019 Google LLC
|
||||||
*/
|
*/
|
||||||
#include <linux/gfp.h>
|
|
||||||
#include <linux/types.h>
|
|
||||||
#include <linux/slab.h>
|
|
||||||
#include <linux/file.h>
|
|
||||||
#include <linux/ktime.h>
|
|
||||||
#include <linux/mm.h>
|
|
||||||
#include <linux/workqueue.h>
|
|
||||||
#include <linux/lz4.h>
|
|
||||||
#include <linux/crc32.h>
|
#include <linux/crc32.h>
|
||||||
|
#include <linux/file.h>
|
||||||
|
#include <linux/gfp.h>
|
||||||
|
#include <linux/ktime.h>
|
||||||
|
#include <linux/lz4.h>
|
||||||
|
#include <linux/mm.h>
|
||||||
|
#include <linux/pagemap.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <linux/workqueue.h>
|
||||||
|
|
||||||
#include "data_mgmt.h"
|
#include "data_mgmt.h"
|
||||||
#include "format.h"
|
#include "format.h"
|
||||||
@@ -382,24 +383,25 @@ static void log_block_read(struct mount_info *mi, incfs_uuid_t *id,
|
|||||||
++head->current_record_no;
|
++head->current_record_no;
|
||||||
|
|
||||||
spin_unlock(&log->rl_lock);
|
spin_unlock(&log->rl_lock);
|
||||||
if (schedule_delayed_work(&log->ml_wakeup_work, msecs_to_jiffies(16)))
|
schedule_delayed_work(&log->ml_wakeup_work, msecs_to_jiffies(16));
|
||||||
pr_debug("incfs: scheduled a log pollers wakeup");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int validate_hash_tree(struct file *bf, struct data_file *df,
|
static int validate_hash_tree(struct file *bf, struct file *f, int block_index,
|
||||||
int block_index, struct mem_range data, u8 *buf)
|
struct mem_range data, u8 *buf)
|
||||||
{
|
{
|
||||||
u8 digest[INCFS_MAX_HASH_SIZE] = {};
|
struct data_file *df = get_incfs_data_file(f);
|
||||||
|
u8 stored_digest[INCFS_MAX_HASH_SIZE] = {};
|
||||||
|
u8 calculated_digest[INCFS_MAX_HASH_SIZE] = {};
|
||||||
struct mtree *tree = NULL;
|
struct mtree *tree = NULL;
|
||||||
struct incfs_df_signature *sig = NULL;
|
struct incfs_df_signature *sig = NULL;
|
||||||
struct mem_range calc_digest_rng;
|
|
||||||
struct mem_range saved_digest_rng;
|
|
||||||
struct mem_range root_hash_rng;
|
|
||||||
int digest_size;
|
int digest_size;
|
||||||
int hash_block_index = block_index;
|
int hash_block_index = block_index;
|
||||||
int hash_per_block;
|
int lvl;
|
||||||
int lvl = 0;
|
|
||||||
int res;
|
int res;
|
||||||
|
loff_t hash_block_offset[INCFS_MAX_MTREE_LEVELS];
|
||||||
|
size_t hash_offset_in_block[INCFS_MAX_MTREE_LEVELS];
|
||||||
|
int hash_per_block;
|
||||||
|
pgoff_t file_pages;
|
||||||
|
|
||||||
tree = df->df_hash_tree;
|
tree = df->df_hash_tree;
|
||||||
sig = df->df_signature;
|
sig = df->df_signature;
|
||||||
@@ -408,38 +410,60 @@ static int validate_hash_tree(struct file *bf, struct data_file *df,
|
|||||||
|
|
||||||
digest_size = tree->alg->digest_size;
|
digest_size = tree->alg->digest_size;
|
||||||
hash_per_block = INCFS_DATA_FILE_BLOCK_SIZE / digest_size;
|
hash_per_block = INCFS_DATA_FILE_BLOCK_SIZE / digest_size;
|
||||||
calc_digest_rng = range(digest, digest_size);
|
|
||||||
res = incfs_calc_digest(tree->alg, data, calc_digest_rng);
|
|
||||||
if (res)
|
|
||||||
return res;
|
|
||||||
|
|
||||||
for (lvl = 0; lvl < tree->depth; lvl++) {
|
for (lvl = 0; lvl < tree->depth; lvl++) {
|
||||||
loff_t lvl_off =
|
loff_t lvl_off = tree->hash_level_suboffset[lvl];
|
||||||
tree->hash_level_suboffset[lvl] + sig->hash_offset;
|
|
||||||
loff_t hash_block_off = lvl_off +
|
|
||||||
round_down(hash_block_index * digest_size,
|
|
||||||
INCFS_DATA_FILE_BLOCK_SIZE);
|
|
||||||
size_t hash_off_in_block = hash_block_index * digest_size
|
|
||||||
% INCFS_DATA_FILE_BLOCK_SIZE;
|
|
||||||
struct mem_range buf_range = range(buf,
|
|
||||||
INCFS_DATA_FILE_BLOCK_SIZE);
|
|
||||||
ssize_t read_res = incfs_kread(bf, buf,
|
|
||||||
INCFS_DATA_FILE_BLOCK_SIZE, hash_block_off);
|
|
||||||
|
|
||||||
if (read_res < 0)
|
hash_block_offset[lvl] =
|
||||||
return read_res;
|
lvl_off + round_down(hash_block_index * digest_size,
|
||||||
if (read_res != INCFS_DATA_FILE_BLOCK_SIZE)
|
INCFS_DATA_FILE_BLOCK_SIZE);
|
||||||
|
hash_offset_in_block[lvl] = hash_block_index * digest_size %
|
||||||
|
INCFS_DATA_FILE_BLOCK_SIZE;
|
||||||
|
hash_block_index /= hash_per_block;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(stored_digest, tree->root_hash, digest_size);
|
||||||
|
|
||||||
|
file_pages = DIV_ROUND_UP(df->df_size, INCFS_DATA_FILE_BLOCK_SIZE);
|
||||||
|
for (lvl = tree->depth - 1; lvl >= 0; lvl--) {
|
||||||
|
pgoff_t hash_page =
|
||||||
|
file_pages +
|
||||||
|
hash_block_offset[lvl] / INCFS_DATA_FILE_BLOCK_SIZE;
|
||||||
|
struct page *page = find_get_page_flags(
|
||||||
|
f->f_inode->i_mapping, hash_page, FGP_ACCESSED);
|
||||||
|
|
||||||
|
if (page && PageChecked(page)) {
|
||||||
|
u8 *addr = kmap_atomic(page);
|
||||||
|
|
||||||
|
memcpy(stored_digest, addr + hash_offset_in_block[lvl],
|
||||||
|
digest_size);
|
||||||
|
kunmap_atomic(addr);
|
||||||
|
put_page(page);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (page)
|
||||||
|
put_page(page);
|
||||||
|
|
||||||
|
res = incfs_kread(bf, buf, INCFS_DATA_FILE_BLOCK_SIZE,
|
||||||
|
hash_block_offset[lvl] + sig->hash_offset);
|
||||||
|
if (res < 0)
|
||||||
|
return res;
|
||||||
|
if (res != INCFS_DATA_FILE_BLOCK_SIZE)
|
||||||
return -EIO;
|
return -EIO;
|
||||||
|
res = incfs_calc_digest(tree->alg,
|
||||||
|
range(buf, INCFS_DATA_FILE_BLOCK_SIZE),
|
||||||
|
range(calculated_digest, digest_size));
|
||||||
|
if (res)
|
||||||
|
return res;
|
||||||
|
|
||||||
saved_digest_rng = range(buf + hash_off_in_block, digest_size);
|
if (memcmp(stored_digest, calculated_digest, digest_size)) {
|
||||||
if (!incfs_equal_ranges(calc_digest_rng, saved_digest_rng)) {
|
|
||||||
int i;
|
int i;
|
||||||
bool zero = true;
|
bool zero = true;
|
||||||
|
|
||||||
pr_debug("incfs: Hash mismatch lvl:%d blk:%d\n",
|
pr_debug("incfs: Hash mismatch lvl:%d blk:%d\n",
|
||||||
lvl, block_index);
|
lvl, block_index);
|
||||||
for (i = 0; i < saved_digest_rng.len; ++i)
|
for (i = 0; i < digest_size; i++)
|
||||||
if (saved_digest_rng.data[i]) {
|
if (stored_digest[i]) {
|
||||||
zero = false;
|
zero = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -449,17 +473,31 @@ static int validate_hash_tree(struct file *bf, struct data_file *df,
|
|||||||
return -EBADMSG;
|
return -EBADMSG;
|
||||||
}
|
}
|
||||||
|
|
||||||
res = incfs_calc_digest(tree->alg, buf_range, calc_digest_rng);
|
memcpy(stored_digest, buf + hash_offset_in_block[lvl],
|
||||||
if (res)
|
digest_size);
|
||||||
return res;
|
|
||||||
hash_block_index /= hash_per_block;
|
page = grab_cache_page(f->f_inode->i_mapping, hash_page);
|
||||||
|
if (page) {
|
||||||
|
u8 *addr = kmap_atomic(page);
|
||||||
|
|
||||||
|
memcpy(addr, buf, INCFS_DATA_FILE_BLOCK_SIZE);
|
||||||
|
kunmap_atomic(addr);
|
||||||
|
SetPageChecked(page);
|
||||||
|
unlock_page(page);
|
||||||
|
put_page(page);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
root_hash_rng = range(tree->root_hash, digest_size);
|
res = incfs_calc_digest(tree->alg, data,
|
||||||
if (!incfs_equal_ranges(calc_digest_rng, root_hash_rng)) {
|
range(calculated_digest, digest_size));
|
||||||
pr_debug("incfs: Root hash mismatch blk:%d\n", block_index);
|
if (res)
|
||||||
|
return res;
|
||||||
|
|
||||||
|
if (memcmp(stored_digest, calculated_digest, digest_size)) {
|
||||||
|
pr_debug("incfs: Leaf hash mismatch blk:%d\n", block_index);
|
||||||
return -EBADMSG;
|
return -EBADMSG;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -871,7 +909,7 @@ static int wait_for_data_block(struct data_file *df, int block_index,
|
|||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
ssize_t incfs_read_data_file_block(struct mem_range dst, struct data_file *df,
|
ssize_t incfs_read_data_file_block(struct mem_range dst, struct file *f,
|
||||||
int index, int timeout_ms,
|
int index, int timeout_ms,
|
||||||
struct mem_range tmp)
|
struct mem_range tmp)
|
||||||
{
|
{
|
||||||
@@ -881,6 +919,7 @@ ssize_t incfs_read_data_file_block(struct mem_range dst, struct data_file *df,
|
|||||||
struct mount_info *mi = NULL;
|
struct mount_info *mi = NULL;
|
||||||
struct file *bf = NULL;
|
struct file *bf = NULL;
|
||||||
struct data_file_block block = {};
|
struct data_file_block block = {};
|
||||||
|
struct data_file *df = get_incfs_data_file(f);
|
||||||
|
|
||||||
if (!dst.data || !df)
|
if (!dst.data || !df)
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
@@ -923,7 +962,7 @@ ssize_t incfs_read_data_file_block(struct mem_range dst, struct data_file *df,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (result > 0) {
|
if (result > 0) {
|
||||||
int err = validate_hash_tree(bf, df, index, dst, tmp.data);
|
int err = validate_hash_tree(bf, f, index, dst, tmp.data);
|
||||||
|
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
result = err;
|
result = err;
|
||||||
|
|||||||
@@ -276,7 +276,7 @@ int incfs_scan_metadata_chain(struct data_file *df);
|
|||||||
struct dir_file *incfs_open_dir_file(struct mount_info *mi, struct file *bf);
|
struct dir_file *incfs_open_dir_file(struct mount_info *mi, struct file *bf);
|
||||||
void incfs_free_dir_file(struct dir_file *dir);
|
void incfs_free_dir_file(struct dir_file *dir);
|
||||||
|
|
||||||
ssize_t incfs_read_data_file_block(struct mem_range dst, struct data_file *df,
|
ssize_t incfs_read_data_file_block(struct mem_range dst, struct file *f,
|
||||||
int index, int timeout_ms,
|
int index, int timeout_ms,
|
||||||
struct mem_range tmp);
|
struct mem_range tmp);
|
||||||
|
|
||||||
|
|||||||
@@ -815,7 +815,7 @@ static int read_single_page(struct file *f, struct page *page)
|
|||||||
tmp.data = (u8 *)__get_free_pages(GFP_NOFS, get_order(tmp.len));
|
tmp.data = (u8 *)__get_free_pages(GFP_NOFS, get_order(tmp.len));
|
||||||
bytes_to_read = min_t(loff_t, size - offset, PAGE_SIZE);
|
bytes_to_read = min_t(loff_t, size - offset, PAGE_SIZE);
|
||||||
read_result = incfs_read_data_file_block(
|
read_result = incfs_read_data_file_block(
|
||||||
range(page_start, bytes_to_read), df, block_index,
|
range(page_start, bytes_to_read), f, block_index,
|
||||||
timeout_ms, tmp);
|
timeout_ms, tmp);
|
||||||
|
|
||||||
free_pages((unsigned long)tmp.data, get_order(tmp.len));
|
free_pages((unsigned long)tmp.data, get_order(tmp.len));
|
||||||
|
|||||||
Reference in New Issue
Block a user