vsw_ldc.c revision cdfc78ad277d82b1080e7c86213063d1b73e7789
/*
* 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"
#include <sys/sysmacros.h>
#include <sys/ethernet.h>
#include <sys/machsystm.h>
#include <sys/mac_ether.h>
#include <sys/mach_descrip.h>
#include <sys/vio_mailbox.h>
#include <sys/vnet_mailbox.h>
#include <sys/vnet_common.h>
#include <sys/vio_util.h>
static void vsw_marker_task(void *);
/* Interrupt routines */
/* Handshake routines */
static void vsw_ldc_reinit(vsw_ldc_t *);
static void vsw_conn_task(void *);
static void vsw_next_milestone(vsw_ldc_t *);
static int vsw_supported_version(vio_ver_msg_t *);
/* Data processing routines */
static void vsw_process_pkt(void *);
static void vsw_process_ctrl_pkt(void *);
static void vsw_process_ctrl_ver_pkt(vsw_ldc_t *, void *);
static void vsw_process_ctrl_attr_pkt(vsw_ldc_t *, void *);
static void vsw_process_ctrl_mcst_pkt(vsw_ldc_t *, void *);
static void vsw_process_ctrl_dring_reg_pkt(vsw_ldc_t *, void *);
static void vsw_process_ctrl_dring_unreg_pkt(vsw_ldc_t *, void *);
static void vsw_process_ctrl_rdx_pkt(vsw_ldc_t *, void *);
uint32_t);
static void vsw_process_data_dring_pkt(vsw_ldc_t *, void *);
static void vsw_process_pkt_data_nop(void *, void *, uint32_t);
static void vsw_process_pkt_data(void *, void *, uint32_t);
static void vsw_process_data_ibnd_pkt(vsw_ldc_t *, void *);
/* Packet creation routines */
static void vsw_send_ver(void *);
static void vsw_send_attr(vsw_ldc_t *);
static void vsw_send_dring_info(vsw_ldc_t *);
static void vsw_send_rdx(vsw_ldc_t *);
/* Dring routines */
static void vsw_create_privring(vsw_ldc_t *);
int *);
static int vsw_check_dring_info(vio_dring_reg_msg_t *);
static void vsw_ldc_tx_worker(void *arg);
static void vsw_ldc_rx_worker(void *arg);
/* Misc support routines */
static int vsw_free_ring(dring_info_t *);
/* Debugging routines */
static void dump_flags(uint64_t);
static void display_state(void);
static void display_lane(lane_t *);
static void display_ring(dring_info_t *);
/*
* Functions imported from other files.
*/
extern void vsw_reconfig_hw(vsw_t *);
/*
* Tunables used in this file.
*/
extern int vsw_num_handshakes;
extern int vsw_wretries;
extern int vsw_desc_delay;
extern int vsw_read_attempts;
extern int vsw_ldc_tx_delay;
extern int vsw_ldc_tx_retries;
extern boolean_t vsw_ldc_rxthr_enabled;
extern boolean_t vsw_ldc_txthr_enabled;
extern uint32_t vsw_max_tx_qcount;
extern uint32_t vsw_chain_len;
extern uint32_t vsw_mblk_size1;
extern uint32_t vsw_mblk_size2;
extern uint32_t vsw_mblk_size3;
extern uint32_t vsw_num_mblks1;
extern uint32_t vsw_num_mblks2;
extern uint32_t vsw_num_mblks3;
#define LDC_ENTER_LOCK(ldcp) \
#define LDC_EXIT_LOCK(ldcp) \
/* supported versions */
/*
* For the moment the state dump routines have their own
* private flag.
*/
#define DUMP_STATE 0
#if DUMP_STATE
{ \
}
#define DUMP_TAG_PTR(tag) \
{ \
}
#define DISPLAY_STATE() display_state()
#else
#define DUMP_TAG_PTR(tag)
#define DUMP_FLAGS(state)
#define DISPLAY_STATE()
#endif /* DUMP_STATE */
/*
* Attach the specified port.
*
* Returns 0 on success, 1 on failure.
*/
int
{
vsw_port_t *p, **pp;
int i;
/* port already exists? */
__func__, p->p_instance);
return (1);
}
}
for (i = 0; i < nids; i++) {
return (1);
}
}
/*
* If the underlying physical device has been setup,
* program the mac address of this port in it.
* Otherwise, port macaddr will be set after the physical
* device is successfully setup by the timeout handler.
*/
}
/* link it into the list of ports for this vsw instance */
/*
* Initialise the port and any ldc's under it.
*/
(void) vsw_init_ldcs(port);
return (0);
}
/*
* Detach the specified port.
*
* Returns 0 on success, 1 on failure.
*/
int
{
return (1);
}
return (1);
}
/* cleanup any HybridIO for this port */
/*
* No longer need to hold writer lock on port list now
* that we have unlinked the target port from the list.
*/
/* Remove any multicast addresses.. */
/* Remove address if was programmed into HW. */
/*
* Port's address may not have been set in hardware. This could
* happen if the underlying physical device is not yet available and
* vsw_setup_switching_timeout() may be in progress.
* We remove its addr from hardware only if it has been set before.
*/
if (vswp->recfg_reqd)
if (vsw_port_delete(port)) {
return (1);
}
return (0);
}
/*
* Detach all active ports.
*
* Returns 0 on success, 1 on failure.
*/
int
{
return (1);
}
/* Remove address if was programmed into HW. */
/* Remove any multicast addresses.. */
/*
* No longer need to hold the lock on the port list
* now that we have unlinked the target port from the
* list.
*/
if (vsw_port_delete(port)) {
return (1);
}
}
return (0);
}
/*
* Delete the specified port.
*
* Returns 0 on success, 1 on failure.
*/
static int
{
int num_ldcs;
(void) vsw_uninit_ldcs(port);
/*
* Wait for any pending ctrl msg tasks which reference this
* port to finish.
*/
if (vsw_drain_port_taskq(port))
return (1);
/*
* Wait for any active callbacks to finish
*/
if (vsw_drain_ldcs(port))
return (1);
while (num_ldcs > 0) {
return (1);
}
num_ldcs--;
}
}
return (0);
}
/*
* Attach a logical domain channel (ldc) under a specified port.
*
* Returns 0 on success, 1 on failure.
*/
static int
{
int status = DDI_FAILURE;
int rv;
char kname[MAXNAMELEN];
PROG_tx_thread = 0x8}
return (1);
}
/* Allocate pools of receive mblks */
if (rv) {
return (1);
}
progress |= PROG_mblks;
/* required for handshake with peer */
ldcp->peer_session = 0;
ldcp->session_status = 0;
/* only set for outbound lane, inbound set by peer */
if (status != 0) {
goto ldc_attach_fail;
}
if (vsw_ldc_rxthr_enabled) {
ldcp->rx_thr_flags = 0;
goto ldc_attach_fail;
}
}
if (vsw_ldc_txthr_enabled) {
ldcp->tx_thr_flags = 0;
goto ldc_attach_fail;
}
}
if (status != 0) {
goto ldc_attach_fail;
}
/*
* allocate a message for ldc_read()s, big enough to hold ctrl and
* data msgs, including raw data msgs used to recv priority frames.
*/
goto ldc_attach_fail;
}
goto ldc_attach_fail;
}
/* link it into the list of channels for this port */
return (0);
if (progress & PROG_callback) {
}
if (progress & PROG_rx_thread) {
}
}
if (progress & PROG_tx_thread) {
}
}
}
if (progress & PROG_mblks) {
}
return (1);
}
/*
* Detach a logical domain channel (ldc) belonging to a
* particular port.
*
* Returns 0 on success, 1 on failure.
*/
static int
{
int rv;
break;
}
}
/* specified ldc id not found */
return (1);
}
/* Stop the receive thread */
}
/* Stop the tx thread */
}
}
/* Destory kstats */
/*
* Before we can close the channel we must release any mapped
* resources (e.g. drings).
*/
/*
* If the close fails we are in serious trouble, as won't
* be able to delete the parent port.
*/
return (1);
}
/*
* Most likely some mblks are still in use and
* have not been returned to the pool. These mblks are
* added to the pool that is maintained in the device instance.
* Another attempt will be made to destroy the pool
* when the device detaches.
*/
/* unlink it from the list */
return (0);
}
/*
* Open and attempt to bring up the channel. Note that channel
* can only be brought up if peer has also opened channel.
*
* Returns 0 if can open and bring up channel, otherwise
* returns 1.
*/
static int
{
ldc_status_t istatus = 0;
int rv;
/* don't start at 0 in case clients don't like that */
if (rv != 0) {
return (1);
}
return (1);
return (1);
}
if (rv != 0) {
/*
* Not a fatal error for ldc_up() to fail, as peer
* end point may simply not be ready yet.
*/
return (1);
}
/*
* ldc_up() call is non-blocking so need to explicitly
* check channel status to see if in fact the channel
* is UP.
*/
return (1);
}
return (0);
}
return (0);
}
/* disable callbacks on the channel */
static int
{
int rv;
if (rv != 0) {
return (1);
}
return (0);
}
static int
{
(void) vsw_ldc_init(ldcp);
}
return (0);
}
static int
{
(void) vsw_ldc_uninit(ldcp);
}
return (0);
}
/*
* Wait until the callback(s) associated with the ldcs under the specified
* port have completed.
*
* Prior to this function being invoked each channel under this port
* should have been quiesced via ldc_set_cb_mode(DISABLE).
*
* A short explaination of what we are doing below..
*
* The simplest approach would be to have a reference counter in
* the ldc structure which is increment/decremented by the callbacks as
* they use the channel. The drain function could then simply disable any
* further callbacks and do a cv_wait for the ref to hit zero. Unfortunately
* there is a tiny window here - before the callback is able to get the lock
* on the channel it is interrupted and this function gets to execute. It
* sees that the ref count is zero and believes its free to delete the
* associated data structures.
*
* We get around this by taking advantage of the fact that before the ldc
* framework invokes a callback it sets a flag to indicate that there is a
* callback active (or about to become active). If when we attempt to
* unregister a callback when this active flag is set then the unregister
* will fail with EWOULDBLOCK.
*
* If the unregister fails we do a cv_timedwait. We will either be signaled
* by the callback as it is exiting (note we have to wait a short period to
* allow the callback to return fully to the ldc framework and it to clear
* the active flag), or by the timer expiring. In either case we again attempt
* the unregister. We repeat this until we can succesfully unregister the
* callback.
*
* The reason we use a cv_timedwait rather than a simple cv_wait is to catch
* the case where the callback has finished but the ldc framework has not yet
* cleared the active flag. In this case we would never get a cv_signal.
*/
static int
{
/*
* If we can unregister the channel callback then we
* know that there is no callback either running or
* scheduled to run for this channel so move on to next
* channel in the list.
*/
/* prompt active callbacks to quit */
continue;
} else {
/*
* If we end up here we know that either 1) a callback
* is currently executing, 2) is about to start (i.e.
* the ldc framework has set the active flag but
* has not actually invoked the callback yet, or 3)
* has finished and has returned to the ldc framework
* but the ldc framework has not yet cleared the
* active bit.
*
* Wait for it to finish.
*/
== EWOULDBLOCK)
}
}
return (0);
}
/*
* Wait until all tasks which reference this port have completed.
*
* Prior to this function being invoked each channel under this port
* should have been quiesced via ldc_set_cb_mode(DISABLE).
*/
static int
{
/*
* Mark the port as in the process of being detached, and
* dispatch a marker task to the queue so we know when all
* relevant tasks have completed.
*/
__func__);
return (1);
}
/*
* Wait for the marker task to finish.
*/
return (0);
}
static void
vsw_marker_task(void *arg)
{
/*
* No further tasks should be dispatched which reference
* this port so ok to mark it as safe to detach.
*/
}
{
return (port);
}
}
return (NULL);
}
void
{
/*
* NOTE: for now, we will assume we have a single channel.
*/
return;
}
/*
* If the peer is vlan_unaware(ver < 1.3), reset channel and terminate
* the connection. See comments in vsw_set_vnet_proto_ops().
*/
}
}
void
{
/*
* NOTE: for now, we will assume we have a single channel.
*/
return;
}
/*
* If the peer is HybridIO capable (ver >= 1.3), reset channel
* to trigger re-negotiation, which inturn trigger HybridIO
*/
} else {
}
}
}
/*
* Search for and remove the specified port from the port
* list. Returns 0 if able to locate and remove port, otherwise
* returns 1.
*/
static int
{
return (1);
} else {
}
break;
} else {
}
}
return (0);
}
/*
* Interrupt handler for ldc messages.
*/
static uint_t
{
return (LDC_SUCCESS);
}
if (event & LDC_EVT_UP) {
/*
* Channel has come up.
*/
}
if (event & LDC_EVT_READ) {
/*
* Data available for reading.
*/
/*
* If the receive thread is enabled, then
* wakeup the receive thread to process the
* LDC messages.
*/
}
} else {
}
goto vsw_cb_exit;
}
}
/*
* Catch either LDC_EVT_WRITE which we don't support or any
* unknown event.
*/
if (event &
}
/*
* Let the drain function know we are finishing if it
* is waiting.
*/
return (LDC_SUCCESS);
}
/*
* Reinitialise data structures associated with the channel.
*/
static void
{
/* remove the port from vlans it has been assigned to */
/*
* Remove parent port from any multicast groups
* it may have registered with. Client must resend
* multicast add command after handshake completes.
*/
ldcp->peer_session = 0;
ldcp->session_status = 0;
}
/*
* Process a connection event.
*
* Note - care must be taken to ensure that this function is
* not called with the dlistrw lock held.
*/
static void
{
/*
* Check if either a reset or restart event is pending
* or in progress. If so just return.
*
* A VSW_CONN_RESET event originates either with a LDC_RESET_EVT
* being received by the callback handler, or a ECONNRESET error
* code being returned from a ldc_read() or ldc_write() call.
*
* A VSW_CONN_RESTART event occurs when some error checking code
* decides that there is a problem with data from the channel,
* and that the handshake should be restarted.
*/
return;
/*
* If it is an LDC_UP event we first check the recorded
* state of the channel. If this is UP then we know that
* the channel moving to the UP state has already been dealt
* with and don't need to dispatch a new task.
*
* The reason for this check is that when we do a ldc_up(),
* depending on the state of the peer, we may or may not get
* a LDC_UP event. As we can't depend on getting a LDC_UP evt
* every time we do ldc_up() we explicitly check the channel
* status to see has it come up (ldc_up() is asynch and will
* complete at some undefined time), and take the appropriate
* action.
*
* The flip side of this is that we may get a LDC_UP event
* when we have already seen that the channel is up and have
* dealt with that.
*/
if (evt == VSW_CONN_UP) {
return;
}
}
/*
* The transaction group id allows us to identify and discard
* any tasks which are still pending on the taskq and refer
* to the handshake session we are about to restart or reset.
* These stale messages no longer have any real meaning.
*/
goto err_exit;
}
DDI_NOSLEEP) != DDI_SUCCESS) {
goto err_exit;
}
return;
/*
* Have mostly likely failed due to memory shortage. Clear the flag so
* that future requests will at least be attempted and will hopefully
* succeed.
*/
ldcp->reset_active = 0;
}
/*
* Deal with events relating to a connection. Invoked from a taskq.
*/
static void
vsw_conn_task(void *arg)
{
/* can safely free now have copied out data */
return;
}
/*
* If we wish to restart the handshake on this channel, then if
* the channel is UP we bring it DOWN to flush the underlying
* ldc queue.
*/
}
/*
* re-init all the associated data structures.
*/
/*
* Bring the channel back up (note it does no harm to
* do this even if the channel is already UP, Just
* becomes effectively a no-op).
*/
/*
* Check if channel is now UP. This will only happen if
* peer has also done a ldc_up().
*/
return;
}
/* channel UP so restart handshake by sending version info */
if (curr_status == LDC_UP) {
" handshake attempts (%d) on channel %ld",
return;
}
if (vsw_obp_ver_proto_workaround == B_FALSE &&
DDI_NOSLEEP) != DDI_SUCCESS)) {
/*
* Don't count as valid restart attempt if couldn't
* send version msg.
*/
}
}
/*
* Mark that the process is complete by clearing the flag.
*
* Note is it possible that the taskq dispatch above may have failed,
* most likely due to memory shortage. We still clear the flag so
* future attempts will at least be attempted and will hopefully
* succeed.
*/
ldcp->reset_active = 0;
}
/*
* returns 0 if legal for event signified by flag to have
* occured at the time it did. Otherwise returns 1.
*/
int
{
else
switch (flag) {
case VSW_VER_INFO_RECV:
if (phase > VSW_MILESTONE0) {
return (1);
}
break;
case VSW_VER_ACK_RECV:
case VSW_VER_NACK_RECV:
if (!(state & VSW_VER_INFO_SENT)) {
return (1);
} else
state &= ~VSW_VER_INFO_SENT;
break;
case VSW_ATTR_INFO_RECV:
return (1);
}
break;
case VSW_ATTR_ACK_RECV:
case VSW_ATTR_NACK_RECV:
if (!(state & VSW_ATTR_INFO_SENT)) {
" or ATTR_NACK when in state %d\n",
return (1);
} else
state &= ~VSW_ATTR_INFO_SENT;
break;
case VSW_DRING_INFO_RECV:
if (phase < VSW_MILESTONE1) {
return (1);
}
break;
case VSW_DRING_ACK_RECV:
case VSW_DRING_NACK_RECV:
if (!(state & VSW_DRING_INFO_SENT)) {
" or DRING_NACK when in state %d\n",
return (1);
} else
state &= ~VSW_DRING_INFO_SENT;
break;
case VSW_RDX_INFO_RECV:
if (phase < VSW_MILESTONE3) {
return (1);
}
break;
case VSW_RDX_ACK_RECV:
case VSW_RDX_NACK_RECV:
if (!(state & VSW_RDX_INFO_SENT)) {
return (1);
} else
state &= ~VSW_RDX_INFO_SENT;
break;
case VSW_MCST_INFO_RECV:
if (phase < VSW_MILESTONE3) {
return (1);
}
break;
default:
return (1);
}
else
return (0);
}
void
{
case VSW_MILESTONE0:
/*
* If we haven't started to handshake with our peer,
* start to do so now.
*/
}
/*
* Only way to pass this milestone is to have successfully
* negotiated version info.
*/
/*
* Next milestone is passed when attribute
* information has been successfully exchanged.
*/
}
break;
case VSW_MILESTONE1:
/*
* Only way to pass this milestone is to have successfully
* negotiated attribute information.
*/
/*
* If the peer device has said it wishes to
* use descriptor rings then we send it our ring
* info, otherwise we just set up a private ring
* which we use an internal buffer
*/
VIO_DRING_MODE_V1_0))) {
}
}
break;
case VSW_MILESTONE2:
/*
* If peer has indicated in its attribute message that
* it wishes to use descriptor rings then the only way
* to pass this milestone is for us to have received
* valid dring info.
*
* If peer is not using descriptor rings then just fall
* through.
*/
VIO_DRING_MODE_V1_0))) {
break;
}
break;
case VSW_MILESTONE3:
/*
* Pass this milestone when all paramaters have been
* successfully exchanged and RDX sent in both directions.
*
* Mark outbound lane as available to transmit data.
*/
/* Start HIO if enabled and capable */
}
} else {
}
break;
case VSW_MILESTONE4:
break;
default:
}
}
/*
* Check if major version is supported.
*
* Returns 0 if finds supported major number, and if necessary
* adjusts the minor field.
*
* to next lowest support values, or to zero if no other values possible.
*/
static int
{
int i;
for (i = 0; i < VSW_NUM_VER; i++) {
/*
* Matching or lower major version found. Update
* minor number if necessary.
*/
vsw_versions[i].ver_minor);
}
return (0);
}
/*
* If the message contains a higher major version number, set
* and return false, so this message will get resent with
* these values.
*/
"values to %d, %d\n",
vsw_versions[i].ver_minor);
return (1);
}
}
/* No match was possible, zero out fields */
return (1);
}
/*
* Set vnet-protocol-version dependent functions based on version.
*/
static void
{
/*
* If the version negotiated with peer is >= 1.3,
* set the mtu in our attributes to max_frame_size.
*/
} else {
/*
* Pre-1.3 peers expect max frame size of ETHERMAX.
* We can negotiate that size with those peers provided the
* following conditions are true:
* - Our max_frame_size is greater only by VLAN_TAGSZ (4).
* - Only pvid is defined for our peer and there are no vids.
* untagged frames of max size ETHERMAX. Note that pvid of the
* peer can be different, as vsw has to serve the vnet in that
* vlan even if itself is not assigned to that vlan.
*/
}
}
/* Versions >= 1.2 */
if (VSW_PRI_ETH_DEFINED(vswp)) {
/*
* enable priority routines and pkt mode only if
* at least one pri-eth-type is specified in MD.
*/
/* set xfer mode for vsw_send_attr() */
} else {
/* no priority eth types defined in MD */
/* set xfer mode for vsw_send_attr() */
}
} else {
/* Versions prior to 1.2 */
}
}
/*
* Reset vnet-protocol-version dependent functions to v1.0.
*/
static void
{
/* set xfer mode for vsw_send_attr() */
}
/*
* Main routine for processing messages received over LDC.
*/
static void
vsw_process_pkt(void *arg)
{
int rv = 0;
/*
* If channel is up read messages until channel is empty.
*/
do {
if (rv != 0) {
}
/* channel has been reset */
if (rv == ECONNRESET) {
break;
}
if (msglen == 0) {
break;
}
/*
* Figure out what sort of packet we have gotten by
* examining the msg tag, and then switch it appropriately.
*/
switch (tagp->vio_msgtype) {
case VIO_TYPE_CTRL:
break;
case VIO_TYPE_DATA:
break;
case VIO_TYPE_ERR:
break;
default:
break;
}
} while (msglen);
}
/*
* Dispatch a task to process a VIO control message.
*/
static void
{
/*
* We need to handle RDX ACK messages in-band as once they
* are exchanged it is possible that we will get an
* immediate (legitimate) data packet.
*/
return;
"(ostate 0x%llx : hphase %d)", __func__,
return;
}
return;
}
/*
* Dispatch task to processing taskq if port is not in
* the process of being detached.
*/
__func__);
return;
}
} else {
}
}
/*
* Process a VIO ctrl message. Invoked from taskq.
*/
static void
vsw_process_ctrl_pkt(void *arg)
{
/* stale pkt check */
return;
}
/* session id check */
return;
}
}
/*
* Switch on vio_subtype envelope, then let lower routines
* decide if its an INFO, ACK or NACK packet.
*/
switch (env) {
case VIO_VER_INFO:
break;
case VIO_DRING_REG:
break;
case VIO_DRING_UNREG:
break;
case VIO_ATTR_INFO:
break;
case VNET_MCAST_INFO:
break;
case VIO_RDX:
break;
case VIO_DDS_INFO:
break;
default:
}
}
/*
* Version negotiation. We can end up here either because our peer
* has responded to a handshake message we have sent it, or our peer
* has initiated a handshake with us. If its the former then can only
* be ACK or NACK, if its the later can only be INFO.
*
* If its an ACK we move to the next stage of the handshake, namely
* attribute exchange. If its a NACK we see if we can specify another
* version, if we can't we stop.
*
* If it is an INFO we reset all params associated with communication
* in that direction over this channel (remember connection is
* essentially 2 independent simplex channels).
*/
void
{
/*
* cast it into the correct structure.
*/
case VIO_SUBTYPE_INFO:
/*
* Record the session id, which we will use from now
* until we see another VER_INFO msg. Even then the
* session id in most cases will be unchanged, execpt
* if channel was reset.
*/
}
/* Legal message at this time ? */
return;
/*
* First check the device class. Currently only expect
* to be talking to a network device. In the future may
* also talk to another switch.
*/
sizeof (vio_ver_msg_t), B_TRUE);
return;
} else {
}
/*
* Now check the version.
*/
if (vsw_supported_version(ver_pkt) == 0) {
/*
* Support this major version and possibly
* adjusted minor version.
*/
/* Store accepted values */
if (vsw_obp_ver_proto_workaround == B_TRUE) {
/*
* Send a version info message
* using the accepted version that
* we are about to ack. Also note that
* we send our ver info before we ack.
* Otherwise, as soon as receiving the
* ack, obp sends attr info msg, which
* breaks vsw_check_flag() invoked
* from vsw_process_ctrl_attr_pkt();
* as we also need VSW_VER_ACK_RECV to
* be set in lane_out.lstate, before
* we can receive attr info.
*/
}
} else {
/*
* pairing we support (if don't suuport any more
* versions then they will be set to zero.
*/
/* Store updated values */
}
sizeof (vio_ver_msg_t), B_TRUE);
break;
case VIO_SUBTYPE_ACK:
return;
/* Store updated values */
break;
case VIO_SUBTYPE_NACK:
return;
/*
* If our peer sent us a NACK with the ver fields set to
* zero then there is nothing more we can do. Otherwise see
* if we support either the version suggested, or a lesser
* one.
*/
"further.", __func__);
return;
}
/*
* Check to see if we support this major version or
* to zero.
*/
(void) vsw_supported_version(ver_pkt);
/* Nothing more we can do */
__func__);
} else {
/* found a supported major version */
sizeof (vio_ver_msg_t), B_TRUE);
}
break;
default:
}
}
/*
* Process an attribute packet. We can end up here either because our peer
* peer has sent us an attribute INFO message
*
* If its an ACK we then move to the next stage of the handshake which
* is to send our descriptor ring info to our peer. If its a NACK then
* there is nothing more we can (currently) do.
*
* If we get a valid/acceptable INFO packet (and we have already negotiated
* a version) we ACK back and set channel state to ATTR_RECV, otherwise we
* NACK back and reset channel state to INACTIV.
*
* FUTURE: in time we will probably negotiate over attributes, but for
* the moment unacceptable attributes are regarded as a fatal error.
*
*/
void
{
int i;
/*
* cast it into the correct structure.
*/
case VIO_SUBTYPE_INFO:
return;
/*
* If the attributes are unacceptable then we NACK back.
*/
sizeof (vnet_attr_msg_t), B_TRUE);
return;
}
/*
* Otherwise store attributes for this lane and update
* lane state.
*/
for (i = ETHERADDRL - 1; i >= 0; i--) {
macaddr >>= 8;
}
/* add the port to the specified vlans */
/* setup device specifc xmit routines */
}
/*
* HybridIO is supported only vnet, not by OBP.
* So, set hio_capable to true only when in DRING mode.
*/
} else {
}
sizeof (vnet_attr_msg_t), B_TRUE);
break;
case VIO_SUBTYPE_ACK:
return;
break;
case VIO_SUBTYPE_NACK:
return;
break;
default:
}
}
/*
* Process a dring info packet. We can end up here either because our peer
* peer has sent us a dring INFO message.
*
* If we get a valid/acceptable INFO packet (and we have already negotiated
* a version) we ACK back and update the lane state, otherwise we NACK back.
*
* FUTURE: nothing to stop client from sending us info on multiple dring's
* but for the moment we will just use the first one we are given.
*
*/
void
{
int dring_found = 0;
/*
* cast it into the correct structure.
*/
case VIO_SUBTYPE_INFO:
return;
/*
* If the dring params are unacceptable then we NACK back.
*/
if (vsw_check_dring_info(dring_pkt)) {
sizeof (vio_dring_reg_msg_t), B_TRUE);
return;
}
/*
* Otherwise, attempt to map in the dring using the
* cookie. If that succeeds we send back a unique dring
* identifier that the sending side will use in future
* to refer to this descriptor ring.
*/
/*
* Note: should only get one cookie. Enforced in
* the ldc layer.
*/
sizeof (ldc_mem_cookie_t));
sizeof (vio_dring_reg_msg_t), B_TRUE);
return;
}
sizeof (vio_dring_reg_msg_t), B_TRUE);
return;
} else {
/* store the address of the pub part of ring */
}
/* no private section as we are importing */
/*
* Using simple mono increasing int for ident at
* the moment.
*/
ldcp->next_ident++;
/*
* Link it onto the end of the list of drings
* for this lane.
*/
} else {
}
/* acknowledge it */
sizeof (vio_dring_reg_msg_t), B_TRUE);
break;
case VIO_SUBTYPE_ACK:
return;
/*
* Peer is acknowledging our dring info and will have
* sent us a dring identifier which we will use to
* refer to this ring w.r.t. our peer.
*/
/*
* Find the ring this ident should be associated
* with.
*/
dring_found = 1;
dring_found = 1;
break;
}
}
if (dring_found == 0) {
__func__);
return;
}
} else {
"allocated", __func__);
return;
}
/* store ident */
break;
case VIO_SUBTYPE_NACK:
return;
break;
default:
}
}
/*
* Process a request from peer to unregister a dring.
*
* For the moment we just restart the handshake if our
* peer endpoint attempts to unregister a dring.
*/
void
{
/*
* cast it into the correct structure.
*/
case VIO_SUBTYPE_INFO:
break;
case VIO_SUBTYPE_ACK:
break;
case VIO_SUBTYPE_NACK:
break;
default:
}
}
sizeof (vnet_mcast_msg_t), B_TRUE);
/*
* Process a multicast request from a vnet.
*
* Vnet's specify a multicast address that they are interested in. This
* address is used as a key into the hash table which forms the multicast
* forwarding database (mFDB).
*
* The table keys are the multicast addresses, while the table entries
* are pointers to lists of ports which wish to receive packets for the
* specified multicast address.
*
* When a multicast packet is being switched we use the address as a key
* into the hash table, and then walk the appropriate port list forwarding
* the pkt to each port in turn.
*
* If a vnet is no longer interested in a particular multicast grouping
* we simply find the correct location in the hash table and then delete
* the relevant port from the port list.
*
* To deal with the case whereby a port is being deleted without first
* removing itself from the lists in the hash table, we maintain a list
* of multicast addresses the port has registered an interest in, within
* the port structure itself. We then simply walk that list of addresses
* using them as keys into the hash table and remove the port from the
* appropriate lists.
*/
static void
{
int i;
/*
* cast it into the correct structure.
*/
case VIO_SUBTYPE_INFO:
/*
* Check if in correct state to receive a multicast
* message (i.e. handshake complete). If not reset
* the handshake.
*/
return;
/*
* Before attempting to add or remove address check
* that they are valid multicast addresses.
* If not, then NACK back.
*/
__func__);
return;
}
}
/*
* NACK back.
*/
return;
}
sizeof (vnet_mcast_msg_t), B_TRUE);
break;
case VIO_SUBTYPE_ACK:
/*
* We shouldn't ever get a multicast ACK message as
* at the moment we never request multicast addresses
* to be set on some other device. This may change in
* the future if we have cascading switches.
*/
return;
/* Do nothing */
break;
case VIO_SUBTYPE_NACK:
/*
* We shouldn't get a multicast NACK packet for the
* same reasons as we shouldn't get a ACK packet.
*/
return;
/* Do nothing */
break;
default:
}
}
static void
{
/*
* cast it into the correct structure.
*/
case VIO_SUBTYPE_INFO:
return;
sizeof (vio_rdx_msg_t), B_TRUE);
break;
case VIO_SUBTYPE_ACK:
/*
* Should be handled in-band by callback handler.
*/
break;
case VIO_SUBTYPE_NACK:
return;
break;
default:
}
}
static void
{
/* session id check */
return;
}
}
/*
* It is an error for us to be getting data packets
* before the handshake has completed.
*/
return;
}
/*
* To reduce the locking contention, release the
* ldc_cblock here and re-acquire it once we are done
* receiving packets.
*/
/*
* Switch on vio_subtype envelope, then let lower routines
* decide if its an INFO, ACK or NACK packet.
*/
if (env == VIO_DRING_DATA) {
} else if (env == VIO_PKT_DATA) {
} else if (env == VIO_DESC_DATA) {
} else {
}
}
sizeof (vio_dring_msg_t), B_TRUE);
static void
{
int read_attempts = 0;
struct ether_header *ehp;
/*
* cast it into the correct structure.
*/
/*
* Switch on the vio_subtype. If its INFO then we need to
* process the data. If its an ACK we need to make sure
* and if its a NACK then we maybe attempt a retry.
*/
case VIO_SUBTYPE_INFO:
return;
}
if (end == -1) {
num = -1;
} else if (end >= 0) {
/* basic sanity check */
"ring length %lld", __func__,
return;
}
} else {
return;
}
"descriptor at pos %d: err %d",
return;
}
/*
* When given a bounded range of descriptors
* to process, its an error to hit a descriptor
* which is not ready. In the non-bounded case
* (end_idx == -1) this simply indicates we have
* reached the end of the current active range.
*/
/* unbound - no error */
if (end == -1) {
if (read_attempts == vsw_read_attempts)
break;
goto vsw_recheck_desc;
}
/* bounded - error - so NACK back */
return;
}
/*
* If we ACK'd the previous descriptor then now
* record the new range start position for later
* ACK's.
*/
if (prev_desc_ack) {
range_start = pos;
}
/*
* Data is padded to align on 8 byte boundary,
* datalen is actual data length, i.e. minus that
* padding.
*/
/*
* Does peer wish us to ACK when we have finished
* with this descriptor ?
*/
ack_needed = B_TRUE;
" 0x%llx : dstate 0x%lx : datalen 0x%lx",
/*
* Mark that we are starting to process descriptor.
*/
/*
* Ensure that we ask ldc for an aligned
* number of bytes.
*/
/*
* No free receive buffers available, so
* fallback onto allocb(9F). Make sure that
* we get a data buffer which is a multiple
* of 8 as this is required by ldc_mem_copy.
*/
break;
}
}
if (rv != 0) {
"from %d cookies in desc %d (rv %d)",
break;
} else {
" using %d cookies", __func__,
}
/* adjust the read pointer to skip over the padding */
/* point to the actual end of data */
/* update statistics */
if (IS_BROADCAST(ehp))
else if (IS_MULTICAST(ehp))
/*
* IPALIGN space can be used for VLAN_TAG
*/
VSW_VNETPORT, mp);
/* build a chain of received packets */
/* first pkt */
chain = 1;
} else {
chain++;
}
/* mark we are finished with this descriptor */
/*
* Send an ACK back to peer if requested.
*/
if (ack_needed) {
sizeof (vio_dring_msg_t), B_FALSE);
/*
* Check if ACK was successfully sent. If not
* we break and deal with that below.
*/
if (msg_rv != 0)
break;
range_start = pos;
}
/* next descriptor */
cnt++;
/*
* Break out of loop here and stop processing to
* allow some other network device (or disk) to
* get access to the cpu.
*/
if (chain > vsw_chain_len) {
break;
}
}
/*
* If when we attempted to send the ACK we found that the
* channel had been reset then now handle this. We deal with
* it here as we cannot reset the channel while holding the
* continuously in the above loop, as a channel reset should
* be a rare event.
*/
if (msg_rv == ECONNRESET) {
break;
}
/* send the chain of packets to be switched */
}
/*
* We are now finished so ACK back with the state
* set to STOPPING so our peer knows we are finished
*/
/*
* We have not processed any more descriptors beyond
* the last one we ACK'd.
*/
if (prev_desc_ack)
sizeof (vio_dring_msg_t), B_TRUE);
break;
case VIO_SUBTYPE_ACK:
/*
* Verify that the relevant descriptors are all
* marked as DONE
*/
return;
}
/*
* If our peer is stopping processing descriptors then
* we check to make sure it has processed all the descriptors
* we have updated. If not then we send it a new message
* to prompt it to restart.
*/
/*
* Check next descriptor in public section of ring.
* If its marked as READY then we need to prompt our
* peer to start processing the ring again.
*/
/*
* Hold the restart lock across all of this to
* make sure that its not possible for us to
* decide that a msg needs to be sent in the future
* but the sending code having already checked is
* about to exit.
*/
sizeof (vio_dring_msg_t), B_FALSE);
} else {
}
}
/* only do channel reset after dropping dlistrw lock */
if (msg_rv == ECONNRESET)
break;
case VIO_SUBTYPE_NACK:
/*
* Something is badly wrong if we are getting NACK's
* for our data pkts. So reset the channel.
*/
break;
default:
}
}
/*
* dummy pkt data handler function for vnet protocol version 1.0
*/
static void
{
}
/*
* This function handles raw pkt data messages received over the channel.
* Currently, only priority-eth-type frames are received through this mechanism.
* In this case, the frame(data) is present within the message itself which
* is copied into an mblk before switching it.
*/
static void
{
return;
}
"unable to process priority frame\n", __func__,
return;
}
}
/* skip over the extra space for vlan tag */
/* copy the frame from the payload of raw data msg into the mblk */
/* update stats */
/*
* VLAN_TAGSZ of extra space has been pre-alloc'd if tag is needed.
*/
/* switch the frame to destination */
}
/*
* Process an in-band descriptor message (most likely from
* OBP).
*/
static void
{
int i, rv;
int j = 0;
case VIO_SUBTYPE_INFO:
return;
/*
* Data is padded to align on a 8 byte boundary,
* nbytes is actual data length, i.e. minus that
* padding.
*/
/*
* allocb(9F) returns an aligned data block. We
* need to ensure that we ask ldc for an aligned
* number of bytes also.
*/
if (nbytes & 0x7) {
}
/* alloc extra space for VLAN_TAG */
return;
}
/* skip over the extra space for VLAN_TAG */
if (rv != 0) {
return;
}
/* point to the actual end of data */
/*
* We ACK back every in-band descriptor message we process
*/
sizeof (vnet_ibnd_desc_t), B_TRUE);
/*
* there is extra space alloc'd for VLAN_TAG
*/
/* send the packet to be switched */
break;
case VIO_SUBTYPE_ACK:
/* Verify the ACK is valid */
return;
}
return;
}
/*
* If the descriptor we are being ACK'ed for is not the
* one we expected, then pkts were lost somwhere, either
* when we tried to send a msg, or a previous ACK msg from
* our peer. In either case we now reclaim the descriptors
* in the range from the last ACK we received up to the
* current ACK.
*/
}
/*
* When we sent the in-band message to our peer we
* marked the copy in our private ring as READY. We now
* check that the descriptor we are being ACK'ed for is in
* fact READY, i.e. it is one we have shared with our peer.
*
* If its not we flag an error, but still reset the descr
* back to FREE.
*/
"READY (0x%lx)", __func__,
"datalen %ld", __func__,
}
/* release resources associated with sent msg */
}
/* update to next expected value */
break;
case VIO_SUBTYPE_NACK:
/*
* We should only get a NACK if our peer doesn't like
* something about a message we have sent it. If this
* happens we just release the resources associated with
* the message. (We are relying on higher layers to decide
* whether or not to resend.
*/
/* limit check */
return;
}
return;
}
/* move to correct location in ring */
/* release resources associated with sent msg */
break;
default:
}
}
static void
{
/*
* Error vio_subtypes have yet to be defined. So for
* the moment we can't do anything.
*/
}
/* transmit the packet over the given port */
int
{
int status = 0;
uint32_t n;
/*
* Note for now, we have a single channel.
*/
return (1);
}
count -= n;
if (count == 0) {
goto vsw_portsend_exit;
}
return (status);
}
/*
* Break up frames into 2 seperate chains: normal and
* priority, based on the frame type. The number of
* priority frames is also counted and returned.
*
* Params:
* vswp: pointer to the instance of vsw
* np: head of packet chain to be broken
* npt: tail of packet chain to be broken
*
* Returns:
* np: head of normal data packets
* npt: tail of normal data packets
* hp: head of high priority packets
* hpt: tail of high priority packets
*/
static uint32_t
{
int i;
struct ether_header *ehp;
for (i = 0; i < num_types; i++) {
/* high priority frame */
} else {
}
count++;
break;
}
}
if (i == num_types) {
/* normal data frame */
} else {
}
}
}
return (count);
}
/*
*/
static int
{
uint32_t n = 0;
/* gather any priority frames from the chain of packets */
/* transmit priority frames */
}
count -= n;
if (count == 0) {
/* no normal data frames to process */
return (0);
}
}
/*
* Wrapper function to transmit normal frames over the channel.
*/
static int
{
/*
* If the TX thread is enabled, then queue the
* ordinary frames and signal the tx thread.
*/
/*
* If we reached queue limit,
* do not queue new packets,
* drop them.
*/
goto exit;
}
} else {
}
} else {
}
}
exit:
return (0);
}
/*
* This function transmits the frame in the payload of a raw data
* (VIO_PKT_DATA) message. Thus, it provides an Out-Of-Band path to
* send special frames with high priorities, without going through
* the normal data path which uses descriptor ring mechanism.
*/
static void
{
int rv;
goto send_pkt_exit;
}
/* frame size bigger than available payload len of raw data msg ? */
goto send_pkt_exit;
}
/* alloc space for a raw data message */
goto send_pkt_exit;
}
/* copy frame into the payload of raw data message */
}
/* setup the raw data msg */
/* send the msg over ldc */
if (rv != 0) {
goto send_pkt_exit;
}
/* update stats */
}
/*
* Transmit the packet over the given LDC channel.
*
* The 'retries' argument indicates how many times a packet
* is retried before it is dropped. Note, the retry is done
* only for a resource related failure, for all other failures
* the packet is dropped immediately.
*/
static int
{
int i;
int rc;
int status = 0;
for (i = 0; i < retries; ) {
/*
* Send the message out using the appropriate
* transmit function which will free mblock when it
* is finished with it.
*/
}
if (status == LDC_TX_SUCCESS) {
break;
}
i++; /* increment the counter here */
/* If its the last retry, then update the oerror */
}
if (status != LDC_TX_NORESOURCES) {
/*
* No retrying required for errors un-related
* to resources.
*/
break;
}
} else {
/*
* If there is no dring or the xfer_mode is
* set to DESC_MODE(ie., OBP), then simply break here.
*/
break;
}
/*
* Delay only if none were reclaimed
* and its not the last retry.
*/
}
}
return (status);
}
/*
* Send packet out via descriptor ring to a logical device.
*/
static int
{
int idx;
int status = LDC_TX_SUCCESS;
/* TODO: make test a macro */
return (LDC_TX_FAILURE);
}
/*
* Note - using first ring only, this may change
* in the future.
*/
return (LDC_TX_FAILURE);
}
return (LDC_TX_FAILURE);
}
/*
* Find a free descriptor
*
* Note: for the moment we are assuming that we will only
* have one dring going from the switch to each of its
* peers. This may change in the future.
*/
/* nothing more we can do */
goto vsw_dringsend_free_exit;
} else {
}
/* copy data into the descriptor */
bufp += VNET_IPALIGN;
bufp += n;
}
/* update statistics */
if (IS_BROADCAST(ehp))
else if (IS_MULTICAST(ehp))
/*
* Determine whether or not we need to send a message to our
* peer prompting them to read our newly updated descriptor(s).
*/
if (dp->restart_reqd) {
/*
* Send a vio_dring_msg to peer to prompt them to read
* the updated descriptor ring.
*/
/* Note - for now using first ring */
/*
* If last_ack_recv is -1 then we know we've not
* received any ack's yet, so this must be the first
* msg sent, so set the start to the begining of the ring.
*/
} else {
}
sizeof (vio_dring_msg_t), B_TRUE);
return (status);
} else {
}
return (status);
}
/*
* Send an in-band descriptor message over ldc.
*/
static int
{
int idx, i;
int status = LDC_TX_SUCCESS;
static int warn_msg = 1;
return (LDC_TX_FAILURE);
}
/*
* only expect single dring to exist, which we use
* as an internal buffer, rather than a transfer channel.
*/
return (LDC_TX_FAILURE);
}
return (LDC_TX_FAILURE);
}
/*
* Find a free descriptor in our buffer ring
*/
if (warn_msg) {
warn_msg = 0;
}
/* nothing more we can do */
goto vsw_descrsend_free_exit;
} else {
warn_msg = 1;
}
/* copy data into the descriptor */
bufp += n;
}
/* create and send the in-band descp msg */
/*
* Copy the mem cookies describing the data from the
* private region of the descriptor ring into the inband
* descriptor.
*/
sizeof (ldc_mem_cookie_t));
}
sizeof (vnet_ibnd_desc_t), B_TRUE);
return (status);
}
static void
vsw_send_ver(void *arg)
{
if (vsw_obp_ver_proto_workaround == B_FALSE) {
} else {
/* use the major,minor that we've ack'd */
}
}
static void
{
/*
* Subtype is set to INFO by default
*/
/* payload copied from default settings for lane */
}
/*
* Create dring info msg (which also results in the creation of
* a dring).
*/
static vio_dring_reg_msg_t *
{
/*
* If we can't create a dring, obviously no point sending
* a message.
*/
return (NULL);
/* payload */
mp->dring_ident = 0;
return (mp);
}
static void
{
return;
}
sizeof (vio_dring_reg_msg_t), B_TRUE);
}
static void
{
}
/*
* Generic routine to send message out over ldc channel.
*
* It is possible that when we attempt to write over the ldc channel
* that we get notified that it has been reset. Depending on the value
* of the handle_reset flag we either handle that event here or simply
* notify the caller that the channel was reset.
*/
int
{
int rv;
}
}
do {
}
}
/*
* If channel has been reset we either handle it here or
* simply report back that it has been reset and let caller
* decide what to do.
*/
if (rv == ECONNRESET) {
/*
* N.B - must never be holding the dlistrw lock when
* we do a reset of the channel.
*/
if (handle_reset) {
}
}
return (rv);
}
/*
* Remove the specified address from the list of address maintained
* in this port node.
*/
{
if (devtype == VSW_VNETPORT) {
} else {
}
/* match found */
/* list head */
if (devtype == VSW_VNETPORT)
else
} else {
}
break;
} else {
}
}
if (devtype == VSW_VNETPORT)
else
return (curr_p);
}
/*
* Creates a descriptor ring (dring) and links it into the
* link of outbound drings for this channel.
*
* Returns NULL if creation failed.
*/
static dring_info_t *
{
int i;
/* create public section of ring */
goto create_fail_exit;
}
/*
* Get the base address of the public section of the ring.
*/
goto dring_fail_exit;
} else {
}
/*
* create private portion of ring
*/
goto dring_fail_exit;
}
/* haven't used any descriptors yet */
/* bind dring to the channel */
goto dring_fail_exit;
}
/*
* Only ever create rings for outgoing lane. Link it onto
* end of list.
*/
} else {
}
return (dp);
for (i = 0; i < vsw_ntxds; i++) {
(void) ldc_mem_free_handle(
priv_addr++;
}
(sizeof (vsw_private_desc_t) * vsw_ntxds));
}
return (NULL);
}
/*
* Create a ring consisting of just a private portion and link
* it into the list of rings for the outbound lane.
*
* These type of rings are used primarily for temporary data
* storage (i.e. as data buffers).
*/
void
{
/* no public section */
(sizeof (vsw_private_desc_t) * vsw_ntxds));
return;
}
/* haven't used any descriptors yet */
/*
* Only ever create rings for outgoing lane. Link it onto
* end of list.
*/
} else {
}
}
/*
* Setup the descriptors in the dring. Returns 0 on success, 1 on
* failure.
*/
int
{
static char *name = "vsw_setup_ring";
/* public section may be null but private should never be */
/*
* Allocate the region of memory which will be used to hold
* the data the descriptors will refer to.
*/
/*
* Initialise some of the private and public (if they exist)
* descriptor fields.
*/
for (i = 0; i < vsw_ntxds; i++) {
goto setup_ring_cleanup;
}
if (rv != 0) {
goto setup_ring_cleanup;
}
"invalid num of cookies (%d) for size 0x%llx",
goto setup_ring_cleanup;
} else {
for (j = 1; j < ncookies; j++) {
if (rv != 0) {
goto setup_ring_cleanup;
}
"size 0x%llx", name, j,
}
}
/* link pub and private sides */
sizeof (ldc_mem_cookie_t));
}
pub_addr++;
}
/*
* move to next element in the dring and the next
* position in the data buffer.
*/
priv_addr++;
}
return (0);
for (j = 0; j < i; j++) {
priv_addr++;
}
return (1);
}
/*
* Searches the private section of a ring for a free descriptor,
* starting at the location of the last free descriptor found
* previously.
*
* Returns 0 if free descriptor is available, and updates state
* of private descriptor to VIO_DESC_READY, otherwise returns 1.
*
* FUTURE: might need to return contiguous range of descriptors
* as dring info msg assumes all will be contiguous.
*/
static int
{
int ret = 1;
ret = 0;
}
/* ring full */
if (ret == 1) {
}
return (ret);
}
/*
* Map from a dring identifier to the ring itself. Returns
* pointer to ring or NULL if no match found.
*
* Should be called with dlistrw rwlock held as reader.
*/
static dring_info_t *
{
return (NULL);
} else {
if (dp->ident == ident)
return (dp);
if (dp->ident == ident)
break;
}
}
return (dp);
}
/*
* Set the default lane attributes. These are copied into
* the attr msg we send to our peer. If they are not acceptable
* then (currently) the handshake ends.
*/
static void
{
}
/*
* Verify that the attributes are acceptable.
*
* FUTURE: If some attributes are not acceptable, change them
* our desired values.
*/
static int
{
int ret = 0;
struct ether_addr ea;
ret = 1;
}
/* Only support MAC addresses at moment. */
ret = 1;
}
/*
* MAC address supplied by device should match that stored
* in the vsw-port OBP node. Need to decide what to do if they
* don't match, for the moment just warn but don't fail.
*/
"0x%llx doesn't match node address 0x%llx\n",
}
/*
* Ack freq only makes sense in pkt mode, in shared
* mode the ring descriptors say whether or not to
* send back an ACK.
*/
" in SHM mode\n");
ret = 1;
}
}
/*
* Note: for the moment we only support ETHER
* frames. This may change in the future.
*/
ret = 1;
}
return (ret);
}
/*
* Returns 1 if there is a problem, 0 otherwise.
*/
static int
{
int ret = 0;
if ((pkt->num_descriptors == 0) ||
(pkt->descriptor_size == 0) ||
ret = 1;
}
return (ret);
}
/*
* Returns 1 if two memory cookies match. Otherwise returns 0.
*/
static int
{
return (0);
} else {
return (1);
}
}
/*
* Returns 1 if ring described in reg message matches that
* described by dring_info structure. Otherwise returns 0.
*/
static int
{
return (0);
} else {
return (1);
}
}
static caddr_t
{
a[0], a[1], a[2], a[3], a[4], a[5]);
return (ebuf);
}
/*
* Reset and free all the resources associated with
* the channel.
*/
static void
{
int rv = 0;
} else {
}
}
} else {
/*
* unbind, destroy exported dring, free dring struct
*/
}
if (rv == 0) {
}
}
}
/*
* Free ring and all associated resources.
*
* Should be called with dlistrw rwlock held as writer.
*/
static int
{
int i, rv = 1;
/*
* First unbind and free the memory handles
* stored in each descriptor within the ring.
*/
for (i = 0; i < vsw_ntxds; i++) {
paddr = (vsw_private_desc_t *)
if (rv != 0) {
"unbinding handle for "
"ring 0x%llx at pos %d",
dp, i);
return (rv);
}
}
if (rv != 0) {
"handle for ring 0x%llx "
"at pos %d", dp, i);
return (rv);
}
}
}
(sizeof (vsw_private_desc_t) * vsw_ntxds));
}
/*
* Now unbind and destroy the ring itself.
*/
}
}
}
return (0);
}
/*
* vsw_ldc_rx_worker -- A per LDC worker thread to receive data.
* This thread is woken up by the LDC interrupt handler to process
* LDC packets and receive data.
*/
static void
vsw_ldc_rx_worker(void *arg)
{
"vsw_rx_thread");
/*
* Wait until the data is received or a stop
* request is received.
*/
while (!(ldcp->rx_thr_flags &
(VSW_WTHR_DATARCVD | VSW_WTHR_STOP))) {
}
/*
* First process the stop request.
*/
break;
}
}
/*
* Update the run status and wakeup the thread that
* has sent the stop request.
*/
thread_exit();
}
/* vsw_stop_rx_thread -- Co-ordinate with receive thread to stop it */
static void
{
/*
* Send a stop request by setting the stop flag and
* wait until the receive thread stops.
*/
}
}
}
/*
* vsw_ldc_tx_worker -- A per LDC worker thread to transmit data.
* This thread is woken up by the vsw_portsend to transmit
* packets.
*/
static void
vsw_ldc_tx_worker(void *arg)
{
"vnet_tx_thread");
/*
* Wait until the data is received or a stop
* request is received.
*/
}
/*
* First process the stop request.
*/
break;
}
}
}
/*
* Update the run status and wakeup the thread that
* has sent the stop request.
*/
thread_exit();
}
/* vsw_stop_tx_thread -- Co-ordinate with receive thread to stop it */
static void
{
/*
* Send a stop request by setting the stop flag and
* wait until the receive thread stops.
*/
}
}
}
/* vsw_reclaim_dring -- reclaim descriptors */
static int
{
int i, j, len;
j = 0;
break;
}
/* clear all the fields */
}
return (j);
}
/*
* Debugging routines
*/
static void
display_state(void)
{
"status %d : phase %u\n",
}
}
}
}
static void
{
}
}
static void
{
uint64_t i;
uint64_t priv_count = 0;
for (i = 0; i < vsw_ntxds; i++) {
pub_count++;
}
priv_count++;
}
}
i, priv_count, pub_count);
}
static void
{
int i;
typedef struct flag_name {
int flag_val;
char *flag_name;
} flag_name_t;
flag_name_t flags[] = {
VSW_VER_INFO_SENT, "VSW_VER_INFO_SENT",
VSW_VER_INFO_RECV, "VSW_VER_INFO_RECV",
VSW_VER_ACK_RECV, "VSW_VER_ACK_RECV",
VSW_VER_ACK_SENT, "VSW_VER_ACK_SENT",
VSW_VER_NACK_RECV, "VSW_VER_NACK_RECV",
VSW_VER_NACK_SENT, "VSW_VER_NACK_SENT",
VSW_ATTR_INFO_SENT, "VSW_ATTR_INFO_SENT",
VSW_ATTR_INFO_RECV, "VSW_ATTR_INFO_RECV",
VSW_ATTR_ACK_SENT, "VSW_ATTR_ACK_SENT",
VSW_ATTR_ACK_RECV, "VSW_ATTR_ACK_RECV",
VSW_ATTR_NACK_SENT, "VSW_ATTR_NACK_SENT",
VSW_ATTR_NACK_RECV, "VSW_ATTR_NACK_RECV",
VSW_DRING_INFO_SENT, "VSW_DRING_INFO_SENT",
VSW_DRING_INFO_RECV, "VSW_DRING_INFO_RECV",
VSW_DRING_ACK_SENT, "VSW_DRING_ACK_SENT",
VSW_DRING_ACK_RECV, "VSW_DRING_ACK_RECV",
VSW_DRING_NACK_SENT, "VSW_DRING_NACK_SENT",
VSW_DRING_NACK_RECV, "VSW_DRING_NACK_RECV",
VSW_RDX_INFO_SENT, "VSW_RDX_INFO_SENT",
VSW_RDX_INFO_RECV, "VSW_RDX_INFO_RECV",
VSW_RDX_ACK_SENT, "VSW_RDX_ACK_SENT",
VSW_RDX_ACK_RECV, "VSW_RDX_ACK_RECV",
VSW_RDX_NACK_SENT, "VSW_RDX_NACK_SENT",
VSW_RDX_NACK_RECV, "VSW_RDX_NACK_RECV",
VSW_MCST_INFO_SENT, "VSW_MCST_INFO_SENT",
VSW_MCST_INFO_RECV, "VSW_MCST_INFO_RECV",
VSW_MCST_ACK_SENT, "VSW_MCST_ACK_SENT",
VSW_MCST_ACK_RECV, "VSW_MCST_ACK_RECV",
VSW_MCST_NACK_SENT, "VSW_MCST_NACK_SENT",
VSW_MCST_NACK_RECV, "VSW_MCST_NACK_RECV",
VSW_LANE_ACTIVE, "VSW_LANE_ACTIVE"};
for (i = 0; i < sizeof (flags)/sizeof (flag_name_t); i++) {
}
}