Blame IbAccess/UserLinux/Public/imemory_osd.c

Packit 857059
/* BEGIN_ICS_COPYRIGHT5 ****************************************
Packit 857059
Packit 857059
Copyright (c) 2015, Intel Corporation
Packit 857059
Packit 857059
Redistribution and use in source and binary forms, with or without
Packit 857059
modification, are permitted provided that the following conditions are met:
Packit 857059
Packit 857059
    * Redistributions of source code must retain the above copyright notice,
Packit 857059
      this list of conditions and the following disclaimer.
Packit 857059
    * Redistributions in binary form must reproduce the above copyright
Packit 857059
      notice, this list of conditions and the following disclaimer in the
Packit 857059
     documentation and/or other materials provided with the distribution.
Packit 857059
    * Neither the name of Intel Corporation nor the names of its contributors
Packit 857059
      may be used to endorse or promote products derived from this software
Packit 857059
      without specific prior written permission.
Packit 857059
Packit 857059
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
Packit 857059
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
Packit 857059
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
Packit 857059
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
Packit 857059
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
Packit 857059
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
Packit 857059
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
Packit 857059
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
Packit 857059
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
Packit 857059
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Packit 857059
Packit 857059
** END_ICS_COPYRIGHT5   ****************************************/
Packit 857059
Packit 857059
#include "datatypes.h"
Packit 857059
#include "imemory.h"
Packit 857059
#include "imath.h"
Packit 857059
#include "idebug.h"
Packit 857059
#include "iquickmap.h"
Packit 857059
#include "ispinlock.h"
Packit 857059
#include "errno.h"
Packit 857059
#include "string.h"
Packit 857059
#include <sys/mman.h>
Packit 857059
Packit 857059
#ifdef IB_STACK_IBACCESS
Packit 857059
static uint32 s_lock_strategy = 0xffffffff;
Packit 857059
Packit 857059
/* 
Packit 857059
 * Certain redhat kernels use these defines without providing them. 
Packit 857059
 * This allows us to support these features with such kernels.
Packit 857059
 *
Packit 857059
 * Note that this has no impact on kernels that really don't support
Packit 857059
 * this feature.
Packit 857059
 */
