551 lines
15 KiB
Groff
551 lines
15 KiB
Groff
.\" $NetBSD: uvm_hotplug.9,v 1.6 2020/01/17 12:34:55 skrll Exp $
|
|
.\"
|
|
.\" Copyright (c) 2016 The NetBSD Foundation, Inc.
|
|
.\" All rights reserved.
|
|
.\"
|
|
.\" This code is derived from software contributed to The NetBSD Foundation
|
|
.\" by Cherry G Mathew and Santhosh N Raju.
|
|
.\"
|
|
.\" Redistribution and use in source and binary forms, with or without
|
|
.\" modification, are permitted provided that the following conditions
|
|
.\" are met:
|
|
.\" 1. Redistributions of source code must retain the above copyright
|
|
.\" notice, this list of conditions and the following disclaimer.
|
|
.\" 2. Redistributions in binary form must reproduce the above copyright
|
|
.\" notice, this list of conditions and the following disclaimer in the
|
|
.\" documentation and/or other materials provided with the distribution.
|
|
.\"
|
|
.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
|
|
.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
|
.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
|
|
.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
.\" POSSIBILITY OF SUCH DAMAGE.
|
|
.\"
|
|
.Dd January 17, 2020
|
|
.Dt UVM_HOTPLUG 9
|
|
.Os
|
|
.Sh NAME
|
|
.Nm uvm_physseg_init ,
|
|
.Nm uvm_physseg_valid_p ,
|
|
.Nm uvm_physseg_get_start ,
|
|
.Nm uvm_physseg_get_end ,
|
|
.Nm uvm_physseg_get_avail_start ,
|
|
.Nm uvm_physseg_get_avail_end ,
|
|
.Nm uvm_physseg_get_pg ,
|
|
.Nm uvm_physseg_get_pmseg ,
|
|
.Nm uvm_physseg_get_free_list ,
|
|
.Nm uvm_physseg_get_start_hint ,
|
|
.Nm uvm_physseg_set_start_hint ,
|
|
.Nm uvm_physseg_get_next ,
|
|
.Nm uvm_physseg_get_prev ,
|
|
.Nm uvm_physseg_get_first ,
|
|
.Nm uvm_physseg_get_last ,
|
|
.Nm uvm_physseg_get_highest_frame ,
|
|
.Nm uvm_physseg_find ,
|
|
.Nm uvm_page_physload ,
|
|
.Nm uvm_page_physunload ,
|
|
.Nm uvm_page_physunload_force ,
|
|
.Nm uvm_physseg_plug ,
|
|
.Nm uvm_physseg_unplug ,
|
|
.Nm uvm_physseg_set_avail_start ,
|
|
.Nm uvm_physseg_set_avail_end
|
|
.Nd memory hotplug manager
|
|
.Sh SYNOPSIS
|
|
.In uvm/uvm_physseg.h
|
|
.Ft void
|
|
.Fn uvm_physseg_init "void"
|
|
.Ft uvm_physseg_t
|
|
.Fn uvm_page_physload "paddr_t start" "paddr_t end" "paddr_t avail_start" \
|
|
"paddr_t avail_end" "int free_list"
|
|
.Ft bool
|
|
.Fn uvm_page_physunload "uvm_physseg_t upm" "int free_list" \
|
|
"paddr_t *paddrp"
|
|
.Ft bool
|
|
.Fn uvm_page_physunload_force "uvm_physseg_t upm" "int free_list" \
|
|
"paddr_t *paddrp"
|
|
.Ft bool
|
|
.Fn uvm_physseg_plug "paddr_t pfn" "size_t npages" "uvm_physseg_t *upmp"
|
|
.Ft bool
|
|
.Fn uvm_physseg_unplug "paddr_t pfn" "size_t npages"
|
|
.Sh DESCRIPTION
|
|
These utility routines provide the ability to tell
|
|
.Xr uvm 9
|
|
about system memory segments.
|
|
When the kernel is compiled with
|
|
.Cd 'options UVM_HOTPLUG' ,
|
|
memory segments are handled in a dynamic data structure
|
|
.Pq Xr rbtree 3
|
|
compared to a static array when not.
|
|
This enables kernel code to add
|
|
or remove information about memory segments at any point after boot -
|
|
thus "hotplug".
|
|
.Pp
|
|
.Fn uvm_page_physload ,
|
|
.Fn uvm_page_physunload ,
|
|
and
|
|
.Fn uvm_page_physunload_force
|
|
are legacy interfaces which may be removed in the future.
|
|
They must
|
|
never be used after
|
|
.Xr uvm_init 9 .
|
|
.Pp
|
|
.Sy WARNING :
|
|
This is an experimental feature and should not be used in production
|
|
environments.
|
|
Furthermore, attempting to "hotplug" without
|
|
.Cd 'options UVM_HOTPLUG'
|
|
after boot will almost certainly end in a
|
|
.Xr panic 9 .
|
|
.Sh USAGE
|
|
.Ss INITIALIZING HOTPLUG
|
|
The function
|
|
.Fn uvm_physseg_init
|
|
initializes the hotplug subsystem.
|
|
This is expected to happen exactly
|
|
once, at boot time, and from MD code.
|
|
.Ss PLUGGING IN MEMORY
|
|
.Fn uvm_page_physload
|
|
registers
|
|
.Xr uvm 9
|
|
with a memory segment span, and on a specified
|
|
.Fa free_list .
|
|
It must be called at system boot time as part of setting up memory
|
|
management.
|
|
The arguments describe the start and end of the physical addresses of the
|
|
segment, and the available start and end addresses of pages not already in use.
|
|
If a system has memory banks of different speeds the slower memory should be
|
|
given a higher
|
|
.Fa free_list
|
|
value.
|
|
.Bl -tag -offset indent -width "avail_start"
|
|
.It Fa start
|
|
Starting page frame number of the physical memory segments.
|
|
.It Fa end
|
|
Ending page frame number of the physical memory segments.
|
|
.It Fa avail_start
|
|
Available starting page frame number of the physical memory segments.
|
|
.It Fa avail_end
|
|
Available ending page frame number of the physical memory segments.
|
|
.It Fa free_list
|
|
The free list types are defined in the Machine Dependent code.
|
|
.El
|
|
.Pp
|
|
This function returns a valid
|
|
.Dv uvm_physseg_t
|
|
handle when a successful plug occurs, else it will return
|
|
.Dv UVM_PHYSSEG_TYPE_INVALID
|
|
when the plug fails.
|
|
.Pp
|
|
.Fn uvm_physseg_plug
|
|
registers
|
|
.Xr uvm 9
|
|
with a memory segment span.
|
|
It can also be called to initiate a hotplug and register a newly
|
|
"hotplugged" physical memory range into the VM.
|
|
Unlike
|
|
.Fn uvm_page_physload
|
|
this function can, if
|
|
.Cd 'options UVM_HOTPLUG'
|
|
is enabled at compile time, be used after
|
|
.Xr uvm_init 9 .
|
|
The arguments describe the start page frame, the number of pages to
|
|
plug starting from the start page frame and an optional return variable, which
|
|
points to a valid
|
|
.Fa uvm_physseg_t
|
|
handle when a successful plug occurs.
|
|
.Bl -tag -offset indent -width "npages"
|
|
.It Fa pfn
|
|
Starting page frame number of the physical memory segment.
|
|
.It Fa npages
|
|
Total number of pages from the starting page frame number to plug in.
|
|
.It Fa upmp
|
|
If upmp is not
|
|
.Dv NULL ,
|
|
then on a successful plug, a valid pointer to the uvm_physseg_t handle
|
|
for the segment which was plugged is returned.
|
|
.El
|
|
.Pp
|
|
This function returns
|
|
.Fa true
|
|
when a successful plug occurs,
|
|
.Fa false
|
|
otherwise.
|
|
.Ss UNPLUGGING MEMORY
|
|
The functions
|
|
.Fn uvm_page_physunload ,
|
|
.Fn uvm_page_physunload_force ,
|
|
and
|
|
.Fn uvm_physseg_unplug
|
|
make
|
|
.Xr uvm 9
|
|
forget about previously registered memory segments or portions of
|
|
such.
|
|
.Pp
|
|
.Fn uvm_page_physunload
|
|
unloads pages from a segment (from the front or from the back)
|
|
depending on its availability.
|
|
When the last page is removed, the
|
|
segment handle is invalidated and supporting metadata is freed.
|
|
.Pp
|
|
Note: This function can only be used during boot time.
|
|
Pages, once unloaded, are unregistered from uvm and are therefore
|
|
assumed to be managed by the code which called
|
|
.Fn uvm_page_physunload 9
|
|
(usually boot time MD code, for boottime memory "allocation").
|
|
.Pp
|
|
The arguments are:
|
|
.Bl -tag -offset indent -width "free_list"
|
|
.It Fa upm
|
|
The handle identifying segment from which we are trying to unload memory.
|
|
.It Fa free_list
|
|
The free list types are defined in the Machine Dependent code.
|
|
.It Fa paddrp
|
|
The pointer to the physical address that was unloaded.
|
|
.El
|
|
.Pp
|
|
If the unload was successful,
|
|
.Fa true
|
|
is returned,
|
|
.Fa false
|
|
otherwise.
|
|
.Pp
|
|
.Fn uvm_page_physunload_force
|
|
unconditionally unloads pages from a segment.
|
|
When the last page is removed, the segment handle
|
|
is invalidated and supporting metadata is freed.
|
|
.Pp
|
|
Note: This function can only be used during boot time.
|
|
Pages, once unloaded, are unregistered from uvm and are therefore
|
|
assumed to be managed by the code which called
|
|
.Fn uvm_page_physunload_force 9
|
|
(usually boot time MD code, for boottime memory "allocation").
|
|
.Pp
|
|
The arguments are:
|
|
.Bl -tag -offset indent -width "free_list"
|
|
.It Fa upm
|
|
The handle identifying segment from which we are trying to unload memory.
|
|
.It Fa free_list
|
|
The free list types are defined in the Machine Dependent code.
|
|
.It Fa paddrp
|
|
The pointer to the physical address that was unloaded.
|
|
.El
|
|
.Pp
|
|
If the unload was successful
|
|
.Fa true
|
|
is returned,
|
|
.Fa false
|
|
otherwise.
|
|
.Pp
|
|
.Fn uvm_physseg_unplug
|
|
can be called to unplug an existing physical memory segment.
|
|
Unlike
|
|
.Fn uvm_page_physunload
|
|
and
|
|
.Fn uvm_page_physunload_force ,
|
|
it can be called after
|
|
.Xr uvm_init 9 ,
|
|
if
|
|
.Cd 'options UVM_HOTPLUG'
|
|
is enabled at compile time.
|
|
.Fn uvm_hotplug 9
|
|
makes no effort to manage the state of the underlying physical
|
|
memory.
|
|
It is up to the caller to ensure that it is not in use,
|
|
either by
|
|
.Xr uvm 9 ,
|
|
or by any other sub-system.
|
|
Further, any hardware
|
|
quiescing that may be required is the responsibility of MD code.
|
|
The arguments
|
|
describe the start page frame and the number of pages to unplug.
|
|
The arguments are:
|
|
.Bl -tag -offset indent -width "npages"
|
|
.It Fa pfn
|
|
Starting page frame number of the physical memory segment.
|
|
.It Fa npages
|
|
Total number of pages from the starting page frame number to unplug.
|
|
.El
|
|
.Pp
|
|
Returns
|
|
.Fa true
|
|
or
|
|
.Fa false
|
|
depending on success or failure respectively.
|
|
.Sh UTILITY FUNCTIONS
|
|
.Bl -ohang
|
|
.It Ft bool
|
|
.Fn uvm_physseg_valid_p "uvm_physseg_t upm"
|
|
.It Ft paddr_t
|
|
.Fn uvm_physseg_get_start "uvm_physseg_t upm"
|
|
.It Ft paddr_t
|
|
.Fn uvm_physseg_get_end "uvm_physseg_t upm"
|
|
.It Ft paddr_t
|
|
.Fn uvm_physseg_get_avail_start "uvm_physseg_t upm"
|
|
.It Ft paddr_t
|
|
.Fn uvm_physseg_get_avail_end "uvm_physseg_t upm"
|
|
.It Ft struct vm_page *
|
|
.Fn uvm_physseg_get_pg "uvm_physseg_t upm" "paddr_t index"
|
|
.It Ft struct pmap_physseg *
|
|
.Fn uvm_physseg_get_pmesg "uvm_physseg_t upm"
|
|
.It Ft int
|
|
.Fn uvm_physseg_get_free_list "uvm_physseg_t upm"
|
|
.It Ft u_int
|
|
.Fn uvm_physseg_get_start_hint "uvm_physseg_t upm"
|
|
.It Ft bool
|
|
.Fn uvm_physseg_set_start_hint "uvm_physseg_t upm" "u_int start_hint"
|
|
.It Ft uvm_physseg_t
|
|
.Fn uvm_physseg_get_next "uvm_physseg_t upm"
|
|
.It Ft uvm_physseg_t
|
|
.Fn uvm_physseg_get_prev "uvm_physseg_t upm"
|
|
.It Ft uvm_physseg_t
|
|
.Fn uvm_physseg_get_first "void"
|
|
.It Ft uvm_physseg_t
|
|
.Fn uvm_physseg_get_last "void"
|
|
.It Ft paddr_t
|
|
.Fn uvm_physseg_get_highest_frame "void"
|
|
.It Ft paddr_t
|
|
.Fn uvm_physseg_find "paddr pframe" "psize_t *offsetp"
|
|
.It Ft void
|
|
.Fn uvm_physseg_set_avail_start "uvm_physseg_t upm" "paddr_t avail_start"
|
|
.It Ft void
|
|
.Fn uvm_physseg_set_avail_end "uvm_physseg_t upm" "paddr_t avail_end"
|
|
.El
|
|
.Pp
|
|
.Fn uvm_physseg_valid_p
|
|
validates a handle that is passed in, returns
|
|
.Fa true
|
|
if the given handle is valid,
|
|
.Fa false
|
|
otherwise.
|
|
.Pp
|
|
.Fn uvm_physseg_get_start
|
|
if a valid
|
|
.Fa uvm_physseg_t
|
|
handle is passed in, it returns the starting physical address of
|
|
the segment.
|
|
The returned value is of type
|
|
.Ft paddr_t .
|
|
In case the handle is invalid the returned value will match
|
|
.Ft ( paddr_t )
|
|
\-1.
|
|
.Pp
|
|
.Fn uvm_physseg_get_end
|
|
if a valid
|
|
.Fa uvm_physseg_t
|
|
handle is passed in, it returns the ending physical address of the
|
|
segment.
|
|
The returned value is of type
|
|
.Ft paddr_t .
|
|
In case the handle is invalid the returned value will match
|
|
.Ft ( paddr_t )
|
|
\-1.
|
|
.Pp
|
|
.Fn uvm_physseg_get_avail_start
|
|
if a valid
|
|
.Fa uvm_physseg_t
|
|
handle is passed in, it returns the available starting physical
|
|
address of the segment.
|
|
The returned value is of type
|
|
.Ft paddr_t .
|
|
In case the handle is invalid the returned value will match
|
|
.Ft ( paddr_t )
|
|
\-1.
|
|
.Pp
|
|
.Fn uvm_physseg_get_avail_end
|
|
if a valid
|
|
.Fa uvm_physseg_t
|
|
handle is passed in, it returns the available ending physical
|
|
address of the segment.
|
|
The returned value is of type
|
|
.Ft paddr_t .
|
|
In case the handle is invalid the returned value will match
|
|
.Ft ( paddr_t )
|
|
\-1.
|
|
.Pp
|
|
.Fn uvm_physseg_get_pg
|
|
if a valid
|
|
.Fa uvm_physseg_t
|
|
handle along with an index value is passed in, it returns the
|
|
.Fa struct vm_page *
|
|
object contained in that location.
|
|
.Pp
|
|
.Fn uvm_physseg_get_pmseg
|
|
if a valid
|
|
.Fa uvm_physseg_t
|
|
handle is passed in, it returns the
|
|
.Fa struct pmap_physseg *
|
|
object contained in the handle.
|
|
.Pp
|
|
.Fn uvm_physseg_get_free_list
|
|
if a valid
|
|
.Fa uvm_physseg_t
|
|
handle is passed in, it returns the
|
|
.Fa free_list
|
|
type for which the current segment is associated with.
|
|
The returned value is of
|
|
type
|
|
.Fa int .
|
|
.Pp
|
|
.Fn uvm_physseg_get_start_hint
|
|
if a valid
|
|
.Fa uvm_physseg_t
|
|
handle is passed in, it returns the
|
|
.Fa start_hint
|
|
type for the current segment.
|
|
The returned value is of type
|
|
.Fa u_int .
|
|
.Pp
|
|
.Fn uvm_physseg_set_start_hint
|
|
if a valid handle along with the
|
|
.Fa start_hint
|
|
is passed in, the value is set in the segment.
|
|
And a
|
|
.Fa true
|
|
is returned to indicate a successful value setting.
|
|
In case the handle is invalid a
|
|
.Fa false
|
|
is returned.
|
|
.Pp
|
|
.Fn uvm_physseg_get_next
|
|
if a valid handle is passed in, it returns the next valid
|
|
.Fa uvm_physseg_t
|
|
handle in the sequence.
|
|
However if the handle passed is the last segment in the
|
|
sequence the function returns
|
|
.Fa UVM_PHYSSEG_TYPE_INVALID_OVERFLOW .
|
|
Passing an invalid handle is not fatal, and returns
|
|
.Fa UVM_PHYSSEG_TYPE_INVALID .
|
|
.
|
|
.Pp
|
|
.Fn uvm_physseg_get_prev
|
|
if a valid handle is passed in, it returns the previous validh
|
|
.Fa uvm_physseg_t
|
|
handle in the sequence.
|
|
However if the handle passed is the first segment in
|
|
the sequence the function returns
|
|
.Fa UVM_PHYSSEG_TYPE_INVALID_EMPTY .
|
|
Passing an invalid handle is not fatal, and returns
|
|
.Fa UVM_PHYSSEG_TYPE_INVALID .
|
|
.
|
|
.Pp
|
|
.Fn uvm_physseg_get_first
|
|
returns the first valid
|
|
.Fa uvm_physseg_t
|
|
handle in the sequence.
|
|
However if there are no valid handles in the sequence
|
|
yet, the function returns
|
|
.Fa UVM_PHYSSEG_TYPE_INVALID_EMPTY .
|
|
.Pp
|
|
.Fn uvm_physseg_get_last
|
|
returns the last valid
|
|
.Fa uvm_physseg_t
|
|
handle in the sequence.
|
|
However if there are no valid handles in the sequence
|
|
yet, the function returns
|
|
.Fa UVM_PHYSSEG_TYPE_INVALID_EMPTY .
|
|
.Pp
|
|
.Fn uvm_physseg_get_highest_frame
|
|
returns the frame number of the highest registered physical page frame
|
|
which is of type
|
|
.Ft paddr_t .
|
|
XXX: Searching on empty sequences are not yet processed in the function.
|
|
.Pp
|
|
.Fn uvm_physseg_find
|
|
searches for a given segment containing the page frame
|
|
.Ft ( paddr_t )
|
|
passed in.
|
|
If a segment that falls between starting and ending addresses is
|
|
found, the corresponding
|
|
.Fa uvm_physseg_t
|
|
handle is returned else a
|
|
.Fa UVM_PHYSSEG_TYPE_INVALID
|
|
is returned.
|
|
The second parameter, if not set to
|
|
.Dv NULL ,
|
|
the offset value of the page frame passed in with respect to the
|
|
starting address is set to the appropriate
|
|
.Fa psize_t
|
|
value if the search was successful in finding the segment.
|
|
.Pp
|
|
.Fn uvm_physseg_set_avail_start
|
|
if a valid
|
|
.Fa uvm_physseg_t
|
|
handle is passed in along with the available starting physical address of the
|
|
segment of type
|
|
.Ft paddr_t ,
|
|
the value is set in the segment.
|
|
.Pp
|
|
.Fn uvm_physseg_set_avail_end
|
|
if a valid
|
|
.Fa uvm_physseg_t
|
|
handle is passed in along with the available ending physical address of the
|
|
segment of type
|
|
.Ft paddr_t ,
|
|
the value is set in the segment.
|
|
.Sh NOTES
|
|
.Fn uvm_physseg_plug
|
|
and
|
|
.Fn uvm_physseg_unplug
|
|
must never be used after
|
|
.Xr uvm_init 9
|
|
in a kernel build where
|
|
.Cd 'options UVM_HOTPLUG'
|
|
is not enabled.
|
|
.Sh DIAGNOSTICS
|
|
Tests for
|
|
.Nm
|
|
are in
|
|
.Pa tests/sys/uvm .
|
|
.Pp
|
|
Unit / functional tests are in
|
|
.Pa tests/sys/uvm/t_uvm_physseg.c .
|
|
These tests focus on the expected working of the
|
|
.Nm
|
|
API and its utility functions.
|
|
.Pp
|
|
Load tests can be found in
|
|
.Pa tests/sys/uvm/t_uvm_physseg_load.c .
|
|
These tests focus on stressing the
|
|
.Nm
|
|
implementation in order to make performance comparisons between kernel
|
|
builds with and without
|
|
.Cd 'options UVM_HOTPLUG'
|
|
.
|
|
.\" .Sh RETURN VALUES
|
|
.\" .Sh EXAMPLES
|
|
.Sh CODE REFERENCES
|
|
The uvm hotplug feature is implemented in the file
|
|
.Pa sys/uvm/uvm_physseg.c .
|
|
The uvm hotplug API is exported via
|
|
.Pa sys/uvm/uvm_physseg.h .
|
|
.Sh SEE ALSO
|
|
.Xr extent 9 ,
|
|
.Xr free 9 ,
|
|
.Xr malloc 9 ,
|
|
.Xr memoryallocators 9 ,
|
|
.Xr uvm 9
|
|
.Sh HISTORY
|
|
This API emerged out of the need to insert new pages at runtime in the
|
|
Xen
|
|
.Xr x86/balloon 4
|
|
driver.
|
|
.Sh AUTHORS
|
|
.An -nosplit
|
|
.An Cherry G. Mathew
|
|
.Aq Mt cherry@NetBSD.org
|
|
designed and integrated the API.
|
|
.Pp
|
|
.An Santhosh N. Raju
|
|
.Aq Mt santhosh.raju@gmail.com
|
|
implemented the dynamic segment handling code and all tests for this API.
|
|
.Pp
|
|
.An Nick Hudson
|
|
.Aq Mt skrll@NetBSD.org
|
|
contributed bugfixes and testing on a wide range of hardware ports.
|