看板 DFBSD_bugs 關於我們 聯絡資訊
Ok, I think I have an idea as to what is going on. I think what is happening is that all the vm_map_entry structures are being eaten by other cpus and then the init process is started on a cpu that has yet to synchronize its reserve. That cpu then panics. If this is the problem then the solution is to reserve two vm_map_entries for each cpu so they can bootstrap themselves. Please try this patch, again using the original unmodified MAX_MAPENT. -Matt Index: sys/globaldata.h =================================================================== RCS file: /cvs/src/sys/sys/globaldata.h,v retrieving revision 1.32 diff -u -r1.32 globaldata.h --- sys/globaldata.h 16 Jul 2004 05:51:57 -0000 1.32 +++ sys/globaldata.h 25 Oct 2004 08:40:30 -0000 @@ -143,7 +143,7 @@ struct thread gd_schedthread; /* userland scheduler helper */ struct thread gd_idlethread; SLGlobalData gd_slab; /* slab allocator */ - int gd_vme_kdeficit; /* vm_map_entry reservation */ + int gd_unused02; int gd_vme_avail; /* vm_map_entry reservation */ struct vm_map_entry *gd_vme_base; /* vm_map_entry reservation */ struct systimerq gd_systimerq; /* per-cpu system timers */ Index: vm/vm_map.c =================================================================== RCS file: /cvs/src/sys/vm/vm_map.c,v retrieving revision 1.34 diff -u -r1.34 vm_map.c --- vm/vm_map.c 25 Oct 2004 19:14:33 -0000 1.34 +++ vm/vm_map.c 25 Oct 2004 23:06:20 -0000 @@ -62,7 +62,7 @@ * rights to redistribute these changes. * * $FreeBSD: src/sys/vm/vm_map.c,v 1.187.2.19 2003/05/27 00:47:02 alc Exp $ - * $DragonFly: src/sys/vm/vm_map.c,v 1.34 2004/10/25 19:14:33 dillon Exp $ + * $DragonFly$ */ /* @@ -133,11 +133,14 @@ * maps and requires map entries. */ +#define VMEPERCPU 2 + static struct vm_zone mapentzone_store, mapzone_store; static vm_zone_t mapentzone, mapzone, vmspace_zone; static struct vm_object mapentobj, mapobj; static struct vm_map_entry map_entry_init[MAX_MAPENT]; +static struct vm_map_entry cpu_map_entry_init[MAXCPU][VMEPERCPU]; static struct vm_map map_init[MAX_KMAP]; static vm_map_entry_t vm_map_entry_create(vm_map_t map, int *); @@ -185,7 +188,8 @@ void vm_init2(void) { - zinitna(mapentzone, &mapentobj, NULL, 0, 0, ZONE_USE_RESERVE, 1); + zinitna(mapentzone, &mapentobj, NULL, 0, 0, + ZONE_USE_RESERVE | ZONE_SPECIAL, 1); zinitna(mapzone, &mapobj, NULL, 0, 0, 0, 1); vmspace_zone = zinit("VMSPACE", sizeof (struct vmspace), 0, 0, 3); pmap_init2(); @@ -329,19 +333,32 @@ } /* - * vm_map_entry_cpu_init: + * vm_map_entry_reserve_cpu_init: * * Set an initial negative count so the first attempt to reserve - * space preloads a bunch of vm_map_entry's for this cpu. This - * routine is called in early boot so we cannot just call + * space preloads a bunch of vm_map_entry's for this cpu. Also + * pre-allocate 2 vm_map_entries which will be needed by zalloc() to + * map a new page for vm_map_entry structures. SMP systems are + * particularly sensitive. + * + * This routine is called in early boot so we cannot just call * vm_map_entry_reserve(). * - * May be called for a gd other then mycpu. + * May be called for a gd other then mycpu, but may only be called + * during early boot. */ void vm_map_entry_reserve_cpu_init(globaldata_t gd) { + vm_map_entry_t entry; + int i; + gd->gd_vme_avail -= MAP_RESERVE_COUNT * 2; + entry = &cpu_map_entry_init[gd->gd_cpuid][0]; + for (i = 0; i < VMEPERCPU; ++i, ++entry) { + entry->next = gd->gd_vme_base; + gd->gd_vme_base = entry; + } } /* @@ -404,14 +421,18 @@ /* * vm_map_entry_kreserve: * - * Reserve map entry structures for use in kernel_map or (if it exists) - * kmem_map. These entries have *ALREADY* been reserved on a per-cpu - * basis when the map was inited. This function is used by zalloc() - * to avoid a recursion when zalloc() itself needs to allocate additional - * kernel memory. - * - * This function should only be used when the caller intends to later - * call vm_map_entry_reserve() to 'normalize' the reserve cache. + * Reserve map entry structures for use in kernel_map itself. These + * entries have *ALREADY* been reserved on a per-cpu basis when the map + * was inited. This function is used by zalloc() to avoid a recursion + * when zalloc() itself needs to allocate additional kernel memory. + * + * This function works like the normal reserve but does not load the + * vm_map_entry cache (because that would result in an infinite + * recursion). Note that gd_vme_avail may go negative. This is expected. + * + * Any caller of this function must be sure to renormalize after + * potentially eating entries to ensure that the reserve supply + * remains intact. */ int vm_map_entry_kreserve(int count) @@ -419,23 +440,18 @@ struct globaldata *gd = mycpu; crit_enter(); - gd->gd_vme_kdeficit += count; + gd->gd_vme_avail -= count; crit_exit(); - KKASSERT(gd->gd_vme_base != NULL); + KASSERT(gd->gd_vme_base != NULL, ("no reserved entries left, gd_vme_avail = %d\n", gd->gd_vme_avail)); return(count); } /* * vm_map_entry_krelease: * - * Release previously reserved map entries for kernel_map or kmem_map - * use. This routine determines how many entries were actually used and - * replentishes the kernel reserve supply from vme_avail. - * - * If there is insufficient supply vme_avail will go negative, which is - * ok. We cannot safely call zalloc in this function without getting - * into a recursion deadlock. zalloc() will call vm_map_entry_reserve() - * to regenerate the lost entries. + * Release previously reserved map entries for kernel_map. We do not + * attempt to clean up like the normal release function as this would + * cause an unnecessary (but probably not fatal) deep procedure call. */ void vm_map_entry_krelease(int count) @@ -443,9 +459,7 @@ struct globaldata *gd = mycpu; crit_enter(); - gd->gd_vme_kdeficit -= count; - gd->gd_vme_avail -= gd->gd_vme_kdeficit; /* can go negative */ - gd->gd_vme_kdeficit = 0; + gd->gd_vme_avail += count; crit_exit(); } Index: vm/vm_zone.c =================================================================== RCS file: /cvs/src/sys/vm/vm_zone.c,v retrieving revision 1.16 diff -u -r1.16 vm_zone.c --- vm/vm_zone.c 18 Sep 2004 22:00:37 -0000 1.16 +++ vm/vm_zone.c 25 Oct 2004 09:00:19 -0000 @@ -308,6 +308,10 @@ panic("zget: null zone"); if (z->zflags & ZONE_INTERRUPT) { + /* + * Interrupt zones do not mess with the kernel_map, they + * simply populate an existing mapping. + */ nbytes = z->zpagecount * PAGE_SIZE; nbytes -= nbytes % z->zsize; item = (char *) z->zkva + nbytes; @@ -329,16 +333,37 @@ vmstats.v_wire_count++; } nitems = ((z->zpagecount * PAGE_SIZE) - nbytes) / z->zsize; - } else { + } else if (z->zflags & ZONE_SPECIAL) { + /* + * The special zone is the one used for vm_map_entry_t's. + * We have to avoid an infinite recursion in + * vm_map_entry_reserve() by using vm_map_entry_kreserve() + * instead. The map entries are pre-reserved by the kernel + * by vm_map_entry_reserve_cpu_init(). + */ nbytes = z->zalloc * PAGE_SIZE; - { - item = (void *)kmem_alloc3(kernel_map, nbytes, KM_KRESERVE); - /* note: z might be modified due to blocking */ - if (item != NULL) - zone_kern_pages += z->zalloc; + item = (void *)kmem_alloc3(kernel_map, nbytes, KM_KRESERVE); + + /* note: z might be modified due to blocking */ + if (item != NULL) { + zone_kern_pages += z->zalloc; + bzero(item, nbytes); + } else { + nbytes = 0; } + nitems = nbytes / z->zsize; + } else { + /* + * Otherwise allocate KVA from the kernel_map. + */ + nbytes = z->zalloc * PAGE_SIZE; + + item = (void *)kmem_alloc3(kernel_map, nbytes, 0); + + /* note: z might be modified due to blocking */ if (item != NULL) { + zone_kern_pages += z->zalloc; bzero(item, nbytes); } else { nbytes = 0; @@ -377,10 +402,12 @@ } /* - * Recover any reserve missing due to a zalloc/kreserve/krelease - * recursion. + * A special zone may have used a kernel-reserved vm_map_entry. If + * so we have to be sure to recover our reserve so we don't run out. + * We will panic if we run out. */ - vm_map_entry_reserve(0); + if (z->zflags & ZONE_SPECIAL) + vm_map_entry_reserve(0); return item; } Index: vm/vm_zone.h =================================================================== RCS file: /cvs/src/sys/vm/vm_zone.h,v retrieving revision 1.5 diff -u -r1.5 vm_zone.h --- vm/vm_zone.h 27 Aug 2003 01:43:08 -0000 1.5 +++ vm/vm_zone.h 25 Oct 2004 08:49:21 -0000 @@ -21,6 +21,7 @@ #define ZONE_INTERRUPT 0x0001 /* If you need to allocate at int time */ #define ZONE_PANICFAIL 0x0002 /* panic if the zalloc fails */ +#define ZONE_SPECIAL 0x0004 /* special vm_map_entry zone, see zget() */ #define ZONE_BOOT 0x0010 /* Internal flag used by zbootinit */ #define ZONE_USE_RESERVE 0x0020 /* use reserve memory if necessary */