Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

mtd: phram: Allow cached mappings

Currently phram always uses ioremap(), but this is unnecessary when
normal memory is used. If the reserved-memory node does not specify the
no-map property, indicating it should be mapped as system RAM and
ioremap() cannot be used on it, use a cached mapping using
memremap(MEMREMAP_WB) instead.

On one of my systems this improves read performance by ~70%.

(Note that this driver has always used normal memcpy/memset functions on
memory obtained from ioremap(), which sparse doesn't like. There is no
memremap() variant which maps exactly to ioremap() on all architectures,
so that behaviour of the driver is not changed to avoid affecting
existing users, but the sparse warnings are suppressed in the moved code
with __force.)

Signed-off-by: Vincent Whitchurch <vincent.whitchurch@axis.com>
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
Link: https://lore.kernel.org/linux-mtd/20220510151822.1809278-1-vincent.whitchurch@axis.com

authored by

Vincent Whitchurch and committed by
Miquel Raynal
9401911f bcdf0315

+37 -6
+37 -6
drivers/mtd/devices/phram.c
··· 34 34 struct phram_mtd_list { 35 35 struct mtd_info mtd; 36 36 struct list_head list; 37 + bool cached; 37 38 }; 38 39 39 40 static LIST_HEAD(phram_list); ··· 81 80 return 0; 82 81 } 83 82 83 + static int phram_map(struct phram_mtd_list *phram, phys_addr_t start, size_t len) 84 + { 85 + void *addr = NULL; 86 + 87 + if (phram->cached) 88 + addr = memremap(start, len, MEMREMAP_WB); 89 + else 90 + addr = (void __force *)ioremap(start, len); 91 + if (!addr) 92 + return -EIO; 93 + 94 + phram->mtd.priv = addr; 95 + 96 + return 0; 97 + } 98 + 99 + static void phram_unmap(struct phram_mtd_list *phram) 100 + { 101 + void *addr = phram->mtd.priv; 102 + 103 + if (phram->cached) { 104 + memunmap(addr); 105 + return; 106 + } 107 + 108 + iounmap((void __iomem *)addr); 109 + } 110 + 84 111 static void unregister_devices(void) 85 112 { 86 113 struct phram_mtd_list *this, *safe; 87 114 88 115 list_for_each_entry_safe(this, safe, &phram_list, list) { 89 116 mtd_device_unregister(&this->mtd); 90 - iounmap(this->mtd.priv); 117 + phram_unmap(this); 91 118 kfree(this->mtd.name); 92 119 kfree(this); 93 120 } ··· 125 96 phys_addr_t start, size_t len, uint32_t erasesize) 126 97 { 127 98 struct device_node *np = pdev ? pdev->dev.of_node : NULL; 99 + bool cached = np ? !of_property_read_bool(np, "no-map") : false; 128 100 struct phram_mtd_list *new; 129 101 int ret = -ENOMEM; 130 102 ··· 133 103 if (!new) 134 104 goto out0; 135 105 136 - ret = -EIO; 137 - new->mtd.priv = ioremap(start, len); 138 - if (!new->mtd.priv) { 106 + new->cached = cached; 107 + 108 + ret = phram_map(new, start, len); 109 + if (ret) { 139 110 pr_err("ioremap failed\n"); 140 111 goto out1; 141 112 } ··· 171 140 return 0; 172 141 173 142 out2: 174 - iounmap(new->mtd.priv); 143 + phram_unmap(new); 175 144 out1: 176 145 kfree(new); 177 146 out0: ··· 393 362 struct phram_mtd_list *phram = platform_get_drvdata(pdev); 394 363 395 364 mtd_device_unregister(&phram->mtd); 396 - iounmap(phram->mtd.priv); 365 + phram_unmap(phram); 397 366 kfree(phram); 398 367 399 368 return 0;