diff --git a/fs/overlayfs/Makefile b/fs/overlayfs/Makefile
index 9164c585eb2f..db555c0e4508 100644
--- a/fs/overlayfs/Makefile
+++ b/fs/overlayfs/Makefile
@@ -7,3 +7,4 @@ obj-$(CONFIG_OVERLAY_FS) += overlay.o
 
 overlay-objs := super.o namei.o util.o inode.o file.o dir.o readdir.o \
 		copy_up.o export.o
+overlay-$(CONFIG_FSINFO)	+= fsinfo.o
diff --git a/fs/overlayfs/fsinfo.c b/fs/overlayfs/fsinfo.c
new file mode 100644
index 000000000000..9857949dcce5
--- /dev/null
+++ b/fs/overlayfs/fsinfo.c
@@ -0,0 +1,133 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Filesystem information for overlayfs
+ *
+ * Copyright (C) 2020 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ */
+
+#include <linux/mount.h>
+#include <linux/fsinfo.h>
+#include "overlayfs.h"
+
+static int __ovl_encode_mnt_opt_fh(struct fsinfo_ovl_source *p,
+				   struct dentry *dentry)
+{
+	int fh_type, dwords;
+	int buflen = MAX_HANDLE_SZ;
+	int err;
+
+	/* we ask for a non connected handle */
+	dwords = buflen >> 2;
+	fh_type = exportfs_encode_fh(dentry, (void *)p->fh.f_handle, &dwords, 0);
+	buflen = (dwords << 2);
+
+	err = -EIO;
+	if (WARN_ON(fh_type < 0) ||
+	    WARN_ON(buflen > MAX_HANDLE_SZ) ||
+	    WARN_ON(fh_type == FILEID_INVALID))
+		goto out_err;
+
+	p->fh.handle_type = fh_type;
+	p->fh.handle_bytes = buflen;
+
+	/*
+	 * Ideally, we want to have mnt_id+fhandle, but overlayfs not
+	 * keep refcnts on layers mounts and we couldn't determine
+	 * mnt_ids for layers. So, let's give s_dev to CRIU.
+	 * It's better than nothing.
+	 */
+	p->s_dev = dentry->d_sb->s_dev;
+
+	return 0;
+
+out_err:
+	return err;
+}
+
+static int ovl_fsinfo_store_source(struct fsinfo_ovl_source *p,
+				   enum fsinfo_ovl_source_type type,
+				   struct dentry *dentry)
+{
+	__ovl_encode_mnt_opt_fh(p, dentry);
+	p->type = type;
+	return 0;
+}
+
+static long ovl_ioctl_stor_lower_fhandle(struct fsinfo_ovl_source *p,
+					 struct super_block *sb,
+					 unsigned long arg)
+{
+	struct ovl_entry *oe = sb->s_root->d_fsdata;
+	struct dentry *origin;
+
+	if (arg >= oe->numlower)
+		return -EINVAL;
+
+	origin = oe->lowerstack[arg].dentry;
+
+	return ovl_fsinfo_store_source(p, FSINFO_OVL_LWR, origin);
+}
+
+static long ovl_ioctl_stor_upper_fhandle(struct fsinfo_ovl_source *p,
+					 struct super_block *sb)
+{
+	struct ovl_fs *ofs = sb->s_fs_info;
+	struct dentry *origin;
+
+	if (!ofs->config.upperdir)
+		return -EINVAL;
+
+	origin = OVL_I(d_inode(sb->s_root))->__upperdentry;
+
+	return ovl_fsinfo_store_source(p, FSINFO_OVL_UPPR, origin);
+}
+
+static long ovl_ioctl_stor_work_fhandle(struct fsinfo_ovl_source *p,
+					struct super_block *sb)
+{
+	struct ovl_fs *ofs = sb->s_fs_info;
+
+	if (!ofs->config.upperdir)
+		return -EINVAL;
+
+	return ovl_fsinfo_store_source(p, FSINFO_OVL_WRK, ofs->workbasedir);
+}
+
+static int ovl_fsinfo_sources(struct path *path, struct fsinfo_context *ctx)
+{
+	struct fsinfo_ovl_source *p = ctx->buffer;
+	struct super_block *sb = path->dentry->d_sb;
+	struct ovl_fs *ofs = sb->s_fs_info;
+	struct ovl_entry *oe = sb->s_root->d_fsdata;
+	size_t nr_sources = (oe->numlower + 2 * !!ofs->config.upperdir);
+	unsigned int i = 0, j;
+	int ret = -ENODATA;
+
+	ret = nr_sources * sizeof(*p);
+	if (ret <= ctx->buf_size) {
+		if (ofs->config.upperdir) {
+			ovl_ioctl_stor_upper_fhandle(&p[i++], sb);
+			ovl_ioctl_stor_work_fhandle(&p[i++], sb);
+		}
+
+		for (j = 0; j < oe->numlower; j++)
+			ovl_ioctl_stor_lower_fhandle(&p[i++], sb, j);
+	}
+
+	return ret;
+}
+
+static const struct fsinfo_attribute ovl_fsinfo_attributes[] = {
+	/* TODO: implement FSINFO_ATTR_SUPPORTS and FSINFO_ATTR_FEATURES */
+	/*
+	FSINFO_VSTRUCT	(FSINFO_ATTR_SUPPORTS,		ovl_fsinfo_supports),
+	FSINFO_VSTRUCT	(FSINFO_ATTR_FEATURES,		ovl_fsinfo_features),
+	*/
+	FSINFO_LIST	(FSINFO_ATTR_OVL_SOURCES,	ovl_fsinfo_sources),
+	{}
+};
+
+int ovl_fsinfo(struct path *path, struct fsinfo_context *ctx)
+{
+	return fsinfo_get_attribute(path, ctx, ovl_fsinfo_attributes);
+}
diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h
index 29bc1ec699e7..1c0ac23ecf8f 100644
--- a/fs/overlayfs/overlayfs.h
+++ b/fs/overlayfs/overlayfs.h
@@ -7,6 +7,7 @@
 #include <linux/kernel.h>
 #include <linux/uuid.h>
 #include <linux/fs.h>
