Add new FUSE operations and capability for famfs DAX file mapping:

- FUSE_CAP_DAX_FMAP: New capability flag at bit 32 (using want_ext/capable_ext
  fields) to indicate kernel and userspace support for DAX fmaps

- GET_FMAP: New operation to retrieve a file map for DAX-mapped files.
  Returns a fuse_famfs_fmap_header followed by simple or interleaved
  extent descriptors. The kernel passes the file size as an argument.

- GET_DAXDEV: New operation to retrieve DAX device info by index.
  Called when GET_FMAP returns an fmap referencing a previously
  unknown DAX device.

These operations enable FUSE filesystems to provide direct access
mappings to persistent memory, allowing the kernel to map files
directly to DAX devices without page cache intermediation.

Signed-off-by: John Groves <[email protected]>
---
 include/fuse_common.h   |  5 +++++
 include/fuse_lowlevel.h | 37 +++++++++++++++++++++++++++++++++++++
 lib/fuse_lowlevel.c     | 31 ++++++++++++++++++++++++++++++-
 3 files changed, 72 insertions(+), 1 deletion(-)

diff --git a/include/fuse_common.h b/include/fuse_common.h
index 041188e..e428ddb 100644
--- a/include/fuse_common.h
+++ b/include/fuse_common.h
@@ -512,6 +512,11 @@ struct fuse_loop_config_v1 {
  */
 #define FUSE_CAP_OVER_IO_URING (1UL << 31)
 
+/**
+ * handle files that use famfs dax fmaps
+ */
+#define FUSE_CAP_DAX_FMAP (1UL<<32)
+
 /**
  * Ioctl flags
  *
diff --git a/include/fuse_lowlevel.h b/include/fuse_lowlevel.h
index d2bbcca..55fcfd7 100644
--- a/include/fuse_lowlevel.h
+++ b/include/fuse_lowlevel.h
@@ -1341,6 +1341,43 @@ struct fuse_lowlevel_ops {
         */
        void (*statx)(fuse_req_t req, fuse_ino_t ino, int flags, int mask,
                      struct fuse_file_info *fi);
+
+       /**
+        * Get a famfs/devdax/fsdax fmap
+        *
+        * Retrieve a file map (aka fmap) for a previously looked-up file.
+        * The fmap is serialized into the buffer, anchored by
+        * struct fuse_famfs_fmap_header, followed by one or more
+        * structs fuse_famfs_simple_ext, or fuse_famfs_iext (which itself
+        * is followed by one or more fuse_famfs_simple_ext...
+        *
+        * Valid replies:
+        *    fuse_reply_buf  (TODO: variable-size reply)
+        *    fuse_reply_err
+        *
+        * @param req request handle
+        * @param ino the inode number
+        */
+       void (*get_fmap) (fuse_req_t req, fuse_ino_t ino, size_t size);
+
+       /**
+        * Get a daxdev by index
+        *
+        * Retrieve info on a daxdev by index. This will be called any time
+        * GET_FMAP has returned a file map that references a previously
+        * unused daxdev. struct famfs_simple_ext, which is used for all
+        * resolutions to daxdev offsets, references daxdevs by index.
+        * In user space we maintain a master list of all referenced daxdevs
+        * by index, which is queried by get_daxdev.
+        *
+        * Valid replies:
+        *    fuse_reply_buf
+        *    fuse_reply_err
+        *
+        * @param req request handle
+        * @param ino the index of the daxdev
+        */
+       void (*get_daxdev) (fuse_req_t req, int daxdev_index);
 };
 
 /**
diff --git a/lib/fuse_lowlevel.c b/lib/fuse_lowlevel.c
index 413e7c3..c3adfa2 100644
--- a/lib/fuse_lowlevel.c
+++ b/lib/fuse_lowlevel.c
@@ -2769,7 +2769,8 @@ _do_init(fuse_req_t req, const fuse_ino_t nodeid, const 
void *op_in,
                        se->conn.capable_ext |= FUSE_CAP_NO_EXPORT_SUPPORT;
                if (inargflags & FUSE_OVER_IO_URING)
                        se->conn.capable_ext |= FUSE_CAP_OVER_IO_URING;
-
+               if (inargflags & FUSE_DAX_FMAP)
+                       se->conn.capable_ext |= FUSE_CAP_DAX_FMAP;
        } else {
                se->conn.max_readahead = 0;
        }
@@ -2932,6 +2933,8 @@ _do_init(fuse_req_t req, const fuse_ino_t nodeid, const 
void *op_in,
                outargflags |= FUSE_REQUEST_TIMEOUT;
                outarg.request_timeout = se->conn.request_timeout;
        }
+       if (se->conn.want_ext & FUSE_CAP_DAX_FMAP)
+               outargflags |= FUSE_DAX_FMAP;
 
        outarg.max_readahead = se->conn.max_readahead;
        outarg.max_write = se->conn.max_write;
@@ -3035,6 +3038,30 @@ static void do_destroy(fuse_req_t req, fuse_ino_t 
nodeid, const void *inarg)
        _do_destroy(req, nodeid, inarg, NULL);
 }
 
+static void
+do_get_fmap(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+       struct fuse_session *se = req->se;
+       struct fuse_getxattr_in *arg = (struct fuse_getxattr_in *) inarg;
+
+       if (se->op.get_fmap)
+               se->op.get_fmap(req, nodeid, arg->size);
+       else
+               fuse_reply_err(req, -EOPNOTSUPP);
+}
+
+static void
+do_get_daxdev(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+       struct fuse_session *se = req->se;
+       (void)inarg;
+
+       if (se->op.get_daxdev)
+               se->op.get_daxdev(req, nodeid); /* Use nodeid as daxdev_index */
+       else
+               fuse_reply_err(req, -EOPNOTSUPP);
+}
+
 static void list_del_nreq(struct fuse_notify_req *nreq)
 {
        struct fuse_notify_req *prev = nreq->prev;
@@ -3470,6 +3497,8 @@ static struct {
        [FUSE_LSEEK]       = { do_lseek,       "LSEEK"       },
        [FUSE_STATX]       = { do_statx,       "STATX"       },
        [CUSE_INIT]        = { cuse_lowlevel_init, "CUSE_INIT"   },
+       [FUSE_GET_FMAP]    = { do_get_fmap, "GET_FMAP"       },
+       [FUSE_GET_DAXDEV]  = { do_get_daxdev, "GET_DAXDEV"   },
 };
 
 static struct {
-- 
2.49.0


Reply via email to