diff mbox series

fs: add the ability for statmount() to report the fs_subtype

Message ID 20241106-statmount-v1-1-b93bafd97621@kernel.org (mailing list archive)
State New
Headers show
Series fs: add the ability for statmount() to report the fs_subtype | expand

Commit Message

Jeff Layton Nov. 6, 2024, 1:29 p.m. UTC
/proc/self/mountinfo prints out the sb->s_subtype after the type. In
particular, FUSE makes use of this to display the fstype as
fuse.<subtype>.

Add STATMOUNT_FS_SUBTYPE and claim one of the __spare2 fields to point
to the offset into the str[] array. The STATMOUNT_FS_SUBTYPE will only
be set in the return mask if there is a subtype associated with the
mount.

Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
 fs/namespace.c             | 20 +++++++++++++++++++-
 include/uapi/linux/mount.h |  5 ++++-
 2 files changed, 23 insertions(+), 2 deletions(-)


---
base-commit: 26213e1a6caa5a7f508b919059b0122b451f4dfe
change-id: 20241106-statmount-3f91a7ed75fa

Best regards,

Comments

Jan Kara Nov. 6, 2024, 1:37 p.m. UTC | #1
On Wed 06-11-24 08:29:19, Jeff Layton wrote:
> /proc/self/mountinfo prints out the sb->s_subtype after the type. In
> particular, FUSE makes use of this to display the fstype as
> fuse.<subtype>.
> 
> Add STATMOUNT_FS_SUBTYPE and claim one of the __spare2 fields to point
> to the offset into the str[] array. The STATMOUNT_FS_SUBTYPE will only
> be set in the return mask if there is a subtype associated with the
> mount.
> 
> Signed-off-by: Jeff Layton <jlayton@kernel.org>

Looks good to me. I'm just curious: Do you have any particular user that is
interested in getting subtype from statmount(2)?

								Honza

