Implement shmat and shmdt

This commit is contained in:
Enrico Fraccaroli (Galfurian)
2023-08-16 14:45:04 -04:00
parent dde6e6bc9e
commit 34319442aa
5 changed files with 171 additions and 130 deletions
+14 -7
View File
@@ -236,13 +236,6 @@ vm_area_struct_t *create_vm_area(mm_struct_t *mm,
uint32_t pgflags,
uint32_t gfpflags);
vm_area_struct_t *create_vm_area_with_phy(mm_struct_t *mm,
uint32_t virt_start,
size_t size,
uint32_t pgflags,
uint32_t gfpflags,
uint32_t phy_vm_start);
/// @brief Clone a virtual memory area, using copy on write if specified
/// @param mm the memory descriptor which will contain the new segment.
/// @param area the area to clone
@@ -266,6 +259,20 @@ int destroy_vm_area(mm_struct_t *mm, vm_area_struct_t *area);
/// @return a pointer to the area if we found it, NULL otherwise.
vm_area_struct_t *find_vm_area(mm_struct_t *mm, uint32_t vm_start);
/// @brief Checks if the given virtual memory area range is valid.
/// @param mm the memory descriptor which we use to check the range.
/// @param vm_start the starting address of the area.
/// @param vm_end the ending address of the area.
/// @return 1 if it's valid, 0 if it's occupied, -1 if it's outside the memory.
int is_valid_vm_area(mm_struct_t *mm, uintptr_t vm_start, uintptr_t vm_end);
/// @brief Searches for an empty spot for a new virtual memory area.
/// @param mm the memory descriptor which should contain the new area.
/// @param length the size of the empty spot.
/// @param vm_start where we save the starting address for the new area.
/// @return 0 on success, 1 on failure.
int find_free_vm_area(mm_struct_t *mm, size_t length, uintptr_t *vm_start);
/// @brief Creates the main memory descriptor.
/// @param stack_size The size of the stack in byte.
/// @return The Memory Descriptor created.
+60 -33
View File
@@ -73,6 +73,7 @@ static inline shm_info_t *__shm_info_alloc(key_t key, size_t size, int shmflg)
// Allocate the memory.
uint32_t order = find_nearest_order_greater(0, size);
shm_info->shm_location = _alloc_pages(GFP_KERNEL, order);
pr_crit("page: %p\n", shm_info->shm_location);
// Return the shared memory structure.
return shm_info;
}
@@ -130,6 +131,25 @@ static inline shm_info_t *__list_find_shm_info_by_key(key_t key)
return NULL;
}
/// @brief Searches for the shared memory with the given page.
/// @param page the page we are searching.
/// @return the shared memory with the given page.
static inline shm_info_t *__list_find_shm_info_by_page(page_t *page)
{
shm_info_t *shm_info;
// Iterate through the list of shared memories.
list_for_each_decl(it, &shm_list)
{
// Get the current entry.
shm_info = list_entry(it, shm_info_t, list);
// If shared memories is valid, check the id.
if (shm_info && (shm_info->shm_location == page)) {
return shm_info;
}
}
return NULL;
}
static inline void __list_add_shm_info(shm_info_t *shm_info)
{
assert(shm_info && "Received a NULL pointer.");
@@ -198,34 +218,12 @@ long sys_shmget(key_t key, size_t size, int shmflg)
return shm_info->id;
}
static inline int __find_vm_free_area(mm_struct_t *mm, size_t length, uintptr_t *vm_start)
{
// Get the stack.
vm_area_struct_t *area, *prev_area;
list_for_each_prev_decl(it, &mm->mmap_list)
{
area = list_entry(it, vm_area_struct_t, vm_list);
assert(area && "There is a NULL area in the list.");
// Check the previous segment.
if (area->vm_list.prev != &mm->mmap_list) {
prev_area = list_entry(area->vm_list.prev, vm_area_struct_t, vm_list);
assert(prev_area && "There is a NULL area in the list.");
// Compute the available space.
unsigned available_space = area->vm_start - prev_area->vm_end;
// If the space is enough, return the address.
if (available_space >= length) {
*vm_start = area->vm_start - length;
return 0;
}
}
}
return 1;
}
void *sys_shmat(int shmid, const void *shmaddr, int shmflg)
{
shm_info_t *shm_info = NULL;
task_struct *task = NULL;
uint32_t vm_start, phy_start;
// The id is less than zero.
if (shmid < 0) {
pr_err("The id is less than zero.\n");
@@ -251,26 +249,55 @@ void *sys_shmat(int shmid, const void *shmaddr, int shmflg)
// Get the calling task.
task = scheduler_get_current_process();
assert(task && "Failed to get the current running process.");
uint32_t phy_start = get_physical_address_from_page(shm_info->shm_location);
uint32_t vm_start;
__find_vm_free_area(
task->mm,
shm_info->shmid.shm_segsz,
&vm_start);
// Get the phyisical address from the allocated pages.
phy_start = get_physical_address_from_page(shm_info->shm_location);
// Find the virtual address for the new area.
if (find_free_vm_area(task->mm, shm_info->shmid.shm_segsz, &vm_start)) {
pr_err("We failed to find space for the new virtual memory area.\n");
return (void *)-EACCES;
}
mem_upd_vm_area(
task->mm->pgd,
vm_start,
phy_start,
shm_info->shmid.shm_segsz,
MM_RW | MM_PRESENT | MM_USER | MM_UPDADDR);
return (void *)vm_start;
}
long sys_shmdt(const void *shmaddr)
{
shm_info_t *shm_info = NULL;
task_struct *task = NULL;
uint32_t phy_start;
size_t size;
page_t *page;
// Get the calling task.
task = scheduler_get_current_process();
assert(task && "Failed to get the current running process.");
// Get the page.
page = mem_virtual_to_page(task->mm->pgd, (uint32_t)shmaddr, &size);
// Check if we can find the page.
if (page == NULL) {
pr_err("Cannot retrieve the page from the give address.\n");
return -ENOENT;
}
shm_info = __list_find_shm_info_by_page(page);
// Check if no shared memory exists for the given address.
if (!shm_info) {
pr_err("No shared memory exists for the given address.\n");
return -ENOENT;
}
// Get the phyisical address from the allocated pages.
phy_start = get_physical_address_from_page(shm_info->shm_location);
// Set all virtual pages as not present.
mem_upd_vm_area(
task->mm->pgd,
(uint32_t)shmaddr,
phy_start,
shm_info->shmid.shm_segsz,
MM_GLOBAL);
return 0;
}
+1 -1
View File
@@ -4,7 +4,7 @@
/// See LICENSE.md for details.
// Setup the logging for this file (do this before any other include).
#include "sys/kernel_levels.h" // Include kernel log levels.
#include "sys/kernel_levels.h" // Include kernel log levels.
#define __DEBUG_HEADER__ "[KHEAP ]" ///< Change header.
#define __DEBUG_LEVEL__ LOGLEVEL_INFO ///< Set log level.
#include "io/debug.h" // Include debugging functions.
+71 -72
View File
@@ -75,98 +75,46 @@ void paging_flush_tlb_single(unsigned long addr)
: "memory");
}
/// @brief
/// @param mm
/// @param vm_start
/// @param vm_end
/// @return int
static inline int __valid_vm_area(mm_struct_t *mm, uintptr_t vm_start, uintptr_t vm_end)
{
if (vm_end <= vm_start)
return -1;
// Get the stack.
vm_area_struct_t *area;
list_for_each_prev_decl(it, &mm->mmap_list)
{
area = list_entry(it, vm_area_struct_t, vm_list);
assert(area && "There is a NULL area in the list.");
if ((vm_start >= area->vm_start) && (vm_start <= area->vm_end))
return 0;
if ((vm_end >= area->vm_start) && (vm_end <= area->vm_end))
return 0;
if ((vm_start <= area->vm_start) && (vm_end >= area->vm_end))
return 0;
}
return 1;
}
/// @brief Searches for an empty spot for a new virtual memory area.
/// @param mm the memory descriptor which should contain the new area.
/// @param length the size of the empty spot.
/// @param vm_start where we save the starting address for the new area.
/// @return 0 on success, 1 on failure.
static inline int __find_vm_free_area(mm_struct_t *mm, size_t length, uintptr_t *vm_start)
{
// Get the stack.
vm_area_struct_t *area, *prev_area;
list_for_each_prev_decl(it, &mm->mmap_list)
{
area = list_entry(it, vm_area_struct_t, vm_list);
assert(area && "There is a NULL area in the list.");
// Check the previous segment.
if (area->vm_list.prev != &mm->mmap_list) {
prev_area = list_entry(area->vm_list.prev, vm_area_struct_t, vm_list);
assert(prev_area && "There is a NULL area in the list.");
// Compute the available space.
unsigned available_space = area->vm_start - prev_area->vm_end;
// If the space is enough, return the address.
if (available_space >= length) {
*vm_start = area->vm_start - length;
return 0;
}
}
}
return 1;
}
vm_area_struct_t *create_vm_area(mm_struct_t *mm,
uint32_t vm_start,
size_t size,
uint32_t pgflags,
uint32_t gfpflags)
{
uint32_t vm_end, phy_start, order;
vm_area_struct_t *segment;
// Compute the end of the virtual memory area.
uint32_t vm_end = vm_start + size;
vm_end = vm_start + size;
// Check if the range is already occupied.
if (__valid_vm_area(mm, vm_start, vm_end) <= 0) {
if (is_valid_vm_area(mm, vm_start, vm_end) <= 0) {
pr_crit("The virtual memory area range [%p, %p] is already in use.\n", vm_start, vm_end);
kernel_panic("Wrong virtual memory area range.");
}
// Allocate on kernel space the structure for the segment.
vm_area_struct_t *new_segment = kmem_cache_alloc(vm_area_cache, GFP_KERNEL);
segment = kmem_cache_alloc(vm_area_cache, GFP_KERNEL);
// Find the nearest order for the given memory size.
uint32_t order = find_nearest_order_greater(vm_start, size);
uint32_t phy_vm_start;
order = find_nearest_order_greater(vm_start, size);
if (pgflags & MM_COW) {
pgflags &= ~(MM_PRESENT | MM_UPDADDR);
phy_vm_start = 0;
pgflags = pgflags & ~(MM_PRESENT | MM_UPDADDR);
phy_start = 0;
} else {
pgflags |= MM_UPDADDR;
pgflags = pgflags | MM_UPDADDR;
page_t *page = _alloc_pages(gfpflags, order);
phy_vm_start = get_physical_address_from_page(page);
phy_start = get_physical_address_from_page(page);
}
mem_upd_vm_area(mm->pgd, vm_start, phy_vm_start, size, pgflags);
mem_upd_vm_area(mm->pgd, vm_start, phy_start, size, pgflags);
// Update vm_area_struct info.
new_segment->vm_start = vm_start;
new_segment->vm_end = vm_end;
new_segment->vm_mm = mm;
segment->vm_start = vm_start;
segment->vm_end = vm_end;
segment->vm_mm = mm;
// Update memory descriptor list of vm_area_struct.
list_head_insert_after(&new_segment->vm_list, &mm->mmap_list);
mm->mmap_cache = new_segment;
list_head_insert_after(&segment->vm_list, &mm->mmap_list);
mm->mmap_cache = segment;
// Sort the mmap_list.
list_head_sort(&mm->mmap_list, vm_area_compare);
@@ -176,7 +124,7 @@ vm_area_struct_t *create_vm_area(mm_struct_t *mm,
mm->total_vm += (1U << order);
return new_segment;
return segment;
}
uint32_t clone_vm_area(mm_struct_t *mm, vm_area_struct_t *area, int cow, uint32_t gfpflags)
@@ -276,6 +224,57 @@ inline vm_area_struct_t *find_vm_area(mm_struct_t *mm, uint32_t vm_start)
return NULL;
}
inline int is_valid_vm_area(mm_struct_t *mm, uintptr_t vm_start, uintptr_t vm_end)
{
if (vm_end <= vm_start) {
return -1;
}
// Get the stack.
vm_area_struct_t *area;
list_for_each_prev_decl(it, &mm->mmap_list)
{
area = list_entry(it, vm_area_struct_t, vm_list);
assert(area && "There is a NULL area in the list.");
if ((vm_start > area->vm_start) && (vm_start < area->vm_end)) {
pr_crit("INSIDE(START): %p <= %p <= %p", area->vm_start, vm_start, area->vm_end);
return 0;
}
if ((vm_end > area->vm_start) && (vm_end < area->vm_end)) {
pr_crit("INSIDE(END): %p <= %p <= %p", area->vm_start, vm_end, area->vm_end);
return 0;
}
if ((vm_start < area->vm_start) && (vm_end > area->vm_end)) {
pr_crit("WRAPS: %p <= (%p, %p) <= %p", vm_start, area->vm_start, area->vm_end, vm_end);
return 0;
}
}
return 1;
}
inline int find_free_vm_area(mm_struct_t *mm, size_t length, uintptr_t *vm_start)
{
// Get the stack.
vm_area_struct_t *area, *prev_area;
list_for_each_prev_decl(it, &mm->mmap_list)
{
area = list_entry(it, vm_area_struct_t, vm_list);
assert(area && "There is a NULL area in the list.");
// Check the previous segment.
if (area->vm_list.prev != &mm->mmap_list) {
prev_area = list_entry(area->vm_list.prev, vm_area_struct_t, vm_list);
assert(prev_area && "There is a NULL area in the list.");
// Compute the available space.
unsigned available_space = area->vm_start - prev_area->vm_end;
// If the space is enough, return the address.
if (available_space >= length) {
*vm_start = area->vm_start - length;
return 0;
}
}
}
return 1;
}
static void __init_pagedir(page_directory_t *pdir)
{
*pdir = (page_directory_t){ { 0 } };
@@ -724,11 +723,11 @@ void *sys_mmap(void *addr, size_t length, int prot, int flags, int fd, off_t off
// Get the current task.
task_struct *task = scheduler_get_current_process();
// Check if we were asked for a specific spot.
if (addr && __valid_vm_area(task->mm, (uintptr_t)addr, (uintptr_t)addr + length)) {
if (addr && is_valid_vm_area(task->mm, (uintptr_t)addr, (uintptr_t)addr + length)) {
vm_start = (uintptr_t)addr;
} else {
// Find an empty spot.
if (__find_vm_free_area(task->mm, length, &vm_start)) {
if (find_free_vm_area(task->mm, length, &vm_start)) {
pr_err("We failed to find a suitable spot for a new virtual memory area.\n");
return NULL;
}
+25 -17
View File
@@ -11,7 +11,7 @@
int main(void)
{
int semid, shmid;
int shmid;
pid_t cpid;
int *array;
@@ -21,40 +21,48 @@ int main(void)
perror("shmget");
return EXIT_FAILURE;
}
// Create a semaphore set containing one semaphore.
semid = semget(IPC_PRIVATE, 1, IPC_CREAT | 0600);
if (semid == -1) {
perror("semget");
return EXIT_FAILURE;
}
printf("shmid = %d;\n", shmid);
// Create a child.
cpid = fork();
if (cpid == 0) {
// Child attaches the shared memory.
array = (int *)shmat(shmid, NULL, 0);
if (array == NULL) {
perror("shmat");
return EXIT_FAILURE;
}
printf("C: %p\n", array);
array[0] = 1;
return 0;
} else {
array = (int *)shmat(shmid, NULL, 0);
printf("F: %p\n", array);
array[1] = 2;
}
// Father attaches the shared memory.
array = (int *)shmat(shmid, NULL, 0);
if (array == NULL) {
perror("shmat");
return EXIT_FAILURE;
}
// Wait for the child to finish.
while (wait(NULL) != -1) continue;
printf("F: %p\n", array);
array[1] = 2;
printf("array[%d] : %d\n", 0, array[0]);
printf("array[%d] : %d\n", 1, array[1]);
// Detatch the shared memory.
if (shmdt(array) < 0) {
perror("shmdt");
return EXIT_FAILURE;
}
// Remove the shared memory.
if (shmctl(shmid, IPC_RMID, NULL) == -1) {
perror("shmctl");
return EXIT_FAILURE;
}
// Remove the semaphore set.
if (semctl(semid, 0, IPC_RMID, NULL) == -1) {
perror("semctl");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}