Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 26 Apr 2020 20:08:58 +0000 (UTC)
From:      Mark Johnston <markj@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r360354 - in head/sys: kern sys vm
Message-ID:  <202004262008.03QK8wxQ038706@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: markj
Date: Sun Apr 26 20:08:57 2020
New Revision: 360354
URL: https://svnweb.freebsd.org/changeset/base/360354

Log:
  Use a single VM object for kernel stacks.
  
  Previously we allocated a separate VM object for each kernel stack.
  However, fully constructed kernel stacks are cached by UMA, so there is
  no harm in using a single global object for all stacks.  This reduces
  memory consumption and makes it easier to define a memory allocation
  policy for kernel stack pages, with the aim of reducing physical memory
  fragmentation.
  
  Add a global kstack_object, and use the stack KVA address to index into
  the object like we do with kernel_object.
  
  Reviewed by:	kib
  Tested by:	pho
  Sponsored by:	The FreeBSD Foundation
  Differential Revision:	https://reviews.freebsd.org/D24473

Modified:
  head/sys/kern/kern_thread.c
  head/sys/sys/proc.h
  head/sys/vm/vm_extern.h
  head/sys/vm/vm_glue.c
  head/sys/vm/vm_kern.h
  head/sys/vm/vm_swapout.c

Modified: head/sys/kern/kern_thread.c
==============================================================================
--- head/sys/kern/kern_thread.c	Sun Apr 26 20:06:08 2020	(r360353)
+++ head/sys/kern/kern_thread.c	Sun Apr 26 20:08:57 2020	(r360354)
@@ -84,7 +84,7 @@ _Static_assert(offsetof(struct thread, td_pflags) == 0
     "struct thread KBI td_pflags");
 _Static_assert(offsetof(struct thread, td_frame) == 0x498,
     "struct thread KBI td_frame");
