/*
 * File: security/sdp/dd.c
 *
 * Samsung Dual-DAR(Data At Rest) cache I/O relay driver
 *
 * Author: Olic Moon <olic.moon@samsung.com>
 */

#include <linux/file.h>
#include <linux/fs.h>
#include <linux/list.h>
#include <linux/device.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/aio.h>
#include <linux/uaccess.h>
#include <linux/ctype.h>
#include <linux/wait.h>
#include <linux/jiffies.h>
#include <linux/atomic.h>
#include <linux/sched/signal.h>

#include <linux/blkdev.h>
#include <linux/bio.h>
#include <linux/blk-crypto.h>

#include <linux/delay.h>
#include <linux/kthread.h>
#include <linux/sched.h>
#include <linux/freezer.h>

#define __FS_HAS_ENCRYPTION IS_ENABLED(CONFIG_FSCRYPT_SDP)
#include <linux/fscrypt.h>
#ifdef CONFIG_FSCRYPT_SDP
#include <sdp/fs_request.h>
#endif

#include "dd_common.h"

#define TIMEOUT_PENDING_REQ (5*1000)

#ifdef CONFIG_FSCRYPT_SDP
extern int fscrypt_sdp_get_storage_type(struct dentry *target_dentry);
#endif

static struct kmem_cache *dd_req_cachep;
static struct workqueue_struct *dd_free_req_workqueue;
static struct workqueue_struct *dd_ioctl_workqueue;
static struct workqueue_struct *dd_read_workqueue;

//static struct task_struct *abort_thread;

static struct dd_context *dd_context_global;
struct dd_context *dd_get_global_context(void)
{
	return dd_context_global;
}
static inline void __benchmark_init(struct dd_benchmark_result *bm_result)
__acquires(&bm_result->lock)
__releases(&bm_result->lock)
{
	int i;
	spin_lock(&bm_result->lock);
	bm_result->count = 0;
	for (i = 0; i < DD_REQ_COUNT; i++)
		bm_result->data[i] = 0;
	spin_unlock(&bm_result->lock);
}

static LIST_HEAD(dd_free_reqs);
static DEFINE_SPINLOCK(dd_req_list_lock);

static inline long req_time_spent(struct dd_req *req)
{
	return jiffies_to_msecs(jiffies) - req->timestamp;
}

#if CONFIG_DD_REQ_DEBUG
static LIST_HEAD(dd_debug_req_list);

static void __dd_debug_req(const char *msg, int mask,
		const char *func, struct dd_req *req)
{
	struct dd_proc *proc = (struct dd_proc *) req->info->proc;

	__dd_debug(mask, func,
			"req {id:%ld ino:%ld (%s) code:%d abort:%d pid:%d(tgid:%d), proc:%p state:%d alive:%ld list:%p(prev:%p, next:%p)}: %s\n",
			req->unique, req->ino, (req->dir == WRITE) ? "write":"read", req->code, req->abort, req->pid, req->tgid, proc,
			req->state, req_time_spent(req), &req->list, req->list.prev, req->list.next, msg);
}

#if 1
#define dd_debug_req(msg, mask, req) \
		__dd_debug_req(msg, mask, __func__, req)
#else
#define dd_debug_req(msg, mask, req) \
		__dd_debug_req(msg, mask, __func__, req)
#endif

static int dd_dump_debug_req_list(int mask)
{
	struct dd_context *ctx = dd_context_global;
	struct dd_proc *proc = NULL;
	struct dd_req *req = NULL;
	int num = 0;

	spin_lock(&ctx->lock);
	list_for_each_entry(proc, &ctx->procs, proc_node) {
		num++;
		dd_debug(mask, "proc { pid:%d(%p) tgid:%d abort:%d req_count:%d pending:%p processing:%p submitted:%p}\n",
				proc->pid, proc, proc->tgid, proc->abort, atomic_read(&proc->reqcount), &proc->pending, &proc->processing, &proc->submitted);
	}
	spin_unlock(&ctx->lock);
	dd_debug(mask, "available process:%d\n", num);

	num = 0;
	spin_lock(&dd_req_list_lock);
	list_for_each_entry(req, &dd_debug_req_list, debug_list) {
		num++;
		dd_debug_req("dump", mask, req);
	}
	spin_unlock(&dd_req_list_lock);
	dd_debug(mask, "allocated requests: %d\n", num);

	return num;
}
#endif

static void dd_info_get(struct dd_info *info);
static struct dd_req *get_req(const char *msg, struct dd_info *info,
		dd_request_code_t code, int dir)
{
	struct dd_context *ctx = dd_context_global;
	struct dd_req  *req;

	BUG_ON(!info);

	spin_lock(&dd_req_list_lock);
	req = list_first_entry_or_null(&dd_free_reqs, struct dd_req, list);
	if (req) {
		if (!list_empty(&req->list))
			list_del_init(&req->list);
		else
			dd_error("get req: debug list is already empty\n");
	}
	spin_unlock(&dd_req_list_lock);

	if (!req) {
		req = kmem_cache_alloc(dd_req_cachep, GFP_NOFS);

		if (!req)
			return ERR_PTR(-ENOMEM);

		INIT_LIST_HEAD(&req->list);
		init_waitqueue_head(&req->waitq);
	}

#if CONFIG_DD_REQ_DEBUG
	INIT_LIST_HEAD(&req->debug_list);

	spin_lock(&dd_req_list_lock);
	list_add(&req->debug_list, &dd_debug_req_list);
	spin_unlock(&dd_req_list_lock);
#endif

	req->context = ctx;
	req->info = info;
	dd_info_get(req->info);

	req->code = code;
	req->dir = dir;
	req->ino = info->ino;
	req->pid = 0;
	req->tgid = 0;
	req->timestamp = jiffies_to_msecs(jiffies);

	req->abort = 0;
	req->need_xattr_flush = 0;
	req->user_space_crypto = dd_policy_user_space_crypto(info->policy.flags);

	memset(&req->bm, 0, sizeof(struct dd_benchmark));

	spin_lock(&ctx->ctr_lock);
	ctx->req_ctr++;
	if (ctx->req_ctr > 4096)
		ctx->req_ctr = 0;
	req->unique = ctx->req_ctr;
	spin_unlock(&ctx->ctr_lock);

	dd_debug_req("req <init>", DD_DEBUG_PROCESS, req);
	dd_req_state(req, DD_REQ_INIT);

	return req;
}

static inline void dd_proc_try_free(struct dd_proc *proc)
{
	int reqcnt = atomic_read(&proc->reqcount);

	BUG_ON(!proc->abort);
	if (reqcnt > 0) {
		dd_info("dd_proc_try_free: delayed freeing proc:%p (reqcnt:%d)\n", proc, reqcnt);
	} else {
		int i;

		dd_info("dd_proc_try_free: freeing proc:%p\n", proc);
		for (i = 0; i < MAX_NUM_CONTROL_PAGE; i++)
			if (proc->control_page[i])
				mempool_free(proc->control_page[i], proc->context->page_pool);

		kfree(proc);
	}
}

static void dd_free_req_work(struct work_struct *work);
static inline void put_req(const char *msg, struct dd_req *req)
{
	if (unlikely(!req))
		return;

	/**
	 * Avoid reentrance. Since request objects are shared
	 * by crypto thread and kernel services and they can
	 * be aborted at any moment. This is to avoid fatal error
	 * even if abort_req and put_req are called multiple times
	 */
	if (unlikely(req->state == DD_REQ_UNALLOCATED))
		return;

	if (!req->abort) {
		if (req->state != DD_REQ_SUBMITTED) {
			dd_error("req unique:%d state is not submitted (%d)\n",
					req->unique, req->state);
		}
		BUG_ON(req->state != DD_REQ_SUBMITTED);
	}

	// delayed freeing req object
	dd_req_state(req, DD_REQ_FINISHING);
	INIT_WORK(&req->delayed_free_work, dd_free_req_work);
	queue_work(dd_free_req_workqueue, &req->delayed_free_work);
}

/**
 * delayed freeing req object. put_req() can be called by an irq handler where we
 * don't want to hold spin lock. put_req() only mark state as finished than this function
 * free those finished requests
 */
static void dd_free_req_work(struct work_struct *work)
{
	struct dd_req *req = container_of(work, struct dd_req, delayed_free_work);

	if (req->state == DD_REQ_FINISHING) {
		struct dd_info *info = req->info;
		struct dd_proc *proc = (struct dd_proc *) info->proc;

		dd_debug_req("req <free>", DD_DEBUG_PROCESS, req);

		/**
		 * proc must not null in majority of cases
		 * except req was aborted before assigned to a crypto task.
		 */
		if (proc && req->user_space_crypto) {
			/**
			 * In case the request was aborted, requested is
			 * dequeued immediately
			 */
			spin_lock(&proc->lock);
			if (!list_empty(&req->list))
				list_del_init(&req->list);
			spin_unlock(&proc->lock);

			if (atomic_dec_and_test(&proc->reqcount)) {
				dd_process("req:%p unbound from proc:%p\n", req, proc);

				if (proc->abort)
					dd_proc_try_free(proc);
			}
		}

		spin_lock(&dd_req_list_lock);
		list_add_tail(&req->list, &dd_free_reqs);
#if CONFIG_DD_REQ_DEBUG
		if (!list_empty(&req->debug_list))
			list_del(&req->debug_list);
		else
			dd_error("free req: debug list is already empty\n");
#endif
		spin_unlock(&dd_req_list_lock);

		dd_req_state(req, DD_REQ_UNALLOCATED);

		// Run benchmark only for read operation
		if (!req->abort && req->dir == READ) {
			dd_submit_benchmark(&req->bm, req->context);
			dd_dump_benchmark(&req->context->bm_result);
		}

		spin_lock(&info->lock);
		if (atomic_dec_and_test(&info->reqcount)) {
			dd_process("req:%p unbound from info:%p\n", req, info);
			info->proc = NULL;
		}
		spin_unlock(&info->lock);

		dd_info_try_free(info);
	} else {
		dd_debug_req("req <free>", DD_DEBUG_PROCESS, req);
	}
}

static void dd_free_pages(struct dd_info *info, struct bio *clone);

