nxge_tcam_mgr.c revision 52ccf843e173e2a4a657360b0a22853fd413905f
/*
* 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.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#define NXGE_DEBUG
#include <npi_fflp.h>
#include <npi_mac.h>
#include <nxge_defs.h>
#include <nxge_flow.h>
#include <nxge_fflp.h>
#include <nxge_impl.h>
#include <nxge_fflp_hash.h>
#include <nxge_common.h>
#include <nxge_tcam.h>
return (ENOENT);
void *tcam_state; /* The TCAM's soft state array */
tcam_range_t *, boolean_t);
/*
* -------------------------------------------------------------
* nxge_ipv4_entry_dump
*
* Dump an ipv4 TCAM key
*
* Input
* entry The key holder.
*
* Output
* void
*
* -------------------------------------------------------------
*/
static
void
{
"TCAM @ %d: cls %d, rdc %d, proto %d, "
"port %d, src %x, dst %x",
}
/*
* -------------------------------------------------------------
* nxge_fflp_tcam_init
*
* Initialize the Neptune TCAM.
*
* Input
* nxge An nxge_t instance.
*
* Output
* NXGE_OK if successful; an error code otherwise.
*
* -------------------------------------------------------------
*/
{
if (rs != NPI_SUCCESS) {
return (NXGE_ERROR | rs);
}
if (rs != NPI_SUCCESS) {
"failed TCAM Access cfg\n"));
return (NXGE_ERROR | rs);
}
/* Disable all the programmable classes. */
/* First, the two ethernet classes. */
class++) {
if (rs != NPI_SUCCESS) {
"TCAM USR Ether Class config failed."));
return (NXGE_ERROR | rs);
}
}
/* Then the 4 IP classes. */
for (class = TCAM_CLASS_IP_USER_4;
if (rs != NPI_SUCCESS) {
"TCAM USR IP Class cnfg failed."));
return (NXGE_ERROR | rs);
}
}
/* If this is the first time through... */
if (tcam_state == 0) {
if ((ddi_soft_state_init(&tcam_state,
sizeof (tcam_control_t), 0)) != 0) {
"failed to init TCAM soft state"));
return (NXGE_ERROR);
}
== DDI_FAILURE) {
return (NXGE_ERROR);
}
}
"ddi_get_soft_state() failed"));
return (NXGE_ERROR);
}
/* Initialize the TCAM data structures. */
KM_SLEEP);
} else {
}
/* We must reserve the top <nports> slots for UDP fragments. */
/* <gap> is just a heuristic. */
range++;
}
return (NXGE_OK);
}
/*
* -------------------------------------------------------------
* nxge_tcam_key_get
* nxge_tcam_key_set
*
* Get the current <tsel> bit in a TCAM key for an IP class. Or,
* set the <tsel> bit in the TCAM key for an IP class.
*
* Input
* nxge
* class The IP class to get or set.
*
* Output
* 0 or 1, depending on tsel. -1 if there was an error.
*
* -------------------------------------------------------------
*/
int
{
if (rs & NPI_FFLP_ERROR) {
return (-1);
}
"TCAM tsel bit for class %d set to %d",
return (cfg.lookup_enable);
}
int
{
}
class, &configuration);
if (rs & NPI_FFLP_ERROR) {
return (-1);
}
return (0);
}
/*
* -------------------------------------------------------------
* nxge_tcam_entry_invalidate
* nxge_tcam_entry_move
*
* A couple of TCAM utility functions.
*
* Input
* nxge
* location The slot to invalidate. [invalidate]
*
* old The old (soon to be former) slot [move].
* new The new slot [move].
*
* Output
* void.
*
* -------------------------------------------------------------
*/
void
{
}
void
{
}
/*
* -------------------------------------------------------------
* nxge_tcam_location_get
*
* Find an empty slot in TCAM region <region>. If <region> is
* is full, expand the region by 1 slot.
*
* Input
* nxge
* region The region to use.
*
* Output
* A valid slot number (nxge->nports-255) if successful;
* 0 otherwise.
*
* -------------------------------------------------------------
*/
int region)
{
int i;
region = 1;
/* region 5 -> .region[0] */
/* region 4 -> .region[1], etc. */
"region: %d, get: %d: top: %d / bottom: %d",
return (0);
/* Is there an empty slot between top & bottom? */
return ((tcam_location_t)i);
}
}
/* Can we append it? */
return (locus);
}
/* That didn't work. Can we prepend it? */
return (locus);
}
/* Try to append it first. */
return (locus);
}
/* Ok, try to prepend it. */
return (locus);
}
/* There's no place left to insert this. */
return (0);
}
/*
* -------------------------------------------------------------
* nxge_tcam_shuffle
*
* Shuffle the TCAM entries until we have a free slot. In other
* words, expand a TCAM region on the top or the bottom, thereby
* creating one new slot.
*
* Input
* nxge The nxge_t instance.
* tcam The TCAM control data structure.
* range The slotless region we want to expand.
* down Shuffle everything down (or up).
*
* Output
* 0 if successful; -1 otherwise.
*
* Notes: The TCAM lock must be held by the calling function.
* -------------------------------------------------------------
*/
int
{
int levels;
if (down) {
levels = 0;
/* We can't go down again. */
/* There's simply no room. */
return (-1);
}
levels++;
break;
}
neighbor++;
range++;
levels++;
}
while (levels) {
range--;
levels--;
}
return (0);
} else {
levels = 0;
return (-1);
}
neighbor--;
range--;
levels++;
}
while (levels) {
range++;
levels--;
}
return (0);
}
}
/*
* -------------------------------------------------------------
* nxge_tcam_ad_write
*
* Write a TCAM entry's associated data (ad) register.
*
* Input
* nxge The nxge_t instance.
* entry The TCAM entry in question.
*
* Output
*
* (I'm a little unsure of this code.)
* -------------------------------------------------------------
*/
int
{
int rv;
/* Crossbow cannot tell us to discard a stream. */
if (rv & NPI_FFLP_ERROR) {
" tcam__ad_write:"
" failed to write associated RAM @ %d",
return (NXGE_ERROR | rv);
}
return (NXGE_OK);
}
/*
* -------------------------------------------------------------
* nxge_classify_add
*
* Build & store a new TCAM entry.
*
* Input
* nxge
* flow A Crossbow flow description.
* resource The flow_resource_t data structure.
*
* Output
* 0 if successful; a standard error number otherwise.
*
* Algorithm
* 1. Check for illegal combinations of flows.
* 2. Allocate an nxge_tcam_entry_t data structure. This is
* where we keep all the data concerning this TCAM entry.
* 3. If this is a VLAN flow, initialize an entry in the VLAN
* table.
* 4. Build a TCAM key.
* 5. Write a TCAM entry.
* -------------------------------------------------------------
*/
int
{
int rv; /* Return Value */
"==> nxge_classify_add: "
"mask %lx / IP ver %d / proto %d / "
"rp: %d / lp: %d",
return (EINVAL);
/* Check for any possibly illegal combinations. */
return (EINVAL);
}
}
/*
* Allocate an nxge_tcam_entry_t.
*/
if (entry == 0)
return (ENOMEM);
/* Set up the RDC table, and offset that we will use. */
/* XXX Fix this later! */
/* VLAN flows are defined outside of the TCAM. */
if (vlan_flow) {
/* 802.1Q packets only. */
return (EINVAL);
/* There's no VID? */
return (EINVAL);
}
if (rv)
goto failure;
}
/* Has the ethernet type been set? */
if (en_flow) {
goto failure;
}
return (rv);
}
}
}
if (ip_flow) {
/* As far as I know, Crossbow always sets this bit. */
switch (flow->fd_ipversion) {
case IPV4_VERSION:
break;
case IPV6_VERSION:
break;
default:
return (EINVAL);
}
} else {
}
if (rv)
goto failure;
/* <tsel> may be 0. */
case 0:
break;
case -1:
goto failure;
default:
break;
}
}
/* It's possible that the only flow variable was the vlan id. */
return (0);
goto failure;
}
if (rv & NPI_FFLP_ERROR) {
" nxge_classify_add()"
goto failure;
}
/* Mark this slot as in use. */
if (entry)
return (rv);
}
/*
* -------------------------------------------------------------
* nxge_ipv4_key_build
*
* Build an ethernet TCAM key.
*
* Input
* nxge
* flow The Crossbow flow description
* spec A flow specification (going away).
* entry A TCAM entry data structure.
*
* Output
* 0 if successful; a standard error number otherwise.
*
* -------------------------------------------------------------
*/
int
/* ARGSUSED */
{
/* The class code. */
default:
case IPPROTO_TCP:
break;
case IPPROTO_UDP:
break;
case IPPROTO_SCTP:
break;
}
/* The Layer 2 RDC Table Number. */
} else {
/* We'll assume that it is remote, then local. */
/* That is, ordered as it is in a ULP header. */
/* tmp16 = ntohl(tmp16); */
}
/* tmp16 = ntohs(tmp16); */
}
}
}
/* tmp32 = ntohl(tmp32); */
}
}
/* Entry->region <= 4. */
return (0);
}
/*
* -------------------------------------------------------------
* nxge_ipv6_key_build
*
* Build an ethernet TCAM key.
*
* Input
* nxge
* flow The Crossbow flow description
* spec A flow specification (going away).
* entry A TCAM entry data structure.
*
* Output
* 0 if successful; a standard error number otherwise.
*
* -------------------------------------------------------------
*/
int
/* ARGSUSED */
{
/* The class code. */
default:
case IPPROTO_TCP:
break;
case IPPROTO_UDP:
break;
case IPPROTO_SCTP:
break;
}
/* The Layer 2 RDC Table Number. */
/* We'll assume that it is remote, then local. */
/* That is, ordered as it is in a ULP header. */
/* tmp16 = ntohl(tmp16); */
}
/* tmp16 = ntohs(tmp16); */
}
}
}
/* XXX Do we need to ntohl()? */
}
/* Add two (2) for the missing nxt_hdr & second address. */
/* Entry->region <= 4. */
return (0);
}
/*
* -------------------------------------------------------------
* nxge_en_key_build
*
* Build an ethernet TCAM key.
*
* Input
* nxge
* flow The Crossbow flow description
* spec A flow specification (going away).
* entry A TCAM entry data structure.
*
* Output
* 0 if successful; a standard error number otherwise.
*
* -------------------------------------------------------------
*/
int
{
/* Almost all of the key is a don't care. */
/* The class code. */
switch (vlan->ether_type) {
case ETHERTYPE_ARP:
return (0);
case ETHERTYPE_REVARP:
return (0);
default:
/* Are both programmable ethernet classes in use? */
return (EAGAIN);
break;
}
/* If ETYPE_1 is in use, then use ETYPE_2. */
pet++;
}
/* Mark this programmable ethertype IN USE. */
vlan->ether_type);
return (0);
}
/*
* -------------------------------------------------------------
* nxge_vid_enable
*
* Define and enable a hardware vlan table entry.
*
* Input
* nxge
* flow A Crossbow flow description.
* (All we need is the TCI).
*
* Output
* 0 if successful; a standard error number otherwise.
*
* -------------------------------------------------------------
*/
int
{
int vid;
/*
* A TCI looks like this:
* 15:13 12 11:0
* user_priority CFI VID
*
* All we want is the VID.
*/
" nxge_vid_enable:"
" vlan id '%d' unconfigured", vid));
return (EIO);
}
/* Enable the VLAN preference. */
" port %d / vid %d / RDC table %d",
if (status & NPI_FFLP_ERROR) {
" nxge_vid_enable:"
" npi_fflp_cfg_enet_vlan_table_assoc() failed"));
return (EIO);
}
return (0);
}
/*
* -------------------------------------------------------------
* nxge_vid_disable
*
* Disnable a hardware vlan table entry.
*
* Input
* nxge
* flow A Crossbow flow description.
* (All we need is the TCI).
*
* Output
* 0 if successful; a standard error number otherwise.
*
* -------------------------------------------------------------
*/
int
{
int vid;
/* Disable the VLAN preference. */
" nxge_vid_disable:"
" port %d / vid %d / RDC table %d)",
if (status & NPI_FFLP_ERROR) {
" nxge_vid_disable:"
" npi_fflp_cfg_enet_vlan_table_assoc(failed)"));
return (EIO);
}
return (0);
}
/*
* -------------------------------------------------------------
* nxge_altmac_enable
*
* Add a Layer 2 classification rule.
*
* Input
* nxge
* flow The Crossbow flow description
*
* Output
* 0 if successful; a standard error number otherwise.
*
* -------------------------------------------------------------
*/
int
{
int i;
/* Verify that the alternate address has been configured. */
continue;
}
/* We found a match. */
break;
}
}
return (EINVAL);
}
return (0);
}
/*
* -------------------------------------------------------------
* nxge_m_classify_add
*
* Add a hardware classification rule.
*
* Input
* arg An nxge_t data structure.
* mrh Receive Completion Ring pointer.
* flow A Crossbow flow description
*
* The next 3 args are provided by Crossbow, & are opaque.
*
* rx_func A Crossbow receive function pointer.
* rx_arg1 The first argument to <rx_func>.
* rx_arg2
*
* handle We will point this at a private data
* structure below. It will turn out to
* be a flow_resource_t.
*
* Output
* 0 if successful; a standard error number otherwise.
*
* -------------------------------------------------------------
*/
int
/* ARGSUSED */
void *arg, /* p_nxge_t */
void *rx_arg1,
{
int rv;
"==> nxge_m_classify_add: arg $%p mrh $%p nxge $%p "
"rx_func $%p rx_arg1 $%p rx_arg2 $%p",
"nxge_tcam_ad_write: %s hardware uninitialized",
"Neptune" : "Niagara 2 NIU"));
return (ENODEV);
}
return (ENOMEM);
}
return (rv);
}
"==> nxge_m_classify_add (exit): "
"flow type %d "
"rcrp $%p arg $%p mrh $%p nxge $%p "
"rx_func $%p rx_arg1 $%p rx_arg2 $%p",
rcr_p->rcr_mac_handle));
return (0);
}
/*
* -------------------------------------------------------------
* nxge_m_classify_add
*
* Add a hardware classification rule.
*
* Input
* arg An nxge_t data structure.
* mrh Receive Completion Ring pointer.
* handle The private data structure we created in
* nxge_m_classify_add() above.
* A flow_resource_t.
*
* Output
* 0 if successful; a standard error number otherwise.
*
* -------------------------------------------------------------
*/
int
void *arg, /* p_nxge_t */
{
"==> nxge_m_classify_remove: "
"arg $%p mrh %p handle %p slot %d",
/* This slot may be reused. */
/* mac_rx_func() is no longer current. */
rcr_p->mac_rx_func = 0;
rcr_p->mac_rx_arg = 0;
/* This object may be a VLAN flow. */
}
/* This object may be a VLAN-only flow. */
/* Does this flow belong to a programmable class? */
/* If no one is using this class anymore, disable it. */
}
}
}
return (0);
}
/*
* -------------------------------------------------------------
* nxge_m_classify_update
*
* Update a hardware classification rule. In other words, change it.
*
* Input
* arg Receive Completion Ring pointer.
* rx_func The new Crossbow receive function.
* rx_arg The new first argument to <rx_func>.
* mrh What is this?
*
* Output
* 0 if successful; a standard error number otherwise.
*
* TBD: check if the ring is valid.
* -------------------------------------------------------------
*/
void
void *arg,
void *mac_rx_arg,
{
"==> nxge_m_classify_update: "
"rcrp $%p arg $%p mrh $%p nxge $%p "
"rx_func $%p mac_rx_arg $%p rcr_mac_handle $%p",
rcr_p->rcr_mac_handle));
}