Add virtual address space allocator for user-space.

This commit is contained in:
Jonas 'Sortie' Termansen 2013-08-22 01:44:46 +02:00
parent e875babf8e
commit b9560409a7
3 changed files with 217 additions and 0 deletions

View File

@ -0,0 +1,70 @@
/*******************************************************************************
Copyright(C) Jonas 'Sortie' Termansen 2013.
This file is part of Sortix.
Sortix is free software: you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
Sortix is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
details.
You should have received a copy of the GNU General Public License along with
Sortix. If not, see <http://www.gnu.org/licenses/>.
sortix/kernel/yielder.h
Template that allows creation of easily-iterable sequences.
*******************************************************************************/
#ifndef INCLUDE_SORTIX_KERNEL_YIELDER_H
#define INCLUDE_SORTIX_KERNEL_YIELDER_H
namespace Sortix {
class finished_yielder { };
template <class yielder_type, class yielded_type> class yielder_iterator
{
public:
yielder_iterator(yielder_type yielder) : yielder_object(yielder)
{
has_value = yielder_object.yield(&current_value);
}
bool is_finished() const
{
return !has_value;
}
bool operator!=(const yielder_iterator& other) const
{
return !(is_finished() && other.is_finished());
}
yielded_type operator*()
{
return current_value;
}
const yielder_iterator& operator++()
{
has_value = yielder_object.yield(&current_value);
return *this;
}
private:
yielder_type yielder_object;
yielded_type current_value;
bool has_value;
};
} // namespace Sortix
#endif

View File

@ -48,4 +48,9 @@
#define MAP_SHARED (1<<0)
#define MAP_PRIVATE (1<<1)
#define MAP_ANONYMOUS (1<<2)
#define MAP_FIXED (1<<3)
#define MAP_FAILED ((void*) -1)
#endif

View File

@ -24,14 +24,19 @@
#include <sys/types.h>
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <sortix/mman.h>
#include <sortix/kernel/decl.h>
#include <sortix/kernel/kernel.h>
#include <sortix/kernel/memorymanagement.h>
#include <sortix/kernel/process.h>
#include <sortix/kernel/segment.h>
#include <sortix/kernel/yielder.h>
namespace Sortix {
@ -105,4 +110,141 @@ bool AddSegment(Process* process, const struct segment* new_segment)
return true;
}
class segment_gaps
{
typedef yielder_iterator<segment_gaps, struct segment> my_iterator;
public:
segment_gaps(finished_yielder) : process(0) { }
segment_gaps(Process* process) :
process(process),
current_segment_index(0),
checked_leading(false),
checked_trailing(false)
{
Memory::GetUserVirtualArea(&userspace_addr, &userspace_size);
}
bool yield(struct segment* result)
{
// process->segment_lock is held at this point.
// Check if we have finished iterating all the segments.
if ( !process )
return false;
// If the process has no segments at all, our job is really easy.
if ( !process->segments_used )
{
result->addr = userspace_addr;
result->size = userspace_size;
result->prot = 0;
process = NULL;
return true;
}
// Find out whether there is a gap before the first segment.
if ( !checked_leading && (checked_leading = true) &&
process->segments[0].addr != userspace_addr )
{
result->addr = userspace_addr;
result->size = process->segments[0].addr - userspace_addr;
result->prot = 0;
return true;
}
// Search through the segments until a gap follows one.
while ( current_segment_index + 1 < process->segments_used )
{
result->addr = process->segments[current_segment_index].addr +
process->segments[current_segment_index].size;
result->size = process->segments[current_segment_index+1].addr -
result->addr;
result->prot = 0;
current_segment_index++;
if ( result->size )
return true;
}
// Find out if there is a gap after the last segment.
if ( !checked_trailing && (checked_trailing = true) &&
process->segments[process->segments_used-1].addr +
process->segments[process->segments_used-1].size !=
userspace_addr + userspace_size )
{
result->addr = process->segments[process->segments_used-1].addr +
process->segments[process->segments_used-1].size;
result->size = userspace_addr + userspace_size - result->addr;
result->prot = 0;
return true;
}
process = NULL;
return false;
}
my_iterator begin() const
{
return my_iterator(segment_gaps(*this));
}
my_iterator end() const
{
return my_iterator(segment_gaps{finished_yielder{}});
}
private:
Process* process;
uintptr_t userspace_addr;
size_t userspace_size;
size_t current_segment_index;
bool checked_leading;
bool checked_trailing;
};
bool PlaceSegment(struct segment* solution, Process* process, void* addr_ptr,
size_t size, int flags)
{
// process->segment_lock is held at this point.
assert(!(flags & MAP_FIXED));
uintptr_t addr = (uintptr_t) addr_ptr;
bool found_any = false;
size_t best_distance = 0;
struct segment best;
for ( struct segment gap : segment_gaps(process) )
{
if ( gap.size < size )
continue;
if ( gap.addr <= addr && addr + size - gap.addr <= gap.size )
{
solution->addr = addr;
solution->size = size;
solution->prot = 0;
return true;
}
struct segment attempt;
size_t distance;
attempt.addr = gap.addr;
attempt.size = size;
attempt.prot = 0;
distance = addr < attempt.addr ? attempt.addr - addr : addr - attempt.addr;
if ( !found_any|| distance < best_distance )
found_any = true, best_distance = distance, best = attempt;
attempt.addr = gap.addr + gap.size - size;
attempt.size = size;
attempt.prot = 0;
distance = addr < attempt.addr ? attempt.addr - addr : addr - attempt.addr;
if ( !found_any|| distance < best_distance )
found_any = true, best_distance = distance, best = attempt;
}
return *solution = best, found_any;
}
} // namespace Sortix