static inline void abort_req(const char *msg, struct dd_req *req, int err)
{
	struct dd_info *info = req->info;
	struct dd_proc *proc = (struct dd_proc *) info->proc;
#ifdef CONFIG_FSCRYPT_SDP
	sdp_fs_command_t *cmd = NULL;
#endif

	if (unlikely(!req))
		return;

	if (unlikely(req->state == DD_REQ_UNALLOCATED))
		return;

	if (req->abort)
		return;

#ifdef CONFIG_FSCRYPT_SDP
	printk("Record audit log in case of a failure during en/decryption of dualdar protected file\n");
	if (req->dir == READ) {
		cmd = sdp_fs_command_alloc(FSOP_AUDIT_FAIL_DECRYPT,
				current->tgid, info->policy.userid, -1, info->ino, err,
				GFP_KERNEL);
	} else {
		cmd = sdp_fs_command_alloc(FSOP_AUDIT_FAIL_ENCRYPT,
				current->tgid, info->policy.userid, -1, info->ino, err,
				GFP_KERNEL);
	}
	if (cmd) {
		sdp_fs_request(cmd, NULL);
		sdp_fs_command_free(cmd);
	}
#endif

	req->abort = 1;
	if (proc) {
		spin_lock(&proc->lock);
		// skip in case req is not assigned to any process
		if (!list_empty(&req->list))
			list_del_init(&req->list);
		spin_unlock(&proc->lock);
	}

	dd_debug_req(msg, DD_DEBUG_ERROR, req);

	switch (req->state) {
	case DD_REQ_INIT:
	case DD_REQ_PENDING:
	case DD_REQ_PROCESSING:
	case DD_REQ_SUBMITTED:
	{
		if (req->code == DD_REQ_CRYPTO_BIO) {
			unsigned dir = bio_data_dir(req->u.bio.orig);
			struct bio *orig = req->u.bio.orig;
			struct bio *clone = req->u.bio.clone;

			if (clone) {
				if (dir == WRITE)
					dd_free_pages(req->info, clone);

				bio_put(clone);
			}

			orig->bi_status = err;
			bio_endio(orig);
		}
		break;
	}
	case DD_REQ_FINISHING:
		// nothing to do
	default:
		break;
	}

	if (req->user_space_crypto)
		wake_up_interruptible(&req->waitq);

	put_req(__func__, req);
}

static inline void proc_abort_req_all(const char *msg, struct dd_proc *proc)
__acquires(&proc->lock)
__releases(&proc->lock)
{
	struct dd_req *pos, *temp;

	BUG_ON(!proc);

	spin_lock(&proc->lock);
	list_for_each_entry_safe(pos, temp, &proc->pending, list) {
		spin_unlock(&proc->lock);
		abort_req(__func__, pos, -EIO);
		spin_lock(&proc->lock);
	}

	list_for_each_entry_safe(pos, temp, &proc->processing, list) {
		spin_unlock(&proc->lock);
		abort_req(__func__, pos, -EIO);
		spin_lock(&proc->lock);
	}

	proc->abort = 1;
	wake_up(&proc->waitq);

	spin_unlock(&proc->lock);
}

#if CONFIG_DD_REQ_DEBUG
static int dd_abort_pending_req_timeout(int mask)
{
	struct dd_req *req = NULL;
	int num = 0;

	spin_lock(&dd_req_list_lock);
	list_for_each_entry(req, &dd_debug_req_list, debug_list) {
		if ((req_time_spent(req) > TIMEOUT_PENDING_REQ) && (req->state != DD_REQ_FINISHING)) {
			num++;
			abort_req(__func__, req, -EIO);
		}
	}
	spin_unlock(&dd_req_list_lock);
	if (num > 0)
		dd_debug(mask, "aborted requests: %d\n", num);

	return num;
}
#endif

static struct dd_req *find_req(struct dd_proc *proc, dd_req_state_t state, unsigned long ino)
__acquires(proc->lock)
__releases(proc->lock)
{
	struct dd_req *req = NULL, *p = NULL;
	struct list_head *head = NULL;

	switch (state) {
	case DD_REQ_PENDING:
		head = &proc->pending;
		break;
	case DD_REQ_PROCESSING:
		head = &proc->processing;
		break;
	case DD_REQ_SUBMITTED:
		head = &proc->submitted;
		break;
	default:
		dd_error("can't find process queue for state %d\n", state);
		break;
	}

	if (!head)
		return NULL;

	spin_lock(&proc->lock);
	list_for_each_entry(p, head, list) {
		if (p->info->ino == ino) {
			req = p;
			break;
		}
	}
	spin_unlock(&proc->lock);

	return req;
}

#define DD_CRYPTO_REQ_TIMEOUT 3000

// caller required to hold proc->lock
static void queue_pending_req_locked(struct dd_proc *proc, struct dd_req *req)
{
	list_move_tail(&req->list, &proc->pending);
	dd_req_state(req, DD_REQ_PENDING);
	req->pid = proc->pid;
	req->tgid = proc->tgid;
}

static void dd_free_pages(struct dd_info *info, struct bio *clone)
{
	struct dd_context *ctx = (struct dd_context *) info->context;

	struct address_space *mapping;
	struct bvec_iter iter;
	struct bio_vec bv;

	bio_for_each_bvec(bv, clone, iter) {
		BUG_ON(!bv.bv_page);
		mapping = page_mapping(bv.bv_page);
		if (mapping) {
			bv.bv_page->mapping = NULL;
		}

		mempool_free(bv.bv_page, ctx->page_pool);
		bv.bv_page = NULL;
	}
}

extern int dd_oem_page_crypto_inplace(struct dd_info *info, struct page *page, int dir);

/**
 * Run CE based OEM encryption
 *
 * This is a security requirement to hide cryptographic artifacts of vendor data-at-rest
 * layer behind device level encryption
 */
int dd_oem_crypto_bio_pages(struct dd_req *req, int dir, struct bio *bio)
{
	struct bio_vec *bv;
	struct bvec_iter_all i;
	int err = 0;

	bio_for_each_segment_all(bv, bio, i) {
		struct page *page = bv->bv_page;
		int ret;

		dd_verbose("unique:%d dir:%d\n", req->unique, dir);
		ret = dd_oem_page_crypto_inplace(req->info, page, dir);

		if (ret) {
			err = ret;
			dd_error("failed to decrypt page:[ino:%ld] [index:%ld] \n",
					page->mapping->host->i_ino, page->index);
			SetPageError(page);
		}
	}

	return err;
}

static void dd_end_io(struct bio *clone);

static struct bio *dd_clone_bio(struct dd_req *req, struct bio *orig, unsigned size)
{
	struct dd_info *info = req->info;
	struct dd_context *ctx = (struct dd_context *) info->context;
	struct bio *clone;
	struct bio_vec bv;
	struct bvec_iter iter;
	unsigned int nr_iovecs = 0;
	gfp_t gfp_mask = GFP_NOWAIT | __GFP_HIGHMEM;
	unsigned i, len, remaining_size;
	struct page *page;
	struct bio_vec *bvec;
	struct bio_vec *orig_bvec;

retry:
	if (unlikely(gfp_mask & __GFP_DIRECT_RECLAIM))
		mutex_lock(&ctx->bio_alloc_lock);

	dd_debug_req("cloning bio", DD_DEBUG_VERBOSE, req);

	bio_for_each_bvec(bv, orig, iter) {
		nr_iovecs++;
	}

	clone = bio_alloc_bioset(GFP_NOIO, nr_iovecs, ctx->bio_set);
	if (!clone) {
		dd_error("failed to alloc bioset\n");
		goto return_clone;
	}

	clone->bi_private = req;
	bio_copy_dev(clone, orig);
	clone->bi_opf = orig->bi_opf;
	bio_crypt_clone(clone, orig, GFP_NOIO);

	remaining_size = size;

	for (i = 0; i < nr_iovecs; i++) {
		page = mempool_alloc(ctx->page_pool, gfp_mask);
		if (!page) {
			dd_free_pages(info, clone);
			bio_put(clone);
			gfp_mask |= __GFP_DIRECT_RECLAIM;
			nr_iovecs = 0;
			goto retry;
		}

		len = (unsigned int)((remaining_size > PAGE_SIZE) ? PAGE_SIZE : remaining_size);

		bvec = &clone->bi_io_vec[clone->bi_vcnt];
		orig_bvec = &orig->bi_io_vec[clone->bi_vcnt++];
		bvec->bv_page = page;
		bvec->bv_len = len;
		bvec->bv_offset = 0;
		if (i == 0) {
			bvec->bv_page->mapping = page_mapping(orig_bvec->bv_page);
		}

		clone->bi_iter.bi_size += len;

		remaining_size -= len;
	}

return_clone:
	if (unlikely(gfp_mask & __GFP_DIRECT_RECLAIM))
		mutex_unlock(&ctx->bio_alloc_lock);

	return clone;
}

static int __request_user(struct dd_req *req)
{
	struct dd_context *context = req->context;
	struct dd_info *info = req->info;

	BUG_ON(!req->user_space_crypto);
	BUG_ON(!info || !context);

	spin_lock(&info->lock);
	if (!info->proc) {
		spin_lock(&context->lock);
		info->proc = list_first_entry_or_null(&context->procs, struct dd_proc, proc_node);
		spin_unlock(&context->lock);
	}

	if (unlikely(!info->proc)) {
		dd_error("no available process");

		spin_unlock(&info->lock);
		return -EIO; // IO error (5)
	} else {
		struct dd_proc *proc = (struct dd_proc *) info->proc;
		atomic_inc(&info->reqcount);
		atomic_inc(&proc->reqcount);

		spin_lock_irq(&proc->lock);
		queue_pending_req_locked(proc, req);
		spin_unlock_irq(&proc->lock);

		spin_lock(&context->lock);
		list_move_tail(&proc->proc_node, &context->procs);
		spin_unlock(&context->lock);

		spin_unlock(&info->lock);
		wake_up(&proc->waitq);

		dd_debug_req("req <queued>", DD_DEBUG_PROCESS, req);
		return 0;
	}
}

