#include <sys/types.h>
#include <sys/cmn_err.h>
#include <sys/mutex.h>
#include <sys/param.h>
#include <sys/debug.h>
#include <sys/memlist.h>
#include <sys/memlist_impl.h>
static struct memlist *memlist_freelist;
static uint_t memlist_freelist_count;
static kmutex_t memlist_freelist_mutex;
struct memlist *
memlist_get_one(void)
{
struct memlist *mlp;
mutex_enter(&memlist_freelist_mutex);
mlp = memlist_freelist;
if (mlp != NULL) {
memlist_freelist = mlp->ml_next;
memlist_freelist_count--;
}
mutex_exit(&memlist_freelist_mutex);
return (mlp);
}
void
memlist_free_one(struct memlist *mlp)
{
ASSERT(mlp != NULL);
mutex_enter(&memlist_freelist_mutex);
mlp->ml_next = memlist_freelist;
memlist_freelist = mlp;
memlist_freelist_count++;
mutex_exit(&memlist_freelist_mutex);
}
void
memlist_free_list(struct memlist *mlp)
{
struct memlist *mlendp;
uint_t count;
if (mlp == NULL) {
return;
}
count = 1;
for (mlendp = mlp; mlendp->ml_next != NULL; mlendp = mlendp->ml_next)
count++;
mutex_enter(&memlist_freelist_mutex);
mlendp->ml_next = memlist_freelist;
memlist_freelist = mlp;
memlist_freelist_count += count;
mutex_exit(&memlist_freelist_mutex);
}
void
memlist_free_block(caddr_t base, size_t bytes)
{
struct memlist *mlp, *mlendp;
uint_t count;
count = bytes / sizeof (struct memlist);
if (count == 0)
return;
mlp = (struct memlist *)base;
mlendp = &mlp[count - 1];
for (; mlp != mlendp; mlp++)
mlp->ml_next = mlp + 1;
mlendp->ml_next = NULL;
mlp = (struct memlist *)base;
mutex_enter(&memlist_freelist_mutex);
mlendp->ml_next = memlist_freelist;
memlist_freelist = mlp;
memlist_freelist_count += count;
mutex_exit(&memlist_freelist_mutex);
}
void
memlist_insert(
struct memlist *new,
struct memlist **curmemlistp)
{
struct memlist *cur, *last;
uint64_t start, end;
start = new->ml_address;
end = start + new->ml_size;
last = NULL;
for (cur = *curmemlistp; cur; cur = cur->ml_next) {
last = cur;
if (cur->ml_address >= end) {
new->ml_next = cur;
new->ml_prev = cur->ml_prev;
cur->ml_prev = new;
if (cur == *curmemlistp)
*curmemlistp = new;
else
new->ml_prev->ml_next = new;
return;
}
if (cur->ml_address + cur->ml_size > start)
panic("munged memory list = 0x%p\n",
(void *)curmemlistp);
}
new->ml_next = NULL;
new->ml_prev = last;
if (last != NULL) {
last->ml_next = new;
} else {
ASSERT3P(*curmemlistp, ==, NULL);
*curmemlistp = new;
}
}
void
memlist_del(struct memlist *memlistp,
struct memlist **curmemlistp)
{
#ifdef DEBUG
struct memlist *mlp;
for (mlp = *curmemlistp; mlp != NULL; mlp = mlp->ml_next)
if (mlp == memlistp)
break;
ASSERT(mlp == memlistp);
#endif
if (*curmemlistp == memlistp) {
ASSERT(memlistp->ml_prev == NULL);
*curmemlistp = memlistp->ml_next;
}
if (memlistp->ml_prev != NULL) {
ASSERT(memlistp->ml_prev->ml_next == memlistp);
memlistp->ml_prev->ml_next = memlistp->ml_next;
}
if (memlistp->ml_next != NULL) {
ASSERT(memlistp->ml_next->ml_prev == memlistp);
memlistp->ml_next->ml_prev = memlistp->ml_prev;
}
}
struct memlist *
memlist_find(struct memlist *mlp, uint64_t address)
{
for (; mlp != NULL; mlp = mlp->ml_next)
if (address >= mlp->ml_address &&
address < (mlp->ml_address + mlp->ml_size))
break;
return (mlp);
}
int
memlist_add_span(
uint64_t address,
uint64_t bytes,
struct memlist **curmemlistp)
{
struct memlist *dst;
struct memlist *prev, *next;
dst = memlist_get_one();
if (dst == NULL) {
return (MEML_SPANOP_EALLOC);
}
dst->ml_address = address;
dst->ml_size = bytes;
if (*curmemlistp == NULL) {
dst->ml_prev = NULL;
dst->ml_next = NULL;
*curmemlistp = dst;
return (MEML_SPANOP_OK);
}
for (prev = NULL, next = *curmemlistp; next != NULL;
prev = next, next = next->ml_next) {
if (address > (next->ml_address + next->ml_size))
continue;
if ((address + bytes) == next->ml_address) {
memlist_free_one(dst);
next->ml_address = address;
next->ml_size += bytes;
return (MEML_SPANOP_OK);
}
if (address == (next->ml_address + next->ml_size)) {
memlist_free_one(dst);
if (next->ml_next) {
if ((address + bytes) >
next->ml_next->ml_address) {
return (MEML_SPANOP_ESPAN);
}
if ((address + bytes) ==
next->ml_next->ml_address) {
struct memlist *mlp = next->ml_next;
if (next == *curmemlistp)
*curmemlistp = next->ml_next;
mlp->ml_address = next->ml_address;
mlp->ml_size += next->ml_size;
mlp->ml_size += bytes;
if (next->ml_prev)
next->ml_prev->ml_next = mlp;
mlp->ml_prev = next->ml_prev;
memlist_free_one(next);
return (MEML_SPANOP_OK);
}
}
next->ml_size += bytes;
return (MEML_SPANOP_OK);
}
if ((address + bytes) > next->ml_address) {
memlist_free_one(dst);
return (MEML_SPANOP_ESPAN);
}
dst->ml_prev = prev;
dst->ml_next = next;
next->ml_prev = dst;
if (prev == NULL) {
*curmemlistp = dst;
} else {
prev->ml_next = dst;
}
return (MEML_SPANOP_OK);
}
prev->ml_next = dst;
dst->ml_prev = prev;
dst->ml_next = NULL;
return (MEML_SPANOP_OK);
}
int
memlist_delete_span(
uint64_t address,
uint64_t bytes,
struct memlist **curmemlistp)
{
struct memlist *dst, *next;
for (next = *curmemlistp; next != NULL; next = next->ml_next) {
if ((address >= next->ml_address) &&
(address < next->ml_address + next->ml_size))
break;
}
if (next == NULL) {
return (MEML_SPANOP_ESPAN);
}
if (address + bytes > next->ml_address + next->ml_size) {
return (MEML_SPANOP_ESPAN);
}
if (address == next->ml_address) {
if (bytes == next->ml_size) {
if (next == *curmemlistp)
*curmemlistp = next->ml_next;
if (next->ml_prev != NULL)
next->ml_prev->ml_next = next->ml_next;
if (next->ml_next != NULL)
next->ml_next->ml_prev = next->ml_prev;
memlist_free_one(next);
} else {
next->ml_address += bytes;
next->ml_size -= bytes;
}
return (MEML_SPANOP_OK);
}
if (address + bytes == next->ml_address + next->ml_size) {
next->ml_size -= bytes;
return (MEML_SPANOP_OK);
}
{
dst = memlist_get_one();
if (dst == NULL) {
return (MEML_SPANOP_EALLOC);
}
dst->ml_address = address + bytes;
dst->ml_size =
(next->ml_address + next->ml_size) - dst->ml_address;
next->ml_size = address - next->ml_address;
dst->ml_next = next->ml_next;
dst->ml_prev = next;
if (next->ml_next != NULL)
next->ml_next->ml_prev = dst;
next->ml_next = dst;
}
return (MEML_SPANOP_OK);
}