Subject: MACHINE_NONCONTIG suggestions
To: None <core@netbsd.org>
From: Gordon W. Ross <gwr@jericho.mc.com>
List: tech-kern
Date: 11/29/1994 16:17:58
I've recently done some studying of the MACHINE_NONCONTIG option
(and converted the sun3 port over to use it). As a result, I
have some suggestions for improving the MACHINE_NONCONTIG code.
Introduction:
Many machines have a "mostly" contiguous physical memory space,
consisting of a large range (i.e. 16MB) marred by a small number
of unusable "holes". For example, the i386/ISA/EISA bus machines
typically have about 16MB of memory which is contiguous except for
a the I/O hole at 640K..1M. As another example, the Sun3/50 has
4MB (or 8MB) of contiguous memory, but 128K of it starting at 1M
is used for video RAM.
For machines like the above, with a few, small holes, it is
desirable to have the VM system define the vm_page_array with
holes in it that correspond directly to the real holes.
This allows the function pmap_page_index() to be fast (it
can be just a macro!) at the cost of a small amount of wasted
memory in the vm_page_array (10 ints per unused page).
This is the classic "speed vs. memory use" trade-off, and
is a big win for machines like the above.
The Problem:
The VM system is almost, but not quite prepared to cooperate
with a pmap module that wants vm_page_array to have holes.
I found that I need to make some refinements to the vm_page.c
function pmap_startup() before "holes" would work. The
changes are backward-compatible with existing pmap code.
First, I'll provide an updated specification for the improved
interface between pmap_startup() and the pmap module. That
is followed by the changes to vm_page.c for this interface.
(Comments appreciated)
Gordon Ross
Improved Interface: (using MACHINE_NONCONTIG)
/* pmap.h */
/*
* pmap_page_index()
*
* Given a physical address, return the page index.
* The main advantage of the "hole" scheme used here is
* that this frequently called function can be a macro!
*/
#define pmap_page_index(pa) sun3_btop(pa)
/* pmap.c */
/*
* For simplicity, this interface retains the variables
* that were used in the old interface (without NONCONTIG).
* These are set in pmap_bootstrap() and used in
* pmap_next_page().
*/
/* Kernel virtual address space available: */
extern vm_offset_t virtual_avail, virtual_end;
/* Physical address space available: */
extern vm_offset_t avail_start, avail_end;
/* The "hole" (used to skip the Sun3/50 video RAM) */
extern vm_offset_t hole_start, hole_size;
/*
* For our convenience, vm_page.c implements:
* pmap_startup(), pmap_steal_memory()
* using the functions:
* pmap_virtual_space(), pmap_free_pages(), pmap_next_page(),
* which are much simpler to implement.
*/
/*
* How much virtual space does this kernel have?
* (After mapping kernel text, data, etc.)
*/
void
pmap_virtual_space(v_start, v_end)
vm_offset_t *v_start;
vm_offset_t *v_end;
{
*v_start = virtual_avail;
*v_end = virtual_end;
}
/*
* Return the number of page indices in the range of
* possible return values for pmap_page_index() for
* all addresses provided by pmap_next_page(). This
* return value is used to allocate per-page data.
*
* Note that a machine with a "hole" in physical memory
* may include the pages in the hole in this count, and
* skip the pages in the hole in pmap_next_page().
*/
u_int
pmap_free_pages()
{
int bytes;
bytes = avail_end - avail_start;
return(sun3_btop(bytes));
}
/*
* If there are still physical pages available, put the address of
* the next available one at paddr and return non-zero, otherwise
* return zero to indicate that there are no more free pages.
* Note that avail_next is set to avail_start in _bootstrap().
*
* Imporant: The page indices of the pages returned here must be
* in ascending order.
*/
int
pmap_next_page(paddr)
vm_offset_t *paddr;
{
/* Is it time to skip over the hole? */
if (avail_next == hole_start)
avail_next += sun3_round_page(hole_size);
/* Used up all ranges? */
if (avail_next >= avail_end)
return FALSE;
/* Still have more memory... */
*paddr = avail_next;
avail_next += NBPG;
return TRUE;
}
/*
* pmap_bootstrap_alloc()
* Note: Not used with MACHINE_NONCONTIG
*/
Finally, here are the changes for vm/vm_page.c:
*** vm_page.c.orig Mon Nov 21 17:49:39 1994
--- vm_page.c Tue Nov 29 14:37:28 1994
***************
*** 492,530 ****
vm_offset_t *startp;
vm_offset_t *endp;
{
! unsigned int i;
vm_offset_t paddr;
/*
! * We calculate how many page frames we will have
! * and then allocate the page structures in one chunk.
*/
! vm_page_count = ((PAGE_SIZE * pmap_free_pages() +
! (round_page(virtual_space_start) - virtual_space_start)) /
! (PAGE_SIZE + sizeof *vm_page_array));
!
! vm_page_array = (vm_page_t) pmap_steal_memory(vm_page_count
! * sizeof *vm_page_array);
!
/*
! * Initialize the page frames.
*/
-
for (i = 0; i < vm_page_count; i++) {
! if (!pmap_next_page(&paddr))
! break;
!
VM_PAGE_INIT(&vm_page_array[i], NULL, NULL);
vm_page_array[i].phys_addr = paddr;
vm_page_free(&vm_page_array[i]);
}
! /*
! * Remember the actual page count and the index of the first page
! */
! vm_page_count = i;
! first_page = pmap_page_index(vm_page_array[0].phys_addr);
!
*startp = virtual_space_start;
*endp = virtual_space_end;
}
--- 492,564 ----
vm_offset_t *startp;
vm_offset_t *endp;
{
! unsigned int i, pmap_idx;
! unsigned int freepages;
vm_offset_t paddr;
/*
! * We calculate how many page frames we will have
! * and then allocate the page structures in one chunk.
! * The calculation is non-trivial. We want:
! *
! * vmpages > (freepages - (vmpages / sizeof(vm_page_t)))
! *
! * which, with some algebra, becomes:
! *
! * vmpages > (freepages * sizeof(...) / (1 + sizeof(...)))
! *
! * The value of vm_page_count need not be exact, but must be
! * large enough so vm_page_array handles the index range.
*/
+
+ freepages = pmap_free_pages();
+ /* Fudge slightly to deal with truncation error. */
+ freepages += 1; /* fudge */
+
+ vm_page_count = (PAGE_SIZE * freepages) /
+ (PAGE_SIZE + sizeof(*vm_page_array));
+
+ vm_page_array = (vm_page_t)
+ pmap_steal_memory(vm_page_count * sizeof(*vm_page_array));
! #ifdef DIAGNOSTIC
/*
! * Zero everyting in case the holes are used...
! * XXX - Set to something that will cause a panic.
*/
for (i = 0; i < vm_page_count; i++) {
! bzero(&vm_page_array[i], sizeof(*vm_page_array));
! }
! #endif
!
! /*
! * Initialize the page frames.
! * Note that some page indices may not be usable
! * when pmap_free_pages() counts pages in a hole.
! * Values returned by pmap_page_index() are the
! * array indices in vm_page_array directly.
! */
! if (!pmap_next_page(&paddr))
! panic("pmap_startup: can't get first page");
! first_page = pmap_page_index(paddr);
! i = 0;
! for (;;) {
! /* Initialize a page array element. */
VM_PAGE_INIT(&vm_page_array[i], NULL, NULL);
vm_page_array[i].phys_addr = paddr;
vm_page_free(&vm_page_array[i]);
+
+ /* Are there more physical pages? */
+ if (!pmap_next_page(&paddr))
+ break;
+ pmap_idx = pmap_page_index(paddr);
+ i = pmap_idx - first_page;
+
+ /* Don't trust pmap_page_index()... */
+ if (i < 0 || i >= vm_page_count)
+ panic("pmap_startup: bad i=0x%x", i);
}
!
*startp = virtual_space_start;
*endp = virtual_space_end;
}