static void dd_decrypt_work(struct work_struct *work)
{
	struct dd_req *req = container_of(work, struct dd_req, decryption_work);
	struct dd_info *info = req->info;
	struct bio *orig = req->u.bio.orig;

	dd_dump_bio_pages("from disk", req->u.bio.orig);

#ifdef CONFIG_SDP_KEY_DUMP
	if (!dd_policy_skip_decryption_inner_and_outer(req->info->policy.flags)) {
#endif
	if (fscrypt_inode_uses_inline_crypto(req->info->inode)) {
		dd_verbose("skip oem s/w crypt. hw encryption enabled\n");
	} else {
		if (dd_oem_crypto_bio_pages(req, READ, orig)) {
			dd_error("failed oem crypto\n");
			goto abort_out;
		}

		dd_dump_bio_pages("outer (s/w) decryption done", req->u.bio.orig);
	}
#ifdef CONFIG_SDP_KEY_DUMP
	} else {
		dd_info("skip decryption for outer layer - ino : %ld, flag : 0x%04x\n",
				req->info->inode->i_ino, req->info->policy.flags);
	}
#endif

	if (req->user_space_crypto) {
#ifdef CONFIG_SDP_KEY_DUMP
		if (!dd_policy_skip_decryption_inner(req->info->policy.flags)) {
#endif
		if (__request_user(req)) {
			dd_error("failed vendor crypto\n");
			goto abort_out;
		}
#ifdef CONFIG_SDP_KEY_DUMP
		} else {
			dd_info("skip decryption for inner layer - ino : %ld, flag : 0x%04x\n",
					req->info->inode->i_ino, req->info->policy.flags);
			dd_req_state(req, DD_REQ_SUBMITTED);

			orig->bi_status = 0;
			bio_endio(orig);
			put_req(__func__, req);
		}
#endif
	} else {
#ifdef CONFIG_SDP_KEY_DUMP
		if (!dd_policy_skip_decryption_inner(req->info->policy.flags)) {
#endif
		if (dd_sec_crypt_bio_pages(info, req->u.bio.orig, NULL, DD_DECRYPT)) {
			dd_error("failed dd crypto\n");
			goto abort_out;
		}
#ifdef CONFIG_SDP_KEY_DUMP
		} else {
			dd_info("skip decryption for inner layer - ino : %ld, flag : 0x%04x\n",
					req->info->inode->i_ino, req->info->policy.flags);
		}
#endif
		dd_req_state(req, DD_REQ_SUBMITTED);

		orig->bi_status = 0;
		bio_endio(orig);
		put_req(__func__, req);
	}

	return;
	abort_out:
	abort_req(__func__, req, -EIO);
}

static void dd_end_io(struct bio *clone)
{
	struct dd_req *req = (struct dd_req *) clone->bi_private;
	struct dd_info *info = req->info;
	struct bio *orig = req->u.bio.orig;
	unsigned dir = bio_data_dir(clone);
	int err;

	err = clone->bi_status;
	if (dir == WRITE)
		dd_free_pages(info, clone);

	// free clone bio in both success or error case
	dd_verbose("ino:%ld info->context:%p\n", info->ino, info->context);
	bio_put(clone);
	req->u.bio.clone = NULL;

	if (!err)
		if (dir == READ) {
			INIT_WORK(&req->decryption_work, dd_decrypt_work);
			queue_work(dd_read_workqueue, &req->decryption_work);
			return;
		}

	// report success or error to caller
	orig->bi_status = err;
	bio_endio(orig);
	put_req(__func__, req);
}

int dd_submit_bio(struct dd_info *info, struct bio *bio)
{
	struct dd_context *context = (struct dd_context *) info->context;
	struct dd_req *req = get_req(__func__, info, DD_REQ_CRYPTO_BIO, bio_data_dir(bio));
	int err = 0;

	dd_verbose("ino:%ld info->context:%p\n", info->ino, info->context);
	req->info = info;
	req->u.bio.orig = bio;
	if (bio_data_dir(bio) == WRITE) {
		req->u.bio.clone = dd_clone_bio(req, bio, bio->bi_iter.bi_size);
		BUG_ON(!req->u.bio.clone);

		if (req->user_space_crypto) {
			if (__request_user(req)) {
				dd_error("failed to request daemon\n");
				goto err_out;
			}
		} else {
			if (dd_sec_crypt_bio_pages(info, req->u.bio.orig, req->u.bio.clone, DD_ENCRYPT)) {
				dd_error("failed dd crypto\n");
				goto err_out;
			}

			if (fscrypt_inode_uses_inline_crypto(req->info->inode)) {
				dd_verbose("skip oem s/w crypt. hw encryption enabled\n");
//				fscrypt_set_ice_dun(req->info->inode, req->u.bio.clone, 0/* temporary */);
			} else {
				if (dd_oem_crypto_bio_pages(req, WRITE, req->u.bio.clone)) {
					dd_error("failed oem crypto\n");
					goto err_out;
				}
			}

			dd_req_state(req, DD_REQ_SUBMITTED);

			req->u.bio.clone->bi_private = req;
			req->u.bio.clone->bi_end_io = dd_end_io;
			submit_bio_noacct(req->u.bio.clone);
		}
	} else {
		// clone a bio that shares original bio's biovec
		req->u.bio.clone = bio_clone_fast(bio, GFP_NOIO, context->bio_set);
		if (!req->u.bio.clone) {
			dd_error("failed in bio clone\n");
			goto err_out;
		}

		req->u.bio.clone->bi_private = req;
		req->u.bio.clone->bi_end_io = dd_end_io;

#ifdef CONFIG_SDP_KEY_DUMP
		if (!dd_policy_skip_decryption_inner_and_outer(req->info->policy.flags)) {
#endif
		if (fscrypt_inode_uses_inline_crypto(req->info->inode)) {
			dd_verbose("skip oem s/w crypt. hw encryption enabled\n");
//			fscrypt_set_ice_dun(req->info->inode, req->u.bio.clone, 0/* temporary */);
		}
#ifdef CONFIG_SDP_KEY_DUMP
		} else {
			if (fscrypt_inode_uses_inline_crypto(req->info->inode)) {
				req->u.bio.clone->bi_crypt_context = NULL;
				dd_info("skip h/w decryption for ino(%ld)\n", req->info->inode->i_ino);
			}
		}
#endif

		submit_bio_noacct(req->u.bio.clone);
	}

	return err;
err_out:
	abort_req(__func__, req, -EIO);
	return -EIO;
}
EXPORT_SYMBOL(dd_submit_bio);

#define DD_PAGE_CRYPTO_REQ_TIMEOUT 10000
static int __wait_user_request(struct dd_req *req)
{
	int intr;

	dd_debug_req("wait for user response", DD_DEBUG_PROCESS, req);
	while (req->state != DD_REQ_SUBMITTED) {
		intr = wait_event_interruptible_timeout(req->waitq,
				req->state == DD_REQ_SUBMITTED,
				msecs_to_jiffies(DD_PAGE_CRYPTO_REQ_TIMEOUT));
		dd_verbose("wake up unique:%d\n", req->unique);

		if (intr == 0 || intr == -ERESTARTSYS) {
			dd_error("timeout or interrupted (%d) [ID:%d] ino:%ld\n",
					intr, req->unique, req->ino);

			abort_req(__func__, req, -EIO);
			break;
		}
	}
	dd_verbose("user request completed (unique:%d)\n", req->unique);

	if (req->abort)
		return -ECONNABORTED;

	put_req(__func__, req);
	return 0; // return 0 if success
}

int dd_page_crypto(struct dd_info *info, dd_crypto_direction_t dir,
		struct page *src_page, struct page *dst_page)
{
	struct dd_req *req = NULL;
	int err = 0;

	BUG_ON(!src_page || !dst_page);

	dd_verbose("dd_page_crypto ino:%ld\n", info->ino);
	req = get_req(__func__, info, DD_REQ_CRYPTO_PAGE, READ);
	if (IS_ERR(req))
		return -ENOMEM;

	req->u.page.dir = dir;
	req->u.page.src_page = src_page;
	req->u.page.dst_page = dst_page;

	if (dir == DD_DECRYPT) {
		if (fscrypt_inode_uses_inline_crypto(req->info->inode)) {
			dd_verbose("skip oem s/w crypt. hw encryption enabled\n");
		} else {
			err = dd_oem_page_crypto_inplace(info, src_page, READ);
			if (err) {
				dd_error("failed oem crypto\n");
				return err;
			}
		}

		if (req->user_space_crypto) {
			err = __request_user(req);
			if (err) {
				dd_error("failed user request err:%d\n", err);
				return err;
			}

			return __wait_user_request(req);
		} else {
			err = dd_sec_crypt_page(info, DD_DECRYPT, src_page, src_page);
			if (err) {
				dd_error("failed page crypto:%d\n", err);
			}

			dd_req_state(req, DD_REQ_SUBMITTED);
			put_req(__func__, req);
			return err;
		}
	} else {
		dd_error("operation not supported\n");
		BUG();
	}
}

/**
 * Performance flag to describe lock status. We don't want to make a blocking
 * user space call for every time a file is opened
 *
 * This flag is set by user space daemon and is protected by SEAndroid MAC enforcement
 */
unsigned int dd_lock_state = 1; // 1: locked 0: unlocked
module_param_named(lock_state, dd_lock_state, uint, 0600);
MODULE_PARM_DESC(lock_state, "ddar lock status");

int dd_is_user_deamon_locked(void)
{
	return dd_lock_state;
}

#ifdef CONFIG_DDAR_USER_PREPARE
int dd_prepare(struct dd_info *info)
{
	struct dd_req *req = NULL;
	int err = 0;

	if (dd_debug_bit_test(DD_DEBUG_CALL_STACK))
		dump_stack();

	req = get_req(__func__, info, DD_REQ_PREPARE);
	if (!req)
		return -ENOMEM;

	req->u.prepare.metadata = info->mdpage;

	err = __request_user(req);
	if (err) {
		dd_error("failed user request err:%d\n", err);
		return err;
	}

	return __wait_user_request(req);
}
#endif

#define LIMIT_PAGE_NUM (MAX_NUM_REQUEST_PER_CONTROL * MAX_NUM_CONTROL_PAGE)
struct dd_mmap_layout default_mmap_layout = {
		.page_size = PAGE_SIZE,
		.page_num_limit = LIMIT_PAGE_NUM,
		.control_area 			= { .start = 0x00000000, .size = PAGE_SIZE * MAX_NUM_CONTROL_PAGE }, // 32KB
		.metadata_area 			= { .start = 0x01000000, .size = PAGE_SIZE * MAX_NUM_INO_PER_USER_REQUEST }, // 128KB
		.plaintext_page_area 	= { .start = 0x02000000, .size = PAGE_SIZE * LIMIT_PAGE_NUM }, // 1 MB
		.ciphertext_page_area	= { .start = 0x03000000, .size = PAGE_SIZE * LIMIT_PAGE_NUM }  //  MB
};

static int dd_open(struct inode *nodp, struct file *filp)
{
	filp->private_data = NULL;
	return 0;
}

static int dd_mmap_available(struct dd_proc *proc)
{
	if (!proc->control_vma ||
			!proc->metadata_vma ||
			!proc->plaintext_vma ||
			!proc->ciphertext_vma)
		return 0;
	return 1;
}

