Re: any idea about auto export multiple btrfs snapshots?

Wang Yugui
2021-06-21T08:34:44Z

[-- Attachment #1: Type: text/plain, Size: 1065 bytes --]

Hi,

> > > It seems more fixes are needed.
> >
> > I think the problem is that the submount doesn't appear in /proc/mounts.
> > "nfsd_fh()" in nfs-utils needs to be able to map from the uuid for a
> > filesystem to the mount point. To do this it walks through /proc/mounts
> > checking the uuid of each filesystem. If a filesystem isn't listed
> > there, it obviously fails.
> >
> > I guess you could add code to nfs-utils to do whatever "btrfs subvol
> > list" does to make up for the fact that btrfs doesn't register in
> > /proc/mounts.
>
> Another approach might be to just change svcxdr_encode_fattr3() and
> nfsd4_encode_fattr() in the 'FSIDSOJURCE_UUID' case to check if
> dentry->d_inode has a different btrfs volume id to
> exp->ex_path.dentry->d_inode.
> If it does, then mix the volume id into the fsid somehow.
>
> With that, you wouldn't want the first change I suggested.

This is what I have done. and it is based on linux 5.10.44

but it still not work, so still more jobs needed.

Best Regards
Wang Yugui (wangyugui@e16-tech.com)
2021/06/21

[-- Attachment #2: 0001-nfsd-btrfs-subvol-support.patch --]
[-- Type: application/octet-stream, Size: 4741 bytes --]

From 57e6b3cec9b8ac396b661c190511af80839ddbe5 Mon Sep 17 00:00:00 2001
From: wangyugui <wangyugui@e16-tech.com>
Date: Thu, 17 Jun 2021 08:33:06 +0800
Subject: [PATCH] nfsd: btrfs subvol support

(struct statfs).f_fsid: uniq between btrfs subvols
(struct stat).st_dev: uniq between btrfs subvols
(struct statx).stx_mnt_id: NOT uniq between btrfs subvols, but yet not used in nfs/nfsd
kernel samples/vfs/test-statx.c
stx_rdev_major/stx_rdev_minor seems be truncated by something
like old_encode_dev()/old_decode_dev()?

TODO: (struct nfs_fattr).fsid
TODO: FSIDSOURCE_FSID in nfs3xdr.c/nfsxdr.c
---
fs/namei.c | 2 ++
fs/nfsd/nfs3xdr.c | 2 +-
fs/nfsd/nfs4xdr.c | 10 ++++++++--
fs/nfsd/vfs.c | 6 ++++++
include/linux/fs.h | 13 +++++++++++++
5 files changed, 30 insertions(+), 3 deletions(-)

diff --git a/fs/namei.c b/fs/namei.c
index fe132e3..6974a95 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -1105,6 +1105,8 @@ int follow_up(struct path *path)
struct mount *parent;
struct dentry *mountpoint;

+ if(unlikely(d_is_btrfs_subvol(path->dentry)))
+ return 0;
read_seqlock_excl(&mount_lock);
parent = mnt->mnt_parent;
if (parent == mnt) {
diff --git a/fs/nfsd/nfs3xdr.c b/fs/nfsd/nfs3xdr.c
index 716566d..45666b3 100644
--- a/fs/nfsd/nfs3xdr.c
+++ b/fs/nfsd/nfs3xdr.c
@@ -877,7 +877,7 @@ compose_entry_fh(struct nfsd3_readdirres *cd, struct svc_fh *fhp,
dchild = lookup_positive_unlocked(name, dparent, namlen);
if (IS_ERR(dchild))
return rv;
- if (d_mountpoint(dchild))
+ if (d_mountpoint(dchild) || unlikely(d_is_btrfs_subvol(dchild)))
goto out;
if (dchild->d_inode->i_ino != ino)
goto out;
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 5f5169b..939d095 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -2728,6 +2728,7 @@ nfsd4_encode_fattr(struct xdr_stream *xdr, struct svc_fh *fhp,
.dentry = dentry,
};
struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
+ bool is_btrfs_subvol= d_is_btrfs_subvol(dentry);

BUG_ON(bmval1 & NFSD_WRITEONLY_ATTRS_WORD1);
BUG_ON(!nfsd_attrs_supported(minorversion, bmval));
@@ -2744,7 +2745,8 @@ nfsd4_encode_fattr(struct xdr_stream *xdr, struct svc_fh *fhp,
if ((bmval0 & (FATTR4_WORD0_FILES_AVAIL | FATTR4_WORD0_FILES_FREE |
FATTR4_WORD0_FILES_TOTAL | FATTR4_WORD0_MAXNAME)) ||
(bmval1 & (FATTR4_WORD1_SPACE_AVAIL | FATTR4_WORD1_SPACE_FREE |
- FATTR4_WORD1_SPACE_TOTAL))) {
+ FATTR4_WORD1_SPACE_TOTAL)) ||
+ unlikely(is_btrfs_subvol)) {
err = vfs_statfs(&path, &statfs);
if (err)
goto out_nfserr;
@@ -2885,7 +2887,11 @@ nfsd4_encode_fattr(struct xdr_stream *xdr, struct svc_fh *fhp,
p = xdr_encode_hyper(p, NFS4_REFERRAL_FSID_MINOR);
} else switch(fsid_source(fhp)) {
case FSIDSOURCE_FSID:
- p = xdr_encode_hyper(p, (u64)exp->ex_fsid);
+ if (unlikely(is_btrfs_subvol)){
+ *p++ = cpu_to_be32(statfs.f_fsid.val[0]);
+ *p++ = cpu_to_be32(statfs.f_fsid.val[1]);
+ } else
+ p = xdr_encode_hyper(p, (u64)exp->ex_fsid);
p = xdr_encode_hyper(p, (u64)0);
break;
case FSIDSOURCE_DEV:
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index 1ecacee..ae34ffc 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -68,6 +68,10 @@ nfsd_cross_mnt(struct svc_rqst *rqstp, struct dentry **dpp,
err = follow_down(&path);
if (err < 0)
goto out;
+ if (unlikely(d_is_btrfs_subvol(dentry))){
+ path_put(&path);
+ goto out;
+ } else
if (path.mnt == exp->ex_path.mnt && path.dentry == dentry &&
nfsd_mountpoint(dentry, exp) == 2) {
/* This is only a mountpoint in some other namespace */
@@ -160,6 +164,8 @@ int nfsd_mountpoint(struct dentry *dentry, struct svc_export *exp)
return 1;
if (nfsd4_is_junction(dentry))
return 1;
+ if (d_is_btrfs_subvol(dentry))
+ return 1;
if (d_mountpoint(dentry))
/*
* Might only be a mountpoint in a different namespace,
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 8bde32c..b0d52e9 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -3399,6 +3399,19 @@ static inline bool is_root_inode(struct inode *inode)
return inode == inode->i_sb->s_root->d_inode;
}

+/*
+ * same logical as fs/btrfs is_subvolume_inode(struct inode *inode)
+ * #define BTRFS_FIRST_FREE_OBJECTID 256ULL
+ * #define BTRFS_SUPER_MAGIC 0x9123683E
+ */
+static inline bool d_is_btrfs_subvol(const struct dentry *dentry)
+{
+ bool ret = dentry->d_inode && unlikely(dentry->d_inode->i_ino == 256ULL) &&
+ dentry->d_sb && dentry->d_sb->s_magic == 0x9123683E;
+ //printk(KERN_INFO "d_is_btrfs_subvol(%s)=%d\n", dentry->d_name.name, ret);
+ return ret;
+}
+
static inline bool dir_emit(struct dir_context *ctx,
const char *name, int namelen,
u64 ino, unsigned type)
--
2.30.2

[-- Attachment #3: 0002-trace-nfsd-btrfs-subvol-support.txt --]
[-- Type: application/octet-stream, Size: 3431 bytes --]

From 6e709554e4c7d5efd3b030fc08b6e6493879a025 Mon Sep 17 00:00:00 2001
From: wangyugui <wangyugui@e16-tech.com>
Date: Thu, 17 Jun 2021 08:33:06 +0800
Subject: [PATCH] trace nfsd: btrfs subvol support

[ 268.994169] follow_down(xfs2)=0
[ 268.997405] nfsd_cross_mnt(xfs2)=0
*[ 269.000840] nfsd4_encode_dirent_fattr(/) ignore_crossmnt=0
why /
*[ 269.006477] nfs_d_automount(xfs2)
why not happen when btrfs subvol
[ 269.009892] follow_down(xfs2)=0
[ 269.013055] nfsd_cross_mnt(xfs2)=0

[ 483.037221] follow_down(sub1)=0
[ 483.040428] nfsd_cross_mnt(sub1)=0
[ 483.043855] nfsd4_encode_dirent_fattr(sub1) ignore_crossmnt=0
[ 483.049635] nfsd4_encode_dirent_fattr(.snapshot) ignore_crossmnt=0
[ 483.055847] nfsd4_encode_dirent_fattr(dir1) ignore_crossmnt=0
[ 483.062669] follow_down(sub2)=0
[ 483.066800] nfsd_cross_mnt(sub2)=0

btrfs subvols =>force crossmnt
subvol nfs/umount: os shutdonw or manual nfs/umount?
special status(BTRFS_LAST_FREE_OBJECTID,only return to nfs)?
#define BTRFS_LAST_FREE_OBJECTID -256ULL
(struct file )->(struct inode *f_inode)->(struct super_block *i_sb;)->(unsigned long s_magic)
btrfs->xfs =>still need crossmnt
xfs->btrfs =>still need crossmnt

NFSEXP_CROSSMOUNT
NFSD_JUNCTION_XATTR_NAME
AT_NO_AUTOMOUNT
NFS_ATTR_FATTR_MOUNTPOINT
S_AUTOMOUNT

---
fs/nfs/dir.c | 2 ++
fs/nfs/namespace.c | 1 +
fs/nfsd/nfs4xdr.c | 3 +++
fs/nfsd/vfs.c | 1 +
4 files changed, 7 insertions(+)

diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index c837675..975440d 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -1799,6 +1799,8 @@ nfs4_do_lookup_revalidate(struct inode *dir, struct dentry *dentry,

if (!(flags & LOOKUP_OPEN) || (flags & LOOKUP_DIRECTORY))
goto full_reval;
+ if (dentry->d_inode && dentry->d_inode->i_ino == 256ULL && dentry->d_sb)
+ printk(KERN_INFO "nfs4_do_lookup_revalidate(%s)=%lx\n", dentry->d_name.name, dentry->d_sb->s_magic);
if (d_mountpoint(dentry))
goto full_reval;

diff --git a/fs/nfs/namespace.c b/fs/nfs/namespace.c
index 2bcbe38..f69715c 100644
--- a/fs/nfs/namespace.c
+++ b/fs/nfs/namespace.c
@@ -152,6 +152,7 @@ struct vfsmount *nfs_d_automount(struct path *path)
int timeout = READ_ONCE(nfs_mountpoint_expiry_timeout);
int ret;

+ printk(KERN_INFO "nfs_d_automount(%s)\n", path->dentry->d_name.name);
if (IS_ROOT(path->dentry))
return ERR_PTR(-ESTALE);

diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 939d095..cb5b328 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -3316,6 +3316,8 @@ nfsd4_encode_dirent_fattr(struct xdr_stream *xdr, struct nfsd4_readdir *cd,
*/
if (nfsd_mountpoint(dentry, exp)) {
int err;
+ // if(d_is_btrfs_subvol(dentry))
+ // cd->rd_bmval[1] |= FATTR4_WORD1_MOUNTED_ON_FILEID;

if (!(exp->ex_flags & NFSEXP_V4ROOT)
&& !attributes_need_mount(cd->rd_bmval)) {
@@ -3343,6 +3345,7 @@ nfsd4_encode_dirent_fattr(struct xdr_stream *xdr, struct nfsd4_readdir *cd,
out_put:
dput(dentry);
exp_put(exp);
+ printk(KERN_INFO "nfsd4_encode_dirent_fattr(%s) ignore_crossmnt=%d\n", dentry->d_name.name, ignore_crossmnt);
return nfserr;
}

diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index ae34ffc..c12a394 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -111,6 +111,7 @@ nfsd_cross_mnt(struct svc_rqst *rqstp, struct dentry **dpp,
path_put(&path);
exp_put(exp2);
out:
+ printk(KERN_INFO "nfsd_cross_mnt(%s)=%d\n", dentry->d_name.name, err);
return err;
}

--
2.30.2