Skip to contentSkip to conversation

Make MTD chardev mmap available under some circumstances

PATCH In review David Howells 2 participants 2 messages Sep 5, 2006 – Sep 6, 2006 Memory technology devices (MTD)

lore
Share & tools
lore
#0 lore raw

PATCH 1/1 Make MTD chardev mmap available under some circumstances

Make it possible to mmap MTD chardevs directly or by copy on a NOMMU system. This is done by:

(1) Presenting backing device capabilities for MTD character device files to allow NOMMU mmap to do direct mapping where possible.

Three BDIs are made available:

(a) One to indicate that a device supports direct mapping with both read and write capability (eg: a RAM device).

(b) One to indicate that a device supports direct mapping, but only with read capability (eg: a ROM device).

(c) One to indicate that a device does not support direct mapping (eg: a NAND flash device).

In all three cases, private mmap by copying *is* supported, which means that such as NAND flashes *can* be mapped privately. The NOMMU mmap routine allocates some memory to hold the private mapping and then reads the data from the device into it. This memory is then presented as the mapping. This is *not* currently possible under MMU conditions; to support that will require changes to mm/mmap.c.

(2) Providing a get_unmapped_area() op to permit the device driver to specify the whereabouts of the mappable data.

If the get_unmapped_area() op is not present in mtd_info, -ENOSYS is returned to the caller to indicate that mapping isn't actually supported. The device driver may also return -ENOSYS if it doesn't want to handle the mapping.

(3) Have the MTD partitioning code adjust and transfer the get_unmapped_area() op to the master device.

The MTDRAM driver has been modified to support this new op, and thus is able to support full readable, writable and executable shared and private mappings.

This can be seen in the following strace excerpt:

	open("/dev/mtd0", O_RDWR)               = 4
	mmap2(NULL, 16384, PROT_READ|PROT_WRITE, MAP_SHARED, 4, 0) = 0xc0c00000
	mmap2(NULL, 16384, PROT_READ|PROT_WRITE, MAP_SHARED, 4, 0) = 0xc0c00000

which results in the following appearing in the /proc/pid/maps file:

	c0c00000-c0c04000 rw-S 00000000 00:0b 8561016    /dev/mtd0
	c0c00000-c0c04000 rw-S 00000000 00:0b 8561016    /dev/mtd0

---

 drivers/mtd/devices/mtdram.c |   14 ++++++
 drivers/mtd/mtdchar.c        |   99 ++++++++++++++++++++++++++++++++++++++++++
 drivers/mtd/mtdpart.c        |   13 ++++++
 include/linux/mtd/mtd.h      |    9 ++++
 4 files changed, 134 insertions(+), 1 deletions(-)