static int dd_mmap(struct file *filp, struct vm_area_struct *vma)
{
	struct dd_proc *proc = filp->private_data;
	struct dd_context *context = proc->context;
	unsigned long addr = vma->vm_pgoff << PAGE_SHIFT;

	BUG_ON(!proc);
	dd_memory("offset:%p\n", (void *)addr);

	spin_lock(&proc->lock);
	if (addr == context->layout->control_area.start) {
		proc->control_vma = vma;
		proc->control_vma->vm_flags |= VM_LOCKED;
	} else if (addr == context->layout->metadata_area.start) {
		proc->metadata_vma = vma;
		proc->metadata_vma->vm_flags |= VM_LOCKED;
	} else if (addr == context->layout->plaintext_page_area.start) {
		proc->plaintext_vma = vma;
		proc->plaintext_vma->vm_flags |= VM_LOCKED;
	} else if (addr == context->layout->ciphertext_page_area.start) {
		proc->ciphertext_vma = vma;
		proc->ciphertext_vma->vm_flags |= VM_LOCKED;
	} else {
		dd_error("mmap failed. mmap addr:%p\n", (void *)addr);
	}
	spin_unlock(&proc->lock);

	return 0;
}

typedef enum {
	DD_TRANS_OK = 0,
	DD_TRANS_ERR_FULL,
	DD_TRANS_ERR_PROC_DIED,
	DD_TRANS_ERR_UNKNOWN
} dd_transaction_result;

// currently ongoing user-space crypto request. This is always on stack, so no locking needed
struct dd_transaction {
	struct dd_proc *proc;
	struct dd_mmap_control *control;
	int num_ctr_pg;
	int num_md_pg;
	int num_mapped_pg;
	unsigned long mapped_ino[MAX_NUM_INO_PER_USER_REQUEST];
};

static inline int __is_ctr_page_full(struct dd_mmap_control *control)
{
	if (control->num_requests == MAX_NUM_REQUEST_PER_CONTROL)
		return 1;

	return 0;
}

static inline struct dd_user_req *__get_next_user_req(struct dd_mmap_control *control)
{
	if (control->num_requests >= MAX_NUM_REQUEST_PER_CONTROL) {
		return NULL;
	}

	return &control->requests[control->num_requests++];
}

/**
 * Returns control page from current transaction
 */
static inline struct dd_mmap_control *__get_next_ctr_page(struct dd_transaction *t)
{
	struct dd_proc *proc = t->proc;
	unsigned long control_area_addr = proc->control_vma->vm_start;
	struct dd_mmap_control *control;

	control = (struct dd_mmap_control *) page_address(proc->control_page[t->num_ctr_pg]);
	memset((void *)control, 0, sizeof(struct dd_mmap_control));

	dd_memory("mapping text pages control:%p (struct page:%p num_ctr:%d)\n",
			(void *)control, (void *)proc->control_page[t->num_ctr_pg],
			t->num_ctr_pg);

	remap_pfn_range(proc->control_vma,
			control_area_addr + (t->num_ctr_pg << PAGE_SHIFT),
			page_to_pfn(proc->control_page[t->num_ctr_pg]), PAGE_SIZE,
			proc->control_vma->vm_page_prot);

	t->num_ctr_pg++;

	dd_memory("mapping control page proc:%p control:%p [mapped pages:%d] [total control pages:%d]\n",
			proc,
			(void *) (control_area_addr + (t->num_ctr_pg << PAGE_SHIFT)),
			t->num_mapped_pg, t->num_ctr_pg);

	BUG_ON(t->num_ctr_pg > MAX_NUM_CONTROL_PAGE);
	return control;
}

/**
 * Map if current transaction hasn't yet mapped meta-data page
 */
static inline void __get_md_page(struct dd_proc *proc, struct page *mdpage,
		unsigned long int ino, struct dd_transaction *t)
{
	int i = 0;
	unsigned long metadata_area_addr = proc->metadata_vma->vm_start;

	while (i < t->num_md_pg) {
		if (t->mapped_ino[i] == ino)
			return;
		i++;
	}

	dd_memory("mapping metadata area proc:%p md:%p (num_md:%d, num_ctr:%d) [ino:%ld]\n",
			proc,
			(void *) (metadata_area_addr + (t->num_md_pg << PAGE_SHIFT)),
			t->num_md_pg, t->num_ctr_pg, ino);
	remap_pfn_range(proc->metadata_vma,
			metadata_area_addr + (t->num_md_pg << PAGE_SHIFT),
			page_to_pfn(mdpage), PAGE_SIZE,
			proc->metadata_vma->vm_page_prot);
	t->mapped_ino[i] = ino;
	t->num_md_pg++;
}

#ifdef CONFIG_DDAR_USER_PREPARE
static dd_transaction_result __process_prepare_request_locked(
		struct dd_req *req, struct dd_transaction *t)
{
	// create single user space request for prepare
	struct dd_user_req *p = __get_next_user_req(t->control);
	p->unique = req->unique;
	p->userid = req->info->crypt_context.policy.userid;
	p->version = req->info->crypt_context.policy.version;
	p->ino = req->info->inode->i_ino;
	p->code = DD_REQ_PREPARE;

	return DD_TRANS_OK;
}
#endif

static dd_transaction_result __process_bio_request_locked(
		struct dd_req *req, struct dd_transaction *t)
{
	struct dd_proc *proc = t->proc;

	struct bio *orig = req->u.bio.orig;
	struct bio *clone = req->u.bio.clone;
	int dir = bio_data_dir(req->u.bio.orig);
	struct bvec_iter iter_backup;

	unsigned require_ctr_pages;

	/**
	 * NOTE: Do not assume the inode object is still alive at this point.
	 *
	 * It's possible bio was requested for a file's dirty page and the file gets
	 * removed before it's scheduled for crypto operation. In such case the inode
	 * object is freed; dd_info->inode points to NULL as well.
	 *
	 * At the very least, dd_info maintains refcount dd_info will be still alive until
	 * the request completes
	 */
	BUG_ON(!req->info);

	/**
	 * page count limit check. If current bio holds more pages than current user space
	 * transaction can mmap, postpone this to next round. Skipped bio will stay in
	 * pending queue until next dd_ioctl_wait_crypto_request() is called.
	 *
	 * We don't support splitting single bio into multiple user space request to avoid
	 * extra memory copy and keep code straight-forward.
	 */
	require_ctr_pages = (orig->bi_vcnt / MAX_NUM_REQUEST_PER_CONTROL);
	if (orig->bi_vcnt % MAX_NUM_REQUEST_PER_CONTROL > 0)
		require_ctr_pages++;

	if (require_ctr_pages > (MAX_NUM_CONTROL_PAGE - t->num_ctr_pg)) {
		dd_verbose("cannot accommodate %d control pages (%d available), continue\n",
				require_ctr_pages, MAX_NUM_CONTROL_PAGE - t->num_ctr_pg);
		return DD_TRANS_ERR_FULL;
	}

//	dd_verbose("%s mapping bio(vcnt:%d, ino:%ld) control available:%d (required:%d)\n",
//			__func__, orig->bi_vcnt, req->ino,
//			MAX_NUM_CONTROL_PAGE - t->num_ctr_pg, require_ctr_pages);

	spin_unlock(&proc->lock);

	// map pages in bio to user space vma
	BUG_ON(dir == WRITE && !clone);

	// back up bio iterator. restore before submitting it to block layer
	memcpy(&iter_backup, &orig->bi_iter, sizeof(struct bvec_iter));

	while (orig->bi_iter.bi_size) {
		int vm_offset = t->num_mapped_pg << PAGE_SHIFT;
		struct dd_user_req *p = __get_next_user_req(t->control);

		if (!p) {
			t->control = __get_next_ctr_page(t);
			continue; // try again
		}

		dd_verbose("bi_size:%d (control->num_requests:%d)\n",
				orig->bi_iter.bi_size, t->control->num_requests);
		p->unique = req->unique;
		p->userid = req->info->policy.userid;
		p->version = req->info->policy.version;
		p->ino = req->info->ino;
		p->code = DD_REQ_CRYPTO_BIO;
		if (dir == WRITE) {
			struct bio_vec orig_bv = bio_iter_iovec(orig, orig->bi_iter);
			struct bio_vec clone_bv = bio_iter_iovec(clone, clone->bi_iter);
			struct page *plaintext_page = orig_bv.bv_page;
			struct page *ciphertext_page = clone_bv.bv_page;

			BUG_ON(req->info->ino != orig_bv.bv_page->mapping->host->i_ino);

			p->u.bio.rw = DD_ENCRYPT;
			p->u.bio.index = plaintext_page->index;
			p->u.bio.plain_addr = proc->plaintext_vma->vm_start + vm_offset;
			p->u.bio.cipher_addr = proc->ciphertext_vma->vm_start + vm_offset;

			remap_pfn_range(proc->plaintext_vma,
					p->u.bio.plain_addr, page_to_pfn(plaintext_page), PAGE_SIZE,
					proc->plaintext_vma->vm_page_prot);
			remap_pfn_range(proc->ciphertext_vma,
					p->u.bio.cipher_addr, page_to_pfn(ciphertext_page), PAGE_SIZE,
					proc->ciphertext_vma->vm_page_prot);

			dd_dump("plain page", page_address(plaintext_page), PAGE_SIZE);
			bio_advance_iter(orig, &orig->bi_iter, 1 << PAGE_SHIFT);
			bio_advance_iter(clone, &clone->bi_iter, 1 << PAGE_SHIFT);
		} else {
			struct bio_vec orig_bv = bio_iter_iovec(orig, orig->bi_iter);
			struct page *ciphertext_page = orig_bv.bv_page;

			BUG_ON(req->info->ino != orig_bv.bv_page->mapping->host->i_ino);

			p->u.bio.rw = DD_DECRYPT;
			// crypto inplace decryption
			p->u.bio.index = ciphertext_page->index;
			p->u.bio.cipher_addr = proc->ciphertext_vma->vm_start + vm_offset;
			p->u.bio.plain_addr = proc->ciphertext_vma->vm_start + vm_offset;
			remap_pfn_range(proc->ciphertext_vma,
					p->u.bio.cipher_addr, page_to_pfn(ciphertext_page), PAGE_SIZE,
					proc->ciphertext_vma->vm_page_prot);

			dd_dump("cipher page", page_address(ciphertext_page), PAGE_SIZE);
			bio_advance_iter(orig, &orig->bi_iter, 1 << PAGE_SHIFT);
		}

		t->num_mapped_pg++;

		dd_memory("remap_pfn_range(plain:%p cipher:%p) dir:%d t->num_mapped_pg:%d\n",
				(void *)p->u.bio.plain_addr, (void *)p->u.bio.cipher_addr, dir, t->num_mapped_pg);
	}

