hment.c revision 977046508bbcbc7faa3e0cc7a3c7bf15c2e5dc57
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/sysmacros.h>
#include <vm/seg_kmem.h>
/*
* When pages are shared by more than one mapping, a list of these
* structs hangs off of the page_t connected by the hm_next and hm_prev
* fields. Every hment is also indexed by a system-wide hash table, using
* hm_hashnext to connect it to the chain of hments in a single hash
* bucket.
*/
struct hment {
#ifdef __amd64
#endif
};
/*
* Value returned by hment_walk() when dealing with a single mapping
* embedded in the page_t.
*/
/*
* The hment reserve is similar to the htable reserve, with the following
* exception. Hment's are never needed for HAT kmem allocs.
*
* The hment_reserve_amount variable is used, so that you can change it's
* value to zero via a kernel debugger to force stealing to get tested.
*/
extern kthread_t *hat_reserves_thread;
/*
* Possible performance RFE: we might need to make this dynamic, perhaps
* based on the number of pages in the system.
*/
static hment_t **hment_hash;
/*
* Lots of highly shared pages will have the same value for "entry" (consider
* the starting address of "xterm" or "sh"). So we'll distinguish them by
* adding the pfn of the page table into both the high bits.
* The shift by 9 corresponds to the range of values for entry (0..511).
*/
/*
* "mlist_lock" is a hashed mutex lock for protecting per-page mapping
* lists and "hash_lock" is a similar lock protecting the hment hash
* table. The hashed approach is taken to avoid the spatial overhead of
* maintaining a separate lock for each page, while still achieving better
* scalability than a single lock would allow.
*/
/*
* the shift by 9 is so that all large pages don't use the same hash bucket
*/
#define MLIST_MUTEX(pp) \
(MLIST_NUM_LOCK - 1)]
static hment_t *hment_steal(void);
/*
* put one hment onto the reserves list
*/
static void
{
}
/*
* Take one hment from the reserve.
*/
static hment_t *
hment_get_reserve(void)
{
/*
* We rely on a "donation system" to refill the hment reserve
* list, which only takes place when we are allocating hments for
* user mappings. It is theoretically possible that an incredibly
* long string of kernel hment_alloc()s with no intervening user
* hment_alloc()s could exhaust that pool.
*/
if (hment_reserve_count != 0) {
}
return (hm);
}
/*
* Allocate an hment
*/
static hment_t *
{
/*
* If we aren't using the reserves, try using kmem to get an hment.
* Donate any successful allocations to reserves if low.
*
* If we're in panic, resort to using the reserves.
*/
if (!USE_HAT_RESERVES()) {
for (;;) {
if (USE_HAT_RESERVES() ||
break;
}
}
/*
* If allocation failed, we need to tap the reserves or steal
*/
if (USE_HAT_RESERVES())
hm = hment_get_reserve();
/*
* If we still haven't gotten an hment, attempt to steal one by
* victimizing a mapping in a user htable.
*/
hm = hment_steal();
/*
* we're in dire straights, try the reserve
*/
hm = hment_get_reserve();
/*
* still no hment is a serious problem.
*/
panic("hment_alloc(): no reserve, couldn't steal");
}
return (hm);
}
/*
* Free an hment, possibly to the reserves list when called from the
* thread using the reserves. For example, when freeing an hment during an
* htable_steal(), we can't recurse into the kmem allocator, so we just
* push the hment onto the reserve list.
*/
void
{
#ifdef DEBUG
/*
* zero out all fields to try and force any race conditions to segfault
*/
#endif
if (USE_HAT_RESERVES() ||
else
}
int
{
}
void
{
}
void
{
}
/*
* Internal routine to add a full hment to a page_t mapping list
*/
static void
{
/*
* Add the hment to the page's mapping list.
*/
/*
* Add the hment to the system-wide hash table.
*/
}
/*
* Prepare a mapping list entry to the given page.
*
* There are 4 different situations to deal with:
*
* - Adding the first mapping to a page_t as an embedded hment
* - Refaulting on an existing embedded mapping
* - Upgrading an embedded mapping when adding a 2nd mapping
* - Adding another mapping to a page_t that already has multiple mappings
* note we don't optimized for the refaulting case here.
*
* same page and the need to drop all locks while allocating hments, any or
* all of the 3 situations can occur (and in almost any order) in any given
* call. Isn't this fun!
*/
hment_t *
{
for (;;) {
/*
* The most common case is establishing the first mapping to a
* page, so check that first. This doesn't need any allocated
* hment.
*/
break;
/*
* we had an hment already, so free it and retry
*/
goto free_and_continue;
}
/*
* If there is an embedded mapping, we may need to
* convert it to an hment.
*/
/* should point to htable */
/*
* If we are faulting on a pre-existing mapping
* This happens a lot due to segmap.
*/
break;
goto free_and_continue;
}
/*
* If we have an hment allocated, use it to promote the
* existing embedded mapping.
*/
}
/*
* We either didn't have an hment allocated or we just
* used it for the embedded mapping. In either case,
* allocate another hment and restart.
*/
goto allocate_and_continue;
}
/*
* Last possibility is that we're adding an hment to a list
* of hments.
*/
break;
hm = hment_alloc();
continue;
/*
* we allocated an hment already, free it and retry
*/
hment_free(hm);
}
return (hm);
}
/*
*
* hment_prepare() should have properly set up the situation.
*/
void
{
/*
* The most common case is establishing the first mapping to a
* page, so check that first. This doesn't need any allocated
* hment.
*/
return;
}
/*
* We should never get here with a pre-existing embedded maping
*/
/*
* add the new hment to the mapping list
*/
}
/*
* Walk through the mappings for a page.
*
* must already have done an x86_hm_enter()
*/
hment_t *
{
hm = HMENT_EMBEDDED;
} else {
}
} else {
} else {
}
}
}
return (hm);
}
/*
* Remove a mapping to a page from its mapping list. Must have
* the corresponding mapping list locked.
* Finds the mapping list entry with the given pte_t and
* unlinks it from the mapping list.
*/
hment_t *
{
/*
* Check if we have only one mapping embedded in the page_t.
*/
return (NULL);
}
/*
* Otherwise it must be in the list of hments.
* Find the hment in the system-wide hash table and remove it.
*/
}
panic("hment_remove() missing in hash table pp=%lx, ht=%lx,"
}
if (prev)
else
/*
* Remove the hment from the page's mapping list
*/
else
return (hm);
}
/*
* Put initial hment's in the reserve pool.
*/
void
{
while (hment_reserve_count < count) {
return;
}
}
/*
* Readjust the hment reserves after they may have been used.
*/
void
{
/*
* Free up any excess reserves
*/
while (hment_reserve_count > hment_reserve_amount) {
hm = hment_get_reserve();
return;
hment_free(hm);
}
}
/*
* initialize hment data structures
*/
void
hment_init(void)
{
int i;
/*
* Initialize kmem caches. On 32 bit kernel's we shut off
* debug information to save on precious kernel VA usage.
*/
KM_SLEEP);
for (i = 0; i < MLIST_NUM_LOCK; i++)
for (i = 0; i < HASH_NUM_LOCK; i++)
}
/*
* return the number of mappings to a page
*
* Note there is no ASSERT() that the MUTEX is held for this.
* Hence the return value might be inaccurate if this is called without
* doing an x86_hm_enter().
*/
{
cnt = 0;
cnt = 1;
else
/*
* walk through all larger mapping sizes counting mappings
*/
continue;
++cnt;
++cnt;
}
}
}
}
return (cnt);
}
/*
* We need to steal an hment. Walk through all the page_t's until we
* find one that has multiple mappings. Unload one of the mappings
* page to try and spread the pain.
*/
static hment_t *
hment_steal(void)
{
while (!found_one) {
pp = page_first();
/*
* The loop and function exit here if nothing found to steal.
*/
return (NULL);
/*
* Only lock the page_t if it has hments.
*/
continue;
/*
* Search the mapping list for a usable mapping.
*/
ht->ht_lock_cnt == 0) {
found_one = 1;
break;
}
}
}
if (!found_one)
}
/*
* Steal the mapping we found. Note that hati_page_unmap() will
* do the x86_hm_exit().
*/
return (hm);
}