etm.c revision 25cf1a301a396c38e8adf52c15f537b80d2483f7
/*
* 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 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* FMA Event Transport Module
*
*/
#include <sys/sysmacros.h>
#include <pthread.h>
#include <strings.h>
#include <ctype.h>
#include <link.h>
#include <libnvpair.h>
#include "etm_xport_api.h"
#include "etm_proto.h"
/*
* ETM declarations
*/
typedef enum etm_connection_status {
C_UNINITIALIZED = 0,
C_OPEN, /* Connection is open */
C_CLOSED, /* Connection is closed */
C_LIMBO, /* Bad value in header from peer */
C_TIMED_OUT /* Reconnection to peer timed out */
typedef enum etm_fmd_queue_status {
Q_UNINITIALIZED = 100,
Q_INIT_PENDING, /* Queue initialization in progress */
Q_OPEN, /* Queue is open */
Q_SUSPENDED /* Queue is suspended */
} etm_qstat_t;
/* Per endpoint data */
typedef struct etm_endpoint_map {
char *epm_ep_str; /* Endpoint ID string */
int epm_xprtflags; /* FMD transport open flags */
int epm_txbusy; /* Busy doing send/transmit */
int epm_timer_in_use; /* Indicates if timer is in use */
struct etm_endpoint_map *epm_next;
} etm_epmap_t;
#define ETM_CLIENT_XPRT_FLAGS FMD_XPRT_RDWR
(x)++; \
(void) pthread_mutex_unlock(&Etm_mod_lock); \
}
(x)--; \
(void) pthread_mutex_unlock(&Etm_mod_lock); \
}
(x) += (y); \
(void) pthread_mutex_unlock(&Etm_mod_lock); \
}
/*
* Global variables
*/
/* Protects globals */
static int Etm_dump = 0; /* Enables hex dump for debug */
static int Etm_exit = 0; /* Flag for exit */
/* Module statistics */
static struct etm_stats {
/* read counters */
/* write counters */
/* error counters */
/* misc */
} Etm_stats = {
/* read counters */
/* write counters */
/* ETM error counters */
/* ETM Misc */
};
/*
* ETM Private functions
*/
/*
* Hex dump for debug.
*/
static void
{
int i, j, k;
int16_t *c;
if (Etm_dump == 0)
return;
if (direction)
else
/* Dump the complete 8-column rows */
for (i = 0; i < j; i++) {
*(c+0), *(c+1), *(c+2), *(c+3),
*(c+4), *(c+5), *(c+6), *(c+7));
}
/* Dump the last (incomplete) row */
switch (k) {
case 4:
break;
case 8:
*(c+2), *(c+3));
break;
case 12:
*(c+1), *(c+2), *(c+3), *(c+4), *(c+5));
break;
}
}
/*
* Provide the length of a message based on the data in the given ETM header.
*/
static size_t
etm_get_msglen(void *buf)
{
}
/*
* Check the contents of the ETM header for errors.
* Return the header type (hdr_type).
*/
static int
{
return (ETM_HDR_INVALID);
}
/* Until version is negotiated, other fields may be wrong */
}
return (ETM_HDR_BADVERSION);
}
return (ETM_HDR_BADTYPE);
}
}
/*
* Create an ETM header of a given type in the given buffer.
* Return length of header.
*/
static size_t
{
return (ETM_HDRLEN);
}
/*
* Convert message bytes to nvlist and post to fmd.
* Return zero for success, non-zero for failure.
*
* Note : nvl is free'd by fmd.
*/
static int
{
int rv;
return (1);
}
(void) pthread_mutex_lock(&Etm_mod_lock);
if (!Etm_exit) {
(void) pthread_mutex_unlock(&Etm_mod_lock);
rv = 0;
if (mp->epm_timer_in_use) {
mp->epm_timer_in_use = 0;
}
mp->epm_ep_str);
rv = 0;
} else {
rv = 2;
}
} else {
(void) pthread_mutex_unlock(&Etm_mod_lock);
rv = 3;
}
return (rv);
}
/*
* Handle the startup handshake to the server. The client always initiates
* the startup handshake. In the following sequence, we are the client and
* the remote endpoint is the server.
*
* Client sends C_HELLO and transitions to Q_INIT_PENDING state.
* Server sends S_HELLO and transitions to Q_INIT_PENDING state.
* Client sends ACK and transitions to Q_OPEN state.
* Server receives ACK and transitions to Q_OPEN state.
*
* Return 0 for success, nonzero for failure.
*/
static int
{
int hdrstat;
char hbuf[ETM_HDRLEN];
return (1);
mp->epm_ep_str);
return (2);
}
mp->epm_ep_str);
return (3);
}
if (hdrstat != ETM_HDR_S_HELLO) {
return (4);
}
/*
* Get version from the server.
* Currently, only one version is supported.
*/
return (5);
}
mp->epm_ep_str);
return (6);
}
/*
* Call fmd_xprt_open and fmd_xprt_setspecific with
* Etm_mod_lock held to avoid race with etm_send thread.
*/
(void) pthread_mutex_lock(&Etm_mod_lock);
mp->epm_ep_str);
}
(void) pthread_mutex_unlock(&Etm_mod_lock);
return (0);
}
/*
* Alloc a nvlist and add a string for the endpoint.
* Return zero for success, non-zero for failure.
*/
static int
{
/*
* Cannot use nvlist_xalloc(3NVPAIR) due to a recursive mutex situation
* in fmd when this nvlist_t is free'd.
*/
return (1);
}
return (0);
}
/*
* Free the nvlist for the endpoint_id string.
*/
/*ARGSUSED*/
static void
{
}
/*
*/
/*ARGSUSED*/
static int
{
return (1);
return (0);
}
/*
* Attempt to re-open a connection with the remote endpoint.
*/
static void
{
mp->epm_ep_str);
} else {
mp->epm_ep_str);
mp->epm_reconn_end = 0;
}
} else {
mp->epm_ep_str);
mp->epm_reconn_end = 0;
}
}
}
}
/*
* Suspend a given connection and setup for reconnection retries.
*/
static void
{
(void) pthread_mutex_lock(&Etm_mod_lock);
if (Etm_exit) {
(void) pthread_mutex_unlock(&Etm_mod_lock);
return;
}
(void) pthread_mutex_unlock(&Etm_mod_lock);
}
if (mp->epm_timer_in_use == 0) {
}
}
}
/*
* Reinitialize the connection. The old fmd_xprt_t handle must be
* Assume caller holds lock on epm_lock.
*/
static void
{
/*
* To avoid a deadlock, wait for etm_send to finish before
* calling fmd_xprt_close()
*/
while (mp->epm_txbusy)
/* mp->epm_ep_nvl is free'd in fmd_xprt_close */
}
if (mp->epm_timer_in_use) {
mp->epm_timer_in_use = 0;
}
}
}
/*
* Receive data from ETM transport layer.
* Note : This is not the fmdo_recv entry point.
*
*/
static int
{
void *buf;
char hbuf[ETM_HDRLEN];
hdrlen = ETM_HDRLEN;
mp->epm_ep_str);
return (EIO);
}
switch (hdrstat) {
case ETM_HDR_INVALID:
break;
case ETM_HDR_BADTYPE:
case ETM_HDR_BADVERSION:
mp->epm_ep_str);
return (EIO);
}
break;
case ETM_HDR_C_HELLO:
/* Client is initiating a startup handshake */
mp->epm_ep_str);
return (EIO);
}
rv = 0;
break;
case ETM_HDR_ACK:
/* This is client's ACK from startup handshake */
/* mp->epm_ep_nvl is free'd in fmd_xprt_close */
/*
* Call fmd_xprt_open and fmd_xprt_setspecific with
* Etm_mod_lock held to avoid race with etm_send thread.
*/
(void) pthread_mutex_lock(&Etm_mod_lock);
}
(void) pthread_mutex_unlock(&Etm_mod_lock);
mp->epm_ep_str);
} else {
}
rv = 0;
break;
case ETM_HDR_SHUTDOWN:
mp->epm_ep_str);
/*
* A server shutdown is considered to be temporary.
* Prepare for reconnection.
*/
}
break;
case ETM_HDR_MSG:
/* Peer (client) is unaware that we've restarted */
ETM_HDR_S_RESTART, 0);
return (EIO);
}
return (ECANCELED);
}
mp->epm_ep_str);
return (EIO);
}
return (EIO);
}
mp->epm_ep_str);
return (EIO);
}
/*
* If we got this far and the current state of the
* LIMBO, then we should reinitialize it.
*/
}
if (mp->epm_timer_in_use) {
mp->epm_timer_in_use = 0;
}
mp->epm_ep_str);
}
rv = 0;
break;
default:
rv = 0;
}
return (rv);
}
/*
* ETM transport layer callback function.
* The transport layer calls this function to :
* (a) pass an incoming message (flag == ETM_CBFLAG_RECV)
* (b) tell us to reinitialize the connection (flag == ETM_CBFLAG_REINIT)
*/
static int
void *arg)
{
int rv = 0;
(void) pthread_mutex_lock(&Etm_mod_lock);
if (Etm_exit) {
(void) pthread_mutex_unlock(&Etm_mod_lock);
return (ECANCELED);
}
(void) pthread_mutex_unlock(&Etm_mod_lock);
switch (flag) {
case ETM_CBFLAG_RECV:
break;
case ETM_CBFLAG_REINIT:
/*
* Return ECANCELED so the transport layer will close the
* server connection. The transport layer is responsible for
* reestablishing this connection (should a connection request
* arrive from the peer).
*/
break;
default:
}
return (rv);
}
/*
* Allocate and initialize an etm_epmap_t struct for the given endpoint
* name string.
*/
static void
{
return;
}
newmap->epm_txbusy = 0;
return;
}
newmap->epm_ep_str);
return;
}
return;
}
}
/* Add this transport instance handle to the list */
Epmap_head = newmap;
}
/*
* Parse the given property list string and call etm_init_epmap
* for each endpoint.
*/
static void
{
char epname[MAXPATHLEN];
return;
/*
* Create a copy of eplist for parsing.
* Therefore, fmd_hdl_strdup/fmd_hdl_strfree cannot be used.
*/
/*
* The following are supported for the "client_list" and
* "server_list" properties :
*
* A space-separated list of endpoints.
* "dev:///dom0 dev:///dom1 dev:///dom2"
*
* An array syntax for a range of instances.
* "dev:///dom[0:2]"
*
* A combination of both.
* "dev:///dom0 dev:///dom[1:2]"
*/
/*
* This string is using array syntax.
* Check the string for correct syntax.
*/
"that includes : %s\n", ep);
continue;
}
/* expand the array syntax */
"that includes : %s[\n", prefix);
continue;
}
"that includes : %s[\n", prefix);
continue;
}
if (nlen > MAXPATHLEN) {
"exceeds MAXPATHLEN\n");
continue;
}
prefix, i);
}
} else {
}
}
}
/*
* Free the transport infrastructure for an endpoint.
*/
static void
{
char hbuf[ETM_HDRLEN];
/*
* If an etm_send thread is in progress, wait for it to finish.
* The etm_recv thread is managed by the transport layer and will
* be destroyed with etm_xport_fini().
*/
while (mp->epm_txbusy)
if (mp->epm_timer_in_use)
ETM_HDR_SHUTDOWN, 0);
hdrlen);
}
/* mp->epm_ep_nvl is free'd in fmd_xprt_close */
}
}
/*
* FMD entry points
*/
/*
* FMD fmdo_send entry point.
* Send an event to the remote endpoint and receive an ACK.
*/
static int
{
(void) pthread_mutex_lock(&Etm_mod_lock);
if (Etm_exit) {
(void) pthread_mutex_unlock(&Etm_mod_lock);
return (FMD_SEND_RETRY);
}
(void) pthread_mutex_unlock(&Etm_mod_lock);
mp->epm_txbusy++;
mp->epm_txbusy--;
return (FMD_SEND_RETRY);
}
}
mp->epm_txbusy--;
return (FMD_SEND_RETRY);
}
== NULL) {
mp->epm_txbusy--;
return (FMD_SEND_RETRY);
} else {
}
}
(void *) ep);
return (FMD_SEND_FAILED);
}
hdrlen = ETM_HDRLEN;
return (FMD_SEND_FAILED);
}
mp->epm_txbusy--;
mp->epm_ep_str);
return (FMD_SEND_RETRY);
}
mp->epm_txbusy--;
mp->epm_ep_str);
return (FMD_SEND_RETRY);
}
if (hdrstat == ETM_HDR_ACK) {
} else {
if (hdrstat == ETM_HDR_NAK) {
/* Peer received a bad value in the header */
}
rv = FMD_SEND_RETRY;
} else if (hdrstat == ETM_HDR_S_RESTART) {
/* Server has restarted */
/* mp->epm_ep_nvl is free'd in fmd_xprt_close */
if (mp->epm_timer_in_use == 0) {
}
}
/*
* fault.* or list.* events will be replayed if a
* transport is opened with the same auth.
* Other events will be discarded.
*/
} else {
rv = FMD_SEND_RETRY;
}
mp->epm_txbusy--;
return (rv);
}
mp->epm_txbusy--;
return (FMD_SEND_SUCCESS);
}
/*
* FMD fmdo_timeout entry point..
*/
/*ARGSUSED*/
static void
{
mp->epm_timer_in_use = 0;
/* Server has shutdown and we (client) need to reconnect */
}
} else {
}
}
/*
* FMD Module declarations
*/
static const fmd_hdl_ops_t etm_ops = {
NULL, /* fmdo_recv */
etm_timeout, /* fmdo_timeout */
NULL, /* fmdo_close */
NULL, /* fmdo_stats */
NULL, /* fmdo_gc */
etm_send, /* fmdo_send */
};
static const fmd_prop_t etm_props[] = {
};
static const fmd_hdl_info_t etm_info = {
};
/*
* Initialize the transport for use by ETM.
*/
void
{
char *propstr;
return; /* invalid data in configuration file */
}
/* Create global stats */
/* Get module properties */
return;
}
}
/*
* Teardown the transport
*/
void
{
(void) pthread_mutex_lock(&Etm_mod_lock);
Etm_exit = 1;
(void) pthread_mutex_unlock(&Etm_mod_lock);
mp = Epmap_head;
while (mp) {
}
}