[PATCH V12 07/12] famfs: MAP_CREATE ioctl and fmap ingest (ABI 44)

John Groves <[email protected]> Mon, 3 Aug 2026 02:29:26 +0000
Newsgroups dev.linux.lists.fuse-devel,dev.linux.lists.nvdimm,org.kernel.vger.linux-cxl,org.kernel.vger.linux-doc,org.kernel.vger.linux-fsdevel,org.kernel.vger.linux-kernel
Message-ID <0100019fc5746312-6cfe9a07-a658-4aba-b1e9-5233b0ddfcf4-000000@email.amazonses.com>
From: John Groves <[email protected]>=0D=0A=0D=0AAdd the famfs file ioctl h=
andler (FAMFSIOC_NOP, FAMFSIOC_MAP_CREATE) and=0D=0Athe KABI-44 self-desc=
ribing fmap message: the wire ABI in famfs_ioctl.h=0D=0A(famfs_ioc_fmap_h=
eader plus the simple and interleaved extent structs), the=0D=0Ain-core f=
amfs_file_meta, and famfs_file_init_dax(), which copies the=0D=0Amessage =
in, parses both the simple-extent and interleaved (striped) wire=0D=0Afor=
ms into inode->i_private, and sets S_DAX.=0D=0A=0D=0AResolving those mapp=
ings to dax-device offsets (iomap_begin) is added in=0D=0Athe following c=
ommit; the read/write/fault paths keep their NULL iomap_ops=0D=0Astub unt=
il then.=0D=0A=0D=0AAlso add famfs ioctls to ioctl-number.rst=0D=0A=0D=0A=
Signed-off-by: John Groves <[email protected]>=0D=0A---=0D=0A .../userspace=
-api/ioctl/ioctl-number.rst      |   1 +=0D=0A fs/famfs/famfs_file.c     =
                    | 326 +++++++++++++++++-=0D=0A fs/famfs/famfs_inode.c=
                        |   1 +=0D=0A fs/famfs/famfs_internal.h          =
           |  46 +++=0D=0A include/uapi/linux/famfs_ioctl.h              =
|  91 +++++=0D=0A 5 files changed, 462 insertions(+), 3 deletions(-)=0D=0A=
 create mode 100644 include/uapi/linux/famfs_ioctl.h=0D=0A=0D=0Adiff --gi=
t a/Documentation/userspace-api/ioctl/ioctl-number.rst b/Documentation/us=
erspace-api/ioctl/ioctl-number.rst=0D=0Aindex 3f0ef1e27eb0..5e244dec1b98 =
100644=0D=0A--- a/Documentation/userspace-api/ioctl/ioctl-number.rst=0D=0A=
+++ b/Documentation/userspace-api/ioctl/ioctl-number.rst=0D=0A@@ -299,6 +=
299,7 @@ Code  Seq#    Include File                                      =
       Comments=0D=0A 'u'   00-2F  linux/ublk_cmd.h                      =
                    conflict!=0D=0A 'u'   20-3F  linux/uvcvideo.h        =
                                  USB video class host driver=0D=0A 'u'  =
 40-4f  linux/udmabuf.h                                           userspa=
ce dma-buf misc device=0D=0A+'u'   50-5F  linux/famfs_ioctl.h            =
                           famfs shared memory file system=0D=0A 'v'   00=
-1F  linux/ext2_fs.h                                           conflict!=0D=
=0A 'v'   00-1F  linux/fs.h                                              =
  conflict!=0D=0A 'v'   00-0F  linux/sonypi.h                            =
                conflict!=0D=0Adiff --git a/fs/famfs/famfs_file.c b/fs/fa=
mfs/famfs_file.c=0D=0Aindex 678f2035fd5f..d710c8a0c923 100644=0D=0A--- a/=
fs/famfs/famfs_file.c=0D=0A+++ b/fs/famfs/famfs_file.c=0D=0A@@ -13,9 +13,=
313 @@=0D=0A #include <linux/mm.h>=0D=0A #include <linux/dax.h>=0D=0A #in=
clude <linux/iomap.h>=0D=0A+#include <linux/capability.h>=0D=0A=20=0D=0A+=
#include <linux/famfs_ioctl.h>=0D=0A #include "famfs_internal.h"=0D=0A=20=
=0D=0A+/* Expose famfs kernel abi version as a read-only module parameter=
 */=0D=0A+static int famfs_kabi_version =3D FAMFS_KABI_VERSION;=0D=0A+mod=
