/*
* 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
*/
/*
*/
#include <stdlib.h>
#include <stdio.h>
#include <strings.h>
#include <time.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <assert.h>
#include <umem.h>
#include <alloca.h>
#include <sys/processor.h>
#include <poll.h>
#include <pthread.h>
#include <signal.h>
#include <values.h>
#include <libscf.h>
#include <ctype.h>
#include "ldmsvcs_utils.h"
#include "ldom_alloc.h"
#include "ldom_utils.h"
"assertion failure in %s:%d: %s\n", \
#define FDS_VLDC \
"/devices/virtual-devices@100/channel-devices@200/" \
"/virtual-channel-client@1:ldmfma"
/* allow timeouts in sec that are nearly forever but small enough for an int */
#define MIN(x, y) ((x) < (y) ? (x) : (y))
/*
* functions in this file are for version 1.0 of FMA domain services
*/
{ 1, 0 }
};
/*
* information for each channel
*/
struct ldmsvcs_info {
int cv_twait;
};
/*
* struct listdata_s and struct poller_s are used to maintain the state of
* the poller thread. this thread is used to manage incoming messages and
* pass those messages onto the correct requesting thread. see the "poller
* functions" section for more details.
*/
struct listdata_s {
enum {
} status;
int fd;
};
static struct poller_s {
int doreset;
int doexit;
int nclients;
int list_len;
int pending_count;
} pollbase = {
0,
{-1, -1},
1,
0,
0,
NULL,
0,
0
};
static int
{
val = default_val;
}
}
return ((int)val);
}
static void
{
}
}
/*
* read size bytes of data from a streaming fd into buf
*/
static int
{
/*
* data may come in bits and pieces
*/
do {
continue; /* retry */
else
return (1);
}
} while (data_left > 0);
return (0);
}
/*
* poller functions
*
* at init time, a thread is created for the purpose of monitoring incoming
* messages and doing one of the following:
*
* 1. doing the initial handshake and version negotiation
*
* 2. handing incoming data off to the requesting thread (which is an fmd
* module or scheme thread)
*/
static int
{
void *pr;
int i;
if (payloadsize < prlen)
return (1);
return (1);
break;
}
}
/*
* now wait for receiving thread to read in the data
*/
}
return (0);
}
/*
* note that this function is meant to handle only DS_DATA messages
*/
static int
{
int ier;
ier = 0;
&twait);
if (ier == 0) {
/*
* need to add req_num to beginning of resp
*/
sizeof (uint64_t);
} else {
if (--(pollbase.pending_count) == 0)
}
}
return (ier);
}
static void
poller_add_client(void)
{
}
static void
poller_remove_client(void)
{
}
static int
{
index = i;
break;
}
}
if (index == -1) {
/*
* get to this point if list is not long enough.
* check for a runaway list. since requests are
* synchronous (clients send a request and need to
* wait for the result before returning) the size
* of the list cannot be much more than the number
* of clients.
*/
sizeof (struct listdata_s *));
sizeof (struct listdata_s *));
ldom_alloc(sizeof (struct listdata_s));
}
}
}
return (index);
}
static void
{
}
static void
{
/*
* Write a byte to the pipe to notify the poller thread to exit.
* Then wait for it to exit.
*/
}
}
/*
* perform the polling of incoming messages. manage any resets (usually
* due to one end of the connection being closed) as well as exit
* conditions.
*/
static void *
{
int ier;
for (;;) {
break;
}
int i;
while (pollbase.pending_count > 0)
}
continue;
} else if (ier == 2) {
/*
* start exit preparations
*/
continue;
}
/* fd got closed */
/* Receive a notification to exit */
pthread_exit((void *)NULL);
/* fail to read a message from the LDOM manager */
}
}
return (NULL);
}
/*
* create the polling thread
*/
static int
{
int rc = 0;
if (pollbase.polling_tid == 0) {
/*
* create a joinable polling thread for receiving messages
* The notify pipe is for stopping the thread
*/
poller_loop, lsp) != 0)
rc = 1;
}
return (rc);
}
/*
* Cleanup the polling thread
*/
static void
poller_fini(void)
{
int i;
/* stop the poller thread */
/* Free up the list of outstanding requests */
sizeof (struct listdata_s));
}
}
sizeof (struct listdata_s *));
}
}
/*
* utilities for message handlers
*/
static int
{
(void) pthread_mutex_lock(&mt);
(void) pthread_mutex_unlock(&mt);
return (ETIMEDOUT);
}
(void) pthread_mutex_unlock(&mt);
return (0);
}
/*
* Find the max and min version supported
*/
static void
{
int i;
for (i = 1; i < DS_NUM_VER; i++) {
}
}
/*
* check whether the major and minor numbers requested by remote ds client
* can be satisfied. if the requested major is supported, true is
* returned, and the agreed minor is returned in new_minor. if the
* requested major is not supported, the routine returns false, and the
* closest major is returned in *new_major, upon which the ds client should
* renegotiate. the closest major is the just lower that the requested
* major number.
*/
static boolean_t
{
int i = 0;
/*
* if the minimum version supported is greater than the version
* requested, return the lowest version supported
*/
*new_majorp = min_major;
return (B_FALSE);
}
/*
* if the largest version supported is lower than the version
* requested, return the largest version supported
*/
*new_majorp = max_major;
return (B_FALSE);
}
/*
* now we know that the requested version lies between the min and
* max versions supported. check if the requested major can be
* found in supported versions.
*/
for (i = 0; i < DS_NUM_VER; i++) {
*new_majorp = major;
break;
lower_major = major;
}
/*
* If no match is found, return the closest available number
*/
if (!found_match)
return (found_match);
}
/*
* return 0 if service is added; 1 if service is a duplicate
*/
static int
{
int i, rc;
break;
}
}
return (0); /* we don't need this service */
/*
* duplicate registration is OK --- we retain the previous entry
* (which has not been unregistered anyway)
*/
rc = 1;
} else {
rc = 0;
}
return (rc);
}
static void
{
if (index >= 0) {
} else {
start = 0;
}
}
}
static int
{
int i;
fds_svc_reset(lsp, i);
return (0);
}
}
return (1);
}
/*
* message handlers
*/
/*ARGSUSED*/
static void
{
}
static void
{
/* sanity check the incoming message */
if (len != sizeof (ds_init_req_t)) {
return;
}
/*
* Check version info. ACK only if the major numbers exactly
* match. The service entity can retry with a new minor
* based on the response sent as part of the NACK.
*/
ds_hdr_t *H;
ds_init_ack_t *R;
H->msg_type = DS_INIT_ACK;
H->payload_len = sizeof (ds_init_ack_t);
return;
/*
* Now the channel is ready after the handshake completes.
* Reset the timeout to a smaller value for receiving messages
* from the domain services.
*/
} else {
ds_hdr_t *H;
ds_init_nack_t *R;
H->msg_type = DS_INIT_NACK;
H->payload_len = sizeof (ds_init_nack_t);
R->major_vers = new_major;
/*
* do not update state; remote end may attempt to initiate
* connection with a different version
*/
}
}
/*ARGSUSED*/
static void
{
char *msg;
int dup_svcreg = 0;
/*
* Service must be NULL terminated
*/
return;
}
/*
* Check version info. ACK only if the major numbers
* exactly match. The service entity can retry with a new
* minor based on the response sent as part of the NACK.
*/
ds_hdr_t *H;
ds_reg_ack_t *R;
H->msg_type = DS_REG_ACK;
H->payload_len = sizeof (ds_reg_ack_t);
} else {
ds_hdr_t *H;
ds_reg_nack_t *R;
H->msg_type = DS_REG_NACK;
H->payload_len = sizeof (ds_reg_nack_t);
R->major_vers = new_major;
if (dup_svcreg)
R->result = DS_REG_DUP;
else
R->result = DS_REG_VER_NACK;
}
}
/*ARGSUSED*/
static void
{
ds_hdr_t *H;
ds_unreg_ack_t *R;
H->msg_type = DS_REG_ACK;
H->payload_len = sizeof (ds_unreg_ack_t);
} else {
ds_hdr_t *H;
ds_unreg_nack_t *R;
H->msg_type = DS_REG_NACK;
H->payload_len = sizeof (ds_unreg_nack_t);
}
}
/*
* Message handler lookup table (v1.0 only for now) Future
* versions can add their own lookup table.
*/
ds_handle_init_req, /* DS_INIT_REQ */
ds_handle_msg_noop, /* DS_INIT_ACK */
ds_handle_msg_noop, /* DS_INIT_NACK */
ds_handle_reg_req, /* DS_REG_REQ */
ds_handle_msg_noop, /* DS_REG_ACK */
ds_handle_msg_noop, /* DS_REG_NACK */
ds_handle_unreg, /* DS_UNREG */
ds_handle_msg_noop, /* DS_UNREG_ACK */
ds_handle_msg_noop, /* DS_UNREG_NACK */
ds_handle_msg_noop, /* DS_DATA */
ds_handle_msg_noop /* DS_NACK */
};
/*
* message and service internal functions
*/
static void
{
int i;
;
}
}
static fds_svc_t *
{
int i, ier;
return (NULL); /* uninitialized or destroyed mutex */
break;
}
}
/* service is not registered */
} else {
ier = 0;
/*
* By now, the ds service should have registered already.
* If it does not, ldmd probably does not support this service.
* Then mark the service state as inactive.
*/
}
}
if (ier == 0)
return (svc);
else
return (NULL);
}
static uint64_t
fds_svc_req_num(void)
{
return (req_num++);
}
/*
* return 0 if successful, 1 if otherwise
*/
static int
{
void *msg_buf;
/*
* read the header
*/
return (1);
sizeof (ds_msg_handlers) / sizeof (ds_msg_handler_t))
return (1);
/*
* handle data as a special case
*/
/*
* all other types of messages should be small
*/
/*
* read the payload
*/
return (1);
return (0);
}
/*
* return values:
* 0 - success
* 1 - problem with opening the channel
* 2 - channed not opened; request to exit has been detected
*/
static int
{
int ier;
return (2);
}
return (2);
} else {
&op) != 0) {
return (1);
}
}
}
/*
* reset various channel parameters
*/
}
return (0);
}
static void
channel_fini(void)
{
int i;
/*
* End the poller thread
*/
poller_fini();
return;
/* Free the ldom service structure */
}
}
static struct ldmsvcs_info *
{
static int busy_init = 0;
int expired;
(void) pthread_mutex_lock(&mt);
while (busy_init == 1)
(void) pthread_mutex_unlock(&mt);
return (root);
}
/*
* get to this point if we need to open the channel
*/
busy_init = 1;
(void) pthread_mutex_unlock(&mt);
root = (struct ldmsvcs_info *)
ldom_alloc(sizeof (struct ldmsvcs_info));
return (NULL);
}
(void) poller_init(root);
expired = 0;
/*
* wait for channel to become uninitialized. this should be quick.
*/
expired = 1;
(void) pthread_mutex_lock(&mt);
busy_init = 0;
(void) pthread_mutex_unlock(&mt);
(void) pthread_cond_broadcast(&cv);
(void) atexit(channel_fini);
if (expired == 0)
return (root);
else
return (NULL);
}
static int
{
i = 0;
maxretries = 1;
do {
/*
* if any of the calls in this loop fail, retry some number
* of times before giving up.
*/
else
continue;
} else {
ier = 0;
}
resplen)) != 0)
} while (i++ < maxretries && ier != 0);
return (ier);
}
/*
* input:
* msg_type - requested operation: FMA_CPU_REQ_STATUS or FMA_CPU_REQ_OFFLINE
* cpuid - physical cpu id
*
* normal return values:
* P_OFFLINE - cpu is offline
* P_ONLINE - cpu is online
*
* abnormal return values:
* ETIMEDOUT - LDOM manager is not responding
* ENOMSG - got an unexpected response from the LDOM cpu service
*/
static int
{
ds_hdr_t *H;
ds_data_handle_t *D;
void *resp;
int rc;
return (ENOMSG);
sizeof (fma_cpu_service_req_t);
R = (void *)((ptrdiff_t)D + sizeof (ds_data_handle_t));
H->payload_len = sizeof (ds_data_handle_t) +
sizeof (fma_cpu_service_req_t);
R->req_num = fds_svc_req_num();
return (rc);
}
} else {
if (msg_type == FMA_CPU_REQ_OFFLINE &&
}
return (rc);
}
/*
* input:
* msg_type - requested operation: FMA_MEM_REQ_STATUS or FMA_MEM_REQ_RETIRE
* pa - starting address of memory page
* pgsize - memory page size in bytes
*
* normal return values for msg_type == FMA_MEM_REQ_STATUS:
* 0 - page is retired
* EAGAIN - page is scheduled for retirement
* EIO - page not scheduled for retirement
* EINVAL - error
*
* normal return values for msg_type == FMA_MEM_REQ_RETIRE:
* 0 - success in retiring page
* EIO - page is already retired
* EAGAIN - page is scheduled for retirement
* EINVAL - error
*
* abnormal return values (regardless of msg_type)
* ETIMEDOUT - LDOM manager is not responding
* ENOMSG - got an unexpected response from the LDOM cpu service
*/
static int
{
ds_hdr_t *H;
ds_data_handle_t *D;
void *resp;
int rc;
return (ENOMSG);
sizeof (fma_mem_service_req_t);
R = (void *)((ptrdiff_t)D + sizeof (ds_data_handle_t));
H->payload_len = sizeof (ds_data_handle_t) +
sizeof (fma_mem_service_req_t);
R->req_num = fds_svc_req_num();
return (rc);
}
if (msg_type == FMA_MEM_REQ_STATUS) {
rc = 0; /* page is retired */
}
} else if (msg_type == FMA_MEM_REQ_RETIRE) {
rc = 0; /* is successfully retired */
}
} else if (msg_type == FMA_MEM_REQ_RESURRECT) {
rc = 0; /* is successfully unretired */
}
}
return (rc);
}
/*
* APIs
*/
int
ldmsvcs_check_channel(void)
{
return (0); /* vldc exists */
return (1); /* vldc does not exist */
else
return (-1); /* miscellaneous error */
}
/*ARGSUSED*/
void
{
if (ldmsvcs_check_channel() != 0)
return;
}
/*ARGSUSED*/
void
{
if (ldmsvcs_check_channel() != 0)
return;
}
/*ARGSUSED*/
{
ds_hdr_t *H;
ds_data_handle_t *D;
fma_req_pri_t *R;
void *resp;
int rc;
return (-1);
sizeof (fma_req_pri_t);
R = (void *)((ptrdiff_t)D + sizeof (ds_data_handle_t));
H->payload_len = sizeof (ds_data_handle_t) +
sizeof (fma_req_pri_t);
R->req_num = fds_svc_req_num();
return (-1);
}
/*
* resp should contain the req_num immediately followed by the PRI
* (the latter may or may not be present). unfortunately, the
* current compiler flags cause a warning for the following
* definition
*
* typedef struct {
* uint64_t req_num;
* uint8_t pri[];
* } fma_pri_resp_t;
*
* so we do not use the struct here.
*/
return (0);
else
return (-1);
}
return (buflen);
}
/*
* see cpu_request() for a description of return values
*/
int
{
}
int
{
}
int
{
}
/*
* see mem_request() for a description of return values
*/
int
{
}
int
{
}
int
{
}
int
{
ds_hdr_t *H;
ds_data_handle_t *D;
fma_io_req_t *R;
void *resp;
int offset;
int rc;
return (-1);
sizeof (fma_io_req_t);
R = (void *)((ptrdiff_t)D + sizeof (ds_data_handle_t));
R->req_num = fds_svc_req_num();
R->rsrc_address = addr;
return (rc);
}
/*
* resp should contain the req_num, status, virtual addr, domain id
* and the domain name. The domain name may or may not be present.
*/
offset = sizeof (fma_io_resp_t);
return (-1);
}
case FMA_IO_RESP_OK:
/* success */
rc = 0;
break;
*name = '\0';
name_len);
}
break;
default:
rc = -1;
break;
}
return (rc);
}
/* end file */