Merge tag 'nds32-for-linux-5.12' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux-2.6-microblaze.git] / kernel / resource.c
index 833394f..627e61b 100644 (file)
 #include <linux/spinlock.h>
 #include <linux/fs.h>
 #include <linux/proc_fs.h>
+#include <linux/pseudo_fs.h>
 #include <linux/sched.h>
 #include <linux/seq_file.h>
 #include <linux/device.h>
 #include <linux/pfn.h>
 #include <linux/mm.h>
+#include <linux/mount.h>
 #include <linux/resource_ext.h>
+#include <uapi/linux/magic.h>
 #include <asm/io.h>
 
 
@@ -1119,6 +1122,55 @@ resource_size_t resource_alignment(struct resource *res)
 
 static DECLARE_WAIT_QUEUE_HEAD(muxed_resource_wait);
 
+static struct inode *iomem_inode;
+
+#ifdef CONFIG_IO_STRICT_DEVMEM
+static void revoke_iomem(struct resource *res)
+{
+       /* pairs with smp_store_release() in iomem_init_inode() */
+       struct inode *inode = smp_load_acquire(&iomem_inode);
+
+       /*
+        * Check that the initialization has completed. Losing the race
+        * is ok because it means drivers are claiming resources before
+        * the fs_initcall level of init and prevent iomem_get_mapping users
+        * from establishing mappings.
+        */
+       if (!inode)
+               return;
+
+       /*
+        * The expectation is that the driver has successfully marked
+        * the resource busy by this point, so devmem_is_allowed()
+        * should start returning false, however for performance this
+        * does not iterate the entire resource range.
+        */
+       if (devmem_is_allowed(PHYS_PFN(res->start)) &&
+           devmem_is_allowed(PHYS_PFN(res->end))) {
+               /*
+                * *cringe* iomem=relaxed says "go ahead, what's the
+                * worst that can happen?"
+                */
+               return;
+       }
+
+       unmap_mapping_range(inode->i_mapping, res->start, resource_size(res), 1);
+}
+#else
+static void revoke_iomem(struct resource *res) {}
+#endif
+
+struct address_space *iomem_get_mapping(void)
+{
+       /*
+        * This function is only called from file open paths, hence guaranteed
+        * that fs_initcalls have completed and no need to check for NULL. But
+        * since revoke_iomem can be called before the initcall we still need
+        * the barrier to appease checkers.
+        */
+       return smp_load_acquire(&iomem_inode)->i_mapping;
+}
+
 /**
  * __request_region - create a new busy resource region
  * @parent: parent resource descriptor
@@ -1186,7 +1238,7 @@ struct resource * __request_region(struct resource *parent,
        write_unlock(&resource_lock);
 
        if (res && orig_parent == &iomem_resource)
-               revoke_devmem(res);
+               revoke_iomem(res);
 
        return res;
 }
@@ -1786,4 +1838,48 @@ static int __init strict_iomem(char *str)
        return 1;
 }
 
+static int iomem_fs_init_fs_context(struct fs_context *fc)
+{
+       return init_pseudo(fc, DEVMEM_MAGIC) ? 0 : -ENOMEM;
+}
+
+static struct file_system_type iomem_fs_type = {
+       .name           = "iomem",
+       .owner          = THIS_MODULE,
+       .init_fs_context = iomem_fs_init_fs_context,
+       .kill_sb        = kill_anon_super,
+};
+
+static int __init iomem_init_inode(void)
+{
+       static struct vfsmount *iomem_vfs_mount;
+       static int iomem_fs_cnt;
+       struct inode *inode;
+       int rc;
+
+       rc = simple_pin_fs(&iomem_fs_type, &iomem_vfs_mount, &iomem_fs_cnt);
+       if (rc < 0) {
+               pr_err("Cannot mount iomem pseudo filesystem: %d\n", rc);
+               return rc;
+       }
+
+       inode = alloc_anon_inode(iomem_vfs_mount->mnt_sb);
+       if (IS_ERR(inode)) {
+               rc = PTR_ERR(inode);
+               pr_err("Cannot allocate inode for iomem: %d\n", rc);
+               simple_release_fs(&iomem_vfs_mount, &iomem_fs_cnt);
+               return rc;
+       }
+
+       /*
+        * Publish iomem revocation inode initialized.
+        * Pairs with smp_load_acquire() in revoke_iomem().
+        */
+       smp_store_release(&iomem_inode, inode);
+
+       return 0;
+}
+
+fs_initcall(iomem_init_inode);
+
 __setup("iomem=", strict_iomem);