r323412: Panic on boot (slab->us_keg == keg)

Mark Johnston markj at FreeBSD.org
Tue Sep 12 03:35:11 UTC 2017


On Mon, Sep 11, 2017 at 09:15:51PM +0300, Andrey V. Elsukov wrote:
> On 11.09.2017 15:23, Andrey V. Elsukov wrote:
> > --- trap 0xc, rip = 0xffffffff80d84870, rsp = 0xffffffff82193970, rbp =
> > 0xffffffff821939b0 ---
> > zone_import() at zone_import+0x110/frame 0xffffffff821939b0
> > zone_alloc_item() at zone_alloc_item+0x36/frame 0xffffffff821939f0
> > uma_startup() at uma_startup+0x1d0/frame 0xffffffff82193ae0
> > vm_page_startup() at vm_page_startup+0x34e/frame 0xffffffff82193b30
> > vm_mem_init() at vm_mem_init+0x1a/frame 0xffffffff82193b50
> > mi_startup() at mi_startup+0x9c/frame 0xffffffff82193b70
> > btext() at btext+0x2c
> > Uptime: 1s
> 
> I bisected revisions, and the last working is r322988.
> This machine is E5-2660 v4@ based.
> 
> [...]
> FreeBSD/SMP: 2 package(s) x 14 core(s) x 2 hardware threads
> 
> Also I determined that it can successfully boot with disabled
> hyper-threading.

After the change to CACHE_LINE_SIZE, we have
sizeof(struct uma_zone) == 448 and sizeof(struct uma_cache) == 64. With
56 CPUs, we therefore need 4032 bytes per UMA zone, plus 80 bytes for
the slab header - "internal" zones always keep the slab header in the
slab itself. That's slightly larger than one page, but the UMA zone
zone's keg will have uk_ppera == 1. So, when allocating slabzone,
keg_alloc_slab() will call startup_alloc(uk_ppera * PAGE_SIZE), which
will allocate 4096 bytes for a structure that is 4032 + 80 = 4112 bytes
in size.

I think the bug is that keg_large_init() doesn't take
sizeof(struct uma_slab) into account when setting uk_ppera for the keg.
In particular, the bug isn't specific to the bootup process; it only
affects internal zones with an item size in the range [4016, 4096].

The patch below should fix this - could you give it a try?

diff --git a/sys/vm/uma_core.c b/sys/vm/uma_core.c
index 44c91e66769a..48daeb18f9c3 100644
--- a/sys/vm/uma_core.c
+++ b/sys/vm/uma_core.c
@@ -1306,10 +1306,6 @@ keg_large_init(uma_keg_t keg)
 	keg->uk_ipers = 1;
 	keg->uk_rsize = keg->uk_size;
 
-	/* We can't do OFFPAGE if we're internal, bail out here. */
-	if (keg->uk_flags & UMA_ZFLAG_INTERNAL)
-		return;
-
 	/* Check whether we have enough space to not do OFFPAGE. */
 	if ((keg->uk_flags & UMA_ZONE_OFFPAGE) == 0) {
 		shsize = sizeof(struct uma_slab);
@@ -1317,8 +1313,17 @@ keg_large_init(uma_keg_t keg)
 			shsize = (shsize & ~UMA_ALIGN_PTR) +
 			    (UMA_ALIGN_PTR + 1);
 
-		if ((PAGE_SIZE * keg->uk_ppera) - keg->uk_rsize < shsize)
-			keg->uk_flags |= UMA_ZONE_OFFPAGE;
+		if ((PAGE_SIZE * keg->uk_ppera) - keg->uk_rsize < shsize) {
+			/*
+			 * We can't do offpage if we're internal, in which case
+			 * we need an extra page per allocation to contain the
+			 * slab header.
+			 */
+			if ((keg->uk_flags & UMA_ZFLAG_INTERNAL) == 0)
+				keg->uk_flags |= UMA_ZONE_OFFPAGE;
+			else
+				keg->uk_ppera++;
+		}
 	}
 
 	if ((keg->uk_flags & UMA_ZONE_OFFPAGE) &&
diff --git a/sys/vm/vm_page.c b/sys/vm/vm_page.c
index ee7b93bbd719..477a816b0bd2 100644
--- a/sys/vm/vm_page.c
+++ b/sys/vm/vm_page.c
@@ -475,7 +475,8 @@ vm_page_startup(vm_offset_t vaddr)
 	 * in proportion to the zone structure size.
 	 */
 	pages_per_zone = howmany(sizeof(struct uma_zone) +
-	    sizeof(struct uma_cache) * (mp_maxid + 1), UMA_SLAB_SIZE);
+	    sizeof(struct uma_slab) + sizeof(struct uma_cache) * (mp_maxid + 1),
+	    UMA_SLAB_SIZE);
 	if (pages_per_zone > 1) {
 		/* Reserve more pages so that we don't run out. */
 		boot_pages = UMA_BOOT_PAGES_ZONES * pages_per_zone;


More information about the freebsd-current mailing list