	dd_memory("done mapping req:[%d] restore bi_iter\n", req->unique);
	memcpy(&orig->bi_iter, &iter_backup, sizeof(struct bvec_iter));
	if (dir == WRITE)
		memcpy(&clone->bi_iter, &iter_backup, sizeof(struct bvec_iter));

	spin_lock(&proc->lock);

	return DD_TRANS_OK;
}

static dd_transaction_result __process_page_request_locked(
		struct dd_req *req,  struct dd_transaction *t)
{
	struct dd_proc *proc = t->proc;
	int vm_offset = t->num_mapped_pg << PAGE_SHIFT;
	struct dd_user_req *p = NULL;
	dd_crypto_direction_t dir = req->u.page.dir;
	unsigned require_ctr_pages = 1;

	BUG_ON(!req->info);

	if (require_ctr_pages > (MAX_NUM_CONTROL_PAGE - t->num_ctr_pg)) {
		dd_verbose("cannot accommodate %d control pages (%d available), continue\n",
				require_ctr_pages, MAX_NUM_CONTROL_PAGE - t->num_ctr_pg);
		return DD_TRANS_ERR_FULL;
	}

	p = __get_next_user_req(t->control);
	if (!p) {
		spin_unlock(&proc->lock);
		t->control = __get_next_ctr_page(t);
		spin_lock(&proc->lock);
		p = __get_next_user_req(t->control);
	}

	p->unique = req->unique;
	p->userid = req->info->policy.userid;
	p->version = req->info->policy.version;
	p->ino = req->info->ino;
	p->code = DD_REQ_CRYPTO_PAGE;

	p->u.bio.rw = dir;

	spin_unlock(&proc->lock);
	if (dir == DD_DECRYPT) {
		struct page *ciphertext_page = req->u.page.src_page;

		p->u.bio.index = ciphertext_page->index;
		p->u.bio.cipher_addr = proc->ciphertext_vma->vm_start + vm_offset;
		p->u.bio.plain_addr = proc->ciphertext_vma->vm_start + vm_offset;
		remap_pfn_range(proc->ciphertext_vma,
				p->u.bio.cipher_addr, page_to_pfn(ciphertext_page), PAGE_SIZE,
				proc->ciphertext_vma->vm_page_prot);

		dd_dump("cipher page", page_address(ciphertext_page), PAGE_SIZE);
		t->num_mapped_pg++;
	} else {
		BUG();
	}
	spin_lock(&proc->lock);

	return DD_TRANS_OK;
}

/**
 * process_request - mmap requested bio to user address space
 * as defined in dd_proc's vma fields
 */
int process_request(struct dd_proc *proc, struct dd_transaction *t)
__releases(proc->lock)
__acquires(proc->lock)
{
	struct dd_req *req, *temp;

	memset(t, 0, sizeof(struct dd_transaction));
	t->proc = proc;
	t->control = NULL;
	spin_lock(&proc->lock);

	list_for_each_entry_safe(req, temp, &proc->pending, list) {
		dd_transaction_result result = DD_TRANS_ERR_UNKNOWN;

		if (t->num_md_pg >= MAX_NUM_INO_PER_USER_REQUEST-1) {
			dd_verbose("can't assign more metadata page (num-ctlpage:%d)\n", t->num_ctr_pg);
			break;
		}

		spin_unlock(&proc->lock);
		if (!t->control || __is_ctr_page_full(t->control))
			t->control = __get_next_ctr_page(t);
		spin_lock(&proc->lock);

		dd_debug_req("req <processing>", DD_DEBUG_PROCESS, req);
		dd_verbose("retrieve req [unique:%d] [code:%d] [ino:%ld] [num_pg:%d] [num_md:%d] [num_ctr:%d]\n",
				req->unique, req->code, req->ino, t->num_mapped_pg, t->num_md_pg, t->num_ctr_pg);
		switch (req->code) {
		case DD_REQ_PREPARE:
		{
#ifdef CONFIG_DDAR_USER_PREPARE
			result = __process_prepare_request_locked(req, t);
#endif
			break;
		}
		case DD_REQ_CRYPTO_BIO:
			result = __process_bio_request_locked(req, t);
			break;
		case DD_REQ_CRYPTO_PAGE:
			result = __process_page_request_locked(req, t);
			break;
		default:
			dd_error("unknown req code:%d\n", req->code);
		}

		BUG_ON(!req->info);
		BUG_ON(t->num_ctr_pg > MAX_NUM_CONTROL_PAGE);

		if (result == DD_TRANS_ERR_FULL) {
			break;
		}

		// todo: handle error when daemon process is not running at this moment
		if (result != DD_TRANS_OK) {
			continue;
		}
		// map meta-data page if not mapped in current transaction
		spin_unlock(&proc->lock);
		__get_md_page(proc, req->info->mdpage, req->ino, t);
		spin_lock(&proc->lock);

		list_move(&req->list, &proc->processing);
		dd_req_state(req, DD_REQ_PROCESSING);
	}
	spin_unlock(&proc->lock);

	return 0;
}

long dd_ioctl_wait_crypto_request(struct dd_proc *proc, struct dd_ioc *ioc)
{
	int rc = 0;
	int err = 0;
	struct dd_transaction t;

	memset(&t, 0, sizeof(struct dd_transaction));

	if (!dd_mmap_available(proc)) {
		dd_error("dd_ioctl_wait_crypto_request: user space memory not registered\n");
		return -ENOMEM;
	}

	spin_lock(&proc->lock);
	dd_verbose("waiting req (current:%p, pid:%p)\n", current, proc);
	if (list_empty(&proc->pending)) {
		DECLARE_WAITQUEUE(wait, current);
		add_wait_queue_exclusive(&proc->waitq, &wait);

		while (!proc->abort && list_empty(&proc->pending)) {
			set_current_state(TASK_INTERRUPTIBLE);
			if (signal_pending(current)) {
				dd_error("dd_ioctl_wait_crypto_request() received signal in interruptible process state\n");
				rc = -EINTR;
				break;
			}

			spin_unlock(&proc->lock);
			freezable_schedule_timeout(msecs_to_jiffies(2000));
			spin_lock(&proc->lock);
		}

		set_current_state(TASK_RUNNING);
		remove_wait_queue(&proc->waitq, &wait);
	}

	spin_unlock(&proc->lock);

	if (proc->abort) {
		dd_error("process abort\n");
		return -EPIPE;
	}

	if (rc) {
		dd_error("dd_ioctl_wait_crypto_request() failed, try again :%d\n", rc);
		return rc;
	}

	dd_verbose("dd_ioctl_wait_crypto_request: retrieving req pid:%p\n", proc);

	err = process_request(proc, &t);
	if (!err) {
		dd_verbose("process_request: control:%d metadata:%d\n",
				t.num_ctr_pg, t.num_md_pg);
		ioc->u.crypto_request.num_control_page = t.num_ctr_pg;
		ioc->u.crypto_request.num_metadata_page = t.num_md_pg;
	}
	return err;
}

long dd_ioctl_submit_crypto_result(struct dd_proc *proc,
		struct dd_user_resp *errs, int num_err)
{
	struct dd_req *req, *temp;
	blk_qc_t ret;

	// should never happen. proc object is freed only when user thread dies or close() the fd
	BUG_ON(!proc);

	spin_lock(&proc->lock);
	list_for_each_entry_safe(req, temp, &proc->processing, list) {
		int err = get_user_resp_err(errs, num_err, req->ino);
		list_move(&req->list, &proc->submitted);
		dd_req_state(req, DD_REQ_SUBMITTED);
		spin_unlock(&proc->lock);

		dd_debug_req("req <submitted>", DD_DEBUG_PROCESS, req);
		if (err) {
			dd_error("req :%d (ino:%ld) failed err:%d\n", req->code, req->ino, err);
			abort_req(__func__, req, -EIO);
		} else {
			switch (req->code) {
			case DD_REQ_CRYPTO_BIO:
			{
				int dir = bio_data_dir(req->u.bio.orig);

				if (dir == WRITE) {
					dd_dump_bio_pages("inner encryption done", req->u.bio.clone);

					if (fscrypt_inode_uses_inline_crypto(req->info->inode)) {
						dd_verbose("skip oem s/w crypt. hw encryption enabled\n");
//						fscrypt_set_ice_dun(req->info->inode, req->u.bio.clone, 0/* temporary */);
					} else {
						if (dd_oem_crypto_bio_pages(req, WRITE, req->u.bio.clone)) {
							abort_req(__func__, req, -EIO);
							goto err_continue;
						}

						dd_dump_bio_pages("outter (s/w) encryption done", req->u.bio.clone);
					}

					req->u.bio.clone->bi_status = err;
					req->u.bio.clone->bi_end_io = dd_end_io;
					ret = submit_bio(req->u.bio.clone);
				} else {
					dd_dump_bio_pages("inner decryption done", req->u.bio.orig);

					bio_endio(req->u.bio.orig);
					put_req(__func__, req);
				}
			}
			break;
			case DD_REQ_CRYPTO_PAGE:
			case DD_REQ_PREPARE:
				/**
				 * do nothing: this will wake up process waiting for user
				 * space request and it is kernel service's responsibility
				 * to free req object
				 */
				break;
			case DD_REQ_INVALID:
			default:
				break;
			}
		}

		wake_up_interruptible(&req->waitq);

err_continue:
		spin_lock(&proc->lock);
	}
	spin_unlock(&proc->lock);

	return 0;
}

struct ioc_set_xattr_work {
	struct work_struct work;
	struct inode *inode;
	char name[MAX_XATTR_NAME_LEN];
	char value[MAX_XATTR_LEN];
	int size;
};

static void dd_write_metadata_work(struct work_struct *work)
{
	struct ioc_set_xattr_work *ioc_work = container_of(work, struct ioc_set_xattr_work, work);

	dd_write_crypto_metadata(ioc_work->inode,
			ioc_work->name, ioc_work->value, ioc_work->size);

	dd_verbose("dd_write_metadata_work %ld complete (i_state:0x%x)\n",
			ioc_work->inode->i_ino, ioc_work->inode->i_state);

	iput(ioc_work->inode);
	kfree(ioc_work);
}

