Coherent4.2.10/conf/streams/src/strmem.c

Compare this file to the similar file:
Show the results in this format:

#define	_DDI_DKI	1
#define	_SYSV4		1

/*
 * STREAMS memory management code.
 *
 * This is layered on top of the fast first-fit heap allocator whose
 * implementation is described in <sys/st_alloc.h>. The particulars of how
 * STREAMS memory is allocated (including synchronisation and watermarks)
 * is kept here so that the generic allocator is just that, generic.
 */

/*
 *-IMPORTS:
 *	<common/ccompat.h>
 *		__USE_PROTO__
 *		__ARGS ()
 *		__LOCAL__
 *	<kernel/ddi_lock.h>
 *		stream_heap_hierarchy
 *		stream_dir_hierarchy
 *		stream_proc_hierarchy
 *	<sys/debug.h>
 *		ASSERT ()
 *	<sys/types.h>
 *		_VOID
 *		size_t
 *	<sys/ksynch.h>
 *		lock_t
 *		LOCK_ALLOC ()
 *		LOCK ()
 *		UNLOCK ()
 *	<sys/cmn_err.h>
 *		CE_WARN
 *		cmn_err ()
 */

#include <common/ccompat.h>
#include <kernel/ddi_lock.h>
#include <sys/types.h>
#include <sys/debug.h>
#include <sys/ksynch.h>
#include <sys/cmn_err.h>

#include <sys/kmem.h>
#include <kernel/strmlib.h>
#include <string.h>


/*
 * Here we'll define the actual instance of the streams memory control
 * structure.
 */

struct streams_mem str_mem [1];


/*
 * We need to define information structures for the various locks and
 * synchronization variables used in the above.
 */

__LOCAL__ lkinfo_t _stream_heap_lkinfo = {
	"STREAMS message memory lock", INTERNAL_LOCK
};

__LOCAL__ lkinfo_t _stream_seq_lkinfo = {
	"STREAMS log sequence-number lock", INTERNAL_LOCK
};

__LOCAL__ lkinfo_t _stream_proc_lkinfo = {
	"STREAMS qprocsoff () lock", INTERNAL_LOCK
};

__LOCAL__ lkinfo_t _stream_dir_lkinfo = {
	"STREAM directory read/write lock", INTERNAL_LOCK
};


/*
 * This private function gathers some of the aspects of streams message memory
 * allocation into a single place (message blocks are allocated in allocb (),
 * dupb (), and esballoc ()). We leave the initialization of the newly
 * allocated memory up to the caller.
 */

#if	__USE_PROTO__
mblk_t * (STRMEM_ALLOC) (size_t size, int pri, int flag)
#else
mblk_t *
STRMEM_ALLOC __ARGS ((size, pri, flag))
size_t		size;
int		pri;
int		flag;
#endif
{
	pl_t		prev_pl;
	mblk_t	      *	mblkp;

	ASSERT (size > 0);
	ASSERT (flag == KM_SLEEP || flag == KM_NOSLEEP);

	/*
	 * Note that if the size is one such that it cannot possibly ever be
	 * satisfied given the allocation watermarks we have set, then we just
	 * return failure now.
	 */

	pri = MAP_PRI_LEVEL (pri);

	if (size > str_mem->sm_max [pri])
		return NULL;


	for (;;) {
		/*
		 * Lock the basic lock protecting access to the memory pool
		 * and attempt to acquire the memory we desire.
		 */

		prev_pl = LOCK (str_mem->sm_msg_lock, str_msg_pl);


		/*
		 * Before allocating any memory, we check to see that it makes
		 * sense to give out that memory to the given priority level.
		 */

		if (str_mem->sm_used + size <= str_mem->sm_max [pri]) {
			/*
			 * Try to get the memory, and if we do update the
			 * priority bookkeeping information to record the
			 * amount of memory that we have allowed out.
			 */

			mblkp = (mblk_t *) st_alloc (str_mem->sm_msg_heap,
						     size);

			if (mblkp != NULL) {

				str_mem->sm_used += size;
				break;
			}
		}


		/*
		 * Depending on the caller, we may sleep waiting for memory
		 * to become available.
		 */

		if (flag == KM_NOSLEEP) {

			mblkp = NULL;
			break;
		}


		/*
		 * RESEARCH NOTE: This policy is a guess, no more. We need to
		 * do some profiling to find out what effect other policies
		 * might have. In particular, the wakeup heuristic could be
		 * altered to broadcast when we can satisfy the largest
		 * request.
		 */

		if (str_mem->sm_msg_needed == 0 ||
		    str_mem->sm_msg_needed > size)
			str_mem->sm_msg_needed = size;

		SV_WAIT (str_mem->sm_msg_sv, prilo, str_mem->sm_msg_lock);
	}

	UNLOCK (str_mem->sm_msg_lock, prev_pl);

	return mblkp;
}


