/* * pmalloc_construct -- allocates a new block of memory with a constructor * * The block offset is written persistently into the off variable, but only * after the constructor function has been called. * * If successful function returns zero. Otherwise an error number is returned. */ int pmalloc_construct(PMEMobjpool *pop, uint64_t *off, size_t size, void (*constructor)(PMEMobjpool *pop, void *ptr, size_t usable_size, void *arg), void *arg, uint64_t data_off) { int err; struct lane_section *lane; lane_hold(pop, &lane, LANE_SECTION_ALLOCATOR); size_t sizeh = size + sizeof (struct allocation_header); struct bucket *b = heap_get_best_bucket(pop, sizeh); struct memory_block m = {0, 0, 0, 0}; m.size_idx = b->calc_units(b, sizeh); err = heap_get_bestfit_block(pop, b, &m); if (err == ENOMEM && b->type == BUCKET_HUGE) goto out; /* there's only one huge bucket */ if (err == ENOMEM) { /* * There's no more available memory in the common heap and in * this lane cache, fallback to the auxiliary (shared) bucket. */ b = heap_get_auxiliary_bucket(pop, sizeh); err = heap_get_bestfit_block(pop, b, &m); } if (err == ENOMEM) { /* * The auxiliary bucket cannot satisfy our request, borrow * memory from other caches. */ heap_drain_to_auxiliary(pop, b, m.size_idx); err = heap_get_bestfit_block(pop, b, &m); } if (err == ENOMEM) { /* we are completely out of memory */ goto out; } /* * Now that the memory is reserved we can go ahead with making the * allocation persistent. */ uint64_t real_size = b->unit_size * m.size_idx; persist_alloc(pop, lane, m, real_size, off, constructor, arg, data_off); err = 0; out: lane_release(pop); return err; }
/* * alloc_reserve_block -- (internal) reserves a memory block in volatile state * * The first step in the allocation of a new block is reserving it in the * transient heap - which is represented by the bucket abstraction. * * To provide optimal scaling for multi-threaded applications and reduce * fragmentation the appropriate bucket is chosen depending on the current * thread context and to which allocation class the requested size falls into. * * Once the bucket is selected, just enough memory is reserved for the requested * size. The underlying block allocation algorithm (best-fit, next-fit, ...) * varies depending on the bucket container. * * Because the heap in general tries to avoid lock-contention on buckets, * the threads might, in near OOM cases, be unable to allocate requested memory * from their assigned buckets. To combat this there's one common collection * of buckets that threads can fallback to. The auxiliary bucket will 'steal' * memory from other caches if that's required to satisfy the current caller * needs. * * Once this method completes no further locking is required on the transient * part of the heap during the allocation process. */ static int alloc_reserve_block(struct palloc_heap *heap, struct memory_block *m, size_t sizeh) { struct bucket *b = heap_get_best_bucket(heap, sizeh); /* * The caller provided size in bytes, but buckets operate in * 'size indexes' which are multiples of the block size in the bucket. * * For example, to allocate 500 bytes from a bucket that provides 256 * byte blocks two memory 'units' are required. */ m->size_idx = b->calc_units(b, sizeh); int err = heap_get_bestfit_block(heap, b, m); if (err == ENOMEM && b->type == BUCKET_HUGE) return ENOMEM; /* there's only one huge bucket */ if (err == ENOMEM) { /* * There's no more available memory in the common heap and in * this lane cache, fallback to the auxiliary (shared) bucket. */ b = heap_get_auxiliary_bucket(heap, sizeh); err = heap_get_bestfit_block(heap, b, m); } if (err == ENOMEM) { /* * The auxiliary bucket cannot satisfy our request, borrow * memory from other caches. */ heap_drain_to_auxiliary(heap, b, m->size_idx); err = heap_get_bestfit_block(heap, b, m); } if (err == ENOMEM) { /* we are completely out of memory */ return ENOMEM; } return 0; }