long dd_ioctl_add_crypto_proc(struct file *filp)
{
	struct dd_proc *proc = kzalloc(sizeof(*proc), GFP_KERNEL);
	int i;

	if (proc == NULL)
		return -ENOMEM;

	spin_lock_init(&proc->lock);

	proc->context = dd_context_global;

	INIT_LIST_HEAD(&proc->proc_node);

	proc->abort = 0;
	init_waitqueue_head(&proc->waitq);

	INIT_LIST_HEAD(&proc->pending);
	INIT_LIST_HEAD(&proc->processing);
	INIT_LIST_HEAD(&proc->submitted);

	filp->private_data = (void *) proc;
	proc->pid = current->pid;
	proc->tgid = current->tgid;
	proc->control_vma = NULL;
	proc->metadata_vma = NULL;
	proc->plaintext_vma = NULL;
	proc->ciphertext_vma = NULL;
	proc->num_control_page = 0;
	for (i = 0; i < MAX_NUM_CONTROL_PAGE; i++) {
		proc->control_page[i] = mempool_alloc(proc->context->page_pool, GFP_NOWAIT);
		if (!proc->control_page[i])
			goto nomem_out;
	}

	atomic_set(&proc->reqcount, 1);

	spin_lock(&dd_context_global->lock);
	list_add_tail(&proc->proc_node, &dd_context_global->procs);
	spin_unlock(&dd_context_global->lock);
	dd_info("process %p (pid:%d, tgid:%d) added\n", proc, proc->pid, proc->tgid);

	return 0;

nomem_out:
	for (i = 0; i < MAX_NUM_CONTROL_PAGE; i++)
		if (proc->control_page[i])
			mempool_free(proc->control_page[i], proc->context->page_pool);

	return -ENOMEM;
}

long dd_ioctl_remove_crypto_proc(int userid)
{
	struct dd_context *ctx = dd_context_global;
	struct dd_proc *proc = NULL;

	spin_lock(&dd_context_global->lock);
	list_for_each_entry(proc, &ctx->procs, proc_node) {
		proc->abort = 1;
		wake_up(&proc->waitq);
	}
	spin_unlock(&dd_context_global->lock);
	return 0;
}

static long dd_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
	struct dd_proc *proc = (struct dd_proc *) filp->private_data;
	struct dd_req *req = NULL;
	struct dd_ioc ioc;
	struct inode *inode;
	int err;

	if (copy_from_user(&ioc, (void __user *) arg, sizeof(struct dd_ioc)))
		return -EFAULT;

	switch (cmd) {
	case DD_IOCTL_REGISTER_CRYPTO_TASK:
	{
		dd_info("DD_IOCTL_REGISTER_CRYPTO_TASK");

		return dd_ioctl_add_crypto_proc(filp);
	}
	case DD_IOCTL_UNREGISTER_CRYPTO_TASK:
	{
		dd_info("DD_IOCTL_UNREGISTER_CRYPTO_TASK");
		return dd_ioctl_remove_crypto_proc(0);
	}
	case DD_IOCTL_GET_DEBUG_MASK:
	{
		dd_verbose("DD_IOCTL_GET_DEBUG_MASK");
		ioc.u.debug_mask = dd_debug_mask;
		if (copy_to_user((void __user *)arg, &ioc, sizeof(struct dd_ioc))) {
			return -EFAULT;
		}
		break;
	}
	case DD_IOCTL_SEND_LOG:
	{
		__dd_debug(ioc.u.log_msg.mask, "dualdard", "%s", ioc.u.log_msg.buf);
		break;
	}
	case DD_IOCTL_UPDATE_LOCK_STATE:
	{
		int old_state = dd_lock_state;
		dd_lock_state = ioc.u.lock.state;
		dd_verbose("DD_IOCTL_UPDATE_LOCK_STATE: %d\n", dd_lock_state);

		if (old_state != dd_lock_state) {
			if (dd_lock_state) {
				dd_error("ddar locked. trigger cache invalidation");
			}
		}

		return 0;
	}
	/* { KNOX_SUPPORT_DAR_DUAL_DO */
	case DD_IOCTL_GET_LOCK_STATE:
	{
		dd_verbose("DD_IOCTL_GET_LOCK_STATE : %u", dd_lock_state);

		ioc.u.lock.state = dd_lock_state;
		if (copy_to_user((void __user *)arg, &ioc, sizeof(unsigned int))) {
			return -EFAULT;
		}

		return 0;
	}
	/* } KNOX_SUPPORT_DAR_DUAL_DO */
#if USE_KEYRING
	case DD_IOCTL_ADD_KEY:
	case DD_IOCTL_EVICT_KEY:
		dd_error("use keyring (no driver support)\n");
		return -EOPNOTSUPP;
#else
	case DD_IOCTL_ADD_KEY:
		dd_info("DD_IOCTL_ADD_KEY");
		return dd_add_master_key(ioc.u.add_key.userid,
				ioc.u.add_key.key, ioc.u.add_key.len);
	case DD_IOCTL_EVICT_KEY:
		dd_info("DD_IOCTL_EVICT_KEY");
		dd_evict_master_key(ioc.u.evict_key.userid);
		return 0;
	case DD_IOCTL_DUMP_KEY:
		dd_info("DD_IOCTL_DUMP_KEY");
#ifdef CONFIG_SDP_KEY_DUMP
		err = dd_dump_key(ioc.u.dump_key.userid,
				ioc.u.dump_key.fileDescriptor);
		return err;
#else
		return 0;
#endif
#endif
	case DD_IOCTL_SKIP_DECRYPTION_BOTH:
	{
		struct fd f = {NULL, 0};
		struct inode *inode;
		struct dd_crypt_context crypt_context;

		dd_info("DD_IOCTL_SKIP_DECRYPTION_BOTH");

		f = fdget(ioc.u.dump_key.fileDescriptor);
		if (unlikely(f.file == NULL)) {
			dd_error("invalid fd : %d\n", ioc.u.dump_key.fileDescriptor);
			return -EINVAL;
		}
		inode = f.file->f_path.dentry->d_inode;
		if (!inode) {
			dd_error("invalid inode address\n");
			return -EBADF;
		}
		if (dd_read_crypt_context(inode, &crypt_context) != sizeof(struct dd_crypt_context)) {
			dd_error("failed to read dd crypt context - ino:%ld\n", inode->i_ino);
			return -EINVAL;
		}
		crypt_context.policy.flags |= DD_POLICY_SKIP_DECRYPTION_INNER;
		crypt_context.policy.flags |= DD_POLICY_SKIP_DECRYPTION_OUTER;
		if (dd_write_crypt_context(inode, &crypt_context, NULL)) {
			dd_error("failed to write dd crypt context - ino:%ld\n", inode->i_ino);
			return -EINVAL;
		}
		dd_info("updated policy - ino:%ld\n", inode->i_ino);

		return 0;
	}
	case DD_IOCTL_SKIP_DECRYPTION_INNER:
	{
		struct fd f = { NULL, 0 };
		struct inode *inode;
		struct dd_crypt_context crypt_context;

		dd_info("DD_IOCTL_SKIP_DECRYPTION_INNER");

		f = fdget(ioc.u.dump_key.fileDescriptor);
		if (unlikely(f.file == NULL)) {
			dd_error("invalid fd : %d\n", ioc.u.dump_key.fileDescriptor);
			return -EINVAL;
		}
		inode = f.file->f_path.dentry->d_inode;
		if (!inode) {
			dd_error("invalid inode address\n");
			return -EBADF;
		}
		if (dd_read_crypt_context(inode, &crypt_context) != sizeof(struct dd_crypt_context)) {
			dd_error("failed to read dd crypt context - ino:%ld\n", inode->i_ino);
			return -EINVAL;
		}
		crypt_context.policy.flags &= ~DD_POLICY_SKIP_DECRYPTION_OUTER;
		crypt_context.policy.flags |= DD_POLICY_SKIP_DECRYPTION_INNER;
		if (dd_write_crypt_context(inode, &crypt_context, NULL)) {
			dd_error("failed to write dd crypt context - ino:%ld\n", inode->i_ino);
		}
		dd_info("updated policy - ino:%ld\n", inode->i_ino);

		return 0;
	}
	case DD_IOCTL_NO_SKIP_DECRYPTION:
	{
		struct fd f = { NULL, 0 };
		struct inode *inode;
		struct dd_crypt_context crypt_context;

		dd_info("DD_IOCTL_NO_SKIP_DECRYPTION");

		f = fdget(ioc.u.dump_key.fileDescriptor);
		if (unlikely(f.file == NULL)) {
			dd_error("invalid fd : %d\n", ioc.u.dump_key.fileDescriptor);
			return -EINVAL;
		}
		inode = f.file->f_path.dentry->d_inode;
		if (!inode) {
			dd_error("invalid inode address\n");
			return -EBADF;
		}
		if (dd_read_crypt_context(inode, &crypt_context) != sizeof(struct dd_crypt_context)) {
			dd_error("failed to read dd crypt context - ino:%ld\n", inode->i_ino);
			return -EINVAL;
		}
		crypt_context.policy.flags &= ~DD_POLICY_SKIP_DECRYPTION_OUTER;
		crypt_context.policy.flags &= ~DD_POLICY_SKIP_DECRYPTION_INNER;
		if (dd_write_crypt_context(inode, &crypt_context, NULL)) {
			dd_error("failed to write dd crypt context - ino:%ld\n", inode->i_ino);
		}
		dd_info("updated policy - ino:%ld\n", inode->i_ino);

		return 0;
	}
	case DD_IOCTL_TRACE_DDAR_FILE: {
#ifdef CONFIG_SDP_KEY_DUMP
		struct fd f = { NULL, 0 };
		struct inode *inode;
		struct dd_crypt_context crypt_context;

		dd_info("DD_IOCTL_TRACE_DDAR_FILE");

		f = fdget(ioc.u.dump_key.fileDescriptor);
		if (unlikely(f.file == NULL)) {
			dd_error("invalid fd : %d\n", ioc.u.dump_key.fileDescriptor);
			return -EINVAL;
		}
		inode = f.file->f_path.dentry->d_inode;
		if (!inode) {
			dd_error("invalid inode address\n");
			return -EBADF;
		}
		if (dd_read_crypt_context(inode, &crypt_context) != sizeof(struct dd_crypt_context)) {
			dd_error("failed to read dd crypt context - ino:%ld\n", inode->i_ino);
			return -EINVAL;
		}
		crypt_context.policy.flags |= DD_POLICY_TRACE_FILE;
		if (dd_write_crypt_context(inode, &crypt_context, NULL)) {
			dd_error("failed to write dd crypt context - ino:%ld\n", inode->i_ino);
		}
		fscrypt_dd_trace_inode(inode);
		dd_info("updated policy - ino:%ld\n", inode->i_ino);
#endif

		return 0;
	}
	case DD_IOCTL_DUMP_REQ_LIST:
	{
		dd_info("DD_IOCTL_DUMP_REQ_LIST");

		dd_dump_debug_req_list(DD_DEBUG_INFO);
		return 0;
	}
	case DD_IOCTL_ABORT_PENDING_REQ_TIMEOUT:
	{
		dd_info("DD_IOCTL_ABORT_LONG_PENDING_REQ");

		dd_abort_pending_req_timeout(DD_DEBUG_INFO);
		return 0;
	}
	case DD_IOCTL_GET_MMAP_LAYOUT:
	{
		dd_verbose("DD_IOCTL_GET_MMAP_LAYOUT");
		memcpy(&ioc.u.layout, dd_context_global->layout, sizeof(struct dd_mmap_layout));
		if (copy_to_user((void __user *)arg, &ioc, sizeof(struct dd_ioc))) {
			return -EFAULT;
		}

		return 0;
	}
	case DD_IOCTL_WAIT_CRYPTO_REQUEST:
	{
		int rc;

		BUG_ON(!proc); // caller is not a crypto task
		rc = dd_ioctl_wait_crypto_request(proc, &ioc);
		if (rc < 0) {
			dd_error("DD_IOCTL_WAIT_CRYPTO_REQUEST failed :%d\n", rc);
			return rc;
		}

		if (copy_to_user((void __user *)arg, &ioc, sizeof(struct dd_ioc))) {
			return -EFAULT;
		}

		dd_verbose("DD_IOCTL_WAIT_CRYPTO_REQUEST finish rc:%d\n", rc);
		break;
	}
	case DD_IOCTL_SUBMIT_CRYPTO_RESULT:
	{
		BUG_ON(!proc); // caller is not a crypto task

		dd_verbose("DD_IOCTL_SUBMIT_CRYPTO_RESULT\n");
		return dd_ioctl_submit_crypto_result(proc, ioc.u.crypto_response.err,
				ioc.u.crypto_response.num_err);
	}
	case DD_IOCTL_ABORT_CRYPTO_REQUEST:
	{
		dd_verbose("DD_IOCTL_ABORT_CRYPTO_REQUEST\n");
		proc_abort_req_all("DD_IOCTL_ABORT_CRYPTO_REQUEST", proc);
		return 0;
	}
	case DD_IOCTL_GET_XATTR:
	{
		BUG_ON(!proc); // caller is not a crypto task

		req = find_req(proc, DD_REQ_PROCESSING, ioc.u.xattr.ino);
		if (!req) {
			dd_error("request not found for ino:%ld!\n", ioc.u.xattr.ino);
			return -EBADF;
		}
		inode = req->info->inode;
		if (!inode) {
			dd_error("DD_IOCTL_GET_XATTR: invalid info->inode address. Ignore\n");
			return -EBADF;
		}

		err = dd_read_crypto_metadata(inode,
				ioc.u.xattr.name, ioc.u.xattr.value, MAX_XATTR_LEN);
		if (err < 0) {
			dd_verbose("failed to read crypto metadata name:%s err:%d\n", ioc.u.xattr.name, err);
			return err;
		}

		ioc.u.xattr.size = err;
		if (copy_to_user((void __user *)arg, &ioc, sizeof(struct dd_ioc)))
			return -EFAULT;

		return 0;
	}
	case DD_IOCTL_SET_XATTR:
	{
		struct ioc_set_xattr_work *ioc_work = kmalloc(sizeof(struct ioc_set_xattr_work), GFP_KERNEL);
		if (!ioc_work) {
			dd_error("DD_IOCTL_SET_XATTR - failed to allocate memory\n");
			return -ENOMEM;
		}

		dd_verbose("DD_IOCTL_SET_XATTR %ld\n", ioc.u.xattr.ino);
		BUG_ON(!proc); // caller is not a crypto task

		if (ioc.u.xattr.size > MAX_XATTR_LEN) {
			dd_error("DD_IOCTL_SET_XATTR - invalid input\n");
			kfree(ioc_work);
			return -EINVAL;
		}

		req = find_req(proc, DD_REQ_PROCESSING, ioc.u.xattr.ino);
		if (!req) {
			dd_error("request not found for ino:%ld!\n", ioc.u.xattr.ino);
			kfree(ioc_work);
			return -EBADF;
		}
		inode = req->info->inode;
		if (!inode) {
			dd_error("DD_IOCTL_SET_XATTR: invalid info->inode address. Ignore\n");
			kfree(ioc_work);
			return -EBADF;
		}

		ioc_work->inode = req->info->inode;
		memcpy(ioc_work->name, ioc.u.xattr.name, MAX_XATTR_NAME_LEN);
		memcpy(ioc_work->value, ioc.u.xattr.value, ioc.u.xattr.size);
		ioc_work->size = ioc.u.xattr.size;

		if (!igrab(ioc_work->inode)) {
			dd_error("failed to grab inode refcount. this inode may be getting removed\n");
			kfree(ioc_work);
			return 0;
		}

		INIT_WORK(&ioc_work->work, dd_write_metadata_work);
		queue_work(dd_ioctl_workqueue, &ioc_work->work);

		req->need_xattr_flush = 1;
		return 0;
	}
	default:
		return -ENOTTY;
	}
	return 0;
}

