1
0
Files
irix-657m-src/eoe/cmd/fcman/fcagent.c
2022-09-29 17:59:04 +03:00

1644 lines
48 KiB
C

#include "options.h"
#include <stdio.h>
#include <sys/types.h>
#include <stdarg.h> /* For varargs */
#include <stdlib.h> /* For string conversion routines */
#include <string.h>
#include <bstring.h>
#include <ctype.h> /* For character definitions */
#include <errno.h>
#include <fcntl.h> /* for I/O functions */
#include <time.h>
#include <ulocks.h>
#include <invent.h> /* for inventory functions */
#include <syslog.h>
#include <sys/signal.h> /* for UNIX signals */
#include <sys/wait.h> /* for UNIX signals */
#define XDR_SHORTCUT
#include "debug.h"
#include "fcagent.h"
#include "fcal.h"
#include "config.h"
#include "esi.h"
#include "scsi.h"
#include "fcagent_structs.h"
#include "fcagent_rpc.h"
#include "hash.h"
#include "usrsignal.h"
#include "dslib.h"
/*
* Forward declarations
*/
channel_e_t agent_get_config();
channel_e_t agent_get_channels();
void agent_check_config(encl_e_t enc);
int agent_get_encl_config(channel_e_t ch, encl_e_t enc);
int agent_get_encl_status(channel_e_t ch, encl_e_t enc, int first_time);
int agent_set_drv_led(channel_e_t ch, fcid_bitmap_t ids, int op);
int agent_set_drv_bypass(channel_e_t ch, fcid_bitmap_t ids, int op);
int agent_set_drv_remove(channel_e_t ch, fcid_bitmap_t ids, int spindown);
int agent_set_drv_insert(channel_e_t ch, fcid_bitmap_t ids, int spinup);
void request_handler_proc(void *arg);
void event_handler_proc(void *arg);
void cleanup();
void signal_handler(int sig);
channel_e_t agent_find_ch_by_cid(int cid);
encl_e_t agent_find_encl_by_tid(channel_e_t ch, int tid);
encl_e_t agent_find_encl_by_eid(channel_e_t ch, int eid);
/*
* Global variables
*/
app_data_struct_t app_data;
usr_sigaction_t actions[] =
{
{SIGINT, signal_handler, SA_RESTART},
{SIGCHLD, signal_handler, SA_RESTART /*| SA_NOCLDSTOP*/},
{SIGHUP, signal_handler, SA_RESTART},
{NULL, NULL, NULL}
};
thread_ctl_struct_t threads[] = {
{ NPID_TH, request_handler_proc, -1, "Asynch. request handler/server" },
{ NPID_EH, event_handler_proc, -1, "Asynch. event hander" },
{ -1, NULL, -1, NULL },
};
int sig_quit = 0;
/*
******************************************************************************
* upid2npid() - UNIX pid to Normalized PID
******************************************************************************
*/
uint upid2npid(pid_t upid)
{
thread_ctl_t th;
for (th = threads; th->th_npid != -1; ++th) {
if (upid == th->th_upid)
return(th->th_npid);
}
return(-1);
}
/*
******************************************************************************
* hfunc() - enclosure WWN hash function
******************************************************************************
*/
int hfunc(ht_key_t key)
{
unsigned long long k = key;
u_int k1, k2;
k1 = k >> 32;
k2 = k & 0xFFFFFFFF;
return(k1 ^ k2);
}
/*
******************************************************************************
* cfunc() - enclosure WWN compare function
******************************************************************************
*/
int cfunc(ht_key_t key1, ht_key_t key2)
{
unsigned long long k1 = key1;
unsigned long long k2 = key2;
if (k1 < k2)
return(-1);
else
if (k1 > k2)
return(1);
else
return(0);
}
/*
******************************************************************************
* ksfunc() - enclosure WWN key print function
******************************************************************************
*/
char *ksfunc(ht_key_t key)
{
unsigned long long k = key;
static char tmpString[100];
sprintf(tmpString, "0x%llx", k);
return(tmpString);
}
/*
******************************************************************************
* vsfunc() - enclosure WWN val print function
******************************************************************************
*/
char *vsfunc(ht_val_t val)
{
static char tmpString[100];
sprintf(tmpString, "0x%x", val);
return(tmpString);
}
/*
******************************************************************************
* get_LIP_map() - for now just create a from drives found.
******************************************************************************
*/
alpa_bitmap_t get_LIP_map(channel_e_t ch)
{
alpa_bitmap_t bm = calloc(1, sizeof(alpa_bitmap_struct_t));
int i;
{
inventory_t *inv;
/*
* FCAgent does not handle fabric disk drives. So when an inventory class
* of INV_FCNODE is found, we want to skip the next inventory entry, which
* would have a class corresponding to the type of fabric device.
* Fabric devices always have two inventory entries, the first of which is
* INV_FCNODE. If that changes, this will have to change too.
*/
setinvent();
while (1) {
inv = getinvent();
if (inv) {
if (inv->inv_class == INV_FCNODE) {
inv = getinvent();
}
else if ((inv->inv_class == INV_DISK) &&
(inv->inv_type == INV_SCSIDRIVE) &&
(inv->inv_controller == ch->id)) {
BM_SET(bm, inv->inv_unit);
}
}
else {
endinvent();
break;
}
}
}
return(bm);
}
/*
******************************************************************************
* cleanup()
******************************************************************************
*/
void cleanup()
{
/*
* Kill and wait for child threads to die
*/
{
sigset_t set, oset;
thread_ctl_t th;
sigemptyset(&set);
sigaddset(&set, SIGCHLD);
if (sigprocmask(SIG_BLOCK, &set, &oset) == -1)
fatal("sigprocmask(SIG_BLOCK) failed : %s\n", strerror(errno));
for (th = threads; th->th_npid != -1; ++th) {
if (th->th_upid != -1) {
sigsend(P_PID, th->th_upid, SIGSOFTKILL);
}
}
if (sigprocmask(SIG_SETMASK, &oset, NULL) == -1)
fatal("sigprocmask(SIG_SETMASK) failed : %s\n", strerror(errno));
for (th = threads; th->th_npid != -1; ++th) {
while (th->th_upid != -1) {
printf("Waiting for '%s' to die.\n", th->th_string);
sleep(1);
}
}
}
/*
* Event cleanup
*/
ev_cleanup();
/*
* Cleanup shared arena stuff
*/
if (ARENA) {
usdetach(ARENA);
remove(ARENA_FN);
free(ARENA_FN);
ARENA = NULL;
ARENA_FN = NULL;
}
/*
* Close syslog
*/
syslog(LOG_INFO, "FCAGENT exiting\n");
closelog();
}
/*
******************************************************************************
* signal_handler()
******************************************************************************
*/
void signal_handler(int sig)
{
int upid, npid, status, rc;
sigset_t pend_set;
switch(sig) {
case SIGINT:
DBG(D_SIGNAL, "AGENT received a SIGINT \n");
sig_quit = 1;
break;
case SIGCHLD:
do {
if ((upid = waitpid(-1, &status, WNOHANG)) <= 0)
break;
npid = upid2npid(upid);
if (WIFSTOPPED(status)) {
DBG(D_SIGNAL, "AGENT received a SIGCHLD (STOPPED) from UPID %d, NPID %d\n",
upid, npid);
}
else
if (WIFEXITED(status)) {
rc = WEXITSTATUS(status);
DBG(D_SIGNAL, "AGENT received a SIGCHLD (EXITED) from UPID %d, NPID %d : RC = %d\n",
upid, npid, rc);
}
else
if (WIFSIGNALED(status)) {
rc = WTERMSIG(status);
DBG(D_SIGNAL, "AGENT received a SIGCHLD (SIGNALED) from UPID %d, NPID %d : SIGNAL = %d\n",
upid, npid, rc);
}
else {
DBG(D_SIGNAL, "AGENT received a SIGCHLD (UNKNOWN) from UPID %d, NPID %d\n",
upid, npid);
}
threads[npid].th_upid = -1;
} while (1);
break;
case SIGHUP:
DBG(D_SIGNAL, "AGENT received a SIGHUP\n");
/*
* Re-read the configuration file.
*/
if (cfg_read(CFG_FN, (char *)CFG, agent_vt) == 0) {
__debug = CFG->ac_debug;
dsdebug = (CFG->ac_debug & D_DSDEBUG) ? 1 : 0;
#if 0
printf("AGENT POLL PERIOD - %d\n", CFG->ac_pollperiod);
printf("AGENT AUTO LOOP RECOVER - %d\n", CFG->ac_autolooprecover);
printf("AGENT PRE-REMOVAL CO - %s\n", CFG->ac_preremovalco);
printf("AGENT POST-REMOVAL CO - %s\n", CFG->ac_postremovalco);
printf("AGENT POST-INSERT CO - %s\n", CFG->ac_postinsertco);
printf("AGENT STAT CHANGED CO - %s\n", CFG->ac_stat_change_co);
printf("AGENT AUTO INTRO - %d\n", CFG->ac_autointro);
printf("AGENT DEBUG LEVEL - %d\n", CFG->ac_debug);
printf("AGENT REMOTE REQUESTS - %d\n", CFG->ac_allow_remote);
#endif
}
break;
default:
DBG(D_SIGNAL, "AGENT received a unknown signal [%d]\n", sig);
}
}
main(int argc, char **argv)
{
char **p;
int rc;
bzero(&app_data, sizeof(app_data_struct_t));
if (argc < 2) {
fprintf(stderr, "Usage: %s <configuration filename>\n", argv[0]);
cleanup();
exit(-1);
}
CFG_FN = argv[1];
CFG = calloc(1, sizeof(agent_cfg_struct_t));
/*
* Read the configuration file.
*/
if (rc = cfg_read(CFG_FN, (char *)CFG, agent_vt)) {
if (rc == -1)
fatal("Configuration read failed for '%s': %s\n", CFG_FN, strerror(errno));
cleanup();
exit(1);
}
__debug = CFG->ac_debug;
dsdebug = (CFG->ac_debug & D_DSDEBUG) ? 1 : 0;
#if 0
printf("AGENT POLL PERIOD - %d\n", CFG->ac_pollperiod);
printf("AGENT AUTO LOOP RECOVER - %d\n", CFG->ac_autolooprecover);
printf("AGENT PRE-REMOVAL CO - %s\n", CFG->ac_preremovalco);
printf("AGENT POST-REMOVAL CO - %s\n", CFG->ac_postremovalco);
printf("AGENT POST-INSERT CO - %s\n", CFG->ac_postinsertco);
printf("AGENT STAT CHANGED CO - %s\n", CFG->ac_stat_change_co);
printf("AGENT AUTO INTRO - %d\n", CFG->ac_autointro);
printf("AGENT DEBUG LEVEL - %d\n", CFG->ac_debug);
#endif
/*
* Configure number of users and size of shared arena.
*/
if (usconfig(CONF_INITUSERS, 5) == -1)
fatal("usconfig() failed : %s\n", strerror(errno));
if (usconfig(CONF_INITSIZE, 65536) == -1)
fatal("usconfig() failed : %s\n", strerror(errno));
ARENA_FN = malloc(200);
sprintf(ARENA_FN, "%s_%d", ARENA_FILENAME_BASE, getpid());
if ((ARENA = usinit(ARENA_FN)) == NULL)
fatal("usinit() failed : %s\n", strerror(errno));
/*
* Configure number of users and size of C arena (sproc).
*/
if (usconfig(CONF_INITUSERS, 5) == -1)
fatal("usconfig() failed : %s\n", strerror(errno));
if (usconfig(CONF_INITSIZE, 65536) == -1)
fatal("usconfig() failed : %s\n", strerror(errno));
/*
* Install signal handlers
*/
if (install_signal_handlers(actions) == -1)
fatal("install_signal_handlers() failed : %s\n", strerror(errno));
/*
* Initialize the event handler
*/
ev_init();
/*
* Initialize the enclosure hash tables
*/
ENCL_HT = hash_new(256, hfunc, cfunc, ksfunc, vsfunc);
CH_HEAD = agent_get_config();
if (CH_HEAD == NULL) {
fprintf(stderr, "%s terminating: No FC channels found.\n", argv[0]);
cleanup();
exit(0);
}
/*
* Become process group leader to release shell
*/
#if 0
if (fork() == 0) {
if (setsid() == -1) {
fatal("setsid() failed: %s\n", strerror(errno));
}
}
else
exit(0);
#endif
/*
* Open syslog
*/
openlog("fcagent", LOG_PID|LOG_CONS, LOG_DAEMON);
syslog(LOG_INFO, "FCAGENT starting\n");
/*
* Check for non-redundant configurations
*/
agent_check_config(NULL);
/* Fork off listener/responder thread and autonomous event handler thread */
if (1) {
sigset_t set, oset;
thread_ctl_t th;
caddr_t sp;
sigemptyset(&set);
sigaddset(&set, SIGCHLD);
if (sigprocmask(SIG_BLOCK, &set, &oset) == -1)
fatal("sigprocmask(SIG_BLOCK) failed : %s\n", strerror(errno));
for (th = threads; th->th_npid != -1; ++th) {
#if 0
th->th_upid = sproc(th->th_proc, PR_SADDR, th->th_npid);
#else
if ((sp = malloc(STACKSIZE)) == NULL)
return(-1);
sp += STACKSIZE;
th->th_upid = sprocsp((void (*)(void *, size_t))th->th_proc, PR_SADDR, (void *)th->th_npid, sp, STACKSIZE);
#endif
if (th->th_upid == -1)
fprintf(stderr, "sproc() failed: %s\n", strerror(errno));
}
if (sigprocmask(SIG_SETMASK, &oset, NULL) == -1)
fatal("sigprocmask(SIG_SETMASK) failed : %s\n", strerror(errno));
}
/*
* Loop forever polling enclosures
*/
while (!sig_quit) {
time_t read_gen_code=0;
channel_e_t ch;
encl_ref_t enc_ref;
encl_e_t enc;
read_gen_code = time(NULL);
for (ch = CH_HEAD; ch; ch = ch->next) {
for (enc_ref = ch->encl_ref_head; enc_ref; enc_ref = enc_ref->next) {
enc = enc_ref->encl;
if (enc->read_gen_code < read_gen_code)
agent_get_encl_status(ch, enc, 0);
if (enc->check_gen_code != enc->status_gen_code)
agent_check_config(enc);
}
}
sleep(CFG->ac_pollperiod);
}
cleanup();
exit(0);
}
/*
******************************************************************************
* agent_get_config() - the bootstrap function which gropes around for
* connected controllers and builds the necessary agent data
* structures.
*
* The ALGORITHM:
* (1) channels = get_channel_list();
* (2) For each ch in channels
* LIP_map = get_LIP_map(ch);
* For each tid in LIP_map
* if (IS_SET(LIP_map, tid) && IS_8067_CONNECTED(ch, tid))
* enc = new(encl_e_struct_t);
* enc->primdrv = tid;
* enc->secdrv = UNDEF;
* agent_get_encl_config(enc);
* For all drives tid in enc
* UN_SET(LIP_map, tid);
* if (tid != enc->primdrv && enc->secdrv == UNDEF && IS_8067_CONNECTED(ch, tid))
* enc->secdrv = tid;
* endif
* end
* endif
* end
* end
******************************************************************************
*/
channel_e_t agent_get_config()
{
char devscsi_filename[256];
channel_e_t head=NULL, ch=NULL;
alpa_bitmap_t lip_map;
int tid, i;
encl_e_t enc=NULL, new_enc=NULL;
encl_ref_t enc_ref;
drv_e_t drv;
inq_struct_t inqdata;
char tmp_str[128];
head = agent_get_channels();
if (head == NULL)
return(NULL);
for (ch = head; ch; ch = ch->next) {
lip_map = get_LIP_map(ch);
for (tid = TID_LO; tid <= TID_HI; ++tid) {
if (!BM_ISSET(lip_map, tid))
continue;
BM_CLR(lip_map, tid);
if (inquiry(make_devscsi_path(ch->id, tid, 0, devscsi_filename), &inqdata))
continue;
strncpy(tmp_str, (char *)inqdata.vid, 8);
tmp_str[8] = ':';
strncpy(tmp_str+9, (char *)inqdata.pid, 16);
tmp_str[25] = ' ';
strncpy(tmp_str+26, (char *)inqdata.prl, 4);
tmp_str[30] = ' ';
strncpy(tmp_str+31, (char *)inqdata.serial, 8);
tmp_str[39] = '\0';
if (inqdata.pdt != DIRECT_ACCESS_DEV)
continue;
#if !SG_WAR_1
if (!inqdata.es)
continue;
#endif
/*
* Clariion specific implementation -- drives in slots 0 and 2
* communicate with one LCC, drives in slots 1 and 3 communicate
* with the other LCC.
*/
if (!((inqdata.port == 0 && (tid%10 == 0 || tid%10 == 2)) ||
(inqdata.port == 1 && (tid%10 == 1 || tid%10 == 3))))
continue;
DBG(D_ESI, " - Found primary ESI disk drive at ID %d - %s: ES = %d, PORT = %d\n",
tid, tmp_str, inqdata.es, inqdata.port);
new_enc = calloc(1, sizeof(encl_e_struct_t));
new_enc->connect_port = inqdata.port;
new_enc->esidrv[0] = tid;
new_enc->esidrv[1] = TID_UNDEF;
if (agent_get_encl_config(ch, new_enc) == 0) {
enc = hash_lookup(ENCL_HT, new_enc->wwn);
if (enc) {
free(new_enc);
}
else {
hash_enter(ENCL_HT, new_enc->wwn, new_enc);
enc = new_enc;
enc->stat_blen_alloc = STATUS_BUFLEN;
enc->stat_buf = (esi_pg2_t)malloc(STATUS_BUFLEN);
if ((new_enc->lock = usnewlock(ARENA)) == NULL)
fatal("usnewlock() failed: %s\n", strerror(errno));
agent_get_encl_status(ch, enc, 1);
for (i = 0, drv = enc->drv; i < enc->drv_count; ++i, ++drv) {
BM_CLR(lip_map, drv->phys_id);
if (drv->phys_id != enc->esidrv[0] && enc->esidrv[1] == TID_UNDEF) {
if (inquiry(make_devscsi_path(ch->id, drv->phys_id, 0, devscsi_filename), &inqdata))
continue;
if (inqdata.pdt != DIRECT_ACCESS_DEV)
continue;
#if !SG_WAR_1
if (!inqdata.es)
continue;
#endif
/*
* Clariion specific implementation -- drives in slots 0 and 2
* communicate with one LCC, drives in slots 1 and 3 communicate
* with the other LCC.
*/
if (!((inqdata.port == 0 && (drv->phys_id%10 == 0 || drv->phys_id%10 == 2)) ||
(inqdata.port == 1 && (drv->phys_id%10 == 1 || drv->phys_id%10 == 3))))
continue;
if (enc->connect_port != inqdata.port) {
DBG(D_ESI, "agent_get_config(%d): Secondary drive (%d) not on same port as primary drive (%d).\n",
ch->id, enc->esidrv[0], drv->phys_id);
continue;
}
strncpy(tmp_str, (char *)inqdata.vid, 8);
tmp_str[8] = ':';
strncpy(tmp_str+9, (char *)inqdata.pid, 16);
tmp_str[25] = ' ';
strncpy(tmp_str+26, (char *)inqdata.prl, 4);
tmp_str[30] = ' ';
strncpy(tmp_str+31, (char *)inqdata.serial, 8);
tmp_str[39] = '\0';
DBG(D_ESI, " - Found secondary ESI disk drive at ID %d - %s: ES = %d, PORT = %d\n",
drv->phys_id, tmp_str, inqdata.es, inqdata.port);
enc->esidrv[1] = drv->phys_id;
}
} /* for (i) */
}
enc_ref = calloc(1, sizeof(encl_ref_struct_t));
enc_ref->encl = enc;
if (ch->encl_ref_tail) {
ch->encl_ref_tail->next = enc_ref;
ch->encl_ref_tail = enc_ref;
}
else {
ch->encl_ref_head = ch->encl_ref_tail = enc_ref;
}
}
else {
free(new_enc);
continue;
}
}
}
return(head);
}
/*
******************************************************************************
* agent_check_config - check for illegal JBOD configurations. Check
* for presence of both odd (1 and 3) and even (0 and 2) ESI drives in
* enclosure. If the parameter 'enc' is non-NULL, only that enclosure
* is checked, otherwise all enclosures are checked.
******************************************************************************
*/
void agent_check_config(encl_e_t enc)
{
channel_e_t ch;
encl_ref_t enc_ref;
if (enc) {
for (ch = CH_HEAD; ch; ch = ch->next) {
for (enc_ref = ch->encl_ref_head; enc_ref; enc_ref = enc_ref->next) {
if (enc_ref->encl == enc) {
if (enc->connect_port) {
if ((enc->drv[1].status != STS_OK) || (enc->drv[3].status != STS_OK))
syslog(LOG_WARNING, "Non-redundant JBOD config found: "
"One of drives %d and %d is missing on channel %d in enclosure %d\n",
enc->drv[1].phys_id, enc->drv[3].phys_id, ch->id, enc->phys_id);
}
else {
if ((enc->drv[0].status != STS_OK) || (enc->drv[2].status != STS_OK))
syslog(LOG_WARNING, "Non-redundant JBOD config found: "
"One of drives %d and %d is missing on channel %d in enclosure %d\n",
enc->drv[0].phys_id, enc->drv[2].phys_id, ch->id, enc->phys_id);
}
enc->check_gen_code = enc->status_gen_code;
return;
}
}
}
}
else {
for (ch = CH_HEAD; ch; ch = ch->next) {
for (enc_ref = ch->encl_ref_head; enc_ref; enc_ref = enc_ref->next) {
enc = enc_ref->encl;
if (enc->connect_port) {
if ((enc->drv[1].status != STS_OK) || (enc->drv[3].status != STS_OK))
syslog(LOG_WARNING, "Non-redundant JBOD config found: "
"One of drives %d and %d is missing on channel %d in enclosure %d\n",
enc->drv[1].phys_id, enc->drv[3].phys_id, ch->id, enc->phys_id);
}
else {
if ((enc->drv[0].status != STS_OK) || (enc->drv[2].status != STS_OK))
syslog(LOG_WARNING, "Non-redundant JBOD config found: "
"One of drives %d and %d is missing on channel %d in enclosure %d\n",
enc->drv[0].phys_id, enc->drv[2].phys_id, ch->id, enc->phys_id);
}
enc->check_gen_code = enc->status_gen_code;
}
}
}
}
/*
******************************************************************************
* agent_get_encl_config() - return the configuration for an enclosure,
* rooted at enc. The field 'esidrv[0]' is set.
******************************************************************************
*/
int agent_get_encl_config(channel_e_t ch, encl_e_t enc)
{
char devscsi_filename[256];
u_char buf[CONFIG_BUFLEN], *p;
u_int buflen;
char *devname;
esi_pg1_t pg1;
esi_pg1_type_desc_t tdesc;
int i, j;
devname = make_devscsi_path(ch->id, enc->esidrv[0], 0, devscsi_filename);
/*
* READ the configuration page
*/
buflen = CONFIG_BUFLEN;
if (recv_diagnostics(devname, RECV_ES_CONFIGURATION, buf, &buflen))
return(-1);
pg1 = (esi_pg1_t)buf;
if (pg1->page_code != RECV_ES_CONFIGURATION)
return(-1);
enc->config_gen_code = pg1->gen_code;
#if DG_WAR_13
pg1->enc_phys_id = enc->esidrv[0]/10;
#endif
#if DG_WAR_3
enc->wwn = (ch->id << 16) | pg1->enc_phys_id;
#else
bcopy(pg1->enc_wwn, &enc->wwn, sizeof(unsigned long long));
#endif
#if 0
bcopy("SGI ", enc->vid, 8);
bcopy("FCHAJBOD", enc->pid, 8);
bcopy("1.0 ", enc->prl, 8);
#else
bcopy(pg1->enc_vid, enc->vid, 8);
bcopy(pg1->enc_pid, enc->pid, 8);
bcopy(pg1->enc_prl, enc->prl, 8);
#endif
enc->phys_id = pg1->enc_phys_id;
enc->type_count = pg1->num_types;
enc->types = calloc(pg1->num_types, sizeof(esi_pg1_type_desc_struct_t));
/*
bcopy(buf + 12 + pg1->glob_desc_len, (u_char *)enc->types, enc->type_count*sizeof(esi_pg1_type_desc_struct_t));
*/
p = buf + 12 + pg1->glob_desc_len;
for (i = 0, tdesc = enc->types;
i < enc->type_count;
++i, ++tdesc) {
bcopy(p, tdesc, sizeof(esi_pg1_type_desc_struct_t));
switch(tdesc->type_code) {
case E_TYPE_DISK:
DBG(D_ESI, "agent_get_encl_config(): Found %d DISKs\n", tdesc->elem_count);
enc->drv_count = tdesc->elem_count;
enc->drv = calloc(enc->drv_count, sizeof(drv_e_struct_t));
for (j = 0; j < enc->drv_count; ++j)
enc->drv[j].status = STS_INVALID;
break;
case E_TYPE_PS:
DBG(D_ESI, "agent_get_encl_config(): Found %d PSs\n", tdesc->elem_count);
enc->ps_count = tdesc->elem_count;
enc->ps = calloc(enc->ps_count, sizeof(ps_e_struct_t));
for (j = 0; j < enc->ps_count; ++j)
enc->ps[j].status = STS_INVALID;
break;
case E_TYPE_FAN:
DBG(D_ESI, "agent_get_encl_config(): Found %d FANs\n", tdesc->elem_count);
enc->fan_count = tdesc->elem_count;
enc->fan = calloc(enc->fan_count, sizeof(fan_e_struct_t));
for (j = 0; j < enc->fan_count; ++j)
enc->fan[j].status = STS_INVALID;
break;
case E_TYPE_LCC:
DBG(D_ESI, "agent_get_encl_config(): Found %d LCCs\n", tdesc->elem_count);
enc->lcc_count = tdesc->elem_count;
enc->lcc = calloc(enc->lcc_count, sizeof(lcc_e_struct_t));
for (j = 0; j < enc->lcc_count; ++j)
enc->lcc[j].status = STS_INVALID;
enc->lcc_str_len = tdesc->text_len + 1;
enc->lcc_str = calloc(1, enc->lcc_str_len);
bcopy(p + sizeof(esi_pg1_type_desc_struct_t), enc->lcc_str, tdesc->text_len);
break;
case E_TYPE_UPS:
DBG(D_ESI, "agent_get_encl_config(): Found %d UPSs\n", tdesc->elem_count);
#if DG_WAR_4
tdesc->elem_count = 2;
enc->types[i].elem_count = 2;
#endif
break;
default:
DBG(D_ESI, "Unknown configuration element type %d, count %d\n", tdesc->type_code, tdesc->elem_count);
}
p += sizeof(esi_pg1_type_desc_struct_t) + tdesc->text_len;
}
return(0);
}
/*
******************************************************************************
* agent_get_encl_status() - return the status for an enclosure, rooted at
* enc.
******************************************************************************
*/
int agent_get_encl_status(channel_e_t ch, encl_e_t enc, int first_time)
{
char devscsi_filename[256];
int state=0, short_read=1;
char *devname;
esi_pg2_t pg2;
esi_pg2_gnrc_desc_t elem_desc;
int type_index, elem_index;
u_char new_status;
event_t ev;
u_int buflen;
int rc=0;
DBG(D_ESI, "Polling CH %d, ENCL %d\n", ch->id, enc->phys_id);
LOCK(enc->lock);
enc->read_gen_code = time(NULL);
if (first_time)
short_read = 0;
#if DG_WAR_2
short_read = 0;
#endif
/*
* READ the status page, first trying the primary 8067 drive and then the
* secondary.
*/
get_status_retry:
state = 0;
while (1) {
switch (state) {
case 0:
case 1:
if (enc->esidrv[state & 0x3] != TID_UNDEF) {
devname = make_devscsi_path(ch->id, enc->esidrv[state], 0, devscsi_filename);
buflen = short_read ? sizeof(esi_pg2_struct_t) : enc->stat_blen_alloc;
if (recv_diagnostics(devname, RECV_ES_ENCL_STATUS, (unchar *)enc->stat_buf, &buflen) == 0) {
/*
* If succeeded on secondary, swap primary and secondary
*/
if (state) {
u_char drv;
drv = enc->esidrv[0];
enc->esidrv[0] = enc->esidrv[1];
enc->esidrv[1] = drv;
}
goto get_status_success;
}
else {
if (state == 0) {
fprintf(stderr, "Primary ESI drive (%d) failure on FC channel %d: "
"trying secondary (%d)\n",
enc->esidrv[0], ch->id, enc->esidrv[1]);
syslog(LOG_CRIT, "Primary ESI drive (%d) failure on FC channel %d: "
"trying secondary (%d)\n",
enc->esidrv[0], ch->id, enc->esidrv[1]);
}
else {
fprintf(stderr, "Secondary ESI drive (%d) failure on FC channel %d: "
"Unable to determine enclosure status\n",
enc->esidrv[1], ch->id);
syslog(LOG_CRIT, "Secondary ESI drive (%d) failure on FC channel %d: "
"Unable to determine enclosure status\n",
enc->esidrv[1], ch->id);
}
++state;
}
}
else
++state;
break;
default:
enc->status = STS_INVALID; /* Mark the enclosure status as unknown */
rc = -1;
goto status_done;
}
}
get_status_success:
pg2 = enc->stat_buf;
if (pg2->page_code != RECV_ES_ENCL_STATUS) {
rc = -1;
goto status_done;
}
if (short_read && pg2->gen_code != enc->status_gen_code) {
short_read = 0;
goto get_status_retry;
}
if (short_read)
goto status_done;
/*
* Parse the raw status
*/
if (first_time)
enc->stat_blen_actual = buflen;
#if DG_WAR_2
if (first_time || 1) {
#else
if (first_time || pg2->gen_code != enc->status_gen_code) {
#endif
/* Enclosure status */
if (pg2->non_crit || pg2->crit || pg2->unrecov)
new_status = STS_FAILED;
else
new_status = STS_OK;
enc->status = new_status;
elem_desc = (esi_pg2_gnrc_desc_t)((unchar *)enc->stat_buf + sizeof(esi_pg2_struct_t));
for (type_index = 0; type_index < enc->type_count; ++type_index) {
++elem_desc; /* Skip over global */
for (elem_index = 0; elem_index < enc->types[type_index].elem_count; ++elem_index, ++elem_desc) {
switch(enc->types[type_index].type_code) {
case E_TYPE_DISK:
{
esi_pg2_drv_desc_t drv_desc = (esi_pg2_drv_desc_t)elem_desc;
drv_e_t drv = &enc->drv[elem_index];
if (first_time) {
drv->connect_port = enc->connect_port;
}
#if DG_WAR_1
drv->phys_id = enc->phys_id * enc->drv_count + elem_index;
#else
drv->phys_id = drv_desc->hard_address;
#endif
drv->drv_asserting_fault = drv_desc->sense_fault;
drv->enc_asserting_fault = drv_desc->fault_reqstd;
drv->drv_asserting_bypass = (drv->connect_port ?
drv_desc->byp_B_enabled : drv_desc->byp_A_enabled);
drv->enc_asserting_bypass = (drv->connect_port ?
drv_desc->enable_byp_B : drv_desc->enable_byp_A);
if (IS_E_ELEM_STS_FAILED(drv_desc->status) || drv_desc->sense_fault)
new_status = STS_FAILED;
else
if (IS_E_ELEM_STS_NOT_PRESENT(drv_desc->status))
new_status = STS_NOT_PRESENT;
else
if (drv_desc->drive_off)
new_status = STS_OFF;
else
if (drv->drv_asserting_bypass || drv->enc_asserting_bypass)
new_status = STS_BYPASSED;
else
new_status = STS_OK;
if ((!first_time && drv->status != new_status) ||
(first_time && new_status == STS_FAILED)) {
ev = calloc(1, sizeof(event_struct_t));
ev->ev_ch_id = ch->id;
ev->ev_encl_id = enc->phys_id;
ev->ev_elem_type = EVT_ELEM_TYPE_DISK;
ev->ev_elem_id = elem_index;
ev->ev_old_status = drv->status;
ev->ev_new_status = new_status;
ev_enqueue(ev);
}
/*
* Potentially added another a secondary ESI drive. Check.
*/
if (!first_time &&
(enc->esidrv[1] == TID_UNDEF) &&
(new_status == STS_OK) && (drv->status != new_status)) {
if (((drv->connect_port == 0) && (drv->phys_id%10 == 0 || drv->phys_id%10 == 2)) ||
((drv->connect_port == 1) && (drv->phys_id%10 == 1 || drv->phys_id%10 == 3))) {
if (enc->esidrv[0] != drv->phys_id)
enc->esidrv[1] = drv->phys_id;
}
}
drv->status = new_status;
DBG(D_ESI, "ENCL 0x%llx: DISK %d:\n", enc->wwn, elem_index);
DBG(D_ESI, " status = %d\n", drv->status);
DBG(D_ESI, " phys_addr = %d\n", drv->phys_id);
DBG(D_ESI, " connect_port = %d\n", drv->connect_port);
DBG(D_ESI, " drv_asserting_fault = %d\n", drv->drv_asserting_fault);
DBG(D_ESI, " enc_asserting_fault = %d\n", drv->enc_asserting_fault);
DBG(D_ESI, " drv_asserting_bypass = %d\n", drv->drv_asserting_bypass);
DBG(D_ESI, " enc_asserting_bypass = %d\n", drv->enc_asserting_bypass);
}
break;
case E_TYPE_PS:
{
esi_pg2_ps_desc_t ps_desc = (esi_pg2_ps_desc_t)elem_desc;
ps_e_t ps = &enc->ps[elem_index];
if (first_time) {
;
}
if (IS_E_ELEM_STS_FAILED(ps_desc->status) || ps_desc->fail || ps_desc->overtemp_fail)
new_status = STS_FAILED;
else
if (IS_E_ELEM_STS_NOT_PRESENT(ps_desc->status))
new_status = STS_NOT_PRESENT;
else
if (!ps_desc->rqsted_on)
new_status = STS_OFF;
else
new_status = STS_OK;
if (ps_desc->overtemp_fail) {
syslog(LOG_CRIT, "Power supply (%d) overheated in enclosure %d on FC channel %d: ",
elem_index, enc->phys_id, ch->id);
syslog(LOG_CRIT, "");
}
if (ps_desc->temp_warning) {
syslog(LOG_CRIT, "Power supply (%d) temperate warning in enclosure %d on FC channel %d:",
elem_index, enc->phys_id, ch->id);
syslog(LOG_CRIT, "");
}
if ((!first_time && ps->status != new_status) ||
(first_time && new_status == STS_FAILED)) {
ev = calloc(1, sizeof(event_struct_t));
ev->ev_ch_id = ch->id;
ev->ev_encl_id = enc->phys_id;
ev->ev_elem_type = EVT_ELEM_TYPE_PS;
ev->ev_elem_id = elem_index;
ev->ev_old_status = ps->status;
ev->ev_new_status = new_status;
ev_enqueue(ev);
}
ps->status = new_status;
DBG(D_ESI, "ENCL 0x%llx: PS %d:\n", enc->wwn, elem_index);
DBG(D_ESI, " status = %d\n", ps->status);
DBG(D_ESI, " rqsted_on = %d\n", ps_desc->status);
DBG(D_ESI, " fail = %d\n", ps_desc->fail);
}
break;
case E_TYPE_FAN:
{
esi_pg2_fan_desc_t fan_desc = (esi_pg2_fan_desc_t)elem_desc;
fan_e_t fan = &enc->fan[elem_index];
if (first_time) {
;
}
if (IS_E_ELEM_STS_FAILED(fan_desc->status) || fan_desc->fail || fan_desc->speed_code != FAN_SPD_NORMAL)
new_status = STS_FAILED;
else
if (IS_E_ELEM_STS_NOT_PRESENT(fan_desc->status))
new_status = STS_NOT_PRESENT;
else
#if 0
if (!fan_desc->rqsted_on)
new_status = STS_OFF;
else
#endif
new_status = STS_OK;
if ((!first_time && fan->status != new_status) ||
(first_time && new_status == STS_FAILED)) {
ev = calloc(1, sizeof(event_struct_t));
ev->ev_ch_id = ch->id;
ev->ev_encl_id = enc->phys_id;
ev->ev_elem_type = EVT_ELEM_TYPE_FAN;
ev->ev_elem_id = elem_index;
ev->ev_old_status = fan->status;
ev->ev_new_status = new_status;
ev_enqueue(ev);
}
fan->status = new_status;
DBG(D_ESI, "ENCL 0x%llx: FAN %d:\n", enc->wwn, elem_index);
DBG(D_ESI, " status = %d\n", fan->status);
}
break;
case E_TYPE_LCC:
{
esi_pg2_lcc_desc_t lcc_desc = (esi_pg2_lcc_desc_t)elem_desc;
lcc_e_t lcc = &enc->lcc[elem_index];
if (first_time) {
;
}
lcc->connect_port = lcc_desc->LCC_slot;
lcc->position = lcc_desc->rack_mounted;
lcc->local_mode = lcc_desc->local_mode;
lcc->peer_present = lcc_desc->other_LCC_inserted;
lcc->peer_failed = lcc_desc->other_LCC_fault;
lcc->exp_port_open = lcc_desc->exp_port_open;
lcc->exp_shunt_closed = lcc_desc->exp_shunt_closed;
if (IS_E_ELEM_STS_FAILED(lcc_desc->status) || lcc_desc->exp_MC_fault || lcc_desc->prim_MC_fault)
new_status = STS_FAILED;
else
if (lcc->peer_failed)
new_status = STS_PEER_FAILED;
else
new_status = STS_OK;
if ((!first_time && lcc->status != new_status) ||
(first_time && (new_status == STS_FAILED ||
new_status == STS_PEER_FAILED))) {
ev = calloc(1, sizeof(event_struct_t));
ev->ev_ch_id = ch->id;
ev->ev_encl_id = enc->phys_id;
ev->ev_elem_type = EVT_ELEM_TYPE_LCC;
ev->ev_elem_id = elem_index;
ev->ev_old_status = lcc->status;
ev->ev_new_status = new_status;
ev_enqueue(ev);
}
lcc->status = new_status;
DBG(D_ESI, "ENCL 0x%llx: LCC %d:\n", enc->wwn, elem_index);
DBG(D_ESI, " status = %d\n", lcc->status);
DBG(D_ESI, " connect_port = %d\n", lcc->connect_port);
DBG(D_ESI, " position = %d\n", lcc->position);
DBG(D_ESI, " local mode = %d\n", lcc->local_mode);
DBG(D_ESI, " peer_present = %d\n", lcc->peer_present);
DBG(D_ESI, " peer_failed = %d\n", lcc->peer_failed);
DBG(D_ESI, " exp_port_open = %d\n", lcc->exp_port_open);
DBG(D_ESI, " exp_shunt_closed = %d\n", lcc->exp_shunt_closed);
}
break;
}
}
}
}
enc->status_gen_code = pg2->gen_code;
status_done:
UNLOCK(enc->lock);
return(rc);
}
/*
******************************************************************************
* agent_get_channels() - returns list of FC channels found.
******************************************************************************
*/
channel_e_t agent_get_channels()
{
channel_e_t head=NULL, curr=NULL;
inventory_t *inv;
setinvent();
while (1) {
inv = getinvent();
if (inv && (inv->inv_class == INV_DISK) &&
(inv->inv_type == INV_SCSICONTROL) &&
(inv->inv_state == INV_FCADP ||
inv->inv_state == INV_QL_2100 ||
inv->inv_state == INV_QL_2200A ||
inv->inv_state == INV_QL_2200)) {
if (head == NULL) {
head = calloc(1, sizeof(channel_e_struct_t));
curr = head;
}
else {
curr->next = calloc(1, sizeof(channel_e_struct_t));
curr = curr->next;
}
curr->next = NULL;
curr->id = inv->inv_controller;
curr->type = inv->inv_state;
}
else
if (inv == NULL) {
endinvent();
break;
}
}
return(head);
}
/*
******************************************************************************
* agent_find_ch_by_cid() - returns enclosure containing drive 'tid'.
******************************************************************************
*/
channel_e_t agent_find_ch_by_cid(int cid)
{
channel_e_t ch;
for (ch = CH_HEAD; ch; ch = ch->next) {
if (ch->id == cid)
return(ch);
}
/* If we get here, we've reached the end of the list */
return(NULL);
}
/*
******************************************************************************
* agent_find_encl_by_tid() - returns enclosure containing drive 'tid'.
******************************************************************************
*/
encl_e_t agent_find_encl_by_tid(channel_e_t ch, int tid)
{
encl_ref_t enc_ref;
encl_e_t enc;
for (enc_ref = ch->encl_ref_head; enc_ref; enc_ref = enc_ref->next) {
enc = enc_ref->encl;
if ((tid >= enc->phys_id * enc->drv_count) &&
(tid < (enc->phys_id + 1) * enc->drv_count))
return(enc);
}
/* If we get here, we've reached the end of the list */
return(NULL);
}
/*
******************************************************************************
* agent_find_encl_by_eid() - returns enclosure with physical id 'eid'.
******************************************************************************
*/
encl_e_t agent_find_encl_by_eid(channel_e_t ch, int eid)
{
encl_ref_t enc_ref;
encl_e_t enc;
for (enc_ref = ch->encl_ref_head; enc_ref; enc_ref = enc_ref->next) {
enc = enc_ref->encl;
if (eid == enc->wwn)
return(enc);
}
/* If we get here, we've reached the end of the list */
return(NULL);
}
esi_pg2_gnrc_desc_t agent_find_e_elem(encl_e_t enc, int type_code)
{
esi_pg2_gnrc_desc_t elem_desc;
int type_index;
elem_desc = (esi_pg2_gnrc_desc_t)((unchar *)enc->stat_buf + sizeof(esi_pg2_struct_t));
for (type_index = 0; type_index < enc->type_count; ++type_index) {
if (enc->types[type_index].type_code == type_code)
return((esi_pg2_gnrc_desc_t)elem_desc);
else
elem_desc += enc->types[type_index].elem_count + 1;
}
return(NULL);
}
void set_drv_led_ca(encl_e_t enc, drv_e_t drv, esi_pg2_drv_desc_t e_drv, int op)
{
((char *)e_drv)[0] = 0x80; /* Clear all but select bit */
e_drv->fault_reqstd = (op ? 1 : 0);
DBG(D_ESI, "Turning %s LED for drive %d in enclosure %d\n", (op ? "ON" : "OFF"), drv->phys_id, enc->phys_id);
}
void set_drv_bypass_ca(encl_e_t enc, drv_e_t drv, esi_pg2_drv_desc_t e_drv, int op)
{
((char *)e_drv)[0] = 0x80; /* Clear all but select bit */
if (enc->connect_port)
e_drv->enable_byp_B = (op ? 1 : 0);
else
e_drv->enable_byp_A = (op ? 1 : 0);
DBG(D_ESI, "Turning %s BYPASS for drive %d in enclosure %d\n", (op ? "ON" : "OFF"), drv->phys_id, enc->phys_id);
}
int agent_set_drv_cntrl(channel_e_t ch, fcid_bitmap_t ids, void (*ca)(), int op)
{
char devscsi_filename[256];
int tid, i;
encl_e_t enc;
drv_e_t drv;
esi_pg2_drv_desc_t e_drv;
esi_pg2_lcc_desc_t e_lcc;
int state=0;
char *devname;
int rc=0;
for (tid = TID_LO; tid <= TID_HI; ++tid) {
if (!FCID_ISSET(ids, tid))
continue;
enc = agent_find_encl_by_tid(ch, tid);
if (enc == NULL) {
rc = -tid;
goto set_control_done;
}
LOCK(enc->lock);
/*
* Put LCC into remote mode
*/
e_lcc = (esi_pg2_lcc_desc_t)agent_find_e_elem(enc, E_TYPE_LCC);
if (e_lcc == NULL)
fatal("agent_set_drv_led(): agent_find_e_elem returns NULL.\n");
/* Set select bit in global descriptor */
((char *)e_lcc)[0] = 0x80; /* Clear all but select bit */
++e_lcc; /* Skip over global */
/* Set select bit in LCC descriptor */
((char *)e_lcc)[0] = 0x80;
/* Reset all other fields */
((char *)e_lcc)[1] = ((char *)e_lcc)[2] = ((char *)e_lcc)[3] = 0x00;
DBG(D_ESI, "Putting encl %d into remote mode.\n", enc->phys_id);
e_lcc->local_mode = 0;
/*
* Set the appropriate mode in DRV descriptor
*/
e_drv = (esi_pg2_drv_desc_t)agent_find_e_elem(enc, E_TYPE_DISK);
if (e_drv == NULL)
fatal("agent_set_drv_led(): agent_find_e_elem returns NULL.\n");
/* Set select bit in global descriptor */
((char *)e_drv)[0] = 0x80; /* Clear all but select bit */
++e_drv; /* Skip over global */
for (i = 0, drv = enc->drv; i < enc->drv_count; ++i, ++drv, ++e_drv) {
if (FCID_ISSET(ids, drv->phys_id)) {
FCID_CLR(ids, drv->phys_id);
(*ca)(enc, drv, e_drv, op);
}
} /* for (i) */
#if DG_WAR_15
/* Rotate drive statuses before writing them out */
{
esi_pg2_drv_desc_struct_t e_drv_tmp[10];
printf("XXXX - rotating drive statuses \n");
e_drv = (esi_pg2_drv_desc_t)agent_find_e_elem(enc, E_TYPE_DISK);
++e_drv;
for (i = 0; i < enc->drv_count; ++i, ++e_drv) {
bcopy(e_drv, &e_drv_tmp[i], sizeof(esi_pg2_drv_desc_struct_t));
}
e_drv = (esi_pg2_drv_desc_t)agent_find_e_elem(enc, E_TYPE_DISK);
++e_drv;
for (i = 0; i < enc->drv_count; ++i, ++e_drv) {
bcopy(&e_drv_tmp[9-i], e_drv, sizeof(esi_pg2_drv_desc_struct_t));
}
}
#endif
#if 1 /* XXX */
/* Clear bytes 4 thru 7 */
for (i = 4; i < 8; ++i)
*((unchar *)enc->stat_buf + i) = 0;
#endif
#if DG_WAR_14
*((unchar *)enc->stat_buf + 1) = 0;
#endif
/*
* WRITE the control page, first trying the primary 8067 drive and then the
* secondary.
*/
set_control_retry:
state = 0;
while (1) {
switch (state) {
case 0:
case 1:
if (enc->esidrv[state & 0x3] != TID_UNDEF) {
devname = make_devscsi_path(ch->id, enc->esidrv[state], 0, devscsi_filename);
enc->stat_buf->page_code = SEND_ES_ENCL_CONTROL;
enc->stat_buf->page_len = enc->stat_blen_actual-4;
if (send_diagnostics(devname, (unchar *)enc->stat_buf, enc->stat_blen_actual) == 0) {
/*
* If succeeded on secondary, swap primary and secondary
*/
if (state) {
u_char drv;
drv = enc->esidrv[0];
enc->esidrv[0] = enc->esidrv[1];
enc->esidrv[1] = drv;
}
goto set_control_success;
}
else {
if (state == 0) {
fprintf(stderr, "Primary ESI drive (%d) failure on FC channel %d: "
"trying secondary (%d)\n",
enc->esidrv[0], ch->id, enc->esidrv[1]);
syslog(LOG_CRIT, "Primary ESI drive (%d) failure on FC channel %d: "
"trying secondary (%d)\n",
enc->esidrv[0], ch->id, enc->esidrv[1]);
}
else {
fprintf(stderr, "Secondary ESI drive (%d) failure on FC channel %d: "
"Unable to set enclosure state\n",
enc->esidrv[1], ch->id);
syslog(LOG_CRIT, "Secondary ESI drive (%d) failure on FC channel %d: "
"Unable to set enclosure state\n",
enc->esidrv[1], ch->id);
}
++state;
}
}
else
++state;
break;
default:
UNLOCK(enc->lock);
rc = 1;
goto set_control_done;
}
}
set_control_success:
UNLOCK(enc->lock);
} /* for (tid) */
set_control_done:
if (rc == 0) {
agent_get_encl_status(ch, enc, 0);
}
return(rc);
}
/*
******************************************************************************
* agent_set_drv_led() - set/reset fault one or more LEDs associated
* with drives.
******************************************************************************
*/
int agent_set_drv_led(channel_e_t ch, fcid_bitmap_t ids, int op)
{
return(agent_set_drv_cntrl(ch, ids, set_drv_led_ca, op));
}
/*
******************************************************************************
* agent_set_drv_bypass() - enabled/disable the LRC associated with drives.
******************************************************************************
*/
int agent_set_drv_bypass(channel_e_t ch, fcid_bitmap_t ids, int op)
{
return(agent_set_drv_cntrl(ch, ids, set_drv_bypass_ca, op));
}
/*
******************************************************************************
* agent_set_drv_remove() - remove one or more drives from loop. This
* happens in 3 steps. (1) if drive is not already bypassed, spin it down
* (2) attempt bypass via LPB primitive
* (3) read status and verify that drives are bypassed
* (4) attempt bypass via ESI mechanism if bypass failed.
******************************************************************************
*/
int agent_set_drv_remove(channel_e_t ch, fcid_bitmap_t ids, int spindown)
{
char devscsi_filename[256];
int tid;
char scsiha_filename[256];
char *devname;
encl_e_t enc;
drv_e_t drv;
esi_pg2_drv_desc_t e_drv;
fcid_bitmap_t ids_save, ids_not_bp;
int check_not_bp;
int rc=0;
int i;
ids_save = FCID_NEW();
ids_not_bp = FCID_NEW();
FCID_COPY(ids, ids_save);
/*
* (A) Spin down drives and attempt to bypass them by using the LPB primitive
*/
for (tid = TID_LO; tid <= TID_HI; ++tid) {
if (!FCID_ISSET(ids, tid))
continue;
enc = agent_find_encl_by_tid(ch, tid);
if (enc == NULL) {
rc = -tid;
goto remove_done;
}
for (i = 0, drv = enc->drv; i < enc->drv_count; ++i, ++drv) {
if (FCID_ISSET(ids, drv->phys_id)) {
FCID_CLR(ids, drv->phys_id);
if (drv->drv_asserting_bypass || drv->enc_asserting_bypass)
continue;
if (spindown) {
devname = make_devscsi_path(ch->id, drv->phys_id, 0, devscsi_filename);
startstop_drive(devname, 0);
}
devname = make_scsiha_path(ch->id, scsiha_filename);
if (enable_pbc(devname, drv->phys_id) == -1)
fprintf(stderr, "FC LPB failed for %d on channel %d. Will attempt ESI bypass.\n", drv->phys_id, ch->id);
}
} /* for (i) */
} /* for (tid) */
FCID_COPY(ids_save, ids);
/*
* (B) Check if drives are in fact bypassed and if not, attempt to
* bypass using the ESI controls.
*/
check_not_bp = 0;
for (tid = TID_LO; tid <= TID_HI; ++tid) {
if (!FCID_ISSET(ids, tid))
continue;
enc = agent_find_encl_by_tid(ch, tid);
if (enc == NULL) {
rc = -tid;
goto remove_done;
}
agent_get_encl_status(ch, enc, 0);
for (i = 0, drv = enc->drv; i < enc->drv_count; ++i, ++drv) {
if (FCID_ISSET(ids, drv->phys_id)) {
FCID_CLR(ids, drv->phys_id);
if (drv->drv_asserting_bypass || drv->enc_asserting_bypass)
continue;
else {
FCID_SET(ids_not_bp, drv->phys_id);
check_not_bp = 1;
}
}
} /* for (i) */
} /* for (tid) */
if (check_not_bp) {
rc = agent_set_drv_cntrl(ch, ids_not_bp, set_drv_bypass_ca, 1);
}
remove_done:
FCID_COPY(ids_save, ids);
FCID_FREE(ids_save);
FCID_FREE(ids_not_bp);
return(rc);
}
int agent_set_drv_insert(channel_e_t ch, fcid_bitmap_t ids, int spinup)
{
char devscsi_filename[256];
int tid;
char scsiha_filename[256];
char *devname;
encl_e_t enc;
drv_e_t drv;
esi_pg2_drv_desc_t e_drv;
fcid_bitmap_t ids_save, ids_not_ubp;
int check_not_ubp;
int rc=0;
int i;
ids_save = FCID_NEW();
ids_not_ubp = FCID_NEW();
FCID_COPY(ids, ids_save);
/*
* (A) Unbypass drives using the LPE primitive
*/
for (tid = TID_LO; tid <= TID_HI; ++tid) {
if (!FCID_ISSET(ids, tid))
continue;
enc = agent_find_encl_by_tid(ch, tid);
if (enc == NULL) {
rc = -tid;
goto insert_done;
}
for (i = 0, drv = enc->drv; i < enc->drv_count; ++i, ++drv) {
if (FCID_ISSET(ids, drv->phys_id)) {
FCID_CLR(ids, drv->phys_id);
if (!drv->drv_asserting_bypass && !drv->enc_asserting_bypass)
continue;
devname = make_scsiha_path(ch->id, scsiha_filename);
if (disable_pbc(devname, drv->phys_id) == -1)
fprintf(stderr, "FC LPE failed for %d on channel %d. Will attempt ESI upbypass.\n", drv->phys_id, ch->id);
}
} /* for (i) */
} /* for (tid) */
FCID_COPY(ids_save, ids);
/*
* (B) Check if drives are in fact unbypassed and if not, attempt to
* unbypass using the ESI controls.
*/
check_not_ubp = 0;
for (tid = TID_LO; tid <= TID_HI; ++tid) {
if (!FCID_ISSET(ids, tid))
continue;
enc = agent_find_encl_by_tid(ch, tid);
if (enc == NULL) {
rc = -tid;
goto insert_done;
}
agent_get_encl_status(ch, enc, 0);
for (i = 0, drv = enc->drv; i < enc->drv_count; ++i, ++drv) {
if (FCID_ISSET(ids, drv->phys_id)) {
FCID_CLR(ids, drv->phys_id);
if (drv->drv_asserting_bypass || drv->enc_asserting_bypass) {
FCID_SET(ids_not_ubp, drv->phys_id);
check_not_ubp = 1;
}
}
} /* for (i) */
} /* for (tid) */
if (check_not_ubp) {
rc = agent_set_drv_cntrl(ch, ids_not_ubp, set_drv_bypass_ca, 0);
}
FCID_COPY(ids_save, ids);
/*
* (C) Spin up drives
*/
for (tid = TID_LO; tid <= TID_HI; ++tid) {
if (!FCID_ISSET(ids, tid))
continue;
enc = agent_find_encl_by_tid(ch, tid);
for (i = 0, drv = enc->drv; i < enc->drv_count; ++i, ++drv) {
if (FCID_ISSET(ids, drv->phys_id)) {
FCID_CLR(ids, drv->phys_id);
if (spinup) {
devname = make_devscsi_path(ch->id, drv->phys_id, 0, devscsi_filename);
startstop_drive(devname, 1);
}
}
} /* for (i) */
} /* for (tid) */
insert_done:
FCID_COPY(ids_save, ids);
FCID_FREE(ids_save);
FCID_FREE(ids_not_ubp);
return(rc);
}