#include <linux/pci.h>
 #include <linux/slab.h>
-#include <linux/anon_inodes.h>
 #include <linux/file.h>
 #include <misc/cxl.h>
-#include <linux/fs.h>
 #include <asm/pnv-pci.h>
 #include <linux/msi.h>
+#include <linux/module.h>
+#include <linux/mount.h>
 
 #include "cxl.h"
 
+/*
+ * Since we want to track memory mappings to be able to force-unmap
+ * when the AFU is no longer reachable, we need an inode. For devices
+ * opened through the cxl user API, this is not a problem, but a
+ * userland process can also get a cxl fd through the cxl_get_fd()
+ * API, which is used by the cxlflash driver.
+ *
+ * Therefore we implement our own simple pseudo-filesystem and inode
+ * allocator. We don't use the anonymous inode, as we need the
+ * meta-data associated with it (address_space) and it is shared by
+ * other drivers/processes, so it could lead to cxl unmapping VMAs
+ * from random processes.
+ */
+
+#define CXL_PSEUDO_FS_MAGIC    0x1697697f
+
+static int cxl_fs_cnt;
+static struct vfsmount *cxl_vfs_mount;
+
+static const struct dentry_operations cxl_fs_dops = {
+       .d_dname        = simple_dname,
+};
+
+static struct dentry *cxl_fs_mount(struct file_system_type *fs_type, int flags,
+                               const char *dev_name, void *data)
+{
+       return mount_pseudo(fs_type, "cxl:", NULL, &cxl_fs_dops,
+                       CXL_PSEUDO_FS_MAGIC);
+}
+
+static struct file_system_type cxl_fs_type = {
+       .name           = "cxl",
+       .owner          = THIS_MODULE,
+       .mount          = cxl_fs_mount,
+       .kill_sb        = kill_anon_super,
+};
+
+
+void cxl_release_mapping(struct cxl_context *ctx)
+{
+       if (ctx->kernelapi && ctx->mapping)
+               simple_release_fs(&cxl_vfs_mount, &cxl_fs_cnt);
+}
+
+static struct file *cxl_getfile(const char *name,
+                               const struct file_operations *fops,
+                               void *priv, int flags)
+{
+       struct qstr this;
+       struct path path;
+       struct file *file;
+       struct inode *inode = NULL;
+       int rc;
+
+       /* strongly inspired by anon_inode_getfile() */
+
+       if (fops->owner && !try_module_get(fops->owner))
+               return ERR_PTR(-ENOENT);
+
+       rc = simple_pin_fs(&cxl_fs_type, &cxl_vfs_mount, &cxl_fs_cnt);
+       if (rc < 0) {
+               pr_err("Cannot mount cxl pseudo filesystem: %d\n", rc);
+               file = ERR_PTR(rc);
+               goto err_module;
+       }
+
+       inode = alloc_anon_inode(cxl_vfs_mount->mnt_sb);
+       if (IS_ERR(inode)) {
+               file = ERR_CAST(inode);
+               goto err_fs;
+       }
+
+       file = ERR_PTR(-ENOMEM);
+       this.name = name;
+       this.len = strlen(name);
+       this.hash = 0;
+       path.dentry = d_alloc_pseudo(cxl_vfs_mount->mnt_sb, &this);
+       if (!path.dentry)
+               goto err_inode;
+
+       path.mnt = mntget(cxl_vfs_mount);
+       d_instantiate(path.dentry, inode);
+
+       file = alloc_file(&path, OPEN_FMODE(flags), fops);
+       if (IS_ERR(file))
+               goto err_dput;
+       file->f_flags = flags & (O_ACCMODE | O_NONBLOCK);
+       file->private_data = priv;
+
+       return file;
+
+err_dput:
+       path_put(&path);
+err_inode:
+       iput(inode);
+err_fs:
+       simple_release_fs(&cxl_vfs_mount, &cxl_fs_cnt);
+err_module:
+       module_put(fops->owner);
+       return file;
+}
+
 struct cxl_context *cxl_dev_context_init(struct pci_dev *dev)
 {
-       struct address_space *mapping;
        struct cxl_afu *afu;
        struct cxl_context  *ctx;
        int rc;
 
        ctx->kernelapi = true;
 
-       /*
-        * Make our own address space since we won't have one from the
-        * filesystem like the user api has, and even if we do associate a file
-        * with this context we don't want to use the global anonymous inode's
-        * address space as that can invalidate unrelated users:
-        */
-       mapping = kmalloc(sizeof(struct address_space), GFP_KERNEL);
-       if (!mapping) {
-               rc = -ENOMEM;
-               goto err_ctx;
-       }
-       address_space_init_once(mapping);
-
        /* Make it a slave context.  We can promote it later? */
-       rc = cxl_context_init(ctx, afu, false, mapping);
+       rc = cxl_context_init(ctx, afu, false);
        if (rc)
-               goto err_mapping;
+               goto err_ctx;
 
        return ctx;
 
-err_mapping:
-       kfree(mapping);
 err_ctx:
        kfree(ctx);
        return ERR_PTR(rc);
 {
        struct file *file;
        int rc, flags, fdtmp;
+       char *name = NULL;
+
+       /* only allow one per context */
+       if (ctx->mapping)
+               return ERR_PTR(-EEXIST);
 
        flags = O_RDWR | O_CLOEXEC;
 
        } else /* use default ops */
                fops = (struct file_operations *)&afu_fops;
 
-       file = anon_inode_getfile("cxl", fops, ctx, flags);
+       name = kasprintf(GFP_KERNEL, "cxl:%d", ctx->pe);
+       file = cxl_getfile(name, fops, ctx, flags);
+       kfree(name);
        if (IS_ERR(file))
                goto err_fd;
 
-       file->f_mapping = ctx->mapping;
-
+       cxl_context_set_mapping(ctx, file->f_mapping);
        *fd = fdtmp;
        return file;
 
 
 /*
  * Initialises a CXL context.
  */
-int cxl_context_init(struct cxl_context *ctx, struct cxl_afu *afu, bool master,
-                    struct address_space *mapping)
+int cxl_context_init(struct cxl_context *ctx, struct cxl_afu *afu, bool master)
 {
        int i;
 
        ctx->master = master;
        ctx->pid = ctx->glpid = NULL; /* Set in start work ioctl */
        mutex_init(&ctx->mapping_lock);
-       ctx->mapping = mapping;
+       ctx->mapping = NULL;
 
        /*
         * Allocate the segment table before we put it in the IDR so that we
        return 0;
 }
 
+void cxl_context_set_mapping(struct cxl_context *ctx,
+                       struct address_space *mapping)
+{
+       mutex_lock(&ctx->mapping_lock);
+       ctx->mapping = mapping;
+       mutex_unlock(&ctx->mapping_lock);
+}
+
 static int cxl_mmap_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
 {
        struct cxl_context *ctx = vma->vm_file->private_data;
        if (ctx->ff_page)
                __free_page(ctx->ff_page);
        ctx->sstp = NULL;
-       if (ctx->kernelapi)
-               kfree(ctx->mapping);
 
        kfree(ctx->irq_bitmap);
 
 
 void cxl_context_free(struct cxl_context *ctx)
 {
+       if (ctx->kernelapi && ctx->mapping)
+               cxl_release_mapping(ctx);
        mutex_lock(&ctx->afu->contexts_lock);
        idr_remove(&ctx->afu->contexts_idr, ctx->pe);
        mutex_unlock(&ctx->afu->contexts_lock);
 
 void init_cxl_native(void);
 
 struct cxl_context *cxl_context_alloc(void);
-int cxl_context_init(struct cxl_context *ctx, struct cxl_afu *afu, bool master,
-                    struct address_space *mapping);
+int cxl_context_init(struct cxl_context *ctx, struct cxl_afu *afu, bool master);
+void cxl_context_set_mapping(struct cxl_context *ctx,
+                       struct address_space *mapping);
 void cxl_context_free(struct cxl_context *ctx);
 int cxl_context_iomap(struct cxl_context *ctx, struct vm_area_struct *vma);
 unsigned int cxl_map_irq(struct cxl *adapter, irq_hw_number_t hwirq,
 void cxl_stop_trace(struct cxl *cxl);
 int cxl_pci_vphb_add(struct cxl_afu *afu);
 void cxl_pci_vphb_remove(struct cxl_afu *afu);
+void cxl_release_mapping(struct cxl_context *ctx);
 
 extern struct pci_driver cxl_pci_driver;
 extern struct platform_driver cxl_of_driver;