Commit b4caecd4 authored by Christoph Hellwig's avatar Christoph Hellwig Committed by Jens Axboe
Browse files

fs: introduce f_op->mmap_capabilities for nommu mmap support



Since "BDI: Provide backing device capability information [try #3]" the
backing_dev_info structure also provides flags for the kind of mmap
operation available in a nommu environment, which is entirely unrelated
to it's original purpose.

Introduce a new nommu-only file operation to provide this information to
the nommu mmap code instead.  Splitting this from the backing_dev_info
structure allows to remove lots of backing_dev_info instance that aren't
otherwise needed, and entirely gets rid of the concept of providing a
backing_dev_info for a character device.  It also removes the need for
the mtd_inodefs filesystem.

Signed-off-by: default avatarChristoph Hellwig <hch@lst.de>
Reviewed-by: default avatarTejun Heo <tj@kernel.org>
Acked-by: default avatarBrian Norris <computersforpeace@gmail.com>
Signed-off-by: default avatarJens Axboe <axboe@fb.com>
parent 97b713ba
Loading
Loading
Loading
Loading
+4 −4
Original line number Original line Diff line number Diff line
@@ -43,12 +43,12 @@ and it's also much more restricted in the latter case:
           even if this was created by another process.
           even if this was created by another process.


         - If possible, the file mapping will be directly on the backing device
         - If possible, the file mapping will be directly on the backing device
           if the backing device has the BDI_CAP_MAP_DIRECT capability and
           if the backing device has the NOMMU_MAP_DIRECT capability and
           appropriate mapping protection capabilities. Ramfs, romfs, cramfs
           appropriate mapping protection capabilities. Ramfs, romfs, cramfs
           and mtd might all permit this.
           and mtd might all permit this.


	 - If the backing device device can't or won't permit direct sharing,
	 - If the backing device device can't or won't permit direct sharing,
           but does have the BDI_CAP_MAP_COPY capability, then a copy of the
           but does have the NOMMU_MAP_COPY capability, then a copy of the
           appropriate bit of the file will be read into a contiguous bit of
           appropriate bit of the file will be read into a contiguous bit of
           memory and any extraneous space beyond the EOF will be cleared
           memory and any extraneous space beyond the EOF will be cleared


@@ -220,7 +220,7 @@ directly (can't be copied).


The file->f_op->mmap() operation will be called to actually inaugurate the
The file->f_op->mmap() operation will be called to actually inaugurate the
mapping. It can be rejected at that point. Returning the ENOSYS error will
mapping. It can be rejected at that point. Returning the ENOSYS error will
cause the mapping to be copied instead if BDI_CAP_MAP_COPY is specified.
cause the mapping to be copied instead if NOMMU_MAP_COPY is specified.


The vm_ops->close() routine will be invoked when the last mapping on a chardev
The vm_ops->close() routine will be invoked when the last mapping on a chardev
is removed. An existing mapping will be shared, partially or not, if possible
is removed. An existing mapping will be shared, partially or not, if possible
@@ -232,7 +232,7 @@ want to handle it, despite the fact it's got an operation. For instance, it
might try directing the call to a secondary driver which turns out not to
might try directing the call to a secondary driver which turns out not to
implement it. Such is the case for the framebuffer driver which attempts to
implement it. Such is the case for the framebuffer driver which attempts to
direct the call to the device-specific driver. Under such circumstances, the
direct the call to the device-specific driver. Under such circumstances, the
mapping request will be rejected if BDI_CAP_MAP_COPY is not specified, and a
mapping request will be rejected if NOMMU_MAP_COPY is not specified, and a
copy mapped otherwise.
copy mapped otherwise.


IMPORTANT NOTE:
IMPORTANT NOTE:
+1 −1
Original line number Original line Diff line number Diff line
@@ -607,7 +607,7 @@ struct request_queue *blk_alloc_queue_node(gfp_t gfp_mask, int node_id)
	q->backing_dev_info.ra_pages =
	q->backing_dev_info.ra_pages =
			(VM_MAX_READAHEAD * 1024) / PAGE_CACHE_SIZE;
			(VM_MAX_READAHEAD * 1024) / PAGE_CACHE_SIZE;
	q->backing_dev_info.state = 0;
	q->backing_dev_info.state = 0;
	q->backing_dev_info.capabilities = BDI_CAP_MAP_COPY;
	q->backing_dev_info.capabilities = 0;
	q->backing_dev_info.name = "block";
	q->backing_dev_info.name = "block";
	q->node = node_id;
	q->node = node_id;


+32 −32
Original line number Original line Diff line number Diff line
@@ -287,13 +287,24 @@ static unsigned long get_unmapped_area_mem(struct file *file,
	return pgoff << PAGE_SHIFT;
	return pgoff << PAGE_SHIFT;
}
}