Packit 857059
#if !defined(MADV_DONTFORK)
Packit 857059
#define MADV_DONTFORK 10
Packit 857059
#endif
Packit 857059
#if !defined(MADV_DOFORK)
Packit 857059
#define MADV_DOFORK 11
Packit 857059
#endif
Packit 857059
Packit 857059
static FSTATUS
Packit 857059
MemoryLockPrepareMlock(uintn start, uintn length);
Packit 857059
static FSTATUS
Packit 857059
MemoryLockUnprepareMlock(uintn start, uintn length);
Packit 857059
Packit 857059
#endif /* IB_STACK_IBACCESS */
Packit 857059
Packit 857059
void*
Packit 857059
MemoryAllocatePriv( IN uint32 Bytes, IN uint32 flags, IN uint32 Tag )
Packit 857059
{
Packit 857059
    return malloc( Bytes );
Packit 857059
}
Packit 857059
Packit 857059
void
Packit 857059
MemoryDeallocatePriv( IN void *pMemory )
Packit 857059
{
Packit 857059
    free( pMemory );
Packit 857059
}
Packit 857059
Packit 857059
void
Packit 857059
MemoryFill( IN void *pMemory, IN uchar Fill, IN uint32 Bytes )
Packit 857059
{
Packit 857059
    memset( pMemory, Fill, Bytes );
Packit 857059
} 
Packit 857059
Packit 857059
void*
Packit 857059
MemoryCopy( IN void *pDest, IN const void *pSrc, IN uint32 Bytes )
Packit 857059
{
Packit 857059
    return memcpy( pDest, pSrc, Bytes );
Packit 857059
}
Packit 857059
Packit 857059
int32
Packit 857059
MemoryCompare( IN const void *pMemory1, IN const void *pMemory2, IN uint32 Bytes )
Packit 857059
{
Packit 857059
    return memcmp( pMemory1, pMemory2, Bytes );
Packit 857059
} 
Packit 857059
Packit 857059
#ifdef IB_STACK_IBACCESS
Packit 857059
FSTATUS MemoryLockPrepare(uintn buf_org, uintn Length)
Packit 857059
{
Packit 857059
	uintn buf_aligned = 0;
Packit 857059
	uintn size = Length;
Packit 857059
	uint32 page_size = getpagesize();
Packit 857059
	uintn tos_aligned;
Packit 857059
Packit 857059
	buf_aligned = ROUNDDOWNP2(buf_org, page_size);
Packit 857059
	size = size + buf_org - buf_aligned;
Packit 857059
	size = ROUNDUPP2(size, page_size);
Packit 857059
	// This is an odd case, if buf_aligned is on the stack, we need to make sure
Packit 857059
	// the stack itself is at least 1 page beyond the buf itself
Packit 857059
	// we use &tos_aligned as an rough indication of top of stack
Packit 857059
	// if we didn't do this, the VMA created by madvise could actually grow
Packit 857059
	// after we locked the memory, which would confuse the memory locking code
Packit 857059
Packit 857059
	// for stacks which grow down:
Packit 857059
	tos_aligned = ROUNDDOWNP2(&tos_aligned, page_size);
Packit 857059
	if (tos_aligned == buf_aligned)
Packit 857059
	{
Packit 857059
		// force stack to grow by 1 page so TOS is on a different page than buf
Packit 857059
		volatile uint8 *temp = (uint8*)alloca(page_size);
Packit 857059
		if (temp == NULL)
Packit 857059
			return FINSUFFICIENT_MEMORY;
Packit 857059
		*temp = 1;	// touch new page so stack grows
Packit 857059
	}
Packit 857059
#if 0
Packit 857059
	// for stacks which grow up:
Packit 857059
	tos_aligned = ROUNDUPP2(&tos_aligned, page_size);
Packit 857059
	if ( buf_aligned + size == tos_aligned)
Packit 857059
	{
Packit 857059
		// force stack to grow by 1 page so TOS is on a different page than buf
Packit 857059
		volatile uint8 *temp = (uint8*)alloca(page_size);
Packit 857059
		if (temp == NULL)
Packit 857059
			return FINSUFFICIENT_MEMORY;;		
Packit 857059
		*(temp+page_size) = 1;	// touch new page so stack grows
Packit 857059
	}
Packit 857059
#endif
Packit 857059
Packit 857059
	switch (s_lock_strategy) {
Packit 857059
	case MEMORY_LOCK_STRAT_MADVISE:
Packit 857059
#if MLOCK_DBG > 1
Packit 857059
		MsgOut("%s: madvise addr:0x%"PRIxN" size:0x%"PRIxN"\n",__func__,buf_aligned,size);
Packit 857059
#endif
Packit 857059
		if(madvise((void *)buf_aligned, (size_t)size, MADV_SEQUENTIAL ))
Packit 857059
		{
Packit 857059
			MsgOut("%s: madvise failed with buf:0x%"PRIxN" size:%"PRIdN"\n",__func__,buf_aligned,size);
Packit 857059
			return FINSUFFICIENT_RESOURCES;;		
Packit 857059
		}
Packit 857059
		break;
Packit 857059
Packit 857059
	case MEMORY_LOCK_STRAT_MLOCK:
Packit 857059
		return MemoryLockPrepareMlock(buf_aligned, size);
Packit 857059
		break;
Packit 857059
Packit 857059
	default:
Packit 857059
		/* strategy not supported or MemoryLockSetStrategy not called first */
Packit 857059
		return FINVALID_OPERATION;
Packit 857059
	}
Packit 857059
	return FSUCCESS;
Packit 857059
}
Packit 857059
Packit 857059
FSTATUS MemoryLockUnprepare(uintn buf_org, uintn Length)
Packit 857059
{
Packit 857059
	switch (s_lock_strategy) {
Packit 857059
	case MEMORY_LOCK_STRAT_MADVISE:
Packit 857059
		/* no action needed */
Packit 857059
		return FSUCCESS;
Packit 857059
		break;
Packit 857059
Packit 857059
	case MEMORY_LOCK_STRAT_MLOCK:
Packit 857059
		{
Packit 857059
		uint32 page_size = getpagesize();
Packit 857059
		uintn buf_aligned = ROUNDDOWNP2(buf_org,page_size);
Packit 857059
		uintn size = Length + buf_org - buf_aligned;
Packit 857059
		size = ROUNDUPP2(size, page_size);
Packit 857059
		return MemoryLockUnprepareMlock(buf_aligned, size);
Packit 857059
		break;
Packit 857059
		}
Packit 857059
Packit 857059
	default:
Packit 857059
		/* strategy not supported or MemoryLockSetStrategy not called first */
Packit 857059
		return FINVALID_OPERATION;
Packit 857059
	}
Packit 857059
}
Packit 857059
Packit 857059
/* This provides to the User Memory module the memory lock strategy which Uvca
Packit 857059
 * obtained via Vka from the kernel Memory module
Packit 857059
 */
Packit 857059
FSTATUS MemorySetLockStrategy(uint32 strategy)
Packit 857059
{
Packit 857059
	switch (strategy) {
Packit 857059
	case MEMORY_LOCK_STRAT_MADVISE:
Packit 857059
		s_lock_strategy = strategy;
Packit 857059
		return FSUCCESS;
Packit 857059
		break;
Packit 857059
Packit 857059
	case MEMORY_LOCK_STRAT_MLOCK:
Packit 857059
		s_lock_strategy = strategy;
Packit 857059
		return FSUCCESS;
Packit 857059
		break;
Packit 857059
Packit 857059
	default:
Packit 857059
		return FINVALID_PARAMETER;
Packit 857059
		break;
Packit 857059
	}
Packit 857059
}
Packit 857059
Packit 857059
/* Tell UVca if MemoryLockUnprepare is necessary for the configured locking
Packit 857059
 * strategy.  If not, UVCA has the option of optimizing its operation to
Packit 857059
 * avoid the MemoryLockUnprepare.
Packit 857059
 * However, if unnecessary, MemoryLockUnprepare should be a noop and should
Packit 857059
 * not fail (hence allowing UVCA to choose not to optimize it out).
Packit 857059
 */
