#include "internal.h"
 #include <linux/prefetch.h>
 #include <linux/iomap.h>
-
+#include <linux/dax.h>
 #include <trace/events/erofs.h>
 
 static void erofs_readendio(struct bio *bio)
                return ret;
 
        iomap->bdev = inode->i_sb->s_bdev;
+       iomap->dax_dev = EROFS_I_SB(inode)->dax_dev;
        iomap->offset = map.m_la;
        iomap->length = map.m_llen;
        iomap->flags = 0;
        if (!iov_iter_count(to))
                return 0;
 
+#ifdef CONFIG_FS_DAX
+       if (IS_DAX(iocb->ki_filp->f_mapping->host))
+               return dax_iomap_rw(iocb, to, &erofs_iomap_ops);
+#endif
        if (iocb->ki_flags & IOCB_DIRECT) {
                int err = erofs_prepare_dio(iocb, to);
 
        .direct_IO = noop_direct_IO,
 };
 
+#ifdef CONFIG_FS_DAX
+static vm_fault_t erofs_dax_huge_fault(struct vm_fault *vmf,
+               enum page_entry_size pe_size)
+{
+       return dax_iomap_fault(vmf, pe_size, NULL, NULL, &erofs_iomap_ops);
+}
+
+static vm_fault_t erofs_dax_fault(struct vm_fault *vmf)
+{
+       return erofs_dax_huge_fault(vmf, PE_SIZE_PTE);
+}
+
+static const struct vm_operations_struct erofs_dax_vm_ops = {
+       .fault          = erofs_dax_fault,
+       .huge_fault     = erofs_dax_huge_fault,
+};
+
+static int erofs_file_mmap(struct file *file, struct vm_area_struct *vma)
+{
+       if (!IS_DAX(file_inode(file)))
+               return generic_file_readonly_mmap(file, vma);
+
+       if ((vma->vm_flags & VM_SHARED) && (vma->vm_flags & VM_MAYWRITE))
+               return -EINVAL;
+
+       vma->vm_ops = &erofs_dax_vm_ops;
+       vma->vm_flags |= VM_HUGEPAGE;
+       return 0;
+}
+#else
+#define erofs_file_mmap        generic_file_readonly_mmap
+#endif
+
 const struct file_operations erofs_file_fops = {
        .llseek         = generic_file_llseek,
        .read_iter      = erofs_file_read_iter,
-       .mmap           = generic_file_readonly_mmap,
+       .mmap           = erofs_file_mmap,
        .splice_read    = generic_file_splice_read,
 };
 
 #include <linux/crc32c.h>
 #include <linux/fs_context.h>
 #include <linux/fs_parser.h>
+#include <linux/dax.h>
 #include "xattr.h"
 
 #define CREATE_TRACE_POINTS
        Opt_user_xattr,
        Opt_acl,
        Opt_cache_strategy,
+       Opt_dax,
+       Opt_dax_enum,
        Opt_err
 };
 
        {}
 };
 
+static const struct constant_table erofs_dax_param_enums[] = {
+       {"always",      EROFS_MOUNT_DAX_ALWAYS},
+       {"never",       EROFS_MOUNT_DAX_NEVER},
+       {}
+};
+
 static const struct fs_parameter_spec erofs_fs_parameters[] = {
        fsparam_flag_no("user_xattr",   Opt_user_xattr),
        fsparam_flag_no("acl",          Opt_acl),
        fsparam_enum("cache_strategy",  Opt_cache_strategy,
                     erofs_param_cache_strategy),
+       fsparam_flag("dax",             Opt_dax),
+       fsparam_enum("dax",             Opt_dax_enum, erofs_dax_param_enums),
        {}
 };
 
+static bool erofs_fc_set_dax_mode(struct fs_context *fc, unsigned int mode)
+{
+#ifdef CONFIG_FS_DAX
+       struct erofs_fs_context *ctx = fc->fs_private;
+
+       switch (mode) {
+       case EROFS_MOUNT_DAX_ALWAYS:
+               warnfc(fc, "DAX enabled. Warning: EXPERIMENTAL, use at your own risk");
+               set_opt(ctx, DAX_ALWAYS);
+               clear_opt(ctx, DAX_NEVER);
+               return true;
+       case EROFS_MOUNT_DAX_NEVER:
+               set_opt(ctx, DAX_NEVER);
+               clear_opt(ctx, DAX_ALWAYS);
+               return true;
+       default:
+               DBG_BUGON(1);
+               return false;
+       }
+#else
+       errorfc(fc, "dax options not supported");
+       return false;
+#endif
+}
+
 static int erofs_fc_parse_param(struct fs_context *fc,
                                struct fs_parameter *param)
 {
                errorfc(fc, "compression not supported, cache_strategy ignored");
 #endif
                break;
+       case Opt_dax:
+               if (!erofs_fc_set_dax_mode(fc, EROFS_MOUNT_DAX_ALWAYS))
+                       return -EINVAL;
+               break;
+       case Opt_dax_enum:
+               if (!erofs_fc_set_dax_mode(fc, result.uint_32))
+                       return -EINVAL;
+               break;
        default:
                return -ENOPARAM;
        }
                return -ENOMEM;
 
        sb->s_fs_info = sbi;
+       sbi->dax_dev = fs_dax_get_by_bdev(sb->s_bdev);
        err = erofs_read_superblock(sb);
        if (err)
                return err;
 
+       if (test_opt(ctx, DAX_ALWAYS) &&
+           !bdev_dax_supported(sb->s_bdev, EROFS_BLKSIZ)) {
+               errorfc(fc, "DAX unsupported by block device. Turning off DAX.");
+               clear_opt(ctx, DAX_ALWAYS);
+       }
        sb->s_flags |= SB_RDONLY | SB_NOATIME;
        sb->s_maxbytes = MAX_LFS_FILESIZE;
        sb->s_time_gran = 1;
        sbi = EROFS_SB(sb);
        if (!sbi)
                return;
+       fs_put_dax(sbi->dax_dev);
        kfree(sbi);
        sb->s_fs_info = NULL;
 }
 
 static int erofs_show_options(struct seq_file *seq, struct dentry *root)
 {
-       struct erofs_sb_info *sbi __maybe_unused = EROFS_SB(root->d_sb);
-       struct erofs_fs_context *ctx __maybe_unused = &sbi->ctx;
+       struct erofs_sb_info *sbi = EROFS_SB(root->d_sb);
+       struct erofs_fs_context *ctx = &sbi->ctx;
 
 #ifdef CONFIG_EROFS_FS_XATTR
        if (test_opt(ctx, XATTR_USER))
        else if (ctx->cache_strategy == EROFS_ZIP_CACHE_READAROUND)
                seq_puts(seq, ",cache_strategy=readaround");
 #endif
+       if (test_opt(ctx, DAX_ALWAYS))
+               seq_puts(seq, ",dax=always");
+       if (test_opt(ctx, DAX_NEVER))
+               seq_puts(seq, ",dax=never");
        return 0;
 }