* Mapping a page might actually need memory - since we usually have locks that
interfere with the page thief, we always need to have reserved a page for this upfront. I introduced a function to the vm_translation_map layer that estimates how much pages a mapping might need at maximum. All functions that map a page now call this and reserve the needed pages upfront. It might not be a nice solution, but it works. * The page thief could run into a panic when trying to call vm_cache_release_ref() on a non-existing (NULL) cache. * Also, it will now ignore wired active pages. * There is still a race condition between the page writer and the vnode destruction - writing a page back needs a valid vnode, but that might just have been deleted. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@22455 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
parent
aa4d8ee6ee
commit
0e18334057
@ -30,6 +30,7 @@ typedef struct vm_translation_map_ops {
|
||||
void (*destroy)(vm_translation_map *map);
|
||||
status_t (*lock)(vm_translation_map *map);
|
||||
status_t (*unlock)(vm_translation_map *map);
|
||||
size_t (*map_max_pages_need)(vm_translation_map *map, addr_t start, addr_t end);
|
||||
status_t (*map)(vm_translation_map *map, addr_t va, addr_t pa,
|
||||
uint32 attributes);
|
||||
status_t (*unmap)(vm_translation_map *map, addr_t start, addr_t end);
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2003-2006, Axel Dörfler, axeld@pinc-software.de.
|
||||
* Copyright 2003-2007, Axel Dörfler, axeld@pinc-software.de.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Copyright 2001, Travis Geiselbrecht. All rights reserved.
|
||||
@ -205,6 +205,13 @@ fill_page_table_entry(page_table_entry *entry, uint32 virtualSegmentID,
|
||||
}
|
||||
|
||||
|
||||
static size_t
|
||||
map_max_pages_need(vm_translation_map *map, addr_t start, addr_t end)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
map_tmap(vm_translation_map *map, addr_t virtualAddress, addr_t physicalAddress, uint32 attributes)
|
||||
{
|
||||
@ -454,6 +461,7 @@ static vm_translation_map_ops tmap_ops = {
|
||||
destroy_tmap,
|
||||
lock_tmap,
|
||||
unlock_tmap,
|
||||
map_max_pages_need,
|
||||
map_tmap,
|
||||
unmap_tmap,
|
||||
query_tmap,
|
||||
|
@ -325,6 +325,13 @@ put_page_table_entry_in_pgtable(page_table_entry *entry,
|
||||
}
|
||||
|
||||
|
||||
static size_t
|
||||
map_max_pages_need(vm_translation_map */*map*/, addr_t start, addr_t end)
|
||||
{
|
||||
return VADDR_TO_PDENT(end) + 1 - VADDR_TO_PDENT(start);
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
map_tmap(vm_translation_map *map, addr_t va, addr_t pa, uint32 attributes)
|
||||
{
|
||||
@ -352,7 +359,7 @@ map_tmap(vm_translation_map *map, addr_t va, addr_t pa, uint32 attributes)
|
||||
vm_page *page;
|
||||
|
||||
// we need to allocate a pgtable
|
||||
page = vm_page_allocate_page(PAGE_STATE_CLEAR, false);
|
||||
page = vm_page_allocate_page(PAGE_STATE_CLEAR, true);
|
||||
|
||||
// mark the page WIRED
|
||||
vm_page_set_state(page, PAGE_STATE_WIRED);
|
||||
@ -737,6 +744,7 @@ static vm_translation_map_ops tmap_ops = {
|
||||
destroy_tmap,
|
||||
lock_tmap,
|
||||
unlock_tmap,
|
||||
map_max_pages_need,
|
||||
map_tmap,
|
||||
unmap_tmap,
|
||||
query_tmap,
|
||||
|
@ -1483,6 +1483,11 @@ vm_create_anonymous_area(team_id team, const char *name, void **address,
|
||||
|
||||
case B_FULL_LOCK:
|
||||
{
|
||||
vm_translation_map *map = &addressSpace->translation_map;
|
||||
size_t reservePages = map->ops->map_max_pages_need(map,
|
||||
area->base, area->base + (area->size - 1));
|
||||
vm_page_reserve_pages(reservePages);
|
||||
|
||||
// Allocate and map all pages for this area
|
||||
mutex_lock(&cache->lock);
|
||||
|
||||
@ -1510,6 +1515,7 @@ vm_create_anonymous_area(team_id team, const char *name, void **address,
|
||||
}
|
||||
|
||||
mutex_unlock(&cache->lock);
|
||||
vm_page_unreserve_pages(reservePages);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1561,9 +1567,12 @@ vm_create_anonymous_area(team_id team, const char *name, void **address,
|
||||
// map them in the address space
|
||||
vm_translation_map *map = &addressSpace->translation_map;
|
||||
addr_t physicalAddress = page->physical_page_number * B_PAGE_SIZE;
|
||||
addr_t virtualAddress;
|
||||
addr_t virtualAddress = area->base;
|
||||
size_t reservePages = map->ops->map_max_pages_need(map,
|
||||
virtualAddress, virtualAddress + (area->size - 1));
|
||||
off_t offset = 0;
|
||||
|
||||
vm_page_reserve_pages(reservePages);
|
||||
mutex_lock(&cache->lock);
|
||||
map->ops->lock(map);
|
||||
|
||||
@ -1587,6 +1596,7 @@ vm_create_anonymous_area(team_id team, const char *name, void **address,
|
||||
|
||||
map->ops->unlock(map);
|
||||
mutex_unlock(&cache->lock);
|
||||
vm_page_unreserve_pages(reservePages);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1687,6 +1697,10 @@ vm_map_physical_memory(team_id team, const char *name, void **_address,
|
||||
// make sure our area is mapped in completely
|
||||
|
||||
vm_translation_map *map = &locker.AddressSpace()->translation_map;
|
||||
size_t reservePages = map->ops->map_max_pages_need(map, area->base,
|
||||
area->base + (size - 1));
|
||||
|
||||
vm_page_reserve_pages(reservePages);
|
||||
map->ops->lock(map);
|
||||
|
||||
for (addr_t offset = 0; offset < size; offset += B_PAGE_SIZE) {
|
||||
@ -1695,6 +1709,7 @@ vm_map_physical_memory(team_id team, const char *name, void **_address,
|
||||
}
|
||||
|
||||
map->ops->unlock(map);
|
||||
vm_page_unreserve_pages(reservePages);
|
||||
}
|
||||
|
||||
if (status < B_OK)
|
||||
@ -1966,6 +1981,10 @@ vm_clone_area(team_id team, const char *name, void **address,
|
||||
map->ops->unlock(map);
|
||||
|
||||
map = &targetAddressSpace->translation_map;
|
||||
size_t reservePages = map->ops->map_max_pages_need(map,
|
||||
newArea->base, newArea->base + (newArea->size - 1));
|
||||
|
||||
vm_page_reserve_pages(reservePages);
|
||||
map->ops->lock(map);
|
||||
|
||||
for (addr_t offset = 0; offset < newArea->size;
|
||||
@ -1975,7 +1994,13 @@ vm_clone_area(team_id team, const char *name, void **address,
|
||||
}
|
||||
|
||||
map->ops->unlock(map);
|
||||
vm_page_unreserve_pages(reservePages);
|
||||
} else {
|
||||
vm_translation_map *map = &targetAddressSpace->translation_map;
|
||||
size_t reservePages = map->ops->map_max_pages_need(map,
|
||||
newArea->base, newArea->base + (newArea->size - 1));
|
||||
vm_page_reserve_pages(reservePages);
|
||||
|
||||
// map in all pages from source
|
||||
for (vm_page *page = cache->page_list; page != NULL;
|
||||
page = page->cache_next) {
|
||||
@ -1983,6 +2008,8 @@ vm_clone_area(team_id team, const char *name, void **address,
|
||||
+ ((page->cache_offset << PAGE_SHIFT) - newArea->cache_offset),
|
||||
protection);
|
||||
}
|
||||
|
||||
vm_page_unreserve_pages(reservePages);
|
||||
}
|
||||
}
|
||||
if (status == B_OK)
|
||||
@ -2549,6 +2576,7 @@ vm_unmap_pages(vm_area *area, addr_t base, size_t size)
|
||||
}
|
||||
|
||||
|
||||
/*! When calling this function, you need to have pages reserved! */
|
||||
status_t
|
||||
vm_map_page(vm_area *area, vm_page *page, addr_t address, uint32 protection)
|
||||
{
|
||||
@ -4193,12 +4221,14 @@ vm_soft_fault(addr_t originalAddress, bool isWrite, bool isUser)
|
||||
// The top most cache has no fault handler, so let's see if the cache or its sources
|
||||
// already have the page we're searching for (we're going from top to bottom)
|
||||
|
||||
vm_page_reserve_pages(2);
|
||||
vm_translation_map *map = &addressSpace->translation_map;
|
||||
size_t reservePages = 2 + map->ops->map_max_pages_need(map,
|
||||
originalAddress, originalAddress);
|
||||
vm_page_reserve_pages(reservePages);
|
||||
// we may need up to 2 pages - reserving them upfront makes sure
|
||||
// we don't have any cache locked, so that the page daemon/thief
|
||||
// can do their job without problems
|
||||
|
||||
vm_translation_map *map = &addressSpace->translation_map;
|
||||
vm_dummy_page dummyPage;
|
||||
dummyPage.cache = NULL;
|
||||
dummyPage.state = PAGE_STATE_INACTIVE;
|
||||
@ -4252,7 +4282,7 @@ vm_soft_fault(addr_t originalAddress, bool isWrite, bool isUser)
|
||||
}
|
||||
|
||||
vm_cache_release_ref(topCache);
|
||||
vm_page_unreserve_pages(2);
|
||||
vm_page_unreserve_pages(reservePages);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
@ -776,7 +776,8 @@ page_thief(void* /*unused*/)
|
||||
enqueue_page(&sActivePageQueue, page);
|
||||
|
||||
if ((page->state == PAGE_STATE_INACTIVE
|
||||
|| stealActive && page->state == PAGE_STATE_ACTIVE)
|
||||
|| (stealActive && page->state == PAGE_STATE_ACTIVE
|
||||
&& page->wired_count == 0))
|
||||
&& page->usage_count <= score)
|
||||
break;
|
||||
}
|
||||
@ -826,8 +827,8 @@ page_thief(void* /*unused*/)
|
||||
{
|
||||
if (fIsLocked)
|
||||
mutex_unlock(&fCache->lock);
|
||||
|
||||
vm_cache_release_ref(fCache);
|
||||
if (fCache != NULL)
|
||||
vm_cache_release_ref(fCache);
|
||||
}
|
||||
|
||||
bool IsLocked() { return fIsLocked; }
|
||||
@ -836,7 +837,7 @@ page_thief(void* /*unused*/)
|
||||
vm_cache *fCache;
|
||||
bool fIsLocked;
|
||||
} cacheLocker(page);
|
||||
|
||||
|
||||
if (!cacheLocker.IsLocked())
|
||||
continue;
|
||||
|
||||
@ -863,7 +864,7 @@ page_thief(void* /*unused*/)
|
||||
|
||||
// we can now steal this page
|
||||
|
||||
//dprintf(" steal page %p from cache %p\n", page, cache);
|
||||
//dprintf(" steal page %p from cache %p\n", page, page->cache);
|
||||
|
||||
vm_cache_remove_page(page->cache, page);
|
||||
vm_page_set_state(page, PAGE_STATE_FREE);
|
||||
|
Loading…
Reference in New Issue
Block a user