ule_param(famfs_kabi_version, int, 0444);=0D=0A+MODULE_PARM_DESC(famfs_ka=
bi_version, "famfs kernel abi version");=0D=0A+=0D=0A+void=0D=0A+famfs_me=
ta_free(struct famfs_file_meta *map)=0D=0A+{=0D=0A+=09if (map) {=0D=0A+=09=
=09switch (map->fm_extent_type) {=0D=0A+=09=09case FAMFS_IOC_EXT_SIMPLE:=0D=
=0A+=09=09=09kfree(map->se);=0D=0A+=09=09=09break;=0D=0A+=09=09case FAMFS=
_IOC_EXT_INTERLEAVE:=0D=0A+=09=09=09if (map->ie) {=0D=0A+=09=09=09=09u32 =
i;=0D=0A+=0D=0A+=09=09=09=09for (i =3D 0; i < map->fm_niext; i++)=0D=0A+=09=
=09=09=09=09kfree(map->ie[i].ie_strips);=0D=0A+=09=09=09}=0D=0A+=09=09=09=
kfree(map->ie);=0D=0A+=09=09=09break;=0D=0A+=09=09default:=0D=0A+=09=09=09=
break;=0D=0A+=09=09}=0D=0A+=09}=0D=0A+=09kfree(map);=0D=0A+}=0D=0A+=0D=0A=
+/**=0D=0A+ * famfs_file_init_dax() - FAMFSIOC_MAP_CREATE ioctl handler=0D=
=0A+ * @file: the un-initialized file=0D=0A+ * @arg:  user pointer to a s=
elf-describing fmap message=0D=0A+ *=0D=0A+ * The map-create ioctl carrie=
s the fmap as a self-describing message: a=0D=0A+ * struct famfs_ioc_fmap=
_header followed by an extent list. The message is=0D=0A+ * copied in, pa=
rsed into a famfs_file_meta, and published on inode->i_private.=0D=0A+ * =
Both the simple-extent and the interleaved (striped) wire forms are handl=
ed.=0D=0A+ * The wire layout byte-matches the fmap carried in a fuse famf=
s GET_FMAP reply.=0D=0A+ */=0D=0A+static int=0D=0A+famfs_check_ext_alignm=
ent(struct famfs_meta_simple_ext *se)=0D=0A+{=0D=0A+=09int errs =3D 0;=0D=
=0A+=0D=0A+=09if (!IS_ALIGNED(se->ext_offset, PMD_SIZE))=0D=0A+=09=09errs=
++;=0D=0A+=09if (!IS_ALIGNED(se->ext_len, PMD_SIZE))=0D=0A+=09=09errs++;=0D=
=0A+=0D=0A+=09return errs;=0D=0A+}=0D=0A+=0D=0A+static int=0D=0A+famfs_fi=
le_init_dax(struct file *file, void __user *arg)=0D=0A+{=0D=0A+=09struct =
famfs_ioc_fmap_header fmh;=0D=0A+=09struct famfs_file_meta *meta =3D NULL=
;=0D=0A+=09struct famfs_fs_info *fsi;=0D=0A+=09struct super_block *sb;=0D=
=0A+=09struct inode *inode;=0D=0A+=09void *fmap_buf =3D NULL;=0D=0A+=09si=
ze_t extent_total =3D 0;=0D=0A+=09size_t next_offset;=0D=0A+=09int errs =3D=
 0;=0D=0A+=09int rc;=0D=0A+=09u32 i, j;=0D=0A+=0D=0A+=09inode =3D file_in=
ode(file);=0D=0A+=09if (!inode)=0D=0A+=09=09return -EBADF;=0D=0A+=09if (i=
node->i_private)=0D=0A+=09=09return -EEXIST;=0D=0A+=0D=0A+=09sb  =3D inod=
e->i_sb;=0D=0A+=09fsi =3D sb->s_fs_info;=0D=0A+=09if (fsi->deverror)=0D=0A=
+=09=09return -ENODEV;=0D=0A+=09if (!famfs_opt_enabled(fsi, FAMFS_OPT_MAP=
_CREATE))=0D=0A+=09=09return -EPERM;=0D=0A+=0D=0A+=09if (copy_from_user(&=
fmh, arg, sizeof(fmh)))=0D=0A+=09=09return -EFAULT;=0D=0A+=0D=0A+=09if (f=
mh.fmap_version !=3D FAMFS_FMAP_VERSION)=0D=0A+=09=09return -EINVAL;=0D=0A=
+=09if (fmh.fmap_size < sizeof(fmh))=0D=0A+=09=09return -EINVAL;=0D=0A+=09=
if (fmh.fmap_size > FAMFS_FMAP_MSG_MAX)=0D=0A+=09=09return -EFBIG;=0D=0A+=
=09if (fmh.nextents < 1)=0D=0A+=09=09return -EINVAL;=0D=0A+=0D=0A+=09fmap=
_buf =3D kvmalloc(fmh.fmap_size, GFP_KERNEL);=0D=0A+=09if (!fmap_buf)=0D=0A=
+=09=09return -ENOMEM;=0D=0A+=0D=0A+=09if (copy_from_user(fmap_buf, arg, =
fmh.fmap_size)) {=0D=0A+=09=09rc =3D -EFAULT;=0D=0A+=09=09goto out;=0D=0A=
+=09}=0D=0A+=09next_offset =3D sizeof(fmh);=09/* start of the extent list=
 */=0D=0A+=0D=0A+=09meta =3D kzalloc_obj(*meta, GFP_KERNEL);=0D=0A+=09if =
(!meta) {=0D=0A+=09=09rc =3D -ENOMEM;=0D=0A+=09=09goto out;=0D=0A+=09}=0D=
=0A+=0D=0A+=09meta->error =3D false;=0D=0A+=09meta->file_type =3D fmh.fil=
e_type;=0D=0A+=09meta->file_size =3D fmh.file_size;=0D=0A+=09meta->fm_ext=
ent_type =3D fmh.ext_type;=0D=0A+=0D=0A+=09switch (fmh.ext_type) {=0D=0A+=
=09case FAMFS_IOC_EXT_SIMPLE: {=0D=0A+=09=09struct famfs_ioc_simple_ext *=
se_in =3D fmap_buf + next_offset;=0D=0A+=0D=0A+=09=09next_offset +=3D (si=
ze_t)fmh.nextents * sizeof(*se_in);=0D=0A+=09=09if (next_offset > fmh.fma=
p_size) {=0D=0A+=09=09=09rc =3D -EINVAL;=0D=0A+=09=09=09goto out;=0D=0A+=09=
=09}=0D=0A+=0D=0A+=09=09meta->fm_nextents =3D fmh.nextents;=0D=0A+=09=09m=
eta->se =3D kcalloc(meta->fm_nextents, sizeof(*meta->se),=0D=0A+=09=09=09=
=09   GFP_KERNEL);=0D=0A+=09=09if (!meta->se) {=0D=0A+=09=09=09rc =3D -EN=
OMEM;=0D=0A+=09=09=09goto out;=0D=0A+=09=09}=0D=0A+=0D=0A+=09=09for (i =3D=
 0; i < fmh.nextents; i++) {=0D=0A+=09=09=09meta->se[i].dev_index  =3D se=
_in[i].se_devindex;=0D=0A+=09=09=09meta->se[i].ext_offset =3D se_in[i].se=
_offset;=0D=0A+=09=09=09meta->se[i].ext_len    =3D se_in[i].se_len;=0D=0A=
+=0D=0A+=09=09=09if (meta->se[i].dev_index >=3D FAMFS_MAX_DAXDEVS) {=0D=0A=
+=09=09=09=09rc =3D -EINVAL;=0D=0A+=09=09=09=09goto out;=0D=0A+=09=09=09}=
=0D=0A+=09=09=09meta->dev_bitmap |=3D BIT_ULL(meta->se[i].dev_index);=0D=0A=
+=09=09=09errs +=3D famfs_check_ext_alignment(&meta->se[i]);=0D=0A+=09=09=
=09extent_total +=3D meta->se[i].ext_len;=0D=0A+=09=09}=0D=0A+=09=09break=
;=0D=0A+=09}=0D=0A+=0D=0A+=09case FAMFS_IOC_EXT_INTERLEAVE: {=0D=0A+=09=09=
s64 size_remainder =3D meta->file_size;=0D=0A+=09=09u32 niext =3D fmh.nex=
tents;=0D=0A+=0D=0A+=09=09meta->fm_niext =3D niext;=0D=0A+=09=09meta->ie =
=3D kcalloc(niext, sizeof(*meta->ie), GFP_KERNEL);=0D=0A+=09=09if (!meta-=
>ie) {=0D=0A+=09=09=09rc =3D -ENOMEM;=0D=0A+=09=09=09goto out;=0D=0A+=09=09=
}=0D=0A+=0D=0A+=09=09/* Outer loop is over the separate interleaved exten=
ts */=0D=0A+=09=09for (i =3D 0; i < niext; i++) {=0D=0A+=09=09=09struct f=
amfs_ioc_iext *ie_in =3D fmap_buf + next_offset;=0D=0A+=09=09=09struct fa=
mfs_ioc_simple_ext *sie_in;=0D=0A+=09=09=09u64 nstrips;=0D=0A+=0D=0A+=09=09=
=09next_offset +=3D sizeof(*ie_in);=0D=0A+=09=09=09if (next_offset > fmh.=
fmap_size) {=0D=0A+=09=09=09=09rc =3D -EINVAL;=0D=0A+=09=09=09=09goto out=
;=0D=0A+=09=09=09}=0D=0A+=0D=0A+=09=09=09if (ie_in->ie_chunk_size =3D=3D =
0 ||=0D=0A+=09=09=09    !IS_ALIGNED(ie_in->ie_chunk_size, PMD_SIZE)) {=0D=
=0A+=09=09=09=09rc =3D -EINVAL;=0D=0A+=09=09=09=09goto out;=0D=0A+=09=09=09=
}=0D=0A+=09=09=09if (ie_in->ie_nbytes =3D=3D 0) {=0D=0A+=09=09=09=09rc =3D=
 -EINVAL;=0D=0A+=09=09=09=09goto out;=0D=0A+=09=09=09}=0D=0A+=0D=0A+=09=09=
=09nstrips =3D ie_in->ie_nstrips;=0D=0A+=09=09=09if (nstrips < 1) {=0D=0A=
+=09=09=09=09rc =3D -EINVAL;=0D=0A+=09=09=09=09goto out;=0D=0A+=09=09=09}=
=0D=0A+=0D=0A+=09=09=09meta->ie[i].fie_chunk_size =3D ie_in->ie_chunk_siz=
e;=0D=0A+=09=09=09meta->ie[i].fie_nstrips    =3D ie_in->ie_nstrips;=0D=0A=
+=09=09=09meta->ie[i].fie_nbytes     =3D ie_in->ie_nbytes;=0D=0A+=0D=0A+=09=
=09=09/* The strip extents follow the interleaved-ext header */=0D=0A+=09=
=09=09sie_in =3D fmap_buf + next_offset;=0D=0A+=09=09=09next_offset +=3D =
nstrips * sizeof(*sie_in);=0D=0A+=09=09=09if (next_offset > fmh.fmap_size=
) {=0D=0A+=09=09=09=09rc =3D -EINVAL;=0D=0A+=09=09=09=09goto out;=0D=0A+=09=
=09=09}=0D=0A+=0D=0A+=09=09=09meta->ie[i].ie_strips =3D=0D=0A+=09=09=09=09=
kcalloc(nstrips, sizeof(meta->ie[i].ie_strips[0]),=0D=0A+=09=09=09=09=09G=
FP_KERNEL);=0D=0A+=09=09=09if (!meta->ie[i].ie_strips) {=0D=0A+=09=09=09=09=
rc =3D -ENOMEM;=0D=0A+=09=09=09=09goto out;=0D=0A+=09=09=09}=0D=0A+=0D=0A=
+=09=09=09/* Inner loop is over the strips */=0D=0A+=09=09=09for (j =3D 0=
; j < nstrips; j++) {=0D=0A+=09=09=09=09struct famfs_meta_simple_ext *so =
=3D=0D=0A+=09=09=09=09=09&meta->ie[i].ie_strips[j];=0D=0A+=0D=0A+=09=09=09=
=09so->dev_index  =3D sie_in[j].se_devindex;=0D=0A+=09=09=09=09so->ext_of=
fset =3D sie_in[j].se_offset;=0D=0A+=09=09=09=09so->ext_len    =3D sie_in=
[j].se_len;=0D=0A+=0D=0A+=09=09=09=09if (so->dev_index >=3D FAMFS_MAX_DAX=
DEVS) {=0D=0A+=09=09=09=09=09rc =3D -EINVAL;=0D=0A+=09=09=09=09=09goto ou=
t;=0D=0A+=09=09=09=09}=0D=0A+=09=09=09=09meta->dev_bitmap |=3D BIT_ULL(so=
->dev_index);=0D=0A+=09=09=09=09errs +=3D famfs_check_ext_alignment(so);=0D=
=0A+=09=09=09=09extent_total +=3D so->ext_len;=0D=0A+=09=09=09=09size_rem=
ainder -=3D so->ext_len;=0D=0A+=09=09=09}=0D=0A+=09=09}=0D=0A+=0D=0A+=09=09=
if (size_remainder > 0) {=0D=0A+=09=09=09/* Strips do not cover the whole=
 file */=0D=0A+=09=09=09rc =3D -EINVAL;=0D=0A+=09=09=09goto out;=0D=0A+=09=
=09}=0D=0A+=09=09break;=0D=0A+=09}=0D=0A+=0D=0A+=09default:=0D=0A+=09=09r=
c =3D -EINVAL;=0D=0A+=09=09goto out;=0D=0A+=09}=0D=0A+=0D=0A+=09if (errs =
> 0) {=0D=0A+=09=09rc =3D -EINVAL;=0D=0A+=09=09goto out;=0D=0A+=09}=0D=0A=
+=09if (extent_total < meta->file_size) {=0D=0A+=09=09rc =3D -EINVAL;=0D=0A=
+=09=09goto out;=0D=0A+=09}=0D=0A+=0D=0A+=09/* Publish the famfs metadata=
 on inode->i_private */=0D=0A+=09inode_lock(inode);=0D=0A+=09if (inode->i=
_private) {=0D=0A+=09=09rc =3D -EEXIST; /* file already has famfs metadat=
a */=0D=0A+=09} else {=0D=0A+=09=09inode->i_private =3D meta;=0D=0A+=09=09=
i_size_write(inode, meta->file_size);=0D=0A+=09=09inode->i_flags |=3D S_D=
AX;=0D=0A+=09=09meta =3D NULL; /* owned by the inode now */=0D=0A+=09=09r=
c =3D 0;=0D=0A+=09}=0D=0A+=09inode_unlock(inode);=0D=0A+=0D=0A+out:=0D=0A=
+=09kvfree(fmap_buf);=0D=0A+=09if (meta)=0D=0A+=09=09famfs_meta_free(meta=
);=0D=0A+=09return rc;=0D=0A+}=0D=0A+=0D=0A+/**=0D=0A+ * famfs_file_ioctl=
() - Top-level famfs file ioctl handler=0D=0A+ * @file: the file=0D=0A+ *=
 @cmd:  ioctl opcode=0D=0A+ * @arg:  ioctl opcode argument (if any)=0D=0A=
+ */=0D=0A+static long=0D=0A+famfs_file_ioctl(struct file *file, unsigned=
 int cmd, unsigned long arg)=0D=0A+{=0D=0A+=09struct inode *inode =3D fil=
e_inode(file);=0D=0A+=09struct famfs_fs_info *fsi =3D inode->i_sb->s_fs_i=
nfo;=0D=0A+=09long rc;=0D=0A+=0D=0A+=09if (fsi->deverror && (cmd !=3D FAM=
FSIOC_NOP))=0D=0A+=09=09return -ENODEV;=0D=0A+=0D=0A+=09switch (cmd) {=0D=
=0A+=09case FAMFSIOC_NOP:=0D=0A+=09=09rc =3D 0;=0D=0A+=09=09break;=0D=0A+=
=0D=0A+=09case FAMFSIOC_MAP_CREATE:=0D=0A+=09=09rc =3D famfs_file_init_da=
x(file, (void __user *)arg);=0D=0A+=09=09break;=0D=0A+=0D=0A+=09default:=0D=
=0A+=09=09rc =3D -ENOTTY;=0D=0A+=09=09break;=0D=0A+=09}=0D=0A+=0D=0A+=09r=
eturn rc;=0D=0A+}=0D=0A+=0D=0A /*****************************************=
****************************=0D=0A  * vm_operations=0D=0A  */=0D=0A@@ -93=
,9 +397,25 @@ const struct vm_operations_struct famfs_file_vm_ops =3D {=0D=
=0A static ssize_t=0D=0A famfs_file_invalid(struct inode *inode)=0D=0A {=0D=
=0A+=09struct famfs_file_meta *meta =3D inode->i_private;=0D=0A+=09size_t=
 i_size =3D i_size_read(inode);=0D=0A+=0D=0A+=09if (!meta) {=0D=0A+=09=09=
pr_debug("%s: un-initialized famfs file\n", __func__);=0D=0A+=09=09return=
 -EIO;=0D=0A+=09}=0D=0A+=09if (meta->error) {=0D=0A+=09=09pr_debug("%s: p=
reviously detected metadata errors\n", __func__);=0D=0A+=09=09return -EIO=
;=0D=0A+=09}=0D=0A+=09if (i_size !=3D meta->file_size) {=0D=0A+=09=09pr_w=
arn("%s: i_size overwritten from %ld to %ld\n",=0D=0A+=09=09       __func=
__, meta->file_size, i_size);=0D=0A+=09=09meta->error =3D true;=0D=0A+=09=
=09return -ENXIO;=0D=0A+=09}=0D=0A =09if (!IS_DAX(inode)) {=0D=0A-=09=09p=
r_debug("%s: inode %llx IS_DAX is false\n",=0D=0A-=09=09=09 __func__, (u6=
4)inode);=0D=0A+=09=09pr_debug("%s: inode %llx IS_DAX is false\n", __func=
__, (u64)inode);=0D=0A =09=09return -ENXIO;=0D=0A =09}=0D=0A =09return 0;=
=0D=0A@@ -222,7 +542,7 @@ const struct file_operations famfs_file_operati=
ons =3D {=0D=0A =09/* Custom famfs operations */=0D=0A =09.write_iter=09 =
  =3D famfs_dax_write_iter,=0D=0A =09.read_iter=09   =3D famfs_dax_read_i=
ter,=0D=0A-=09.unlocked_ioctl    =3D NULL /*famfs_file_ioctl*/,=0D=0A+=09=
=2Eunlocked_ioctl    =3D famfs_file_ioctl,=0D=0A =09.mmap=09=09   =3D fam=
fs_file_mmap,=0D=0A=20=0D=0A =09/* Force PMD alignment for mmap */=0D=0Ad=
iff --git a/fs/famfs/famfs_inode.c b/fs/famfs/famfs_inode.c=0D=0Aindex 91=
0a143dad30..a6c3b4574e69 100644=0D=0A--- a/fs/famfs/famfs_inode.c=0D=0A++=
+ b/fs/famfs/famfs_inode.c=0D=0A@@ -300,6 +300,7 @@ static int famfs_show=
_options(struct seq_file *m, struct dentry *root)=0D=0A=20=0D=0A static v=
oid famfs_evict_inode(struct inode *inode)=0D=0A {=0D=0A+=09famfs_meta_fr=
ee((struct famfs_file_meta *)inode->i_private);=0D=0A =09inode->i_private=
 =3D NULL;=0D=0A =09dax_break_layout_final(inode);=0D=0A =09truncate_inod=
e_pages_final(&inode->i_data);=0D=0Adiff --git a/fs/famfs/famfs_internal.=
h b/fs/famfs/famfs_internal.h=0D=0Aindex 26f5abda96dc..b5f9c8d0349f 10064=
4=0D=0A--- a/fs/famfs/famfs_internal.h=0D=0A+++ b/fs/famfs/famfs_internal=
=2Eh=0D=0A@@ -15,8 +15,52 @@=0D=0A #include <linux/bits.h>=0D=0A #include=
 <linux/build_bug.h>=0D=0A=20=0D=0A+#include <linux/famfs_ioctl.h>=0D=0A+=
=0D=0A extern const struct file_operations famfs_file_operations;=0D=0A=20=
=0D=0A+/*=0D=0A+ * Internal sanity bound on a FAMFSIOC_MAP_CREATE fmap me=
ssage. The ABI does=0D=0A+ * not advertise a maximum (the message is self=
-describing); this only guards=0D=0A+ * the copy-in against an unreasonab=
le allocation. Oversize is rejected with=0D=0A+ * -EFBIG.=0D=0A+ */=0D=0A=
+#define FAMFS_FMAP_MSG_MAX (4 * 1024 * 1024)=0D=0A+=0D=0A+struct famfs_m=
eta_simple_ext {=0D=0A+=09u64 dev_index;=0D=0A+=09u64 ext_offset;=0D=0A+=09=
u64 ext_len;=0D=0A+};=0D=0A+=0D=0A+struct famfs_meta_interleaved_ext {=0D=
=0A+=09u64 fie_nstrips;=0D=0A+=09u64 fie_chunk_size;=0D=0A+=09u64 fie_nby=
tes;=0D=0A+=09struct famfs_meta_simple_ext *ie_strips;=0D=0A+};=0D=0A+=0D=
=0A+/*=0D=0A+ * Each famfs dax file has this hanging from its inode->i_pr=
ivate.=0D=0A+ */=0D=0A+struct famfs_file_meta {=0D=0A+=09bool            =
       error;=0D=0A+=09enum famfs_file_type   file_type;=0D=0A+=09size_t =
                file_size;=0D=0A+=09enum famfs_ioc_ext_type fm_extent_typ=
e;=0D=0A+=09u64                    dev_bitmap; /* referenced daxdev indic=
es */=0D=0A+=09union { /* This will make code a bit more readable */=0D=0A=
+=09=09struct {=0D=0A+=09=09=09size_t         fm_nextents;=0D=0A+=09=09=09=
struct famfs_meta_simple_ext  *se;=0D=0A+=09=09};=0D=0A+=09=09struct {=0D=
=0A+=09=09=09size_t         fm_niext;=0D=0A+=09=09=09struct famfs_meta_in=
terleaved_ext *ie;=0D=0A+=09=09};=0D=0A+=09};=0D=0A+};=0D=0A+=0D=0A struc=
t famfs_mount_opts {=0D=0A =09umode_t mode;=0D=0A };=0D=0A@@ -83,4 +127,6=
 @@ int famfs_devlist_alloc(struct famfs_fs_info *fsi);=0D=0A int famfs_i=
nstall_daxdev(struct famfs_fs_info *fsi, struct super_block *sb,=0D=0A =09=
=09=09 u64 index, dev_t devno, const char *name);=0D=0A=20=0D=0A+void fam=
fs_meta_free(struct famfs_file_meta *map);=0D=0A+=0D=0A #endif /* FAMFS_I=
NTERNAL_H */=0D=0Adiff --git a/include/uapi/linux/famfs_ioctl.h b/include=
/uapi/linux/famfs_ioctl.h=0D=0Anew file mode 100644=0D=0Aindex 0000000000=
00..b4eb373c1ade=0D=0A--- /dev/null=0D=0A+++ b/include/uapi/linux/famfs_i=
octl.h=0D=0A@@ -0,0 +1,91 @@=0D=0A+/* SPDX-License-Identifier: GPL-2.0 WI=
TH Linux-syscall-note */=0D=0A+/*=0D=0A+ * famfs - dax file system for sh=
ared fabric-attached memory=0D=0A+ *=0D=0A+ * Copyright 2023-2024 Micron =
Technology, Inc.=0D=0A+ *=0D=0A+ * This file system, originally based on =
ramfs the dax support from xfs,=0D=0A+ * is intended to allow multiple ho=
st systems to mount a common file system=0D=0A+ * view of dax files that =
map to shared memory.=0D=0A+ */=0D=0A+#ifndef FAMFS_IOCTL_H=0D=0A+#define=
 FAMFS_IOCTL_H=0D=0A+=0D=0A+#include <linux/ioctl.h>=0D=0A+#include <linu=
x/uuid.h>=0D=0A+=0D=0A+#define FAMFS_KABI_VERSION 44=0D=0A+=0D=0A+enum fa=
mfs_file_type {=0D=0A+=09FAMFS_REG,=0D=0A+=09FAMFS_SUPERBLOCK,=0D=0A+=09F=
AMFS_LOG,=0D=0A+};=0D=0A+=0D=0A+/*=0D=0A+ * Extent type in a famfs fmap m=
essage, and of the in-core map=0D=0A+ * (famfs_file_meta.fm_extent_type).=
=0D=0A+ */=0D=0A+enum famfs_ioc_ext_type {=0D=0A+=09FAMFS_IOC_EXT_SIMPLE,=
=0D=0A+=09FAMFS_IOC_EXT_INTERLEAVE,=0D=0A+};=0D=0A+=0D=0A+/*=0D=0A+ * The=
 FAMFSIOC_MAP_CREATE payload is a self-describing fmap message: a=0D=0A+ =
* struct famfs_ioc_fmap_header immediately followed by @nextents extent=0D=
=0A+ * records. @fmap_size gives the total message length, so a reader is=
=0D=0A+ * self-delimiting.=0D=0A+ *=0D=0A+ * For ext_type =3D=3D FAMFS_IO=
C_EXT_SIMPLE the records are an array of=0D=0A+ * @nextents famfs_ioc_sim=
ple_ext. For ext_type =3D=3D FAMFS_IOC_EXT_INTERLEAVE=0D=0A+ * each of th=
e @nextents records is a famfs_ioc_iext header immediately=0D=0A+ * follo=
wed by ie_nstrips famfs_ioc_simple_ext strip extents.=0D=0A+ *=0D=0A+ * T=
his wire layout is byte-identical to the fmap carried in a fuse famfs=0D=0A=
+ * GET_FMAP reply, so the same userspace serializer emits both.=0D=0A+ *=
=0D=0A+ * The message is self-describing (@fmap_size bounds it), so neith=
er the extent=0D=0A+ * and strip counts nor the total size are capped by =
this ABI. The kernel=0D=0A+ * applies an internal sanity limit to the cop=
y-in and returns -EFBIG for a=0D=0A+ * message larger than it will accept=
=2E=0D=0A+ */=0D=0A+#define FAMFS_FMAP_VERSION 1=0D=0A+=0D=0A+struct famf=
s_ioc_simple_ext {=0D=0A+=09__u32 se_devindex;=0D=0A+=09__u32 reserved;=0D=
=0A+=09__u64 se_offset;=0D=0A+=09__u64 se_len;=0D=0A+};=0D=0A+=0D=0A+stru=
ct famfs_ioc_iext {=09=09/* interleaved (striped) extent */=0D=0A+=09__u3=
2 ie_nstrips;=0D=0A+=09__u32 ie_chunk_size;=0D=0A+=09__u64 ie_nbytes;=09/=
* total bytes mapped by this interleaved extent */=0D=0A+=09__u64 reserve=
d;=0D=0A+};=0D=0A+=0D=0A+struct famfs_ioc_fmap_header {=0D=0A+=09__u8  fi=
le_type;=09/* enum famfs_file_type */=0D=0A+=09__u8  reserved;=0D=0A+=09_=
_u16 fmap_version;=09/* FAMFS_FMAP_VERSION */=0D=0A+=09__u32 ext_type;=09=
=09/* enum famfs_ioc_ext_type */=0D=0A+=09__u32 nextents;=0D=0A+=09__u32 =
fmap_size;=09/* total message bytes, including this header */=0D=0A+=09__=
u64 file_size;=0D=0A+=09__u64 reserved1;=0D=0A+};=0D=0A+=0D=0A+#define FA=
MFSIOC_MAGIC 'u'=0D=0A+=0D=0A+/* famfs file ioctl opcodes */=0D=0A+#defin=
e FAMFSIOC_NOP           _IO(FAMFSIOC_MAGIC,   0x50)=0D=0A+=0D=0A+/*=0D=0A=
+ * MAP_CREATE carries the self-describing fmap message - struct=0D=0A+ *=
 famfs_ioc_fmap_header followed by the extent list (see above).=0D=0A+ */=
=0D=0A+#define FAMFSIOC_MAP_CREATE    _IOW(FAMFSIOC_MAGIC,  0x51, struct =
famfs_ioc_fmap_header)=0D=0A+=0D=0A+#endif /* FAMFS_IOCTL_H */=0D=0A--=20=
=0D=0A2.53.0=0D=0A=0D=0A