drivers/mtd/devices/mtdram.c +14 -0
index e427c82..438cdb9 100644
@@ -62,6 +62,19 @@ static void ram_unpoint(struct mtd_info 
 {
 }
 
+/*
+ * Allow NOMMU mmap() to directly map the device (if not NULL)
+ * - return the address to which the offset maps
+ * - return -ENOSYS to indicate refusal to do the mapping
+ */
+static unsigned long ram_get_unmapped_area(struct mtd_info *mtd,
+					   unsigned long len,
+					   unsigned long offset,
+					   unsigned long flags)
+{
+	return (unsigned long) mtd->priv + offset;
+}
+
 static int ram_read(struct mtd_info *mtd, loff_t from, size_t len,
 		size_t *retlen, u_char *buf)
 {
@@ -113,6 +126,7 @@ int mtdram_init_device(struct mtd_info *
 	mtd->erase = ram_erase;
 	mtd->point = ram_point;
 	mtd->unpoint = ram_unpoint;
+	mtd->get_unmapped_area = ram_get_unmapped_area;
 	mtd->read = ram_read;
 	mtd->write = ram_write;
 
drivers/mtd/mtdchar.c +98 -1

131 lines not on this page · raw message

drivers/mtd/mtdpart.c +13 -0
index 06a9303..48bbc77 100644
@@ -86,6 +86,17 @@ static void part_unpoint (struct mtd_inf
 	part->master->unpoint (part->master, addr, from + part->offset, len);
 }
 
+static unsigned long part_get_unmapped_area(struct mtd_info *mtd,
+					    unsigned long len,
+					    unsigned long offset,
+					    unsigned long flags)
+{
+	struct mtd_part *part = PART(mtd);
+
+	offset += part->offset;
+	return part->master->get_unmapped_area(mtd, len, offset, flags);
+}
+
 static int part_read_oob(struct mtd_info *mtd, loff_t from,
 			 struct mtd_oob_ops *ops)
 {
@@ -354,6 +365,8 @@ int add_mtd_partitions(struct mtd_info *
 			slave->mtd.unpoint = part_unpoint;
 		}
 
+		if (master->get_unmapped_area)
+			slave->mtd.get_unmapped_area = part_get_unmapped_area;
 		if (master->read_oob)
 			slave->mtd.read_oob = part_read_oob;
 		if (master->write_oob)
include/linux/mtd/mtd.h +9 -0
index 94a443d..ab0f21f 100644
@@ -156,6 +156,15 @@ #define MTD_PROGREGION_CTRLMODE_INVALID(
 	/* We probably shouldn't allow XIP if the unpoint isn't a NULL */
 	void (*unpoint) (struct mtd_info *mtd, u_char * addr, loff_t from, size_t len);
 
+	/* Allow NOMMU mmap() to directly map the device (if not NULL)
+	 * - return the address to which the offset maps
+	 * - return -ENOSYS to indicate refusal to do the mapping
+	 */
+	unsigned long (*get_unmapped_area) (struct mtd_info *mtd,
+					    unsigned long len,
+					    unsigned long offset,
+					    unsigned long flags);
+
 
 	int (*read) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
 	int (*write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);
#1 lore raw
Quoted 2 lines · David Howells

Make it possible to mmap MTD chardevs directly or by copy on a NOMMU system.

Why would you want to do this? I'm just curious.

Quoted 4 lines

+/*
+ * Allow NOMMU mmap() to directly map the device (if not NULL)
+ * - return the address to which the offset maps
+ * - return -ENOSYS to indicate refusal to do the mapping

Where do you actually do the -ENOSYS part? I think that comment belongs to the actual mtd_get_unmapped_area call in mtdchar.c, no?

Quoted 25 lines
drivers/mtd/mtdchar.c +25 -0
+/*
+ * try to determine where a shared mapping can be made
+ * - only supported for NOMMU at the moment (MMU can't doesn't copy private
+ *   mappings)
+ */
+#ifndef CONFIG_MMU
+static unsigned long mtd_get_unmapped_area(struct file *file,
+                                          unsigned long addr,
+                                          unsigned long len,
+                                          unsigned long pgoff,
+                                          unsigned long flags)
+{
+       struct mtd_file_info *mfi = file->private_data;
+       struct mtd_info *mtd = mfi->mtd;
+
+       if (mtd->get_unmapped_area) {
+               unsigned long offset;
+
+               if (addr != 0)
+                       return (unsigned long) -EINVAL;
+
+               if (len > mtd->size || pgoff >= (mtd->size >> PAGE_SHIFT))
+                       return (unsigned long) -EINVAL;
+
+               offset = pgoff << PAGE_SHIFT;

drivers/mtd/mtdchar.c #endif · line 840

Quoted 10 lines
drivers/mtd/mtdchar.c +10 -0
+               if (offset > mtd->size - len)
+                       return (unsigned long) -EINVAL;
+
+               return mtd->get_unmapped_area(mtd, len, offset, flags);
+       }
+
+       /* can't map directly */
+       return (unsigned long) -ENOSYS;
+}
+#endif

Could we do something like:

#else
#define mtd_get_unmapped_area (NULL);

which would prevent the ugly ifdef in the mtd_fops structure?

drivers/mtd/mtdchar.c static struct file_operations mtd_fops = · line 870

Quoted 11 lines

static struct file_operations mtd_fops = {
.owner = THIS_MODULE,
.llseek = mtd_lseek,

drivers/mtd/mtdchar.c +4 -0
@@ -771,6 +864,10 @@ static struct file_operations mtd_fops =
        .ioctl          = mtd_ioctl,
        .open           = mtd_open,
        .release        = mtd_close,
+       .mmap           = mtd_mmap,
+#ifndef CONFIG_MMU
+       .get_unmapped_area = mtd_get_unmapped_area,
+#endif

See above.

josh