Skip to contentSkip to conversation

f2fs crypto: free encryption_info at f2fs_evict_inode

PATCH New Jaegeuk Kim 1 participant 6 messages May 13, 2015

Subsystems (2)
lore
Share & tools
lore

Jaegeuk Kim

author maintainer
#0 lore raw

PATCH 1/6 f2fs crypto: free encryption_info at f2fs_evict_inode

This patch fixes not calling freeing encryption info when f2fs_evict_inode was called.

---
 fs/f2fs/inode.c | 4 ++++
 1 file changed, 4 insertions(+)
fs/f2fs/inode.c +4 -0
index 13936f9..ea6ba3b 100644
@@ -362,6 +362,10 @@ no_delete:
 	if (is_inode_flag_set(F2FS_I(inode), FI_UPDATE_WRITE))
 		add_dirty_inode(sbi, inode->i_ino, UPDATE_INO);
 out_clear:
+#ifdef CONFIG_F2FS_FS_ENCRYPTION
+	if (F2FS_I(inode)->i_crypt_info)
+		f2fs_free_encryption_info(inode);
+#endif
 	clear_inode(inode);
 }
 
Signature · 1 line
2.1.1
author maintainer
#1 lore raw

PATCH 2/6 f2fs crypto: not support fcollapse for encrypted inode

The fcollapse replaces block addresses without IOs, but this violates encryption rule in which each block is encrypted with its block offset.

---
 fs/f2fs/file.c | 3 +++
 1 file changed, 3 insertions(+)
fs/f2fs/file.c +3 -0
index 57f9a02..c4f9c08 100644
@@ -1081,6 +1081,9 @@ static long f2fs_fallocate(struct file *file, int mode,
 	struct inode *inode = file_inode(file);
 	long ret = 0;
 
+	if (f2fs_encrypted_inode(inode) && (mode & FALLOC_FL_COLLAPSE_RANGE))
+		return -EOPNOTSUPP;
+
 	if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE |
 			FALLOC_FL_COLLAPSE_RANGE | FALLOC_FL_ZERO_RANGE))
 		return -EOPNOTSUPP;
Signature · 9 lines
2.1.1


------------------------------------------------------------------------------
One dashboard for servers and applications across Physical-Virtual-Cloud 
Widest out-of-the-box monitoring support with 50+ applications
Performance metrics, stats and reports that give you Actionable Insights
Deep dive visibility with transaction tracing using APM Insight.
http://ad.doubleclick.net/ddm/clk/290420510;117567292;y
author maintainer
#2 lore raw

PATCH 3/6 f2fs crypto: use slab caches

In reply to Jaegeuk Kim

This patch integrates the below patch into f2fs.

"ext4 crypto: use slab caches

Use slab caches the ext4_crypto_ctx and ext4_crypt_info structures for slighly better memory efficiency and debuggability."

---
 fs/f2fs/crypto.c     | 58 ++++++++++++++++++++++++++--------------------------
 fs/f2fs/crypto_key.c |  6 +++---
 fs/f2fs/f2fs.h       |  1 +
 3 files changed, 33 insertions(+), 32 deletions(-)
fs/f2fs/crypto.c +29 -29
index 64c4139..5491d1e 100644
@@ -66,6 +66,9 @@ static DEFINE_SPINLOCK(f2fs_crypto_ctx_lock);
 static struct workqueue_struct *f2fs_read_workqueue;
 static DEFINE_MUTEX(crypto_init);
 
