diff --git a/drivers/md/Makefile b/drivers/md/Makefile
index ef7ddc27685c..af349b7cbf8b 100644
--- a/drivers/md/Makefile
+++ b/drivers/md/Makefile
@@ -92,6 +92,8 @@ ifeq ($(CONFIG_DM_UEVENT),y)
 dm-mod-objs			+= dm-uevent.o
 endif
 
+dm-mod-objs			+= dm-ima.o
+
 ifeq ($(CONFIG_DM_VERITY_FEC),y)
 dm-verity-objs			+= dm-verity-fec.o
 endif
diff --git a/drivers/md/dm-core.h b/drivers/md/dm-core.h
index 5953ff2bd260..f67f898803db 100644
--- a/drivers/md/dm-core.h
+++ b/drivers/md/dm-core.h
@@ -18,6 +18,7 @@
 #include <trace/events/block.h>
 
 #include "dm.h"
+#include "dm-ima.h"
 
 #define DM_RESERVED_MAX_IOS		1024
 
@@ -114,6 +115,10 @@ struct mapped_device {
 	bool init_tio_pdu:1;
 
 	struct srcu_struct io_barrier;
+
+#ifdef CONFIG_IMA
+	struct dm_ima_measurements ima;
+#endif
 };
 
 void disable_discard(struct mapped_device *md);
