| // SPDX-License-Identifier: MIT |
| /* |
| * Copyright © 2021 Intel Corporation |
| */ |
| |
| #include <sys/ioctl.h> |
| #include <stdlib.h> |
| #include "igt.h" |
| #include "igt_x86.h" |
| #include "intel_allocator.h" |
| #include "intel_bufops.h" |
| #include "igt_map.h" |
| |
| |
| /* Avoid compilation warning */ |
| struct intel_allocator * |
| intel_allocator_simple_create(int fd, uint64_t start, uint64_t end, |
| enum allocator_strategy strategy); |
| |
| struct simple_vma_heap { |
| struct igt_list_head holes; |
| enum allocator_strategy strategy; |
| }; |
| |
| struct simple_vma_hole { |
| struct igt_list_head link; |
| uint64_t offset; |
| uint64_t size; |
| }; |
| |
| struct intel_allocator_simple { |
| struct igt_map *objects; |
| struct igt_map *reserved; |
| struct simple_vma_heap heap; |
| |
| uint64_t start; |
| uint64_t end; |
| |
| /* statistics */ |
| uint64_t total_size; |
| uint64_t allocated_size; |
| uint64_t allocated_objects; |
| uint64_t reserved_size; |
| uint64_t reserved_areas; |
| }; |
| |
| struct intel_allocator_record { |
| uint32_t handle; |
| uint64_t offset; |
| uint64_t size; |
| uint8_t pat_index; |
| }; |
| |
| #define simple_vma_foreach_hole(_hole, _heap) \ |
| igt_list_for_each_entry(_hole, &(_heap)->holes, link) |
| |
| #define simple_vma_foreach_hole_safe(_hole, _heap, _tmp) \ |
| igt_list_for_each_entry_safe(_hole, _tmp, &(_heap)->holes, link) |
| |
| #define simple_vma_foreach_hole_safe_rev(_hole, _heap, _tmp) \ |
| igt_list_for_each_entry_safe_reverse(_hole, _tmp, &(_heap)->holes, link) |
| |
| static void map_entry_free_func(struct igt_map_entry *entry) |
| { |
| free(entry->data); |
| } |
| |
| #define GEN8_GTT_ADDRESS_WIDTH 48 |
| #define DECANONICAL(offset) (offset & ((1ull << GEN8_GTT_ADDRESS_WIDTH) - 1)) |
| |
| static void simple_vma_heap_validate(struct simple_vma_heap *heap) |
| { |
| uint64_t prev_offset = 0; |
| struct simple_vma_hole *hole; |
| |
| simple_vma_foreach_hole(hole, heap) { |
| igt_assert(hole->size > 0); |
| |
| if (&hole->link == heap->holes.next) { |
| /* |
| * This must be the top-most hole. Assert that, |
| * if it overflows, it overflows to 0, i.e. 2^64. |
| */ |
| igt_assert(hole->size + hole->offset == 0 || |
| hole->size + hole->offset > hole->offset); |
| } else { |
| /* |
| * This is not the top-most hole so it must not overflow and, |
| * in fact, must be strictly lower than the top-most hole. If |
| * hole->size + hole->offset == prev_offset, then we failed to |
| * join holes during a simple_vma_heap_free. |
| */ |
| igt_assert(hole->size + hole->offset > hole->offset && |
| hole->size + hole->offset < prev_offset); |
| } |
| prev_offset = hole->offset; |
| } |
| } |
| |
| |
| static void simple_vma_heap_free(struct simple_vma_heap *heap, |
| uint64_t offset, uint64_t size) |
| { |
| struct simple_vma_hole *high_hole = NULL, *low_hole = NULL, *hole; |
| bool high_adjacent, low_adjacent; |
| |
| /* Freeing something with a size of 0 is not valid. */ |
| igt_assert(size > 0); |
| |
| /* |
| * It's possible for offset + size to wrap around if we touch the top of |
| * the 64-bit address space, but we cannot go any higher than 2^64. |
| */ |
| igt_assert(offset + size == 0 || offset + size > offset); |
| |
| simple_vma_heap_validate(heap); |
| |
| /* Find immediately higher and lower holes if they exist. */ |
| simple_vma_foreach_hole(hole, heap) { |
| if (hole->offset <= offset) { |
| low_hole = hole; |
| break; |
| } |
| high_hole = hole; |
| } |
| |
| if (high_hole) |
| igt_assert(offset + size <= high_hole->offset); |
| high_adjacent = high_hole && offset + size == high_hole->offset; |
| |
| if (low_hole) { |
| igt_assert(low_hole->offset + low_hole->size > low_hole->offset); |
| igt_assert(low_hole->offset + low_hole->size <= offset); |
| } |
| low_adjacent = low_hole && low_hole->offset + low_hole->size == offset; |
| |
| if (low_adjacent && high_adjacent) { |
| /* Merge the two holes */ |
| low_hole->size += size + high_hole->size; |
| igt_list_del(&high_hole->link); |
| free(high_hole); |
| } else if (low_adjacent) { |
| /* Merge into the low hole */ |
| low_hole->size += size; |
| } else if (high_adjacent) { |
| /* Merge into the high hole */ |
| high_hole->offset = offset; |
| high_hole->size += size; |
| } else { |
| /* Neither hole is adjacent; make a new one */ |
| hole = calloc(1, sizeof(*hole)); |
| igt_assert(hole); |
| |
| hole->offset = offset; |
| hole->size = size; |
| /* |
| * Add it after the high hole so we maintain high-to-low |
| * ordering |
| */ |
| if (high_hole) |
| igt_list_add(&hole->link, &high_hole->link); |
| else |
| igt_list_add(&hole->link, &heap->holes); |
| } |
| |
| simple_vma_heap_validate(heap); |
| } |
| |
| static void simple_vma_heap_init(struct simple_vma_heap *heap, |
| uint64_t start, uint64_t size, |
| enum allocator_strategy strategy) |
| { |
| IGT_INIT_LIST_HEAD(&heap->holes); |
| simple_vma_heap_free(heap, start, size); |
| |
| /* Use LOW_TO_HIGH or HIGH_TO_LOW strategy only */ |
| if (strategy == ALLOC_STRATEGY_LOW_TO_HIGH) |
| heap->strategy = strategy; |
| else |
| heap->strategy = ALLOC_STRATEGY_HIGH_TO_LOW; |
| } |
| |
| static void simple_vma_heap_finish(struct simple_vma_heap *heap) |
| { |
| struct simple_vma_hole *hole, *tmp; |
| |
| simple_vma_foreach_hole_safe(hole, heap, tmp) |
| free(hole); |
| } |
| |
| static void simple_vma_hole_alloc(struct simple_vma_hole *hole, |
| uint64_t offset, uint64_t size) |
| { |
| struct simple_vma_hole *high_hole; |
| uint64_t waste; |
| |
| igt_assert(hole->offset <= offset); |
| igt_assert(hole->size >= offset - hole->offset + size); |
| |
| if (offset == hole->offset && size == hole->size) { |
| /* Just get rid of the hole. */ |
| igt_list_del(&hole->link); |
| free(hole); |
| return; |
| } |
| |
| igt_assert(offset - hole->offset <= hole->size - size); |
| waste = (hole->size - size) - (offset - hole->offset); |
| if (waste == 0) { |
| /* We allocated at the top-> Shrink the hole down. */ |
| hole->size -= size; |
| return; |
| } |
| |
| if (offset == hole->offset) { |
| /* We allocated at the bottom. Shrink the hole up-> */ |
| hole->offset += size; |
| hole->size -= size; |
| return; |
| } |
| |
| /* |
| * We allocated in the middle. We need to split the old hole into two |
| * holes, one high and one low. |
| */ |
| high_hole = calloc(1, sizeof(*hole)); |
| igt_assert(high_hole); |
| |
| high_hole->offset = offset + size; |
| high_hole->size = waste; |
| |
| /* |
| * Adjust the hole to be the amount of space left at he bottom of the |
| * original hole. |
| */ |
| hole->size = offset - hole->offset; |
| |
| /* |
| * Place the new hole before the old hole so that the list is in order |
| * from high to low. |
| */ |
| igt_list_add_tail(&high_hole->link, &hole->link); |
| } |
| |
| static bool simple_vma_heap_alloc(struct simple_vma_heap *heap, |
| uint64_t *offset, uint64_t size, |
| uint64_t alignment, |
| enum allocator_strategy strategy) |
| { |
| struct simple_vma_hole *hole, *tmp; |
| uint64_t misalign; |
| |
| /* The caller is expected to reject zero-size allocations */ |
| igt_assert(size > 0); |
| igt_assert(alignment > 0); |
| |
| simple_vma_heap_validate(heap); |
| |
| /* Ensure we support only NONE/LOW_TO_HIGH/HIGH_TO_LOW strategies */ |
| igt_assert(strategy == ALLOC_STRATEGY_NONE || |
| strategy == ALLOC_STRATEGY_LOW_TO_HIGH || |
| strategy == ALLOC_STRATEGY_HIGH_TO_LOW); |
| |
| /* Use default strategy chosen on open */ |
| if (strategy == ALLOC_STRATEGY_NONE) |
| strategy = heap->strategy; |
| |
| if (strategy == ALLOC_STRATEGY_HIGH_TO_LOW) { |
| simple_vma_foreach_hole_safe(hole, heap, tmp) { |
| if (size > hole->size) |
| continue; |
| /* |
| * Compute the offset as the highest address where a chunk of the |
| * given size can be without going over the top of the hole. |
| * |
| * This calculation is known to not overflow because we know that |
| * hole->size + hole->offset can only overflow to 0 and size > 0. |
| */ |
| *offset = (hole->size - size) + hole->offset; |
| |
| /* |
| * Align the offset. We align down and not up because we are |
| * |
| * allocating from the top of the hole and not the bottom. |
| */ |
| *offset = (*offset / alignment) * alignment; |
| |
| if (*offset < hole->offset) |
| continue; |
| |
| simple_vma_hole_alloc(hole, *offset, size); |
| simple_vma_heap_validate(heap); |
| return true; |
| } |
| } else { |
| simple_vma_foreach_hole_safe_rev(hole, heap, tmp) { |
| if (size > hole->size) |
| continue; |
| |
| *offset = hole->offset; |
| |
| /* Align the offset */ |
| misalign = *offset % alignment; |
| if (misalign) { |
| uint64_t pad = alignment - misalign; |
| |
| if (pad > hole->size - size) |
| continue; |
| |
| *offset += pad; |
| } |
| |
| simple_vma_hole_alloc(hole, *offset, size); |
| simple_vma_heap_validate(heap); |
| return true; |
| } |
| } |
| |
| /* Failed to allocate */ |
| return false; |
| } |
| |
| static void intel_allocator_simple_get_address_range(struct intel_allocator *ial, |
| uint64_t *startp, |
| uint64_t *endp) |
| { |
| struct intel_allocator_simple *ials = ial->priv; |
| |
| if (startp) |
| *startp = ials->start; |
| |
| if (endp) |
| *endp = ials->end; |
| } |
| |
| static bool simple_vma_heap_alloc_addr(struct intel_allocator_simple *ials, |
| uint64_t offset, uint64_t size) |
| { |
| struct simple_vma_heap *heap = &ials->heap; |
| struct simple_vma_hole *hole, *tmp; |
| |
| /* Allocating something with a size of 0 is not valid. */ |
| igt_assert(size > 0); |
| |
| /* |
| * It's possible for offset + size to wrap around if we touch the top of |
| * the 64-bit address space, but we cannot go any higher than 2^64. |
| */ |
| igt_assert(offset + size == 0 || offset + size > offset); |
| |
| /* Find the hole if one exists. */ |
| simple_vma_foreach_hole_safe(hole, heap, tmp) { |
| if (hole->offset > offset) |
| continue; |
| |
| /* |
| * Holes are ordered high-to-low so the first hole we find with |
| * hole->offset <= is our hole. If it's not big enough to contain the |
| * requested range, then the allocation fails. |
| */ |
| igt_assert(hole->offset <= offset); |
| if (hole->size < offset - hole->offset + size) |
| return false; |
| |
| simple_vma_hole_alloc(hole, offset, size); |
| return true; |
| } |
| |
| /* We didn't find a suitable hole */ |
| return false; |
| } |
| |
| static uint64_t intel_allocator_simple_alloc(struct intel_allocator *ial, |
| uint32_t handle, uint64_t size, |
| uint64_t alignment, uint8_t pat_index, |
| enum allocator_strategy strategy) |
| { |
| struct intel_allocator_record *rec; |
| struct intel_allocator_simple *ials; |
| uint64_t offset; |
| |
| igt_assert(ial); |
| ials = (struct intel_allocator_simple *) ial->priv; |
| igt_assert(ials); |
| igt_assert(handle); |
| |
| rec = igt_map_search(ials->objects, &handle); |
| if (rec) { |
| offset = rec->offset; |
| igt_assert(rec->size == size); |
| igt_assert(rec->pat_index == pat_index); |
| } else { |
| if (!simple_vma_heap_alloc(&ials->heap, &offset, |
| size, alignment, strategy)) |
| return ALLOC_INVALID_ADDRESS; |
| |
| rec = malloc(sizeof(*rec)); |
| rec->handle = handle; |
| rec->offset = offset; |
| rec->size = size; |
| rec->pat_index = pat_index; |
| |
| igt_map_insert(ials->objects, &rec->handle, rec); |
| ials->allocated_objects++; |
| ials->allocated_size += size; |
| } |
| |
| return offset; |
| } |
| |
| static bool intel_allocator_simple_free(struct intel_allocator *ial, uint32_t handle) |
| { |
| struct intel_allocator_record *rec = NULL; |
| struct intel_allocator_simple *ials; |
| struct igt_map_entry *entry; |
| |
| igt_assert(ial); |
| ials = (struct intel_allocator_simple *) ial->priv; |
| igt_assert(ials); |
| |
| entry = igt_map_search_entry(ials->objects, &handle); |
| if (entry) { |
| igt_map_remove_entry(ials->objects, entry); |
| if (entry->data) { |
| rec = (struct intel_allocator_record *) entry->data; |
| simple_vma_heap_free(&ials->heap, rec->offset, rec->size); |
| ials->allocated_objects--; |
| ials->allocated_size -= rec->size; |
| free(rec); |
| |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| static inline bool __same(const struct intel_allocator_record *rec, |
| uint32_t handle, uint64_t size, uint64_t offset) |
| { |
| return rec->handle == handle && rec->size == size && |
| DECANONICAL(rec->offset) == DECANONICAL(offset); |
| } |
| |
| static bool intel_allocator_simple_is_allocated(struct intel_allocator *ial, |
| uint32_t handle, uint64_t size, |
| uint64_t offset) |
| { |
| struct intel_allocator_record *rec; |
| struct intel_allocator_simple *ials; |
| bool same = false; |
| |
| igt_assert(ial); |
| ials = (struct intel_allocator_simple *) ial->priv; |
| igt_assert(ials); |
| igt_assert(handle); |
| |
| rec = igt_map_search(ials->objects, &handle); |
| if (rec && __same(rec, handle, size, offset)) |
| same = true; |
| |
| return same; |
| } |
| |
| static uint64_t get_size(uint64_t start, uint64_t end) |
| { |
| end = end ? end : 1ull << GEN8_GTT_ADDRESS_WIDTH; |
| |
| return end - start; |
| } |
| |
| static bool intel_allocator_simple_reserve(struct intel_allocator *ial, |
| uint32_t handle, |
| uint64_t start, uint64_t end) |
| { |
| uint64_t size; |
| struct intel_allocator_record *rec = NULL; |
| struct intel_allocator_simple *ials; |
| |
| igt_assert(ial); |
| ials = (struct intel_allocator_simple *) ial->priv; |
| igt_assert(ials); |
| |
| /* don't allow end equal to 0 before decanonical */ |
| igt_assert(end); |
| |
| /* clear [63:48] bits to get rid of canonical form */ |
| start = DECANONICAL(start); |
| end = DECANONICAL(end); |
| igt_assert(end > start || end == 0); |
| size = get_size(start, end); |
| igt_assert(start + size <= ials->end); |
| igt_assert(start >= ials->start); |
| |
| if (simple_vma_heap_alloc_addr(ials, start, size)) { |
| rec = malloc(sizeof(*rec)); |
| rec->handle = handle; |
| rec->offset = start; |
| rec->size = size; |
| |
| igt_map_insert(ials->reserved, &rec->offset, rec); |
| |
| ials->reserved_areas++; |
| ials->reserved_size += rec->size; |
| return true; |
| } |
| |
| igt_debug("Failed to reserve %llx + %llx\n", (long long)start, (long long)size); |
| return false; |
| } |
| |
| static bool intel_allocator_simple_unreserve(struct intel_allocator *ial, |
| uint32_t handle, |
| uint64_t start, uint64_t end) |
| { |
| uint64_t size; |
| struct intel_allocator_record *rec = NULL; |
| struct intel_allocator_simple *ials; |
| struct igt_map_entry *entry; |
| |
| igt_assert(ial); |
| ials = (struct intel_allocator_simple *) ial->priv; |
| igt_assert(ials); |
| |
| /* don't allow end equal to 0 before decanonical */ |
| igt_assert(end); |
| |
| /* clear [63:48] bits to get rid of canonical form */ |
| start = DECANONICAL(start); |
| end = DECANONICAL(end); |
| igt_assert(end > start || end == 0); |
| size = get_size(start, end); |
| |
| entry = igt_map_search_entry(ials->reserved, &start); |
| |
| if (!entry || !entry->data) { |
| igt_debug("Only reserved blocks can be unreserved\n"); |
| return false; |
| } |
| rec = entry->data; |
| |
| if (rec->size != size) { |
| igt_debug("Only the whole block unreservation allowed\n"); |
| return false; |
| } |
| |
| if (rec->handle != handle) { |
| igt_debug("Handle %u doesn't match reservation handle: %u\n", |
| rec->handle, handle); |
| return false; |
| } |
| |
| igt_map_remove_entry(ials->reserved, entry); |
| ials->reserved_areas--; |
| ials->reserved_size -= rec->size; |
| free(rec); |
| simple_vma_heap_free(&ials->heap, start, size); |
| |
| return true; |
| } |
| |
| static bool intel_allocator_simple_is_reserved(struct intel_allocator *ial, |
| uint64_t start, uint64_t end) |
| { |
| uint64_t size; |
| struct intel_allocator_record *rec = NULL; |
| struct intel_allocator_simple *ials; |
| |
| igt_assert(ial); |
| ials = (struct intel_allocator_simple *) ial->priv; |
| igt_assert(ials); |
| |
| /* don't allow end equal to 0 before decanonical */ |
| igt_assert(end); |
| |
| /* clear [63:48] bits to get rid of canonical form */ |
| start = DECANONICAL(start); |
| end = DECANONICAL(end); |
| igt_assert(end > start || end == 0); |
| size = get_size(start, end); |
| |
| rec = igt_map_search(ials->reserved, &start); |
| |
| if (!rec) |
| return false; |
| |
| if (rec->offset == start && rec->size == size) |
| return true; |
| |
| return false; |
| } |
| |
| static void intel_allocator_simple_destroy(struct intel_allocator *ial) |
| { |
| struct intel_allocator_simple *ials; |
| |
| igt_assert(ial); |
| ials = (struct intel_allocator_simple *) ial->priv; |
| simple_vma_heap_finish(&ials->heap); |
| |
| igt_map_destroy(ials->objects, map_entry_free_func); |
| |
| igt_map_destroy(ials->reserved, map_entry_free_func); |
| |
| free(ial->priv); |
| free(ial); |
| } |
| |
| static bool intel_allocator_simple_is_empty(struct intel_allocator *ial) |
| { |
| struct intel_allocator_simple *ials = ial->priv; |
| |
| igt_debug("<ial: %p, fd: %d> objects: %" PRId64 |
| ", reserved_areas: %" PRId64 "\n", |
| ial, ial->fd, |
| ials->allocated_objects, ials->reserved_areas); |
| |
| return !ials->allocated_objects && !ials->reserved_areas; |
| } |
| |
| static void intel_allocator_simple_print(struct intel_allocator *ial, bool full) |
| { |
| struct intel_allocator_simple *ials; |
| struct simple_vma_hole *hole; |
| struct simple_vma_heap *heap; |
| struct igt_map_entry *pos; |
| uint64_t total_free = 0, allocated_size = 0, allocated_objects = 0; |
| uint64_t reserved_size = 0, reserved_areas = 0; |
| |
| igt_assert(ial); |
| ials = (struct intel_allocator_simple *) ial->priv; |
| igt_assert(ials); |
| heap = &ials->heap; |
| |
| igt_info("intel_allocator_simple <ial: %p, fd: %d> on " |
| "[0x%"PRIx64" : 0x%"PRIx64"]:\n", ial, ial->fd, |
| ials->start, ials->end); |
| |
| if (full) { |
| igt_info("holes:\n"); |
| simple_vma_foreach_hole(hole, heap) { |
| igt_info("offset = %"PRIu64" (0x%"PRIx64", " |
| "size = %"PRIu64" (0x%"PRIx64")\n", |
| hole->offset, hole->offset, hole->size, |
| hole->size); |
| total_free += hole->size; |
| } |
| igt_assert(total_free <= ials->total_size); |
| igt_info("total_free: %" PRIx64 |
| ", total_size: %" PRIx64 |
| ", allocated_size: %" PRIx64 |
| ", reserved_size: %" PRIx64 "\n", |
| total_free, ials->total_size, ials->allocated_size, |
| ials->reserved_size); |
| igt_assert(total_free == |
| ials->total_size - ials->allocated_size - ials->reserved_size); |
| |
| igt_info("objects:\n"); |
| igt_map_foreach(ials->objects, pos) { |
| struct intel_allocator_record *rec = pos->data; |
| |
| igt_info("handle = %d, offset = %"PRIu64" " |
| "(0x%"PRIx64", size = %"PRIu64" (0x%"PRIx64")\n", |
| rec->handle, rec->offset, rec->offset, |
| rec->size, rec->size); |
| allocated_objects++; |
| allocated_size += rec->size; |
| } |
| igt_assert(ials->allocated_size == allocated_size); |
| igt_assert(ials->allocated_objects == allocated_objects); |
| |
| igt_info("reserved areas:\n"); |
| igt_map_foreach(ials->reserved, pos) { |
| struct intel_allocator_record *rec = pos->data; |
| |
| igt_info("offset = %"PRIu64" (0x%"PRIx64", " |
| "size = %"PRIu64" (0x%"PRIx64")\n", |
| rec->offset, rec->offset, |
| rec->size, rec->size); |
| reserved_areas++; |
| reserved_size += rec->size; |
| } |
| igt_assert(ials->reserved_areas == reserved_areas); |
| igt_assert(ials->reserved_size == reserved_size); |
| } else { |
| simple_vma_foreach_hole(hole, heap) |
| total_free += hole->size; |
| } |
| |
| igt_info("free space: %"PRIu64"B (0x%"PRIx64") (%.2f%% full)\n" |
| "allocated objects: %"PRIu64", reserved areas: %"PRIu64"\n", |
| total_free, total_free, |
| ((double) (ials->total_size - total_free) / |
| (double) ials->total_size) * 100, |
| ials->allocated_objects, ials->reserved_areas); |
| } |
| |
| struct intel_allocator * |
| intel_allocator_simple_create(int fd, uint64_t start, uint64_t end, |
| enum allocator_strategy strategy) |
| { |
| struct intel_allocator *ial; |
| struct intel_allocator_simple *ials; |
| |
| igt_debug("Using simple allocator\n"); |
| |
| ial = calloc(1, sizeof(*ial)); |
| igt_assert(ial); |
| |
| ial->fd = fd; |
| ial->get_address_range = intel_allocator_simple_get_address_range; |
| ial->alloc = intel_allocator_simple_alloc; |
| ial->free = intel_allocator_simple_free; |
| ial->is_allocated = intel_allocator_simple_is_allocated; |
| ial->reserve = intel_allocator_simple_reserve; |
| ial->unreserve = intel_allocator_simple_unreserve; |
| ial->is_reserved = intel_allocator_simple_is_reserved; |
| ial->destroy = intel_allocator_simple_destroy; |
| ial->is_empty = intel_allocator_simple_is_empty; |
| ial->print = intel_allocator_simple_print; |
| ials = ial->priv = malloc(sizeof(struct intel_allocator_simple)); |
| igt_assert(ials); |
| |
| ials->objects = igt_map_create(igt_map_hash_32, igt_map_equal_32); |
| ials->reserved = igt_map_create(igt_map_hash_64, igt_map_equal_64); |
| igt_assert(ials->objects && ials->reserved); |
| |
| ials->start = start; |
| ials->end = end; |
| ials->total_size = end - start; |
| simple_vma_heap_init(&ials->heap, ials->start, ials->total_size, |
| strategy); |
| |
| ials->allocated_size = 0; |
| ials->allocated_objects = 0; |
| ials->reserved_size = 0; |
| ials->reserved_areas = 0; |
| |
| return ial; |
| } |