+static struct kmem_cache *f2fs_crypto_ctx_cachep;
+struct kmem_cache *f2fs_crypt_info_cachep;
+
 /**
  * f2fs_release_crypto_ctx() - Releases an encryption context
  * @ctx: The encryption context to release.
@@ -90,7 +93,7 @@ void f2fs_release_crypto_ctx(struct f2fs_crypto_ctx *ctx)
 	if (ctx->flags & F2FS_CTX_REQUIRES_FREE_ENCRYPT_FL) {
 		if (ctx->tfm)
 			crypto_free_tfm(ctx->tfm);
-		kfree(ctx);
+		kmem_cache_free(f2fs_crypto_ctx_cachep, ctx);
 	} else {
 		spin_lock_irqsave(&f2fs_crypto_ctx_lock, flags);
 		list_add(&ctx->free_list, &f2fs_free_crypto_ctxs);
@@ -99,23 +102,6 @@ void f2fs_release_crypto_ctx(struct f2fs_crypto_ctx *ctx)
 }
 
 /**
- * f2fs_alloc_and_init_crypto_ctx() - Allocates and inits an encryption context
- * @mask: The allocation mask.
- *
- * Return: An allocated and initialized encryption context on success. An error
- * value or NULL otherwise.
- */
-static struct f2fs_crypto_ctx *f2fs_alloc_and_init_crypto_ctx(gfp_t mask)
-{
-	struct f2fs_crypto_ctx *ctx = kzalloc(sizeof(struct f2fs_crypto_ctx),
-						mask);
-
-	if (!ctx)
-		return ERR_PTR(-ENOMEM);
-	return ctx;
-}
-
-/**
  * f2fs_get_crypto_ctx() - Gets an encryption context
  * @inode:       The inode for which we are doing the crypto
  *
@@ -151,9 +137,9 @@ struct f2fs_crypto_ctx *f2fs_get_crypto_ctx(struct inode *inode)
 		list_del(&ctx->free_list);
 	spin_unlock_irqrestore(&f2fs_crypto_ctx_lock, flags);
 	if (!ctx) {
-		ctx = f2fs_alloc_and_init_crypto_ctx(GFP_NOFS);
-		if (IS_ERR(ctx)) {
-			res = PTR_ERR(ctx);
+		ctx = kmem_cache_zalloc(f2fs_crypto_ctx_cachep, GFP_NOFS);
+		if (!ctx) {
+			res = -ENOMEM;
 			goto out;
 		}
 		ctx->flags |= F2FS_CTX_REQUIRES_FREE_ENCRYPT_FL;
@@ -263,7 +249,7 @@ void f2fs_exit_crypto(void)
 		}
 		if (pos->tfm)
 			crypto_free_tfm(pos->tfm);
-		kfree(pos);
+		kmem_cache_free(f2fs_crypto_ctx_cachep, pos);
 	}
 	INIT_LIST_HEAD(&f2fs_free_crypto_ctxs);
 	if (f2fs_bounce_page_pool)
@@ -272,6 +258,12 @@ void f2fs_exit_crypto(void)
 	if (f2fs_read_workqueue)
 		destroy_workqueue(f2fs_read_workqueue);
 	f2fs_read_workqueue = NULL;
+	if (f2fs_crypto_ctx_cachep)
+		kmem_cache_destroy(f2fs_crypto_ctx_cachep);
+	f2fs_crypto_ctx_cachep = NULL;
+	if (f2fs_crypt_info_cachep)
+		kmem_cache_destroy(f2fs_crypt_info_cachep);
+	f2fs_crypt_info_cachep = NULL;
 }
 
 /**
@@ -284,24 +276,32 @@ void f2fs_exit_crypto(void)
  */
 int __init f2fs_init_crypto(void)
 {
-	int i, res;
+	int i, res = -ENOMEM;
 
 	mutex_lock(&crypto_init);
 	if (f2fs_read_workqueue)
 		goto already_initialized;
 
 	f2fs_read_workqueue = alloc_workqueue("f2fs_crypto", WQ_HIGHPRI, 0);
-	if (!f2fs_read_workqueue) {
-		res = -ENOMEM;
+	if (!f2fs_read_workqueue)
+		goto fail;
+
+	f2fs_crypto_ctx_cachep = KMEM_CACHE(f2fs_crypto_ctx,
+					    SLAB_RECLAIM_ACCOUNT);
+	if (!f2fs_crypto_ctx_cachep)
+		goto fail;
+
+	f2fs_crypt_info_cachep = KMEM_CACHE(f2fs_crypt_info,
+					    SLAB_RECLAIM_ACCOUNT);
+	if (!f2fs_crypt_info_cachep)
 		goto fail;
-	}
 
 	for (i = 0; i < num_prealloc_crypto_ctxs; i++) {
 		struct f2fs_crypto_ctx *ctx;
 
-		ctx = f2fs_alloc_and_init_crypto_ctx(GFP_KERNEL);
-		if (IS_ERR(ctx)) {
-			res = PTR_ERR(ctx);
+		ctx = kmem_cache_zalloc(f2fs_crypto_ctx_cachep, GFP_KERNEL);
+		if (!ctx) {
+			res = -ENOMEM;
 			goto fail;
 		}
 		list_add(&ctx->free_list, &f2fs_free_crypto_ctxs);
fs/f2fs/crypto_key.c +3 -3
index aec7e17..2bdcae5 100644
@@ -99,7 +99,7 @@ void f2fs_free_encryption_info(struct inode *inode)
 		key_put(ci->ci_keyring_key);
 	crypto_free_ablkcipher(ci->ci_ctfm);
 	memzero_explicit(&ci->ci_raw, sizeof(ci->ci_raw));
-	kfree(ci);
+	kmem_cache_free(f2fs_crypt_info_cachep, ci);
 	fi->i_crypt_info = NULL;
 }
 
@@ -131,7 +131,7 @@ int _f2fs_get_encryption_info(struct inode *inode)
 		return -EINVAL;
 	res = 0;
 
-	crypt_info = kmalloc(sizeof(struct f2fs_crypt_info), GFP_NOFS);
+	crypt_info = kmem_cache_alloc(f2fs_crypt_info_cachep, GFP_NOFS);
 	if (!crypt_info)
 		return -ENOMEM;
 
@@ -181,7 +181,7 @@ out:
 	if (res < 0) {
 		if (res == -ENOKEY)
 			res = 0;
-		kfree(crypt_info);
+		kmem_cache_free(f2fs_crypt_info_cachep, crypt_info);
 	} else {
 		fi->i_crypt_info = crypt_info;
 		crypt_info->ci_keyring_key = keyring_key;
fs/f2fs/f2fs.h +1 -0
index 5809590..8f1f21a 100644
@@ -2004,6 +2004,7 @@ int f2fs_process_policy(const struct f2fs_encryption_policy *, struct inode *);
 int f2fs_get_policy(struct inode *, struct f2fs_encryption_policy *);
 
 /* crypt.c */
+extern struct kmem_cache *f2fs_crypt_info_cachep;
 bool f2fs_valid_contents_enc_mode(uint32_t);
 uint32_t f2fs_validate_encryption_key_size(uint32_t, uint32_t);
 struct f2fs_crypto_ctx *f2fs_get_crypto_ctx(struct inode *);
Signature · 1 line
2.1.1
author maintainer
#3 lore raw

PATCH 4/6 f2fs crypto: get rid of ci_mode from struct f2fs_crypt_info

In reply to Jaegeuk Kim

This patch integrates the below patch into f2fs.

"ext4 crypto: get rid of ci_mode from struct ext4_crypt_info

The ci_mode field was superfluous, and getting rid of it gets rid of an unused hole in the structure."

---
 fs/f2fs/crypto.c       | 11 +++++------
 fs/f2fs/crypto_fname.c |  4 ++--
 fs/f2fs/crypto_key.c   | 12 ++++++------
 fs/f2fs/f2fs_crypto.h  |  1 -
 4 files changed, 13 insertions(+), 15 deletions(-)
fs/f2fs/crypto.c +5 -6
index 5491d1e..c1a5c6f 100644
@@ -151,14 +151,13 @@ struct f2fs_crypto_ctx *f2fs_get_crypto_ctx(struct inode *inode)
 	 * Allocate a new Crypto API context if we don't already have
 	 * one or if it isn't the right mode.
 	 */
-	BUG_ON(ci->ci_mode == F2FS_ENCRYPTION_MODE_INVALID);
-	if (ctx->tfm && (ctx->mode != ci->ci_mode)) {
+	if (ctx->tfm && (ctx->mode != ci->ci_data_mode)) {
 		crypto_free_tfm(ctx->tfm);
 		ctx->tfm = NULL;
 		ctx->mode = F2FS_ENCRYPTION_MODE_INVALID;
 	}
 	if (!ctx->tfm) {
-		switch (ci->ci_mode) {
+		switch (ci->ci_data_mode) {
 		case F2FS_ENCRYPTION_MODE_AES_256_XTS:
 			ctx->tfm = crypto_ablkcipher_tfm(
 				crypto_alloc_ablkcipher("xts(aes)", 0, 0));
@@ -178,9 +177,9 @@ struct f2fs_crypto_ctx *f2fs_get_crypto_ctx(struct inode *inode)
 			ctx->tfm = NULL;
 			goto out;
 		}
-		ctx->mode = ci->ci_mode;
+		ctx->mode = ci->ci_data_mode;
 	}
-	BUG_ON(ci->ci_size != f2fs_encryption_key_size(ci->ci_mode));
+	BUG_ON(ci->ci_size != f2fs_encryption_key_size(ci->ci_data_mode));
 
 	/*
 	 * There shouldn't be a bounce page attached to the crypto
@@ -388,7 +387,7 @@ static int f2fs_page_crypto(struct f2fs_crypto_ctx *ctx,
 	int res = 0;
 
 	BUG_ON(!ctx->tfm);
-	BUG_ON(ctx->mode != fi->i_crypt_info->ci_mode);
+	BUG_ON(ctx->mode != fi->i_crypt_info->ci_data_mode);
 
 	if (ctx->mode != F2FS_ENCRYPTION_MODE_AES_256_XTS) {
 		printk_ratelimited(KERN_ERR
fs/f2fs/crypto_fname.c +2 -2
index 8f3ff9b..e3a1bdc 100644
@@ -269,9 +269,9 @@ int f2fs_setup_fname_crypto(struct inode *inode)
 	if (!ci || ci->ci_ctfm)
 		return 0;
 
-	if (ci->ci_mode != F2FS_ENCRYPTION_MODE_AES_256_CTS) {
+	if (ci->ci_filename_mode != F2FS_ENCRYPTION_MODE_AES_256_CTS) {
 		printk_once(KERN_WARNING "f2fs: unsupported key mode %d\n",
-				ci->ci_mode);
+				ci->ci_filename_mode);
 		return -ENOKEY;
 	}
 
fs/f2fs/crypto_key.c +6 -6
index 2bdcae5..dbfc616 100644
@@ -142,14 +142,14 @@ int _f2fs_get_encryption_info(struct inode *inode)
 	memcpy(crypt_info->ci_master_key, ctx.master_key_descriptor,
 				sizeof(crypt_info->ci_master_key));
 	if (S_ISREG(inode->i_mode))
-		crypt_info->ci_mode = ctx.contents_encryption_mode;
+		crypt_info->ci_size =
+			f2fs_encryption_key_size(crypt_info->ci_data_mode);
 	else if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))
-		crypt_info->ci_mode = ctx.filenames_encryption_mode;
-	else {
-		printk(KERN_ERR "f2fs crypto: Unsupported inode type.\n");
+		crypt_info->ci_size =
+			f2fs_encryption_key_size(crypt_info->ci_filename_mode);
+	else
 		BUG();
-	}
-	crypt_info->ci_size = f2fs_encryption_key_size(crypt_info->ci_mode);
+
 	BUG_ON(!crypt_info->ci_size);
 
 	memcpy(full_key_descriptor, F2FS_KEY_DESC_PREFIX,
fs/f2fs/f2fs_crypto.h +0 -1
index 6e41394..58682d2 100644
@@ -75,7 +75,6 @@ struct f2fs_encryption_key {
 } __attribute__((__packed__));
 
 struct f2fs_crypt_info {
-	unsigned char	ci_mode;
 	unsigned char	ci_size;
 	char		ci_data_mode;
 	char		ci_filename_mode;
Signature · 9 lines
2.1.1


------------------------------------------------------------------------------
One dashboard for servers and applications across Physical-Virtual-Cloud 
Widest out-of-the-box monitoring support with 50+ applications
Performance metrics, stats and reports that give you Actionable Insights
Deep dive visibility with transaction tracing using APM Insight.
http://ad.doubleclick.net/ddm/clk/290420510;117567292;y
author maintainer
#4 lore raw

PATCH 5/6 f2fs crypto: shrink size of the f2fs_crypto_ctx structure

In reply to Jaegeuk Kim

This patch integrates the below patch into f2fs.

"ext4 crypto: shrink size of the ext4_crypto_ctx structure

Some fields are only used when the crypto_ctx is being used on the read path, some are only used on the write path, and some are only used when the structure is on free list. Optimize memory use by using a union."

---
 fs/f2fs/crypto.c      | 40 ++++++++++++++++++----------------------
 fs/f2fs/f2fs_crypto.h | 21 ++++++++++++++-------
 fs/f2fs/segment.c     |  2 +-
 3 files changed, 33 insertions(+), 30 deletions(-)
fs/f2fs/crypto.c +18 -22
index c1a5c6f..e36eddd 100644
@@ -82,14 +82,14 @@ void f2fs_release_crypto_ctx(struct f2fs_crypto_ctx *ctx)
 {
 	unsigned long flags;
 
-	if (ctx->bounce_page) {
+	if (ctx->flags & F2FS_WRITE_PATH_FL && ctx->w.bounce_page) {
 		if (ctx->flags & F2FS_BOUNCE_PAGE_REQUIRES_FREE_ENCRYPT_FL)
-			__free_page(ctx->bounce_page);
+			__free_page(ctx->w.bounce_page);
 		else
-			mempool_free(ctx->bounce_page, f2fs_bounce_page_pool);
-		ctx->bounce_page = NULL;
+			mempool_free(ctx->w.bounce_page, f2fs_bounce_page_pool);
+		ctx->w.bounce_page = NULL;
 	}
-	ctx->control_page = NULL;
+	ctx->w.control_page = NULL;
 	if (ctx->flags & F2FS_CTX_REQUIRES_FREE_ENCRYPT_FL) {
 		if (ctx->tfm)
 			crypto_free_tfm(ctx->tfm);
@@ -146,6 +146,7 @@ struct f2fs_crypto_ctx *f2fs_get_crypto_ctx(struct inode *inode)
 	} else {
 		ctx->flags &= ~F2FS_CTX_REQUIRES_FREE_ENCRYPT_FL;
 	}
+	ctx->flags &= ~F2FS_WRITE_PATH_FL;
 
 	/*
 	 * Allocate a new Crypto API context if we don't already have
@@ -181,12 +182,6 @@ struct f2fs_crypto_ctx *f2fs_get_crypto_ctx(struct inode *inode)
 	}
 	BUG_ON(ci->ci_size != f2fs_encryption_key_size(ci->ci_data_mode));
 
-	/*
-	 * There shouldn't be a bounce page attached to the crypto
-	 * context at this point.
-	 */
-	BUG_ON(ctx->bounce_page);
-
 out:
 	if (res) {
 		if (!IS_ERR_OR_NULL(ctx))
@@ -203,8 +198,8 @@ out:
 static void completion_pages(struct work_struct *work)
 {
 	struct f2fs_crypto_ctx *ctx =
-		container_of(work, struct f2fs_crypto_ctx, work);
-	struct bio *bio	= ctx->bio;
+		container_of(work, struct f2fs_crypto_ctx, r.work);
+	struct bio *bio = ctx->r.bio;
 	struct bio_vec *bv;
 	int i;
 
@@ -225,9 +220,9 @@ static void completion_pages(struct work_struct *work)
 
 void f2fs_end_io_crypto_work(struct f2fs_crypto_ctx *ctx, struct bio *bio)
 {
-	INIT_WORK(&ctx->work, completion_pages);
-	ctx->bio = bio;
-	queue_work(f2fs_read_workqueue, &ctx->work);
+	INIT_WORK(&ctx->r.work, completion_pages);
+	ctx->r.bio = bio;
+	queue_work(f2fs_read_workqueue, &ctx->r.work);
 }
 
 /**
@@ -238,12 +233,12 @@ void f2fs_exit_crypto(void)
 	struct f2fs_crypto_ctx *pos, *n;
 
 	list_for_each_entry_safe(pos, n, &f2fs_free_crypto_ctxs, free_list) {
-		if (pos->bounce_page) {
+		if (pos->w.bounce_page) {
 			if (pos->flags &
 				F2FS_BOUNCE_PAGE_REQUIRES_FREE_ENCRYPT_FL)
-				__free_page(pos->bounce_page);
+				__free_page(pos->w.bounce_page);
 			else
-				mempool_free(pos->bounce_page,
+				mempool_free(pos->w.bounce_page,
 						f2fs_bounce_page_pool);
 		}
 		if (pos->tfm)
@@ -335,7 +330,7 @@ void f2fs_restore_and_release_control_page(struct page **page)
 	ctx = (struct f2fs_crypto_ctx *)page_private(bounce_page);
 
 	/* restore control page */
-	*page = ctx->control_page;
+	*page = ctx->w.control_page;
 
 	f2fs_restore_control_page(bounce_page);
 }
@@ -492,8 +487,9 @@ struct page *f2fs_encrypt(struct inode *inode,
 	} else {
 		ctx->flags |= F2FS_BOUNCE_PAGE_REQUIRES_FREE_ENCRYPT_FL;
 	}
-	ctx->bounce_page = ciphertext_page;
-	ctx->control_page = plaintext_page;
+	ctx->flags |= F2FS_WRITE_PATH_FL;
+	ctx->w.bounce_page = ciphertext_page;
+	ctx->w.control_page = plaintext_page;
 	err = f2fs_page_crypto(ctx, inode, F2FS_ENCRYPT, plaintext_page->index,
 					plaintext_page, ciphertext_page);
 	if (err) {
fs/f2fs/f2fs_crypto.h +14 -7
index 58682d2..e33cec9 100644
@@ -87,16 +87,23 @@ struct f2fs_crypt_info {
 
 #define F2FS_CTX_REQUIRES_FREE_ENCRYPT_FL             0x00000001
 #define F2FS_BOUNCE_PAGE_REQUIRES_FREE_ENCRYPT_FL     0x00000002
+#define F2FS_WRITE_PATH_FL			      0x00000004
 
 struct f2fs_crypto_ctx {
 	struct crypto_tfm *tfm;         /* Crypto API context */
-	struct page *bounce_page;       /* Ciphertext page on write path */
-	struct page *control_page;      /* Original page on write path */
-	struct bio *bio;                /* The bio for this context */
-	struct work_struct work;        /* Work queue for read complete path */
-	struct list_head free_list;     /* Free list */
-	int flags;                      /* Flags */
-	int mode;                       /* Encryption mode for tfm */
+	union {
+		struct {
+			struct page *bounce_page;       /* Ciphertext page */
+			struct page *control_page;      /* Original page  */
+		} w;
+		struct {
+			struct bio *bio;
+			struct work_struct work;
+		} r;
+		struct list_head free_list;     /* Free list */
+	};
+	char flags;                      /* Flags */
+	char mode;                       /* Encryption mode for tfm */
 };
 
 struct f2fs_completion_result {
fs/f2fs/segment.c +1 -1
index 17e89ba..70d1ccb 100644
@@ -1369,7 +1369,7 @@ static inline bool is_merged_page(struct f2fs_sb_info *sbi,
 			/* encrypted page */
 			ctx = (struct f2fs_crypto_ctx *)page_private(
 								bvec->bv_page);
-			target = ctx->control_page;
+			target = ctx->w.control_page;
 		}
 
 		if (page == target) {
Signature · 9 lines
2.1.1


------------------------------------------------------------------------------
One dashboard for servers and applications across Physical-Virtual-Cloud 
Widest out-of-the-box monitoring support with 50+ applications
Performance metrics, stats and reports that give you Actionable Insights
Deep dive visibility with transaction tracing using APM Insight.
http://ad.doubleclick.net/ddm/clk/290420510;117567292;y
author maintainer
#5 lore raw

PATCH 6/6 f2fs crypto: require CONFIG_CRYPTO_CTR if f2fs encryption is enabled

In reply to Jaegeuk Kim

This patch integrates the below patch into f2fs.

"ext4 crypto: require CONFIG_CRYPTO_CTR if ext4 encryption is enabled

On arm64 this is apparently needed for CTS mode to function correctly. Otherwise attempts to use CTS return ENOENT."

---
 fs/f2fs/Kconfig | 1 +
 1 file changed, 1 insertion(+)
fs/f2fs/Kconfig +1 -0
index 28f21fe..c629762 100644
@@ -81,6 +81,7 @@ config F2FS_FS_ENCRYPTION
 	select CRYPTO_ECB
 	select CRYPTO_XTS
 	select CRYPTO_CTS
+	select CRYPTO_CTR
 	select CRYPTO_SHA256
 	select KEYS
 	select ENCRYPTED_KEYS
Signature · 9 lines
2.1.1


------------------------------------------------------------------------------
One dashboard for servers and applications across Physical-Virtual-Cloud 
Widest out-of-the-box monitoring support with 50+ applications
Performance metrics, stats and reports that give you Actionable Insights
Deep dive visibility with transaction tracing using APM Insight.
http://ad.doubleclick.net/ddm/clk/290420510;117567292;y