-_Static_assert(offsetof(struct thread, td_emuldata) == 0x6b0,
+_Static_assert(offsetof(struct thread, td_emuldata) == 0x6a0,
     "struct thread KBI td_emuldata");
 _Static_assert(offsetof(struct proc, p_flag) == 0xb0,
     "struct proc KBI p_flag");

Modified: head/sys/sys/proc.h
==============================================================================
--- head/sys/sys/proc.h	Sun Apr 26 20:06:08 2020	(r360353)
+++ head/sys/sys/proc.h	Sun Apr 26 20:08:57 2020	(r360354)
@@ -348,7 +348,6 @@ struct thread {
 	/* LP64 hole */
 	struct callout	td_slpcallout;	/* (h) Callout for sleep. */
 	struct trapframe *td_frame;	/* (k) */
-	struct vm_object *td_kstack_obj;/* (a) Kstack object. */
 	vm_offset_t	td_kstack;	/* (a) Kernel VA of kstack. */
 	int		td_kstack_pages; /* (a) Size of the kstack. */
 	volatile u_int	td_critnest;	/* (k*) Critical section nest level. */

Modified: head/sys/vm/vm_extern.h
==============================================================================
--- head/sys/vm/vm_extern.h	Sun Apr 26 20:06:08 2020	(r360353)
+++ head/sys/vm/vm_extern.h	Sun Apr 26 20:08:57 2020	(r360354)
@@ -126,6 +126,8 @@ struct sf_buf *vm_imgact_map_page(vm_object_t object, 
 void vm_imgact_unmap_page(struct sf_buf *sf);
 void vm_thread_dispose(struct thread *td);
 int vm_thread_new(struct thread *td, int pages);
+void vm_thread_stack_back(struct domainset *ds, vm_offset_t kaddr,
+    vm_page_t ma[], int npages, int req_class);
 u_int vm_active_count(void);
 u_int vm_inactive_count(void);
 u_int vm_laundry_count(void);

Modified: head/sys/vm/vm_glue.c
==============================================================================
--- head/sys/vm/vm_glue.c	Sun Apr 26 20:06:08 2020	(r360353)
+++ head/sys/vm/vm_glue.c	Sun Apr 26 20:08:57 2020	(r360354)
@@ -264,9 +264,9 @@ vm_sync_icache(vm_map_t map, vm_offset_t va, vm_offset
 	pmap_sync_icache(map->pmap, va, sz);
 }
 
+vm_object_t kstack_object;
 static uma_zone_t kstack_cache;
 static int kstack_cache_size;
-static int kstack_domain_iter;
 
 static int
 sysctl_kstack_cache_size(SYSCTL_HANDLER_ARGS)
@@ -285,23 +285,15 @@ SYSCTL_PROC(_vm, OID_AUTO, kstack_cache_size,
 
 /*
  * Create the kernel stack (including pcb for i386) for a new thread.
- * This routine directly affects the fork perf for a process and
- * create performance for a thread.
  */
 static vm_offset_t
-vm_thread_stack_create(struct domainset *ds, vm_object_t *ksobjp, int pages)
+vm_thread_stack_create(struct domainset *ds, int pages)
 {
 	vm_page_t ma[KSTACK_MAX_PAGES];
-	vm_object_t ksobj;
 	vm_offset_t ks;
 	int i;
 
 	/*
-	 * Allocate an object for the kstack.
-	 */
-	ksobj = vm_object_allocate(OBJT_DEFAULT, pages);
-	
-	/*
 	 * Get a kernel virtual address for this thread's kstack.
 	 */
 #if defined(__mips__)
@@ -319,54 +311,45 @@ vm_thread_stack_create(struct domainset *ds, vm_object
 #endif
 	if (ks == 0) {
 		printf("%s: kstack allocation failed\n", __func__);
-		vm_object_deallocate(ksobj);
 		return (0);
 	}
-	if (vm_ndomains > 1) {
-		ksobj->domain.dr_policy = ds;
-		ksobj->domain.dr_iter =
-		    atomic_fetchadd_int(&kstack_domain_iter, 1);
-	}
 
 	if (KSTACK_GUARD_PAGES != 0) {
 		pmap_qremove(ks, KSTACK_GUARD_PAGES);
 		ks += KSTACK_GUARD_PAGES * PAGE_SIZE;
 	}
 
-	/* 
-	 * For the length of the stack, link in a real page of ram for each
-	 * page of stack.
+	/*
+	 * Allocate physical pages to back the stack.
 	 */
-	VM_OBJECT_WLOCK(ksobj);
-	(void)vm_page_grab_pages(ksobj, 0, VM_ALLOC_NORMAL | VM_ALLOC_WIRED,
-	    ma, pages);
+	vm_thread_stack_back(ds, ks, ma, pages, VM_ALLOC_NORMAL);
 	for (i = 0; i < pages; i++)
 		vm_page_valid(ma[i]);
-	VM_OBJECT_WUNLOCK(ksobj);
 	pmap_qenter(ks, ma, pages);
-	*ksobjp = ksobj;
 
 	return (ks);
 }
 
 static void
-vm_thread_stack_dispose(vm_object_t ksobj, vm_offset_t ks, int pages)
+vm_thread_stack_dispose(vm_offset_t ks, int pages)
 {
 	vm_page_t m;
+	vm_pindex_t pindex;
 	int i;
 
+	pindex = atop(ks - VM_MIN_KERNEL_ADDRESS);
+
 	pmap_qremove(ks, pages);
-	VM_OBJECT_WLOCK(ksobj);
+	VM_OBJECT_WLOCK(kstack_object);
 	for (i = 0; i < pages; i++) {
-		m = vm_page_lookup(ksobj, i);
+		m = vm_page_lookup(kstack_object, pindex + i);
 		if (m == NULL)
 			panic("%s: kstack already missing?", __func__);
 		vm_page_xbusy_claim(m);
 		vm_page_unwire_noq(m);
 		vm_page_free(m);
 	}
-	VM_OBJECT_WUNLOCK(ksobj);
-	vm_object_deallocate(ksobj);
+	VM_OBJECT_WUNLOCK(kstack_object);
 	kva_free(ks - (KSTACK_GUARD_PAGES * PAGE_SIZE),
 	    (pages + KSTACK_GUARD_PAGES) * PAGE_SIZE);
 }
@@ -377,7 +360,6 @@ vm_thread_stack_dispose(vm_object_t ksobj, vm_offset_t
 int
 vm_thread_new(struct thread *td, int pages)
 {
-	vm_object_t ksobj;
 	vm_offset_t ks;
 
 	/* Bounds check */
@@ -387,12 +369,8 @@ vm_thread_new(struct thread *td, int pages)
 		pages = KSTACK_MAX_PAGES;
 
 	ks = 0;
-	ksobj = NULL;
-	if (pages == kstack_pages && kstack_cache != NULL) {
+	if (pages == kstack_pages && kstack_cache != NULL)
 		ks = (vm_offset_t)uma_zalloc(kstack_cache, M_NOWAIT);
-		if (ks != 0) 
-			ksobj = PHYS_TO_VM_PAGE(pmap_kextract(ks))->object;
-	}
 
 	/*
 	 * Ensure that kstack objects can draw pages from any memory
@@ -401,10 +379,9 @@ vm_thread_new(struct thread *td, int pages)
 	 */
 	if (ks == 0)
 		ks = vm_thread_stack_create(DOMAINSET_PREF(PCPU_GET(domain)),
-		    &ksobj, pages);
+		    pages);
 	if (ks == 0)
 		return (0);
-	td->td_kstack_obj = ksobj;
 	td->td_kstack = ks;
 	td->td_kstack_pages = pages;
 	return (1);
@@ -416,26 +393,52 @@ vm_thread_new(struct thread *td, int pages)
 void
 vm_thread_dispose(struct thread *td)
 {
-	vm_object_t ksobj;
 	vm_offset_t ks;
 	int pages;
 
 	pages = td->td_kstack_pages;
-	ksobj = td->td_kstack_obj;
 	ks = td->td_kstack;
 	td->td_kstack = 0;
 	td->td_kstack_pages = 0;
 	if (pages == kstack_pages)
 		uma_zfree(kstack_cache, (void *)ks);
 	else
-		vm_thread_stack_dispose(ksobj, ks, pages);
+		vm_thread_stack_dispose(ks, pages);
 }
 
+/*
+ * Allocate physical pages, following the specified NUMA policy, to back a
+ * kernel stack.
+ */
+void
+vm_thread_stack_back(struct domainset *ds, vm_offset_t ks, vm_page_t ma[],
+    int npages, int req_class)
+{
+	vm_pindex_t pindex;
+	int n;
+
+	pindex = atop(ks - VM_MIN_KERNEL_ADDRESS);
+
+	VM_OBJECT_WLOCK(kstack_object);
+	for (n = 0; n < npages;) {
+		if (vm_ndomains > 1)
+			kstack_object->domain.dr_policy = ds;
+
+		/*
+		 * Use WAITFAIL to force a reset of the domain selection policy
+		 * if we had to sleep for pages.
+		 */
+		n += vm_page_grab_pages(kstack_object, pindex + n,
+		    req_class | VM_ALLOC_WIRED | VM_ALLOC_WAITFAIL,
+		    &ma[n], npages - n);
+	}
+	VM_OBJECT_WUNLOCK(kstack_object);
+}
+
 static int
 kstack_import(void *arg, void **store, int cnt, int domain, int flags)
 {
 	struct domainset *ds;
-	vm_object_t ksobj;
 	int i;
 
 	if (domain == UMA_ANYDOMAIN)
@@ -444,8 +447,7 @@ kstack_import(void *arg, void **store, int cnt, int do
 		ds = DOMAINSET_PREF(domain);
 
 	for (i = 0; i < cnt; i++) {
-		store[i] = (void *)vm_thread_stack_create(ds, &ksobj,
-		    kstack_pages);
+		store[i] = (void *)vm_thread_stack_create(ds, kstack_pages);
 		if (store[i] == NULL)
 			break;
 	}
@@ -460,15 +462,15 @@ kstack_release(void *arg, void **store, int cnt)
 
 	for (i = 0; i < cnt; i++) {
 		ks = (vm_offset_t)store[i];
-		vm_thread_stack_dispose(
-		    PHYS_TO_VM_PAGE(pmap_kextract(ks))->object,
-		    ks, kstack_pages);
+		vm_thread_stack_dispose(ks, kstack_pages);
 	}
 }
 
 static void
 kstack_cache_init(void *null)
 {
+	kstack_object = vm_object_allocate(OBJT_SWAP,
+	    atop(VM_MAX_KERNEL_ADDRESS - VM_MIN_KERNEL_ADDRESS));
 	kstack_cache = uma_zcache_create("kstack_cache",
 	    kstack_pages * PAGE_SIZE, NULL, NULL, NULL, NULL,
 	    kstack_import, kstack_release, NULL,
@@ -476,8 +478,7 @@ kstack_cache_init(void *null)
 	kstack_cache_size = imax(128, mp_ncpus * 4);
 	uma_zone_set_maxcache(kstack_cache, kstack_cache_size);
 }
-
-SYSINIT(vm_kstacks, SI_SUB_KTHREAD_INIT, SI_ORDER_ANY, kstack_cache_init, NULL);
+SYSINIT(vm_kstacks, SI_SUB_KMEM, SI_ORDER_ANY, kstack_cache_init, NULL);
 
 #ifdef KSTACK_USAGE_PROF
 /*

Modified: head/sys/vm/vm_kern.h
==============================================================================
--- head/sys/vm/vm_kern.h	Sun Apr 26 20:06:08 2020	(r360353)
+++ head/sys/vm/vm_kern.h	Sun Apr 26 20:08:57 2020	(r360354)
@@ -77,5 +77,6 @@ extern struct vmem *memguard_arena;
 extern u_long vm_kmem_size;
 extern u_int exec_map_entries;
 extern u_int exec_map_entry_size;
+extern vm_object_t kstack_object;
 
 #endif /* _VM_VM_KERN_H_ */

Modified: head/sys/vm/vm_swapout.c
==============================================================================
--- head/sys/vm/vm_swapout.c	Sun Apr 26 20:06:08 2020	(r360353)
+++ head/sys/vm/vm_swapout.c	Sun Apr 26 20:08:57 2020	(r360354)
@@ -104,6 +104,7 @@ __FBSDID("$FreeBSD$");
 
 #include <vm/vm.h>
 #include <vm/vm_param.h>
+#include <vm/vm_kern.h>
 #include <vm/vm_object.h>
 #include <vm/vm_page.h>
 #include <vm/vm_map.h>
@@ -526,24 +527,26 @@ again:
 static void
 vm_thread_swapout(struct thread *td)
 {
-	vm_object_t ksobj;
 	vm_page_t m;
+	vm_offset_t kaddr;
+	vm_pindex_t pindex;
 	int i, pages;
 
 	cpu_thread_swapout(td);
+	kaddr = td->td_kstack;
 	pages = td->td_kstack_pages;
-	ksobj = td->td_kstack_obj;
-	pmap_qremove(td->td_kstack, pages);
-	VM_OBJECT_WLOCK(ksobj);
+	pindex = atop(kaddr - VM_MIN_KERNEL_ADDRESS);
+	pmap_qremove(kaddr, pages);
+	VM_OBJECT_WLOCK(kstack_object);
 	for (i = 0; i < pages; i++) {
-		m = vm_page_lookup(ksobj, i);
+		m = vm_page_lookup(kstack_object, pindex + i);
 		if (m == NULL)
 			panic("vm_thread_swapout: kstack already missing?");
 		vm_page_dirty(m);
 		vm_page_xunbusy_unchecked(m);
 		vm_page_unwire(m, PQ_LAUNDRY);
 	}
-	VM_OBJECT_WUNLOCK(ksobj);
+	VM_OBJECT_WUNLOCK(kstack_object);
 }
 
 /*
@@ -552,38 +555,36 @@ vm_thread_swapout(struct thread *td)
 static void
 vm_thread_swapin(struct thread *td, int oom_alloc)
 {
-	vm_object_t ksobj;
 	vm_page_t ma[KSTACK_MAX_PAGES];
+	vm_offset_t kaddr;
 	int a, count, i, j, pages, rv;
 
+	kaddr = td->td_kstack;
 	pages = td->td_kstack_pages;
-	ksobj = td->td_kstack_obj;
-	VM_OBJECT_WLOCK(ksobj);
-	(void)vm_page_grab_pages(ksobj, 0, oom_alloc | VM_ALLOC_WIRED, ma,
-	    pages);
-	VM_OBJECT_WUNLOCK(ksobj);
+	vm_thread_stack_back(td->td_domain.dr_policy, kaddr, ma, pages,
+	    oom_alloc);
 	for (i = 0; i < pages;) {
 		vm_page_assert_xbusied(ma[i]);
 		if (vm_page_all_valid(ma[i])) {
 			i++;
 			continue;
 		}
-		vm_object_pip_add(ksobj, 1);
+		vm_object_pip_add(kstack_object, 1);
 		for (j = i + 1; j < pages; j++)
 			if (vm_page_all_valid(ma[j]))
 				break;
-		VM_OBJECT_WLOCK(ksobj);
-		rv = vm_pager_has_page(ksobj, ma[i]->pindex, NULL, &a);
-		VM_OBJECT_WUNLOCK(ksobj);
+		VM_OBJECT_WLOCK(kstack_object);
+		rv = vm_pager_has_page(kstack_object, ma[i]->pindex, NULL, &a);
+		VM_OBJECT_WUNLOCK(kstack_object);
 		KASSERT(rv == 1, ("%s: missing page %p", __func__, ma[i]));
 		count = min(a + 1, j - i);
-		rv = vm_pager_get_pages(ksobj, ma + i, count, NULL, NULL);
+		rv = vm_pager_get_pages(kstack_object, ma + i, count, NULL, NULL);
 		KASSERT(rv == VM_PAGER_OK, ("%s: cannot get kstack for proc %d",
 		    __func__, td->td_proc->p_pid));
-		vm_object_pip_wakeup(ksobj);
+		vm_object_pip_wakeup(kstack_object);
 		i += count;
 	}
-	pmap_qenter(td->td_kstack, ma, pages);
+	pmap_qenter(kaddr, ma, pages);
 	cpu_thread_swapin(td);
 }
 



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202004262008.03QK8wxQ038706>