Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • kernel/generic/src/mm/as.c

    rca4c5596 rca21f1e2  
    6363#include <synch/mutex.h>
    6464#include <adt/list.h>
     65#include <adt/btree.h>
    6566#include <proc/task.h>
    6667#include <proc/thread.h>
     
    8889as_operations_t *as_operations = NULL;
    8990
    90 /** Cache for as_t objects */
     91/** Slab for as_t objects.
     92 *
     93 */
    9194static slab_cache_t *as_cache;
    92 
    93 /** Cache for as_page_mapping_t objects */
    94 static slab_cache_t *as_page_mapping_cache;
    95 
    96 /** Cache for used_space_ival_t objects */
    97 static slab_cache_t *used_space_ival_cache;
    9895
    9996/** ASID subsystem lock.
     
    119116static int as_areas_cmp(void *, void *);
    120117
    121 static void used_space_initialize(used_space_t *);
    122 static void used_space_finalize(used_space_t *);
    123 static void *used_space_getkey(odlink_t *);
    124 static int used_space_cmp(void *, void *);
    125 static used_space_ival_t *used_space_last(used_space_t *);
    126 static void used_space_remove_ival(used_space_ival_t *);
    127 static void used_space_shorten_ival(used_space_ival_t *, size_t);
    128 
    129118NO_TRACE static errno_t as_constructor(void *obj, unsigned int flags)
    130119{
     
    149138        as_cache = slab_cache_create("as_t", sizeof(as_t), 0,
    150139            as_constructor, as_destructor, SLAB_CACHE_MAGDEFERRED);
    151 
    152         as_page_mapping_cache = slab_cache_create("as_page_mapping_t",
    153             sizeof(as_page_mapping_t), 0, NULL, NULL, SLAB_CACHE_MAGDEFERRED);
    154 
    155         used_space_ival_cache = slab_cache_create("used_space_ival_t",
    156             sizeof(used_space_ival_t), 0, NULL, NULL, SLAB_CACHE_MAGDEFERRED);
    157140
    158141        AS_KERNEL = as_create(FLAG_AS_KERNEL);
     
    541524}
    542525
    543 /** Get key function for pagemap ordered dictionary.
    544  *
    545  * The key is the virtual address of the page (as_page_mapping_t.vaddr)
    546  *
    547  * @param odlink Link to as_pagemap_t.map ordered dictionary
    548  * @return Pointer to virtual address cast as @c void *
    549  */
    550 static void *as_pagemap_getkey(odlink_t *odlink)
    551 {
    552         as_page_mapping_t *mapping;
    553 
    554         mapping = odict_get_instance(odlink, as_page_mapping_t, lpagemap);
    555         return (void *) &mapping->vaddr;
    556 }
    557 
    558 /** Comparison function for pagemap ordered dictionary.
    559  *
    560  * @param a Pointer to virtual address cast as @c void *
    561  * @param b Pointer to virtual address cast as @c void *
    562  * @return <0, =0, >0 if virtual address a is less than, equal to, or
    563  *         greater than b, respectively.
    564  */
    565 static int as_pagemap_cmp(void *a, void *b)
    566 {
    567         uintptr_t va = *(uintptr_t *)a;
    568         uintptr_t vb = *(uintptr_t *)b;
    569 
    570         if (va < vb)
    571                 return -1;
    572         else if (va == vb)
    573                 return 0;
    574         else
    575                 return +1;
    576 }
    577 
    578 /** Initialize pagemap.
    579  *
    580  * @param pagemap Pagemap
    581  */
    582 NO_TRACE void as_pagemap_initialize(as_pagemap_t *pagemap)
    583 {
    584         odict_initialize(&pagemap->map, as_pagemap_getkey, as_pagemap_cmp);
    585 }
    586 
    587 /** Finalize pagemap.
    588  *
    589  * Destroy any entries in the pagemap.
    590  *
    591  * @param pagemap Pagemap
    592  */
    593 NO_TRACE void as_pagemap_finalize(as_pagemap_t *pagemap)
    594 {
    595         as_page_mapping_t *mapping = as_pagemap_first(pagemap);
    596         while (mapping != NULL) {
    597                 as_pagemap_remove(mapping);
    598                 mapping = as_pagemap_first(pagemap);
    599         }
    600         odict_finalize(&pagemap->map);
    601 }
    602 
    603 /** Get first page mapping.
    604  *
    605  * @param pagemap Pagemap
    606  * @return First mapping or @c NULL if there is none
    607  */
    608 NO_TRACE as_page_mapping_t *as_pagemap_first(as_pagemap_t *pagemap)
    609 {
    610         odlink_t *odlink;
    611 
    612         odlink = odict_first(&pagemap->map);
    613         if (odlink == NULL)
    614                 return NULL;
    615 
    616         return odict_get_instance(odlink, as_page_mapping_t, lpagemap);
    617 }
    618 
    619 /** Get next page mapping.
    620  *
    621  * @param cur Current mapping
    622  * @return Next mapping or @c NULL if @a cur is the last one
    623  */
    624 NO_TRACE as_page_mapping_t *as_pagemap_next(as_page_mapping_t *cur)
    625 {
    626         odlink_t *odlink;
    627 
    628         odlink = odict_next(&cur->lpagemap, &cur->pagemap->map);
    629         if (odlink == NULL)
    630                 return NULL;
    631 
    632         return odict_get_instance(odlink, as_page_mapping_t, lpagemap);
    633 }
    634 
    635 /** Find frame by virtual address.
    636  *
    637  * @param pagemap Pagemap
    638  * @param vaddr Virtual address of page
    639  * @param rframe Place to store physical frame address
    640  * @return EOK on succcess or ENOENT if no mapping found
    641  */
    642 NO_TRACE errno_t as_pagemap_find(as_pagemap_t *pagemap, uintptr_t vaddr,
    643     uintptr_t *rframe)
    644 {
    645         odlink_t *odlink;
    646         as_page_mapping_t *mapping;
    647 
    648         odlink = odict_find_eq(&pagemap->map, &vaddr, NULL);
    649         if (odlink == NULL)
    650                 return ENOENT;
    651 
    652         mapping = odict_get_instance(odlink, as_page_mapping_t, lpagemap);
    653         *rframe = mapping->frame;
    654         return EOK;
    655 }
    656 
    657 /** Insert new page mapping.
    658  *
    659  * This function can block to allocate kernel memory.
    660  *
    661  * @param pagemap Pagemap
    662  * @param vaddr Virtual page address
    663  * @param frame Physical frame address
    664  */
    665 NO_TRACE void as_pagemap_insert(as_pagemap_t *pagemap, uintptr_t vaddr,
    666     uintptr_t frame)
    667 {
    668         as_page_mapping_t *mapping;
    669 
    670         mapping = slab_alloc(as_page_mapping_cache, 0);
    671         mapping->pagemap = pagemap;
    672         odlink_initialize(&mapping->lpagemap);
    673         mapping->vaddr = vaddr;
    674         mapping->frame = frame;
    675         odict_insert(&mapping->lpagemap, &pagemap->map, NULL);
    676 }
    677 
    678 /** Remove page mapping.
    679  *
    680  * @param mapping Mapping
    681  */
    682 NO_TRACE void as_pagemap_remove(as_page_mapping_t *mapping)
    683 {
    684         odict_remove(&mapping->lpagemap);
    685         slab_free(as_page_mapping_cache, mapping);
    686 }
    687 
    688526/** Remove reference to address space area share info.
    689527 *
     
    703541                dealloc = true;
    704542
    705                 as_page_mapping_t *mapping = as_pagemap_first(&sh_info->pagemap);
    706                 while (mapping != NULL) {
    707                         frame_free(mapping->frame, 1);
    708                         mapping = as_pagemap_next(mapping);
     543                /*
     544                 * Now walk carefully the pagemap B+tree and free/remove
     545                 * reference from all frames found there.
     546                 */
     547                list_foreach(sh_info->pagemap.leaf_list, leaf_link,
     548                    btree_node_t, node) {
     549                        btree_key_t i;
     550
     551                        for (i = 0; i < node->keys; i++)
     552                                frame_free((uintptr_t) node->value[i], 1);
    709553                }
    710554
     
    717561                            sh_info->backend_shared_data);
    718562                }
    719                 as_pagemap_finalize(&sh_info->pagemap);
     563                btree_destroy(&sh_info->pagemap);
    720564                free(sh_info);
    721565        }
     
    792636        area->attributes = attrs;
    793637        area->pages = pages;
     638        area->resident = 0;
    794639        area->base = *base;
    795640        area->backend = backend;
     
    820665                si->backend_shared_data = NULL;
    821666                si->backend = backend;
    822                 as_pagemap_initialize(&si->pagemap);
     667                btree_create(&si->pagemap);
    823668
    824669                area->sh_info = si;
     
    844689        }
    845690
    846         used_space_initialize(&area->used_space);
     691        btree_create(&area->used_space);
    847692        odict_insert(&area->las_areas, &as->as_areas, NULL);
    848693
     
    952797
    953798                /*
    954                  * Start TLB shootdown sequence.
    955                  */
    956 
    957                 ipl_t ipl = tlb_shootdown_start(TLB_INVL_PAGES,
    958                     as->asid, area->base + P2SZ(pages),
    959                     area->pages - pages);
    960 
    961                 /*
    962799                 * Remove frames belonging to used space starting from
    963800                 * the highest addresses downwards until an overlap with
    964                  * the resized address space area is found.
     801                 * the resized address space area is found. Note that this
     802                 * is also the right way to remove part of the used_space
     803                 * B+tree leaf list.
    965804                 */
    966805                bool cond = true;
    967806                while (cond) {
    968                         used_space_ival_t *ival =
    969                             used_space_last(&area->used_space);
    970                         assert(ival != NULL);
    971 
    972                         uintptr_t ptr = ival->page;
    973                         size_t pcount = ival->count;
    974                         size_t i = 0;
    975 
    976                         if (overlaps(ptr, P2SZ(pcount), area->base,
    977                             P2SZ(pages))) {
    978 
    979                                 if (ptr + P2SZ(pcount) <= start_free) {
     807                        assert(!list_empty(&area->used_space.leaf_list));
     808
     809                        btree_node_t *node =
     810                            list_get_instance(list_last(&area->used_space.leaf_list),
     811                            btree_node_t, leaf_link);
     812
     813                        if ((cond = (node->keys != 0))) {
     814                                uintptr_t ptr = node->key[node->keys - 1];
     815                                size_t node_size =
     816                                    (size_t) node->value[node->keys - 1];
     817                                size_t i = 0;
     818
     819                                if (overlaps(ptr, P2SZ(node_size), area->base,
     820                                    P2SZ(pages))) {
     821
     822                                        if (ptr + P2SZ(node_size) <= start_free) {
     823                                                /*
     824                                                 * The whole interval fits
     825                                                 * completely in the resized
     826                                                 * address space area.
     827                                                 */
     828                                                break;
     829                                        }
     830
    980831                                        /*
    981                                          * The whole interval fits completely
    982                                          * in the resized address space area.
     832                                         * Part of the interval corresponding
     833                                         * to b and c overlaps with the resized
     834                                         * address space area.
    983835                                         */
    984                                         break;
     836
     837                                        /* We are almost done */
     838                                        cond = false;
     839                                        i = (start_free - ptr) >> PAGE_WIDTH;
     840                                        if (!used_space_remove(area, start_free,
     841                                            node_size - i))
     842                                                panic("Cannot remove used space.");
     843                                } else {
     844                                        /*
     845                                         * The interval of used space can be
     846                                         * completely removed.
     847                                         */
     848                                        if (!used_space_remove(area, ptr, node_size))
     849                                                panic("Cannot remove used space.");
    985850                                }
    986851
    987852                                /*
    988                                  * Part of the interval corresponding to b and
    989                                  * c overlaps with the resized address space
    990                                  * area.
     853                                 * Start TLB shootdown sequence.
     854                                 *
     855                                 * The sequence is rather short and can be
     856                                 * repeated multiple times. The reason is that
     857                                 * we don't want to have used_space_remove()
     858                                 * inside the sequence as it may use a blocking
     859                                 * memory allocation for its B+tree. Blocking
     860                                 * while holding the tlblock spinlock is
     861                                 * forbidden and would hit a kernel assertion.
    991862                                 */
    992863
    993                                 /* We are almost done */
    994                                 cond = false;
    995                                 i = (start_free - ptr) >> PAGE_WIDTH;
    996 
    997                                 /* Shorten the interval to @c i pages */
    998                                 used_space_shorten_ival(ival, i);
    999                         } else {
     864                                ipl_t ipl = tlb_shootdown_start(TLB_INVL_PAGES,
     865                                    as->asid, area->base + P2SZ(pages),
     866                                    area->pages - pages);
     867
     868                                for (; i < node_size; i++) {
     869                                        pte_t pte;
     870                                        bool found = page_mapping_find(as,
     871                                            ptr + P2SZ(i), false, &pte);
     872
     873                                        (void) found;
     874                                        assert(found);
     875                                        assert(PTE_VALID(&pte));
     876                                        assert(PTE_PRESENT(&pte));
     877
     878                                        if ((area->backend) &&
     879                                            (area->backend->frame_free)) {
     880                                                area->backend->frame_free(area,
     881                                                    ptr + P2SZ(i),
     882                                                    PTE_GET_FRAME(&pte));
     883                                        }
     884
     885                                        page_mapping_remove(as, ptr + P2SZ(i));
     886                                }
     887
    1000888                                /*
    1001                                  * The interval of used space can be completely
    1002                                  * removed.
     889                                 * Finish TLB shootdown sequence.
    1003890                                 */
    1004                                 used_space_remove_ival(ival);
     891
     892                                tlb_invalidate_pages(as->asid,
     893                                    area->base + P2SZ(pages),
     894                                    area->pages - pages);
     895
     896                                /*
     897                                 * Invalidate software translation caches
     898                                 * (e.g. TSB on sparc64, PHT on ppc32).
     899                                 */
     900                                as_invalidate_translation_cache(as,
     901                                    area->base + P2SZ(pages),
     902                                    area->pages - pages);
     903                                tlb_shootdown_finalize(ipl);
    1005904                        }
    1006 
    1007                         for (; i < pcount; i++) {
    1008                                 pte_t pte;
    1009                                 bool found = page_mapping_find(as,
    1010                                     ptr + P2SZ(i), false, &pte);
    1011 
    1012                                 (void) found;
    1013                                 assert(found);
    1014                                 assert(PTE_VALID(&pte));
    1015                                 assert(PTE_PRESENT(&pte));
    1016 
    1017                                 if ((area->backend) &&
    1018                                     (area->backend->frame_free)) {
    1019                                         area->backend->frame_free(area,
    1020                                             ptr + P2SZ(i),
    1021                                             PTE_GET_FRAME(&pte));
    1022                                 }
    1023 
    1024                                 page_mapping_remove(as, ptr + P2SZ(i));
    1025                         }
    1026 
    1027                 }
    1028 
    1029                 /*
    1030                  * Finish TLB shootdown sequence.
    1031                  */
    1032 
    1033                 tlb_invalidate_pages(as->asid,
    1034                     area->base + P2SZ(pages),
    1035                     area->pages - pages);
    1036 
    1037                 /*
    1038                  * Invalidate software translation caches
    1039                  * (e.g. TSB on sparc64, PHT on ppc32).
    1040                  */
    1041                 as_invalidate_translation_cache(as,
    1042                     area->base + P2SZ(pages),
    1043                     area->pages - pages);
    1044                 tlb_shootdown_finalize(ipl);
    1045 
     905                }
    1046906                page_table_unlock(as, false);
    1047907        } else {
     
    1102962
    1103963        page_table_lock(as, false);
     964
    1104965        /*
    1105966         * Start TLB shootdown sequence.
     
    1109970
    1110971        /*
    1111          * Visit only the pages mapped by used_space.
    1112          */
    1113         used_space_ival_t *ival = used_space_first(&area->used_space);
    1114         while (ival != NULL) {
    1115                 uintptr_t ptr = ival->page;
    1116 
    1117                 for (size_t size = 0; size < ival->count; size++) {
    1118                         pte_t pte;
    1119                         bool found = page_mapping_find(as,
    1120                             ptr + P2SZ(size), false, &pte);
    1121 
    1122                         (void) found;
    1123                         assert(found);
    1124                         assert(PTE_VALID(&pte));
    1125                         assert(PTE_PRESENT(&pte));
    1126 
    1127                         if ((area->backend) &&
    1128                             (area->backend->frame_free)) {
    1129                                 area->backend->frame_free(area,
    1130                                     ptr + P2SZ(size),
    1131                                     PTE_GET_FRAME(&pte));
     972         * Visit only the pages mapped by used_space B+tree.
     973         */
     974        list_foreach(area->used_space.leaf_list, leaf_link, btree_node_t,
     975            node) {
     976                btree_key_t i;
     977
     978                for (i = 0; i < node->keys; i++) {
     979                        uintptr_t ptr = node->key[i];
     980                        size_t size;
     981
     982                        for (size = 0; size < (size_t) node->value[i]; size++) {
     983                                pte_t pte;
     984                                bool found = page_mapping_find(as,
     985                                    ptr + P2SZ(size), false, &pte);
     986
     987                                (void) found;
     988                                assert(found);
     989                                assert(PTE_VALID(&pte));
     990                                assert(PTE_PRESENT(&pte));
     991
     992                                if ((area->backend) &&
     993                                    (area->backend->frame_free)) {
     994                                        area->backend->frame_free(area,
     995                                            ptr + P2SZ(size),
     996                                            PTE_GET_FRAME(&pte));
     997                                }
     998
     999                                page_mapping_remove(as, ptr + P2SZ(size));
    11321000                        }
    1133 
    1134                         page_mapping_remove(as, ptr + P2SZ(size));
    1135                 }
    1136 
    1137                 used_space_remove_ival(ival);
    1138                 ival = used_space_first(&area->used_space);
     1001                }
    11391002        }
    11401003
     
    11541017        page_table_unlock(as, false);
    11551018
    1156         used_space_finalize(&area->used_space);
     1019        btree_destroy(&area->used_space);
     1020
    11571021        area->attributes |= AS_AREA_ATTR_PARTIAL;
     1022
    11581023        sh_info_remove_reference(area->sh_info);
    11591024
     
    13911256        mutex_unlock(&area->sh_info->lock);
    13921257
     1258        /*
     1259         * Compute total number of used pages in the used_space B+tree
     1260         */
     1261        size_t used_pages = 0;
     1262
     1263        list_foreach(area->used_space.leaf_list, leaf_link, btree_node_t,
     1264            node) {
     1265                btree_key_t i;
     1266
     1267                for (i = 0; i < node->keys; i++)
     1268                        used_pages += (size_t) node->value[i];
     1269        }
     1270
    13931271        /* An array for storing frame numbers */
    1394         uintptr_t *old_frame = malloc(area->used_space.pages *
    1395             sizeof(uintptr_t));
     1272        uintptr_t *old_frame = malloc(used_pages * sizeof(uintptr_t));
    13961273        if (!old_frame) {
    13971274                mutex_unlock(&area->lock);
     
    14141291        size_t frame_idx = 0;
    14151292
    1416         used_space_ival_t *ival = used_space_first(&area->used_space);
    1417         while (ival != NULL) {
    1418                 uintptr_t ptr = ival->page;
    1419                 size_t size;
    1420 
    1421                 for (size = 0; size < ival->count; size++) {
    1422                         pte_t pte;
    1423                         bool found = page_mapping_find(as, ptr + P2SZ(size),
    1424                             false, &pte);
    1425 
    1426                         (void) found;
    1427                         assert(found);
    1428                         assert(PTE_VALID(&pte));
    1429                         assert(PTE_PRESENT(&pte));
    1430 
    1431                         old_frame[frame_idx++] = PTE_GET_FRAME(&pte);
    1432 
    1433                         /* Remove old mapping */
    1434                         page_mapping_remove(as, ptr + P2SZ(size));
    1435                 }
    1436 
    1437                 ival = used_space_next(ival);
     1293        list_foreach(area->used_space.leaf_list, leaf_link, btree_node_t,
     1294            node) {
     1295                btree_key_t i;
     1296
     1297                for (i = 0; i < node->keys; i++) {
     1298                        uintptr_t ptr = node->key[i];
     1299                        size_t size;
     1300
     1301                        for (size = 0; size < (size_t) node->value[i]; size++) {
     1302                                pte_t pte;
     1303                                bool found = page_mapping_find(as,
     1304                                    ptr + P2SZ(size), false, &pte);
     1305
     1306                                (void) found;
     1307                                assert(found);
     1308                                assert(PTE_VALID(&pte));
     1309                                assert(PTE_PRESENT(&pte));
     1310
     1311                                old_frame[frame_idx++] = PTE_GET_FRAME(&pte);
     1312
     1313                                /* Remove old mapping */
     1314                                page_mapping_remove(as, ptr + P2SZ(size));
     1315                        }
     1316                }
    14381317        }
    14391318
     
    14651344        frame_idx = 0;
    14661345
    1467         ival = used_space_first(&area->used_space);
    1468         while (ival != NULL) {
    1469                 uintptr_t ptr = ival->page;
    1470                 size_t size;
    1471 
    1472                 for (size = 0; size < ival->count; size++) {
    1473                         page_table_lock(as, false);
    1474 
    1475                         /* Insert the new mapping */
    1476                         page_mapping_insert(as, ptr + P2SZ(size),
    1477                             old_frame[frame_idx++], page_flags);
    1478 
    1479                         page_table_unlock(as, false);
    1480                 }
    1481 
    1482                 ival = used_space_next(ival);
     1346        list_foreach(area->used_space.leaf_list, leaf_link, btree_node_t,
     1347            node) {
     1348                btree_key_t i;
     1349
     1350                for (i = 0; i < node->keys; i++) {
     1351                        uintptr_t ptr = node->key[i];
     1352                        size_t size;
     1353
     1354                        for (size = 0; size < (size_t) node->value[i]; size++) {
     1355                                page_table_lock(as, false);
     1356
     1357                                /* Insert the new mapping */
     1358                                page_mapping_insert(as, ptr + P2SZ(size),
     1359                                    old_frame[frame_idx++], page_flags);
     1360
     1361                                page_table_unlock(as, false);
     1362                        }
     1363                }
    14831364        }
    14841365
     
    18441725}
    18451726
    1846 /** Initialize used space map.
    1847  *
    1848  * @param used_space Used space map
    1849  */
    1850 static void used_space_initialize(used_space_t *used_space)
    1851 {
    1852         odict_initialize(&used_space->ivals, used_space_getkey, used_space_cmp);
    1853         used_space->pages = 0;
    1854 }
    1855 
    1856 /** Finalize used space map.
    1857  *
    1858  * @param used_space Used space map
    1859  */
    1860 static void used_space_finalize(used_space_t *used_space)
    1861 {
    1862         assert(odict_empty(&used_space->ivals));
    1863         odict_finalize(&used_space->ivals);
    1864 }
    1865 
    1866 /** Get first interval of used space.
    1867  *
    1868  * @param used_space Used space map
    1869  * @return First interval or @c NULL if there are none
    1870  */
    1871 used_space_ival_t *used_space_first(used_space_t *used_space)
    1872 {
    1873         odlink_t *odlink = odict_first(&used_space->ivals);
    1874 
    1875         if (odlink == NULL)
    1876                 return NULL;
    1877 
    1878         return odict_get_instance(odlink, used_space_ival_t, lused_space);
    1879 }
    1880 
    1881 /** Get next interval of used space.
    1882  *
    1883  * @param cur Current interval
    1884  * @return Next interval or @c NULL if there are none
    1885  */
    1886 used_space_ival_t *used_space_next(used_space_ival_t *cur)
    1887 {
    1888         odlink_t *odlink = odict_next(&cur->lused_space,
    1889             &cur->used_space->ivals);
    1890 
    1891         if (odlink == NULL)
    1892                 return NULL;
    1893 
    1894         return odict_get_instance(odlink, used_space_ival_t, lused_space);
    1895 }
    1896 
    1897 /** Get last interval of used space.
    1898  *
    1899  * @param used_space Used space map
    1900  * @return First interval or @c NULL if there are none
    1901  */
    1902 static used_space_ival_t *used_space_last(used_space_t *used_space)
    1903 {
    1904         odlink_t *odlink = odict_last(&used_space->ivals);
    1905 
    1906         if (odlink == NULL)
    1907                 return NULL;
    1908 
    1909         return odict_get_instance(odlink, used_space_ival_t, lused_space);
    1910 }
    1911 
    1912 /** Find the first interval that contains addresses greater than or equal to
    1913  * @a ptr.
    1914  *
    1915  * @param used_space Used space map
    1916  * @param ptr Virtual address
    1917  *
    1918  * @return Used space interval or @c NULL if none matches
    1919  */
    1920 used_space_ival_t *used_space_find_gteq(used_space_t *used_space, uintptr_t ptr)
    1921 {
    1922         odlink_t *odlink;
    1923         used_space_ival_t *ival;
    1924 
    1925         /* Find last interval to start at address less than @a ptr */
    1926         odlink = odict_find_lt(&used_space->ivals, &ptr, NULL);
    1927         if (odlink != NULL) {
    1928                 ival = odict_get_instance(odlink, used_space_ival_t,
    1929                     lused_space);
    1930 
    1931                 /* If the interval extends above @a ptr, return it */
    1932                 if (ival->page + P2SZ(ival->count) > ptr)
    1933                         return ival;
    1934 
    1935                 /*
    1936                  * Otherwise, if a next interval exists, it must match
    1937                  * the criteria.
    1938                  */
    1939                 odlink = odict_next(&ival->lused_space, &used_space->ivals);
    1940         } else {
    1941                 /*
    1942                  * No interval with lower base address, so if there is any
    1943                  * interval at all, it must match the criteria
    1944                  */
    1945                 odlink = odict_first(&used_space->ivals);
    1946         }
    1947 
    1948         if (odlink != NULL) {
    1949                 ival = odict_get_instance(odlink, used_space_ival_t,
    1950                     lused_space);
    1951                 return ival;
    1952         }
    1953 
    1954         return NULL;
    1955 }
    1956 
    1957 /** Get key function for used space ordered dictionary.
    1958  *
    1959  * The key is the virtual address of the first page
    1960  *
    1961  * @param odlink Ordered dictionary link (used_space_ival_t.lused_space)
    1962  * @return Pointer to virtual address of first page cast as @c void *.
    1963  */
    1964 static void *used_space_getkey(odlink_t *odlink)
    1965 {
    1966         used_space_ival_t *ival = odict_get_instance(odlink, used_space_ival_t,
    1967             lused_space);
    1968         return (void *) &ival->page;
    1969 }
    1970 
    1971 /** Compare function for used space ordered dictionary.
    1972  *
    1973  * @param a Pointer to virtual address of first page cast as @c void *
    1974  * @param b Pointer to virtual address of first page cast as @c void *
    1975  * @return Less than zero, zero, greater than zero if virtual address @a a
    1976  *         is less than, equal to, greater than virtual address b, respectively.
    1977  */
    1978 static int used_space_cmp(void *a, void *b)
    1979 {
    1980         uintptr_t va = *(uintptr_t *) a;
    1981         uintptr_t vb = *(uintptr_t *) b;
    1982 
    1983         if (va < vb)
    1984                 return -1;
    1985         else if (va == vb)
    1986                 return 0;
    1987         else
    1988                 return +1;
    1989 }
    1990 
    1991 /** Remove used space interval.
    1992  *
    1993  * @param ival Used space interval
    1994  */
    1995 static void used_space_remove_ival(used_space_ival_t *ival)
    1996 {
    1997         ival->used_space->pages -= ival->count;
    1998         odict_remove(&ival->lused_space);
    1999         slab_free(used_space_ival_cache, ival);
    2000 }
    2001 
    2002 /** Shorten used space interval.
    2003  *
    2004  * @param ival Used space interval
    2005  * @param count New number of pages in the interval
    2006  */
    2007 static void used_space_shorten_ival(used_space_ival_t *ival, size_t count)
    2008 {
    2009         assert(count > 0);
    2010         assert(count < ival->count);
    2011 
    2012         ival->used_space->pages -= ival->count - count;
    2013         ival->count = count;
    2014 }
    2015 
    20161727/** Mark portion of address space area as used.
    20171728 *
    20181729 * The address space area must be already locked.
    20191730 *
    2020  * @param used_space Used space map
    2021  * @param page First page to be marked.
     1731 * @param area  Address space area.
     1732 * @param page  First page to be marked.
    20221733 * @param count Number of page to be marked.
    20231734 *
     
    20251736 *
    20261737 */
    2027 bool used_space_insert(used_space_t *used_space, uintptr_t page, size_t count)
    2028 {
    2029         used_space_ival_t *a;
    2030         used_space_ival_t *b;
    2031         bool adj_a;
    2032         bool adj_b;
    2033         odlink_t *odlink;
    2034         used_space_ival_t *ival;
    2035 
     1738bool used_space_insert(as_area_t *area, uintptr_t page, size_t count)
     1739{
     1740        assert(mutex_locked(&area->lock));
    20361741        assert(IS_ALIGNED(page, PAGE_SIZE));
    20371742        assert(count);
    20381743
    2039         /* Interval to the left */
    2040         odlink = odict_find_lt(&used_space->ivals, &page, NULL);
    2041         a = (odlink != NULL) ?
    2042             odict_get_instance(odlink, used_space_ival_t, lused_space) :
    2043             NULL;
    2044 
    2045         /* Interval to the right */
    2046         b = (a != NULL) ? used_space_next(a) :
    2047             used_space_first(used_space);
    2048 
    2049         /* Check for conflict with left interval */
    2050         if (a != NULL && overlaps(a->page, P2SZ(a->count), page, P2SZ(count)))
     1744        btree_node_t *leaf = NULL;
     1745        size_t pages = (size_t) btree_search(&area->used_space, page, &leaf);
     1746        if (pages) {
     1747                /*
     1748                 * We hit the beginning of some used space.
     1749                 */
    20511750                return false;
    2052 
    2053         /* Check for conflict with right interval */
    2054         if (b != NULL && overlaps(page, P2SZ(count), b->page, P2SZ(b->count)))
     1751        }
     1752
     1753        assert(leaf != NULL);
     1754
     1755        if (!leaf->keys) {
     1756                btree_insert(&area->used_space, page, (void *) count, leaf);
     1757                goto success;
     1758        }
     1759
     1760        btree_node_t *node = btree_leaf_node_left_neighbour(&area->used_space, leaf);
     1761        if (node) {
     1762                uintptr_t left_pg = node->key[node->keys - 1];
     1763                uintptr_t right_pg = leaf->key[0];
     1764                size_t left_cnt = (size_t) node->value[node->keys - 1];
     1765                size_t right_cnt = (size_t) leaf->value[0];
     1766
     1767                /*
     1768                 * Examine the possibility that the interval fits
     1769                 * somewhere between the rightmost interval of
     1770                 * the left neigbour and the first interval of the leaf.
     1771                 */
     1772
     1773                if (page >= right_pg) {
     1774                        /* Do nothing. */
     1775                } else if (overlaps(page, P2SZ(count), left_pg,
     1776                    P2SZ(left_cnt))) {
     1777                        /* The interval intersects with the left interval. */
     1778                        return false;
     1779                } else if (overlaps(page, P2SZ(count), right_pg,
     1780                    P2SZ(right_cnt))) {
     1781                        /* The interval intersects with the right interval. */
     1782                        return false;
     1783                } else if ((page == left_pg + P2SZ(left_cnt)) &&
     1784                    (page + P2SZ(count) == right_pg)) {
     1785                        /*
     1786                         * The interval can be added by merging the two already
     1787                         * present intervals.
     1788                         */
     1789                        node->value[node->keys - 1] += count + right_cnt;
     1790                        btree_remove(&area->used_space, right_pg, leaf);
     1791                        goto success;
     1792                } else if (page == left_pg + P2SZ(left_cnt)) {
     1793                        /*
     1794                         * The interval can be added by simply growing the left
     1795                         * interval.
     1796                         */
     1797                        node->value[node->keys - 1] += count;
     1798                        goto success;
     1799                } else if (page + P2SZ(count) == right_pg) {
     1800                        /*
     1801                         * The interval can be addded by simply moving base of
     1802                         * the right interval down and increasing its size
     1803                         * accordingly.
     1804                         */
     1805                        leaf->value[0] += count;
     1806                        leaf->key[0] = page;
     1807                        goto success;
     1808                } else {
     1809                        /*
     1810                         * The interval is between both neigbouring intervals,
     1811                         * but cannot be merged with any of them.
     1812                         */
     1813                        btree_insert(&area->used_space, page, (void *) count,
     1814                            leaf);
     1815                        goto success;
     1816                }
     1817        } else if (page < leaf->key[0]) {
     1818                uintptr_t right_pg = leaf->key[0];
     1819                size_t right_cnt = (size_t) leaf->value[0];
     1820
     1821                /*
     1822                 * Investigate the border case in which the left neighbour does
     1823                 * not exist but the interval fits from the left.
     1824                 */
     1825
     1826                if (overlaps(page, P2SZ(count), right_pg, P2SZ(right_cnt))) {
     1827                        /* The interval intersects with the right interval. */
     1828                        return false;
     1829                } else if (page + P2SZ(count) == right_pg) {
     1830                        /*
     1831                         * The interval can be added by moving the base of the
     1832                         * right interval down and increasing its size
     1833                         * accordingly.
     1834                         */
     1835                        leaf->key[0] = page;
     1836                        leaf->value[0] += count;
     1837                        goto success;
     1838                } else {
     1839                        /*
     1840                         * The interval doesn't adjoin with the right interval.
     1841                         * It must be added individually.
     1842                         */
     1843                        btree_insert(&area->used_space, page, (void *) count,
     1844                            leaf);
     1845                        goto success;
     1846                }
     1847        }
     1848
     1849        node = btree_leaf_node_right_neighbour(&area->used_space, leaf);
     1850        if (node) {
     1851                uintptr_t left_pg = leaf->key[leaf->keys - 1];
     1852                uintptr_t right_pg = node->key[0];
     1853                size_t left_cnt = (size_t) leaf->value[leaf->keys - 1];
     1854                size_t right_cnt = (size_t) node->value[0];
     1855
     1856                /*
     1857                 * Examine the possibility that the interval fits
     1858                 * somewhere between the leftmost interval of
     1859                 * the right neigbour and the last interval of the leaf.
     1860                 */
     1861
     1862                if (page < left_pg) {
     1863                        /* Do nothing. */
     1864                } else if (overlaps(page, P2SZ(count), left_pg,
     1865                    P2SZ(left_cnt))) {
     1866                        /* The interval intersects with the left interval. */
     1867                        return false;
     1868                } else if (overlaps(page, P2SZ(count), right_pg,
     1869                    P2SZ(right_cnt))) {
     1870                        /* The interval intersects with the right interval. */
     1871                        return false;
     1872                } else if ((page == left_pg + P2SZ(left_cnt)) &&
     1873                    (page + P2SZ(count) == right_pg)) {
     1874                        /*
     1875                         * The interval can be added by merging the two already
     1876                         * present intervals.
     1877                         */
     1878                        leaf->value[leaf->keys - 1] += count + right_cnt;
     1879                        btree_remove(&area->used_space, right_pg, node);
     1880                        goto success;
     1881                } else if (page == left_pg + P2SZ(left_cnt)) {
     1882                        /*
     1883                         * The interval can be added by simply growing the left
     1884                         * interval.
     1885                         */
     1886                        leaf->value[leaf->keys - 1] += count;
     1887                        goto success;
     1888                } else if (page + P2SZ(count) == right_pg) {
     1889                        /*
     1890                         * The interval can be addded by simply moving base of
     1891                         * the right interval down and increasing its size
     1892                         * accordingly.
     1893                         */
     1894                        node->value[0] += count;
     1895                        node->key[0] = page;
     1896                        goto success;
     1897                } else {
     1898                        /*
     1899                         * The interval is between both neigbouring intervals,
     1900                         * but cannot be merged with any of them.
     1901                         */
     1902                        btree_insert(&area->used_space, page, (void *) count,
     1903                            leaf);
     1904                        goto success;
     1905                }
     1906        } else if (page >= leaf->key[leaf->keys - 1]) {
     1907                uintptr_t left_pg = leaf->key[leaf->keys - 1];
     1908                size_t left_cnt = (size_t) leaf->value[leaf->keys - 1];
     1909
     1910                /*
     1911                 * Investigate the border case in which the right neighbour
     1912                 * does not exist but the interval fits from the right.
     1913                 */
     1914
     1915                if (overlaps(page, P2SZ(count), left_pg, P2SZ(left_cnt))) {
     1916                        /* The interval intersects with the left interval. */
     1917                        return false;
     1918                } else if (left_pg + P2SZ(left_cnt) == page) {
     1919                        /*
     1920                         * The interval can be added by growing the left
     1921                         * interval.
     1922                         */
     1923                        leaf->value[leaf->keys - 1] += count;
     1924                        goto success;
     1925                } else {
     1926                        /*
     1927                         * The interval doesn't adjoin with the left interval.
     1928                         * It must be added individually.
     1929                         */
     1930                        btree_insert(&area->used_space, page, (void *) count,
     1931                            leaf);
     1932                        goto success;
     1933                }
     1934        }
     1935
     1936        /*
     1937         * Note that if the algorithm made it thus far, the interval can fit
     1938         * only between two other intervals of the leaf. The two border cases
     1939         * were already resolved.
     1940         */
     1941        btree_key_t i;
     1942        for (i = 1; i < leaf->keys; i++) {
     1943                if (page < leaf->key[i]) {
     1944                        uintptr_t left_pg = leaf->key[i - 1];
     1945                        uintptr_t right_pg = leaf->key[i];
     1946                        size_t left_cnt = (size_t) leaf->value[i - 1];
     1947                        size_t right_cnt = (size_t) leaf->value[i];
     1948
     1949                        /*
     1950                         * The interval fits between left_pg and right_pg.
     1951                         */
     1952
     1953                        if (overlaps(page, P2SZ(count), left_pg,
     1954                            P2SZ(left_cnt))) {
     1955                                /*
     1956                                 * The interval intersects with the left
     1957                                 * interval.
     1958                                 */
     1959                                return false;
     1960                        } else if (overlaps(page, P2SZ(count), right_pg,
     1961                            P2SZ(right_cnt))) {
     1962                                /*
     1963                                 * The interval intersects with the right
     1964                                 * interval.
     1965                                 */
     1966                                return false;
     1967                        } else if ((page == left_pg + P2SZ(left_cnt)) &&
     1968                            (page + P2SZ(count) == right_pg)) {
     1969                                /*
     1970                                 * The interval can be added by merging the two
     1971                                 * already present intervals.
     1972                                 */
     1973                                leaf->value[i - 1] += count + right_cnt;
     1974                                btree_remove(&area->used_space, right_pg, leaf);
     1975                                goto success;
     1976                        } else if (page == left_pg + P2SZ(left_cnt)) {
     1977                                /*
     1978                                 * The interval can be added by simply growing
     1979                                 * the left interval.
     1980                                 */
     1981                                leaf->value[i - 1] += count;
     1982                                goto success;
     1983                        } else if (page + P2SZ(count) == right_pg) {
     1984                                /*
     1985                                 * The interval can be addded by simply moving
     1986                                 * base of the right interval down and
     1987                                 * increasing its size accordingly.
     1988                                 */
     1989                                leaf->value[i] += count;
     1990                                leaf->key[i] = page;
     1991                                goto success;
     1992                        } else {
     1993                                /*
     1994                                 * The interval is between both neigbouring
     1995                                 * intervals, but cannot be merged with any of
     1996                                 * them.
     1997                                 */
     1998                                btree_insert(&area->used_space, page,
     1999                                    (void *) count, leaf);
     2000                                goto success;
     2001                        }
     2002                }
     2003        }
     2004
     2005        panic("Inconsistency detected while adding %zu pages of used "
     2006            "space at %p.", count, (void *) page);
     2007
     2008success:
     2009        area->resident += count;
     2010        return true;
     2011}
     2012
     2013/** Mark portion of address space area as unused.
     2014 *
     2015 * The address space area must be already locked.
     2016 *
     2017 * @param area  Address space area.
     2018 * @param page  First page to be marked.
     2019 * @param count Number of page to be marked.
     2020 *
     2021 * @return False on failure or true on success.
     2022 *
     2023 */
     2024bool used_space_remove(as_area_t *area, uintptr_t page, size_t count)
     2025{
     2026        assert(mutex_locked(&area->lock));
     2027        assert(IS_ALIGNED(page, PAGE_SIZE));
     2028        assert(count);
     2029
     2030        btree_node_t *leaf;
     2031        size_t pages = (size_t) btree_search(&area->used_space, page, &leaf);
     2032        if (pages) {
     2033                /*
     2034                 * We are lucky, page is the beginning of some interval.
     2035                 */
     2036                if (count > pages) {
     2037                        return false;
     2038                } else if (count == pages) {
     2039                        btree_remove(&area->used_space, page, leaf);
     2040                        goto success;
     2041                } else {
     2042                        /*
     2043                         * Find the respective interval.
     2044                         * Decrease its size and relocate its start address.
     2045                         */
     2046                        btree_key_t i;
     2047                        for (i = 0; i < leaf->keys; i++) {
     2048                                if (leaf->key[i] == page) {
     2049                                        leaf->key[i] += P2SZ(count);
     2050                                        leaf->value[i] -= count;
     2051                                        goto success;
     2052                                }
     2053                        }
     2054
     2055                        goto error;
     2056                }
     2057        }
     2058
     2059        btree_node_t *node = btree_leaf_node_left_neighbour(&area->used_space,
     2060            leaf);
     2061        if ((node) && (page < leaf->key[0])) {
     2062                uintptr_t left_pg = node->key[node->keys - 1];
     2063                size_t left_cnt = (size_t) node->value[node->keys - 1];
     2064
     2065                if (overlaps(left_pg, P2SZ(left_cnt), page, P2SZ(count))) {
     2066                        if (page + P2SZ(count) == left_pg + P2SZ(left_cnt)) {
     2067                                /*
     2068                                 * The interval is contained in the rightmost
     2069                                 * interval of the left neighbour and can be
     2070                                 * removed by updating the size of the bigger
     2071                                 * interval.
     2072                                 */
     2073                                node->value[node->keys - 1] -= count;
     2074                                goto success;
     2075                        } else if (page + P2SZ(count) <
     2076                            left_pg + P2SZ(left_cnt)) {
     2077                                size_t new_cnt;
     2078
     2079                                /*
     2080                                 * The interval is contained in the rightmost
     2081                                 * interval of the left neighbour but its
     2082                                 * removal requires both updating the size of
     2083                                 * the original interval and also inserting a
     2084                                 * new interval.
     2085                                 */
     2086                                new_cnt = ((left_pg + P2SZ(left_cnt)) -
     2087                                    (page + P2SZ(count))) >> PAGE_WIDTH;
     2088                                node->value[node->keys - 1] -= count + new_cnt;
     2089                                btree_insert(&area->used_space, page +
     2090                                    P2SZ(count), (void *) new_cnt, leaf);
     2091                                goto success;
     2092                        }
     2093                }
     2094
    20552095                return false;
    2056 
    2057         /* Check if A is adjacent to the new interval */
    2058         adj_a = (a != NULL) && (a->page + P2SZ(a->count) == page);
    2059         /* Check if the new interval is adjacent to B*/
    2060         adj_b = (b != NULL) && page + P2SZ(count) == b->page;
    2061 
    2062         if (adj_a && adj_b) {
    2063                 /* Fuse into a single interval */
    2064                 a->count += count + b->count;
    2065                 used_space_remove_ival(b);
    2066         } else if (adj_a) {
    2067                 /* Append to A */
    2068                 a->count += count;
    2069         } else if (adj_b) {
    2070                 /* Prepend to B */
    2071                 b->page = page;
    2072                 b->count += count;
    2073         } else {
    2074                 /* Create new interval */
    2075                 ival = slab_alloc(used_space_ival_cache, 0);
    2076                 ival->used_space = used_space;
    2077                 odlink_initialize(&ival->lused_space);
    2078                 ival->page = page;
    2079                 ival->count = count;
    2080 
    2081                 odict_insert(&ival->lused_space, &used_space->ivals,
    2082                     NULL);
    2083         }
    2084 
    2085         used_space->pages += count;
     2096        } else if (page < leaf->key[0])
     2097                return false;
     2098
     2099        if (page > leaf->key[leaf->keys - 1]) {
     2100                uintptr_t left_pg = leaf->key[leaf->keys - 1];
     2101                size_t left_cnt = (size_t) leaf->value[leaf->keys - 1];
     2102
     2103                if (overlaps(left_pg, P2SZ(left_cnt), page, P2SZ(count))) {
     2104                        if (page + P2SZ(count) == left_pg + P2SZ(left_cnt)) {
     2105                                /*
     2106                                 * The interval is contained in the rightmost
     2107                                 * interval of the leaf and can be removed by
     2108                                 * updating the size of the bigger interval.
     2109                                 */
     2110                                leaf->value[leaf->keys - 1] -= count;
     2111                                goto success;
     2112                        } else if (page + P2SZ(count) < left_pg +
     2113                            P2SZ(left_cnt)) {
     2114                                size_t new_cnt;
     2115
     2116                                /*
     2117                                 * The interval is contained in the rightmost
     2118                                 * interval of the leaf but its removal
     2119                                 * requires both updating the size of the
     2120                                 * original interval and also inserting a new
     2121                                 * interval.
     2122                                 */
     2123                                new_cnt = ((left_pg + P2SZ(left_cnt)) -
     2124                                    (page + P2SZ(count))) >> PAGE_WIDTH;
     2125                                leaf->value[leaf->keys - 1] -= count + new_cnt;
     2126                                btree_insert(&area->used_space, page +
     2127                                    P2SZ(count), (void *) new_cnt, leaf);
     2128                                goto success;
     2129                        }
     2130                }
     2131
     2132                return false;
     2133        }
     2134
     2135        /*
     2136         * The border cases have been already resolved.
     2137         * Now the interval can be only between intervals of the leaf.
     2138         */
     2139        btree_key_t i;
     2140        for (i = 1; i < leaf->keys - 1; i++) {
     2141                if (page < leaf->key[i]) {
     2142                        uintptr_t left_pg = leaf->key[i - 1];
     2143                        size_t left_cnt = (size_t) leaf->value[i - 1];
     2144
     2145                        /*
     2146                         * Now the interval is between intervals corresponding
     2147                         * to (i - 1) and i.
     2148                         */
     2149                        if (overlaps(left_pg, P2SZ(left_cnt), page,
     2150                            P2SZ(count))) {
     2151                                if (page + P2SZ(count) ==
     2152                                    left_pg + P2SZ(left_cnt)) {
     2153                                        /*
     2154                                         * The interval is contained in the
     2155                                         * interval (i - 1) of the leaf and can
     2156                                         * be removed by updating the size of
     2157                                         * the bigger interval.
     2158                                         */
     2159                                        leaf->value[i - 1] -= count;
     2160                                        goto success;
     2161                                } else if (page + P2SZ(count) <
     2162                                    left_pg + P2SZ(left_cnt)) {
     2163                                        size_t new_cnt;
     2164
     2165                                        /*
     2166                                         * The interval is contained in the
     2167                                         * interval (i - 1) of the leaf but its
     2168                                         * removal requires both updating the
     2169                                         * size of the original interval and
     2170                                         * also inserting a new interval.
     2171                                         */
     2172                                        new_cnt = ((left_pg + P2SZ(left_cnt)) -
     2173                                            (page + P2SZ(count))) >>
     2174                                            PAGE_WIDTH;
     2175                                        leaf->value[i - 1] -= count + new_cnt;
     2176                                        btree_insert(&area->used_space, page +
     2177                                            P2SZ(count), (void *) new_cnt,
     2178                                            leaf);
     2179                                        goto success;
     2180                                }
     2181                        }
     2182
     2183                        return false;
     2184                }
     2185        }
     2186
     2187error:
     2188        panic("Inconsistency detected while removing %zu pages of used "
     2189            "space from %p.", count, (void *) page);
     2190
     2191success:
     2192        area->resident -= count;
    20862193        return true;
    20872194}
Note: See TracChangeset for help on using the changeset viewer.