nsc_mem.c revision fcf3ce441efd61da9bb2884968af01cb7c1452cc
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#define __NSC_GEN__
#include "nsc_dev.h"
#include "nsc_gen.h"
#include "nsc_mem.h"
#include "../nsctl.h"
#ifdef DS_DDICT
#include "../contract.h"
#endif
static size_t _nsc_rm_size;
static kmutex_t _nsc_mem_lock;
static nsc_mem_t *_nsc_anon_mem;
static nsc_mem_t *_nsc_rmhdr_mem;
static int _nsc_mem_free(void *, size_t);
static int _nsc_rm_free(void *, size_t);
extern void nscsetup(void);
extern int _nsc_lock_all_rm(void);
extern void _nsc_unlock_all_rm(void);
extern void _nsc_set_max_devices(int);
/*
* void
* _nsc_init_mem (void)
* Initialise memory allocation system.
*
* Called at driver initialisation time to allocate necessary
* data structures.
*/
void
{
if (!_nsc_anon_mem)
}
/*
* void
* _nsc_deinit_mem (void)
* De-initialise memory alloation system.
*
* Called at driver unload time to de-allocate
* resources.
*/
void
{
if (_nsc_rm_nvmem_base)
_nsc_rm_base = NULL;
}
/*
* int
* _nsc_clear_dirty(int force)
* mark the global area clean by clearing the header dirty bit number.
*
* returns 0 if successfully cleared, valid errno otherwise
*
* this function should only be called at system shutdown.
*/
/*ARGSUSED*/
int
_nsc_clear_dirty(int force)
{
int rc = 0;
#ifdef DEBUG
if (force) {
if (_nsc_rm_nvmem_base) {
if (nsc_commit_mem((void *)&longzeros,
(void *)&((nsc_rmhdr_t *)
sizeof (ulong_t), nsc_cm_errhdlr) < 0) {
"nsctl: _nsc_clear_magic: "
"hdr force clear failed 0x%p",
(void *)_nsc_rm_nvmem_base);
} else {
"nsctl: _nsc_clear_magic: "
"hdr force cleared 0x%p",
(void *)_nsc_rm_nvmem_base);
_nsc_rmhdr_ptr->rh_dirty = 0;
}
return (0);
} else
return (EINVAL);
}
if (_nsc_rm_nvmem_base) {
if (_nsc_global_lock_init) {
if (!_nsc_check_mapinuse()) {
if (nsc_commit_mem((void *)&longzeros,
(void *)&((nsc_rmhdr_t *)
sizeof (ulong_t), nsc_cm_errhdlr) < 0) {
"nsctl: _nsc_clear_magic: "
"hdr clear failed 0x%p",
(void *)_nsc_rm_nvmem_base);
} else {
"nsctl: _nsc_clear_magic: "
"hdr cleared 0x%p",
(void *)_nsc_rm_nvmem_base);
_nsc_rmhdr_ptr->rh_dirty = 0;
}
rc = 0;
} else {
"nsctl: _nsc_clear_magic: global area in use. cannot clear magic");
}
} else {
"nsctl: _nsc_clear_magic: cannot clear magic");
}
} else
#else
#endif /* DEBUG */
return (rc);
}
/*
* int
* _nsc_check_mapinuse()
* check if any global maps are still inuse;
*
* return 1 if any non-nsctl map is in use, 0 otherwise
* should be called with _nsc_global_lock held
*
* for nvmem support. if a client of nsctl is still
* using the global maps then the global area will not
* be marked clean.
*/
int
_nsc_check_mapinuse(void)
{
rmapend = (nsc_rmmap_t *)
return (1);
return (0);
}
/* names of maps in the global area that belong to nsctl */
static char *nsctl_mapnames[] = {
"nsc_global",
"nsc_lock"
};
int
_nsc_is_nsctl_map(char *mapname)
{
int i;
for (i = 0; i < sizeof (nsctl_mapnames)/sizeof (char *); ++i)
return (1);
return (0);
}
/*
* nsc_mem_t *
* nsc_register_mem(char *name, int type, int flag)
* Register a category of memory usage.
*
* Returns a token for use in future calls to nsc_kmem_alloc.
* type is NSC_MEM_LOCAL, or NSC_MEM_GLOBAL.
* flag is passed through to kmem_alloc on allocate.
*
* Description:
* The parameters associated with a category can be changed
* by making a subsequent call to nsc_register_mem.
*/
{
break;
return (NULL);
}
_nsc_mem_top = mp;
}
return (mp);
}
/*
* void
* nsc_unregister_mem(nsc_mem_t *)
* Un-register a category of memory usage.
*
* Description:
* The specified category is un-registered. For correct
* operation this should only be called when all memory
* associated with the category has been free'd.
*/
void
{
if (!mp)
return;
break;
}
}
/*
* void
* _nsc_global_setup
* Setup global variables.
*
* Called to setup the global header.
*/
void
{
return;
size = sizeof (nsc_rmhdr_t) +
if (_nsc_rm_nvmem_base) {
"nsctl: _nsc_global_setup: nv bad header");
return;
}
if (nsc_commit_mem((void *)_nsc_rm_base,
(void *)_nsc_rm_nvmem_base,
size, nsc_cm_errhdlr) < 0)
"_nsc_global_setup: "
"nvmem header not updated");
}
}
return;
}
"nsctl: _nsc_global_setup: setting nsc_max_devices to %d",
}
"nsctl: _nsc_global_setup: global map init failed");
return;
}
}
/*
* int
* _nsc_need_global_mem ()
* Expected global memory usage.
*
* Returns the amount of global memory expected to be
* used by internal data structures.
*
* Remarks:
* This is provided purely as a configuration aid to
* systems without global memory and as such is not
* declared in nsctl.h.
*/
int
{
int size = sizeof (nsc_rmhdr_t) +
return (size);
}
/*
* void *
* nsc_kmem_alloc (size_t size, int flag, nsc_mem_t *mem)
* Allocate memory of the specified type.
*
* Returns a pointer to a word aligned area of memory.
* If mem is zero then an anonymous category is used.
*
* Description:
* Allocates the required memory and updates the usage
* statistics stored in mem.
*
* Remarks:
* VME memory is guaranteed to be eight byte aligned.
*/
void *
{
void *vp;
if (!mem)
mem = _nsc_anon_mem;
return (NULL);
return (vp);
}
/*
* void *
* _nsc_mem_alloc (size_t *sizep, int flag, nsc_mem_t *mem)
* Allocate memory of the specified type.
*
* Returns a pointer to a word aligned area of memory.
*
* Description:
* Uses the type field to determine whether to allocate RM,
* VME or kernel memory. For types other then RM a copy of
* mem is stored immediately prior to the returned area.
* size is updated to reflect the header.
*
* Remarks:
* A two word header is user for VME memory to ensure
* eight byte alignment.
*/
static void *
{
void *vp;
if (flag & KM_NOSLEEP)
if (!vp)
return (NULL);
}
/*
* void
* nsc_kmem_free (void *addr, size_t size)
* Free a previously allocated area of memory.
*
* The memory specified by addr is returned to the free pool.
*
* Description:
* Updates the usage statistics appropriately.
*/
void
{
int rc;
if (_nsc_rm_nvmem_base)
else
else
if (rc < 0)
}
/*
* nsc_mem_t *
* _nsc_mem_free (void *addr, size_t size)
* Free a previously allocated area of memory.
*
* Frees the VME or kernel memory at addr and updates
* the associated mem structure.
*/
static int
{
break;
return (-1);
}
return (0);
}
/*
* void *
* nsc_kmem_zalloc(size_t size, int flags, nsc_mem_t *mem)
* Allocate and zero memory.
*
* Same as nsc_kmem_alloc(), except that the memory is zeroed.
*/
void *
{
if (vp)
return (vp);
}
/*
* void
* nsc_mem_sizes (nsc_mem_t *mem, size_t *usedp, size_t *hwmp, size_t *reqp)
* Access size information for category.
*
* If the corresponding pointer is non-zero returns
* respectively, the number of bytes currently allocated, the
* high water mark in bytes and an estimate of the number of
* bytes needed for the category assuming that each request
* is satisfied from a different page.
*
* Remarks:
* The reqp parameter is used to estimate the amount of special
* purpose memory needed to support the category.
*/
void
{
if (!mem)
mem = _nsc_anon_mem;
if (usedp)
if (hwmp)
if (reqp)
}
/*
* size_t
* nsc_mem_avail (nsc_mem_t *mem)
* Memory available for use by category.
*
* Returns the number of bytes of memory currently
* available for use by the category.
*
* Remarks:
* Reduces the memory available to allow for one unit
* of allocation overhead.
*
* Only implemented for NSC_MEM_GLOBAL.
*/
{
if (!mem)
mem = _nsc_anon_mem;
return (_nsc_rm_avail(mem));
#ifdef DEBUG
#endif
return (0);
}
/*
* void
* _nsc_global_zero (ulong_t offset, size_t size)
* Zero global memory.
*
* Description:
* Zeroes an area of global memory at the specified offset.
*/
#define ZSIZE 4096
static char _nsc_nvmem_zeroes[ZSIZE];
static void
{
int i;
int rc;
int failed = 0;
if (_nsc_rm_nvmem_base) {
(void *)(_nsc_rm_nvmem_base + offset +
i * ZSIZE),
if (rc < 0)
++failed;
}
return;
}
if (_nsc_rm_base)
}
/*
* void *
* _nsc_rm_alloc (size_t *sizep, nsc_mem_t *mem)
* Allocate next available section of RM.
*
* Returns a pointer to an area of global memory.
*
* Description:
* Only one allocation request is allowed for each
* category of global memory.
*/
static void *
{
if (!_nsc_global_map) {
return (NULL);
}
return (NULL);
}
/* CONSTCOND */
while (1) {
break;
if (offset)
break;
continue;
}
}
"_nsc_rm_alloc: alloc %ld bytes - %ld available",
return (NULL);
}
if (_nsc_rm_nvmem_base)
else
return (retaddr);
}
/*
* nsc_mem_t *
* _nsc_rm_free (void *addr, size_t size)
* Free an area of RM.
*
* Returns 0 on success, -1 on failure.
*/
static int
{
break;
if (!mp) {
return (-1);
}
if (_nsc_global_map)
return (0);
}
/*
* static size_t
* _nsc_rm_avail (mem)
* Amount of RM available.
*
* Returns 0 if the specified category has already been
* allocated. Returns the size of the region if it already
* exists, otherwise the number of bytes of global memory
* available.
*/
static size_t
{
return (0);
return (size);
return (_nsc_rmmap_avail(_nsc_global_map));
}
/*
* nvram support
* given a map address, return the address of the copy
* in nvram.
* Assumes that _nsc_rm_nvmem_base is valid.
*/
{
/* LINTED */
}
int
{
if (!_nsc_rmhdr_ptr)
return (EINVAL);
sizeof (_nsc_rmhdr_ptr->size)) < 0)
return (EFAULT);
*rvp = 0;
return (0);
}
int
{
if (!_nsc_rmhdr_ptr)
return (EINVAL);
return (EFAULT);
if (_nsc_rm_nvmem_base) {
char *taddr;
return (ENOMEM);
return (EFAULT);
}
}
*rvp = 0;
return (0);
}