Packit 857059
boolean MemoryNeedLockUnprepare(void)
Packit 857059
{
Packit 857059
	switch (s_lock_strategy) {
Packit 857059
	case MEMORY_LOCK_STRAT_MADVISE:
Packit 857059
		return FALSE;
Packit 857059
		break;
Packit 857059
	case MEMORY_LOCK_STRAT_MLOCK:
Packit 857059
		return TRUE;
Packit 857059
		break;
Packit 857059
	default:	/* unexpected case, play it safe */
Packit 857059
		return TRUE;
Packit 857059
		break;
Packit 857059
	}
Packit 857059
}
Packit 857059
Packit 857059
Packit 857059
/* ********************************************************************** */
Packit 857059
/* This implements the MEMORY_LOCK_STRAT_MLOCK core functionality
Packit 857059
 * in this strategy the user space uses mlock and madvise prior to
Packit 857059
 * the kernel MemoryLock being called.  Since mlock and madvise do not "stack"
Packit 857059
 * we must track the presently locked regions and only call mlock/madvise for
Packit 857059
 * the "delta" caused by the given lock/unlock of memory.
Packit 857059
 * note that MemoryLockPrepare is called before MemoryLock
Packit 857059
 * and MemoryLockUnprepare is called after MemoryUnlock
Packit 857059
 *
Packit 857059
 * This implementation of MemoryLockPrepare/MemoryLockUnprepare "stacks"
Packit 857059
 * This assumes every MemoryLockUnprepare exactly matches a previous
Packit 857059
 * call to MemoryLockPrepare.  It will assert otherwise.
Packit 857059
 * UVCA follows this requirement.
Packit 857059
 */
Packit 857059
Packit 857059
#define MLOCK_TAG	MAKE_MEM_TAG(k,c,l,m)
Packit 857059
Packit 857059
/* initial testing indicates most applications have relatively few
Packit 857059
 * locked areas at a time, hence the cost of compressing then uncompressing
Packit 857059
 * them (on unlock) is more expensive that having more areas to track and store
Packit 857059
 * especially since the locked_area structure is relatively small and a
Packit 857059
 * cl_qmap is used to search them
Packit 857059
 */
Packit 857059
#define COMPRESS_AREAS 0	/* conserve memory by merging areas when possible */
Packit 857059
Packit 857059
typedef struct _locked_area {
Packit 857059
	cl_map_item_t MapItem;
Packit 857059
	uintn		start;	// virtual address
Packit 857059
	uintn		end;	// last address included in area
Packit 857059
	uint32		lock_cnt;// number of times locked
Packit 857059
} LOCKED_AREA;
Packit 857059
Packit 857059
typedef struct _addr_range {
Packit 857059
	uintn		start;	// virtual address
Packit 857059
	uintn		end;	// last address included in range
Packit 857059
} ADDR_RANGE;
Packit 857059
Packit 857059
static cl_qmap_t	LockedArea_Map;
Packit 857059
static SPIN_LOCK 	LockedAreaMap_Lock;
Packit 857059
#if MLOCK_DBG
Packit 857059
static unsigned s_num_locks = 0;	/* count of MemoryLockPrepare calls */
Packit 857059
static unsigned s_num_split = 0;	/* count of LOCKED_AREAs split */
Packit 857059
static unsigned s_num_merge = 0;	/* count of LOCKED_AREAs merged */
Packit 857059
static unsigned s_max_areas = 0;	/* maximum number of areas */
Packit 857059
#endif
Packit 857059
Packit 857059
#if MLOCK_DBG
Packit 857059
/* dump all the locked areas, must be called with LockedAreaMap_Lock held */
Packit 857059
static void LockedAreaDump(void)
Packit 857059
{
Packit 857059
	cl_map_item_t *pMapItem;
Packit 857059
Packit 857059
	MsgOut("      start              end               cnt\n");
Packit 857059
	for (pMapItem = cl_qmap_head(&LockedArea_Map);
Packit 857059
		 pMapItem != cl_qmap_end(&LockedArea_Map);
Packit 857059
		 pMapItem = cl_qmap_next(pMapItem))
Packit 857059
	{
Packit 857059
		LOCKED_AREA *pArea = PARENT_STRUCT(pMapItem, LOCKED_AREA, MapItem);
Packit 857059
Packit 857059
		MsgOut("0x%16.16"PRIxN" 0x%16.16"PRIxN"  %10u\n", pArea->start, pArea->end, pArea->lock_cnt);
Packit 857059
	}
Packit 857059
}
Packit 857059
#endif
Packit 857059
Packit 857059
/* compare start addresses of 2 entries in map
Packit 857059
 * used as primary sort for LockedArea_Map.  The map should not have any
Packit 857059
 * overlapping areas, hence sort by start address is sufficient
Packit 857059
 * Return:
Packit 857059
 *	-1: key1 < key 2
Packit 857059
 *	 0: key1 = key 2
Packit 857059
 *	 1: key1 > key 2
Packit 857059
 */
