From nobody Sun May 24 22:35:56 2026 Received: from mail-pl1-f170.google.com (mail-pl1-f170.google.com [209.85.214.170]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 29C78233933 for ; Wed, 20 May 2026 16:36:28 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.170 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779294991; cv=none; b=kmn1nCy1aHYgj3dbhF4udmOnCMAlGDB2R+kWsefdfuxfvfc516xxyVklPHzf1+CnWLpx7It1JQtIp9qJJoGL90hH0RHEaTWoP2+6JxBNC/fn50cFyYBBpdwjQHXX+0W8VjtZNijPFyv34Cp7dPD2MLuVgct4yrTkTStO+FUqbLE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779294991; c=relaxed/simple; bh=kAdQqrtZA2YDIDLr0v2uJNhof4GBTtaqzxHau1RxIBg=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version; b=FonDPNOV2KsLl3oyPwiK/gWYnScz6gpa3cA4CESuKOJtCEQmmzcD78OrfasZ2XsGTbPaqLNoRuuz8qJ+LUTH90Gtka7T7YghslMg57RbCiL8xPuH/MnH9TOU9FECOJn3T9DkQlQHuzKXUqzL1aD8bKZxH8iv3sfeN4AZf/bvLU4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=thingy.jp; spf=pass smtp.mailfrom=0x0f.com; dkim=pass (1024-bit key) header.d=thingy.jp header.i=@thingy.jp header.b=PskGWt//; arc=none smtp.client-ip=209.85.214.170 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=thingy.jp Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=0x0f.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=thingy.jp header.i=@thingy.jp header.b="PskGWt//" Received: by mail-pl1-f170.google.com with SMTP id d9443c01a7336-2b9e9a6802aso21667415ad.3 for ; Wed, 20 May 2026 09:36:28 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=thingy.jp; s=google; t=1779294988; x=1779899788; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=Vkm10lsOT14iQwI/CNWfdgpDANwu8qAoGqLZTVEsxR8=; b=PskGWt//Sz7T30JRB3GCYsuHz5nCC3WTUNmyya0WmM35s4YHOpaab+nr8YV4hsHQ8E PW7bCjsyGlZ83ViTcdtSq4spbpfTzL1O6oqIRIbWdYDa7s3OfMCnnX4m3nD+fxZTSsb8 sdGOb5Q0D6Il0iX5t9hVcQPVyEi/48MT6ltSU= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1779294988; x=1779899788; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=Vkm10lsOT14iQwI/CNWfdgpDANwu8qAoGqLZTVEsxR8=; b=d5DENle3KGn+qjfhDev9lV8mE1tkjMr+e7cgs7uZN0UtjSR3II5vQIj5E9q3yIx7nI Ygt21LqR9WaZ+wxLjvzWuQFtR+uYXMPT80e/fgp5Xi/l6rrTv9ut5iAFuCGjhQaf6fVl 2vcgk9/8Y9gdXo8W/UATBNCb3RqRICXlG89Tl2i4QQSkZvsK0oBJ64xJWnKEnTeIy3lU o7c6A2FM4pXr3ghgCFWu/x0NbCOdSS8dpRO2sqZEbNp/vdzftC7u2dincOyekkuUHjnD BozS+qGf5P1zkfz/hQVZgYXkt3uFDnpmAys9j9VmrwOa5KMVVvQivlZ2jdpkI4er22Wx LM5w== X-Forwarded-Encrypted: i=1; AFNElJ/HS/hCld9V3Zw0ae1uFOzljkunoDuSDf9QhbdPHAlbNTh/rvHt3Bxyu/Sf4pic9ZnRgVjsfoC6I4ndKU8=@vger.kernel.org X-Gm-Message-State: AOJu0Yz41ot0q6wFH3Q8rSVl0KQ5Sb86S9BSDuuiku4c81Wpw0c/Uogo /s9kzkKtvpwqgOiCxwavkn9HOOCrX25yysZeA/YYKiBAIqI+4p6bKWytVIanHKvpY9U= X-Gm-Gg: Acq92OEvxAT2A3gzjhL7Qq/woY13A+q79kjB/DL7OcfBY3nswlMAAbzgjwB1Wh89ROP /IrTH2+jr47phwaow4mYmn3KEffu9pBTwzp7xD7NnyfeQtUc0P5dqbgXBES/+FWjR5J+z7Rw64V CBY1V1VRPDqBTO/kp4id45VHS2O/PuWxlQ2+BHdU5j82viOdimn8NotNNF7lw1G+i8yKvIkudeb zKd5CZtpz88U+lzUD4hf/c9DwMQfCKKcBhGteboVriK4MXgrm3QYuQczaLboXjcBnNK42bsscY8 yKf2gRlKFxnp0TWjWa+kfzrQcyUnFljVKWJRinNsuucvNMaWhUrA/e7Vbg+lvNqvs8rlFAENjpP alpreFPdJ9vvNwRn2DV/t8pWYJ5LezP5Y7G4wHWoQsOVAkHj3CopQT708bn8m810RJkSYopjbQf JUkg0dDO4rWLu24OiX1bAq141HZZD8QPsPzujBKzVkwhxawsQlZwWYVZOpe5A7ChepWfvpbc/wo 95rWuIQkTRHmfm4o1L9bl1I X-Received: by 2002:a17:903:987:b0:2ba:5a20:1d94 with SMTP id d9443c01a7336-2bd7e8459ddmr287248235ad.13.1779294988312; Wed, 20 May 2026 09:36:28 -0700 (PDT) Received: from kinako.work.home.arpa (p1620034-ipxg00d01sizuokaden.shizuoka.ocn.ne.jp. [122.29.136.34]) by smtp.googlemail.com with ESMTPSA id d9443c01a7336-2bd5c0600a4sm211290525ad.30.2026.05.20.09.36.26 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 20 May 2026 09:36:27 -0700 (PDT) From: Daniel Palmer To: Cc: akpm@linux-foundation.org, liam@infradead.org, ljs@kernel.org, vbabka@kernel.org, jannh@google.com, pfalcato@suse.de, linux-mm@kvack.org, linux-kernel@vger.kernel.org, Daniel Palmer Subject: [RFC PATCH] mm/nommu: Implement just enough vmap that compressed erofs can be mounted Date: Thu, 21 May 2026 01:34:38 +0900 Message-ID: <20260520163442.1099667-1-daniel@thingy.jp> X-Mailer: git-send-email 2.53.0 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" This implements a very poor imitation of vmap that works just enough that compressed erofs filesystems can be mounted on nommu machines. Right now compressed erofs filesystems trigger a BUG() on nommu due to this missing. This is awful, doesn't work like real vmap etc,.. but if you really cared about stuff working you'd have an MMU I guess? Signed-off-by: Daniel Palmer --- Did I miss anything massive that is going to come back and bite me? Maybe it would have made more sense just to change the erofs code so on !CONFIG_MMU it doesn't use vmap? Why: I'm attempting to get a kernel and userspace into ~3.5MB of memory without an MMU. The kernel is just a bit over 2MB so I don't have much left. I've constructed a userspace that is completely made up of nolibc binaries and with a bit of tweaking and all the debugging turned off it fits into a ~64KB erofs. mm/nommu.c | 133 ++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 127 insertions(+), 6 deletions(-) diff --git a/mm/nommu.c b/mm/nommu.c index ed3934bc2de4..a7dbb67b3b69 100644 --- a/mm/nommu.c +++ b/mm/nommu.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -53,6 +54,18 @@ static struct kmem_cache *vm_region_jar; struct rb_root nommu_region_tree =3D RB_ROOT; DECLARE_RWSEM(nommu_region_sem); =20 +/* Tracking for our "poor man's" vmap */ +#define VMAP_HASH_BITS 6 +static struct hlist_head vmap_hash[1 << VMAP_HASH_BITS]; +static DEFINE_SPINLOCK(vmap_lock); + +struct nommu_vmap_area { + struct hlist_node node; + struct page **pages; + unsigned int count; + void *addr; +}; + const struct vm_operations_struct generic_file_vm_ops =3D { }; =20 @@ -305,29 +318,137 @@ void *vmalloc_32_user_noprof(unsigned long size) } EXPORT_SYMBOL(vmalloc_32_user_noprof); =20 -void *vmap(struct page **pages, unsigned int count, unsigned long flags, p= gprot_t prot) +static bool vmap_needs_bounce(struct page **pages, unsigned int count) { - BUG(); + unsigned long pfn =3D page_to_pfn(pages[0]); + unsigned int i; + + for (i =3D 1; i < count; i++) + if (page_to_pfn(pages[i]) !=3D pfn + i) + return true; + + return false; +} + +static inline unsigned int vmap_key(const void *addr) +{ + return hash_ptr(addr, VMAP_HASH_BITS); +} + +static struct nommu_vmap_area *vmap_area_find(const void *addr) +{ + struct nommu_vmap_area *va; + + hlist_for_each_entry(va, &vmap_hash[vmap_key(addr)], node) + if (va->addr =3D=3D addr) + return va; + return NULL; } + +static void *nommu_vmap_map(struct page **pages, unsigned int count) +{ + struct nommu_vmap_area *va __free(kfree) =3D NULL; + struct page **_pages __free(kfree) =3D NULL; + void *copy __free(kvfree) =3D NULL; + unsigned int i; + + va =3D kmalloc_obj(struct nommu_vmap_area); + if (!va) + return NULL; + + if (vmap_needs_bounce(pages, count)) { + copy =3D kvmalloc_array(count, PAGE_SIZE, GFP_KERNEL); + if (!copy) + return NULL; + + _pages =3D kmemdup(pages, count * sizeof(*pages), GFP_KERNEL); + if (!_pages) + return NULL; + + /* + * Copy the original contents of the pages into the new + * pages to pretend we virtually mapped them. + */ + for (i =3D 0; i < count; i++) { + void *p =3D copy + (i * PAGE_SIZE); + + memcpy(p, page_address(pages[i]), PAGE_SIZE); + } + + va->addr =3D no_free_ptr(copy); + va->pages =3D no_free_ptr(_pages); + } else { + va->addr =3D page_address(pages[0]); + va->pages =3D NULL; + } + + va->count =3D count; + + scoped_guard(spinlock, &vmap_lock) { + hlist_add_head(&va->node, + &vmap_hash[vmap_key(va->addr)]); + } + + return no_free_ptr(va)->addr; +} + +static void nommu_vmap_unmap(const void *addr) +{ + struct nommu_vmap_area *va; + unsigned int i; + + scoped_guard(spinlock, &vmap_lock) { + va =3D vmap_area_find(addr); + if (va) + hlist_del(&va->node); + } + + if (WARN_ON_ONCE(!va)) + return; + + if (va->pages) { + /* + * Write back the new contents of the pages to + * the original ones, this is a waste of time if + * the pages weren't written to but we can't tell. + */ + for (i =3D 0; i < va->count; i++) { + const void *src =3D addr + (i * PAGE_SIZE); + void *dst =3D page_address(va->pages[i]); + + memcpy(dst, src, PAGE_SIZE); + } + + kvfree(va->addr); + kfree(va->pages); + } + + kfree(va); +} + +void *vmap(struct page **pages, unsigned int count, + unsigned long flags, pgprot_t prot) +{ + return nommu_vmap_map(pages, count); +} EXPORT_SYMBOL(vmap); =20 void vunmap(const void *addr) { - BUG(); + nommu_vmap_unmap(addr); } EXPORT_SYMBOL(vunmap); =20 void *vm_map_ram(struct page **pages, unsigned int count, int node) { - BUG(); - return NULL; + return nommu_vmap_map(pages, count); } EXPORT_SYMBOL(vm_map_ram); =20 void vm_unmap_ram(const void *mem, unsigned int count) { - BUG(); + nommu_vmap_unmap(mem); } EXPORT_SYMBOL(vm_unmap_ram); =20 --=20 2.53.0