diff --git a/drivers/md/dm-ima.c b/drivers/md/dm-ima.c
new file mode 100644
index 000000000000..8903e746ead6
--- /dev/null
+++ b/drivers/md/dm-ima.c
@@ -0,0 +1,219 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2021 Microsoft Corporation
+ *
+ * Author: Tushar Sugandhi <tusharsu@linux.microsoft.com>
+ *
+ * File: dm-ima.c
+ *       Enables IMA measurements for DM targets
+ */
+
+#include "dm-core.h"
+#include "dm-ima.h"
+
+#include <linux/ima.h>
+#include <linux/device-mapper.h>
+#include <crypto/hash.h>
+#include <linux/crypto.h>
+#include <crypto/hash_info.h>
+
+#define DM_MSG_PREFIX "dm-ima"
+
+#ifdef CONFIG_IMA
+/*
+ * Internal function to prefix separator characters in input buffer with escape
+ * character, so that they don't interfere with the construction of key-value pairs,
+ * and clients can split the key1=value1;key2=value2; pairs properly.
+ */
+void fix_separator_chars(char **buf)
+{
+	int l = strlen(*buf);
+	int i, j, sp = 0;
+
+	for (i = 0; i < l; i++)
+		if ((*buf)[i] == '\\' || (*buf)[i] == ';' || (*buf)[i] == '=')
+			sp++;
+
+	if (sp == 0)
+		return;
+
+	for (i = l-1, j = i+sp; i >= 0; i--) {
+		(*buf)[j--] = (*buf)[i];
+		if ((*buf)[i] == '\\' || (*buf)[i] == ';' || (*buf)[i] == '=')
+			(*buf)[j--] = '\\';
+	}
+}
+
+/*
+ * Internal wrapper function to call IMA to measure DM data.
+ */
+void dm_ima_measure_data(const char *event_name, const void *buf, size_t buf_len)
+{
+	unsigned int noio_flag;
+
+	noio_flag = memalloc_noio_save();
+	ima_measure_critical_data(DM_NAME, event_name, buf, buf_len, false);
+	memalloc_noio_restore(noio_flag);
+}
+
+/*
+ * Build up the IMA data for each target, and finally measure.
+ */
+void dm_ima_measure_on_table_load(struct dm_table *table, unsigned int status_flags)
+{
+	char *ima_buf_head = NULL, *ima_buf_cur = NULL, *device_data = NULL, *digest_buf = NULL;
+	size_t total_buf_len = DM_IMA_DEVICE_BUF_LEN + DM_IMA_TABLE_BUF_LEN;
+	unsigned int num_targets, device_data_len, i;
+	int desc_size, digest_size, last_i = -1, r;
+	char *dev_name = NULL, *dev_uuid = NULL;
+	status_type_t type = STATUSTYPE_IMA;
+	struct shash_desc *desc = NULL;
+	struct crypto_shash *tfm;
+	long ima_remaining;
+	sector_t capacity;
+	u8 *digest = NULL;
+
+	dev_name = kzalloc(DM_NAME_LEN*2, GFP_KERNEL);
+	if (!dev_name)
+		goto error;
+
+	dev_uuid = kzalloc(DM_UUID_LEN*2, GFP_KERNEL);
+	if (!dev_uuid)
+		goto error;
+
+	if (dm_copy_name_and_uuid(table->md, dev_name, dev_uuid))
+		goto error;
+
+	fix_separator_chars(&dev_name);
+	fix_separator_chars(&dev_uuid);
+
+	ima_buf_head = ima_buf_cur = kzalloc(total_buf_len, GFP_KERNEL);
+	if (!ima_buf_head)
+		goto error;
+
+	device_data = kzalloc(DM_IMA_DEVICE_BUF_LEN, GFP_KERNEL);
+	if (!device_data)
+		goto error;
+
+	tfm = crypto_alloc_shash("sha256", 0, 0);
+	if (IS_ERR(tfm))
+		goto error;
+
+	desc_size = crypto_shash_descsize(tfm) + sizeof(*desc);
+	digest_size = crypto_shash_digestsize(tfm);
+
+	digest = kzalloc(digest_size, GFP_KERNEL);
+	if (!digest)
+		goto error;
+
+	desc = kzalloc(desc_size, GFP_KERNEL);
+	if (!desc)
+		goto error;
+
+	desc->tfm = tfm;
+
+	num_targets = dm_table_get_num_targets(table);
+	capacity = get_capacity(table->md->disk);
+
+	scnprintf(device_data, DM_IMA_DEVICE_BUF_LEN,
+		  "name=%s;uuid=%s;capacity=%llu;major=%d;minor=%d;minor_count=%d;num_targets=%u;",
+		  dev_name, dev_uuid, capacity, table->md->disk->major,
+		  table->md->disk->first_minor, table->md->disk->minors, num_targets);
+
+	device_data_len = strlen(device_data);
+	memcpy(ima_buf_cur, device_data, device_data_len);
+	ima_buf_cur += device_data_len;
+
+	for (i = 0; i < num_targets; i++) {
+		struct dm_target *ti = dm_table_get_target(table, i);
+		size_t l = 0;
+
+		ima_remaining = total_buf_len - (ima_buf_cur - ima_buf_head);
+
+		if (ti->type->status)
+			ti->type->status(ti, type, status_flags, ima_buf_cur, ima_remaining);
+		else
+			ima_buf_cur[0] = '\0';
+
+		l = strlen(ima_buf_cur);
+
+		ima_remaining -= (l+1);
+
+		if (ima_remaining > 0) {
+			ima_buf_cur += l;
+			last_i = -1;
+			continue;
+		}
+
+		if (unlikely(i == last_i)) {
+		/* IMA measurements for DM targets are best-effort.
+		 * If the IMA data from current target type is too large to fit
+		 * into total_buf_len, measure what we have in the current buffer, and bail-out.
+		 */
+			DMERR("IMA data for %s target too large.", ti->type->name);
+			break;
+		}
+		/* The buffer ima_buf_head is full if we reach here.
+		 * In this case, measure the current buffer, free it, and reallocate a new buffer,
+		 * and continue measuring the remaining targets in the table.
+		 */
+		ima_buf_cur[0] = '\0';
+		dm_ima_measure_data("table_load", ima_buf_head, strlen(ima_buf_head));
+		crypto_shash_update(desc, (const u8 *)ima_buf_head, strlen(ima_buf_head));
+		kfree(ima_buf_head);
+		ima_buf_head = ima_buf_cur = kzalloc(total_buf_len, GFP_KERNEL);
+		if (!ima_buf_head)
+			goto error;
+		/*
+		 * Each new "table_load" entry in IMA log should have device data prefix,
+		 * so that multiple records from the same table load can be linked together.
+		 */
+		memcpy(ima_buf_cur, device_data, device_data_len);
+		ima_buf_cur += device_data_len;
+
+		last_i = i;
+		i--;
+	}
+
+	dm_ima_measure_data("table_load", ima_buf_head, strlen(ima_buf_head));
+
+	kfree(table->md->ima.inactive_table_hash);
+	table->md->ima.inactive_table_hash = NULL;
+	table->md->ima.inactive_table_hash_len = 0;
+
+	r = crypto_shash_update(desc, (const u8 *)ima_buf_head, strlen(ima_buf_head));
+	if (r < 0)
+		goto error;
+
+	r = crypto_shash_final(desc, digest);
+	if (r < 0)
+		goto error;
+
+	digest_buf = kzalloc((digest_size*2)+1, GFP_KERNEL);
+	if (!digest_buf)
+		goto error;
+
+	for (i = 0; i < digest_size; i++)
+		snprintf((digest_buf+(i*2)), 3, "%02x", digest[i]);
+
+	table->md->ima.inactive_table_hash = digest_buf;
+	table->md->ima.inactive_table_hash_len = strlen(digest_buf);
+	table->md->ima.device_data = device_data;
+	table->md->ima.device_data_len = device_data_len;
+	table->md->ima.num_targets = num_targets;
+
+error:
+	kfree(dev_name);
+	kfree(dev_uuid);
+	kfree(digest);
+	kfree(desc);
+	crypto_free_shash(tfm);
+	kfree(ima_buf_head);
+}
+
+#else
+void dm_ima_measure_on_table_load(struct dm_table *table, unsigned int status_flags) {}
+#endif
+MODULE_AUTHOR("Tushar Sugandhi <tusharsu@linux.microsoft.com>");
+MODULE_DESCRIPTION("Enables IMA measurements for DM targets");
+MODULE_LICENSE("GPL");
diff --git a/drivers/md/dm-ima.h b/drivers/md/dm-ima.h
new file mode 100644
index 000000000000..a71e0682fcb9
--- /dev/null
+++ b/drivers/md/dm-ima.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright (C) 2021 Microsoft Corporation
+ *
+ * Author: Tushar Sugandhi <tusharsu@linux.microsoft.com>
+ *
+ * File: dm-ima.h
+ *       Header file for device mapper IMA measurements.
+ */
+
+#ifndef DM_IMA_H
+#define DM_IMA_H
+
+#define DM_IMA_TABLE_BUF_LEN 4096
+#define DM_IMA_DEVICE_BUF_LEN 1024
+
+struct dm_ima_measurements {
+	/* Contains data specific to the device which is common across
+	 * all the targets in the table.e.g. name, uuid, major, minor, capacity etc.
+	 * The values are stored in semicolon separated key=value; pairs.
+	 */
+	char *device_data;
+	unsigned int device_data_len;
+	unsigned int num_targets;
+
+	/* Contains the sha256 hashs of the IMA measurements of the
+	 * target attributes key-value pairs from the active/inactive tables.
+	 */
+	char *active_table_hash;
+	unsigned int active_table_hash_len;
+	char *inactive_table_hash;
+	unsigned int inactive_table_hash_len;
+};
+
+void dm_ima_measure_on_table_load(struct dm_table *table, unsigned int status_flags);
+#endif /*DM_IMA_H*/
diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c
index 2209cbcd84db..21deff8513a7 100644
--- a/drivers/md/dm-ioctl.c
+++ b/drivers/md/dm-ioctl.c
@@ -21,6 +21,9 @@
 
 #include <linux/uaccess.h>
 