Packit 857059
static int LockedAreaCompare(uint64 key1, uint64 key2)
Packit 857059
{
Packit 857059
	LOCKED_AREA *pArea1 = (LOCKED_AREA*)(uintn)key1;
Packit 857059
	LOCKED_AREA *pArea2 = (LOCKED_AREA*)(uintn)key2;
Packit 857059
Packit 857059
	if (pArea1->start < pArea2->start)
Packit 857059
		return -1;
Packit 857059
	else if (pArea1->start == pArea2->start)
Packit 857059
		return 0;
Packit 857059
	else
Packit 857059
		return 1;
Packit 857059
}
Packit 857059
Packit 857059
/* compare address range in key2 against address range in key1
Packit 857059
 * key1 is a LOCKED_AREA*, key2 is a ADDR_RANGE*
Packit 857059
 * Return:
Packit 857059
 *	-1: key1 < key 2 (eg. key 2 is above range of key 1)
Packit 857059
 *	 0: key1 = key 2 (eg. key 2 overlaps with range of key 1)
Packit 857059
 *	 1: key1 > key 2 (eg. key 2 is below range of key 1)
Packit 857059
 * Note that more than 1 range in the LockedArea_Map could overlap
Packit 857059
 * a given value for key2
Packit 857059
 */
Packit 857059
static int LockedAreaRangeCompare(uint64 key1, uint64 key2)
Packit 857059
{
Packit 857059
	LOCKED_AREA *pArea1 = (LOCKED_AREA*)(uintn)key1;
Packit 857059
	ADDR_RANGE *pRange2 = (ADDR_RANGE*)(uintn)key2;
Packit 857059
Packit 857059
	if (pArea1->start > pRange2->end)
Packit 857059
		return 1;
Packit 857059
	else if (pArea1->end < pRange2->start)
Packit 857059
		return -1;
Packit 857059
	else 
Packit 857059
		return 0;
Packit 857059
}
Packit 857059
Packit 857059
/* convert a pMapItem into a pArea, returns NULL if "end marker" */
Packit 857059
static LOCKED_AREA *LockedAreaFromMapItem(cl_map_item_t *pMapItem)
Packit 857059
{
Packit 857059
	if (pMapItem != cl_qmap_end(&LockedArea_Map)) {
Packit 857059
		return PARENT_STRUCT(pMapItem, LOCKED_AREA, MapItem);
Packit 857059
	} else {
Packit 857059
		return NULL;
Packit 857059
	}
Packit 857059
}
Packit 857059
Packit 857059
/* next area after pArea, returns NULL if no areas after pArea */
Packit 857059
static _inline LOCKED_AREA *LockedAreaNext(LOCKED_AREA *pArea)
Packit 857059
{
Packit 857059
	return (LockedAreaFromMapItem(cl_qmap_next(&pArea->MapItem)));
Packit 857059
}
Packit 857059
Packit 857059
/* prev area before pArea, returns NULL if no areas before pArea */
Packit 857059
static _inline LOCKED_AREA *LockedAreaPrev(LOCKED_AREA *pArea)
Packit 857059
{
Packit 857059
	return (LockedAreaFromMapItem(cl_qmap_prev(&pArea->MapItem)));
Packit 857059
}
Packit 857059
Packit 857059
/* must call with Map locked
Packit 857059
 * merges pArea1 and pArea2 (which must be adjacent areas)
Packit 857059
 * returns resulting merged area (Area2 is freed)
Packit 857059
 * cannot fail
Packit 857059
 */
Packit 857059
static LOCKED_AREA *LockedAreaMerge(LOCKED_AREA *pArea1, LOCKED_AREA *pArea2)
Packit 857059
{
Packit 857059
	ASSERT(pArea1->end+1 == pArea2->start);
Packit 857059
	ASSERT(pArea1->lock_cnt == pArea2->lock_cnt);
Packit 857059
	pArea1->end = pArea2->end;
Packit 857059
	cl_qmap_remove_item(&LockedArea_Map, &pArea2->MapItem);
Packit 857059
#if MLOCK_DBG
Packit 857059
	s_num_merge++;
Packit 857059
#endif
Packit 857059
	return pArea1;
Packit 857059
}
Packit 857059
Packit 857059
/* create a new LOCKED_AREA and insert it into LockedArea_Map
Packit 857059
 * returns NULL on memory allocation errors, otherwise pointer to new area
Packit 857059
 */