/* permit direct mmap, for read, write or exec */
static unsigned memory_mmap_capabilities(struct file *file)
{
	return NOMMU_MAP_DIRECT |
		NOMMU_MAP_READ | NOMMU_MAP_WRITE | NOMMU_MAP_EXEC;
}

static unsigned zero_mmap_capabilities(struct file *file)
{
	return NOMMU_MAP_COPY;
}

/* can't do an in-place private mapping if there's no MMU */
/* can't do an in-place private mapping if there's no MMU */
static inline int private_mapping_ok(struct vm_area_struct *vma)
static inline int private_mapping_ok(struct vm_area_struct *vma)
{
{
	return vma->vm_flags & VM_MAYSHARE;
	return vma->vm_flags & VM_MAYSHARE;
}
}
#else
#else
#define get_unmapped_area_mem	NULL


static inline int private_mapping_ok(struct vm_area_struct *vma)
static inline int private_mapping_ok(struct vm_area_struct *vma)
{
{
@@ -721,7 +732,10 @@ static const struct file_operations mem_fops = {
	.write		= write_mem,
	.write		= write_mem,
	.mmap		= mmap_mem,
	.mmap		= mmap_mem,
	.open		= open_mem,
	.open		= open_mem,
#ifndef CONFIG_MMU
	.get_unmapped_area = get_unmapped_area_mem,
	.get_unmapped_area = get_unmapped_area_mem,
	.mmap_capabilities = memory_mmap_capabilities,
#endif
};
};


#ifdef CONFIG_DEVKMEM
#ifdef CONFIG_DEVKMEM
@@ -731,7 +745,10 @@ static const struct file_operations kmem_fops = {
	.write		= write_kmem,
	.write		= write_kmem,
	.mmap		= mmap_kmem,
	.mmap		= mmap_kmem,
	.open		= open_kmem,
	.open		= open_kmem,
#ifndef CONFIG_MMU
	.get_unmapped_area = get_unmapped_area_mem,
	.get_unmapped_area = get_unmapped_area_mem,
	.mmap_capabilities = memory_mmap_capabilities,
#endif
};
};
#endif
#endif


@@ -760,16 +777,9 @@ static const struct file_operations zero_fops = {
	.read_iter	= read_iter_zero,
	.read_iter	= read_iter_zero,
	.aio_write	= aio_write_zero,
	.aio_write	= aio_write_zero,
	.mmap		= mmap_zero,
	.mmap		= mmap_zero,
};
#ifndef CONFIG_MMU

	.mmap_capabilities = zero_mmap_capabilities,
/*
#endif
 * capabilities for /dev/zero
 * - permits private mappings, "copies" are taken of the source of zeros
 * - no writeback happens
 */
static struct backing_dev_info zero_bdi = {
	.name		= "char/mem",
	.capabilities	= BDI_CAP_MAP_COPY | BDI_CAP_NO_ACCT_AND_WRITEBACK,
};
};