> ---
>  fs/namespace.c             | 20 +++++++++++++++++++-
>  include/uapi/linux/mount.h |  5 ++++-
>  2 files changed, 23 insertions(+), 2 deletions(-)
> 
> diff --git a/fs/namespace.c b/fs/namespace.c
> index ba77ce1c6788dfe461814b5826fcbb3aab68fad4..5f2fb692449a9c0a15b60549fb9f7bedd10f1f3d 100644
> --- a/fs/namespace.c
> +++ b/fs/namespace.c
> @@ -5006,6 +5006,14 @@ static int statmount_fs_type(struct kstatmount *s, struct seq_file *seq)
>  	return 0;
>  }
>  
> +static int statmount_fs_subtype(struct kstatmount *s, struct seq_file *seq)
> +{
> +	struct super_block *sb = s->mnt->mnt_sb;
> +
> +	seq_puts(seq, sb->s_subtype);
> +	return 0;
> +}
> +
>  static void statmount_mnt_ns_id(struct kstatmount *s, struct mnt_namespace *ns)
>  {
>  	s->sm.mask |= STATMOUNT_MNT_NS_ID;
> @@ -5064,6 +5072,13 @@ static int statmount_string(struct kstatmount *s, u64 flag)
>  		sm->mnt_opts = seq->count;
>  		ret = statmount_mnt_opts(s, seq);
>  		break;
> +	case STATMOUNT_FS_SUBTYPE:
> +		/* ignore if no s_subtype */
> +		if (!s->mnt->mnt_sb->s_subtype)
> +			return 0;
> +		sm->fs_subtype = seq->count;
> +		ret = statmount_fs_subtype(s, seq);
> +		break;
>  	default:
>  		WARN_ON_ONCE(true);
>  		return -EINVAL;
> @@ -5203,6 +5218,9 @@ static int do_statmount(struct kstatmount *s, u64 mnt_id, u64 mnt_ns_id,
>  	if (!err && s->mask & STATMOUNT_MNT_OPTS)
>  		err = statmount_string(s, STATMOUNT_MNT_OPTS);
>  
> +	if (!err && s->mask & STATMOUNT_FS_SUBTYPE)
> +		err = statmount_string(s, STATMOUNT_FS_SUBTYPE);
> +
>  	if (!err && s->mask & STATMOUNT_MNT_NS_ID)
>  		statmount_mnt_ns_id(s, ns);
>  
> @@ -5224,7 +5242,7 @@ static inline bool retry_statmount(const long ret, size_t *seq_size)
>  }
>  
>  #define STATMOUNT_STRING_REQ (STATMOUNT_MNT_ROOT | STATMOUNT_MNT_POINT | \
> -			      STATMOUNT_FS_TYPE | STATMOUNT_MNT_OPTS)
> +			      STATMOUNT_FS_TYPE | STATMOUNT_MNT_OPTS | STATMOUNT_FS_SUBTYPE)
>  
>  static int prepare_kstatmount(struct kstatmount *ks, struct mnt_id_req *kreq,
>  			      struct statmount __user *buf, size_t bufsize,
> diff --git a/include/uapi/linux/mount.h b/include/uapi/linux/mount.h
> index 225bc366ffcbf0319929e2f55f1fbea88e4d7b81..fa206fb56b3b25cf80f7d430e1b6bab19c3220e4 100644
> --- a/include/uapi/linux/mount.h
> +++ b/include/uapi/linux/mount.h
> @@ -173,7 +173,9 @@ struct statmount {
>  	__u32 mnt_root;		/* [str] Root of mount relative to root of fs */
>  	__u32 mnt_point;	/* [str] Mountpoint relative to current root */
>  	__u64 mnt_ns_id;	/* ID of the mount namespace */
> -	__u64 __spare2[49];
> +	__u32 fs_subtype;	/* [str] Subtype of fs_type (if any) */
> +	__u32 __spare1[1];
> +	__u64 __spare2[48];
>  	char str[];		/* Variable size part containing strings */
>  };
>  
> @@ -207,6 +209,7 @@ struct mnt_id_req {
>  #define STATMOUNT_FS_TYPE		0x00000020U	/* Want/got fs_type */
>  #define STATMOUNT_MNT_NS_ID		0x00000040U	/* Want/got mnt_ns_id */
>  #define STATMOUNT_MNT_OPTS		0x00000080U	/* Want/got mnt_opts */
> +#define STATMOUNT_FS_SUBTYPE		0x00000100U	/* Want/got subtype */
>  
>  /*
>   * Special @mnt_id values that can be passed to listmount
> 
> ---
> base-commit: 26213e1a6caa5a7f508b919059b0122b451f4dfe
> change-id: 20241106-statmount-3f91a7ed75fa
> 
> Best regards,
> -- 
> Jeff Layton <jlayton@kernel.org>
>
Jeff Layton Nov. 6, 2024, 1:56 p.m. UTC | #2
On Wed, 2024-11-06 at 14:37 +0100, Jan Kara wrote:
> On Wed 06-11-24 08:29:19, Jeff Layton wrote:
> > /proc/self/mountinfo prints out the sb->s_subtype after the type. In
> > particular, FUSE makes use of this to display the fstype as
> > fuse.<subtype>.
> > 
> > Add STATMOUNT_FS_SUBTYPE and claim one of the __spare2 fields to point
> > to the offset into the str[] array. The STATMOUNT_FS_SUBTYPE will only
> > be set in the return mask if there is a subtype associated with the
> > mount.
> > 
> > Signed-off-by: Jeff Layton <jlayton@kernel.org>
> 
> Looks good to me. I'm just curious: Do you have any particular user that is
> interested in getting subtype from statmount(2)?
> 
> 

Thanks! Can I count that as a R-b?

Meta does. They have some logging internally that tracks mounts, and
knowing what sort of FUSE mounts they have is useful info (when the
driver bothers to fill out the field anyway).

> 
> > ---
> >  fs/namespace.c             | 20 +++++++++++++++++++-
> >  include/uapi/linux/mount.h |  5 ++++-
> >  2 files changed, 23 insertions(+), 2 deletions(-)
> > 
> > diff --git a/fs/namespace.c b/fs/namespace.c
> > index ba77ce1c6788dfe461814b5826fcbb3aab68fad4..5f2fb692449a9c0a15b60549fb9f7bedd10f1f3d 100644
> > --- a/fs/namespace.c
> > +++ b/fs/namespace.c
> > @@ -5006,6 +5006,14 @@ static int statmount_fs_type(struct kstatmount *s, struct seq_file *seq)
> >  	return 0;
> >  }
> >  
> > +static int statmount_fs_subtype(struct kstatmount *s, struct seq_file *seq)
> > +{
> > +	struct super_block *sb = s->mnt->mnt_sb;
> > +
> > +	seq_puts(seq, sb->s_subtype);
> > +	return 0;
> > +}
> > +
> >  static void statmount_mnt_ns_id(struct kstatmount *s, struct mnt_namespace *ns)
> >  {
> >  	s->sm.mask |= STATMOUNT_MNT_NS_ID;
> > @@ -5064,6 +5072,13 @@ static int statmount_string(struct kstatmount *s, u64 flag)
> >  		sm->mnt_opts = seq->count;
> >  		ret = statmount_mnt_opts(s, seq);
> >  		break;
> > +	case STATMOUNT_FS_SUBTYPE:
> > +		/* ignore if no s_subtype */
> > +		if (!s->mnt->mnt_sb->s_subtype)
> > +			return 0;
> > +		sm->fs_subtype = seq->count;
> > +		ret = statmount_fs_subtype(s, seq);
> > +		break;
> >  	default:
> >  		WARN_ON_ONCE(true);
> >  		return -EINVAL;
> > @@ -5203,6 +5218,9 @@ static int do_statmount(struct kstatmount *s, u64 mnt_id, u64 mnt_ns_id,
> >  	if (!err && s->mask & STATMOUNT_MNT_OPTS)
> >  		err = statmount_string(s, STATMOUNT_MNT_OPTS);
> >  
> > +	if (!err && s->mask & STATMOUNT_FS_SUBTYPE)
> > +		err = statmount_string(s, STATMOUNT_FS_SUBTYPE);
> > +
> >  	if (!err && s->mask & STATMOUNT_MNT_NS_ID)
> >  		statmount_mnt_ns_id(s, ns);
> >  
> > @@ -5224,7 +5242,7 @@ static inline bool retry_statmount(const long ret, size_t *seq_size)
> >  }
> >  
> >  #define STATMOUNT_STRING_REQ (STATMOUNT_MNT_ROOT | STATMOUNT_MNT_POINT | \
> > -			      STATMOUNT_FS_TYPE | STATMOUNT_MNT_OPTS)
> > +			      STATMOUNT_FS_TYPE | STATMOUNT_MNT_OPTS | STATMOUNT_FS_SUBTYPE)
> >  
> >  static int prepare_kstatmount(struct kstatmount *ks, struct mnt_id_req *kreq,
> >  			      struct statmount __user *buf, size_t bufsize,
> > diff --git a/include/uapi/linux/mount.h b/include/uapi/linux/mount.h
> > index 225bc366ffcbf0319929e2f55f1fbea88e4d7b81..fa206fb56b3b25cf80f7d430e1b6bab19c3220e4 100644
> > --- a/include/uapi/linux/mount.h
> > +++ b/include/uapi/linux/mount.h
> > @@ -173,7 +173,9 @@ struct statmount {
> >  	__u32 mnt_root;		/* [str] Root of mount relative to root of fs */
> >  	__u32 mnt_point;	/* [str] Mountpoint relative to current root */
> >  	__u64 mnt_ns_id;	/* ID of the mount namespace */
> > -	__u64 __spare2[49];
> > +	__u32 fs_subtype;	/* [str] Subtype of fs_type (if any) */
> > +	__u32 __spare1[1];
> > +	__u64 __spare2[48];
> >  	char str[];		/* Variable size part containing strings */
> >  };
> >  
> > @@ -207,6 +209,7 @@ struct mnt_id_req {
> >  #define STATMOUNT_FS_TYPE		0x00000020U	/* Want/got fs_type */
> >  #define STATMOUNT_MNT_NS_ID		0x00000040U	/* Want/got mnt_ns_id */
> >  #define STATMOUNT_MNT_OPTS		0x00000080U	/* Want/got mnt_opts */
> > +#define STATMOUNT_FS_SUBTYPE		0x00000100U	/* Want/got subtype */
> >  
> >  /*
> >   * Special @mnt_id values that can be passed to listmount
> > 
> > ---
> > base-commit: 26213e1a6caa5a7f508b919059b0122b451f4dfe
> > change-id: 20241106-statmount-3f91a7ed75fa
> > 
> > Best regards,
> > -- 
> > Jeff Layton <jlayton@kernel.org>
> >
Jan Kara Nov. 6, 2024, 4:39 p.m. UTC | #3
On Wed 06-11-24 08:56:15, Jeff Layton wrote:
> On Wed, 2024-11-06 at 14:37 +0100, Jan Kara wrote:
> > On Wed 06-11-24 08:29:19, Jeff Layton wrote:
> > > /proc/self/mountinfo prints out the sb->s_subtype after the type. In
> > > particular, FUSE makes use of this to display the fstype as
> > > fuse.<subtype>.
> > > 
> > > Add STATMOUNT_FS_SUBTYPE and claim one of the __spare2 fields to point
> > > to the offset into the str[] array. The STATMOUNT_FS_SUBTYPE will only
> > > be set in the return mask if there is a subtype associated with the
> > > mount.
> > > 
> > > Signed-off-by: Jeff Layton <jlayton@kernel.org>
> > 
> > Looks good to me. I'm just curious: Do you have any particular user that is
> > interested in getting subtype from statmount(2)?
> > 
> > 
> 
> Thanks! Can I count that as a R-b?

Yes. Feel free to add:

Reviewed-by: Jan Kara <jack@suse.cz>

> Meta does. They have some logging internally that tracks mounts, and
> knowing what sort of FUSE mounts they have is useful info (when the
> driver bothers to fill out the field anyway).

OK, I thought it's something like that. It would be nice to mention the
usecase in the changelog.

								Honza


> > >  fs/namespace.c             | 20 +++++++++++++++++++-
> > >  include/uapi/linux/mount.h |  5 ++++-
> > >  2 files changed, 23 insertions(+), 2 deletions(-)
> > > 
> > > diff --git a/fs/namespace.c b/fs/namespace.c
> > > index ba77ce1c6788dfe461814b5826fcbb3aab68fad4..5f2fb692449a9c0a15b60549fb9f7bedd10f1f3d 100644
> > > --- a/fs/namespace.c
> > > +++ b/fs/namespace.c
> > > @@ -5006,6 +5006,14 @@ static int statmount_fs_type(struct kstatmount *s, struct seq_file *seq)
> > >  	return 0;
> > >  }
> > >  
> > > +static int statmount_fs_subtype(struct kstatmount *s, struct seq_file *seq)
> > > +{
> > > +	struct super_block *sb = s->mnt->mnt_sb;
> > > +
> > > +	seq_puts(seq, sb->s_subtype);
> > > +	return 0;
> > > +}
> > > +
> > >  static void statmount_mnt_ns_id(struct kstatmount *s, struct mnt_namespace *ns)
> > >  {
> > >  	s->sm.mask |= STATMOUNT_MNT_NS_ID;
> > > @@ -5064,6 +5072,13 @@ static int statmount_string(struct kstatmount *s, u64 flag)
> > >  		sm->mnt_opts = seq->count;
> > >  		ret = statmount_mnt_opts(s, seq);
> > >  		break;
> > > +	case STATMOUNT_FS_SUBTYPE:
> > > +		/* ignore if no s_subtype */
> > > +		if (!s->mnt->mnt_sb->s_subtype)
> > > +			return 0;
> > > +		sm->fs_subtype = seq->count;
> > > +		ret = statmount_fs_subtype(s, seq);
> > > +		break;
> > >  	default:
> > >  		WARN_ON_ONCE(true);
> > >  		return -EINVAL;
> > > @@ -5203,6 +5218,9 @@ static int do_statmount(struct kstatmount *s, u64 mnt_id, u64 mnt_ns_id,
> > >  	if (!err && s->mask & STATMOUNT_MNT_OPTS)
> > >  		err = statmount_string(s, STATMOUNT_MNT_OPTS);
> > >  
> > > +	if (!err && s->mask & STATMOUNT_FS_SUBTYPE)
> > > +		err = statmount_string(s, STATMOUNT_FS_SUBTYPE);
> > > +
> > >  	if (!err && s->mask & STATMOUNT_MNT_NS_ID)
> > >  		statmount_mnt_ns_id(s, ns);
> > >  
> > > @@ -5224,7 +5242,7 @@ static inline bool retry_statmount(const long ret, size_t *seq_size)
> > >  }
> > >  
> > >  #define STATMOUNT_STRING_REQ (STATMOUNT_MNT_ROOT | STATMOUNT_MNT_POINT | \
> > > -			      STATMOUNT_FS_TYPE | STATMOUNT_MNT_OPTS)
> > > +			      STATMOUNT_FS_TYPE | STATMOUNT_MNT_OPTS | STATMOUNT_FS_SUBTYPE)
> > >  
> > >  static int prepare_kstatmount(struct kstatmount *ks, struct mnt_id_req *kreq,
> > >  			      struct statmount __user *buf, size_t bufsize,
> > > diff --git a/include/uapi/linux/mount.h b/include/uapi/linux/mount.h
> > > index 225bc366ffcbf0319929e2f55f1fbea88e4d7b81..fa206fb56b3b25cf80f7d430e1b6bab19c3220e4 100644
> > > --- a/include/uapi/linux/mount.h
> > > +++ b/include/uapi/linux/mount.h
> > > @@ -173,7 +173,9 @@ struct statmount {
> > >  	__u32 mnt_root;		/* [str] Root of mount relative to root of fs */
> > >  	__u32 mnt_point;	/* [str] Mountpoint relative to current root */
> > >  	__u64 mnt_ns_id;	/* ID of the mount namespace */
> > > -	__u64 __spare2[49];
> > > +	__u32 fs_subtype;	/* [str] Subtype of fs_type (if any) */
> > > +	__u32 __spare1[1];
> > > +	__u64 __spare2[48];
> > >  	char str[];		/* Variable size part containing strings */
> > >  };
> > >  
> > > @@ -207,6 +209,7 @@ struct mnt_id_req {
> > >  #define STATMOUNT_FS_TYPE		0x00000020U	/* Want/got fs_type */
> > >  #define STATMOUNT_MNT_NS_ID		0x00000040U	/* Want/got mnt_ns_id */
> > >  #define STATMOUNT_MNT_OPTS		0x00000080U	/* Want/got mnt_opts */
> > > +#define STATMOUNT_FS_SUBTYPE		0x00000100U	/* Want/got subtype */
> > >  
> > >  /*
> > >   * Special @mnt_id values that can be passed to listmount
> > > 
> > > ---
> > > base-commit: 26213e1a6caa5a7f508b919059b0122b451f4dfe
> > > change-id: 20241106-statmount-3f91a7ed75fa
> > > 
> > > Best regards,
> > > -- 
> > > Jeff Layton <jlayton@kernel.org>
> > > 
> 
> -- 
> Jeff Layton <jlayton@kernel.org>
Ian Kent Nov. 6, 2024, 11:07 p.m. UTC | #4
On 6/11/24 21:29, Jeff Layton wrote:
> /proc/self/mountinfo prints out the sb->s_subtype after the type. In
> particular, FUSE makes use of this to display the fstype as
> fuse.<subtype>.
>
> Add STATMOUNT_FS_SUBTYPE and claim one of the __spare2 fields to point
> to the offset into the str[] array. The STATMOUNT_FS_SUBTYPE will only
> be set in the return mask if there is a subtype associated with the

Looks ok to me too.

Reviewed-by: Ian Kent <raven@themaw.net>


Ian

> mount.
>
> Signed-off-by: Jeff Layton <jlayton@kernel.org>
> ---
>   fs/namespace.c             | 20 +++++++++++++++++++-
>   include/uapi/linux/mount.h |  5 ++++-
>   2 files changed, 23 insertions(+), 2 deletions(-)
>
> diff --git a/fs/namespace.c b/fs/namespace.c
> index ba77ce1c6788dfe461814b5826fcbb3aab68fad4..5f2fb692449a9c0a15b60549fb9f7bedd10f1f3d 100644
> --- a/fs/namespace.c
> +++ b/fs/namespace.c
> @@ -5006,6 +5006,14 @@ static int statmount_fs_type(struct kstatmount *s, struct seq_file *seq)
>   	return 0;
>   }
>   
> +static int statmount_fs_subtype(struct kstatmount *s, struct seq_file *seq)
> +{
> +	struct super_block *sb = s->mnt->mnt_sb;
> +
> +	seq_puts(seq, sb->s_subtype);
> +	return 0;
> +}
> +
>   static void statmount_mnt_ns_id(struct kstatmount *s, struct mnt_namespace *ns)
>   {
>   	s->sm.mask |= STATMOUNT_MNT_NS_ID;
> @@ -5064,6 +5072,13 @@ static int statmount_string(struct kstatmount *s, u64 flag)
>   		sm->mnt_opts = seq->count;
>   		ret = statmount_mnt_opts(s, seq);
>   		break;
> +	case STATMOUNT_FS_SUBTYPE:
> +		/* ignore if no s_subtype */
> +		if (!s->mnt->mnt_sb->s_subtype)
> +			return 0;
> +		sm->fs_subtype = seq->count;
> +		ret = statmount_fs_subtype(s, seq);
> +		break;
>   	default:
>   		WARN_ON_ONCE(true);
>   		return -EINVAL;
> @@ -5203,6 +5218,9 @@ static int do_statmount(struct kstatmount *s, u64 mnt_id, u64 mnt_ns_id,
>   	if (!err && s->mask & STATMOUNT_MNT_OPTS)
>   		err = statmount_string(s, STATMOUNT_MNT_OPTS);
>   
> +	if (!err && s->mask & STATMOUNT_FS_SUBTYPE)
> +		err = statmount_string(s, STATMOUNT_FS_SUBTYPE);
> +
>   	if (!err && s->mask & STATMOUNT_MNT_NS_ID)
>   		statmount_mnt_ns_id(s, ns);
>   
> @@ -5224,7 +5242,7 @@ static inline bool retry_statmount(const long ret, size_t *seq_size)
>   }
>   
>   #define STATMOUNT_STRING_REQ (STATMOUNT_MNT_ROOT | STATMOUNT_MNT_POINT | \
> -			      STATMOUNT_FS_TYPE | STATMOUNT_MNT_OPTS)
> +			      STATMOUNT_FS_TYPE | STATMOUNT_MNT_OPTS | STATMOUNT_FS_SUBTYPE)
>   
>   static int prepare_kstatmount(struct kstatmount *ks, struct mnt_id_req *kreq,
>   			      struct statmount __user *buf, size_t bufsize,
> diff --git a/include/uapi/linux/mount.h b/include/uapi/linux/mount.h
> index 225bc366ffcbf0319929e2f55f1fbea88e4d7b81..fa206fb56b3b25cf80f7d430e1b6bab19c3220e4 100644
> --- a/include/uapi/linux/mount.h
> +++ b/include/uapi/linux/mount.h
> @@ -173,7 +173,9 @@ struct statmount {
>   	__u32 mnt_root;		/* [str] Root of mount relative to root of fs */
>   	__u32 mnt_point;	/* [str] Mountpoint relative to current root */
>   	__u64 mnt_ns_id;	/* ID of the mount namespace */
> -	__u64 __spare2[49];
> +	__u32 fs_subtype;	/* [str] Subtype of fs_type (if any) */
> +	__u32 __spare1[1];
> +	__u64 __spare2[48];
>   	char str[];		/* Variable size part containing strings */
>   };
>   
> @@ -207,6 +209,7 @@ struct mnt_id_req {
>   #define STATMOUNT_FS_TYPE		0x00000020U	/* Want/got fs_type */
>   #define STATMOUNT_MNT_NS_ID		0x00000040U	/* Want/got mnt_ns_id */
>   #define STATMOUNT_MNT_OPTS		0x00000080U	/* Want/got mnt_opts */
> +#define STATMOUNT_FS_SUBTYPE		0x00000100U	/* Want/got subtype */
>   
>   /*
>    * Special @mnt_id values that can be passed to listmount
>
> ---
> base-commit: 26213e1a6caa5a7f508b919059b0122b451f4dfe
> change-id: 20241106-statmount-3f91a7ed75fa
>
> Best regards,
diff mbox series

Patch

diff --git a/fs/namespace.c b/fs/namespace.c
index ba77ce1c6788dfe461814b5826fcbb3aab68fad4..5f2fb692449a9c0a15b60549fb9f7bedd10f1f3d 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -5006,6 +5006,14 @@  static int statmount_fs_type(struct kstatmount *s, struct seq_file *seq)
 	return 0;
 }
 
+static int statmount_fs_subtype(struct kstatmount *s, struct seq_file *seq)
+{
+	struct super_block *sb = s->mnt->mnt_sb;
+
+	seq_puts(seq, sb->s_subtype);
+	return 0;
+}
+
 static void statmount_mnt_ns_id(struct kstatmount *s, struct mnt_namespace *ns)
 {
 	s->sm.mask |= STATMOUNT_MNT_NS_ID;
@@ -5064,6 +5072,13 @@  static int statmount_string(struct kstatmount *s, u64 flag)
 		sm->mnt_opts = seq->count;
 		ret = statmount_mnt_opts(s, seq);
 		break;
+	case STATMOUNT_FS_SUBTYPE:
+		/* ignore if no s_subtype */
+		if (!s->mnt->mnt_sb->s_subtype)
+			return 0;
+		sm->fs_subtype = seq->count;
+		ret = statmount_fs_subtype(s, seq);
+		break;
 	default:
 		WARN_ON_ONCE(true);
 		return -EINVAL;
@@ -5203,6 +5218,9 @@  static int do_statmount(struct kstatmount *s, u64 mnt_id, u64 mnt_ns_id,
 	if (!err && s->mask & STATMOUNT_MNT_OPTS)
 		err = statmount_string(s, STATMOUNT_MNT_OPTS);
 
+	if (!err && s->mask & STATMOUNT_FS_SUBTYPE)
+		err = statmount_string(s, STATMOUNT_FS_SUBTYPE);
+
 	if (!err && s->mask & STATMOUNT_MNT_NS_ID)
 		statmount_mnt_ns_id(s, ns);
 
@@ -5224,7 +5242,7 @@  static inline bool retry_statmount(const long ret, size_t *seq_size)
 }
 
 #define STATMOUNT_STRING_REQ (STATMOUNT_MNT_ROOT | STATMOUNT_MNT_POINT | \
-			      STATMOUNT_FS_TYPE | STATMOUNT_MNT_OPTS)
+			      STATMOUNT_FS_TYPE | STATMOUNT_MNT_OPTS | STATMOUNT_FS_SUBTYPE)
 
 static int prepare_kstatmount(struct kstatmount *ks, struct mnt_id_req *kreq,
 			      struct statmount __user *buf, size_t bufsize,
diff --git a/include/uapi/linux/mount.h b/include/uapi/linux/mount.h
index 225bc366ffcbf0319929e2f55f1fbea88e4d7b81..fa206fb56b3b25cf80f7d430e1b6bab19c3220e4 100644
--- a/include/uapi/linux/mount.h
+++ b/include/uapi/linux/mount.h
@@ -173,7 +173,9 @@  struct statmount {
 	__u32 mnt_root;		/* [str] Root of mount relative to root of fs */
 	__u32 mnt_point;	/* [str] Mountpoint relative to current root */
 	__u64 mnt_ns_id;	/* ID of the mount namespace */
-	__u64 __spare2[49];
+	__u32 fs_subtype;	/* [str] Subtype of fs_type (if any) */
+	__u32 __spare1[1];
+	__u64 __spare2[48];
 	char str[];		/* Variable size part containing strings */
 };
 
@@ -207,6 +209,7 @@  struct mnt_id_req {
 #define STATMOUNT_FS_TYPE		0x00000020U	/* Want/got fs_type */
 #define STATMOUNT_MNT_NS_ID		0x00000040U	/* Want/got mnt_ns_id */
 #define STATMOUNT_MNT_OPTS		0x00000080U	/* Want/got mnt_opts */
+#define STATMOUNT_FS_SUBTYPE		0x00000100U	/* Want/got subtype */
 
 /*
  * Special @mnt_id values that can be passed to listmount