+#include <linux/xattr.h>
 #include "ovl_entry.h"
 
 #undef pr_fmt
@@ -492,3 +493,8 @@ int ovl_set_origin(struct dentry *dentry, struct dentry *lower,
 
 /* export.c */
 extern const struct export_operations ovl_export_operations;
+
+/* fsinfo.c */
+#ifdef CONFIG_FSINFO
+extern int ovl_fsinfo(struct path *path, struct fsinfo_context *ctx);
+#endif
diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c
index 4b38141c2985..1a4cdbbd766f 100644
--- a/fs/overlayfs/super.c
+++ b/fs/overlayfs/super.c
@@ -392,6 +392,9 @@ static const struct super_operations ovl_super_operations = {
 	.put_super	= ovl_put_super,
 	.sync_fs	= ovl_sync_fs,
 	.statfs		= ovl_statfs,
+#ifdef CONFIG_FSINFO
+	.fsinfo		= ovl_fsinfo,
+#endif
 	.show_options	= ovl_show_options,
 	.remount_fs	= ovl_remount,
 };
diff --git a/include/uapi/linux/fsinfo.h b/include/uapi/linux/fsinfo.h
index dcd764771a7d..83c2511691e4 100644
--- a/include/uapi/linux/fsinfo.h
+++ b/include/uapi/linux/fsinfo.h
@@ -10,6 +10,8 @@
 #include <linux/types.h>
 #include <linux/socket.h>
 #include <linux/openat2.h>
+#include <linux/fs.h>
+#include <linux/exportfs.h>
 
 /*
  * The filesystem attributes that can be requested.  Note that some attributes
@@ -44,6 +46,8 @@
 #define FSINFO_ATTR_AFS_SERVER_NAME	0x301	/* Name of the Nth server (string) */
 #define FSINFO_ATTR_AFS_SERVER_ADDRESSES 0x302	/* List of addresses of the Nth server */
 
+#define FSINFO_ATTR_OVL_SOURCES		0x400	/* List of overlayfs source dirs fhandles+sdev */
+
 /*
  * Optional fsinfo() parameter structure.
  *
@@ -341,4 +345,31 @@ struct fsinfo_error_state {
 
 #define FSINFO_ATTR_ERROR_STATE__STRUCT struct fsinfo_error_state
 
+/*
+ * Information struct for fsinfo(FSINFO_ATTR_FSINFO_ATTRIBUTE_INFO).
+ *
+ * This gives information about the overlayfs upperdir, workdir, lowerdir
+ * superblock options (exported as fhandles).
+ */
+enum fsinfo_ovl_source_type {
+	FSINFO_OVL_UPPR	= 0,	/* upperdir */
+	FSINFO_OVL_WRK	= 1,	/* workdir */
+	FSINFO_OVL_LWR	= 2,	/* lowerdir list item */
+};
+
+/* DISCUSS: we can also export mnt_unique_id here which introduced by fsinfo patchset
+ * and then use him to detect if source was unmounted in the time gap between the moment when
+ * overlayfs was mounted and C/R process was started.
+ * We can get mnt_unique_id also by using fsinfo(FSINFO_ATTR_MOUNT_ALL)
+ */
+struct fsinfo_ovl_source {
+	enum fsinfo_ovl_source_type type;
+	__u32 s_dev;
+	struct file_handle fh;
+	/* use f_handle field from struct file_handle */
+	__u8 __fhdata[MAX_HANDLE_SZ];
+};
+
+#define FSINFO_ATTR_OVL_SOURCES__STRUCT struct fsinfo_ovl_source
+
 #endif /* _UAPI_LINUX_FSINFO_H */