static int dd_release(struct inode *node, struct file *filp)
{
	struct dd_proc *proc = filp->private_data;

	if (proc) {
		struct dd_context *ctx = proc->context;

		dd_info("process %p (pid:%d tgid:%d) released\n", proc, proc->pid, proc->tgid);

		spin_lock(&ctx->lock);
		list_del(&proc->proc_node);
		spin_unlock(&ctx->lock);

		proc_abort_req_all(__func__, proc);

		proc->abort = 1;
		dd_proc_try_free(proc);
	}

	return 0;
}

static ssize_t dd_read(struct file *filp, char __user *ubuf, size_t len, loff_t *offset)
{
	return 0;
}

ssize_t dd_write(struct file *filp, const char __user *ubuf, size_t len, loff_t *offset)
{
	return 0;
}

const struct file_operations dd_dev_operations = {
		.owner = THIS_MODULE,
		.unlocked_ioctl = dd_ioctl,
		.compat_ioctl = dd_ioctl,
		.mmap = dd_mmap,
		.open = dd_open,
		.release = dd_release,
		.read = dd_read,
		.write = dd_write,
};

static struct miscdevice dd_miscdevice = {
		.minor = MISC_DYNAMIC_MINOR,
		.name  = "dd",
		.fops = &dd_dev_operations,
};

static int enforce_caller_gid(uid_t uid)
{
	struct group_info *group_info;
	int i;

	if (!uid_is_app(uid)) {
		return 0;
	}

	group_info = get_current_groups();
	for (i = 0; i < group_info->ngroups; i++) {
		kgid_t gid = group_info->gid[i];
		if (gid.val == AID_VENDOR_DDAR_DE_ACCESS) {
			goto enforce;
		}
	}

	return 0;
enforce:
	{
		char msg[256];
		snprintf(msg, 128, "gid restricted uid:%d pid:%d gids:\n", uid, current->tgid);

		for (i = 0; i < group_info->ngroups; i++) {
			kgid_t gid = group_info->gid[i];
			if (gid.val == AID_VENDOR_DDAR_DE_ACCESS) {
				snprintf(msg, 128, "%s %d", msg, gid.val);
				break;
			}
		}
		dd_info("%s\n", msg);
	}
	return -1;
}

static struct kmem_cache *ext4_dd_info_cachep;

struct dd_info *alloc_dd_info(struct inode *inode,
				const struct dd_policy *dd_policy, struct dd_crypt_context *crypt_context)
{
	struct dd_info *info;
	uid_t calling_uid = from_kuid(&init_user_ns, current_uid());
	unsigned char master_key[256];
	int rc = 0;
#ifdef CONFIG_FSCRYPT_SDP
	sdp_fs_command_t *cmd = NULL;
#endif

	if (!dd_policy) {
		dd_error("alloc_dd_info: dd_policy is null (ino:%ld)\n", inode->i_ino);
		return ERR_PTR(-EINVAL);
	}

	BUG_ON(!ext4_dd_info_cachep);

	if (S_ISREG(inode->i_mode)) {
		if (dd_policy_user_space_crypto(dd_policy->flags)) {
			if (dd_is_user_deamon_locked()) {
				dd_error("dd state locked (dd_lock_state:%d)\n", dd_lock_state);
				return ERR_PTR(-ENOKEY);
			}
		} else if (dd_policy_kernel_crypto(dd_policy->flags)) {
			rc = get_dd_master_key(dd_policy->userid, (void *)master_key);
			if (rc) {
				dd_error("dd state locked (can't find master key for user:%d)\n", dd_policy->userid);
				return ERR_PTR(-ENOKEY);
			}
		}
	}

	if (dd_policy_gid_restriction(dd_policy->flags)) {
		if (enforce_caller_gid(calling_uid)) {
#ifdef CONFIG_FSCRYPT_SDP
			int partition_id = -1;
			struct dentry *de = NULL;
			de = d_find_alias(inode);
			if (de) {
				partition_id = fscrypt_sdp_get_storage_type(de);
			}

			cmd = sdp_fs_command_alloc(FSOP_AUDIT_FAIL_DE_ACCESS,
					current->tgid, dd_policy->userid, partition_id,
					inode->i_ino, -EACCES, GFP_KERNEL);
			if (cmd) {
				sdp_fs_request(cmd, NULL);
				sdp_fs_command_free(cmd);
			}
#endif
			dd_error("gid restricted.. calling-uid:%d ino:%ld\n", calling_uid, inode->i_ino);
			return ERR_PTR(-EACCES);
		}
	}

	if (dd_policy_secure_erase(dd_policy->flags)) {
		mapping_set_sensitive(inode->i_mapping);
	}

	info = kmem_cache_alloc(ext4_dd_info_cachep, GFP_NOFS);
	if (!info) {
		dd_error("failed to allocate dd info\n");
		return ERR_PTR(-ENOMEM);
	}
	memcpy(&info->policy, dd_policy, sizeof(struct dd_policy));

	info->context = (void *) dd_context_global;
	info->inode = inode;
	info->ino = inode->i_ino;
	info->ctfm = NULL;
	info->proc = NULL;
	info->mdpage = NULL;
	spin_lock_init(&info->lock);
	atomic_set(&info->reqcount, 0);
	atomic_set(&info->refcount, 1);

	if (dd_policy_user_space_crypto(dd_policy->flags)) {
		struct metadata_hdr *hdr;

		info->mdpage = alloc_page(GFP_KERNEL);
		hdr = (struct metadata_hdr *) page_address(info->mdpage);
		memset(hdr, 0, sizeof(struct metadata_hdr));
		hdr->ino = inode->i_ino;
		hdr->userid = dd_policy->userid;
		hdr->initialized = 0;

		if (crypt_context == NULL) {
			struct dd_crypt_context temp_crypt_context;
			memset(&temp_crypt_context, 0, sizeof(struct dd_crypt_context));
			memcpy(&temp_crypt_context.policy, dd_policy, sizeof(struct dd_policy));
			memcpy(&info->crypt_context, &temp_crypt_context, sizeof(struct dd_crypt_context));
		} else {
			memcpy(&info->crypt_context, crypt_context, sizeof(struct dd_crypt_context));
		}

#ifdef CONFIG_DDAR_USER_PREPARE
		if (dd_prepare(info)) {
			dd_info_try_free(info);
			return NULL;
		}
#endif
	} else if (dd_policy_kernel_crypto(dd_policy->flags) && S_ISREG(inode->i_mode)) {
		struct crypto_skcipher *ctfm;

		if (crypt_context == NULL) {
			struct dd_crypt_context temp_crypt_context;
			memset(&temp_crypt_context, 0, sizeof(struct dd_crypt_context));
			memcpy(&temp_crypt_context.policy, dd_policy, sizeof(struct dd_policy));
			rc = dd_generate_file_key(dd_policy->userid, &temp_crypt_context);
			if (rc) {
				dd_error("failed to generate file key (ino:%ld)\n", inode->i_ino);
				goto out;
			}
			memcpy(&info->crypt_context, &temp_crypt_context, sizeof(struct dd_crypt_context));
		} else {
			memcpy(&info->crypt_context, crypt_context, sizeof(struct dd_crypt_context));
		}

		ctfm = dd_alloc_ctfm(&info->crypt_context, (void *)master_key);
		if (!ctfm || IS_ERR(ctfm)) {
			rc = ctfm ? PTR_ERR(ctfm) : -ENOMEM;
			goto out;
		}

		info->ctfm = ctfm;

out:
		memzero_explicit(master_key, 256);

		if (rc) {
			dd_info_try_free(info);
			return ERR_PTR(rc);
		}
	} else if (dd_policy_gid_restriction(dd_policy->flags)
			|| dd_policy_kernel_crypto(dd_policy->flags)) {
		if (crypt_context == NULL) {
			struct dd_crypt_context temp_crypt_context;
			memset(&temp_crypt_context, 0, sizeof(struct dd_crypt_context));
			memcpy(&temp_crypt_context.policy, dd_policy, sizeof(struct dd_policy));
			memcpy(&info->crypt_context, &temp_crypt_context, sizeof(struct dd_crypt_context));
		} else {
			memcpy(&info->crypt_context, crypt_context, sizeof(struct dd_crypt_context));
		}
	} else {
		// nothing to do
	}

	dd_process("ino:%ld user:%d flags:0x%04x context:%p\n", inode->i_ino,
			dd_policy->userid, dd_policy->flags, info->context);
	return info;
}