Packit 857059
static LOCKED_AREA *LockedAreaAlloc(uintn start, uintn end, uint32 lock_cnt)
Packit 857059
{
Packit 857059
	LOCKED_AREA *pNewArea;
Packit 857059
	cl_map_item_t *pMapItem;
Packit 857059
Packit 857059
	pNewArea = (LOCKED_AREA*)MemoryAllocate2(
Packit 857059
			sizeof(LOCKED_AREA), IBA_MEM_FLAG_NONE, MLOCK_TAG);
Packit 857059
	if (! pNewArea)
Packit 857059
		return NULL;
Packit 857059
	pNewArea->start = start;
Packit 857059
	pNewArea->end = end;
Packit 857059
	pNewArea->lock_cnt = lock_cnt;
Packit 857059
	pMapItem = cl_qmap_insert(&LockedArea_Map, (uintn)pNewArea, &pNewArea->MapItem);
Packit 857059
	// assert new area is unique
Packit 857059
	ASSERT(pMapItem == &pNewArea->MapItem);
Packit 857059
#if MLOCK_DBG
Packit 857059
	if (cl_qmap_count(&LockedArea_Map) > s_max_areas)
Packit 857059
		s_max_areas = cl_qmap_count(&LockedArea_Map);
Packit 857059
#endif
Packit 857059
	return pNewArea;
Packit 857059
}
Packit 857059
Packit 857059
/* must call with Map locked
Packit 857059
 * splits pArea into 2 areas at addr
Packit 857059
 * 1st area will be pArea->start to addr-1
Packit 857059
 * 2nd area will be addr to pArea->end
Packit 857059
 * returns 2nd area (pArea becomes 1st area)
Packit 857059
 * returns NULL on failure to allocate memory
Packit 857059
 */
Packit 857059
static LOCKED_AREA *LockedAreaSplit(LOCKED_AREA *pArea, uintn addr)
Packit 857059
{
Packit 857059
	LOCKED_AREA *pNewArea;
Packit 857059
	cl_map_item_t *pMapItem;
Packit 857059
Packit 857059
	ASSERT(pArea->start < addr);
Packit 857059
	ASSERT(pArea->end >= addr);
Packit 857059
	pNewArea = (LOCKED_AREA*)MemoryAllocate2(
Packit 857059
			sizeof(LOCKED_AREA), IBA_MEM_FLAG_NONE, MLOCK_TAG);
Packit 857059
	if (! pNewArea)
Packit 857059
		return NULL;
Packit 857059
	pNewArea->start = addr;
Packit 857059
	pNewArea->end = pArea->end;
Packit 857059
	pNewArea->lock_cnt = pArea->lock_cnt;
Packit 857059
	pArea->end = addr-1;	// must fixup before insert pNewArea
Packit 857059
	pMapItem = cl_qmap_insert(&LockedArea_Map, (uintn)pNewArea, &pNewArea->MapItem);
Packit 857059
	ASSERT(pMapItem == &pNewArea->MapItem); /* assert new area is unique */
Packit 857059
#if MLOCK_DBG
Packit 857059
	s_num_split++;
Packit 857059
	if (cl_qmap_count(&LockedArea_Map) > s_max_areas)
Packit 857059
		s_max_areas = cl_qmap_count(&LockedArea_Map);
Packit 857059
#endif
Packit 857059
	return pNewArea;
Packit 857059
}
Packit 857059
Packit 857059
/* must call with Map locked
Packit 857059
 * find the 1st area in LockedArea_Map which overlaps start-end
Packit 857059
 * returns NULL if no overlapping area(s)
Packit 857059
 */
Packit 857059
static LOCKED_AREA *FindFirstOverlap(uintn start, uintn end)
Packit 857059
{
Packit 857059
	ADDR_RANGE Range;
Packit 857059
	cl_map_item_t *pMapItem;
Packit 857059
	LOCKED_AREA *pArea;
Packit 857059
Packit 857059
	Range.start = start;
Packit 857059
	Range.end = end;
Packit 857059
Packit 857059
	/* first find if any locked areas overlap the Range */
Packit 857059
	pMapItem = cl_qmap_get_compare(&LockedArea_Map, (uint64)(uintn)&Range, LockedAreaRangeCompare);
Packit 857059
	if (pMapItem == cl_qmap_end(&LockedArea_Map)) {
Packit 857059
		/* no overlap with existing locks */
Packit 857059
		return NULL;
Packit 857059
	}
Packit 857059
	pArea = PARENT_STRUCT(pMapItem, LOCKED_AREA, MapItem);
Packit 857059
	/* because RangeCompare is not unique, we could get any area which
Packit 857059
	 * overlaps, so we must back up to find first area which overlaps
Packit 857059
	 */
Packit 857059
	while ((pMapItem = cl_qmap_prev(&pArea->MapItem)) != cl_qmap_end(&LockedArea_Map)) {
Packit 857059
		if (LockedAreaRangeCompare(cl_qmap_key(pMapItem), (uint64)(uintn)&Range) != 0) {
Packit 857059
			// no overlap, pArea is 1st to overlap Range
Packit 857059
			break;
Packit 857059
		}
Packit 857059
		pArea = PARENT_STRUCT(pMapItem, LOCKED_AREA, MapItem);
Packit 857059
	}
Packit 857059
	/* now pArea is 1st area to overlap Range */
Packit 857059
	return pArea;
Packit 857059
}
Packit 857059
Packit 857059
/* account for the locking/unlocking of start-end in pArea
Packit 857059
 * as needed we could split pArea into up to 2 pieces
Packit 857059
 * the returned value is the area which overlaps desired start/end
Packit 857059
 * returns NULL on error
Packit 857059
 */