/*
 * This simple function factors out some common code from different calls to
 * st_free () inside freeb (). This just reduces some of the cost of all the
 * error checking that is my custom, and consolidates the interface to the
 * bookkeeping for callback events and so forth.
 *
 * The streams heap must be locked on entry to this function.
 */

#if	__USE_PROTO__
void (STRMEM_FREE) (mblk_t * bp, size_t size)
#else
void
STRMEM_FREE __ARGS ((bp, size))
mblk_t	      *	bp;
size_t		size;
#endif
{
	int		free_ok;

	ASSERT (TRYLOCK (str_mem->sm_msg_lock, str_msg_pl) == invpl);

	free_ok = st_free (str_mem->sm_msg_heap, bp, size);

	if (free_ok != 0) {
		/*
		 * The heap manager has a problem with freeing the block that
		 * was passed to it, display a console diagnostic. For
		 * simplicity we display addresses as longs.
		 */

		cmn_err (CE_WARN,
			 "MSGB_FREE : st_free () complained with %d freeing %d bytes at %lx",
			 free_ok, size, (long) bp);
	} else {
		/*
		 * Update the allocation bookkeeping, and request that the
		 * routine that processes bufcall () events be run. This other
		 * procedure also has responsibility for waking up message and
		 * possibly "other" allocations if there is sufficient memory
		 * available.
		 */

		str_mem->sm_used -= size;

		SCHEDULE_BUFCALLS ();
	}
}


__EXTERN_C__
#if	__USE_PROTO__
int (STRMEM_INIT) (__VOID__ * addr, size_t size)
#else
int
STRMEM_INIT __ARGS ((addr, size))
__VOID__      *	addr;
size_t		size;
#endif
{
	int		i;

	/*
	 * Now initialize the fast-first-fit heap manager.
	 */

	str_mem->sm_msg_heap = st_heap_init (addr, size);

	str_mem->sm_msg_lock =
			LOCK_ALLOC (stream_heap_hierarchy, str_msg_pl,
				    & _stream_heap_lkinfo, KM_NOSLEEP);

	str_mem->sm_msg_sv = SV_ALLOC (KM_NOSLEEP);


	/*
	 * If either of the above allocations failed, we have some kind of
	 * major problem, so we exit without unlocking the initialization flag
	 * with an error indication.
	 */

	if (str_mem->sm_msg_lock == NULL || str_mem->sm_msg_sv == NULL) {

init_error:
		cmn_err (CE_PANIC, "Could not initialize STREAMS subsystem");
		return -1;
	}


	/*
	 * Now we can calculate the watermarks... start at the
	 * top and make each lower one some percentage of the
	 * next higher one (say, 15/16 or 93%, so that it's
	 * easy to calculate).
	 */

	for (i = N_PRI_LEVELS ; i -- > 0 ;) {

		str_mem->sm_max [i] = size;
		size -= size >> 4;	/* - 1/16 */
	}


	/*
	 * Do other kinds of initialization for the "str_mem" structure.
	 */

	for (i = N_PRI_LEVELS ; i -- > 0 ; ) {

		if (SELIST_INIT (& str_mem->sm_bcevents [i],
				 KM_SLEEP) == NULL)
			goto init_error;
	}


	str_mem->sm_seq_lock = LOCK_ALLOC (stream_seq_hierarchy, plstr,
					   & _stream_seq_lkinfo, KM_SLEEP);

	str_mem->sm_head_lock = RW_ALLOC (stream_dir_hierarchy, plstr,
					  & _stream_dir_lkinfo, KM_SLEEP);

	str_mem->sm_proc_lock = LOCK_ALLOC (stream_proc_hierarchy, plstr,
					    & _stream_proc_lkinfo, KM_SLEEP);

	str_mem->sm_proc_sv = SV_ALLOC (KM_SLEEP);

	if (SCHED_INIT (str_mem->sm_sched, KM_SLEEP) == NULL ||
	    str_mem->sm_seq_lock == NULL || str_mem->sm_head_lock == NULL ||
	    str_mem->sm_proc_lock == NULL || str_mem->sm_proc_sv == NULL)
		goto init_error;

	return 0;		/* all OK */
}