#include "options.h" #include #include #include /* For varargs */ #include /* For string conversion routines */ #include #include #include /* For character definitions */ #include #include /* for I/O functions */ #include #include #include /* for inventory functions */ #include #include /* for UNIX signals */ #include /* 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 \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); }