fcal_leds_thread.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (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 (c) 2001 by Sun Microsystems, Inc.
* All rights reserved.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <stdarg.h>
#include <pthread.h>
#include <libintl.h>
#include <libdevinfo.h>
#include <syslog.h>
#include <poll.h>
#include "fcal_leds.h"
static char fcal_disk_unit[] = FCAL_PICL_DISK_UNIT;
static int wait_a_while(void);
/*
* variant of strerror() which guards against negative errno and null strings
*/
char *
mystrerror(int err)
{
static char *unknown_errno = "unknown errno";
char *ptr;
ptr = unknown_errno;
}
return (ptr);
}
void
{
int r;
if (r != PICL_SUCCESS)
return;
/*
* is there a disk-unit node here?
*/
sizeof (fcal_disk_unit), &diskndh);
if (r != PICL_SUCCESS)
return;
/*
* remove disk-unit node and its properties
*/
r = ptree_delete_node(diskndh);
if (r != PICL_SUCCESS)
return;
(void) ptree_destroy_node(diskndh);
}
/*
* update_picl
* Called when disk goes off-line or goes to ready status.
* In the case of disk ready, locate platform tree node for the disk
* and add a target property (if missing).
* (The target address is fixed for a given disk slot and is used to
* tie the frutree disk-unit to the correct ssd node).
* Returns EAGAIN for a retriable failure, otherwise 0.
*/
static int
{
static char trailer[] = ",0";
int r;
char valbuf[80];
char addr[MAXPATHLEN];
char *ptrd;
int len;
int addr_len;
for (;;) {
&fpndh);
if (r != PICL_SUCCESS) {
return (0);
}
PICL_PROP_CLASSNAME, (void *)valbuf,
sizeof (valbuf));
if (r != PICL_SUCCESS) {
return (0);
/*
* The node with class fp (if present) is a
* holding node representing no actual hardware.
* Its presence results in two nodes with the
* same effective address. (The fp class node is
* UnitAddress 0,0 and the other fp node [class
* devctl] has bus-addr 0,0). Locating the
* required fp node for dynamic reconfiguration
* then goes wrong. So, just remove it.
*/
r = ptree_delete_node(fpndh);
if (r == PICL_SUCCESS) {
(void) ptree_destroy_node(fpndh);
continue;
}
return (0);
} else {
break;
}
}
/*
* Got a good parent node. Look at its children for a node
* with this new port name.
*
* generate expected bus-addr property from the port-wwn
* Note: dtls->disk_port[disk] points to an array of uchar_t,
* the first character contains the length of the residue.
* The bus-addr property is formatted as follows:
* wabcdef0123456789,0
* where the 16 hex-digits represent 8 bytes from disk_port[];
*/
return (0);
*ptrd++ = 'w';
"%.2x", *ptrs++);
}
if (addr_len > MAXPATHLEN)
return (0);
/*
* If the disk node corresponding to the newly inserted disk
* cannot be found in the platform tree, we have probably
* got in too early - probably before it's up to speed. In
* this case, the WWN gleaned from devinfo may also be wrong.
* This case is worth retrying in later polls when it may
* succeed, so return EAGAIN. All other failures are probably
* terminal, so log a failure and quit.
*/
if (r == PICL_NODENOTFOUND)
return (EAGAIN);
if (r != PICL_SUCCESS) {
return (0);
}
/*
* Found platform entry for disk, add target prop
*/
PICL_PTYPE_INT, PICL_READ, sizeof (int),
if (r != PICL_SUCCESS)
return (0);
NULL);
/*
* Remove pre-existing disk-unit node and its
* properties - maybe its reference property is
* out-of-date.
*/
/*
* Add a disk-unit node in frutree
*/
if (r != PICL_SUCCESS)
return (0);
if (r != PICL_SUCCESS)
return (0);
if (r != PICL_SUCCESS)
return (0);
r = ptree_init_propinfo(&propinfo,
if (r != PICL_SUCCESS)
return (0);
&row_props_h[0]);
if (r != PICL_SUCCESS)
return (0);
if (r != PICL_SUCCESS)
return (0);
if (r != PICL_SUCCESS)
return (0);
if (r != PICL_SUCCESS)
return (0);
} else {
/*
* disk gone, remove disk_unit fru from frutree
*/
}
return (0);
}
static int
{
int *target_data;
int i, r;
int t = -1;
return (0);
newStatus[i] = MINORS_UNKNOWN;
}
&target_data);
for (i = 0; i < r; i++) {
t = target_data[i];
/* set no minors until we know */
break; /* go with this node */
}
}
r = di_prop_lookup_bytes(
/*
* The first byte of the array dtls->disk_port[t] contains
* the length of the residue. So 255 is the maximum length
* which can be handled. Limit the property data to this.
*/
if (r > 255) {
r = 0;
}
/*
* existing data is of different length,
* free it and malloc a fresh array.
*/
}
}
port_data, r);
}
}
if (min_node != DI_MINOR_NIL) {
/*
* device has minor device node(s)
*/
}
}
/*
* propagate attachment status and note changes
* don't propagate to absent disks, otherwise we may not detect a
* status change when they're replugged.
*/
r = 0;
if ((newStatus[i] != MINORS_UNKNOWN) &&
dtls->disk_detected[i] &&
r = 1;
}
}
return (r);
}
/*
* Nodes belonging to the configured driver (dtls->fcal_driver) are
* located in the device tree. A check is applied that any node found has
* a physical address beginning with the configured search string
* (dtls->fcal_disk_parent). For each suitable node found, get_drv_info()
* is called to determine if a change of status has occurred.
* Returns 1 if any status has changed - else 0.
*/
static int
{
static char *sl_platform_sl = "/platform/";
int r = 0;
int len;
/* find "/platform/" */
return (0);
/* skip over "/platform" */
node != DI_NODE_NIL;
/* no memory, just hope things get better */
continue;
}
/*
* path name doesn't start right, skip this one
*/
continue;
}
r = 1; /* change observed */
}
}
return (r);
}
static int
{
/*
* sets disk_ready flags for disks with minor devices (attached)
* returns 1 if any flags have changed else 0
*/
int err = 0;
int r = 0;
if (tree == DI_NODE_NIL) {
}
if (err == 0)
if (tree != DI_NODE_NIL)
return (r);
}
{
int disk;
return (B_TRUE);
}
return (B_FALSE);
}
static int
{
int mask = 0;
if (fd < 0)
return (0);
/*
* generate a mask for all controlled LEDs
*/
}
}
/* read current light settings */
if (err < 0) {
return (EAGAIN);
}
/*
* get bit setting for led to be changed
*/
if (dtls->assert_led_on == 0) {
if (set == 0)
else
} else {
if (set == 0)
else
}
/*
* re-write the leds
*/
if (err == 0)
return (0);
return (EAGAIN);
}
static void
{
}
void
{
}
/*
* have another go at getting the leds in step with required values
*/
static void
{
int r = 0;
int onFlag;
int diskNo;
int ledNo;
if ((state == LED_STATE_ON) ||
(state == LED_STATE_TEST))
onFlag = 1;
else
onFlag = 0;
}
}
}
static void
{
int led_no;
/*
* if the poll thread has failed, can't do led test
*/
return;
/*
* set test interval - doubles as flag for LED-test in progress
*/
}
}
static void
{
/*
* There is a problem with a disk coming on-line.
* All its leds are lit for 10 seconds to meet the led-test
* requirement. The true state for the fault led can be determined
* immediately, but determination of whether to light blue or green
* requires a response from libdevinfo. Device reconfiguration logic
* (likely to be active at this time) holds a long term
* lock preventing devinfo calls from completing. Rather than
* leave a contradictory led indication showing during this
* period, it is better to anticipate the green led result
* and correct it when the facts are known.
*/
}
/*
* Evaluate required wait time and wait until that time or an event.
* Returns 0 for a time-out, otherwise the pending event(s).
* If the finish_now flag is becomes set, this routine acknowledges
* the request and waits for it to go away,
* i.e. no return while finish_now is set.
*/
static int
wait_a_while(void)
{
int r;
int events;
do {
r = pthread_mutex_lock(&g_mutex);
if (r != 0) {
return (0);
}
if (g_finish_now && !acksent) {
(void) pthread_cond_signal(&g_cv_ack);
}
if (r != 0) {
(void) pthread_mutex_unlock(&g_mutex);
return (0);
}
/*
* whilst under the mutex, take a local copy of the events
* and clear those that we handle
*/
g_event_flag ^= events;
(void) pthread_mutex_unlock(&g_mutex);
} while (g_finish_now);
return (events);
}
/*ARGSUSED*/
void *
fcal_leds_thread(void *args)
{
int c, v;
int err = 0;
int events = 0;
int fd_bkplane;
int lastVal = I2C_IOCTL_INIT;
int ws;
int mask;
/*
* generate a mask for presence and fault status bits
*/
mask = 0;
}
/*
* enter poll loop
*/
for (;;) {
/*
* see if a LED-test timer has expired
*/
if (dtls->led_test_end[c] > 0) {
/* poll thread failure, end led-test */
dtls->led_test_end[c] = 0;
} else if ((events & FCAL_EV_POLL) != 0) {
dtls->led_test_end[c]--;
}
if (dtls->led_test_end[c] == 0) {
/*
* clear blue and amber leds
*/
end_led_test(dtls, c);
/* treat any status as a change */
}
}
}
if (fd_bkplane < 0) {
break;
}
/*
* the direction and dir_mask fields are ignored,
* so one can only guess at their possible use
*/
if (c < 0) {
(void) close(fd_bkplane);
if (lastVal != I2C_IOCTL_FAIL) {
mystrerror(err));
events |= FCAL_EV_CONFIG;
}
} else {
(void) close(fd_bkplane);
}
events |= FCAL_EV_CONFIG;
/*
* first get the value of the relevant
* presence bit (as 0 or 1)
*/
/* hold previous presence value */
/*
* the disk is present if the backplane
* status bit for this disk is equal to the
* configured assert_presence value
*/
dtls->disk_detected[c] =
(v == dtls->assert_presence);
/*
* Don't add disk-unit node here for
* newly arrived disks. While the led
* test is running (and beyond)
* libdevinfo is locked out and we
* can't get port or target info.
*/
/*
* disk has just come on-line
*/
start_led_test(dtls, c);
}
/*
* clear leds and ready status
* for disks which have been removed
*/
v = update_picl(dtls, c);
/*
* set or clear retry flag
*/
}
/*
* for present disks which are not doing a
* led test, adjust fault LED
*/
if ((dtls->led_test_end[c] != 0) ||
(!dtls->disk_detected[c]))
continue;
if (v == dtls->assert_fault)
else
}
}
/*
* For detected disks whose status has changed, choose between
* ready and ok to remove.
* libdevinfo can be locked out for the entire duration of a
* disk spin-up. So it is best not to seek this info while
* a led-test is in progress. Otherwise the leds can be stuck
* on for about 40 seconds.
* Note that chk_minors() returns 0 unless a status change
* has occurred.
*/
if (!dtls->disk_detected[c])
continue;
/*
* When disk_ready changes, disk_prev is set
* to its previous value. This allows the
* direction of the last transistion to be
* determined.
*/
} else {
}
}
}
/*
* Update PICL (disk-unit) for newly attached disks
* ** see note in header file for significance
* of disk_prev and disk_ready flags.
*/
v = update_picl(dtls, c);
/*
* set or clear retry flag
*/
}
}
if ((events & FCAL_EV_CONFIG) != 0) {
/*
* set fast polling
*/
}
/*
* if updating a led failed (e.g. I2C busy), try again
*/
events = wait_a_while();
/*
* when picl is recycled, wait_a_while sleeps until the
* init routine has been called again.
* This is the moment when dtls may have become stale.
*/
if (dtls != g_led_dtls) {
dtls = g_led_dtls;
/*
* re-generate the presence and fault status mask
* in case the .conf file has changed
*/
mask = 0;
}
}
/*
* count down relaxation time counter if a poll event
*/
if ((events & FCAL_EV_POLL) != 0) {
if (dtls->fast_poll_end > 0)
dtls->fast_poll_end--;
}
/*
* if updating PICL needs retrying, try it now
*/
if (dtls->picl_retry[c]) {
v = update_picl(dtls, c);
}
}
}
return ((void *)err);
}