Fork us on GitHub Follow us on Facebook Follow us on Twitter

Changeset e5a015b in mainline for kernel/generic/src/mm/backend_elf.c


Ignore:
Timestamp:
2011-04-16T20:45:36Z (11 years ago)
Author:
Jakub Jermar <jakub@…>
Branches:
lfn, master
Children:
a7dbd49
Parents:
b2fb47f (diff), 9e953bda (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the (diff) links above to see all the changes relative to each parent.
Message:

Merge the memory reservation feature (Phase 1) from
lp:~jakub/helenos/mm.

This merge makes the testcase from ticket #114 non-reproducible. The
testcase is now available as tester's malloc2 test. It also seems to me
that this merge makes it harder for the system to run out of memory
during kconsole 'test *' and 'tester *', even though I did see several
hangs already with this feature in place. See below for what is still
missing to make the hangs even less probable or even impossible.

In Phase 1, I am targeting just the low-hanging fruits. In particular,
only anonymous and ELF backend pages are reserved physical memory at
time of as_area_create() and as_area_resize(). Memory is unreserved on
as_area_destroy(). In all other cases, memory is reserved at the same
time as it is allocated, making those calls subject to infinite
blocking if FRAME_ATOMIC is not used.

Possible sources of memory overcommit not addressed in this merge:

  • As mentioned above, only backend pages are reserved; pages for supporting structures such as B+tree nodes, TTEs are not reserved or handled otherwise. Kernel heap allocator fragmentation is not included in the reservations either.
  • The initial amount of reservable memory is fed from zone_construct(). Zone merging is not taken into account, which can make the reservable memory tracking inaccurate.
File:
1 edited

Legend:

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

    rb2fb47f re5a015b  
    4343#include <mm/slab.h>
    4444#include <mm/page.h>
     45#include <mm/reserve.h>
    4546#include <genarch/mm/page_pt.h>
    4647#include <genarch/mm/page_ht.h>
     
    5152#include <arch/barrier.h>
    5253
    53 #ifdef CONFIG_VIRT_IDX_DCACHE
    54 #include <arch/mm/cache.h>
    55 #endif
     54static bool elf_create(as_area_t *);
     55static bool elf_resize(as_area_t *, size_t);
     56static void elf_share(as_area_t *);
     57static void elf_destroy(as_area_t *);
    5658
    5759static int elf_page_fault(as_area_t *area, uintptr_t addr, pf_access_t access);
    5860static void elf_frame_free(as_area_t *area, uintptr_t page, uintptr_t frame);
    59 static void elf_share(as_area_t *area);
    6061
    6162mem_backend_t elf_backend = {
     63        .create = elf_create,
     64        .resize = elf_resize,
     65        .share = elf_share,
     66        .destroy = elf_destroy,
     67
    6268        .page_fault = elf_page_fault,
    6369        .frame_free = elf_frame_free,
    64         .share = elf_share
    6570};
    6671
    67 /** Service a page fault in the ELF backend address space area.
    68  *
    69  * The address space area and page tables must be already locked.
    70  *
    71  * @param area          Pointer to the address space area.
    72  * @param addr          Faulting virtual address.
    73  * @param access        Access mode that caused the fault (i.e.
    74  *                      read/write/exec).
    75  *
    76  * @return              AS_PF_FAULT on failure (i.e. page fault) or AS_PF_OK
    77  *                      on success (i.e. serviced).
    78  */
    79 int elf_page_fault(as_area_t *area, uintptr_t addr, pf_access_t access)
    80 {
    81         elf_header_t *elf = area->backend_data.elf;
    82         elf_segment_header_t *entry = area->backend_data.segment;
    83         btree_node_t *leaf;
    84         uintptr_t base, frame, page, start_anon;
    85         size_t i;
    86         bool dirty = false;
    87 
    88         ASSERT(page_table_locked(AS));
    89         ASSERT(mutex_locked(&area->lock));
    90 
    91         if (!as_area_check_access(area, access))
    92                 return AS_PF_FAULT;
     72bool elf_create(as_area_t *area)
     73{
     74        elf_segment_header_t *entry = area->backend_data.segment;
     75        size_t nonanon_pages = ALIGN_DOWN(entry->p_filesz, PAGE_SIZE);
     76
     77        if (area->pages <= nonanon_pages)
     78                return true;
    9379       
    94         if (addr < ALIGN_DOWN(entry->p_vaddr, PAGE_SIZE))
    95                 return AS_PF_FAULT;
     80        return reserve_try_alloc(area->pages - nonanon_pages);
     81}
     82
     83bool elf_resize(as_area_t *area, size_t new_pages)
     84{
     85        elf_segment_header_t *entry = area->backend_data.segment;
     86        size_t nonanon_pages = ALIGN_DOWN(entry->p_filesz, PAGE_SIZE);
     87
     88        if (new_pages > area->pages) {
     89                /* The area is growing. */
     90                if (area->pages >= nonanon_pages)
     91                        return reserve_try_alloc(new_pages - area->pages);
     92                else if (new_pages > nonanon_pages)
     93                        return reserve_try_alloc(new_pages - nonanon_pages);
     94        } else if (new_pages < area->pages) {
     95                /* The area is shrinking. */
     96                if (new_pages >= nonanon_pages)
     97                        reserve_free(area->pages - new_pages);
     98                else if (area->pages > nonanon_pages)
     99                        reserve_free(nonanon_pages - new_pages);
     100        }
    96101       
    97         if (addr >= entry->p_vaddr + entry->p_memsz)
    98                 return AS_PF_FAULT;
    99        
    100         i = (addr - ALIGN_DOWN(entry->p_vaddr, PAGE_SIZE)) >> PAGE_WIDTH;
    101         base = (uintptr_t)
    102             (((void *) elf) + ALIGN_DOWN(entry->p_offset, PAGE_SIZE));
    103 
    104         /* Virtual address of faulting page*/
    105         page = ALIGN_DOWN(addr, PAGE_SIZE);
    106 
    107         /* Virtual address of the end of initialized part of segment */
    108         start_anon = entry->p_vaddr + entry->p_filesz;
    109 
    110         if (area->sh_info) {
    111                 bool found = false;
    112 
    113                 /*
    114                  * The address space area is shared.
    115                  */
    116                
    117                 mutex_lock(&area->sh_info->lock);
    118                 frame = (uintptr_t) btree_search(&area->sh_info->pagemap,
    119                     page - area->base, &leaf);
    120                 if (!frame) {
    121                         unsigned int i;
    122 
    123                         /*
    124                          * Workaround for valid NULL address.
    125                          */
    126 
    127                         for (i = 0; i < leaf->keys; i++) {
    128                                 if (leaf->key[i] == page - area->base) {
    129                                         found = true;
    130                                         break;
    131                                 }
    132                         }
    133                 }
    134                 if (frame || found) {
    135                         frame_reference_add(ADDR2PFN(frame));
    136                         page_mapping_insert(AS, addr, frame,
    137                             as_area_get_flags(area));
    138                         if (!used_space_insert(area, page, 1))
    139                                 panic("Cannot insert used space.");
    140                         mutex_unlock(&area->sh_info->lock);
    141                         return AS_PF_OK;
    142                 }
    143         }
    144 
    145         /*
    146          * The area is either not shared or the pagemap does not contain the
    147          * mapping.
    148          */
    149         if (page >= entry->p_vaddr && page + PAGE_SIZE <= start_anon) {
    150                 /*
    151                  * Initialized portion of the segment. The memory is backed
    152                  * directly by the content of the ELF image. Pages are
    153                  * only copied if the segment is writable so that there
    154                  * can be more instantions of the same memory ELF image
    155                  * used at a time. Note that this could be later done
    156                  * as COW.
    157                  */
    158                 if (entry->p_flags & PF_W) {
    159                         frame = (uintptr_t)frame_alloc(ONE_FRAME, 0);
    160                         memcpy((void *) PA2KA(frame),
    161                             (void *) (base + i * FRAME_SIZE), FRAME_SIZE);
    162                         if (entry->p_flags & PF_X) {
    163                                 smc_coherence_block((void *) PA2KA(frame),
    164                                     FRAME_SIZE);
    165                         }
    166                         dirty = true;
    167                 } else {
    168                         frame = KA2PA(base + i * FRAME_SIZE);
    169                 }       
    170         } else if (page >= start_anon) {
    171                 /*
    172                  * This is the uninitialized portion of the segment.
    173                  * It is not physically present in the ELF image.
    174                  * To resolve the situation, a frame must be allocated
    175                  * and cleared.
    176                  */
    177                 frame = (uintptr_t)frame_alloc(ONE_FRAME, 0);
    178                 memsetb((void *) PA2KA(frame), FRAME_SIZE, 0);
    179                 dirty = true;
    180         } else {
    181                 size_t pad_lo, pad_hi;
    182                 /*
    183                  * The mixed case.
    184                  *
    185                  * The middle part is backed by the ELF image and
    186                  * the lower and upper parts are anonymous memory.
    187                  * (The segment can be and often is shorter than 1 page).
    188                  */
    189                 if (page < entry->p_vaddr)
    190                         pad_lo = entry->p_vaddr - page;
    191                 else
    192                         pad_lo = 0;
    193 
    194                 if (start_anon < page + PAGE_SIZE)
    195                         pad_hi = page + PAGE_SIZE - start_anon;
    196                 else
    197                         pad_hi = 0;
    198 
    199                 frame = (uintptr_t)frame_alloc(ONE_FRAME, 0);
    200                 memcpy((void *) (PA2KA(frame) + pad_lo),
    201                     (void *) (base + i * FRAME_SIZE + pad_lo),
    202                     FRAME_SIZE - pad_lo - pad_hi);
    203                 if (entry->p_flags & PF_X) {
    204                         smc_coherence_block((void *) (PA2KA(frame) + pad_lo),
    205                             FRAME_SIZE - pad_lo - pad_hi);
    206                 }
    207                 memsetb((void *) PA2KA(frame), pad_lo, 0);
    208                 memsetb((void *) (PA2KA(frame) + FRAME_SIZE - pad_hi), pad_hi,
    209                     0);
    210                 dirty = true;
    211         }
    212 
    213         if (dirty && area->sh_info) {
    214                 frame_reference_add(ADDR2PFN(frame));
    215                 btree_insert(&area->sh_info->pagemap, page - area->base,
    216                     (void *) frame, leaf);
    217         }
    218 
    219         if (area->sh_info)
    220                 mutex_unlock(&area->sh_info->lock);
    221 
    222         page_mapping_insert(AS, addr, frame, as_area_get_flags(area));
    223         if (!used_space_insert(area, page, 1))
    224                 panic("Cannot insert used space.");
    225 
    226         return AS_PF_OK;
    227 }
    228 
    229 /** Free a frame that is backed by the ELF backend.
    230  *
    231  * The address space area and page tables must be already locked.
    232  *
    233  * @param area          Pointer to the address space area.
    234  * @param page          Page that is mapped to frame. Must be aligned to
    235  *                      PAGE_SIZE.
    236  * @param frame         Frame to be released.
    237  *
    238  */
    239 void elf_frame_free(as_area_t *area, uintptr_t page, uintptr_t frame)
    240 {
    241         elf_segment_header_t *entry = area->backend_data.segment;
    242         uintptr_t start_anon;
    243 
    244         ASSERT(page_table_locked(area->as));
    245         ASSERT(mutex_locked(&area->lock));
    246 
    247         ASSERT(page >= ALIGN_DOWN(entry->p_vaddr, PAGE_SIZE));
    248         ASSERT(page < entry->p_vaddr + entry->p_memsz);
    249 
    250         start_anon = entry->p_vaddr + entry->p_filesz;
    251 
    252         if (page >= entry->p_vaddr && page + PAGE_SIZE <= start_anon) {
    253                 if (entry->p_flags & PF_W) {
    254                         /*
    255                          * Free the frame with the copy of writable segment
    256                          * data.
    257                          */
    258                         frame_free(frame);
    259                 }
    260         } else {
    261                 /*
    262                  * The frame is either anonymous memory or the mixed case (i.e.
    263                  * lower part is backed by the ELF image and the upper is
    264                  * anonymous). In any case, a frame needs to be freed.
    265                  */
    266                 frame_free(frame);
    267         }
     102        return true;
    268103}
    269104
     
    356191}
    357192
     193void elf_destroy(as_area_t *area)
     194{
     195        elf_segment_header_t *entry = area->backend_data.segment;
     196        size_t nonanon_pages = ALIGN_DOWN(entry->p_filesz, PAGE_SIZE);
     197
     198        if (area->pages > nonanon_pages)
     199                reserve_free(area->pages - nonanon_pages);
     200}
     201
     202/** Service a page fault in the ELF backend address space area.
     203 *
     204 * The address space area and page tables must be already locked.
     205 *
     206 * @param area          Pointer to the address space area.
     207 * @param addr          Faulting virtual address.
     208 * @param access        Access mode that caused the fault (i.e.
     209 *                      read/write/exec).
     210 *
     211 * @return              AS_PF_FAULT on failure (i.e. page fault) or AS_PF_OK
     212 *                      on success (i.e. serviced).
     213 */
     214int elf_page_fault(as_area_t *area, uintptr_t addr, pf_access_t access)
     215{
     216        elf_header_t *elf = area->backend_data.elf;
     217        elf_segment_header_t *entry = area->backend_data.segment;
     218        btree_node_t *leaf;
     219        uintptr_t base, frame, page, start_anon;
     220        size_t i;
     221        bool dirty = false;
     222
     223        ASSERT(page_table_locked(AS));
     224        ASSERT(mutex_locked(&area->lock));
     225
     226        if (!as_area_check_access(area, access))
     227                return AS_PF_FAULT;
     228       
     229        if (addr < ALIGN_DOWN(entry->p_vaddr, PAGE_SIZE))
     230                return AS_PF_FAULT;
     231       
     232        if (addr >= entry->p_vaddr + entry->p_memsz)
     233                return AS_PF_FAULT;
     234       
     235        i = (addr - ALIGN_DOWN(entry->p_vaddr, PAGE_SIZE)) >> PAGE_WIDTH;
     236        base = (uintptr_t)
     237            (((void *) elf) + ALIGN_DOWN(entry->p_offset, PAGE_SIZE));
     238
     239        /* Virtual address of faulting page*/
     240        page = ALIGN_DOWN(addr, PAGE_SIZE);
     241
     242        /* Virtual address of the end of initialized part of segment */
     243        start_anon = entry->p_vaddr + entry->p_filesz;
     244
     245        if (area->sh_info) {
     246                bool found = false;
     247
     248                /*
     249                 * The address space area is shared.
     250                 */
     251               
     252                mutex_lock(&area->sh_info->lock);
     253                frame = (uintptr_t) btree_search(&area->sh_info->pagemap,
     254                    page - area->base, &leaf);
     255                if (!frame) {
     256                        unsigned int i;
     257
     258                        /*
     259                         * Workaround for valid NULL address.
     260                         */
     261
     262                        for (i = 0; i < leaf->keys; i++) {
     263                                if (leaf->key[i] == page - area->base) {
     264                                        found = true;
     265                                        break;
     266                                }
     267                        }
     268                }
     269                if (frame || found) {
     270                        frame_reference_add(ADDR2PFN(frame));
     271                        page_mapping_insert(AS, addr, frame,
     272                            as_area_get_flags(area));
     273                        if (!used_space_insert(area, page, 1))
     274                                panic("Cannot insert used space.");
     275                        mutex_unlock(&area->sh_info->lock);
     276                        return AS_PF_OK;
     277                }
     278        }
     279
     280        /*
     281         * The area is either not shared or the pagemap does not contain the
     282         * mapping.
     283         */
     284        if (page >= entry->p_vaddr && page + PAGE_SIZE <= start_anon) {
     285                /*
     286                 * Initialized portion of the segment. The memory is backed
     287                 * directly by the content of the ELF image. Pages are
     288                 * only copied if the segment is writable so that there
     289                 * can be more instantions of the same memory ELF image
     290                 * used at a time. Note that this could be later done
     291                 * as COW.
     292                 */
     293                if (entry->p_flags & PF_W) {
     294                        frame = (uintptr_t)frame_alloc_noreserve(ONE_FRAME, 0);
     295                        memcpy((void *) PA2KA(frame),
     296                            (void *) (base + i * FRAME_SIZE), FRAME_SIZE);
     297                        if (entry->p_flags & PF_X) {
     298                                smc_coherence_block((void *) PA2KA(frame),
     299                                    FRAME_SIZE);
     300                        }
     301                        dirty = true;
     302                } else {
     303                        frame = KA2PA(base + i * FRAME_SIZE);
     304                }       
     305        } else if (page >= start_anon) {
     306                /*
     307                 * This is the uninitialized portion of the segment.
     308                 * It is not physically present in the ELF image.
     309                 * To resolve the situation, a frame must be allocated
     310                 * and cleared.
     311                 */
     312                frame = (uintptr_t) frame_alloc_noreserve(ONE_FRAME, 0);
     313                memsetb((void *) PA2KA(frame), FRAME_SIZE, 0);
     314                dirty = true;
     315        } else {
     316                size_t pad_lo, pad_hi;
     317                /*
     318                 * The mixed case.
     319                 *
     320                 * The middle part is backed by the ELF image and
     321                 * the lower and upper parts are anonymous memory.
     322                 * (The segment can be and often is shorter than 1 page).
     323                 */
     324                if (page < entry->p_vaddr)
     325                        pad_lo = entry->p_vaddr - page;
     326                else
     327                        pad_lo = 0;
     328
     329                if (start_anon < page + PAGE_SIZE)
     330                        pad_hi = page + PAGE_SIZE - start_anon;
     331                else
     332                        pad_hi = 0;
     333
     334                frame = (uintptr_t) frame_alloc_noreserve(ONE_FRAME, 0);
     335                memcpy((void *) (PA2KA(frame) + pad_lo),
     336                    (void *) (base + i * FRAME_SIZE + pad_lo),
     337                    FRAME_SIZE - pad_lo - pad_hi);
     338                if (entry->p_flags & PF_X) {
     339                        smc_coherence_block((void *) (PA2KA(frame) + pad_lo),
     340                            FRAME_SIZE - pad_lo - pad_hi);
     341                }
     342                memsetb((void *) PA2KA(frame), pad_lo, 0);
     343                memsetb((void *) (PA2KA(frame) + FRAME_SIZE - pad_hi), pad_hi,
     344                    0);
     345                dirty = true;
     346        }
     347
     348        if (dirty && area->sh_info) {
     349                frame_reference_add(ADDR2PFN(frame));
     350                btree_insert(&area->sh_info->pagemap, page - area->base,
     351                    (void *) frame, leaf);
     352        }
     353
     354        if (area->sh_info)
     355                mutex_unlock(&area->sh_info->lock);
     356
     357        page_mapping_insert(AS, addr, frame, as_area_get_flags(area));
     358        if (!used_space_insert(area, page, 1))
     359                panic("Cannot insert used space.");
     360
     361        return AS_PF_OK;
     362}
     363
     364/** Free a frame that is backed by the ELF backend.
     365 *
     366 * The address space area and page tables must be already locked.
     367 *
     368 * @param area          Pointer to the address space area.
     369 * @param page          Page that is mapped to frame. Must be aligned to
     370 *                      PAGE_SIZE.
     371 * @param frame         Frame to be released.
     372 *
     373 */
     374void elf_frame_free(as_area_t *area, uintptr_t page, uintptr_t frame)
     375{
     376        elf_segment_header_t *entry = area->backend_data.segment;
     377        uintptr_t start_anon;
     378
     379        ASSERT(page_table_locked(area->as));
     380        ASSERT(mutex_locked(&area->lock));
     381
     382        ASSERT(page >= ALIGN_DOWN(entry->p_vaddr, PAGE_SIZE));
     383        ASSERT(page < entry->p_vaddr + entry->p_memsz);
     384
     385        start_anon = entry->p_vaddr + entry->p_filesz;
     386
     387        if (page >= entry->p_vaddr && page + PAGE_SIZE <= start_anon) {
     388                if (entry->p_flags & PF_W) {
     389                        /*
     390                         * Free the frame with the copy of writable segment
     391                         * data.
     392                         */
     393                        frame_free_noreserve(frame);
     394                }
     395        } else {
     396                /*
     397                 * The frame is either anonymous memory or the mixed case (i.e.
     398                 * lower part is backed by the ELF image and the upper is
     399                 * anonymous). In any case, a frame needs to be freed.
     400                 */
     401                frame_free_noreserve(frame);
     402        }
     403}
     404
    358405/** @}
    359406 */
Note: See TracChangeset for help on using the changeset viewer.