diff --git a/libcheckers/emc_clariion.c b/libcheckers/emc_clariion.c index a883e3d..1539921 100644 --- a/libcheckers/emc_clariion.c +++ b/libcheckers/emc_clariion.c @@ -10,10 +10,13 @@ #include #include #include +#include #include "checkers.h" #include "../libmultipath/sg_include.h" +#include "../libmultipath/vector.h" +#include "../libmultipath/debug.h" #define INQUIRY_CMD 0x12 #define INQUIRY_CMDLEN 6 @@ -22,31 +25,285 @@ struct emc_clariion_checker_context { char wwn[16]; unsigned wwn_set; + dev_t rdev; }; +/* + * Mechanism to track active and passive CLARiiON paths to inactive + * CLARiiON snapshot LUs. This is done so that we can fail passive paths + * to an inactive snapshot LU even though the read test contained herein + * will return 02/04/03 instead of 05/25/01 sense code/ASC/ASCQ data. + */ +int sg_read (int sg_fd, unsigned char * buff, unsigned char * senseBuff); + +/* + * Entry in fwwn_vec for each inactive snapshot LU. path_vec vector contains + * dev_t entries for each path to the LU. + */ +typedef struct fwwne { + char wwn[16]; /* WWN of inactive snapshot LU */ + vector path_vec; /* array of paths to this LU */ +} fwwne; +vector fwwn_vec = NULL; /* vector of inactive snapshot LUs */ +pthread_mutex_t fwwn_mutex = PTHREAD_MUTEX_INITIALIZER; + +#define WWN_SIZE 16 +#define INIT_WWN(dwwn, swwn) memcpy(dwwn, swwn, WWN_SIZE) +#define MATCH_WWNS(wwn1, wwn2) (memcmp(wwn1, wwn2, WWN_SIZE) == 0) + +#define INIT_PATH(dpath, spath) memcpy(dpath, spath, sizeof(dev_t)) +#define MATCH_PATHS(rdev1, rdev2) (memcmp(rdev1, rdev2, \ + sizeof(dev_t)) == 0) + +/* + * Setup LU and path associated with this path checker to be known as an + * inactive snapshot LU and path to same. + */ +void failwwn(struct checker *c) +{ + struct emc_clariion_checker_context * ct = + (struct emc_clariion_checker_context *)c->context; + char *wwn = ct->wwn; + vector path_vec; + fwwne *fp; + dev_t *pp; + int i, j; + + pthread_mutex_lock(&fwwn_mutex); + if (fwwn_vec) { + vector_foreach_slot(fwwn_vec, fp, i) { + /* + * Check for already known inactive snapshot LU. + */ + if (MATCH_WWNS(fp->wwn, wwn)) { + path_vec = fp->path_vec; + vector_foreach_slot(path_vec, pp, j) { + /* + * Check for already known path + * to already known inactive + * snapshot LU. + */ + if (MATCH_PATHS(pp, &ct->rdev)) { + pthread_mutex_unlock( + &fwwn_mutex); + return; + } + } + /* + * Identify new path to already known + * inactive snapshot LU. + */ + if (vector_alloc_slot(path_vec)) { + vector_set_slot(path_vec, &ct->rdev); + condlog(4, "failwwn %s count is %d.\n", + wwn, VECTOR_SIZE(path_vec)); + } + pthread_mutex_unlock(&fwwn_mutex); + return; + } + } + } else { + /* + * Allocate the inactive snapshot LU vector. + */ + fwwn_vec = vector_alloc(); + if (fwwn_vec == 0) { + pthread_mutex_unlock(&fwwn_mutex); + return; + } + } + + /* + * Allocate the path vector for this inactive snapshot LU. + */ + path_vec = vector_alloc(); + if (path_vec == 0) { + pthread_mutex_unlock(&fwwn_mutex); + return; + } + if (!vector_alloc_slot(path_vec)) { + vector_free(path_vec); + pthread_mutex_unlock(&fwwn_mutex); + return; + } + vector_set_slot(path_vec, &ct->rdev); + + /* + * Allocate the inactive snapshot vector entry itself. + */ + fp = malloc(sizeof(fwwne)); + if (fp == 0) { + vector_free(path_vec); + pthread_mutex_unlock(&fwwn_mutex); + return; + } + if (!vector_alloc_slot(fwwn_vec)) { + free(fp); + vector_free(path_vec); + pthread_mutex_unlock(&fwwn_mutex); + return; + } + + /* + * Initialize fields and link to the vector. + */ + INIT_WWN(fp->wwn, wwn); + fp->path_vec = path_vec; + vector_set_slot(fwwn_vec, fp); + + condlog(4, "failwwn %s count is %d.\n", wwn, VECTOR_SIZE(fp->path_vec)); + pthread_mutex_unlock(&fwwn_mutex); + return; +} + +/* + * Determine if the LU and path associated with this path checker are already + * associated with an-active snapshot LU. + */ +int findwwn(struct checker *c) +{ + struct emc_clariion_checker_context * ct = + (struct emc_clariion_checker_context *)c->context; + char *wwn = ct->wwn; + vector path_vec; + fwwne *fp; + dev_t *pp; + int i, j; + + pthread_mutex_lock(&fwwn_mutex); + if (fwwn_vec) { + vector_foreach_slot(fwwn_vec, fp, i) { + /* + * Check for already known inactive snapshot LU. + */ + if (MATCH_WWNS(fp->wwn, wwn)) { + path_vec = fp->path_vec; + vector_foreach_slot(path_vec, pp, j) { + /* + * Check for already known path + * to already known inactive + * snapshot LU. + */ + if (MATCH_PATHS(pp, &ct->rdev)) { + pthread_mutex_unlock( + &fwwn_mutex); + return 1; + } + } + /* + * Identify new path to already known + * inactive snapshot LU. + */ + if (vector_alloc_slot(path_vec)) { + vector_set_slot(path_vec, &ct->rdev); + condlog(4, "failwwn %s count is %d.\n", + wwn, VECTOR_SIZE(path_vec)); + } + pthread_mutex_unlock(&fwwn_mutex); + return 1; + } + } + } + pthread_mutex_unlock(&fwwn_mutex); + return 0; +} + +#define UNFAILWWN(c) unfailwwnpath(c, 0) +#define UNFAILWWNPATH(c) unfailwwnpath(c, 1) + +/* + * Remove LU and path associated with this path checker as + * inactive snapshot LU and path to same. + */ +void unfailwwnpath(struct checker *c, int match_paths) +{ + struct emc_clariion_checker_context * ct = + (struct emc_clariion_checker_context *)c->context; + char *wwn = ct->wwn; + vector path_vec; + fwwne *fp; + dev_t *pp; + int i, j; + + pthread_mutex_lock(&fwwn_mutex); + if (fwwn_vec) { + vector_foreach_slot(fwwn_vec, fp, i) { + /* + * Check for already known inactive snapshot LU. + */ + if (MATCH_WWNS(fp->wwn, wwn)) { + path_vec = fp->path_vec; + vector_foreach_slot(path_vec, pp, j) { + /* + * Check for already known path + * to already known inactive + * snapshot LU. + */ + if (!match_paths || + MATCH_PATHS(pp, &ct->rdev)) { + vector_del_slot(path_vec, j); + condlog(4, "unfailwwn %s " + "count is %d.\n", + wwn, + VECTOR_SIZE(path_vec)); + if (match_paths) + break; + } + } + if (VECTOR_SIZE(path_vec) == 0) { + vector_del_slot(fwwn_vec, i); + free(fp); + vector_free(path_vec); + pthread_mutex_unlock(&fwwn_mutex); + condlog(4, "unfailwwn de-alloc " + "%s.\n", wwn); + } + pthread_mutex_unlock(&fwwn_mutex); + return; + } + } + } + pthread_mutex_unlock(&fwwn_mutex); + return; +} + int emc_clariion_init (struct checker * c) { + struct stat stat; + int err; + c->context = malloc(sizeof(struct emc_clariion_checker_context)); if (!c->context) return 1; ((struct emc_clariion_checker_context *)c->context)->wwn_set = 0; + + err = fstat(c->fd, &stat); + if (err == 0) { + INIT_PATH(&((struct emc_clariion_checker_context *) + c->context)->rdev, &stat.st_rdev); + } + return 0; } void emc_clariion_free (struct checker * c) { + UNFAILWWNPATH(c); free(c->context); } int emc_clariion(struct checker * c) { - unsigned char sense_buffer[256] = { 0, }; + unsigned char sense_buffer[256] = { 0, }, *sbb; unsigned char sb[128] = { 0, }; + unsigned char buf[512]; unsigned char inqCmdBlk[INQUIRY_CMDLEN] = {INQUIRY_CMD, 1, 0xC0, 0, sizeof(sb), 0}; struct sg_io_hdr io_hdr; struct emc_clariion_checker_context * ct = (struct emc_clariion_checker_context *)c->context; + int ret; + int retry_count=3; memset(&io_hdr, 0, sizeof (struct sg_io_hdr)); io_hdr.interface_id = 'S'; @@ -69,7 +326,8 @@ int emc_clariion(struct checker * c) } if (/* Verify the code page - right page & revision */ sense_buffer[1] != 0xc0 || sense_buffer[9] != 0x00) { - MSG(c, "emc_clariion_checker: Path unit report page in unknown format"); + MSG(c, "emc_clariion_checker: Path unit report page in " + "unknown format"); return PATH_DOWN; } @@ -79,13 +337,15 @@ int emc_clariion(struct checker * c) || (sense_buffer[28] & 0x07) != 0x04 /* Arraycommpath should be set to 1 */ || (sense_buffer[30] & 0x04) != 0x04) { - MSG(c, "emc_clariion_checker: Path not correctly configured for failover"); + MSG(c, "emc_clariion_checker: Path not correctly " + "configured for failover"); return PATH_DOWN; } if ( /* LUN operations should indicate normal operations */ sense_buffer[48] != 0x00) { - MSG(c, "emc_clariion_checker: Path not available for normal operations"); + MSG(c, "emc_clariion_checker: Path not available for " + "normal operations"); return PATH_SHAKY; } @@ -102,15 +362,76 @@ int emc_clariion(struct checker * c) */ if (ct->wwn_set) { if (memcmp(ct->wwn, &sense_buffer[10], 16) != 0) { - MSG(c, "emc_clariion_checker: Logical Unit WWN has changed!"); + MSG(c, "emc_clariion_checker: Logical Unit WWN " + "has changed!"); return PATH_DOWN; } } else { memcpy(ct->wwn, &sense_buffer[10], 16); ct->wwn_set = 1; } - - - MSG(c, "emc_clariion_checker: Path healthy"); - return PATH_UP; + +retry: + memset(sense_buffer, 0, sizeof(sense_buffer)); + ret = sg_read(c->fd, &buf[0], sbb = &sense_buffer[0]); + if (ret == PATH_DOWN) { + char str[256]; + + condlog(4, "emc_clariion_checker: Read error for " + "(0x%llx), sense data are 0x%x/0x%x/0x%x", + ct->rdev, sbb[2]&0xf, sbb[12], sbb[13]); + + /* + * Check for inactive snapshot lu this way. Must fail these. + */ + if (((sbb[2]&0xf) == 5) && (sbb[12] == 0x25) && (sbb[13]==1)) { + /* + * Do this so that we can fail even the passive paths + * which will return 02/04/03 not 05/25/01. + */ + failwwn(c); + sprintf(str, "emc_clariion_checker: Active path " + "(0x%llx) to inactive snapshot.", ct->rdev); + MSG(c, str); + } + /* + * Check for passive/standby path this way. Must OK these + * as long as not path to inactive snapshot LU. + */ + else if (((sbb[2]&0xf)==2) && (sbb[12]==4) && (sbb[13]==3)) { + if (findwwn(c)) { + MSG(c, "emc_clariion_checker: Passive path " + "to inactive snapshot."); + } else { + MSG(c, + "emc_clariion_checker: Passive path is healthy."); + ret = PATH_UP; /* not ghost */ + } + } else { + sprintf(str, "emc_clariion_checker: Read error for " + "(0x%llx), sense data are 0x%x/0x%x/0x%x", + ct->rdev, sbb[2]&0xf, sbb[12], sbb[13]); + MSG(c, str); + /* + * Retry if UNIT_ATTENTION check condition since + * this likely indicates a path group re-assignment + * operation (trespass) took place. + */ + if (((sbb[2]&0xf) == 6) && (sbb[12] == 0x29) && + (sbb[13]==0)) { + if (--retry_count) + goto retry; + } + } + } else { + MSG(c, "emc_clariion_checker: Active path is healthy."); + /* + * Remove the path from the set of paths to inactive + * snapshot LUs if it was in this list since the snapshot + * is no longer inactive. + */ + UNFAILWWN(c); + } + + return ret; }