{"id":818141,"url":"http://patchwork.ozlabs.org/api/patches/818141/?format=json","web_url":"http://patchwork.ozlabs.org/project/linux-imx/patch/1506330344-31556-8-git-send-email-vladimir.murzin@arm.com/","project":{"id":19,"url":"http://patchwork.ozlabs.org/api/projects/19/?format=json","name":"Linux IMX development","link_name":"linux-imx","list_id":"linux-imx-kernel.lists.patchwork.ozlabs.org","list_email":"linux-imx-kernel@lists.patchwork.ozlabs.org","web_url":null,"scm_url":null,"webscm_url":null,"list_archive_url":"","list_archive_url_format":"","commit_url_format":""},"msgid":"<1506330344-31556-8-git-send-email-vladimir.murzin@arm.com>","list_archive_url":null,"date":"2017-09-25T09:05:43","name":"[v3,7/8] ARM: NOMMU: Use more MPU regions to cover memory","commit_ref":null,"pull_url":null,"state":"new","archived":false,"hash":"e01db198efa3288fcbdcd3afc8f29246aeb4eb31","submitter":{"id":65248,"url":"http://patchwork.ozlabs.org/api/people/65248/?format=json","name":"Vladimir Murzin","email":"vladimir.murzin@arm.com"},"delegate":null,"mbox":"http://patchwork.ozlabs.org/project/linux-imx/patch/1506330344-31556-8-git-send-email-vladimir.murzin@arm.com/mbox/","series":[{"id":4911,"url":"http://patchwork.ozlabs.org/api/series/4911/?format=json","web_url":"http://patchwork.ozlabs.org/project/linux-imx/list/?series=4911","date":"2017-09-25T09:05:36","name":"ARM: NOMMU: MPU updates","version":3,"mbox":"http://patchwork.ozlabs.org/series/4911/mbox/"}],"comments":"http://patchwork.ozlabs.org/api/patches/818141/comments/","check":"pending","checks":"http://patchwork.ozlabs.org/api/patches/818141/checks/","tags":{},"related":[],"headers":{"Return-Path":"<linux-arm-kernel-bounces+incoming-imx=patchwork.ozlabs.org@lists.infradead.org>","X-Original-To":"incoming-imx@patchwork.ozlabs.org","Delivered-To":"patchwork-incoming-imx@bilbo.ozlabs.org","Authentication-Results":["ozlabs.org;\n\tspf=none (mailfrom) smtp.mailfrom=lists.infradead.org\n\t(client-ip=65.50.211.133; helo=bombadil.infradead.org;\n\tenvelope-from=linux-arm-kernel-bounces+incoming-imx=patchwork.ozlabs.org@lists.infradead.org;\n\treceiver=<UNKNOWN>)","ozlabs.org; dkim=pass (2048-bit key;\n\tunprotected) header.d=lists.infradead.org\n\theader.i=@lists.infradead.org\n\theader.b=\"ixA4GoaY\"; dkim-atps=neutral"],"Received":["from bombadil.infradead.org (bombadil.infradead.org\n\t[65.50.211.133])\n\t(using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256\n\tbits)) (No client certificate requested)\n\tby ozlabs.org (Postfix) with ESMTPS id 3y0ywS1rg7z9t30\n\tfor <incoming-imx@patchwork.ozlabs.org>;\n\tMon, 25 Sep 2017 19:10:16 +1000 (AEST)","from localhost ([127.0.0.1] helo=bombadil.infradead.org)\n\tby bombadil.infradead.org with esmtp (Exim 4.87 #1 (Red Hat Linux))\n\tid 1dwPP2-0005Ey-4t; Mon, 25 Sep 2017 09:10:12 +0000","from usa-sjc-mx-foss1.foss.arm.com ([217.140.101.70]\n\thelo=foss.arm.com)\n\tby bombadil.infradead.org with esmtp (Exim 4.87 #1 (Red Hat Linux))\n\tid 1dwPLi-0001XX-Ip for linux-arm-kernel@lists.infradead.org;\n\tMon, 25 Sep 2017 09:07:16 +0000","from usa-sjc-imap-foss1.foss.arm.com (unknown [10.72.51.249])\n\tby usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 4D75D1993;\n\tMon, 25 Sep 2017 02:06:17 -0700 (PDT)","from bc-c11-3-12.euhpc.arm.com. (bc-c11-3-12.euhpc.arm.com\n\t[10.6.2.250])\n\tby usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPA id\n\tC94703F3E1; Mon, 25 Sep 2017 02:06:15 -0700 (PDT)"],"DKIM-Signature":"v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed;\n\td=lists.infradead.org; s=bombadil.20170209; h=Sender:\n\tContent-Transfer-Encoding:Content-Type:Cc:List-Subscribe:List-Help:List-Post:\n\tList-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To:\n\tMessage-Id:Date:Subject:To:From:Reply-To:Content-ID:Content-Description:\n\tResent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:\n\tList-Owner; bh=8f2Z5neb7Rp9pnsxAQz7cI4heqdnLjxQUwP82lwEkd8=;\n\tb=ixA4GoaYkk6MUe\n\tYU+GiyRNTMM7I9a/k0eu0RP9aitXn0UW4LRq4Wv51QJiOsnLKRSA0cZop3pqkkt3aET/xNZ0g7Tvp\n\tv795QH3PaitdnPptu+23gYv24/HUvhGD9jhB1d7GhV9fWucinCo+JOHzy+8mvjKZuE677nOqZwmIA\n\t8Vr24YeCK49K7belHacRGlTfV4hpwu/OJVFymtNie0ifya3PiLp0U+K0Ub9WqPhl1Rkz2H6zieyDH\n\tJnWXRouKCkFDm0+E//K5ysSf+RuTCoZPU8uRHyhBVCQK2tuBBjnwy7okpI1c2MhhTwO4Ilub42/mj\n\tCPR9aAmHNHFfC1OavnIA==;","From":"Vladimir Murzin <vladimir.murzin@arm.com>","To":"linux-arm-kernel@lists.infradead.org","Subject":"[PATCH v3 7/8] ARM: NOMMU: Use more MPU regions to cover memory","Date":"Mon, 25 Sep 2017 10:05:43 +0100","Message-Id":"<1506330344-31556-8-git-send-email-vladimir.murzin@arm.com>","X-Mailer":"git-send-email 2.0.0","In-Reply-To":"<1506330344-31556-1-git-send-email-vladimir.murzin@arm.com>","References":"<1506330344-31556-1-git-send-email-vladimir.murzin@arm.com>","MIME-Version":"1.0","X-CRM114-Version":"20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 ","X-CRM114-CacheID":"sfid-20170925_020647_578469_91F2A403 ","X-CRM114-Status":"GOOD (  23.39  )","X-Spam-Score":"-6.9 (------)","X-Spam-Report":"SpamAssassin version 3.4.1 on bombadil.infradead.org summary:\n\tContent analysis details:   (-6.9 points)\n\tpts rule name              description\n\t---- ----------------------\n\t--------------------------------------------------\n\t-5.0 RCVD_IN_DNSWL_HI RBL: Sender listed at http://www.dnswl.org/,\n\thigh trust [217.140.101.70 listed in list.dnswl.org]\n\t-0.0 SPF_PASS               SPF: sender matches SPF record\n\t-0.0 RP_MATCHES_RCVD Envelope sender domain matches handover relay\n\tdomain\n\t-1.9 BAYES_00               BODY: Bayes spam probability is 0 to 1%\n\t[score: 0.0000]","X-BeenThere":"linux-arm-kernel@lists.infradead.org","X-Mailman-Version":"2.1.21","Precedence":"list","List-Unsubscribe":"<http://lists.infradead.org/mailman/options/linux-arm-kernel>,\n\t<mailto:linux-arm-kernel-request@lists.infradead.org?subject=unsubscribe>","List-Archive":"<http://lists.infradead.org/pipermail/linux-arm-kernel/>","List-Post":"<mailto:linux-arm-kernel@lists.infradead.org>","List-Help":"<mailto:linux-arm-kernel-request@lists.infradead.org?subject=help>","List-Subscribe":"<http://lists.infradead.org/mailman/listinfo/linux-arm-kernel>,\n\t<mailto:linux-arm-kernel-request@lists.infradead.org?subject=subscribe>","Cc":"alexandre.torgue@st.com, manabian@gmail.com, linux@armlinux.org.uk,\n\tstefan@agner.ch, kbuild-all@01.org, u.kleine-koenig@pengutronix.de,\n\tsza@esh.hu","Content-Type":"text/plain; charset=\"utf-8\"","Content-Transfer-Encoding":"base64","Sender":"\"linux-arm-kernel\" <linux-arm-kernel-bounces@lists.infradead.org>","Errors-To":"linux-arm-kernel-bounces+incoming-imx=patchwork.ozlabs.org@lists.infradead.org","List-Id":"linux-imx-kernel.lists.patchwork.ozlabs.org"},"content":"PMSAv7 defines curious alignment requirements to the regions:\n- size must be power of 2, and\n- region start must be aligned to the region size\n\nBecause of that we currently adjust lowmem bounds plus we assign\nonly one MPU region to cover memory all these lead to significant amount of\nmemory could be wasted. As an example, consider 64Mb of memory at\n0x70000000 - it fits alignment requirements nicely; now, imagine that\n2Mb of memory is reserved for coherent DMA allocation, so now Linux is\nexpected to see 62Mb of memory... and here annoying thing happens -\nmemory gets truncated to 32Mb (we've lost 30Mb!), i.e. MPU layout\nlooks like:\n\n0: base 0x70000000, size 0x2000000\n\nThis patch tries to allocate as much as possible MPU slots to minimise\namount of truncated memory. Moreover, with this patch MPU subregions\nstarting to get used. MPU subregions allow us reduce the number of MPU\nslots used. For example given above, MPU layout looks like:\n\n0: base 0x70000000, size 0x2000000\n1: base 0x72000000, size 0x1000000\n2: base 0x73000000, size 0x1000000, disable subreg 7 (0x73e00000 - 0x73ffffff)\n\nWhere without subregions we'd get:\n\n0: base 0x70000000, size 0x2000000\n1: base 0x72000000, size 0x1000000\n2: base 0x73000000, size 0x800000\n3: base 0x73800000, size 0x400000\n4: base 0x73c00000, size 0x200000\n\nTo achieve better layout we fist try to cover specified memory as is\n(maybe with help of subregions) and if we failed, we truncate memory\nto fit alignment requirements (so it occupies one MPU slot) and\nperform one more attempt with the reminder, and so on till we either\ncover all memory or run out of MPU slots.\n\nTested-by: Szemző András <sza@esh.hu>\nTested-by: Alexandre TORGUE <alexandre.torgue@st.com>\nSigned-off-by: Vladimir Murzin <vladimir.murzin@arm.com>\n---\n arch/arm/include/asm/mpu.h |   5 ++\n arch/arm/mm/pmsa-v7.c      | 190 ++++++++++++++++++++++++++++++++++-----------\n 2 files changed, 149 insertions(+), 46 deletions(-)","diff":"diff --git a/arch/arm/include/asm/mpu.h b/arch/arm/include/asm/mpu.h\nindex 403462e..5db37a6 100644\n--- a/arch/arm/include/asm/mpu.h\n+++ b/arch/arm/include/asm/mpu.h\n@@ -15,6 +15,11 @@\n /* MPU D/I Size Register fields */\n #define MPU_RSR_SZ\t\t1\n #define MPU_RSR_EN\t\t0\n+#define MPU_RSR_SD\t\t8\n+\n+/* Number of subregions (SD) */\n+#define MPU_NR_SUBREGS\t\t8\n+#define MPU_MIN_SUBREG_SIZE\t256\n \n /* The D/I RSR value for an enabled region spanning the whole of memory */\n #define MPU_RSR_ALL_MEM\t\t63\ndiff --git a/arch/arm/mm/pmsa-v7.c b/arch/arm/mm/pmsa-v7.c\nindex 72f1a9f..c1f1fc7 100644\n--- a/arch/arm/mm/pmsa-v7.c\n+++ b/arch/arm/mm/pmsa-v7.c\n@@ -4,6 +4,7 @@\n  * ARM uCLinux supporting functions.\n  */\n \n+#include <linux/bitops.h>\n #include <linux/memblock.h>\n \n #include <asm/cp15.h>\n@@ -12,9 +13,20 @@\n \n #include \"mm.h\"\n \n+struct region {\n+\tphys_addr_t base;\n+\tphys_addr_t size;\n+\tunsigned long subreg;\n+};\n+\n+static struct region __initdata mem[MPU_MAX_REGIONS];\n+\n static unsigned int __initdata mpu_min_region_order;\n static unsigned int __initdata mpu_max_regions;\n \n+static int __init __mpu_min_region_order(void);\n+static int __init __mpu_max_regions(void);\n+\n #ifndef CONFIG_CPU_V7M\n \n #define DRBAR\t__ACCESS_CP15(c6, 0, c1, 0)\n@@ -130,19 +142,120 @@ static int __init mpu_present(void)\n \treturn ((read_cpuid_ext(CPUID_EXT_MMFR0) & MMFR0_PMSA) == MMFR0_PMSAv7);\n }\n \n+static bool __init try_split_region(phys_addr_t base, phys_addr_t size, struct region *region)\n+{\n+\tunsigned long  subreg, bslots, sslots;\n+\tphys_addr_t abase = base & ~(size - 1);\n+\tphys_addr_t asize = base + size - abase;\n+\tphys_addr_t p2size = 1 << __fls(asize);\n+\tphys_addr_t bdiff, sdiff;\n+\n+\tif (p2size != asize)\n+\t\tp2size *= 2;\n+\n+\tbdiff = base - abase;\n+\tsdiff = p2size - asize;\n+\tsubreg = p2size / MPU_NR_SUBREGS;\n+\n+\tif ((bdiff % subreg) || (sdiff % subreg))\n+\t\treturn false;\n+\n+\tbslots = bdiff / subreg;\n+\tsslots = sdiff / subreg;\n+\n+\tif (bslots || sslots) {\n+\t\tint i;\n+\n+\t\tif (subreg < MPU_MIN_SUBREG_SIZE)\n+\t\t\treturn false;\n+\n+\t\tif (bslots + sslots > MPU_NR_SUBREGS)\n+\t\t\treturn false;\n+\n+\t\tfor (i = 0; i < bslots; i++)\n+\t\t\t_set_bit(i, &region->subreg);\n+\n+\t\tfor (i = 1; i <= sslots; i++)\n+\t\t\t_set_bit(MPU_NR_SUBREGS - i, &region->subreg);\n+\t}\n+\n+\tregion->base = abase;\n+\tregion->size = p2size;\n+\n+\treturn true;\n+}\n+\n+static int __init allocate_region(phys_addr_t base, phys_addr_t size,\n+\t\t\t\t  unsigned int limit, struct region *regions)\n+{\n+\tint count = 0;\n+\tphys_addr_t diff = size;\n+\tint attempts = MPU_MAX_REGIONS;\n+\n+\twhile (diff) {\n+\t\t/* Try cover region as is (maybe with help of subregions) */\n+\t\tif (try_split_region(base, size, &regions[count])) {\n+\t\t\tcount++;\n+\t\t\tbase += size;\n+\t\t\tdiff -= size;\n+\t\t\tsize = diff;\n+\t\t} else {\n+\t\t\t/*\n+\t\t\t * Maximum aligned region might overflow phys_addr_t\n+\t\t\t * if \"base\" is 0. Hence we keep everything below 4G\n+\t\t\t * until we take the smaller of the aligned region\n+\t\t\t * size (\"asize\") and rounded region size (\"p2size\"),\n+\t\t\t * one of which is guaranteed to be smaller than the\n+\t\t\t * maximum physical address.\n+\t\t\t */\n+\t\t\tphys_addr_t asize = (base - 1) ^ base;\n+\t\t\tphys_addr_t p2size = (1 <<  __fls(diff)) - 1;\n+\n+\t\t\tsize = asize < p2size ? asize + 1 : p2size + 1;\n+\t\t}\n+\n+\t\tif (count > limit)\n+\t\t\tbreak;\n+\n+\t\tif (!attempts)\n+\t\t\tbreak;\n+\n+\t\tattempts--;\n+\t}\n+\n+\treturn count;\n+}\n+\n /* MPU initialisation functions */\n void __init adjust_lowmem_bounds_mpu(void)\n {\n \tphys_addr_t phys_offset = PHYS_OFFSET;\n-\tphys_addr_t aligned_region_size, specified_mem_size, rounded_mem_size;\n+\tphys_addr_t  specified_mem_size, total_mem_size = 0;\n \tstruct memblock_region *reg;\n \tbool first = true;\n \tphys_addr_t mem_start;\n \tphys_addr_t mem_end;\n+\tunsigned int mem_max_regions;\n+\tint num, i;\n \n \tif (!mpu_present())\n \t\treturn;\n \n+\t/* Free-up MPU_PROBE_REGION */\n+\tmpu_min_region_order = __mpu_min_region_order();\n+\n+\t/* How many regions are supported */\n+\tmpu_max_regions = __mpu_max_regions();\n+\n+\tmem_max_regions = min((unsigned int)MPU_MAX_REGIONS, mpu_max_regions);\n+\n+\t/* We need to keep one slot for background region */\n+\tmem_max_regions--;\n+\n+#ifndef CONFIG_CPU_V7M\n+\t/* ... and one for vectors */\n+\tmem_max_regions--;\n+#endif\n \tfor_each_memblock(memory, reg) {\n \t\tif (first) {\n \t\t\t/*\n@@ -168,40 +281,23 @@ void __init adjust_lowmem_bounds_mpu(void)\n \t\t}\n \t}\n \n-\t/*\n-\t * MPU has curious alignment requirements: Size must be power of 2, and\n-\t * region start must be aligned to the region size\n-\t */\n-\tif (phys_offset != 0)\n-\t\tpr_info(\"PHYS_OFFSET != 0 => MPU Region size constrained by alignment requirements\\n\");\n-\n-\t/*\n-\t * Maximum aligned region might overflow phys_addr_t if phys_offset is\n-\t * 0. Hence we keep everything below 4G until we take the smaller of\n-\t * the aligned_region_size and rounded_mem_size, one of which is\n-\t * guaranteed to be smaller than the maximum physical address.\n-\t */\n-\taligned_region_size = (phys_offset - 1) ^ (phys_offset);\n-\t/* Find the max power-of-two sized region that fits inside our bank */\n-\trounded_mem_size = (1 <<  __fls(specified_mem_size)) - 1;\n+\tnum = allocate_region(mem_start, specified_mem_size, mem_max_regions, mem);\n \n-\t/* The actual region size is the smaller of the two */\n-\taligned_region_size = aligned_region_size < rounded_mem_size\n-\t\t\t\t? aligned_region_size + 1\n-\t\t\t\t: rounded_mem_size + 1;\n+\tfor (i = 0; i < num; i++) {\n+\t\tunsigned long  subreg = mem[i].size / MPU_NR_SUBREGS;\n \n-\tif (aligned_region_size != specified_mem_size) {\n-\t\tpr_warn(\"Truncating memory from %pa to %pa (MPU region constraints)\",\n-\t\t\t\t&specified_mem_size, &aligned_region_size);\n-\t\tmemblock_remove(mem_start + aligned_region_size,\n-\t\t\t\tspecified_mem_size - aligned_region_size);\n+\t\ttotal_mem_size += mem[i].size - subreg * hweight_long(mem[i].subreg);\n \n-\t\tmem_end = mem_start + aligned_region_size;\n+\t\tpr_debug(\"MPU: base %pa size %pa disable subregions: %*pbl\\n\",\n+\t\t\t &mem[i].base, &mem[i].size, MPU_NR_SUBREGS, &mem[i].subreg);\n \t}\n \n-\tpr_debug(\"MPU Region from %pa size %pa (end %pa))\\n\",\n-\t\t&phys_offset, &aligned_region_size, &mem_end);\n-\n+\tif (total_mem_size != specified_mem_size) {\n+\t\tpr_warn(\"Truncating memory from %pa to %pa (MPU region constraints)\",\n+\t\t\t\t&specified_mem_size, &total_mem_size);\n+\t\tmemblock_remove(mem_start + total_mem_size,\n+\t\t\t\tspecified_mem_size - total_mem_size);\n+\t}\n }\n \n static int __init __mpu_max_regions(void)\n@@ -258,7 +354,8 @@ static int __init __mpu_min_region_order(void)\n }\n \n static int __init mpu_setup_region(unsigned int number, phys_addr_t start,\n-\t\t\tunsigned int size_order, unsigned int properties)\n+\t\t\t\t   unsigned int size_order, unsigned int properties,\n+\t\t\t\t   unsigned int subregions)\n {\n \tu32 size_data;\n \n@@ -275,6 +372,7 @@ static int __init mpu_setup_region(unsigned int number, phys_addr_t start,\n \n \t/* Writing N to bits 5:1 (RSR_SZ)  specifies region size 2^N+1 */\n \tsize_data = ((size_order - 1) << MPU_RSR_SZ) | 1 << MPU_RSR_EN;\n+\tsize_data |= subregions << MPU_RSR_SD;\n \n \tdsb(); /* Ensure all previous data accesses occur with old mappings */\n \trgnr_write(number);\n@@ -308,33 +406,33 @@ static int __init mpu_setup_region(unsigned int number, phys_addr_t start,\n */\n void __init mpu_setup(void)\n {\n-\tint region = 0, err = 0;\n+\tint i, region = 0, err = 0;\n \n \tif (!mpu_present())\n \t\treturn;\n \n-\t/* Free-up MPU_PROBE_REGION */\n-\tmpu_min_region_order = __mpu_min_region_order();\n-\n-\t/* How many regions are supported */\n-\tmpu_max_regions = __mpu_max_regions();\n-\n-\t/* Now setup MPU (order is important) */\n+\t/* Setup MPU (order is important) */\n \n \t/* Background */\n \terr |= mpu_setup_region(region++, 0, 32,\n-\t\t\t\tMPU_ACR_XN | MPU_RGN_STRONGLY_ORDERED | MPU_AP_PL1RW_PL0NA);\n+\t\t\t\tMPU_ACR_XN | MPU_RGN_STRONGLY_ORDERED | MPU_AP_PL1RW_PL0NA,\n+\t\t\t\t0);\n \n \t/* RAM */\n-\terr |= mpu_setup_region(region++, PHYS_OFFSET,\n-\t\t\t\tilog2(memblock.memory.regions[0].size),\n-\t\t\t\tMPU_AP_PL1RW_PL0RW | MPU_RGN_NORMAL);\n+\tfor (i = 0; i < ARRAY_SIZE(mem); i++) {\n+\t\tif (!mem[i].size)\n+\t\t\tcontinue;\n+\n+\t\terr |= mpu_setup_region(region++, mem[i].base, ilog2(mem[i].size),\n+\t\t\t\t\tMPU_AP_PL1RW_PL0RW | MPU_RGN_NORMAL,\n+\t\t\t\t\tmem[i].subreg);\n+\t}\n \n \t/* Vectors */\n #ifndef CONFIG_CPU_V7M\n-\terr |= mpu_setup_region(region++, vectors_base,\n-\t\t\t\tilog2(2 * PAGE_SIZE),\n-\t\t\t\tMPU_AP_PL1RW_PL0NA | MPU_RGN_NORMAL);\n+\terr |= mpu_setup_region(region++, vectors_base, ilog2(2 * PAGE_SIZE),\n+\t\t\t\tMPU_AP_PL1RW_PL0NA | MPU_RGN_NORMAL,\n+\t\t\t\t0);\n #endif\n \tif (err) {\n \t\tpanic(\"MPU region initialization failure! %d\", err);\n","prefixes":["v3","7/8"]}