+#include <linux/ima.h>
+#include "dm-ima.h"
+
 #define DM_MSG_PREFIX "ioctl"
 #define DM_DRIVER_EMAIL "dm-devel@redhat.com"
 
@@ -1224,6 +1227,8 @@ static void retrieve_status(struct dm_table *table,
 
 	if (param->flags & DM_STATUS_TABLE_FLAG)
 		type = STATUSTYPE_TABLE;
+	else if (param->flags & DM_IMA_MEASUREMENT_FLAG)
+		type = STATUSTYPE_IMA;
 	else
 		type = STATUSTYPE_INFO;
 
@@ -1391,6 +1396,8 @@ static int populate_table(struct dm_table *table,
 		next = spec->next;
 	}
 
+	dm_ima_measure_on_table_load(table, STATUSTYPE_IMA);
+
 	return dm_table_complete(table);
 }
 
diff --git a/include/linux/device-mapper.h b/include/linux/device-mapper.h
index ff700fb6ce1d..738a7d023650 100644
--- a/include/linux/device-mapper.h
+++ b/include/linux/device-mapper.h
@@ -31,7 +31,7 @@ enum dm_queue_mode {
 	DM_TYPE_DAX_BIO_BASED	 = 3,
 };
 
-typedef enum { STATUSTYPE_INFO, STATUSTYPE_TABLE } status_type_t;
+typedef enum { STATUSTYPE_INFO, STATUSTYPE_TABLE, STATUSTYPE_IMA } status_type_t;
 
 union map_info {
 	void *ptr;
diff --git a/include/uapi/linux/dm-ioctl.h b/include/uapi/linux/dm-ioctl.h
index e5c6e458bdf7..c12ce30b52df 100644
--- a/include/uapi/linux/dm-ioctl.h
+++ b/include/uapi/linux/dm-ioctl.h
@@ -376,4 +376,10 @@ enum {
  */
 #define DM_INTERNAL_SUSPEND_FLAG	(1 << 18) /* Out */
 
+/*
+ * If set, returns in the in buffer passed by UM, the raw table information
+ * that would be measured by IMA subsystem on device state change.
+ */
+#define DM_IMA_MEASUREMENT_FLAG	(1 << 19) /* In */
+
 #endif				/* _LINUX_DM_IOCTL_H */