static const struct file_operations full_fops = {
static const struct file_operations full_fops = {
@@ -783,22 +793,22 @@ static const struct memdev {
	const char *name;
	const char *name;
	umode_t mode;
	umode_t mode;
	const struct file_operations *fops;
	const struct file_operations *fops;
	struct backing_dev_info *dev_info;
	fmode_t fmode;
} devlist[] = {
} devlist[] = {
	 [1] = { "mem", 0, &mem_fops, &directly_mappable_cdev_bdi },
	 [1] = { "mem", 0, &mem_fops, FMODE_UNSIGNED_OFFSET },
#ifdef CONFIG_DEVKMEM
#ifdef CONFIG_DEVKMEM
	 [2] = { "kmem", 0, &kmem_fops, &directly_mappable_cdev_bdi },
	 [2] = { "kmem", 0, &kmem_fops, FMODE_UNSIGNED_OFFSET },
#endif
#endif
	 [3] = { "null", 0666, &null_fops, NULL },
	 [3] = { "null", 0666, &null_fops, 0 },
#ifdef CONFIG_DEVPORT
#ifdef CONFIG_DEVPORT
	 [4] = { "port", 0, &port_fops, NULL },
	 [4] = { "port", 0, &port_fops, 0 },
#endif
#endif
	 [5] = { "zero", 0666, &zero_fops, &zero_bdi },
	 [5] = { "zero", 0666, &zero_fops, 0 },
	 [7] = { "full", 0666, &full_fops, NULL },
	 [7] = { "full", 0666, &full_fops, 0 },
	 [8] = { "random", 0666, &random_fops, NULL },
	 [8] = { "random", 0666, &random_fops, 0 },
	 [9] = { "urandom", 0666, &urandom_fops, NULL },
	 [9] = { "urandom", 0666, &urandom_fops, 0 },
#ifdef CONFIG_PRINTK
#ifdef CONFIG_PRINTK
	[11] = { "kmsg", 0644, &kmsg_fops, NULL },
	[11] = { "kmsg", 0644, &kmsg_fops, 0 },
#endif
#endif
};
};


@@ -816,12 +826,7 @@ static int memory_open(struct inode *inode, struct file *filp)
		return -ENXIO;
		return -ENXIO;


	filp->f_op = dev->fops;
	filp->f_op = dev->fops;
	if (dev->dev_info)
	filp->f_mode |= dev->fmode;
		filp->f_mapping->backing_dev_info = dev->dev_info;

	/* Is /dev/mem or /dev/kmem ? */
	if (dev->dev_info == &directly_mappable_cdev_bdi)
		filp->f_mode |= FMODE_UNSIGNED_OFFSET;


	if (dev->fops->open)
	if (dev->fops->open)
		return dev->fops->open(inode, filp);
		return dev->fops->open(inode, filp);
@@ -846,11 +851,6 @@ static struct class *mem_class;
static int __init chr_dev_init(void)
static int __init chr_dev_init(void)
{
{
	int minor;
	int minor;
	int err;

	err = bdi_init(&zero_bdi);
	if (err)
		return err;


	if (register_chrdev(MEM_MAJOR, "mem", &memory_fops))
	if (register_chrdev(MEM_MAJOR, "mem", &memory_fops))
		printk("unable to get major %d for memory devs\n", MEM_MAJOR);
		printk("unable to get major %d for memory devs\n", MEM_MAJOR);
+10 −62
Original line number Original line Diff line number Diff line
@@ -49,7 +49,6 @@ static DEFINE_MUTEX(mtd_mutex);
 */
 */
struct mtd_file_info {
struct mtd_file_info {
	struct mtd_info *mtd;
	struct mtd_info *mtd;
	struct inode *ino;
	enum mtd_file_modes mode;
	enum mtd_file_modes mode;
};
};


@@ -59,10 +58,6 @@ static loff_t mtdchar_lseek(struct file *file, loff_t offset, int orig)
	return fixed_size_llseek(file, offset, orig, mfi->mtd->size);
	return fixed_size_llseek(file, offset, orig, mfi->mtd->size);
}
}


static int count;
static struct vfsmount *mnt;
static struct file_system_type mtd_inodefs_type;

static int mtdchar_open(struct inode *inode, struct file *file)
static int mtdchar_open(struct inode *inode, struct file *file)
{
{
	int minor = iminor(inode);
	int minor = iminor(inode);
@@ -70,7 +65,6 @@ static int mtdchar_open(struct inode *inode, struct file *file)
	int ret = 0;
	int ret = 0;
	struct mtd_info *mtd;
	struct mtd_info *mtd;
	struct mtd_file_info *mfi;
	struct mtd_file_info *mfi;
	struct inode *mtd_ino;


	pr_debug("MTD_open\n");
	pr_debug("MTD_open\n");


@@ -78,10 +72,6 @@ static int mtdchar_open(struct inode *inode, struct file *file)
	if ((file->f_mode & FMODE_WRITE) && (minor & 1))
	if ((file->f_mode & FMODE_WRITE) && (minor & 1))
		return -EACCES;
		return -EACCES;


	ret = simple_pin_fs(&mtd_inodefs_type, &mnt, &count);
	if (ret)
		return ret;

	mutex_lock(&mtd_mutex);
	mutex_lock(&mtd_mutex);
	mtd = get_mtd_device(NULL, devnum);
	mtd = get_mtd_device(NULL, devnum);


@@ -95,43 +85,26 @@ static int mtdchar_open(struct inode *inode, struct file *file)
		goto out1;
		goto out1;
	}
	}


	mtd_ino = iget_locked(mnt->mnt_sb, devnum);
	if (!mtd_ino) {
		ret = -ENOMEM;
		goto out1;
	}
	if (mtd_ino->i_state & I_NEW) {
		mtd_ino->i_private = mtd;
		mtd_ino->i_mode = S_IFCHR;
		mtd_ino->i_data.backing_dev_info = mtd->backing_dev_info;
		unlock_new_inode(mtd_ino);
	}
	file->f_mapping = mtd_ino->i_mapping;

	/* You can't open it RW if it's not a writeable device */
	/* You can't open it RW if it's not a writeable device */
	if ((file->f_mode & FMODE_WRITE) && !(mtd->flags & MTD_WRITEABLE)) {
	if ((file->f_mode & FMODE_WRITE) && !(mtd->flags & MTD_WRITEABLE)) {
		ret = -EACCES;
		ret = -EACCES;
		goto out2;
		goto out1;
	}
	}


	mfi = kzalloc(sizeof(*mfi), GFP_KERNEL);
	mfi = kzalloc(sizeof(*mfi), GFP_KERNEL);
	if (!mfi) {
	if (!mfi) {
		ret = -ENOMEM;
		ret = -ENOMEM;
		goto out2;
		goto out1;
	}
	}
	mfi->ino = mtd_ino;
	mfi->mtd = mtd;
	mfi->mtd = mtd;
	file->private_data = mfi;
	file->private_data = mfi;
	mutex_unlock(&mtd_mutex);
	mutex_unlock(&mtd_mutex);
	return 0;
	return 0;


out2:
	iput(mtd_ino);
out1:
out1:
	put_mtd_device(mtd);
	put_mtd_device(mtd);
out:
out:
	mutex_unlock(&mtd_mutex);
	mutex_unlock(&mtd_mutex);
	simple_release_fs(&mnt, &count);
	return ret;
	return ret;
} /* mtdchar_open */
} /* mtdchar_open */