static void dd_info_get(struct dd_info *info)
{
	atomic_inc(&info->refcount);
}

// decrease info->refcount. if new count is 0, free the object
void dd_info_try_free(struct dd_info *info)
{
	if (!info)
		return;

	dd_verbose("info refcount:%d, ino:%ld\n", atomic_read(&info->refcount), info->ino);
	if (atomic_dec_and_test(&info->refcount)) {
		dd_verbose("freeing dd info ino:%ld\n", info->ino);
		if (info->mdpage)
			__free_page(info->mdpage);

		if (info->ctfm)
			crypto_free_skcipher(info->ctfm);

		kmem_cache_free(ext4_dd_info_cachep, info);
	}
}

void dd_init_context(const char *name)
{
	struct dd_context *context = (struct dd_context *) kmalloc(sizeof(struct dd_context), GFP_KERNEL);

	if (context) {
		int ret = 0;
		dd_info("dd_init_context %s\n", name);
		strncpy(context->name, name, sizeof(context->name) - 1);
		context->name[sizeof(context->name) - 1] = 0;

		context->req_ctr = 0;
		spin_lock_init(&context->lock);
		spin_lock_init(&context->ctr_lock);

		INIT_LIST_HEAD(&context->procs);

		spin_lock_init(&context->bm_result.lock);
		/* Enable message ratelimiting. Default is 10 messages per 5 secs. */
		ratelimit_state_init(&context->bm_result.ratelimit_state, 5 * HZ, 10);
		__benchmark_init(&context->bm_result);

		context->layout = &default_mmap_layout;

		mutex_init(&context->bio_alloc_lock);
		context->bio_set = kzalloc(sizeof(struct bio_set), GFP_KERNEL);
		if (!context->bio_set) {
			dd_error("Failed to allocate bioset\n");
			return;
		}
		ret = bioset_init(context->bio_set, 64, 0, (BIOSET_NEED_BVECS | BIOSET_NEED_RESCUER));
		if (ret) {
			dd_error("Failed to init bioset\n");
		}
		context->page_pool = mempool_create_page_pool(256, 0);

		dd_context_global = context;

		// TODO: remove me: this is temporary defence code to enable QA testing
//		abort_thread = kthread_run(abort_req_timeout_thread, NULL, "ddar_abort_req_timeoutd");
//		if (abort_thread) {
//			dd_info("abort req timeout thread created\n");
//		} else {
//			dd_error("Failed to create abort req timeout thread\n");
//		}
	} else {
		dd_error("dd_init_context %s failed\n", name);
	}
}

atomic64_t dd_count;

void set_ddar_count(long count)
{
	atomic64_set(&dd_count, count);
}

long get_ddar_count(void)
{
	long count = atomic64_read(&dd_count);
	return count;
}

void inc_ddar_count(void)
{
	atomic64_inc(&dd_count);
}

void dec_ddar_count(void)
{
	atomic64_dec(&dd_count);
}

static int __init dd_init(void)
{
	int err = -ENOMEM;

	BUG_ON(sizeof(struct metadata_hdr) != METADATA_HEADER_LEN);

	set_ddar_count(0);

	dd_free_req_workqueue = alloc_workqueue("dd_free_req_workqueue", WQ_HIGHPRI, 0);
	if (!dd_free_req_workqueue)
		goto out;

	dd_read_workqueue = alloc_workqueue("dd_read_workqueue", WQ_HIGHPRI, 0);
	if (!dd_read_workqueue)
		goto out;

	dd_ioctl_workqueue = alloc_workqueue("dd_ioctl_workqueue", WQ_HIGHPRI, 0);
	if (!dd_ioctl_workqueue)
		goto out;

	dd_req_cachep = KMEM_CACHE(dd_req, SLAB_RECLAIM_ACCOUNT);
	if (!dd_req_cachep)
		goto out_cache_clean;

	ext4_dd_info_cachep = KMEM_CACHE(dd_info, SLAB_RECLAIM_ACCOUNT);
	if (!ext4_dd_info_cachep)
		goto out_cache_clean;

	err = misc_register(&dd_miscdevice);
	if (err)
		goto out_cache_clean;

	dd_init_context("knox-ddar");

	return 0;

	out_cache_clean:
	destroy_workqueue(dd_free_req_workqueue);
	destroy_workqueue(dd_read_workqueue);
	destroy_workqueue(dd_ioctl_workqueue);
	kmem_cache_destroy(ext4_dd_info_cachep);
	kmem_cache_destroy(dd_req_cachep);
	out:
	return err;
}

module_init(dd_init);

unsigned int dd_debug_mask = 0x03;
module_param_named(debug_mask, dd_debug_mask, uint, 0600);
MODULE_PARM_DESC(debug_mask, "dd driver debug mask");

#define LOGBUF_MAX (512)
void dd_dump(const char *msg, char *buf, int len)
{
	unsigned char logbuf[LOGBUF_MAX];
	int i, j;

	if (!(dd_debug_mask & DD_DEBUG_MEMDUMP))
		return;

	if (len > LOGBUF_MAX)
		len = LOGBUF_MAX;
	memset(logbuf, 0, LOGBUF_MAX);
	i = 0;
	while (i < len) {
		int l = (len-i > 16) ? 16 : len-i;

		snprintf(logbuf, LOGBUF_MAX, "%s\t:", logbuf);

		for (j = 0; j < l; j++) {
			snprintf(logbuf, LOGBUF_MAX, "%s%02hhX", logbuf, buf[i+j]);
			if (j % 2)
				snprintf(logbuf, LOGBUF_MAX, "%s ", logbuf);
		}

		if (l < 16)
			for (j = 0; j < (16 - l); j++) {
				snprintf(logbuf, LOGBUF_MAX, "%s  ", logbuf);
				if (j % 2)
					snprintf(logbuf, LOGBUF_MAX, "%s ", logbuf);
			}

		snprintf(logbuf, LOGBUF_MAX, "%s\t\t", logbuf);

		for (j = 0; j < l; j++)
			snprintf(logbuf, LOGBUF_MAX,
					"%s%c", logbuf, isalnum(buf[i+j]) ? buf[i+j]:'.');

		snprintf(logbuf, LOGBUF_MAX, "%s\n", logbuf);

		i += l;
	}

	printk("knox-dd: %s (%p) %d bytes:\n%s\n", msg, buf, len, logbuf);
}

void dd_dump_bio_pages(const char *msg, struct bio *bio)
{
	struct bio_vec *bv;
	struct bvec_iter_all i;

	if (!(dd_debug_mask & DD_DEBUG_MEMDUMP))
		return;

	dd_verbose("%s: bio bi_flags:%x bi_opf:%x bi_status:%d\n",
			msg, bio->bi_flags, bio->bi_opf, bio->bi_status);

	bio_for_each_segment_all(bv, bio, i) {
		struct page *page = bv->bv_page;

		BUG_ON(!page); // empty bio
		dd_dump(msg, page_address(page), PAGE_SIZE);
	}
}


void __dd_debug(unsigned int mask,
		const char *func, const char *fmt, ...)
{
	if (dd_debug_bit_test(mask)) {
		struct va_format vaf;
		va_list args;
		int buf_len = 256;
		char buf[256];

		va_start(args, fmt);
		vaf.fmt = fmt;
		vaf.va = &args;

		snprintf(buf, buf_len, "[%.2x:%16.s] %pV", mask, func, &vaf);
		va_end(args);
		printk("knox-dd%s\n", buf);

		if (mask & (DD_DEBUG_ERROR | DD_DEBUG_INFO)) {
			dek_add_to_log(-1, buf);
		}
	}
}