Commit 402d326519c1a4859c527702383f4e60f606ef52
Committed by
David Woodhouse
1 parent
9ce969082e
Exists in
master
and in
39 other branches
NOMMU: Present backing device capabilities for MTD chardevs
Present backing device capabilities for MTD character device files to allow NOMMU mmap to do direct mapping where possible. Signed-off-by: David Howells <dhowells@redhat.com> Tested-by: Bernd Schmidt <bernd.schmidt@analog.com> Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
Showing 10 changed files with 215 additions and 2 deletions Side-by-side Diff
drivers/mtd/Makefile
drivers/mtd/chips/map_ram.c
... | ... | @@ -21,6 +21,8 @@ |
21 | 21 | static int mapram_erase (struct mtd_info *, struct erase_info *); |
22 | 22 | static void mapram_nop (struct mtd_info *); |
23 | 23 | static struct mtd_info *map_ram_probe(struct map_info *map); |
24 | +static unsigned long mapram_unmapped_area(struct mtd_info *, unsigned long, | |
25 | + unsigned long, unsigned long); | |
24 | 26 | |
25 | 27 | |
26 | 28 | static struct mtd_chip_driver mapram_chipdrv = { |
... | ... | @@ -64,6 +66,7 @@ |
64 | 66 | mtd->type = MTD_RAM; |
65 | 67 | mtd->size = map->size; |
66 | 68 | mtd->erase = mapram_erase; |
69 | + mtd->get_unmapped_area = mapram_unmapped_area; | |
67 | 70 | mtd->read = mapram_read; |
68 | 71 | mtd->write = mapram_write; |
69 | 72 | mtd->sync = mapram_nop; |
... | ... | @@ -78,6 +81,20 @@ |
78 | 81 | return mtd; |
79 | 82 | } |
80 | 83 | |
84 | + | |
85 | +/* | |
86 | + * Allow NOMMU mmap() to directly map the device (if not NULL) | |
87 | + * - return the address to which the offset maps | |
88 | + * - return -ENOSYS to indicate refusal to do the mapping | |
89 | + */ | |
90 | +static unsigned long mapram_unmapped_area(struct mtd_info *mtd, | |
91 | + unsigned long len, | |
92 | + unsigned long offset, | |
93 | + unsigned long flags) | |
94 | +{ | |
95 | + struct map_info *map = mtd->priv; | |
96 | + return (unsigned long) map->virt + offset; | |
97 | +} | |
81 | 98 | |
82 | 99 | static int mapram_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) |
83 | 100 | { |
drivers/mtd/chips/map_rom.c
... | ... | @@ -20,6 +20,8 @@ |
20 | 20 | static void maprom_nop (struct mtd_info *); |
21 | 21 | static struct mtd_info *map_rom_probe(struct map_info *map); |
22 | 22 | static int maprom_erase (struct mtd_info *mtd, struct erase_info *info); |
23 | +static unsigned long maprom_unmapped_area(struct mtd_info *, unsigned long, | |
24 | + unsigned long, unsigned long); | |
23 | 25 | |
24 | 26 | static struct mtd_chip_driver maprom_chipdrv = { |
25 | 27 | .probe = map_rom_probe, |
... | ... | @@ -40,6 +42,7 @@ |
40 | 42 | mtd->name = map->name; |
41 | 43 | mtd->type = MTD_ROM; |
42 | 44 | mtd->size = map->size; |
45 | + mtd->get_unmapped_area = maprom_unmapped_area; | |
43 | 46 | mtd->read = maprom_read; |
44 | 47 | mtd->write = maprom_write; |
45 | 48 | mtd->sync = maprom_nop; |
... | ... | @@ -52,6 +55,20 @@ |
52 | 55 | return mtd; |
53 | 56 | } |
54 | 57 | |
58 | + | |
59 | +/* | |
60 | + * Allow NOMMU mmap() to directly map the device (if not NULL) | |
61 | + * - return the address to which the offset maps | |
62 | + * - return -ENOSYS to indicate refusal to do the mapping | |
63 | + */ | |
64 | +static unsigned long maprom_unmapped_area(struct mtd_info *mtd, | |
65 | + unsigned long len, | |
66 | + unsigned long offset, | |
67 | + unsigned long flags) | |
68 | +{ | |
69 | + struct map_info *map = mtd->priv; | |
70 | + return (unsigned long) map->virt + offset; | |
71 | +} | |
55 | 72 | |
56 | 73 | static int maprom_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) |
57 | 74 | { |
drivers/mtd/devices/mtdram.c
... | ... | @@ -65,6 +65,19 @@ |
65 | 65 | { |
66 | 66 | } |
67 | 67 | |
68 | +/* | |
69 | + * Allow NOMMU mmap() to directly map the device (if not NULL) | |
70 | + * - return the address to which the offset maps | |
71 | + * - return -ENOSYS to indicate refusal to do the mapping | |
72 | + */ | |
73 | +static unsigned long ram_get_unmapped_area(struct mtd_info *mtd, | |
74 | + unsigned long len, | |
75 | + unsigned long offset, | |
76 | + unsigned long flags) | |
77 | +{ | |
78 | + return (unsigned long) mtd->priv + offset; | |
79 | +} | |
80 | + | |
68 | 81 | static int ram_read(struct mtd_info *mtd, loff_t from, size_t len, |
69 | 82 | size_t *retlen, u_char *buf) |
70 | 83 | { |
... | ... | @@ -116,6 +129,7 @@ |
116 | 129 | mtd->erase = ram_erase; |
117 | 130 | mtd->point = ram_point; |
118 | 131 | mtd->unpoint = ram_unpoint; |
132 | + mtd->get_unmapped_area = ram_get_unmapped_area; | |
119 | 133 | mtd->read = ram_read; |
120 | 134 | mtd->write = ram_write; |
121 | 135 |
drivers/mtd/internal.h
1 | +/* Internal MTD definitions | |
2 | + * | |
3 | + * Copyright ยฉ 2006 Red Hat, Inc. All Rights Reserved. | |
4 | + * Written by David Howells (dhowells@redhat.com) | |
5 | + * | |
6 | + * This program is free software; you can redistribute it and/or | |
7 | + * modify it under the terms of the GNU General Public License | |
8 | + * as published by the Free Software Foundation; either version | |
9 | + * 2 of the License, or (at your option) any later version. | |
10 | + */ | |
11 | + | |
12 | +/* | |
13 | + * mtdbdi.c | |
14 | + */ | |
15 | +extern struct backing_dev_info mtd_bdi_unmappable; | |
16 | +extern struct backing_dev_info mtd_bdi_ro_mappable; | |
17 | +extern struct backing_dev_info mtd_bdi_rw_mappable; |
drivers/mtd/mtdbdi.c
1 | +/* MTD backing device capabilities | |
2 | + * | |
3 | + * Copyright ยฉ 2006 Red Hat, Inc. All Rights Reserved. | |
4 | + * Written by David Howells (dhowells@redhat.com) | |
5 | + * | |
6 | + * This program is free software; you can redistribute it and/or | |
7 | + * modify it under the terms of the GNU General Public License | |
8 | + * as published by the Free Software Foundation; either version | |
9 | + * 2 of the License, or (at your option) any later version. | |
10 | + */ | |
11 | + | |
12 | +#include <linux/backing-dev.h> | |
13 | +#include <linux/mtd/mtd.h> | |
14 | +#include "internal.h" | |
15 | + | |
16 | +/* | |
17 | + * backing device capabilities for non-mappable devices (such as NAND flash) | |
18 | + * - permits private mappings, copies are taken of the data | |
19 | + */ | |
20 | +struct backing_dev_info mtd_bdi_unmappable = { | |
21 | + .capabilities = BDI_CAP_MAP_COPY, | |
22 | +}; | |
23 | + | |
24 | +/* | |
25 | + * backing device capabilities for R/O mappable devices (such as ROM) | |
26 | + * - permits private mappings, copies are taken of the data | |
27 | + * - permits non-writable shared mappings | |
28 | + */ | |
29 | +struct backing_dev_info mtd_bdi_ro_mappable = { | |
30 | + .capabilities = (BDI_CAP_MAP_COPY | BDI_CAP_MAP_DIRECT | | |
31 | + BDI_CAP_EXEC_MAP | BDI_CAP_READ_MAP), | |
32 | +}; | |
33 | + | |
34 | +/* | |
35 | + * backing device capabilities for writable mappable devices (such as RAM) | |
36 | + * - permits private mappings, copies are taken of the data | |
37 | + * - permits non-writable shared mappings | |
38 | + */ | |
39 | +struct backing_dev_info mtd_bdi_rw_mappable = { | |
40 | + .capabilities = (BDI_CAP_MAP_COPY | BDI_CAP_MAP_DIRECT | | |
41 | + BDI_CAP_EXEC_MAP | BDI_CAP_READ_MAP | | |
42 | + BDI_CAP_WRITE_MAP), | |
43 | +}; |
drivers/mtd/mtdchar.c
... | ... | @@ -13,6 +13,7 @@ |
13 | 13 | #include <linux/slab.h> |
14 | 14 | #include <linux/sched.h> |
15 | 15 | #include <linux/smp_lock.h> |
16 | +#include <linux/backing-dev.h> | |
16 | 17 | |
17 | 18 | #include <linux/mtd/mtd.h> |
18 | 19 | #include <linux/mtd/compatmac.h> |
19 | 20 | |
... | ... | @@ -107,12 +108,15 @@ |
107 | 108 | goto out; |
108 | 109 | } |
109 | 110 | |
110 | - if (MTD_ABSENT == mtd->type) { | |
111 | + if (mtd->type == MTD_ABSENT) { | |
111 | 112 | put_mtd_device(mtd); |
112 | 113 | ret = -ENODEV; |
113 | 114 | goto out; |
114 | 115 | } |
115 | 116 | |
117 | + if (mtd->backing_dev_info) | |
118 | + file->f_mapping->backing_dev_info = mtd->backing_dev_info; | |
119 | + | |
116 | 120 | /* You can't open it RW if it's not a writeable device */ |
117 | 121 | if ((file->f_mode & FMODE_WRITE) && !(mtd->flags & MTD_WRITEABLE)) { |
118 | 122 | put_mtd_device(mtd); |
... | ... | @@ -781,6 +785,59 @@ |
781 | 785 | return ret; |
782 | 786 | } /* memory_ioctl */ |
783 | 787 | |
788 | +/* | |
789 | + * try to determine where a shared mapping can be made | |
790 | + * - only supported for NOMMU at the moment (MMU can't doesn't copy private | |
791 | + * mappings) | |
792 | + */ | |
793 | +#ifndef CONFIG_MMU | |
794 | +static unsigned long mtd_get_unmapped_area(struct file *file, | |
795 | + unsigned long addr, | |
796 | + unsigned long len, | |
797 | + unsigned long pgoff, | |
798 | + unsigned long flags) | |
799 | +{ | |
800 | + struct mtd_file_info *mfi = file->private_data; | |
801 | + struct mtd_info *mtd = mfi->mtd; | |
802 | + | |
803 | + if (mtd->get_unmapped_area) { | |
804 | + unsigned long offset; | |
805 | + | |
806 | + if (addr != 0) | |
807 | + return (unsigned long) -EINVAL; | |
808 | + | |
809 | + if (len > mtd->size || pgoff >= (mtd->size >> PAGE_SHIFT)) | |
810 | + return (unsigned long) -EINVAL; | |
811 | + | |
812 | + offset = pgoff << PAGE_SHIFT; | |
813 | + if (offset > mtd->size - len) | |
814 | + return (unsigned long) -EINVAL; | |
815 | + | |
816 | + return mtd->get_unmapped_area(mtd, len, offset, flags); | |
817 | + } | |
818 | + | |
819 | + /* can't map directly */ | |
820 | + return (unsigned long) -ENOSYS; | |
821 | +} | |
822 | +#endif | |
823 | + | |
824 | +/* | |
825 | + * set up a mapping for shared memory segments | |
826 | + */ | |
827 | +static int mtd_mmap(struct file *file, struct vm_area_struct *vma) | |
828 | +{ | |
829 | +#ifdef CONFIG_MMU | |
830 | + struct mtd_file_info *mfi = file->private_data; | |
831 | + struct mtd_info *mtd = mfi->mtd; | |
832 | + | |
833 | + if (mtd->type == MTD_RAM || mtd->type == MTD_ROM) | |
834 | + return 0; | |
835 | + return -ENOSYS; | |
836 | +#else | |
837 | + return vma->vm_flags & VM_SHARED ? 0 : -ENOSYS; | |
838 | +#endif | |
839 | +} | |
840 | + | |
784 | 841 | static const struct file_operations mtd_fops = { |
785 | 842 | .owner = THIS_MODULE, |
786 | 843 | .llseek = mtd_lseek, |
... | ... | @@ -789,6 +846,10 @@ |
789 | 846 | .ioctl = mtd_ioctl, |
790 | 847 | .open = mtd_open, |
791 | 848 | .release = mtd_close, |
849 | + .mmap = mtd_mmap, | |
850 | +#ifndef CONFIG_MMU | |
851 | + .get_unmapped_area = mtd_get_unmapped_area, | |
852 | +#endif | |
792 | 853 | }; |
793 | 854 | |
794 | 855 | static int __init init_mtdchar(void) |
drivers/mtd/mtdcore.c
... | ... | @@ -19,6 +19,7 @@ |
19 | 19 | #include <linux/proc_fs.h> |
20 | 20 | |
21 | 21 | #include <linux/mtd/mtd.h> |
22 | +#include "internal.h" | |
22 | 23 | |
23 | 24 | #include "mtdcore.h" |
24 | 25 | |
... | ... | @@ -45,6 +46,20 @@ |
45 | 46 | int add_mtd_device(struct mtd_info *mtd) |
46 | 47 | { |
47 | 48 | int i; |
49 | + | |
50 | + if (!mtd->backing_dev_info) { | |
51 | + switch (mtd->type) { | |
52 | + case MTD_RAM: | |
53 | + mtd->backing_dev_info = &mtd_bdi_rw_mappable; | |
54 | + break; | |
55 | + case MTD_ROM: | |
56 | + mtd->backing_dev_info = &mtd_bdi_ro_mappable; | |
57 | + break; | |
58 | + default: | |
59 | + mtd->backing_dev_info = &mtd_bdi_unmappable; | |
60 | + break; | |
61 | + } | |
62 | + } | |
48 | 63 | |
49 | 64 | BUG_ON(mtd->writesize == 0); |
50 | 65 | mutex_lock(&mtd_table_mutex); |
drivers/mtd/mtdpart.c
... | ... | @@ -84,6 +84,18 @@ |
84 | 84 | part->master->unpoint(part->master, from + part->offset, len); |
85 | 85 | } |
86 | 86 | |
87 | +static unsigned long part_get_unmapped_area(struct mtd_info *mtd, | |
88 | + unsigned long len, | |
89 | + unsigned long offset, | |
90 | + unsigned long flags) | |
91 | +{ | |
92 | + struct mtd_part *part = PART(mtd); | |
93 | + | |
94 | + offset += part->offset; | |
95 | + return part->master->get_unmapped_area(part->master, len, offset, | |
96 | + flags); | |
97 | +} | |
98 | + | |
87 | 99 | static int part_read_oob(struct mtd_info *mtd, loff_t from, |
88 | 100 | struct mtd_oob_ops *ops) |
89 | 101 | { |
... | ... | @@ -342,6 +354,7 @@ |
342 | 354 | |
343 | 355 | slave->mtd.name = part->name; |
344 | 356 | slave->mtd.owner = master->owner; |
357 | + slave->mtd.backing_dev_info = master->backing_dev_info; | |
345 | 358 | |
346 | 359 | slave->mtd.read = part_read; |
347 | 360 | slave->mtd.write = part_write; |
... | ... | @@ -354,6 +367,8 @@ |
354 | 367 | slave->mtd.unpoint = part_unpoint; |
355 | 368 | } |
356 | 369 | |
370 | + if (master->get_unmapped_area) | |
371 | + slave->mtd.get_unmapped_area = part_get_unmapped_area; | |
357 | 372 | if (master->read_oob) |
358 | 373 | slave->mtd.read_oob = part_read_oob; |
359 | 374 | if (master->write_oob) |
include/linux/mtd/mtd.h
... | ... | @@ -162,6 +162,20 @@ |
162 | 162 | /* We probably shouldn't allow XIP if the unpoint isn't a NULL */ |
163 | 163 | void (*unpoint) (struct mtd_info *mtd, loff_t from, size_t len); |
164 | 164 | |
165 | + /* Allow NOMMU mmap() to directly map the device (if not NULL) | |
166 | + * - return the address to which the offset maps | |
167 | + * - return -ENOSYS to indicate refusal to do the mapping | |
168 | + */ | |
169 | + unsigned long (*get_unmapped_area) (struct mtd_info *mtd, | |
170 | + unsigned long len, | |
171 | + unsigned long offset, | |
172 | + unsigned long flags); | |
173 | + | |
174 | + /* Backing device capabilities for this device | |
175 | + * - provides mmap capabilities | |
176 | + */ | |
177 | + struct backing_dev_info *backing_dev_info; | |
178 | + | |
165 | 179 | |
166 | 180 | int (*read) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); |
167 | 181 | int (*write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf); |