602 lines
14 KiB
C
602 lines
14 KiB
C
|
|
#include <sys/types.h>
|
|
#include <sys/cpu.h>
|
|
#include <sys/mips_addrspace.h>
|
|
#include <sys/nodepda.h>
|
|
#include <sys/SN/vector.h>
|
|
#include <sys/SN/agent.h>
|
|
#if defined (SN0)
|
|
#include <sys/SN/SN0/IP27.h>
|
|
#endif /* SN0 */
|
|
|
|
#ifndef _STANDALONE
|
|
#include <sys/systm.h>
|
|
#include <sys/debug.h>
|
|
#else
|
|
#include <libsk.h>
|
|
#endif
|
|
|
|
/* TRACE is for simulation. For now, just undefine it. */
|
|
#define TRACE(x)
|
|
|
|
int vec_polls_r = VEC_POLLS_R;
|
|
int vec_polls_w = VEC_POLLS_W;
|
|
|
|
/*
|
|
* vector_write_node
|
|
*
|
|
* Writes a value to a remote hub NI or router register.
|
|
* Waits for the response before continuing.
|
|
*
|
|
* Performs retries because when vector traffic is very
|
|
* heavy, routers may drop some requests/responses.
|
|
*
|
|
* Returns 0 for success, or a negative value for failure.
|
|
*/
|
|
|
|
int
|
|
vector_write_node(net_vec_t dest, nasid_t nasid,
|
|
int write_id, int address,
|
|
__uint64_t value)
|
|
{
|
|
__uint64_t status;
|
|
int try, polls;
|
|
int r;
|
|
hubreg_t age_regaddr;
|
|
hubreg_t age_oldval;
|
|
hubreg_t age_newval;
|
|
|
|
|
|
r = 0;
|
|
|
|
age_regaddr = ( private.p_slice ) ? NI_AGE_CPU1_MEMORY : NI_AGE_CPU0_MEMORY; age_oldval = REMOTE_HUB_L(nasid, age_regaddr);
|
|
age_newval = (age_oldval & ~NAGE_VCH_MASK) | (VCHANNEL_B << NAGE_VCH_SHFT);
|
|
|
|
for (try = 0; try < VEC_RETRIES_W; try++) {
|
|
REMOTE_HUB_L(nasid, NI_VECTOR_CLEAR);
|
|
|
|
if (REMOTE_HUB_L(nasid, NI_VECTOR_STATUS) & NVS_VALID) {
|
|
r = NET_ERROR_HARDWARE;
|
|
goto done;
|
|
}
|
|
|
|
REMOTE_HUB_S(nasid, NI_VECTOR_DATA, value);
|
|
|
|
REMOTE_HUB_S(nasid, NI_VECTOR, dest);
|
|
/*
|
|
* In order to workaround a router bug, we need to
|
|
* make all craylink traffic go on VCHANNEL_A normally, and only
|
|
* Vector operations to go on VCHANNEL_B.
|
|
* First part is done when the system boots. As part
|
|
* of doing the vector operation, change the PI_CPUx_MEMORY to
|
|
* point to B instead of A, launch the Vector operation, and
|
|
* change it back to A.
|
|
*/
|
|
REMOTE_HUB_S(nasid, age_regaddr, age_newval);
|
|
|
|
/*
|
|
* Do the write that triggers the Vector operation.
|
|
*/
|
|
|
|
REMOTE_HUB_S(nasid, NI_VECTOR_PARMS,
|
|
123L << NVP_PIOID_SHFT | /* Unique PIO ID number */
|
|
(__uint64_t) write_id << NVP_WRITEID_SHFT |
|
|
address & NVP_ADDRESS_MASK |
|
|
PIOTYPE_WRITE);
|
|
|
|
/*
|
|
* Revert back to old mode of virtual channel usage.
|
|
*/
|
|
REMOTE_HUB_S(nasid, age_regaddr, age_oldval);
|
|
|
|
|
|
for (polls = 0; polls < vec_polls_w; polls++) {
|
|
#ifndef _STANDALONE
|
|
us_delay(1);
|
|
#else
|
|
delay(1);
|
|
#endif
|
|
status = REMOTE_HUB_L(nasid, NI_VECTOR_STATUS);
|
|
if (status & NVS_VALID)
|
|
break;
|
|
}
|
|
|
|
if (polls == vec_polls_w)
|
|
continue; /* Timed out; retry */
|
|
|
|
if (status & NVS_OVERRUN) { /* Overrun; retry */
|
|
r = NET_ERROR_OVERRUN;
|
|
continue;
|
|
}
|
|
|
|
switch (status & NVS_TYPE_MASK) {
|
|
case PIOTYPE_ADDR_ERR:
|
|
return NET_ERROR_ADDRESS;
|
|
case PIOTYPE_CMD_ERR:
|
|
return NET_ERROR_COMMAND;
|
|
case PIOTYPE_PROT_ERR:
|
|
return NET_ERROR_PROT;
|
|
case PIOTYPE_WRITE:
|
|
break;
|
|
default:
|
|
return NET_ERROR_REPLY;
|
|
}
|
|
|
|
if ((status & NVS_PIOID_MASK) >> NVS_PIOID_SHFT != 123 ||
|
|
(status & NVS_WRITEID_MASK) >> NVS_WRITEID_SHFT != write_id ||
|
|
(status & NVS_ADDRESS_MASK) != (address & NVP_ADDRESS_MASK)) {
|
|
r = NET_ERROR_REPLY; /* Fatal */
|
|
goto done;
|
|
}
|
|
|
|
break; /* Success */
|
|
}
|
|
|
|
if (try == VEC_RETRIES_W) {
|
|
r = NET_ERROR_TIMEOUT;
|
|
goto done;
|
|
}
|
|
|
|
done:
|
|
|
|
return r;
|
|
}
|
|
|
|
/*
|
|
* vector_read_node
|
|
*
|
|
* Reads a value from a remote hub NI or router register.
|
|
* Waits for the response before continuing.
|
|
*
|
|
* Performs retries because when vector traffic is very
|
|
* heavy, routers may drop some requests/responses.
|
|
*
|
|
* Returns 0 for success, or a negative value for failure.
|
|
*/
|
|
|
|
int
|
|
vector_read_node(net_vec_t dest, nasid_t nasid,
|
|
int write_id, int address,
|
|
__uint64_t *value)
|
|
{
|
|
__uint64_t status;
|
|
int try, polls;
|
|
int r;
|
|
hubreg_t age_regaddr;
|
|
hubreg_t age_oldval;
|
|
hubreg_t age_newval;
|
|
|
|
r = 0;
|
|
age_regaddr = ( private.p_slice ) ? NI_AGE_CPU1_MEMORY : NI_AGE_CPU0_MEMORY; age_oldval = REMOTE_HUB_L(nasid, age_regaddr);
|
|
age_newval = (age_oldval & ~NAGE_VCH_MASK) | (VCHANNEL_B << NAGE_VCH_SHFT);
|
|
|
|
for (try = 0; try < VEC_RETRIES_R; try++) {
|
|
REMOTE_HUB_L(nasid, NI_VECTOR_CLEAR);
|
|
|
|
if (REMOTE_HUB_L(nasid, NI_VECTOR_STATUS) & NVS_VALID) {
|
|
r = NET_ERROR_HARDWARE;
|
|
goto done;
|
|
}
|
|
|
|
#if 1
|
|
REMOTE_HUB_S(nasid, NI_VECTOR_READ_DATA, 0xdeadbeefdeadbeef);
|
|
#endif
|
|
|
|
REMOTE_HUB_S(nasid, NI_VECTOR, dest);
|
|
|
|
/*
|
|
* In order to workaround a router bug, we need to
|
|
* make all craylink traffic go on VCHANNEL_A normally, and only
|
|
* Vector operations to go on VCHANNEL_B.
|
|
* First part is done when the system boots. As part
|
|
* of doing the vector operation, change the PI_CPUx_MEMORY to
|
|
* point to B instead of A, launch the Vector operation, and
|
|
* change it back to A.
|
|
*/
|
|
REMOTE_HUB_S(nasid, age_regaddr, age_newval);
|
|
|
|
/*
|
|
* Do the write that triggers the Vector operation.
|
|
*/
|
|
|
|
REMOTE_HUB_S(nasid, NI_VECTOR_PARMS,
|
|
456L << NVP_PIOID_SHFT | /* Unique PIO ID number */
|
|
(__uint64_t) write_id << NVP_WRITEID_SHFT |
|
|
address & NVP_ADDRESS_MASK |
|
|
PIOTYPE_READ);
|
|
|
|
/*
|
|
* Revert back to old mode of virtual channel usage.
|
|
*/
|
|
REMOTE_HUB_S(nasid, age_regaddr, age_oldval);
|
|
|
|
for (polls = 0; polls < vec_polls_r; polls++) {
|
|
status = REMOTE_HUB_L(nasid, NI_VECTOR_STATUS);
|
|
if (status & NVS_VALID)
|
|
break;
|
|
}
|
|
|
|
if (polls == vec_polls_r)
|
|
continue; /* Timed out; retry */
|
|
|
|
if (status & NVS_OVERRUN) {
|
|
r = NET_ERROR_OVERRUN;
|
|
goto done;
|
|
}
|
|
|
|
switch (status & NVS_TYPE_MASK) {
|
|
case PIOTYPE_ADDR_ERR:
|
|
return NET_ERROR_ADDRESS;
|
|
case PIOTYPE_CMD_ERR:
|
|
return NET_ERROR_COMMAND;
|
|
case PIOTYPE_PROT_ERR:
|
|
return NET_ERROR_PROT;
|
|
case PIOTYPE_READ:
|
|
break;
|
|
default:
|
|
return NET_ERROR_REPLY;
|
|
}
|
|
|
|
if ((status & NVS_PIOID_MASK) >> NVS_PIOID_SHFT != 456 ||
|
|
(status & NVS_WRITEID_MASK) >> NVS_WRITEID_SHFT != write_id ||
|
|
(status & NVS_ADDRESS_MASK) != (address & NVP_ADDRESS_MASK)) {
|
|
r = NET_ERROR_REPLY; /* Fatal */
|
|
goto done;
|
|
}
|
|
|
|
*value = REMOTE_HUB_L(nasid, NI_VECTOR_READ_DATA);
|
|
|
|
break; /* Success */
|
|
}
|
|
|
|
if (try == VEC_RETRIES_R) {
|
|
r = NET_ERROR_TIMEOUT;
|
|
goto done;
|
|
}
|
|
|
|
done:
|
|
|
|
return r;
|
|
}
|
|
|
|
|
|
/*
|
|
* vector_exch_node
|
|
*
|
|
* Conditionally and atomically writes a value into a remote hub NI
|
|
* or router register, provided that the current content of the
|
|
* register is zero, and returns the value of the register prior to
|
|
* the exchange. Waits for the response before continuing.
|
|
*
|
|
* The value written is intended to be an ID to identify the locker
|
|
* when the scratch registers are being used for router locking.
|
|
*
|
|
* Performs retries because when vector traffic is very
|
|
* heavy, routers may drop some requests/responses.
|
|
*
|
|
* The caller should check the returned value. If it's either zero
|
|
* or the requested value, then the lock will have been obtained.
|
|
* (It could equal the requested ID if a response was lost and the
|
|
* exchange was retried).
|
|
*
|
|
* Returns 0 for success, or a negative value for failure.
|
|
*/
|
|
int
|
|
vector_exch_node(net_vec_t dest, nasid_t nasid,
|
|
int write_id, int address,
|
|
__uint64_t *value)
|
|
{
|
|
__uint64_t status;
|
|
int try, polls;
|
|
int r;
|
|
hubreg_t age_regaddr;
|
|
hubreg_t age_oldval;
|
|
hubreg_t age_newval;
|
|
|
|
TRACE(write_id);
|
|
TRACE(dest);
|
|
TRACE(address);
|
|
TRACE(value);
|
|
|
|
r = 0;
|
|
age_regaddr = ( private.p_slice ) ? NI_AGE_CPU1_MEMORY : NI_AGE_CPU0_MEMORY; age_oldval = REMOTE_HUB_L(nasid, age_regaddr);
|
|
age_newval = (age_oldval & ~NAGE_VCH_MASK) | (VCHANNEL_B << NAGE_VCH_SHFT);
|
|
|
|
for (try = 0; try < VEC_RETRIES_X; try++) {
|
|
REMOTE_HUB_L(nasid, NI_VECTOR_CLEAR);
|
|
|
|
if (REMOTE_HUB_L(nasid, NI_VECTOR_STATUS) & NVS_VALID) {
|
|
r = NET_ERROR_HARDWARE;
|
|
goto done;
|
|
}
|
|
|
|
REMOTE_HUB_S(nasid, NI_VECTOR_READ_DATA, 0xdeadbeefdeadbeef);
|
|
|
|
REMOTE_HUB_S(nasid, NI_VECTOR_DATA, *value);
|
|
|
|
REMOTE_HUB_S(nasid, NI_VECTOR, dest);
|
|
|
|
/*
|
|
* In order to workaround a router bug, we need to
|
|
* make all craylink traffic go on VCHANNEL_A normally, and only
|
|
* Vector operations to go on VCHANNEL_B.
|
|
* First part is done when the system boots. As part
|
|
* of doing the vector operation, change the PI_CPUx_MEMORY to
|
|
* point to B instead of A, launch the Vector operation, and
|
|
* change it back to A.
|
|
*/
|
|
REMOTE_HUB_S(nasid, age_regaddr, age_newval);
|
|
|
|
/*
|
|
* Do the write that triggers the Vector operation.
|
|
*/
|
|
|
|
REMOTE_HUB_S(nasid, NI_VECTOR_PARMS,
|
|
789L << NVP_PIOID_SHFT | /* Unique PIO ID number */
|
|
(__uint64_t) write_id << NVP_WRITEID_SHFT |
|
|
address & NVP_ADDRESS_MASK |
|
|
PIOTYPE_EXCHANGE);
|
|
|
|
/*
|
|
* Revert back to old mode of virtual channel usage.
|
|
*/
|
|
REMOTE_HUB_S(nasid, age_regaddr, age_oldval);
|
|
|
|
for (polls = 0; polls < VEC_POLLS_X; polls++) {
|
|
status = REMOTE_HUB_L(nasid, NI_VECTOR_STATUS);
|
|
if (status & NVS_VALID)
|
|
break;
|
|
}
|
|
|
|
if (polls == VEC_POLLS_X)
|
|
continue; /* Timed out; retry */
|
|
|
|
if (status & NVS_OVERRUN) {
|
|
r = NET_ERROR_OVERRUN;
|
|
goto done;
|
|
}
|
|
|
|
switch (status & NVS_TYPE_MASK) {
|
|
case PIOTYPE_ADDR_ERR:
|
|
return NET_ERROR_ADDRESS;
|
|
case PIOTYPE_CMD_ERR:
|
|
return NET_ERROR_COMMAND;
|
|
case PIOTYPE_PROT_ERR:
|
|
return NET_ERROR_PROT;
|
|
case PIOTYPE_EXCHANGE:
|
|
break;
|
|
default:
|
|
return NET_ERROR_REPLY;
|
|
}
|
|
|
|
if ((status & NVS_PIOID_MASK) >> NVS_PIOID_SHFT != 789 ||
|
|
(status & NVS_WRITEID_MASK) >> NVS_WRITEID_SHFT != write_id ||
|
|
(status & NVS_ADDRESS_MASK) != (address & NVP_ADDRESS_MASK)) {
|
|
r = NET_ERROR_REPLY; /* Fatal */
|
|
goto done;
|
|
}
|
|
|
|
*value = REMOTE_HUB_L(nasid, NI_VECTOR_READ_DATA);
|
|
|
|
break; /* Success */
|
|
}
|
|
|
|
if (try == VEC_RETRIES_X) {
|
|
r = NET_ERROR_TIMEOUT;
|
|
goto done;
|
|
}
|
|
|
|
done:
|
|
|
|
TRACE(r);
|
|
|
|
return r;
|
|
}
|
|
|
|
/* Same as vector_read_node but for the local node. */
|
|
int
|
|
vector_read(net_vec_t dest, int write_id, int address, __uint64_t *value)
|
|
{
|
|
return vector_read_node(dest, get_nasid(), write_id, address, value);
|
|
|
|
}
|
|
|
|
/* Same as vector_write_node but for the local node. */
|
|
int
|
|
vector_write(net_vec_t dest, int write_id, int address, __uint64_t value)
|
|
{
|
|
return vector_write_node(dest, get_nasid(), write_id, address, value);
|
|
|
|
}
|
|
|
|
/* Same as vector_exch_node but for the local node. */
|
|
int
|
|
vector_exch(net_vec_t dest, int write_id, int address, __uint64_t *value)
|
|
{
|
|
return vector_exch_node(dest, get_nasid(), write_id, address, value);
|
|
|
|
}
|
|
|
|
/*
|
|
* Routines for performing simple operations on network vectors
|
|
*/
|
|
|
|
int
|
|
vector_length(net_vec_t vec)
|
|
{
|
|
int len;
|
|
|
|
for (len = 0; vec & 0xfULL << 4 * len; len++)
|
|
;
|
|
|
|
return len;
|
|
}
|
|
|
|
net_vec_t
|
|
vector_get(net_vec_t vec, int n)
|
|
{
|
|
return vec >> n * 4 & 0xfULL;
|
|
}
|
|
|
|
net_vec_t
|
|
vector_prefix(net_vec_t vec, int n)
|
|
{
|
|
return n ? vec & ~0ULL >> 64 - 4 * n : 0;
|
|
}
|
|
|
|
net_vec_t
|
|
vector_modify(net_vec_t entry, int n, int route)
|
|
{
|
|
return entry & ~(0xfULL << 4 * n) | (net_vec_t) route << 4 * n;
|
|
}
|
|
|
|
net_vec_t
|
|
vector_reverse(net_vec_t vec)
|
|
{
|
|
net_vec_t result;
|
|
|
|
for (result = 0; vec; vec >>= 4)
|
|
result = result << 4 | vec & 0xfULL;
|
|
|
|
return result;
|
|
}
|
|
|
|
net_vec_t
|
|
vector_concat(net_vec_t vec1, net_vec_t vec2)
|
|
{
|
|
return vec1 | vec2 << 4 * vector_length(vec1);
|
|
}
|
|
|
|
/*
|
|
* net_errmsg
|
|
*
|
|
* Translates an NET_ERROR_code into an appropriate message string.
|
|
*/
|
|
|
|
char
|
|
*net_errmsg(int rc)
|
|
{
|
|
switch (rc) {
|
|
case NET_ERROR_NONE:
|
|
return "No error";
|
|
case NET_ERROR_HARDWARE:
|
|
return "Hardware error";
|
|
case NET_ERROR_OVERRUN:
|
|
return "Extra response(s)";
|
|
case NET_ERROR_REPLY:
|
|
return "Reply parameters mismatch";
|
|
case NET_ERROR_ADDRESS:
|
|
return "Address error response";
|
|
case NET_ERROR_COMMAND:
|
|
return "Command error response";
|
|
case NET_ERROR_PROT:
|
|
return "Protection error response";
|
|
case NET_ERROR_TIMEOUT:
|
|
return "Vector response timeout";
|
|
case NET_ERROR_VECTOR:
|
|
return "Invalid vector";
|
|
case NET_ERROR_ROUTERLOCK:
|
|
return "Timed out locking router";
|
|
case NET_ERROR_INVAL:
|
|
return "Invalid vector request";
|
|
default:
|
|
return "Undefined error code";
|
|
}
|
|
}
|
|
|
|
|
|
#ifndef _STANDALONE
|
|
|
|
#define LOCK_DEFS int s
|
|
#define LOCK if (!in_panic_mode()) \
|
|
s = mutex_spinlock(&npdap->vector_lock)
|
|
#define UNLOCK if (!in_panic_mode()) \
|
|
mutex_spinunlock(&npdap->vector_lock, s)
|
|
|
|
|
|
/*
|
|
* Same as vector_read_node, but acquires the hub lock before
|
|
* starting the read.
|
|
*/
|
|
int
|
|
hub_vector_read(cnodeid_t cnode, net_vec_t vector, int writeid,
|
|
int addr, net_reg_t *value)
|
|
{
|
|
LOCK_DEFS;
|
|
int rc = 0;
|
|
nodepda_t *npdap = NODEPDA(cnode);
|
|
nasid_t nasid = COMPACT_TO_NASID_NODEID(cnode);
|
|
|
|
LOCK;
|
|
|
|
/*
|
|
* Vector operations only work correctly when called on the
|
|
* node whose vector registers we're using.
|
|
*/
|
|
if (cnodeid() != cnode) {
|
|
rc = NET_ERROR_INVAL;
|
|
goto done;
|
|
}
|
|
|
|
/* Mark the vector unit as being busy till the vector operation
|
|
* is completed.
|
|
*/
|
|
|
|
npdap->vector_unit_busy = 1;
|
|
rc = vector_read_node(vector, nasid, writeid, addr, value);
|
|
|
|
/* Vector operation is done . Now we can go ahead and mark
|
|
* the vector unit as being idle.
|
|
*/
|
|
npdap->vector_unit_busy = 0;
|
|
|
|
done:
|
|
UNLOCK;
|
|
return rc;
|
|
}
|
|
|
|
|
|
/*
|
|
* Same as vector_write_node, but acquires the hub lock before
|
|
* starting the read.
|
|
*/
|
|
int
|
|
hub_vector_write(cnodeid_t cnode, net_vec_t vector, int writeid,
|
|
int addr, net_reg_t value)
|
|
{
|
|
LOCK_DEFS;
|
|
int rc;
|
|
nodepda_t *npdap = NODEPDA(cnode);
|
|
nasid_t nasid = COMPACT_TO_NASID_NODEID(cnode);
|
|
|
|
LOCK;
|
|
|
|
/*
|
|
* Vector operations only work correctly when called on the
|
|
* node whose vector registers we're using.
|
|
*/
|
|
if (cnodeid() != cnode) {
|
|
rc = NET_ERROR_INVAL;
|
|
goto done;
|
|
}
|
|
|
|
/* Mark the vector unit as being busy till the vector operation
|
|
* is completed.
|
|
*/
|
|
|
|
npdap->vector_unit_busy = 1;
|
|
rc = vector_write_node(vector, nasid, writeid, addr, value);
|
|
|
|
/* Vector operation is done . Now we can go ahead and mark
|
|
* the vector unit as being idle.
|
|
*/
|
|
npdap->vector_unit_busy = 0;
|
|
|
|
done:
|
|
UNLOCK;
|
|
return rc;
|
|
}
|
|
|
|
#endif /* !_STANDALONE */
|
|
|