diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index c61787b15f27..9b5dbdcde9e6 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -91,6 +91,7 @@
 #include <linux/bpf.h>
 #include <linux/kernfs.h>
 #include <linux/stringhash.h>	/* for hashlen_string() */
+#include <linux/fsinfo.h>
 #include <uapi/linux/mount.h>
 
 #include "avc.h"
@@ -2735,6 +2736,43 @@ static int selinux_sb_statfs(struct dentry *dentry)
 	return superblock_has_perm(cred, dentry->d_sb, FILESYSTEM__GETATTR, &ad);
 }
 
+#ifdef CONFIG_FSINFO
+/*
+ * Retrieve the SELinux filesystem information, including mount parameters.
+ */
+static int selinux_sb_fsinfo(struct path *path, struct fsinfo_kparams *params)
+{
+	struct superblock_security_struct *sbsec = path->dentry->d_sb->s_security;
+
+	switch (params->request) {
+	case FSINFO_ATTR_LSM_PARAMETERS:
+		if (!(sbsec->flags & SE_SBINITIALIZED) ||
+		    !selinux_state.initialized)
+			return params->usage;
+
+		if (sbsec->flags & FSCONTEXT_MNT)
+			fsinfo_note_sid(params, FSCONTEXT_STR, sbsec->sid);
+		if (sbsec->flags & CONTEXT_MNT)
+			fsinfo_note_sid(params, CONTEXT_STR, sbsec->mntpoint_sid);
+		if (sbsec->flags & DEFCONTEXT_MNT)
+			fsinfo_note_sid(params, DEFCONTEXT_STR, sbsec->def_sid);
+		if (sbsec->flags & ROOTCONTEXT_MNT) {
+			struct dentry *root = sbsec->sb->s_root;
+			struct inode_security_struct *isec = backing_inode_security(root);
+			fsinfo_note_sid(params, ROOTCONTEXT_STR, isec->sid);
+		}
+		if (sbsec->flags & SBLABEL_MNT)
+			fsinfo_note_param(params, SECLABEL_STR, NULL);
+
+		return params->usage;
+
+	default:
+		return -ENODATA;
+	}
+	return 0;
+}
+#endif
+
 static int selinux_mount(const char *dev_name,
 			 const struct path *path,
 			 const char *type,
@@ -6761,6 +6799,9 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = {
 	LSM_HOOK_INIT(sb_kern_mount, selinux_sb_kern_mount),
 	LSM_HOOK_INIT(sb_show_options, selinux_sb_show_options),
 	LSM_HOOK_INIT(sb_statfs, selinux_sb_statfs),
+#ifdef CONFIG_FSINFO
+	LSM_HOOK_INIT(sb_fsinfo, selinux_sb_fsinfo),
+#endif
 	LSM_HOOK_INIT(sb_mount, selinux_mount),
 	LSM_HOOK_INIT(sb_umount, selinux_umount),
 	LSM_HOOK_INIT(sb_set_mnt_opts, selinux_set_mnt_opts),
diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h
index 111121281c47..e9617bfcc6ee 100644
--- a/security/selinux/include/security.h
+++ b/security/selinux/include/security.h
@@ -67,6 +67,7 @@
 #define SECLABEL_STR "seclabel"
 
 struct netlbl_lsm_secattr;
+struct fsinfo_kparams;
 
 extern int selinux_enabled;
 
@@ -258,6 +259,7 @@ int security_sid_to_context_force(struct selinux_state *state,
 
 int security_sid_to_context_inval(struct selinux_state *state,
 				  u32 sid, char **scontext, u32 *scontext_len);
+void fsinfo_note_sid(struct fsinfo_kparams *params, const char *key, u32 sid);
 
 int security_context_to_sid(struct selinux_state *state,
 			    const char *scontext, u32 scontext_len,
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index cc043bc8fd4c..1111b02a999b 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -50,6 +50,7 @@
 #include <linux/audit.h>
 #include <linux/mutex.h>
 #include <linux/vmalloc.h>
+#include <linux/fsinfo.h>
 #include <net/netlabel.h>
 
 #include "flask.h"
@@ -1374,6 +1375,54 @@ int security_sid_to_context_inval(struct selinux_state *state, u32 sid,
 					    scontext_len, 1, 1);
 }
 
+#ifdef CONFIG_FSINFO
+void fsinfo_note_sid(struct fsinfo_kparams *params, const char *key, u32 sid)
+{
+	struct selinux_state *state = &selinux_state;
+	struct policydb *policydb;
+	struct context *context;
+	const char *val = "<<<INVALID>>>";
+	char *p;
+	int n;
+
+	if (!state->initialized) {
+		if (sid <= SECINITSID_NUM) {
+			val = initial_sid_to_string[sid];
+			goto out;
+		}
+
+		pr_err("SELinux: %s:  called before initial "
+		       "load_policy on unknown SID %d\n", __func__, sid);
+		goto out;
+	}
+
+	read_lock(&state->ss->policy_rwlock);
+
+	policydb = &state->ss->policydb;
+	context = sidtab_search(state->ss->sidtab, sid);
+	if (!context) {
+		pr_err("SELinux: %s:  unrecognized SID %d\n", __func__, sid);
+	} else {
+		/* Copy the user name, role name and type name into the scratch
+		 * buffer and then tack on the MLS.
+		 */
+		val = p = params->scratch_buffer;
+		n = sprintf(p, "%s:%s:%s",
+			    sym_name(policydb, SYM_USERS, context->user - 1),
+			    sym_name(policydb, SYM_ROLES, context->role - 1),
+			    sym_name(policydb, SYM_TYPES, context->type - 1));
+
+		p += n;
+		mls_sid_to_context(policydb, context, &p);
+		*p = 0;
+	}
+
+	read_unlock(&state->ss->policy_rwlock);
+out:
+	fsinfo_note_param(params, key, val);
+}
+#endif
+
 /*
  * Caveat:  Mutates scontext.
  */
