Ignore:
File:
1 edited

Legend:

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

    r6785b88b rbab75df6  
    11/*
    22 * Copyright (c) 2010 Jakub Jermar
    3  * Copyright (c) 2018 Jiri Svoboda
    43 * All rights reserved.
    54 *
     
    112111as_t *AS_KERNEL = NULL;
    113112
    114 static void *as_areas_getkey(odlink_t *);
    115 static int as_areas_cmp(void *, void *);
    116 
    117113NO_TRACE static errno_t as_constructor(void *obj, unsigned int flags)
    118114{
     
    160156        (void) as_create_arch(as, 0);
    161157
    162         odict_initialize(&as->as_areas, as_areas_getkey, as_areas_cmp);
     158        btree_create(&as->as_area_btree);
    163159
    164160        if (flags & FLAG_AS_KERNEL)
     
    234230        /*
    235231         * Destroy address space areas of the address space.
    236          * Need to start from the beginning each time since we are destroying
    237          * the areas.
    238          */
    239         as_area_t *area = as_area_first(as);
    240         while (area != NULL) {
    241                 /*
    242                  * XXX We already have as_area_t, but as_area_destroy will
    243                  * have to search for it. This could be made faster.
    244                  */
    245                 as_area_destroy(as, area->base);
    246                 area = as_area_first(as);
    247         }
    248 
    249         odict_finalize(&as->as_areas);
     232         * The B+tree must be walked carefully because it is
     233         * also being destroyed.
     234         */
     235        bool cond = true;
     236        while (cond) {
     237                assert(!list_empty(&as->as_area_btree.leaf_list));
     238
     239                btree_node_t *node =
     240                    list_get_instance(list_first(&as->as_area_btree.leaf_list),
     241                    btree_node_t, leaf_link);
     242
     243                if ((cond = node->keys))
     244                        as_area_destroy(as, node->key[0]);
     245        }
     246
     247        btree_destroy(&as->as_area_btree);
    250248
    251249#ifdef AS_PAGE_TABLE
     
    285283}
    286284
    287 /** Return first address space area.
    288  *
    289  * @param as Address space
    290  * @return First area in @a as (i.e. area with the lowest base address)
    291  *         or @c NULL if there is none
    292  */
    293 as_area_t *as_area_first(as_t *as)
    294 {
    295         odlink_t *odlink = odict_first(&as->as_areas);
    296         if (odlink == NULL)
    297                 return NULL;
    298 
    299         return odict_get_instance(odlink, as_area_t, las_areas);
    300 }
    301 
    302 /** Return next address space area.
    303  *
    304  * @param cur Current area
    305  * @return Next area in the same address space or @c NULL if @a cur is the
    306  *         last area.
    307  */
    308 as_area_t *as_area_next(as_area_t *cur)
    309 {
    310         odlink_t *odlink = odict_next(&cur->las_areas, &cur->as->as_areas);
    311         if (odlink == NULL)
    312                 return NULL;
    313 
    314         return odict_get_instance(odlink, as_area_t, las_areas);
    315 }
    316 
    317 /** Determine if area with specified parameters would conflict with
    318  * a specific existing address space area.
    319  *
    320  * @param addr    Starting virtual address of the area being tested.
    321  * @param count   Number of pages in the area being tested.
    322  * @param guarded True if the area being tested is protected by guard pages.
    323  * @param area    Area against which we are testing.
    324  *
    325  * @return True if the two areas conflict, false otherwise.
    326  */
    327 NO_TRACE static bool area_is_conflicting(uintptr_t addr,
    328     size_t count, bool guarded, as_area_t *area)
    329 {
    330         assert((addr % PAGE_SIZE) == 0);
    331 
    332         size_t gsize = P2SZ(count);
    333         size_t agsize = P2SZ(area->pages);
    334 
    335         /*
    336          * A guarded area has one guard page before, one page after.
    337          * What we do here is: if either area is guarded, we add
    338          * PAGE_SIZE to the size of both areas. That guarantees
    339          * they will be spaced at least one page apart.
    340          */
    341         if (guarded || (area->flags & AS_AREA_GUARD) != 0) {
    342                 /* Add guard page size unless area is at the end of VA domain */
    343                 if (!overflows(addr, P2SZ(count)))
    344                         gsize += PAGE_SIZE;
    345 
    346                 /* Add guard page size unless area is at the end of VA domain */
    347                 if (!overflows(area->base, P2SZ(area->pages)))
    348                         agsize += PAGE_SIZE;
    349         }
    350 
    351         return overlaps(addr, gsize, area->base, agsize);
    352 
    353 }
    354 
    355285/** Check area conflicts with other areas.
    356286 *
     
    359289 * @param count   Number of pages in the area being tested.
    360290 * @param guarded True if the area being tested is protected by guard pages.
    361  * @param avoid   Do not touch this area. I.e. this area is not considered
    362  *                as presenting a conflict.
     291 * @param avoid   Do not touch this area.
    363292 *
    364293 * @return True if there is no conflict, false otherwise.
     
    385314
    386315        /*
    387          * To determine if we overlap with another area, we just need
    388          * to look at overlap with the last area with base address <=
    389          * to ours and on the first area with base address > than ours.
    390          *
    391          * First find last area with <= base address.
    392          */
    393         odlink_t *odlink = odict_find_leq(&as->as_areas, &addr, NULL);
    394         if (odlink != NULL) {
    395                 as_area_t *area = odict_get_instance(odlink, as_area_t,
    396                     las_areas);
     316         * The leaf node is found in O(log n), where n is proportional to
     317         * the number of address space areas belonging to as.
     318         * The check for conflicts is then attempted on the rightmost
     319         * record in the left neighbour, the leftmost record in the right
     320         * neighbour and all records in the leaf node itself.
     321         */
     322        btree_node_t *leaf;
     323        as_area_t *area =
     324            (as_area_t *) btree_search(&as->as_area_btree, addr, &leaf);
     325        if (area) {
     326                if (area != avoid)
     327                        return false;
     328        }
     329
     330        /* First, check the two border cases. */
     331        btree_node_t *node =
     332            btree_leaf_node_left_neighbour(&as->as_area_btree, leaf);
     333        if (node) {
     334                area = (as_area_t *) node->value[node->keys - 1];
    397335
    398336                if (area != avoid) {
    399337                        mutex_lock(&area->lock);
    400                         if (area_is_conflicting(addr, count, guarded, area)) {
     338
     339                        /*
     340                         * If at least one of the two areas are protected
     341                         * by the AS_AREA_GUARD flag then we must be sure
     342                         * that they are separated by at least one unmapped
     343                         * page.
     344                         */
     345                        int const gp = (guarded ||
     346                            (area->flags & AS_AREA_GUARD)) ? 1 : 0;
     347
     348                        /*
     349                         * The area comes from the left neighbour node, which
     350                         * means that there already are some areas in the leaf
     351                         * node, which in turn means that adding gp is safe and
     352                         * will not cause an integer overflow.
     353                         */
     354                        if (overlaps(addr, P2SZ(count), area->base,
     355                            P2SZ(area->pages + gp))) {
    401356                                mutex_unlock(&area->lock);
    402357                                return false;
     
    405360                        mutex_unlock(&area->lock);
    406361                }
    407 
    408                 /* Next area */
    409                 odlink = odict_next(odlink, &as->as_areas);
    410         }
    411 
    412         /*
    413          * Next area, if any, is the first with base > than our base address.
    414          * If there was no area with <= base, we need to look at the first area.
    415          */
    416         if (odlink == NULL)
    417                 odlink = odict_first(&as->as_areas);
    418 
    419         if (odlink != NULL) {
    420                 as_area_t *area = odict_get_instance(odlink, as_area_t,
    421                     las_areas);
     362        }
     363
     364        node = btree_leaf_node_right_neighbour(&as->as_area_btree, leaf);
     365        if (node) {
     366                area = (as_area_t *) node->value[0];
    422367
    423368                if (area != avoid) {
     369                        int gp;
     370
    424371                        mutex_lock(&area->lock);
    425                         if (area_is_conflicting(addr, count, guarded, area)) {
     372
     373                        gp = (guarded || (area->flags & AS_AREA_GUARD)) ? 1 : 0;
     374                        if (gp && overflows(addr, P2SZ(count))) {
     375                                /*
     376                                 * Guard page not needed if the supposed area
     377                                 * is adjacent to the end of the address space.
     378                                 * We already know that the following test is
     379                                 * going to fail...
     380                                 */
     381                                gp--;
     382                        }
     383
     384                        if (overlaps(addr, P2SZ(count + gp), area->base,
     385                            P2SZ(area->pages))) {
    426386                                mutex_unlock(&area->lock);
    427387                                return false;
     
    430390                        mutex_unlock(&area->lock);
    431391                }
     392        }
     393
     394        /* Second, check the leaf node. */
     395        btree_key_t i;
     396        for (i = 0; i < leaf->keys; i++) {
     397                area = (as_area_t *) leaf->value[i];
     398                int agp;
     399                int gp;
     400
     401                if (area == avoid)
     402                        continue;
     403
     404                mutex_lock(&area->lock);
     405
     406                gp = (guarded || (area->flags & AS_AREA_GUARD)) ? 1 : 0;
     407                agp = gp;
     408
     409                /*
     410                 * Sanitize the two possible unsigned integer overflows.
     411                 */
     412                if (gp && overflows(addr, P2SZ(count)))
     413                        gp--;
     414                if (agp && overflows(area->base, P2SZ(area->pages)))
     415                        agp--;
     416
     417                if (overlaps(addr, P2SZ(count + gp), area->base,
     418                    P2SZ(area->pages + agp))) {
     419                        mutex_unlock(&area->lock);
     420                        return false;
     421                }
     422
     423                mutex_unlock(&area->lock);
    432424        }
    433425
     
    496488
    497489        /* Eventually check the addresses behind each area */
    498         as_area_t *area = as_area_first(as);
    499         while (area != NULL) {
    500                 mutex_lock(&area->lock);
    501 
    502                 addr = area->base + P2SZ(area->pages);
    503 
    504                 if (guarded || area->flags & AS_AREA_GUARD) {
    505                         /*
    506                          * We must leave an unmapped page
    507                          * between the two areas.
    508                          */
    509                         addr += P2SZ(1);
    510                 }
    511 
    512                 bool avail =
    513                     ((addr >= bound) && (addr >= area->base) &&
    514                     (check_area_conflicts(as, addr, pages, guarded, area)));
    515 
    516                 mutex_unlock(&area->lock);
    517 
    518                 if (avail)
    519                         return addr;
    520 
    521                 area = as_area_next(area);
     490        list_foreach(as->as_area_btree.leaf_list, leaf_link, btree_node_t, node) {
     491
     492                for (btree_key_t i = 0; i < node->keys; i++) {
     493                        as_area_t *area = (as_area_t *) node->value[i];
     494
     495                        mutex_lock(&area->lock);
     496
     497                        addr =
     498                            ALIGN_UP(area->base + P2SZ(area->pages), PAGE_SIZE);
     499
     500                        if (guarded || area->flags & AS_AREA_GUARD) {
     501                                /*
     502                                 * We must leave an unmapped page
     503                                 * between the two areas.
     504                                 */
     505                                addr += P2SZ(1);
     506                        }
     507
     508                        bool avail =
     509                            ((addr >= bound) && (addr >= area->base) &&
     510                            (check_area_conflicts(as, addr, pages, guarded, area)));
     511
     512                        mutex_unlock(&area->lock);
     513
     514                        if (avail)
     515                                return addr;
     516                }
    522517        }
    523518
     
    634629
    635630        area->as = as;
    636         odlink_initialize(&area->las_areas);
    637631        area->flags = flags;
    638632        area->attributes = attrs;
     
    692686
    693687        btree_create(&area->used_space);
    694         odict_insert(&area->las_areas, &as->as_areas, NULL);
     688        btree_insert(&as->as_area_btree, *base, (void *) area,
     689            NULL);
    695690
    696691        mutex_unlock(&as->lock);
     
    712707        assert(mutex_locked(&as->lock));
    713708
    714         odlink_t *odlink = odict_find_leq(&as->as_areas, &va, NULL);
    715         if (odlink == NULL)
    716                 return NULL;
    717 
    718         as_area_t *area = odict_get_instance(odlink, as_area_t, las_areas);
    719         mutex_lock(&area->lock);
    720 
    721         assert(area->base <= va);
    722 
    723         if (va <= area->base + (P2SZ(area->pages) - 1))
     709        btree_node_t *leaf;
     710        as_area_t *area = (as_area_t *) btree_search(&as->as_area_btree, va,
     711            &leaf);
     712        if (area) {
     713                /* va is the base address of an address space area */
     714                mutex_lock(&area->lock);
    724715                return area;
    725 
    726         mutex_unlock(&area->lock);
     716        }
     717
     718        /*
     719         * Search the leaf node and the rightmost record of its left neighbour
     720         * to find out whether this is a miss or va belongs to an address
     721         * space area found there.
     722         */
     723
     724        /* First, search the leaf node itself. */
     725        btree_key_t i;
     726
     727        for (i = 0; i < leaf->keys; i++) {
     728                area = (as_area_t *) leaf->value[i];
     729
     730                mutex_lock(&area->lock);
     731
     732                if ((area->base <= va) &&
     733                    (va <= area->base + (P2SZ(area->pages) - 1)))
     734                        return area;
     735
     736                mutex_unlock(&area->lock);
     737        }
     738
     739        /*
     740         * Second, locate the left neighbour and test its last record.
     741         * Because of its position in the B+tree, it must have base < va.
     742         */
     743        btree_node_t *lnode = btree_leaf_node_left_neighbour(&as->as_area_btree,
     744            leaf);
     745        if (lnode) {
     746                area = (as_area_t *) lnode->value[lnode->keys - 1];
     747
     748                mutex_lock(&area->lock);
     749
     750                if (va <= area->base + (P2SZ(area->pages) - 1))
     751                        return area;
     752
     753                mutex_unlock(&area->lock);
     754        }
     755
    727756        return NULL;
    728757}
     
    962991                area->backend->destroy(area);
    963992
     993        uintptr_t base = area->base;
     994
    964995        page_table_lock(as, false);
    965996
     
    10281059         * Remove the empty area from address space.
    10291060         */
    1030         odict_remove(&area->las_areas);
     1061        btree_remove(&as->as_area_btree, base, NULL);
    10311062
    10321063        free(area);
     
    15831614
    15841615        return area_flags_to_page_flags(area->flags);
    1585 }
    1586 
    1587 /** Get key function for the @c as_t.as_areas ordered dictionary.
    1588  *
    1589  * @param odlink Link
    1590  * @return Pointer to task ID cast as 'void *'
    1591  */
    1592 static void *as_areas_getkey(odlink_t *odlink)
    1593 {
    1594         as_area_t *area = odict_get_instance(odlink, as_area_t, las_areas);
    1595         return (void *) &area->base;
    1596 }
    1597 
    1598 /** Key comparison function for the @c as_t.as_areas ordered dictionary.
    1599  *
    1600  * @param a Pointer to area A base
    1601  * @param b Pointer to area B base
    1602  * @return -1, 0, 1 iff base of A is lower than, equal to, higher than B
    1603  */
    1604 static int as_areas_cmp(void *a, void *b)
    1605 {
    1606         uintptr_t base_a = *(uintptr_t *)a;
    1607         uintptr_t base_b = *(uintptr_t *)b;
    1608 
    1609         if (base_a < base_b)
    1610                 return -1;
    1611         else if (base_a == base_b)
    1612                 return 0;
    1613         else
    1614                 return +1;
    16151616}
    16161617
     
    22472248        mutex_lock(&as->lock);
    22482249
    2249         /* Count number of areas. */
    2250         size_t area_cnt = odict_count(&as->as_areas);
     2250        /* First pass, count number of areas. */
     2251
     2252        size_t area_cnt = 0;
     2253
     2254        list_foreach(as->as_area_btree.leaf_list, leaf_link, btree_node_t,
     2255            node) {
     2256                area_cnt += node->keys;
     2257        }
    22512258
    22522259        size_t isize = area_cnt * sizeof(as_area_info_t);
    22532260        as_area_info_t *info = nfmalloc(isize);
    22542261
    2255         /* Record area data. */
     2262        /* Second pass, record data. */
    22562263
    22572264        size_t area_idx = 0;
    22582265
    2259         as_area_t *area = as_area_first(as);
    2260         while (area != NULL) {
    2261                 assert(area_idx < area_cnt);
    2262                 mutex_lock(&area->lock);
    2263 
    2264                 info[area_idx].start_addr = area->base;
    2265                 info[area_idx].size = P2SZ(area->pages);
    2266                 info[area_idx].flags = area->flags;
    2267                 ++area_idx;
    2268 
    2269                 mutex_unlock(&area->lock);
    2270                 area = as_area_next(area);
     2266        list_foreach(as->as_area_btree.leaf_list, leaf_link, btree_node_t,
     2267            node) {
     2268                btree_key_t i;
     2269
     2270                for (i = 0; i < node->keys; i++) {
     2271                        as_area_t *area = node->value[i];
     2272
     2273                        assert(area_idx < area_cnt);
     2274                        mutex_lock(&area->lock);
     2275
     2276                        info[area_idx].start_addr = area->base;
     2277                        info[area_idx].size = P2SZ(area->pages);
     2278                        info[area_idx].flags = area->flags;
     2279                        ++area_idx;
     2280
     2281                        mutex_unlock(&area->lock);
     2282                }
    22712283        }
    22722284
     
    22872299
    22882300        /* Print out info about address space areas */
    2289         as_area_t *area = as_area_first(as);
    2290         while (area != NULL) {
    2291                 mutex_lock(&area->lock);
    2292                 printf("as_area: %p, base=%p, pages=%zu"
    2293                     " (%p - %p)\n", area, (void *) area->base,
    2294                     area->pages, (void *) area->base,
    2295                     (void *) (area->base + P2SZ(area->pages)));
    2296                 mutex_unlock(&area->lock);
    2297 
    2298                 area = as_area_next(area);
     2301        list_foreach(as->as_area_btree.leaf_list, leaf_link, btree_node_t,
     2302            node) {
     2303                btree_key_t i;
     2304
     2305                for (i = 0; i < node->keys; i++) {
     2306                        as_area_t *area = node->value[i];
     2307
     2308                        mutex_lock(&area->lock);
     2309                        printf("as_area: %p, base=%p, pages=%zu"
     2310                            " (%p - %p)\n", area, (void *) area->base,
     2311                            area->pages, (void *) area->base,
     2312                            (void *) (area->base + P2SZ(area->pages)));
     2313                        mutex_unlock(&area->lock);
     2314                }
    22992315        }
    23002316
Note: See TracChangeset for help on using the changeset viewer.