Packit 857059
static LOCKED_AREA *LockedAreaAddLock(LOCKED_AREA *pArea, uintn start, uintn end, uint32 add)
Packit 857059
{
Packit 857059
	LOCKED_AREA *p = NULL;
Packit 857059
Packit 857059
	if (pArea->start < start) {
Packit 857059
		ASSERT(pArea->end >= start);
Packit 857059
		p = pArea;	// save for recovery
Packit 857059
		pArea = LockedAreaSplit(pArea, start);	// 2nd half
Packit 857059
		if (! pArea)
Packit 857059
			return NULL;	// failed
Packit 857059
	}
Packit 857059
	if (pArea->end > end) {
Packit 857059
		ASSERT(pArea->start <= end);
Packit 857059
		if (! LockedAreaSplit(pArea, end+1)) {
Packit 857059
			// error, undo split above
Packit 857059
			if (p)
Packit 857059
				(void)LockedAreaMerge(p, pArea);
Packit 857059
			return NULL;	// failed
Packit 857059
		}
Packit 857059
		// pArea is still 1st half
Packit 857059
	}
Packit 857059
	pArea->lock_cnt += add;
Packit 857059
	return pArea;
Packit 857059
}
Packit 857059
Packit 857059
static _inline LOCKED_AREA *LockedAreaIncLock(LOCKED_AREA *pArea, uintn start, uintn end)
Packit 857059
{
Packit 857059
	return LockedAreaAddLock(pArea, start, end, 1);
Packit 857059
}
Packit 857059
Packit 857059
static _inline LOCKED_AREA *LockedAreaDecLock(LOCKED_AREA *pArea, uintn start, uintn end)
Packit 857059
{
Packit 857059
	return LockedAreaAddLock(pArea, start, end, (uint32)-1);
Packit 857059
}
Packit 857059
Packit 857059
/* create a new locked area covering start-end */
Packit 857059
static LOCKED_AREA *LockedAreaCreate(uintn start, uintn end)
Packit 857059
{
Packit 857059
	LOCKED_AREA *pArea;
Packit 857059
	uintn length = (end-start)+1;
Packit 857059
Packit 857059
#if MLOCK_DBG > 1
Packit 857059
	MsgOut("%s: mlock addr:0x%"PRIxN" size:0x%"PRIxN"\n",__func__,start,length);
Packit 857059
#endif
Packit 857059
	if (mlock((void*)start, (size_t)length) < 0) {
Packit 857059
		MsgOut("mlock failed: start=0x%p, len=%u, errno=%s (%d)\n",
Packit 857059
			(void*)start, (unsigned)length, strerror(errno), errno);
Packit 857059
		return NULL;
Packit 857059
	}
Packit 857059
#if MLOCK_DBG > 1
Packit 857059
	MsgOut("%s: madvise DONTFORK addr:0x%"PRIxN" size:0x%"PRIxN"\n",__func__,start,length);
Packit 857059
#endif
Packit 857059
	if (madvise((void*)start, (size_t)length, MADV_DONTFORK) < 0) {
Packit 857059
		MsgOut("madvise DONTFORK failed: start=0x%p, len=%u, errno=%s (%d)\n",
Packit 857059
			(void*)start, (unsigned)length, strerror(errno), errno);
Packit 857059
		(void)munlock((void*)start, (size_t)length);
Packit 857059
		return NULL;
Packit 857059
	}
Packit 857059
	pArea = LockedAreaAlloc(start, end, 1);
Packit 857059
	if (! pArea) {
Packit 857059
		(void)madvise((void*)start, (size_t)length, MADV_DOFORK);
Packit 857059
		(void)munlock((void*)start, (size_t)length);
Packit 857059
	}
Packit 857059
	return pArea;
Packit 857059
}
Packit 857059
Packit 857059
/* destroy a locked area */
Packit 857059
static void LockedAreaDestroy(LOCKED_AREA *pArea)
Packit 857059
{
Packit 857059
	uintn length = (pArea->end - pArea->start)+1;
Packit 857059
#if MLOCK_DBG > 1
Packit 857059
	MsgOut("%s: madvise DOFORK addr:0x%"PRIxN" size:0x%"PRIxN"\n",__func__,pArea->start,length);
Packit 857059
#endif
Packit 857059
	(void)madvise((void*)pArea->start, (size_t)length, MADV_DOFORK);
Packit 857059
#if MLOCK_DBG > 1
Packit 857059
	MsgOut("%s: munlock addr:0x%"PRIxN" size:0x%"PRIxN"\n",__func__,pArea->start,length);
Packit 857059
#endif
Packit 857059
	(void)munlock((void*)pArea->start, (size_t)length);
Packit 857059
	cl_qmap_remove_item(&LockedArea_Map, &pArea->MapItem);
Packit 857059
	MemoryDeallocate(pArea);
Packit 857059
}
Packit 857059
Packit 857059
#if COMPRESS_AREAS
Packit 857059
/* merge Areas with same lock count.  This saves space but
Packit 857059
 * may make for more work in unlock
Packit 857059
 */
