wrsm_cmmu.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (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 2001-2003 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* This file manages the CMMU entries for the Wildcat RSM driver. It keeps
* track of which entries can be used for large pages, which entries are
* free. It also keeps the CMMU entries on each WCI in sync, by providing
*/
#include <sys/wci_regs.h>
#include <sys/wci_offsets.h>
#include <sys/wrsm_cmmu.h>
#include <sys/wrsm_config.h>
/*
* The following macros define a DPRINTF macro which can be used to enable
* or disable various levels of logging for this module.
*/
#ifdef DEBUG
#define CMMUDBG 0x1
#define CMMUWARN 0x2
#define CMMUERR 0x4
#define CMMUTRACE 0x8
#else /* DEBUG */
#define DPRINTF(a, b) { }
#endif /* DEBUG */
#define CMMU_SUCCESS 0
/*
* Local types, constants and macros
*/
/*
* A cmmu index is 21 bits (up to 2 million entries, with 16MB SRAM).
* Each NcSlice can export either large pages or small pages.
*
* A single small page ncslice can export all 21 million pages. To go
* from cmmu entry to ncslice offset for small pages, just shift the
* cmmu index left 13 bits.
*
* Each large page ncslice can export 4096 entries of 4MB each. The
* large page CMMU indices are defined as follows:
* index<11:0> = ncslice offset<33:22>
* index<14:12> = ncslice<2:0>
* index<20:15> = 0
* Since the lower three bits of the ncslice are used to discriminate between
* large page ncslice entries, only 8 large page ncslices can be exported
* (actually, more can be exported, but they would share CMMU entries, so
* there's no value in exporting more than 8).
*/
#define MAX_LARGE_NCSLICES 8
/* large ncslice can export 4096 pgs, minus pages 0 and 1 which are special */
#define LOW_3_BITS 0x7
#define MAX_LARGE_PAGE_INDEX 0x7fff
#define CESR_PAGE 0
#define WRITELOCKOUT_PAGE 1
/* Constants for cluster_members_bits */
/* Constants for ncslice_config bits */
/*
* The free_region structure defines a range of free CMMU entries (i.e.,
* a start and an end). It is designed to be an element of a doubly
* linked list to track free CMMU entries.
*/
typedef struct free_region_struct {
struct free_region_struct *prev;
struct free_region_struct *next;
/*
* The free_list_t contains a linked list of free regions and the
* ncslice that these CMMU entries belong to. It also includes a mutex,
*/
typedef struct {
} free_list_t;
/* Set of free pages for absolute alloc */
typedef struct {
uint32_t b[COMM_MASKS];
/* Structure to keep track of which WCIs belong to us */
typedef struct wci_handle_struct
{
struct wci_handle_struct *next;
struct wci_handle_struct *prev;
} wci_handle_t;
/* State structure for the CMMU allocator */
struct wrsm_cmmu_alloc {
unsigned num_free_lists;
};
/*
* Local Functions
*/
static void cmmu_attributes(wrsm_network_t *);
static wrsm_cmmu_index_t
{
} else {
}
return (index);
}
/* Converts a CMMU index to an ncslice offset */
static wrsm_cmmu_offset_t
{
} else {
}
return (offset);
}
/* Converts large page ncslice to starting CMMU entry */
static wrsm_cmmu_index_t
{
return (start);
}
/* Converts large page ncslice to starting CMMU entry */
static wrsm_cmmu_index_t
{
}
/* Converts an index to an ncslice */
static free_list_t *
{
unsigned i;
/* If only lower 15 bits are set, this could be a large page */
if (index <= MAX_LARGE_PAGE_INDEX) {
/* Look for large ncslices with matching lower 3 bits */
for (i = 0; i < cmmu->num_free_lists; i++) {
return (&cmmu->free_lists[i]);
}
}
}
/* We didn't find a large page ncslice, so use first small ncslice */
for (i = 0; i < cmmu->num_free_lists; i++) {
return (&cmmu->free_lists[i]);
}
}
/* No ncslice was found */
return (NULL);
}
/* Copies the wci_cluster_members_bits registers from one WCI to another */
static void
{
uint_t i;
DTRC("clustermember_copy");
for (i = 0; i < ENTRIES_WCI_CLUSTER_MEMBERS_BITS; i++) {
}
}
/* Copies the wci_ncslice_config_array registers from one WCI to another */
static void
{
uint_t i;
DTRC("ncsliceconfig_copy");
for (i = 0; i < ENTRIES_WCI_NC_SLICE_CONFIG_ARRAY; i++) {
}
}
/*
* Functions to manipulate a free region structure
*/
#ifdef DEBUG
/* Prints a region */
static void
{
"%s 0x%p: start=%u, end=%u, next=0x%p, prev=0x%p",
}
#endif /* DEBUG */
#ifdef DEBUG
/* Returns the size of a region, i.e., the number of pages in the region */
static unsigned
{
}
#endif
/* Creates a free region (from start to end), in the list 'head' */
static void
{
ASSERT(p);
/* If after is NULL, adding to head of list */
} else {
}
if (p->next) {
}
}
/* Deletes a region from a free list, and frees its memory */
static void
{
DTRC("region_delete");
/* If this is first region in list, update head to point around it */
} else {
}
}
}
/*
* The following function allows you to allocate a range of items from
* a given region. Assumes the start and end are within the region. If
* necessary, it will split the region, or delete it.
*/
static int
{
int retval = CMMU_SUCCESS;
/* Check that range is inclusive */
WARN("region_alloc: invalid args");
/* A perfect fit, remove the region */
/* Removing from the front of the region */
/* Removing from the end of the region */
} else {
/* Removing from the middle, must create new region */
/* Now "shrink" old region */
}
return (retval);
}
/*
* Functions to manipulate a free list
*/
#ifdef DEBUG
/* Traverses a list, print all its regions */
static void
/* LINTED: static unused: list_print (E_STATIC_UNUSED) */
{
free_region_t *p;
region_print(" region:", p);
}
}
#endif /* DEBUG */
#ifdef DEBUG
/* Traverses a list, checking the pointers */
static void
/* LINTED: static unused: list_check (E_STATIC_UNUSED) */
{
free_region_t *p;
break;
}
if (p->next) {
"p->end=%u < p->next->start=%u",
break;
}
"p->next->prev=%p != p=%p",
break;
}
}
}
if (!ok) {
}
}
#endif /* DEBUG */
#ifdef DEBUG
/*
* Returns the size of the list, i.e., the free pages in all regions
* You must already own the mutex
*/
static unsigned
{
free_region_t *p;
unsigned size = 0;
size += region_size(p);
}
return (size);
}
#endif
/* Initializes the given ncslice free list */
static void
{
}
/* Deletes all regions in the list */
static void
{
DTRC("list_delete");
}
}
/* Destroys the given ncslice free list */
static void
{
DTRC("list_fini");
}
/*
* Allocates a page at the given index.
* Returns ENOMEM if the index was already allocated.
*/
static int
unsigned count)
{
free_region_t *p;
#ifdef DEBUG
#endif /* DEBUG */
DTRC("list_absolute_alloc");
#ifdef DEBUG
#endif /* DEBUG */
/* Starts somewhere within this region */
break;
}
}
if (p == NULL) {
}
#ifdef DEBUG
/* Make sure we aren't leaking any pages */
if (retval == 0) {
} else {
}
#endif /* DEBUG */
return (retval);
}
/*
* Allocates a range of entries with the best fit available. If it can't
* allocate all the requested pages, it will respond with the number
* of entries actually allocated. Returns ENOMEM if there aren't any
* entries left.
*/
static int
{
free_region_t *p;
unsigned biggest_count = 0;
#ifdef DEBUG
#endif /* DEBUG */
#ifdef DEBUG
#endif /* DEBUG */
if (count == desired_num) {
/* An exact match, can't beat that! */
best_region = p;
break;
} else if (count > desired_num) {
/* This range is too big, look for best fit */
best_region = p;
}
} else {
/* Remember biggest, in case we don't fit anywhere */
if (count > biggest_count) {
biggest_region = p;
}
}
}
if (best_region != NULL) {
/* We found a region at least large enough */
} else if (biggest_region != NULL) {
/* None were big enough, so just use the biggest region */
}
#ifdef DEBUG
/* Make sure we aren't leaking any pages */
if (retval == 0) {
} else {
}
#endif /* DEBUG */
return (retval);
}
/* Frees a region to a free list. Returns EINVAL if region wasn't allocated */
static int
{
free_region_t *p;
int retval = CMMU_SUCCESS;
#ifdef DEBUG
#endif /* DEBUG */
#ifdef DEBUG
#endif /* DEBUG */
/* Make sure the region being freed isn't already free */
/* Region is contiguous at end of this region */
/* Check to see if we can merge with next region */
}
break;
/* We over shot */
/* See if it fits just before this region */
} else {
/* Need to insert new region in list */
}
break;
/* We're at the end of the line */
break;
}
}
#ifdef DEBUG
/* Make sure we aren't leaking any pages */
if (retval == 0) {
} else {
}
#endif /* DEBUG */
return (retval);
}
static unsigned
{
unsigned i, n;
DTRC("cmmu_free");
n = 0;
for (i = 0; i < ntuples; i++) {
/*
* Don't trust ncslice provided by user. We may have
* allocated small pages from large page free list,
* so we'd want to free them back to the right list,
* so use index to find correct free list.
*/
free_list_t *head =
}
return (n);
}
/*
* Allocates a region from a specific ncslice, specified by head.
* Attempts a best-fit allocation. This function can be used iteratively on
* each available ncslice when searching for an ncslice to allocate entries
* from.
*/
static void
{
/* Allocate from this ncslice until we're done or, or it's drained */
while (*nentries > 0 &&
*ntuples < availtuples &&
(*ntuples)++;
}
}
/* Adds a WCI to the linked list */
static void
{
wci_handle_t *p;
DTRC("wci_add");
/* Make sure the WCI doesn't already exist */
if (p->wci_handle == wci) {
WARN("wci_add: WCI already exists");
return;
}
}
/* Check if we need to replicate, and if we have ANY other WCIs */
unsigned index;
unsigned i;
/* First, replicate the ncslice array */
/* Now, replicate the cluster members bits registers */
/*
* Next, the CMMU. Initialize an array of pointers for all
* free lists. We'll walk the free lists, and skip all free
* entries -- there's no need to copy free (i.e., unused)
* CMMU entries, and there should be a lot of them!
*/
for (i = 0; i < cmmu->num_free_lists; i++) {
}
/* Walk the CMMU array from start to finish */
/* First, check if this index is on free list */
for (i = 0; i < cmmu->num_free_lists; i++) {
/* On a free list, so not in use */
/* Skip over rest of pages in region */
/* Get next region for this list */
p[i] = p[i]->next;
break;
}
}
if (in_use) {
/* Not free, so make copy */
}
}
}
/* Add it to the list */
}
}
/* Initializes free list structures once we have WCIs */
static void
{
unsigned i;
DTRC("init_free_lists");
/* First, allocate all non-comm pages to the small page ncslice */
/* Mark all comm pages as free (zero) */
/* Then allocate the CESR and write lockout pages */
/* For each large page ncslice, move pages from small ncslice */
/* Allocate these pages away from small page free list */
/* LINTED */
ASSERT(0);
}
/* Free these pages to the large page free list */
}
#ifdef DEBUG
for (i = 0; i < cmmu->num_free_lists; i++) {
}
#endif /* DEBUG */
}
/* Calculates CMMU-based attributes */
static void
{
DTRC("wrsm_cmmu_attributes");
} else {
}
}
/*
* API Functions. See wrsm_cmmu.h for function descriptions.
*/
void
{
unsigned i;
DTRC("wrsm_cmmu_init");
for (i = 0; i < nwcis; i++) {
int n = 0;
/* Our max will be the smallest of all WCIs */
if (n < cmmu->max_entries) {
cmmu->max_entries = n;
}
/* Add this WCI to our linked list */
}
cmmu->max_entries));
/*
* Build Empty Free Lists
*/
/* Make sure there is a small page ncslice */
cmmu->num_free_lists++;
/* For each large page ncslice, move pages from small ncslice */
for (i = 1; i < WRSM_NODE_NCSLICES; i++) {
if (ncslice == 0)
continue;
/* Create new list for this ncslice */
cmmu->num_free_lists++;
}
/* If there are real WCIs, populate free lists */
if (nwcis > 0) {
}
}
void
{
unsigned i;
DTRC("wrsm_cmmu_fini");
}
for (i = 0; i < cmmu->num_free_lists; i++) {
}
cmmu->num_free_lists = 0;
}
int
{
int retval = CMMU_SUCCESS;
DTRC("wrsm_cmmu_newwci");
/* If this is the first WCI, initialize free lists */
}
/* Make sure the WCI has enough SRAM */
return (ENOMEM);
}
/* Update attributes */
return (retval);
}
int
{
wci_handle_t *p;
unsigned i;
DTRC("wrsm_cmmu_delwci");
if (p->wci_handle == wci) {
if (p->prev) {
} else {
}
if (p->next) {
}
kmem_free(p, sizeof (wci_handle_t));
break;
}
}
/* If this was the last WCI, delete all free lists */
for (i = 0; i < cmmu->num_free_lists; i++) {
}
cmmu->max_entries = 0;
}
/* Update attributes */
return (retval);
}
int
{
int retval = CMMU_SUCCESS;
unsigned i, n;
KM_SLEEP);
/*
* For each free list (ncslice) of the right size, try a
* best-fit alloc from that ncslice's free list, until we
* either run out of free lists, succeed in allocating
* enough entries, or run out of tuples to store results.
*/
n = nentries;
*ntuples = 0;
for (i = 0; i < cmmu->num_free_lists &&
n > 0 &&
*ntuples < availtuples; i++) {
/* If wrong page size, bail */
continue;
}
&n, ntuples, availtuples);
}
/*
* If we didn't allocate all the entries requested, and
* there's still free tuples, and we were allocating small
* pages, let's steal from the large page ncslices.
*/
if (n > 0 && *ntuples < availtuples &&
}
}
if (n > 0) {
/*
* we failed, so free up any cmmu entries we
* allocated
*/
if (*ntuples > 0)
if (*ntuples == availtuples) {
} else {
if (sleep) {
&cmmu->resource_cv,
if (retval > 0) {
goto retry;
} else {
/* got a signal */
}
} else {
}
}
*ntuples = 0;
} else {
/* copy the data to the output array */
}
#ifdef DEBUG
if (retval == CMMU_SUCCESS) {
int i;
for (i = 0; i < *ntuples; i++) {
"ncslice %d count %d offset 0x%p index %d\n",
i,
}
}
#endif
return (retval);
}
void
{
unsigned nentries;
DTRC("wrsm_cmmu_free");
return;
#ifdef DEBUG
{
int i;
for (i = 0; i < ntuples; i++) {
"ncslice %d count %d offset 0x%p index %d\n",
i,
}
}
#endif
}
int
{
int retval = CMMU_SUCCESS;
"wrsm_cmmu_comm_alloc(ncslice=0x%x, offset=0x%p)",
/* Make sure user requested small page ncslice */
WARN("wrsm_cmmu_comm_alloc: Must use small pages");
return (EINVAL);
}
if (index >= NUM_COMM_PAGES) {
WARN("wrsm_cmmu_comm_alloc: index out of range");
return (EINVAL);
}
/* If page isn't already allocated, allocate it */
} else {
cmmu->num_free_entries--;
}
return (retval);
}
void
{
cmmu->num_free_entries++;
}
void
{
wci_handle_t *p;
}
}
void
{
DTRC("wrsm_cmmu_read");
/* If there are WCIs, just use the first WCI on our list */
}
/*
* Cluster members bits functions
*/
void
{
/*
* The wci_cluster_members_bits is an array of 4 64-bit registers,
* spaced out by some stride. Need to calculate which of the 4
* entries to modify, which position within the entry to modify,
* and the offset of the entry for the request to LC.
*/
/* If there are no WCIs, just return */
return;
}
}
}
void
{
/*
* The wci_cluster_members_bits is an array of 4 64-bit registers,
* spaced out by some stride. Need to calculate which of the 4
* entries to modify, which position within the entry to modify,
* and the offset of the entry for the request to LC.
*/
/* If there are no WCIs, just return */
return;
}
}
}
void
{
uint_t i;
uint_t j;
DTRC("wrsm_clustermember_list");
/* If there are no WCIs, just return */
return;
}
/* Loop for each of the array entries... */
for (i = 0; i < ENTRIES_WCI_CLUSTER_MEMBERS_BITS; i++) {
/* Loop for each bit in the array... */
for (j = 0; j < MEMBERS_PER_REGISTER; j++) {
j + i * MEMBERS_PER_REGISTER);
}
}
}
}
/*
* ncslice config array functions
*/
void
{
/*
* The wci_nc_slice_config_array is an 8 entry array with 64-bits
* per entry. Each entry has 2-bits per ncslice. Need to determine
* which entry to modify, the starting position within the entry
* for this ncslice, and the register offset. Also need to create
* masks to isolate the two bits being set by mode.
*/
/* Mask has two zeros where bits go, all other bits are 1 */
/* Make sure only 2 bits are set! Also, cast to 64-bits */
/* If there are no WCIs, just return */
WARN("NO WCIS!!!!!");
return;
}
}
}
{
/*
* The wci_nc_slice_config_array is an 8 entry array with 64-bits
* per entry. Each entry has 2-bits per ncslice. Need to determine
* which entry to modify, the starting position within the entry
* for this ncslice, and the register offset.
*/
DTRC("wrsm_ncsliceconfig_get");
/* If there are no WCIs, just return */
return (ncslice_invalid);
}
return (mode);
}
{
}