Ejemplo n.º 1
0
/*
 * 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;
}
Ejemplo n.º 2
0
/*
 * 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;
}