Packit 857059
static void CompressAreas(LOCKED_AREA *pFirstArea, uintn end)
Packit 857059
{
Packit 857059
	LOCKED_AREA *pArea;
Packit 857059
Packit 857059
	ASSERT(pFirstArea);
Packit 857059
	/* start merge process with 1st area before this lock (if any) */
Packit 857059
	pArea = LockedAreaPrev(pFirstArea);
Packit 857059
	if (pArea)
Packit 857059
		pFirstArea = pArea;
Packit 857059
	while (pFirstArea->start <= end) {
Packit 857059
		pArea = LockedAreaNext(pFirstArea);
Packit 857059
		if (! pArea)
Packit 857059
			break;
Packit 857059
		if (pArea->lock_cnt == pFirstArea->lock_cnt
Packit 857059
			&& pFirstArea->end+1 == pArea->start)
Packit 857059
			pFirstArea = LockedAreaMerge(pFirstArea, pArea);
Packit 857059
		else
Packit 857059
			pFirstArea = pArea;
Packit 857059
	}
Packit 857059
}
Packit 857059
#endif /* COMPRESS_AREAS */
Packit 857059
Packit 857059
Packit 857059
/* heart of managing mlock/madvise locked areas.
Packit 857059
 * start and length must be pagesize aligned
Packit 857059
 */
Packit 857059
static FSTATUS
Packit 857059
MemoryLockPrepareMlock(uintn start, uintn length)
Packit 857059
{
Packit 857059
	LOCKED_AREA *pArea;
Packit 857059
#if COMPRESS_AREAS
Packit 857059
	LOCKED_AREA *pFirstArea;
Packit 857059
#endif
Packit 857059
	uintn end = start + length-1;
Packit 857059
	uintn addr;
Packit 857059
Packit 857059
#if MLOCK_DBG > 1
Packit 857059
	MsgOut("%s: addr:0x%"PRIxN" size:0x%"PRIxN"\n",__func__,start,length);
Packit 857059
#endif
Packit 857059
	if (! length)
Packit 857059
		return FSUCCESS;
Packit 857059
// TBD - should we just mlock and madvise whole area 1st outside spin lock?
Packit 857059
// however error unwind would be impossible if madvise failed?
Packit 857059
// does kernel properly count mlock if done twice to same page?
Packit 857059
	SpinLockAcquire(&LockedAreaMap_Lock);
Packit 857059
#if MLOCK_DBG
Packit 857059
	s_num_locks++;
Packit 857059
#endif
Packit 857059
	pArea = FindFirstOverlap(start, end);
Packit 857059
#if COMPRESS_AREAS
Packit 857059
	pFirstArea = NULL;
Packit 857059
#endif
Packit 857059
	for (addr=start; addr<=end; ) {
Packit 857059
		if (! pArea) {
Packit 857059
			pArea = LockedAreaCreate(addr, end);
Packit 857059
		} else if (pArea->start > addr) {
Packit 857059
			// fill gap
Packit 857059
			pArea = LockedAreaCreate(addr, MIN(end, pArea->start-1));
Packit 857059
		} else {
Packit 857059
			pArea = LockedAreaIncLock(pArea, addr, end);
Packit 857059
		}
Packit 857059
		if (! pArea) {
Packit 857059
			// error, unwind what we did so far
Packit 857059
			SpinLockRelease(&LockedAreaMap_Lock);
Packit 857059
#if MLOCK_DBG
Packit 857059
			MsgOut("failure for MemoryLockPrepare(0x%"PRIxN", 0x%"PRIxN") for 0x%"PRIxN"-0x%"PRIxN"\n", start, length, addr, end);
Packit 857059
			LockedAreaDump();
Packit 857059
			MsgOut("MemoryLockUnprepare(0x%"PRIxN", 0x%"PRIxN") for 0x%"PRIxN"-0x%"PRIxN"\n", start, length, addr, end);
Packit 857059
#endif
Packit 857059
			if (addr != start)
Packit 857059
				(void)MemoryLockUnprepareMlock(start, addr-start);	
Packit 857059
			return FINSUFFICIENT_RESOURCES;
Packit 857059
		}
Packit 857059
		// adjust to reflect what is left to do
Packit 857059
		addr = pArea->end+1;
Packit 857059
#if COMPRESS_AREAS
Packit 857059
		if (! pFirstArea)
Packit 857059
			pFirstArea = pArea;
Packit 857059
#endif
Packit 857059
		pArea = LockedAreaNext(pArea);
Packit 857059
	}
Packit 857059
#if COMPRESS_AREAS
Packit 857059
	/* merge Areas with same lock count.  This saves space but
Packit 857059
	 * may make for more work in unlock
Packit 857059
	 */
Packit 857059
	ASSERT(pFirstArea);
Packit 857059
	CompressAreas(pFirstArea, end);
Packit 857059
#endif
Packit 857059
#if MLOCK_DBG > 1
Packit 857059
	LockedAreaDump();
Packit 857059
#endif
Packit 857059
	SpinLockRelease(&LockedAreaMap_Lock);
Packit 857059
	return FSUCCESS;
Packit 857059
}
Packit 857059
Packit 857059
/* start and length must be pagesize aligned
Packit 857059
 */