@@ -148,12 +121,9 @@ static int mtdchar_close(struct inode *inode, struct file *file)
	if ((file->f_mode & FMODE_WRITE))
	if ((file->f_mode & FMODE_WRITE))
		mtd_sync(mtd);
		mtd_sync(mtd);


	iput(mfi->ino);

	put_mtd_device(mtd);
	put_mtd_device(mtd);
	file->private_data = NULL;
	file->private_data = NULL;
	kfree(mfi);
	kfree(mfi);
	simple_release_fs(&mnt, &count);


	return 0;
	return 0;
} /* mtdchar_close */
} /* mtdchar_close */
@@ -1117,6 +1087,13 @@ static unsigned long mtdchar_get_unmapped_area(struct file *file,
	ret = mtd_get_unmapped_area(mtd, len, offset, flags);
	ret = mtd_get_unmapped_area(mtd, len, offset, flags);
	return ret == -EOPNOTSUPP ? -ENODEV : ret;
	return ret == -EOPNOTSUPP ? -ENODEV : ret;
}
}

static unsigned mtdchar_mmap_capabilities(struct file *file)
{
	struct mtd_file_info *mfi = file->private_data;

	return mtd_mmap_capabilities(mfi->mtd);
}
#endif
#endif


/*
/*
@@ -1160,27 +1137,10 @@ static const struct file_operations mtd_fops = {
	.mmap		= mtdchar_mmap,
	.mmap		= mtdchar_mmap,
#ifndef CONFIG_MMU
#ifndef CONFIG_MMU
	.get_unmapped_area = mtdchar_get_unmapped_area,
	.get_unmapped_area = mtdchar_get_unmapped_area,
	.mmap_capabilities = mtdchar_mmap_capabilities,
#endif
#endif
};
};


static const struct super_operations mtd_ops = {
	.drop_inode = generic_delete_inode,
	.statfs = simple_statfs,
};

static struct dentry *mtd_inodefs_mount(struct file_system_type *fs_type,
				int flags, const char *dev_name, void *data)
{
	return mount_pseudo(fs_type, "mtd_inode:", &mtd_ops, NULL, MTD_INODE_FS_MAGIC);
}

static struct file_system_type mtd_inodefs_type = {
       .name = "mtd_inodefs",
       .mount = mtd_inodefs_mount,
       .kill_sb = kill_anon_super,
};
MODULE_ALIAS_FS("mtd_inodefs");

int __init init_mtdchar(void)
int __init init_mtdchar(void)
{
{
	int ret;
	int ret;
@@ -1193,23 +1153,11 @@ int __init init_mtdchar(void)
		return ret;
		return ret;
	}
	}


	ret = register_filesystem(&mtd_inodefs_type);
	if (ret) {
		pr_err("Can't register mtd_inodefs filesystem, error %d\n",
		       ret);
		goto err_unregister_chdev;
	}

	return ret;

err_unregister_chdev:
	__unregister_chrdev(MTD_CHAR_MAJOR, 0, 1 << MINORBITS, "mtd");
	return ret;
	return ret;
}
}


void __exit cleanup_mtdchar(void)
void __exit cleanup_mtdchar(void)
{
{
	unregister_filesystem(&mtd_inodefs_type);
	__unregister_chrdev(MTD_CHAR_MAJOR, 0, 1 << MINORBITS, "mtd");
	__unregister_chrdev(MTD_CHAR_MAJOR, 0, 1 << MINORBITS, "mtd");
}
}


+0 −10
Original line number Original line Diff line number Diff line
@@ -732,8 +732,6 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c


	concat->mtd.ecc_stats.badblocks = subdev[0]->ecc_stats.badblocks;
	concat->mtd.ecc_stats.badblocks = subdev[0]->ecc_stats.badblocks;


	concat->mtd.backing_dev_info = subdev[0]->backing_dev_info;

	concat->subdev[0] = subdev[0];
	concat->subdev[0] = subdev[0];


	for (i = 1; i < num_devs; i++) {
	for (i = 1; i < num_devs; i++) {
@@ -761,14 +759,6 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c
				    subdev[i]->flags & MTD_WRITEABLE;
				    subdev[i]->flags & MTD_WRITEABLE;
		}
		}


		/* only permit direct mapping if the BDIs are all the same
		 * - copy-mapping is still permitted
		 */
		if (concat->mtd.backing_dev_info !=
		    subdev[i]->backing_dev_info)
			concat->mtd.backing_dev_info =
				&default_backing_dev_info;

		concat->mtd.size += subdev[i]->size;
		concat->mtd.size += subdev[i]->size;
		concat->mtd.ecc_stats.badblocks +=
		concat->mtd.ecc_stats.badblocks +=
			subdev[i]->ecc_stats.badblocks;
			subdev[i]->ecc_stats.badblocks;
Loading