Implemented unmapping of partial areas (mmap()/munmap()). Review
welcome. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@25629 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
+121
-17
@@ -204,6 +204,11 @@ static mutex sAvailableMemoryLock;
|
|||||||
static void delete_area(vm_address_space *addressSpace, vm_area *area);
|
static void delete_area(vm_address_space *addressSpace, vm_area *area);
|
||||||
static vm_address_space *get_address_space_by_area_id(area_id id);
|
static vm_address_space *get_address_space_by_area_id(area_id id);
|
||||||
static status_t vm_soft_fault(addr_t address, bool isWrite, bool isUser);
|
static status_t vm_soft_fault(addr_t address, bool isWrite, bool isUser);
|
||||||
|
static status_t map_backing_store(vm_address_space *addressSpace,
|
||||||
|
vm_cache *cache, void **_virtualAddress, off_t offset, addr_t size,
|
||||||
|
uint32 addressSpec, int wiring, int protection, int mapping,
|
||||||
|
vm_area **_area, const char *areaName, bool unmapAddressRange, bool kernel);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// #pragma mark -
|
// #pragma mark -
|
||||||
@@ -1190,37 +1195,129 @@ insert_area(vm_address_space *addressSpace, void **_address,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*! Cuts a piece out of an area. If the given cut range covers the complete
|
||||||
|
area, it is deleted. If it covers the beginning or the end, the area is
|
||||||
|
resized accordingly. If the range covers some part in the middle of the
|
||||||
|
area, it is split in two; in this case the second area is returned via
|
||||||
|
\a _secondArea (the variable is left untouched in the other cases).
|
||||||
|
The address space must be write locked.
|
||||||
|
*/
|
||||||
|
static status_t
|
||||||
|
cut_area(vm_address_space* addressSpace, vm_area* area, addr_t address,
|
||||||
|
addr_t lastAddress, vm_area** _secondArea, bool kernel)
|
||||||
|
{
|
||||||
|
// Does the cut range intersect with the area at all?
|
||||||
|
addr_t areaLast = area->base + (area->size - 1);
|
||||||
|
if (area->base > lastAddress || areaLast < address)
|
||||||
|
return B_OK;
|
||||||
|
|
||||||
|
// Is the area fully covered?
|
||||||
|
if (area->base >= address && areaLast <= lastAddress) {
|
||||||
|
delete_area(addressSpace, area);
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
AreaCacheLocker cacheLocker(area);
|
||||||
|
vm_cache* cache = area->cache;
|
||||||
|
|
||||||
|
// Cut the end only?
|
||||||
|
if (areaLast <= lastAddress) {
|
||||||
|
addr_t newSize = address - area->base;
|
||||||
|
|
||||||
|
// unmap pages
|
||||||
|
vm_unmap_pages(area, address, area->size - newSize, false);
|
||||||
|
|
||||||
|
// If no one else uses the area's cache, we can resize it, too.
|
||||||
|
if (cache->areas == area && area->cache_next == NULL
|
||||||
|
&& list_is_empty(&cache->consumers)) {
|
||||||
|
status_t error = vm_cache_resize(cache, newSize);
|
||||||
|
if (error != B_OK)
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
area->size = newSize;
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cut the beginning only?
|
||||||
|
if (area->base >= address) {
|
||||||
|
addr_t newBase = lastAddress + 1;
|
||||||
|
addr_t newSize = areaLast - lastAddress;
|
||||||
|
|
||||||
|
// unmap pages
|
||||||
|
vm_unmap_pages(area, area->base, newBase - area->base, false);
|
||||||
|
|
||||||
|
// TODO: If no one else uses the area's cache, we should resize it, too!
|
||||||
|
|
||||||
|
area->cache_offset += newBase - area->base;
|
||||||
|
area->base = newBase;
|
||||||
|
area->size = newSize;
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The tough part -- cut a piece out of the middle of the area.
|
||||||
|
// We do that by shrinking the area to the begin section and creating a
|
||||||
|
// new area for the end section.
|
||||||
|
|
||||||
|
addr_t firstNewSize = address - area->base;
|
||||||
|
addr_t secondBase = lastAddress + 1;
|
||||||
|
addr_t secondSize = areaLast - lastAddress;
|
||||||
|
|
||||||
|
// unmap pages
|
||||||
|
vm_unmap_pages(area, address, area->size - firstNewSize, false);
|
||||||
|
|
||||||
|
// resize the area
|
||||||
|
addr_t oldSize = area->size;
|
||||||
|
area->size = firstNewSize;
|
||||||
|
|
||||||
|
// TODO: If no one else uses the area's cache, we might want to create a
|
||||||
|
// new cache for the second area, transfer the concerned pages from the
|
||||||
|
// first cache to it and resize the first cache.
|
||||||
|
|
||||||
|
// map the second area
|
||||||
|
vm_area* secondArea;
|
||||||
|
void* secondBaseAddress = (void*)secondBase;
|
||||||
|
status_t error = map_backing_store(addressSpace, cache, &secondBaseAddress,
|
||||||
|
area->cache_offset + (secondBase - area->base), secondSize,
|
||||||
|
B_EXACT_ADDRESS, area->wiring, area->protection, REGION_NO_PRIVATE_MAP,
|
||||||
|
&secondArea, area->name, false, kernel);
|
||||||
|
if (error != B_OK) {
|
||||||
|
area->size = oldSize;
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We need a cache reference for the new area.
|
||||||
|
vm_cache_acquire_ref(cache);
|
||||||
|
|
||||||
|
if (_secondArea != NULL)
|
||||||
|
*_secondArea = secondArea;
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*! Deletes all areas in the given address range.
|
/*! Deletes all areas in the given address range.
|
||||||
The address space must be write-locked.
|
The address space must be write-locked.
|
||||||
NOTE: At the moment deleting only complete areas is supported.
|
|
||||||
*/
|
*/
|
||||||
static status_t
|
static status_t
|
||||||
unmap_address_range(vm_address_space *addressSpace, addr_t address, addr_t size,
|
unmap_address_range(vm_address_space *addressSpace, addr_t address, addr_t size,
|
||||||
bool kernel)
|
bool kernel)
|
||||||
{
|
{
|
||||||
// TODO: Support deleting partial areas!
|
|
||||||
|
|
||||||
size = PAGE_ALIGN(size);
|
size = PAGE_ALIGN(size);
|
||||||
addr_t lastAddress = address + (size - 1);
|
addr_t lastAddress = address + (size - 1);
|
||||||
|
|
||||||
// check whether any areas are only partially covered
|
// Check, whether the caller is allowed to modify the concerned areas.
|
||||||
vm_area* area = vm_area_lookup(addressSpace, address);
|
vm_area* area;
|
||||||
if (area != NULL && area->base < address)
|
|
||||||
return B_UNSUPPORTED;
|
|
||||||
|
|
||||||
area = vm_area_lookup(addressSpace, lastAddress);
|
|
||||||
if (area != NULL && lastAddress - area->base < area->size - 1)
|
|
||||||
return B_UNSUPPORTED;
|
|
||||||
|
|
||||||
// all areas (if any) are fully covered; we can delete them,
|
|
||||||
// but first we need to check, whether the caller is allowed to do that
|
|
||||||
if (!kernel) {
|
if (!kernel) {
|
||||||
area = addressSpace->areas;
|
area = addressSpace->areas;
|
||||||
while (area != NULL) {
|
while (area != NULL) {
|
||||||
vm_area* nextArea = area->address_space_next;
|
vm_area* nextArea = area->address_space_next;
|
||||||
|
|
||||||
if (area->id != RESERVED_AREA_ID) {
|
if (area->id != RESERVED_AREA_ID) {
|
||||||
if (area->base >= address && area->base < lastAddress) {
|
addr_t areaLast = area->base + (area->size - 1);
|
||||||
|
if (area->base < lastAddress && address < areaLast) {
|
||||||
if ((area->protection & B_KERNEL_AREA) != 0)
|
if ((area->protection & B_KERNEL_AREA) != 0)
|
||||||
return B_NOT_ALLOWED;
|
return B_NOT_ALLOWED;
|
||||||
}
|
}
|
||||||
@@ -1235,8 +1332,15 @@ unmap_address_range(vm_address_space *addressSpace, addr_t address, addr_t size,
|
|||||||
vm_area* nextArea = area->address_space_next;
|
vm_area* nextArea = area->address_space_next;
|
||||||
|
|
||||||
if (area->id != RESERVED_AREA_ID) {
|
if (area->id != RESERVED_AREA_ID) {
|
||||||
if (area->base >= address && area->base < lastAddress)
|
addr_t areaLast = area->base + (area->size - 1);
|
||||||
delete_area(addressSpace, area);
|
if (area->base < lastAddress && address < areaLast) {
|
||||||
|
status_t error = cut_area(addressSpace, area, address,
|
||||||
|
lastAddress, NULL, kernel);
|
||||||
|
if (error != B_OK)
|
||||||
|
return error;
|
||||||
|
// Failing after already messing with areas is ugly, but we
|
||||||
|
// can't do anything about it.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
area = nextArea;
|
area = nextArea;
|
||||||
|
|||||||
Reference in New Issue
Block a user