Packit 857059
static FSTATUS
Packit 857059
MemoryLockUnprepareMlock(uintn start, uintn length)
Packit 857059
{
Packit 857059
	LOCKED_AREA *pArea;
Packit 857059
	LOCKED_AREA *p;
Packit 857059
#if COMPRESS_AREAS
Packit 857059
	LOCKED_AREA *pFirstArea;
Packit 857059
#endif
Packit 857059
	uintn end = start + length-1;
Packit 857059
	uintn addr;
Packit 857059
Packit 857059
#if MLOCK_DBG > 1
Packit 857059
	MsgOut("%s: addr:0x%"PRIxN" size:0x%"PRIxN"\n",__func__,start,length);
Packit 857059
#endif
Packit 857059
	if (! length)
Packit 857059
		return FSUCCESS;
Packit 857059
	SpinLockAcquire(&LockedAreaMap_Lock);
Packit 857059
	pArea = FindFirstOverlap(start, end);
Packit 857059
#if COMPRESS_AREAS
Packit 857059
	pFirstArea = NULL;
Packit 857059
#endif
Packit 857059
	for (addr=start; addr<=end; ) {
Packit 857059
		ASSERT(pArea);
Packit 857059
		ASSERT(pArea->start <= addr);
Packit 857059
		pArea = LockedAreaDecLock(pArea, addr, end);
Packit 857059
#if COMPRESS_AREAS
Packit 857059
		if (! pArea) {
Packit 857059
			// stuck, can't really re-lock it, so leave it
Packit 857059
			// partially unlocked and give up
Packit 857059
			DbgOut("Unable to unlock: addr=0x%p, end=0x%p\n",
Packit 857059
				(void*)addr, (void*)end);
Packit 857059
			break;
Packit 857059
		}
Packit 857059
#else
Packit 857059
		// since never merge, should be no need to split, so should not fail
Packit 857059
		ASSERT(pArea);
Packit 857059
#endif
Packit 857059
		// adjust to reflect what is left to do
Packit 857059
		addr = pArea->end+1;
Packit 857059
		p = LockedAreaNext(pArea);
Packit 857059
		if (! pArea->lock_cnt) {
Packit 857059
			LockedAreaDestroy(pArea);
Packit 857059
#if COMPRESS_AREAS
Packit 857059
		} else {
Packit 857059
			if (! pFirstArea)
Packit 857059
				pFirstArea = pArea;
Packit 857059
#endif
Packit 857059
		}
Packit 857059
		pArea = p;
Packit 857059
	}
Packit 857059
#if COMPRESS_AREAS
Packit 857059
	/* merge Areas with same lock count */
Packit 857059
	if (pFirstArea)
Packit 857059
		CompressAreas(pFirstArea, end);
Packit 857059
#endif
Packit 857059
#if MLOCK_DBG > 1
Packit 857059
	LockedAreaDump();
Packit 857059
#endif
Packit 857059
	SpinLockRelease(&LockedAreaMap_Lock);
Packit 857059
	return FSUCCESS;
Packit 857059
}
Packit 857059
/* end of MEMORY_LOCK_STRAT_MLOCK specific code */
Packit 857059
/* ********************************************************************** */
Packit 857059
Packit 857059
Packit 857059
FSTATUS
Packit 857059
MemoryLockPrepareInit(void)
Packit 857059
{
Packit 857059
Packit 857059
	cl_qmap_init(&LockedArea_Map, LockedAreaCompare);
Packit 857059
	SpinLockInitState( &LockedAreaMap_Lock );
Packit 857059
Packit 857059
	if ( !SpinLockInit ( &LockedAreaMap_Lock ) )
Packit 857059
	{
Packit 857059
		MsgOut ("MemoryLockPrepareInit: Locked Area SpinLock init failed\n");
Packit 857059
		return FERROR;
Packit 857059
	}
Packit 857059
Packit 857059
Packit 857059
	return FSUCCESS;
Packit 857059
}
Packit 857059
Packit 857059
void
Packit 857059
MemoryLockPrepareCleanup(void)
Packit 857059
{
Packit 857059
Packit 857059
#if MLOCK_DBG
Packit 857059
	MsgOut("MLock Stats: numLocks: %u, maxAreas: %u, numSplit: %u, numMerge: %u\n",
Packit 857059
			s_num_locks, s_max_areas, s_num_split, s_num_merge);
Packit 857059
#endif
Packit 857059
	if (! cl_is_qmap_empty(&LockedArea_Map)) {
Packit 857059
		DbgOut("MemoryLockPrepareCleanup: Locked Area Map not empty\n");
Packit 857059
	}
Packit 857059
	SpinLockDestroy( &LockedAreaMap_Lock );
Packit 857059
Packit 857059
} 
Packit 857059
#endif /* IB_STACK_IBACCESS */