r = dm_rename(tmp, name);
if (r == 1) {
+ free(tmp);
*part_uuid = uuid;
return 1;
}
FILE1=kpartx1
FILE2=kpartx2
FILE3=kpartx3
+FILE4=kpartx4
SIZE=$((1024*1024*1024)) # use bytes as units here
SECTSIZ=512
OFFS=32 # offset of linear mapping into dev, sectors
truncate -s $SIZE $FILE1
truncate -s $SIZE $FILE2
truncate -s $SIZE $FILE3
+truncate -s $SIZE $FILE4
LO1=$(losetup -f $FILE1 --show)
push_cleanup 'losetup -d $LO1'
push_cleanup 'losetup -d $LO2'
LO3=$(losetup -f $FILE3 --show)
push_cleanup 'losetup -d $LO3'
+LO4=$(losetup -f $FILE4 --show)
+push_cleanup 'losetup -d $LO4'
-[[ $LO1 && $LO2 && $LO3 && -b $LO1 && -b $LO2 && -b $LO3 ]]
+[[ $LO1 && $LO2 && $LO3 && $LO4 && -b $LO1 && -b $LO2 && -b $LO3 && -b $LO4 ]]
DEV1=$(stat -c "%t:%T" $LO1)
DEV2=$(stat -c "%t:%T" $LO2)
DEV3=$(stat -c "%t:%T" $LO3)
mk_partitions $LO1
mk_partitions $LO2
+mk_partitions $LO4
# Test invocation of kpartx with regular file here
LO2P1=/dev/mapper/$(basename $LO2)-foo1
[[ -b /dev/mapper/$SPAN1 ]]
[[ -b /dev/mapper/$SPAN2 ]]
+step "test kpartx creation/deletion on an image file with no existing loopdev"
+losetup -d $LO4
+
+OUTPUT=$($KPARTX $KPARTX_OPTS -v -a $FILE4 2>&1)
+read loop dm < \
+ <(sed -n 's/^add map \(loop[0-9]*\)p1 ([0-9]*:\([0-9]*\)).*$/\1 dm-\2/p' \
+ <<<$OUTPUT)
+[[ $dm && $loop ]]
+push_cleanup "dmsetup remove -f /dev/$dm"
+push_cleanup "losetup -d /dev/$loop"
+
+[[ -b /dev/mapper/${loop}p1 ]]
+$KPARTX -d $KPARTX_OPTS $FILE4
+[[ ! -b /dev/mapper/${loop}p1 ]]
+# /dev/$loop is _not_ automatically deleted
+[[ -b /dev/${loop} ]]
+
OK=yes
#include <stdlib.h>
#include <string.h>
#include <errno.h>
-#include <sys/resource.h>
#define __STDC_FORMAT_MACROS 1
return NULL;
}
- if (conf->max_fds) {
- struct rlimit fd_limit;
-
- fd_limit.rlim_cur = conf->max_fds;
- fd_limit.rlim_max = conf->max_fds;
- if (setrlimit(RLIMIT_NOFILE, &fd_limit) < 0)
- condlog(0, "can't set open fds limit to %d : %s",
- conf->max_fds, strerror(errno));
- }
+ set_max_fds(conf->max_fds);
return conf;
}
}
if (rollback && ((rq_servact == MPATH_PROUT_REG_SA) && sa_key != 0 )){
condlog (3, "%s: ERROR: initiating pr out rollback", mpp->wwid);
+ memcpy(¶mp->key, ¶mp->sa_key, 8);
+ memset(¶mp->sa_key, 0, 8);
for( i=0 ; i < count ; i++){
if(thread[i].param.status == MPATH_PR_SUCCESS) {
- memcpy(&thread[i].param.paramp->key, &thread[i].param.paramp->sa_key, 8);
- memset(&thread[i].param.paramp->sa_key, 0, 8);
- thread[i].param.status = MPATH_PR_SUCCESS;
rc = pthread_create(&thread[i].id, &attr, mpath_prout_pthread_fn,
(void *)(&thread[i].param));
if (rc){
#include "checkers.h"
#include "vector.h"
+struct checker_class {
+ struct list_head node;
+ void *handle;
+ int refcount;
+ int sync;
+ char name[CHECKER_NAME_LEN];
+ int (*check)(struct checker *);
+ int (*init)(struct checker *); /* to allocate the context */
+ void (*free)(struct checker *); /* to free the context */
+ const char **msgtable;
+ short msgtable_size;
+};
+
char *checker_state_names[] = {
"wild",
"unchecked",
static LIST_HEAD(checkers);
-char * checker_state_name (int i)
+const char *checker_state_name(int i)
{
return checker_state_names[i];
}
-int init_checkers (char *multipath_dir)
-{
- if (!add_checker(multipath_dir, DEFAULT_CHECKER))
- return 1;
- return 0;
-}
-
-struct checker * alloc_checker (void)
+static struct checker_class *alloc_checker_class(void)
{
- struct checker *c;
+ struct checker_class *c;
- c = MALLOC(sizeof(struct checker));
+ c = MALLOC(sizeof(struct checker_class));
if (c) {
INIT_LIST_HEAD(&c->node);
c->refcount = 1;
- c->fd = -1;
}
return c;
}
-void free_checker (struct checker * c)
+void free_checker_class(struct checker_class *c)
{
if (!c)
return;
c->refcount--;
if (c->refcount) {
- condlog(3, "%s checker refcount %d",
+ condlog(4, "%s checker refcount %d",
c->name, c->refcount);
return;
}
void cleanup_checkers (void)
{
- struct checker * checker_loop;
- struct checker * checker_temp;
+ struct checker_class *checker_loop;
+ struct checker_class *checker_temp;
list_for_each_entry_safe(checker_loop, checker_temp, &checkers, node) {
- free_checker(checker_loop);
+ free_checker_class(checker_loop);
}
}
-struct checker * checker_lookup (char * name)
+static struct checker_class *checker_class_lookup(const char *name)
{
- struct checker * c;
+ struct checker_class *c;
if (!name || !strlen(name))
return NULL;
return NULL;
}
-struct checker * add_checker (char *multipath_dir, char * name)
+static struct checker_class *add_checker_class(const char *multipath_dir,
+ const char *name)
{
char libname[LIB_CHECKER_NAMELEN];
struct stat stbuf;
- struct checker * c;
+ struct checker_class *c;
char *errstr;
- c = alloc_checker();
+ c = alloc_checker_class();
if (!c)
return NULL;
snprintf(c->name, CHECKER_NAME_LEN, "%s", name);
if (!c->free)
goto out;
+ c->msgtable_size = 0;
+ c->msgtable = dlsym(c->handle, "libcheck_msgtable");
+
+ if (c->msgtable != NULL) {
+ const char **p;
+
+ for (p = c->msgtable;
+ *p && (p - c->msgtable) < CHECKER_MSGTABLE_SIZE; p++)
+ /* nothing */;
+
+ c->msgtable_size = p - c->msgtable;
+ } else
+ c->msgtable_size = 0;
+ condlog(3, "checker %s: message table size = %d",
+ c->name, c->msgtable_size);
+
done:
- c->fd = -1;
c->sync = 1;
list_add(&c->node, &checkers);
return c;
out:
- free_checker(c);
+ free_checker_class(c);
return NULL;
}
void checker_set_sync (struct checker * c)
{
- if (!c)
+ if (!c || !c->cls)
return;
- c->sync = 1;
+ c->cls->sync = 1;
}
void checker_set_async (struct checker * c)
{
- if (!c)
+ if (!c || !c->cls)
return;
- c->sync = 0;
+ c->cls->sync = 0;
}
void checker_enable (struct checker * c)
int checker_init (struct checker * c, void ** mpctxt_addr)
{
- if (!c)
+ if (!c || !c->cls)
return 1;
c->mpcontext = mpctxt_addr;
- if (c->init)
- return c->init(c);
+ if (c->cls->init)
+ return c->cls->init(c);
return 0;
}
void checker_put (struct checker * dst)
{
- struct checker * src;
+ struct checker_class *src;
- if (!dst || !strlen(dst->name))
+ if (!dst)
return;
- src = checker_lookup(dst->name);
- if (dst->free)
- dst->free(dst);
+ src = dst->cls;
+
+ if (src && src->free)
+ src->free(dst);
checker_clear(dst);
- free_checker(src);
+ free_checker_class(src);
}
int checker_check (struct checker * c, int path_state)
if (!c)
return PATH_WILD;
- c->message[0] = '\0';
+ c->msgid = CHECKER_MSGID_NONE;
if (c->disable) {
- MSG(c, "checker disabled");
+ c->msgid = CHECKER_MSGID_DISABLED;
return PATH_UNCHECKED;
}
- if (!strncmp(c->name, NONE, 4))
+ if (!strncmp(c->cls->name, NONE, 4))
return path_state;
if (c->fd < 0) {
- MSG(c, "no usable fd");
+ c->msgid = CHECKER_MSGID_NO_FD;
return PATH_WILD;
}
- r = c->check(c);
+ r = c->cls->check(c);
return r;
}
-int checker_selected (struct checker * c)
+int checker_selected(const struct checker *c)
{
if (!c)
return 0;
- if (!strncmp(c->name, NONE, 4))
- return 1;
- return (c->check) ? 1 : 0;
+ return c->cls != NULL;
}
-char * checker_name (struct checker * c)
+const char *checker_name(const struct checker *c)
{
- if (!c)
+ if (!c || !c->cls)
return NULL;
- return c->name;
+ return c->cls->name;
}
-char * checker_message (struct checker * c)
+int checker_is_sync(const struct checker *c)
{
- if (!c)
- return NULL;
- return c->message;
+ return c && c->cls && c->cls->sync;
+}
+
+static const char *generic_msg[CHECKER_GENERIC_MSGTABLE_SIZE] = {
+ [CHECKER_MSGID_NONE] = "",
+ [CHECKER_MSGID_DISABLED] = " is disabled",
+ [CHECKER_MSGID_NO_FD] = " has no usable fd",
+ [CHECKER_MSGID_INVALID] = " provided invalid message id",
+ [CHECKER_MSGID_UP] = " reports path is up",
+ [CHECKER_MSGID_DOWN] = " reports path is down",
+ [CHECKER_MSGID_GHOST] = " reports path is ghost",
+ [CHECKER_MSGID_UNSUPPORTED] = " doesn't support this device",
+};
+
+const char *checker_message(const struct checker *c)
+{
+ int id;
+
+ if (!c || c->msgid < 0 ||
+ (c->msgid >= CHECKER_GENERIC_MSGTABLE_SIZE &&
+ c->msgid < CHECKER_FIRST_MSGID))
+ goto bad_id;
+
+ if (c->msgid < CHECKER_GENERIC_MSGTABLE_SIZE)
+ return generic_msg[c->msgid];
+
+ id = c->msgid - CHECKER_FIRST_MSGID;
+ if (id < c->cls->msgtable_size)
+ return c->cls->msgtable[id];
+
+bad_id:
+ return generic_msg[CHECKER_MSGID_NONE];
}
void checker_clear_message (struct checker *c)
{
if (!c)
return;
- c->message[0] = '\0';
+ c->msgid = CHECKER_MSGID_NONE;
}
-void checker_get (char *multipath_dir, struct checker * dst, char * name)
+void checker_get(const char *multipath_dir, struct checker *dst,
+ const char *name)
{
- struct checker * src = NULL;
+ struct checker_class *src = NULL;
if (!dst)
return;
if (name && strlen(name)) {
- src = checker_lookup(name);
+ src = checker_class_lookup(name);
if (!src)
- src = add_checker(multipath_dir, name);
+ src = add_checker_class(multipath_dir, name);
}
- if (!src) {
- dst->check = NULL;
+ dst->cls = src;
+ if (!src)
return;
- }
- dst->fd = src->fd;
- dst->sync = src->sync;
- strncpy(dst->name, src->name, CHECKER_NAME_LEN);
- strncpy(dst->message, src->message, CHECKER_MSG_LEN);
- dst->check = src->check;
- dst->init = src->init;
- dst->free = src->free;
- dst->handle = NULL;
+
src->refcount++;
}
+
+int init_checkers(const char *multipath_dir)
+{
+ if (!add_checker_class(multipath_dir, DEFAULT_CHECKER))
+ return 1;
+ return 0;
+}
* Userspace (multipath/multipathd) path states
*
* PATH_WILD:
- * - Use: None of the checkers (returned if we don't have an fd)
- * - Description: Corner case where "fd < 0" for path fd (see checker_check())
+ * - Use: Any checker
+ * - Description: Corner case where "fd < 0" for path fd (see checker_check()),
+ * or where a checker detects an unsupported device
+ * (e.g. wrong checker configured for a given device).
*
* PATH_UNCHECKED:
* - Use: Only in directio checker
#define CHECKER_DEV_LEN 256
#define LIB_CHECKER_NAMELEN 256
+/*
+ * Generic message IDs for use in checkers.
+ */
+enum {
+ CHECKER_MSGID_NONE = 0,
+ CHECKER_MSGID_DISABLED,
+ CHECKER_MSGID_NO_FD,
+ CHECKER_MSGID_INVALID,
+ CHECKER_MSGID_UP,
+ CHECKER_MSGID_DOWN,
+ CHECKER_MSGID_GHOST,
+ CHECKER_MSGID_UNSUPPORTED,
+ CHECKER_GENERIC_MSGTABLE_SIZE,
+ CHECKER_FIRST_MSGID = 100, /* lowest msgid for checkers */
+ CHECKER_MSGTABLE_SIZE = 100, /* max msg table size for checkers */
+};
+
+struct checker_class;
struct checker {
- struct list_head node;
- void *handle;
- int refcount;
+ struct checker_class *cls;
int fd;
- int sync;
unsigned int timeout;
int disable;
- char name[CHECKER_NAME_LEN];
- char message[CHECKER_MSG_LEN]; /* comm with callers */
+ short msgid; /* checker-internal extra status */
void * context; /* store for persistent data */
void ** mpcontext; /* store for persistent data shared
multipath-wide. Use MALLOC if
you want to stuff data in. */
- int (*check)(struct checker *);
- int (*init)(struct checker *); /* to allocate the context */
- void (*free)(struct checker *); /* to free the context */
};
-#define MSG(c, fmt, args...) snprintf((c)->message, CHECKER_MSG_LEN, fmt, ##args);
-
-char * checker_state_name (int);
-int init_checkers (char *);
+const char *checker_state_name(int);
+int init_checkers(const char *);
void cleanup_checkers (void);
-struct checker * add_checker (char *, char *);
-struct checker * checker_lookup (char *);
int checker_init (struct checker *, void **);
void checker_clear (struct checker *);
void checker_put (struct checker *);
void checker_enable (struct checker *);
void checker_disable (struct checker *);
int checker_check (struct checker *, int);
-int checker_selected (struct checker *);
-char * checker_name (struct checker *);
-char * checker_message (struct checker *);
+int checker_selected(const struct checker *);
+int checker_is_sync(const struct checker *);
+const char *checker_name (const struct checker *);
+/*
+ * This returns a string that's best prepended with "$NAME checker",
+ * where $NAME is the return value of checker_name().
+ */
+const char *checker_message(const struct checker *);
void checker_clear_message (struct checker *c);
-void checker_get (char *, struct checker *, char *);
+void checker_get(const char *, struct checker *, const char *);
-/* Functions exported by path checker dynamic libraries (.so) */
+/* Prototypes for symbols exported by path checker dynamic libraries (.so) */
int libcheck_check(struct checker *);
int libcheck_init(struct checker *);
void libcheck_free(struct checker *);
+/*
+ * msgid => message map.
+ *
+ * It only needs to be provided if the checker defines specific
+ * message IDs.
+ * Message IDs available to checkers start at CHECKER_FIRST_MSG.
+ * The msgtable array is 0-based, i.e. msgtable[0] is the message
+ * for msgid == __CHECKER_FIRST_MSG.
+ * The table ends with a NULL element.
+ */
+extern const char *libcheck_msgtable[];
#endif /* _CHECKERS_H */
#define TUR_CMD_LEN 6
#define HEAVY_CHECK_COUNT 10
-#define MSG_CCISS_TUR_UP "cciss_tur checker reports path is up"
-#define MSG_CCISS_TUR_DOWN "cciss_tur checker reports path is down"
-
struct cciss_tur_checker_context {
void * dummy;
};
IOCTL_Command_struct cic; // cciss ioctl command
if ((c->fd) < 0) {
- MSG(c,"no usable fd");
+ c->msgid = CHECKER_MSGID_NO_FD;
ret = -1;
goto out;
}
perror("Error: ");
fprintf(stderr, "cciss TUR failed in CCISS_GETLUNINFO: %s\n",
strerror(errno));
- MSG(c,MSG_CCISS_TUR_DOWN);
+ c->msgid = CHECKER_MSGID_DOWN;
ret = PATH_DOWN;
goto out;
} else {
if (rc < 0) {
fprintf(stderr, "cciss TUR failed: %s\n",
strerror(errno));
- MSG(c,MSG_CCISS_TUR_DOWN);
+ c->msgid = CHECKER_MSGID_DOWN;
ret = PATH_DOWN;
goto out;
}
if ((cic.error_info.CommandStatus | cic.error_info.ScsiStatus )) {
- MSG(c,MSG_CCISS_TUR_DOWN);
+ c->msgid = CHECKER_MSGID_DOWN;
ret = PATH_DOWN;
goto out;
}
- MSG(c,MSG_CCISS_TUR_UP);
+ c->msgid = CHECKER_MSGID_UP;
ret = PATH_UP;
out:
#include "checkers.h"
#include "../libmultipath/debug.h"
-#define MSG_DIRECTIO_UNKNOWN "directio checker is not available"
-#define MSG_DIRECTIO_UP "directio checker reports path is up"
-#define MSG_DIRECTIO_DOWN "directio checker reports path is down"
-#define MSG_DIRECTIO_PENDING "directio checker is waiting on aio"
+enum {
+ MSG_DIRECTIO_UNKNOWN = CHECKER_FIRST_MSGID,
+ MSG_DIRECTIO_PENDING,
+ MSG_DIRECTIO_BLOCKSIZE,
+};
+
+#define _IDX(x) (MSG_DIRECTIO_##x - CHECKER_FIRST_MSGID)
+const char *libcheck_msgtable[] = {
+ [_IDX(UNKNOWN)] = " is not available",
+ [_IDX(PENDING)] = " is waiting on aio",
+ [_IDX(BLOCKSIZE)] = " cannot get blocksize, set default",
+ NULL,
+};
#define LOG(prio, fmt, args...) condlog(prio, "directio: " fmt, ##args)
}
if (ioctl(c->fd, BLKBSZGET, &ct->blksize) < 0) {
- MSG(c, "cannot get blocksize, set default");
+ c->msgid = MSG_DIRECTIO_BLOCKSIZE;
ct->blksize = 512;
}
if (ct->blksize > 4096) {
if (!ct)
return PATH_UNCHECKED;
- ret = check_state(c->fd, ct, c->sync, c->timeout);
+ ret = check_state(c->fd, ct, checker_is_sync(c), c->timeout);
switch (ret)
{
case PATH_UNCHECKED:
- MSG(c, MSG_DIRECTIO_UNKNOWN);
+ c->msgid = MSG_DIRECTIO_UNKNOWN;
break;
case PATH_DOWN:
- MSG(c, MSG_DIRECTIO_DOWN);
+ c->msgid = CHECKER_MSGID_DOWN;
break;
case PATH_UP:
- MSG(c, MSG_DIRECTIO_UP);
+ c->msgid = CHECKER_MSGID_UP;
break;
case PATH_PENDING:
- MSG(c, MSG_DIRECTIO_PENDING);
+ c->msgid = MSG_DIRECTIO_PENDING;
break;
default:
break;
#define INQUIRY_CMD 0x12
#define INQUIRY_CMDLEN 6
#define HEAVY_CHECK_COUNT 10
+#define SCSI_COMMAND_TERMINATED 0x22
+#define SCSI_CHECK_CONDITION 0x2
+#define RECOVERED_ERROR 0x01
+#define ILLEGAL_REQUEST 0x05
+#define SG_ERR_DRIVER_SENSE 0x08
/*
* Mechanism to track CLARiiON inactive snapshot LUs.
((struct emc_clariion_checker_LU_context *)\
(*c->mpcontext))->inactive_snap = 0
+enum {
+ MSG_CLARIION_QUERY_FAILED = CHECKER_FIRST_MSGID,
+ MSG_CLARIION_QUERY_ERROR,
+ MSG_CLARIION_PATH_CONFIG,
+ MSG_CLARIION_UNIT_REPORT,
+ MSG_CLARIION_PATH_NOT_AVAIL,
+ MSG_CLARIION_LUN_UNBOUND,
+ MSG_CLARIION_WWN_CHANGED,
+ MSG_CLARIION_READ_ERROR,
+ MSG_CLARIION_PASSIVE_GOOD,
+};
+
+#define _IDX(x) (MSG_CLARIION_ ## x - CHECKER_FIRST_MSGID)
+const char *libcheck_msgtable[] = {
+ [_IDX(QUERY_FAILED)] = ": sending query command failed",
+ [_IDX(QUERY_ERROR)] = ": query command indicates error",
+ [_IDX(PATH_CONFIG)] =
+ ": Path not correctly configured for failover",
+ [_IDX(UNIT_REPORT)] =
+ ": Path unit report page in unknown format",
+ [_IDX(PATH_NOT_AVAIL)] =
+ ": Path not available for normal operations",
+ [_IDX(LUN_UNBOUND)] = ": Logical Unit is unbound or LUNZ",
+ [_IDX(WWN_CHANGED)] = ": Logical Unit WWN has changed",
+ [_IDX(READ_ERROR)] = ": Read error",
+ [_IDX(PASSIVE_GOOD)] = ": Active path is healthy",
+ NULL,
+};
+
struct emc_clariion_checker_path_context {
char wwn[16];
unsigned wwn_set;
(struct emc_clariion_checker_path_context *)c->context;
char wwnstr[33];
int ret;
+ int retry_emc = 5;
+retry:
memset(&io_hdr, 0, sizeof (struct sg_io_hdr));
memset(sense_buffer, 0, 128);
memset(sb, 0, SENSE_BUFF_LEN);
io_hdr.timeout = c->timeout * 1000;
io_hdr.pack_id = 0;
if (ioctl(c->fd, SG_IO, &io_hdr) < 0) {
- MSG(c, "emc_clariion_checker: sending query command failed");
+ if (errno == ENOTTY) {
+ c->msgid = CHECKER_MSGID_UNSUPPORTED;
+ return PATH_WILD;
+ }
+ c->msgid = MSG_CLARIION_QUERY_FAILED;
return PATH_DOWN;
}
+
+ if (io_hdr.info & SG_INFO_OK_MASK) {
+ switch (io_hdr.host_status) {
+ case DID_BUS_BUSY:
+ case DID_ERROR:
+ case DID_SOFT_ERROR:
+ case DID_TRANSPORT_DISRUPTED:
+ /* Transport error, retry */
+ if (--retry_emc)
+ goto retry;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (SCSI_CHECK_CONDITION == io_hdr.status ||
+ SCSI_COMMAND_TERMINATED == io_hdr.status ||
+ SG_ERR_DRIVER_SENSE == (0xf & io_hdr.driver_status)) {
+ if (io_hdr.sbp && (io_hdr.sb_len_wr > 2)) {
+ unsigned char *sbp = io_hdr.sbp;
+ int sense_key;
+
+ if (sbp[0] & 0x2)
+ sense_key = sbp[1] & 0xf;
+ else
+ sense_key = sbp[2] & 0xf;
+
+ if (sense_key == ILLEGAL_REQUEST) {
+ c->msgid = CHECKER_MSGID_UNSUPPORTED;
+ return PATH_WILD;
+ } else if (sense_key != RECOVERED_ERROR) {
+ condlog(1, "emc_clariion_checker: INQUIRY failed with sense key %02x",
+ sense_key);
+ c->msgid = MSG_CLARIION_QUERY_ERROR;
+ return PATH_DOWN;
+ }
+ }
+ }
+
if (io_hdr.info & SG_INFO_OK_MASK) {
- MSG(c, "emc_clariion_checker: query command indicates error");
+ condlog(1, "emc_clariion_checker: INQUIRY failed without sense, status %02x",
+ io_hdr.status);
+ c->msgid = MSG_CLARIION_QUERY_ERROR;
return PATH_DOWN;
}
+
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");
+ c->msgid = MSG_CLARIION_UNIT_REPORT;
return PATH_DOWN;
}
((sense_buffer[28] & 0x07) != 0x06))
/* Arraycommpath should be set to 1 */
|| (sense_buffer[30] & 0x04) != 0x04) {
- MSG(c, "emc_clariion_checker: Path not correctly configured "
- "for failover");
+ c->msgid = MSG_CLARIION_PATH_CONFIG;
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");
+ c->msgid = MSG_CLARIION_PATH_NOT_AVAIL;
return PATH_SHAKY;
}
if ( /* LUN should at least be bound somewhere and not be LUNZ */
sense_buffer[4] == 0x00) {
- MSG(c, "emc_clariion_checker: Logical Unit is unbound "
- "or LUNZ");
+ c->msgid = MSG_CLARIION_LUN_UNBOUND;
return PATH_DOWN;
}
*/
if (ct->wwn_set) {
if (memcmp(ct->wwn, &sense_buffer[10], 16) != 0) {
- MSG(c, "emc_clariion_checker: Logical Unit WWN "
- "has changed!");
+ c->msgid = MSG_CLARIION_WWN_CHANGED;
return PATH_DOWN;
}
} else {
condlog(3, "emc_clariion_checker: Active "
"path to inactive snapshot WWN %s.",
wwnstr);
- } else
- MSG(c, "emc_clariion_checker: Read "
+ } else {
+ condlog(3, "emc_clariion_checker: Read "
"error for WWN %s. Sense data are "
"0x%x/0x%x/0x%x.", wwnstr,
sbb[2]&0xf, sbb[12], sbb[13]);
+ c->msgid = MSG_CLARIION_READ_ERROR;
+ }
} else {
- MSG(c, "emc_clariion_checker: Active path is "
- "healthy.");
+ c->msgid = MSG_CLARIION_PASSIVE_GOOD;
/*
* Remove the path from the set of paths to inactive
* snapshot LUs if it was in this list since the
wwnstr);
ret = PATH_DOWN;
} else {
- MSG(c,
- "emc_clariion_checker: Passive path is healthy.");
+ c->msgid = MSG_CLARIION_PASSIVE_GOOD;
ret = PATH_UP; /* not ghost */
}
}
#define SCSI_COMMAND_TERMINATED 0x22
#define SG_ERR_DRIVER_SENSE 0x08
#define RECOVERED_ERROR 0x01
+#define ILLEGAL_REQUEST 0x05
#define MX_ALLOC_LEN 255
#define HEAVY_CHECK_COUNT 10
-#define MSG_HP_SW_UP "hp_sw checker reports path is up"
-#define MSG_HP_SW_DOWN "hp_sw checker reports path is down"
-#define MSG_HP_SW_GHOST "hp_sw checker reports path is ghost"
-
struct sw_checker_context {
void * dummy;
};
io_hdr.sbp = sense_b;
io_hdr.timeout = timeout * 1000;
- if (ioctl(sg_fd, SG_IO, &io_hdr) < 0)
- return 1;
+ if (ioctl(sg_fd, SG_IO, &io_hdr) < 0) {
+ if (errno == ENOTTY)
+ return PATH_WILD;
+ else
+ return PATH_DOWN;
+ }
/* treat SG_ERR here to get rid of sg_err.[ch] */
io_hdr.status &= 0x7e;
if ((0 == io_hdr.status) && (0 == io_hdr.host_status) &&
(0 == io_hdr.driver_status))
- return 0;
+ return PATH_UP;
+
if ((SCSI_CHECK_CONDITION == io_hdr.status) ||
(SCSI_COMMAND_TERMINATED == io_hdr.status) ||
(SG_ERR_DRIVER_SENSE == (0xf & io_hdr.driver_status))) {
sense_key = sense_buffer[1] & 0xf;
else
sense_key = sense_buffer[2] & 0xf;
- if(RECOVERED_ERROR == sense_key)
- return 0;
+ if (RECOVERED_ERROR == sense_key)
+ return PATH_UP;
+ else if (ILLEGAL_REQUEST == sense_key)
+ return PATH_WILD;
}
}
- return 1;
+ return PATH_DOWN;
}
static int
int libcheck_check(struct checker * c)
{
char buff[MX_ALLOC_LEN];
+ int ret = do_inq(c->fd, 0, 1, 0x80, buff, MX_ALLOC_LEN, 0, c->timeout);
- if (0 != do_inq(c->fd, 0, 1, 0x80, buff, MX_ALLOC_LEN, 0, c->timeout)) {
- MSG(c, MSG_HP_SW_DOWN);
- return PATH_DOWN;
+ if (ret == PATH_WILD) {
+ c->msgid = CHECKER_MSGID_UNSUPPORTED;
+ return ret;
}
+ if (ret != PATH_UP) {
+ c->msgid = CHECKER_MSGID_DOWN;
+ return ret;
+ };
if (do_tur(c->fd, c->timeout)) {
- MSG(c, MSG_HP_SW_GHOST);
+ c->msgid = CHECKER_MSGID_GHOST;
return PATH_GHOST;
}
- MSG(c, MSG_HP_SW_UP);
+ c->msgid = CHECKER_MSGID_UP;
return PATH_UP;
}
#define SCSI_COMMAND_TERMINATED 0x22
#define SG_ERR_DRIVER_SENSE 0x08
#define RECOVERED_ERROR 0x01
+#define ILLEGAL_REQUEST 0x05
#define CURRENT_PAGE_CODE_VALUES 0
#define CHANGEABLE_PAGE_CODE_VALUES 1
-#define MSG_RDAC_UP "rdac checker reports path is up"
-#define MSG_RDAC_DOWN "rdac checker reports path is down"
-#define MSG_RDAC_GHOST "rdac checker reports path is ghost"
+#define MSG_RDAC_DOWN " reports path is down"
#define MSG_RDAC_DOWN_TYPE(STR) MSG_RDAC_DOWN": "STR
#define RTPG_UNAVAILABLE 0x3
io_hdr.sbp = sense_b;
io_hdr.timeout = timeout * 1000;
- if (ioctl(sg_fd, SG_IO, &io_hdr) < 0)
- return 1;
+ if (ioctl(sg_fd, SG_IO, &io_hdr) < 0 && errno == ENOTTY)
+ return PATH_WILD;
/* treat SG_ERR here to get rid of sg_err.[ch] */
io_hdr.status &= 0x7e;
if ((0 == io_hdr.status) && (0 == io_hdr.host_status) &&
(0 == io_hdr.driver_status))
- return 0;
+ return PATH_UP;
/* check if we need to retry this error */
if (io_hdr.info & SG_INFO_OK_MASK) {
else
sense_key = sense_buffer[2] & 0xf;
if (RECOVERED_ERROR == sense_key)
- return 0;
+ return PATH_UP;
+ else if (ILLEGAL_REQUEST == sense_key)
+ return PATH_WILD;
+ condlog(1, "rdac checker: INQUIRY failed with sense key %02x",
+ sense_key);
}
}
- return 1;
+ return PATH_DOWN;
}
struct volume_access_inq
char dontcare1[34];
};
-const char
-*checker_msg_string(struct volume_access_inq *inq)
+enum {
+ RDAC_MSGID_NOT_CONN = CHECKER_FIRST_MSGID,
+ RDAC_MSGID_IN_STARTUP,
+ RDAC_MSGID_NON_RESPONSIVE,
+ RDAC_MSGID_IN_RESET,
+ RDAC_MSGID_FW_DOWNLOADING,
+ RDAC_MSGID_QUIESCED,
+ RDAC_MSGID_SERVICE_MODE,
+ RDAC_MSGID_UNAVAILABLE,
+ RDAC_MSGID_INQUIRY_FAILED,
+};
+
+#define _IDX(x) (RDAC_MSGID_##x - CHECKER_FIRST_MSGID)
+const char *libcheck_msgtable[] = {
+ [_IDX(NOT_CONN)] = MSG_RDAC_DOWN_TYPE("lun not connected"),
+ [_IDX(IN_STARTUP)] = MSG_RDAC_DOWN_TYPE("ctlr is in startup sequence"),
+ [_IDX(NON_RESPONSIVE)] =
+ MSG_RDAC_DOWN_TYPE("non-responsive to queries"),
+ [_IDX(IN_RESET)] = MSG_RDAC_DOWN_TYPE("ctlr held in reset"),
+ [_IDX(FW_DOWNLOADING)] =
+ MSG_RDAC_DOWN_TYPE("ctlr firmware downloading"),
+ [_IDX(QUIESCED)] = MSG_RDAC_DOWN_TYPE("ctlr quiesced by admin request"),
+ [_IDX(SERVICE_MODE)] = MSG_RDAC_DOWN_TYPE("ctlr is in service mode"),
+ [_IDX(UNAVAILABLE)] = MSG_RDAC_DOWN_TYPE("ctlr is unavailable"),
+ [_IDX(INQUIRY_FAILED)] = MSG_RDAC_DOWN_TYPE("inquiry failed"),
+ NULL,
+};
+
+static int
+checker_msg_string(const struct volume_access_inq *inq)
{
/* lun not connected */
if (((inq->PQ_PDT & 0xE0) == 0x20) || (inq->PQ_PDT & 0x7f))
- return MSG_RDAC_DOWN_TYPE("lun not connected");
+ return RDAC_MSGID_NOT_CONN;
/* if no tpg data is available, give the generic path down message */
if (!(inq->avtcvp & 0x10))
- return MSG_RDAC_DOWN;
+ return CHECKER_MSGID_DOWN;
/* controller is booting up */
if (((inq->aas_cur & 0x0F) == RTPG_TRANSITIONING) &&
(inq->aas_alt & 0x0F) != RTPG_TRANSITIONING)
- return MSG_RDAC_DOWN_TYPE("ctlr is in startup sequence");
+ return RDAC_MSGID_IN_STARTUP;
/* if not unavailable, give generic message */
if ((inq->aas_cur & 0x0F) != RTPG_UNAVAILABLE)
- return MSG_RDAC_DOWN;
+ return CHECKER_MSGID_DOWN;
/* target port group unavailable */
switch (inq->vendor_specific_cur) {
case RTPG_UNAVAIL_NON_RESPONSIVE:
- return MSG_RDAC_DOWN_TYPE("non-responsive to queries");
+ return RDAC_MSGID_NON_RESPONSIVE;
case RTPG_UNAVAIL_IN_RESET:
- return MSG_RDAC_DOWN_TYPE("ctlr held in reset");
+ return RDAC_MSGID_IN_RESET;
case RTPG_UNAVAIL_CFW_DL1:
case RTPG_UNAVAIL_CFW_DL2:
- return MSG_RDAC_DOWN_TYPE("ctlr firmware downloading");
+ return RDAC_MSGID_FW_DOWNLOADING;
case RTPG_UNAVAIL_QUIESCED:
- return MSG_RDAC_DOWN_TYPE("ctlr quiesced by admin request");
+ return RDAC_MSGID_QUIESCED;
case RTPG_UNAVAIL_SERVICE_MODE:
- return MSG_RDAC_DOWN_TYPE("ctlr is in service mode");
+ return RDAC_MSGID_SERVICE_MODE;
default:
- return MSG_RDAC_DOWN_TYPE("ctlr is unavailable");
+ return RDAC_MSGID_UNAVAILABLE;
}
}
inqfail = 0;
memset(&inq, 0, sizeof(struct volume_access_inq));
- if (0 != do_inq(c->fd, 0xC9, &inq, sizeof(struct volume_access_inq),
- c->timeout)) {
- ret = PATH_DOWN;
+ ret = do_inq(c->fd, 0xC9, &inq, sizeof(struct volume_access_inq),
+ c->timeout);
+ if (ret != PATH_UP) {
inqfail = 1;
goto done;
- } else if (((inq.PQ_PDT & 0xE0) == 0x20) || (inq.PQ_PDT & 0x7f)) {
+ }
+
+ if (((inq.PQ_PDT & 0xE0) == 0x20) || (inq.PQ_PDT & 0x7f)) {
/* LUN not connected*/
ret = PATH_DOWN;
goto done;
done:
switch (ret) {
+ case PATH_WILD:
+ c->msgid = CHECKER_MSGID_UNSUPPORTED;
+ break;
case PATH_DOWN:
- MSG(c, "%s", (inqfail) ? MSG_RDAC_DOWN_TYPE("inquiry failed") :
- checker_msg_string(&inq));
+ c->msgid = (inqfail ? RDAC_MSGID_INQUIRY_FAILED :
+ checker_msg_string(&inq));
break;
case PATH_UP:
- MSG(c, MSG_RDAC_UP);
+ c->msgid = CHECKER_MSGID_UP;
break;
case PATH_GHOST:
- MSG(c, MSG_RDAC_GHOST);
+ c->msgid = CHECKER_MSGID_GHOST;
break;
}
#include "checkers.h"
#include "libsg.h"
-#define MSG_READSECTOR0_UP "readsector0 checker reports path is up"
-#define MSG_READSECTOR0_DOWN "readsector0 checker reports path is down"
-
struct readsector0_checker_context {
void * dummy;
};
switch (ret)
{
case PATH_DOWN:
- MSG(c, MSG_READSECTOR0_DOWN);
+ c->msgid = CHECKER_MSGID_DOWN;
break;
case PATH_UP:
- MSG(c, MSG_READSECTOR0_UP);
+ c->msgid = CHECKER_MSGID_UP;
break;
default:
break;
#define TUR_CMD_LEN 6
#define HEAVY_CHECK_COUNT 10
-#define MSG_TUR_UP "tur checker reports path is up"
-#define MSG_TUR_DOWN "tur checker reports path is down"
-#define MSG_TUR_GHOST "tur checker reports path is in standby state"
-#define MSG_TUR_RUNNING "tur checker still running"
-#define MSG_TUR_TIMEOUT "tur checker timed out"
-#define MSG_TUR_FAILED "tur checker failed to initialize"
+enum {
+ MSG_TUR_RUNNING = CHECKER_FIRST_MSGID,
+ MSG_TUR_TIMEOUT,
+ MSG_TUR_FAILED,
+};
+
+#define _IDX(x) (MSG_ ## x - CHECKER_FIRST_MSGID)
+const char *libcheck_msgtable[] = {
+ [_IDX(TUR_RUNNING)] = " still running",
+ [_IDX(TUR_TIMEOUT)] = " timed out",
+ [_IDX(TUR_FAILED)] = " failed to initialize",
+ NULL,
+};
struct tur_checker_context {
dev_t devt;
pthread_mutex_t lock;
pthread_cond_t active;
int holders; /* uatomic access only */
- char message[CHECKER_MSG_LEN];
+ int msgid;
};
int libcheck_init (struct checker * c)
}
static int
-tur_check(int fd, unsigned int timeout, char *msg)
+tur_check(int fd, unsigned int timeout, short *msgid)
{
struct sg_io_hdr io_hdr;
unsigned char turCmdBlk[TUR_CMD_LEN] = { 0x00, 0, 0, 0, 0, 0 };
io_hdr.timeout = timeout * 1000;
io_hdr.pack_id = 0;
if (ioctl(fd, SG_IO, &io_hdr) < 0) {
- snprintf(msg, CHECKER_MSG_LEN, MSG_TUR_DOWN);
+ if (errno == ENOTTY) {
+ *msgid = CHECKER_MSGID_UNSUPPORTED;
+ return PATH_WILD;
+ }
+ *msgid = CHECKER_MSGID_DOWN;
return PATH_DOWN;
}
if ((io_hdr.status & 0x7e) == 0x18) {
* SCSI-3 arrays might return
* reservation conflict on TUR
*/
- snprintf(msg, CHECKER_MSG_LEN, MSG_TUR_UP);
+ *msgid = CHECKER_MSGID_UP;
return PATH_UP;
}
if (io_hdr.info & SG_INFO_OK_MASK) {
* LOGICAL UNIT NOT ACCESSIBLE,
* TARGET PORT IN STANDBY STATE
*/
- snprintf(msg, CHECKER_MSG_LEN, MSG_TUR_GHOST);
+ *msgid = CHECKER_MSGID_GHOST;
return PATH_GHOST;
}
}
- snprintf(msg, CHECKER_MSG_LEN, MSG_TUR_DOWN);
+ *msgid = CHECKER_MSGID_DOWN;
return PATH_DOWN;
}
- snprintf(msg, CHECKER_MSG_LEN, MSG_TUR_UP);
+ *msgid = CHECKER_MSGID_UP;
return PATH_UP;
}
rcu_unregister_thread();
}
+/*
+ * Test code for "zombie tur thread" handling.
+ * Compile e.g. with CFLAGS=-DTUR_TEST_MAJOR=8
+ * Additional parameters can be configure with the macros below.
+ *
+ * Everty nth started TUR thread will hang in non-cancellable state
+ * for given number of seconds, for device given by major/minor.
+ */
+#ifdef TUR_TEST_MAJOR
+
+#ifndef TUR_TEST_MINOR
+#define TUR_TEST_MINOR 0
+#endif
+#ifndef TUR_SLEEP_INTERVAL
+#define TUR_SLEEP_INTERVAL 3
+#endif
+#ifndef TUR_SLEEP_SECS
+#define TUR_SLEEP_SECS 60
+#endif
+
+static void tur_deep_sleep(const struct tur_checker_context *ct)
+{
+ static int sleep_cnt;
+ const struct timespec ts = { .tv_sec = TUR_SLEEP_SECS, .tv_nsec = 0 };
+ int oldstate;
+
+ if (ct->devt != makedev(TUR_TEST_MAJOR, TUR_TEST_MINOR) ||
+ ++sleep_cnt % TUR_SLEEP_INTERVAL != 0)
+ return;
+
+ condlog(1, "tur thread going to sleep for %ld seconds", ts.tv_sec);
+ if (pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &oldstate) != 0)
+ condlog(0, "pthread_setcancelstate: %m");
+ if (nanosleep(&ts, NULL) != 0)
+ condlog(0, "nanosleep: %m");
+ condlog(1, "tur zombie thread woke up");
+ if (pthread_setcancelstate(oldstate, NULL) != 0)
+ condlog(0, "pthread_setcancelstate (2): %m");
+ pthread_testcancel();
+}
+#else
+#define tur_deep_sleep(x) do {} while (0)
+#endif /* TUR_TEST_MAJOR */
+
static void *tur_thread(void *ctx)
{
struct tur_checker_context *ct = ctx;
int state, running;
- char msg[CHECKER_MSG_LEN];
+ short msgid;
/* This thread can be canceled, so setup clean up */
tur_thread_cleanup_push(ct);
condlog(3, "%d:%d : tur checker starting up", major(ct->devt),
minor(ct->devt));
- state = tur_check(ct->fd, ct->timeout, msg);
+ tur_deep_sleep(ct);
+ state = tur_check(ct->fd, ct->timeout, &msgid);
pthread_testcancel();
/* TUR checker done */
pthread_mutex_lock(&ct->lock);
ct->state = state;
- strlcpy(ct->message, msg, sizeof(ct->message));
+ ct->msgid = msgid;
pthread_cond_signal(&ct->active);
pthread_mutex_unlock(&ct->lock);
if (!ct)
return PATH_UNCHECKED;
- if (c->sync)
- return tur_check(c->fd, c->timeout, c->message);
+ if (checker_is_sync(c))
+ return tur_check(c->fd, c->timeout, &c->msgid);
/*
* Async mode
pthread_cancel(ct->thread);
condlog(3, "%d:%d : tur checker timeout",
major(ct->devt), minor(ct->devt));
- MSG(c, MSG_TUR_TIMEOUT);
+ c->msgid = MSG_TUR_TIMEOUT;
tur_status = PATH_TIMEOUT;
} else {
pthread_mutex_lock(&ct->lock);
tur_status = ct->state;
- strlcpy(c->message, ct->message,
- sizeof(c->message));
+ c->msgid = ct->msgid;
pthread_mutex_unlock(&ct->lock);
}
ct->thread = 0;
ct->thread = 0;
pthread_mutex_lock(&ct->lock);
tur_status = ct->state;
- strlcpy(c->message, ct->message, sizeof(c->message));
+ c->msgid = ct->msgid;
pthread_mutex_unlock(&ct->lock);
}
} else {
if (uatomic_read(&ct->holders) > 1) {
- /* The thread has been cancelled but hasn't
- * quilt. Fail back to synchronous mode */
- condlog(3, "%d:%d : tur checker failing back to sync",
+ /*
+ * The thread has been cancelled but hasn't quit.
+ * We have to prevent it from interfering with the new
+ * thread. We create a new context and leave the old
+ * one with the stale thread, hoping it will clean up
+ * eventually.
+ */
+ condlog(3, "%d:%d : tur thread not responding",
major(ct->devt), minor(ct->devt));
- return tur_check(c->fd, c->timeout, c->message);
+
+ /*
+ * libcheck_init will replace c->context.
+ * It fails only in OOM situations. In this case, return
+ * PATH_UNCHECKED to avoid prematurely failing the path.
+ */
+ if (libcheck_init(c) != 0)
+ return PATH_UNCHECKED;
+
+ if (!uatomic_sub_return(&ct->holders, 1))
+ /* It did terminate, eventually */
+ cleanup_context(ct);
+
+ ct = c->context;
}
/* Start new TUR checker */
pthread_mutex_lock(&ct->lock);
tur_status = ct->state = PATH_PENDING;
- ct->message[0] = '\0';
+ ct->msgid = CHECKER_MSGID_NONE;
pthread_mutex_unlock(&ct->lock);
ct->fd = c->fd;
ct->timeout = c->timeout;
ct->thread = 0;
condlog(3, "%d:%d : failed to start tur thread, using"
" sync mode", major(ct->devt), minor(ct->devt));
- return tur_check(c->fd, c->timeout, c->message);
+ return tur_check(c->fd, c->timeout, &c->msgid);
}
tur_timeout(&tsp);
pthread_mutex_lock(&ct->lock);
if (ct->state == PATH_PENDING)
- r = pthread_cond_timedwait(&ct->active, &ct->lock,
+ r = pthread_cond_timedwait(&ct->active, &ct->lock,
&tsp);
if (!r) {
tur_status = ct->state;
- strlcpy(c->message, ct->message, sizeof(c->message));
+ c->msgid = ct->msgid;
}
pthread_mutex_unlock(&ct->lock);
if (tur_status == PATH_PENDING) {
FREE(conf);
}
-static void free_namelist(void *nl)
-{
- free(nl);
-}
-
/* if multipath fails to process the config directory, it should continue,
* with just a warning message */
static void
process_config_dir(struct config *conf, vector keywords, char *dir)
{
struct dirent **namelist;
+ struct scandir_result sr;
int i, n;
char path[LINE_MAX];
int old_hwtable_size;
return;
} else if (n == 0)
return;
- pthread_cleanup_push(free_namelist, namelist);
+ sr.di = namelist;
+ sr.n = n;
+ pthread_cleanup_push_cast(free_scandir_result, &sr);
for (i = 0; i < n; i++) {
if (!strstr(namelist[i]->d_name, ".conf"))
continue;
}
}
vector_foreach_slot (pathvec, pp1, k) {
- int invalid = 0;
+ int invalid;
/* skip this path for some reason */
/* 1. if path has no unique id or wwid blacklisted */
+ if (strlen(pp1->wwid) == 0) {
+ orphan_path(pp1, "no WWID");
+ continue;
+ }
+
conf = get_multipath_config();
pthread_cleanup_push(put_multipath_config, conf);
- if (strlen(pp1->wwid) == 0 || filter_path(conf, pp1) > 0)
- invalid = 1;
+ invalid = (filter_path(conf, pp1) > 0);
pthread_cleanup_pop(1);
if (invalid) {
orphan_path(pp1, "blacklisted");
#define DEFAULT_UNKNOWN_FIND_MULTIPATHS_TIMEOUT 1
#define DEFAULT_ALL_TG_PT ALL_TG_PT_OFF
+#define CHECKINT_UNDEF (~0U)
#define DEFAULT_CHECKINT 5
#define MAX_CHECKINT(a) (a << 2)
strftime(buff, sizeof(buff), "%b %d %H:%M:%S", tb);
buff[sizeof(buff)-1] = '\0';
- fprintf(stdout, "%s | ", buff);
+ fprintf(stderr, "%s | ", buff);
}
- fprintf(stdout, "libdevmapper: %s(%i): ", file, line);
- vfprintf(stdout, f, ap);
- fprintf(stdout, "\n");
+ fprintf(stderr, "libdevmapper: %s(%i): ", file, line);
+ vfprintf(stderr, f, ap);
+ fprintf(stderr, "\n");
} else {
condlog(level, "libdevmapper: %s(%i): ", file, line);
log_safe(level + 3, f, ap);
*int_ptr = atoi(buff);
+ FREE(buff);
return 0;
}
return function (buff, len, mpe->option); \
}
-declare_def_handler(checkint, set_int)
+static int checkint_handler(struct config *conf, vector strvec)
+{
+ int rc = set_int(strvec, &conf->checkint);
+
+ if (rc)
+ return rc;
+ if (conf->checkint == CHECKINT_UNDEF)
+ conf->checkint--;
+ return 0;
+}
+
declare_def_snprint(checkint, print_int)
declare_def_handler(max_checkint, set_int)
{
install_keyword_root("defaults", NULL);
install_keyword("verbosity", &def_verbosity_handler, &snprint_def_verbosity);
- install_keyword("polling_interval", &def_checkint_handler, &snprint_def_checkint);
+ install_keyword("polling_interval", &checkint_handler, &snprint_def_checkint);
install_keyword("max_polling_interval", &def_max_checkint_handler, &snprint_def_max_checkint);
install_keyword("reassign_maps", &def_reassign_maps_handler, &snprint_def_reassign_maps);
install_keyword("multipath_dir", &def_multipath_dir_handler, &snprint_def_multipath_dir);
err = store_path(pathvec, pp);
if (err)
goto out;
+ pp->checkint = conf->checkint;
out:
if (err)
} else {
snprintf(value, 11, "%u", mpp->fast_io_fail);
if (sysfs_attr_set_value(session_dev, "recovery_tmo",
- value, 11) <= 0) {
+ value, strlen(value)) <= 0) {
condlog(3, "%s: Failed to set recovery_tmo, "
" error %d", pp->dev, errno);
}
if (mpp->dev_loss) {
snprintf(value, 11, "%u", mpp->dev_loss);
if (sysfs_attr_set_value(sas_dev, "I_T_nexus_loss_timeout",
- value, 11) <= 0)
+ value, strlen(value)) <= 0)
condlog(3, "%s: failed to update "
"I_T Nexus loss timeout, error %d",
pp->dev, errno);
}
}
-static int
+static void
scsi_ioctl_pathinfo (struct path * pp, struct config *conf, int mask)
{
struct udev_device *parent;
detect_alua(pp, conf);
if (!(mask & DI_SERIAL))
- return 0;
+ return;
parent = pp->udev;
while (parent) {
parent = udev_device_get_parent(parent);
}
if (!attr_path || pp->sg_id.host_no == -1)
- return 0;
+ return;
if (get_vpd_sysfs(parent, 0x80, pp->serial, SERIAL_SIZE) <= 0) {
if (get_serial(pp->serial, SERIAL_SIZE, pp->fd)) {
- condlog(2, "%s: fail to get serial", pp->dev);
- return 0;
+ condlog(3, "%s: fail to get serial", pp->dev);
+ return;
}
}
condlog(3, "%s: serial = %s", pp->dev, pp->serial);
- return 0;
+ return;
}
-static int
-cciss_ioctl_pathinfo (struct path * pp, int mask)
+static void
+cciss_ioctl_pathinfo(struct path *pp)
{
- if (mask & DI_SERIAL) {
- get_serial(pp->serial, SERIAL_SIZE, pp->fd);
- condlog(3, "%s: serial = %s", pp->dev, pp->serial);
- }
- return 0;
+ get_serial(pp->serial, SERIAL_SIZE, pp->fd);
+ condlog(3, "%s: serial = %s", pp->dev, pp->serial);
}
int
checker_name(c), checker_state_name(state));
if (state != PATH_UP && state != PATH_GHOST &&
strlen(checker_message(c)))
- condlog(3, "%s: checker msg is \"%s\"",
- pp->dev, checker_message(c));
+ condlog(3, "%s: %s checker%s",
+ pp->dev, checker_name(c), checker_message(c));
return state;
}
if (mask & DI_SERIAL)
get_geometry(pp);
- if (path_state == PATH_UP && pp->bus == SYSFS_BUS_SCSI &&
- scsi_ioctl_pathinfo(pp, conf, mask))
- goto blank;
+ if (path_state == PATH_UP && pp->bus == SYSFS_BUS_SCSI)
+ scsi_ioctl_pathinfo(pp, conf, mask);
- if (pp->bus == SYSFS_BUS_CCISS &&
- cciss_ioctl_pathinfo(pp, mask))
- goto blank;
+ if (pp->bus == SYSFS_BUS_CCISS && mask & DI_SERIAL)
+ cciss_ioctl_pathinfo(pp);
if (mask & DI_CHECKER) {
if (path_state == PATH_UP) {
pp->chkrstate = pp->state = get_state(pp, conf, 0,
path_state);
- if (pp->state == PATH_UNCHECKED ||
- pp->state == PATH_WILD)
- goto blank;
if (pp->state == PATH_TIMEOUT)
pp->state = PATH_DOWN;
if (pp->state == PATH_UP && !pp->size) {
{
char pathbuf[PATH_MAX];
struct dirent **di;
+ struct scandir_result sr;
int r, i;
foreigns = vector_alloc();
return -r;
}
- pthread_cleanup_push(free, di);
+ sr.di = di;
+ sr.n = r;
+ pthread_cleanup_push_cast(free_scandir_result, &sr);
for (i = 0; i < r; i++) {
const char *msg, *fn, *c;
struct foreign *fgn;
#include <dirent.h>
#include <errno.h>
#include <ctype.h>
+#include "util.h"
#include "vector.h"
#include "generic.h"
#include "foreign.h"
{
char pathbuf[PATH_MAX], realbuf[PATH_MAX];
struct dirent **di = NULL;
+ struct scandir_result sr;
struct udev_device *subsys;
struct nvme_path *path;
int r, i, n;
return;
}
- pthread_cleanup_push(free, di);
+ sr.di = di;
+ sr.n = r;
+ pthread_cleanup_push_cast(free_scandir_result, &sr);
for (i = 0; i < r; i++) {
char *fn = di[i]->d_name;
struct udev_device *ctrl, *udev;
snprint_path_checker (char * buff, size_t len, const struct path * pp)
{
const struct checker * c = &pp->checker;
- return snprint_str(buff, len, c->name);
+ return snprint_str(buff, len, checker_name(c));
}
static int
int select_checker(struct config *conf, struct path *pp)
{
const char *origin;
- char *checker_name;
+ char *ckr_name;
struct checker * c = &pp->checker;
if (pp->detect_checker == DETECT_CHECKER_ON) {
origin = autodetect_origin;
if (check_rdac(pp)) {
- checker_name = RDAC;
+ ckr_name = RDAC;
goto out;
} else if (pp->tpgs > 0) {
- checker_name = TUR;
+ ckr_name = TUR;
goto out;
}
}
- do_set(checker_name, conf->overrides, checker_name, overrides_origin);
- do_set_from_hwe(checker_name, pp, checker_name, hwe_origin);
- do_set(checker_name, conf, checker_name, conf_origin);
- do_default(checker_name, DEFAULT_CHECKER);
+ do_set(checker_name, conf->overrides, ckr_name, overrides_origin);
+ do_set_from_hwe(checker_name, pp, ckr_name, hwe_origin);
+ do_set(checker_name, conf, ckr_name, conf_origin);
+ do_default(ckr_name, DEFAULT_CHECKER);
out:
- checker_get(conf->multipath_dir, c, checker_name);
- condlog(3, "%s: path_checker = %s %s", pp->dev, c->name, origin);
+ checker_get(conf->multipath_dir, c, ckr_name);
+ condlog(3, "%s: path_checker = %s %s", pp->dev,
+ checker_name(c), origin);
if (conf->checker_timeout) {
c->timeout = conf->checker_timeout;
condlog(3, "%s: checker timeout = %u s %s",
pp->fd = -1;
pp->tpgs = TPGS_UNDEF;
pp->priority = PRIO_UNDEF;
+ pp->checkint = CHECKINT_UNDEF;
checker_clear(&pp->checker);
dm_path_to_gen(pp)->ops = &dm_gen_path_ops;
pp->hwe = vector_alloc();
else if ((pp->dmstate == PSTATE_ACTIVE ||
pp->dmstate == PSTATE_UNDEF) &&
(pp->state == PATH_DOWN ||
- pp->state == PATH_SHAKY))
+ pp->state == PATH_SHAKY)) {
+ condlog(2, "sync_map_state: failing %s state %d dmstate %d",
+ pp->dev, pp->state, pp->dmstate);
dm_fail_path(mpp->alias, pp->dev_t);
+ }
}
}
}
bool sysfs_is_multipathed(const struct path *pp)
{
char pathbuf[PATH_MAX];
+ struct scandir_result sr;
struct dirent **di;
int n, r, i;
bool found = false;
return false;
}
- pthread_cleanup_push(free, di);
+ sr.di = di;
+ sr.n = r;
+ pthread_cleanup_push_cast(free_scandir_result, &sr);
for (i = 0; i < r && !found; i++) {
long fd;
int nr;
#include <sys/sysmacros.h>
#include <sys/types.h>
#include <sys/utsname.h>
+#include <sys/time.h>
+#include <sys/resource.h>
#include <dirent.h>
#include <unistd.h>
#include <errno.h>
}
return 0;
}
+
+void set_max_fds(int max_fds)
+{
+ struct rlimit fd_limit;
+
+ if (!max_fds)
+ return;
+
+ if (getrlimit(RLIMIT_NOFILE, &fd_limit) < 0) {
+ condlog(0, "can't get open fds limit: %s",
+ strerror(errno));
+ fd_limit.rlim_cur = 0;
+ fd_limit.rlim_max = 0;
+ }
+ if (fd_limit.rlim_cur < max_fds) {
+ fd_limit.rlim_cur = max_fds;
+ if (fd_limit.rlim_max < max_fds)
+ fd_limit.rlim_max = max_fds;
+ if (setrlimit(RLIMIT_NOFILE, &fd_limit) < 0) {
+ condlog(0, "can't set open fds limit to "
+ "%lu/%lu : %s",
+ fd_limit.rlim_cur, fd_limit.rlim_max,
+ strerror(errno));
+ } else {
+ condlog(3, "set open fds limit to %lu/%lu",
+ fd_limit.rlim_cur, fd_limit.rlim_max);
+ }
+ }
+}
+
+void free_scandir_result(struct scandir_result *res)
+{
+ int i;
+
+ for (i = 0; i < res->n; i++)
+ FREE(res->di[i]);
+ FREE(res->di);
+}
int parse_prkey(char *ptr, uint64_t *prkey);
int parse_prkey_flags(char *ptr, uint64_t *prkey, uint8_t *flags);
int safe_write(int fd, const void *buf, size_t count);
+void set_max_fds(int max_fds);
#define KERNEL_VERSION(maj, min, ptc) ((((maj) * 256) + (min)) * 256 + (ptc))
#define safe_snprintf(var, size, format, args...) \
snprintf(var, size, format, ##args) >= size
+#define pthread_cleanup_push_cast(f, arg) \
+ pthread_cleanup_push(((void (*)(void *))&f), (arg))
+
+struct scandir_result {
+ struct dirent **di;
+ int n;
+};
+void free_scandir_result(struct scandir_result *);
+
#endif /* _UTIL_H */
#ifndef _VERSION_H
#define _VERSION_H
-#define VERSION_CODE 0x000708
-#define DATE_CODE 0x0a0a12
+#define VERSION_CODE 0x000709
+#define DATE_CODE 0x0b0e12
#define PROG "multipath-tools"
#include "pgpolicies.h"
#include "version.h"
#include <errno.h>
-#include <sys/time.h>
-#include <sys/resource.h>
#include "wwids.h"
#include "uxsock.h"
#include "mpath_cmd.h"
logsink = 1;
}
- if (conf->max_fds) {
- struct rlimit fd_limit;
-
- fd_limit.rlim_cur = conf->max_fds;
- fd_limit.rlim_max = conf->max_fds;
- if (setrlimit(RLIMIT_NOFILE, &fd_limit) < 0)
- condlog(0, "can't set open fds limit to %d : %s",
- conf->max_fds, strerror(errno));
- }
+ set_max_fds(conf->max_fds);
libmp_udev_set_sync_support(1);
.\"
.\" ----------------------------------------------------------------------------
.
-.TH MULTIPATH 8 2016-10-26 "Linux"
+.TH MULTIPATH 8 2018-10-10 "Linux"
.
.
.\" ----------------------------------------------------------------------------
.
.B multipath
.RB [\| \-v\ \c
-.IR verbosity \|]
+.IR level \|]
+.RB [\| \-B | \-d | \-i | \-q | \-r \|]
.RB [\| \-b\ \c
-.IR bindings_file \|]
-.RB [\| \-d \|]
-.RB [\| \-h | \-l | \-ll | \-f | \-t | \-T | \-F | \-B | \-c | \-C | \-q | \-r | \-i | \-a | \-u | \-U | \-w | \-W \|]
+.IR file \|]
.RB [\| \-p\ \c
-.IR failover | multibus | group_by_serial | group_by_prio | group_by_node_name \|]
+.IR policy \|]
+.RB [\| device \|]
+.
+.LP
+.B multipath
+.RB [\| \-v\ \c
+.IR level \|]
+.RB [\| \-R\ \c
+.IR retries \|]
+.B \-f device
+.
+.LP
+.B multipath
+.RB [\| \-v\ \c
+.IR level \|]
.RB [\| \-R\ \c
.IR retries \|]
+.B \-F
+.
+.LP
+.B multipath
+.RB [\| \-v\ \c
+.IR level \|]
+.RB [\| \-l | \-ll \|]
.RB [\| device \|]
.
+.LP
+.B multipath
+.RB [\| \-v\ \c
+.IR level \|]
+.RB [\| \-a | \-w \|]
+.B device
+.
+.LP
+.B multipath
+.RB [\| \-v\ \c
+.IR level \|]
+.B -W
+.
+.LP
+.B multipath
+.RB [\| \-v\ \c
+.IR level \|]
+.RB [\| \-i \|]
+.RB [\| \-c | \-C \|]
+.B device
+.
+.LP
+.B multipath
+.RB [\| \-v\ \c
+.IR level \|]
+.RB [\| \-i \|]
+.RB [\| \-u | \-U \|]
+.
+.LP
+.B multipath
+.RB [\| \-h | \-t | \-T \|]
.
.\" ----------------------------------------------------------------------------
.SH DESCRIPTION
.B multipath
is used to detect and coalesce multiple paths to devices, for fail-over or performance reasons.
.
-.
.\" ----------------------------------------------------------------------------
-.SH OPTIONS
+.SH ARGUMENTS
.\" ----------------------------------------------------------------------------
.
-.TP
-.BI \-v " level"
-Verbosity, print all paths and multipaths:
-.RS 1.2i
-.TP 1.2i
-.I 0
-No output.
-.TP
-.I 1
-Print the created or updated multipath names only, for use to feed other tools like kpartx.
-.TP
-.I 2 +
-Print all info: detected paths, coalesced paths (ie multipaths) and device maps.
-.RE
+The \fBdevice\fR argument restricts \fBmultipath\fR's operation to devices matching the given
+expression. The argument may refer either to a multipath map or to
+its components ("paths"). The expression may be in one of the following formats:
.
-.TP
-.B \-h
-Print usage text.
+.TP 1.4i
+.B device node
+file name of a device node, e.g. \fI/dev/dm-10\fR or \fI/dev/sda\fR. If the node refers
+to an existing device mapper device representing a multipath map, this selects
+the map or its paths, depending on the operation mode. Otherwise, it selects a path device.
.
.TP
-.B \-d
-Dry run, do not create or update devmaps.
+.B device ID
+kernel device number specified by major:minor numbers, e.g. \fI65:16\fR. This
+format can only be used for path devices.
.
.TP
-.B \-l
-Show the current multipath topology from information fetched in sysfs and the device mapper.
+.B WWID
+a World Wide Identifier matching a multipath map or its paths. To list WWIDs of devices
+present in the system, use e.g. the command "\fImultipath -d -v3 2>/dev/null\fR".
.
-.TP
-.B \-ll
-Show the current multipath topology from all available information (sysfs, the device mapper, path checkers ...).
+.\" ----------------------------------------------------------------------------
+.SH OPERATION MODES
+.\" ----------------------------------------------------------------------------
+.
+The default operation mode is to detect and set up multipath maps from the devices found in
+the system.
.
+Other operation modes are chosen by using one of the following command line switches:
.TP
.B \-f
-Flush a multipath device map specified as parameter, if unused.
+Flush (remove) a multipath device map specified as parameter, if unused.
.
.TP
.B \-F
-Flush all unused multipath device maps.
+Flush (remove) all unused multipath device maps.
.
.TP
-.B \-t
-Display the currently used multipathd configuration.
-.
-.TP
-.B \-T
-Display the currently used multipathd configuration, limiting the output to
-those devices actually present in the system. This can be used a template for
-creating \fImultipath.conf\fR.
+.B \-l
+Show ("list") the current multipath topology from information fetched in sysfs and the device mapper.
.
.TP
-.B \-r
-Force devmap reload.
+.B \-ll
+Show ("list") the current multipath topology from all available information (sysfs, the
+device mapper, path checkers ...).
.
.TP
-.B \-i
-Ignore WWIDs file when processing devices. If
-\fIfind_multipaths strict\fR or \fIfind_multipaths no\fR is set in
-\fImultipath.conf\fR, multipath only considers devices that are
-listed in the WWIDs file. This option overrides that behavior. For other values
-of \fIfind_multipaths\fR, this option has no effect. See the description of
-\fIfind_multipaths\fR in
-.BR multipath.conf (5).
-This option should only be used in rare circumstances.
+.B \-a
+Add the WWID for the specified device to the WWIDs file.
.
.TP
-.B \-B
-Treat the bindings file as read only.
+.B \-w
+Remove the WWID for the specified device from the WWIDs file.
.
.TP
-.BI \-b " bindings_file"
-Set user_friendly_names bindings file location. The default is
-\fI/etc/multipath/bindings\fR.
+.B \-W
+Reset the WWIDs file to only include the current multipath devices.
.
.TP
.B \-c
itself doesn't attempt to do I/O on the device.
.
.TP
-.B \-q
-Allow device tables with \fIqueue_if_no_path\fR when multipathd is not running.
-.
-.TP
-.B \-a
-Add the WWID for the specified device to the WWIDs file.
-.
-.TP
.B \-u
Check if the device specified in the program environment should be
a path in a multipath device.
with usable paths. See \fB-C\fB.
.
.TP
-.B \-w
-Remove the WWID for the specified device from the WWIDs file.
+.B \-h
+Print usage text.
.
.TP
-.B \-W
-Reset the WWIDs file to only include the current multipath devices.
+.B \-t
+Display the currently used multipathd configuration.
.
.TP
-.BI \-p " policy"
-Force new maps to use the specified policy:
+.B \-T
+Display the currently used multipathd configuration, limiting the output to
+those devices actually present in the system. This can be used a template for
+creating \fImultipath.conf\fR.
+.
+.\" ----------------------------------------------------------------------------
+.SH OPTIONS
+.\" ----------------------------------------------------------------------------
+.
+.TP
+.BI \-v " level"
+Verbosity of information printed to stdout in default and "list" operation
+modes. The default level is \fI-v 2\fR.
.RS 1.2i
.TP 1.2i
-.I failover
-One path per priority group.
+.I 0
+Nothing is printed.
.TP
-.I multibus
-All paths in one priority group.
+.I 1
+In default mode, Names/WWIDs of created or modified multipath maps are
+printed. In list mode, WWIDs of all multipath maps are printed.
.TP
-.I group_by_serial
-One priority group per serial number.
+.I 2
+In default mode,
+Topology of created or modified multipath maps is printed.
+In list mode, topology of all multipath maps is printed.
.TP
-.I group_by_prio
-One priority group per priority value. Priorities are determined by
-callout programs specified as a global, per-controller or
-per-multipath option in the configuration file.
+.I 3
+All detected paths and the topology of all multipath maps are printed.
+.
+.LP
+.
+The verbosity level also controls the level of log and debug messages printed to
+\fIstderr\fR. The default level corresponds to \fILOG_NOTICE\fR
+(important messages that shouldn't be missed in normal operation).
+.
+.RE
.TP
-.I group_by_node_name
-One priority group per target node name. Target node names are fetched
-in \fI/sys/class/fc_transport/target*/node_name\fR.
+.B \-d
+Dry run, do not create or update devmaps.
+.
.TP
-.RE
-Existing maps are not modified.
+.B \-i
+Ignore WWIDs file when processing devices. If
+\fIfind_multipaths strict\fR or \fIfind_multipaths no\fR is set in
+\fImultipath.conf\fR, multipath only considers devices that are
+listed in the WWIDs file. This option overrides that behavior. For other values
+of \fIfind_multipaths\fR, this option has no effect. See the description of
+\fIfind_multipaths\fR in
+.BR multipath.conf (5).
+This option should only be used in rare circumstances.
.
.TP
-.BI \-R " retries"
-Number of times to retry flushing multipath devices that are in-use. The default
-is \fI0\fR.
+.B \-B
+Treat the bindings file as read only.
.
.TP
-.BI device
-Update only the devmap specified by
-.IR device ,
-which is either:
-.RS 1.2i
-.IP \[bu]
-A devmap name.
-.IP \[bu]
-A path associated with the desired devmap; the path may be in one of the following formats:
-.RS 1.2i
-.IP \[bu]
-.B /dev/sdX
-.IP \[bu]
-.B major:minor
+.BI \-b " file"
+Set \fIuser_friendly_names\fR bindings file location. The default is
+\fI/etc/multipath/bindings\fR.
.
+.TP
+.B \-q
+Don't unset the device mapper feature \fIqueue_if_no_path\fR for multipath
+maps. Normally, \fBmultipath\fR would do so if \fBmultipathd\fR is not
+running, because only a running multipath daemon guarantees that unusable
+paths are reinstated when they become usable again.
+.
+.TP
+.BI \-p " policy"
+Force new maps to use the specified policy, overriding the configuration in
+\fBmultipath.conf(5)\fR. The possible values for
+\fIpolicy\fR are the same as the values for \fIpath_grouping_policy\fR in
+\fBmultipath.conf(5)\fR. Existing maps are not modified.
+.
+.TP
+.B \-r
+Force a reload of all existing multipath maps. This command is delegated to
+the multipathd daemon if it's running. In this case, other command line
+switches of the \fImultipath\fR command have no effect.
+.
+.TP
+.BI \-R " retries"
+Number of times to retry flushing multipath devices that are in use. The default
+is \fI0\fR.
.
.\" ----------------------------------------------------------------------------
.SH "SEE ALSO"
# Code below here is only run in "smart" mode.
# multipath -u has indicated this is "maybe" multipath.
+# Note that DM_MULTIPATH_DEVICE_PATH has the value 2 at this point.
+# This value will never propagate to other rules files, because
+# it will be reset to 1 in the "pretend_multipath" section below.
+
# This shouldn't happen, just in case.
ENV{FIND_MULTIPATHS_WAIT_UNTIL}!="?*", GOTO="end_mpath"
udevice = udev_device_new_from_subsystem_sysname(udev,
"block",
param);
+ if (!udevice) {
+ condlog(0, "%s: can't find path", param);
+ return 1;
+ }
conf = get_multipath_config();
pthread_cleanup_push(put_multipath_config, conf);
r = store_pathinfo(vecs->pathvec, conf,
condlog(0, "%s: failed to store path info", param);
return 1;
}
- pp->checkint = conf->checkint;
}
return ev_add_path(pp, vecs, 1);
blacklisted:
int
cli_reconfigure(void * v, char ** reply, int * len, void * data)
{
+ int rc;
+
condlog(2, "reconfigure (operator)");
- if (set_config_state(DAEMON_CONFIGURE) == ETIMEDOUT) {
+ rc = set_config_state(DAEMON_CONFIGURE);
+ if (rc == ETIMEDOUT) {
condlog(2, "timeout starting reconfiguration");
return 1;
- }
+ } else if (rc == EINVAL)
+ /* daemon shutting down */
+ return 1;
return 0;
}
#include <sys/types.h>
#include <fcntl.h>
#include <errno.h>
-#include <sys/time.h>
-#include <sys/resource.h>
#include <limits.h>
#include <linux/oom.h>
#include <libudev.h>
#include "pgpolicies.h"
#include "uevent.h"
#include "log.h"
+#include "uxsock.h"
#include "mpath_cmd.h"
#include "mpath_persist.h"
#define FILE_NAME_SIZE 256
#define CMDSIZE 160
-#define LOG_MSG(a, b) \
-do { \
- if (pp->offline) \
- condlog(a, "%s: %s - path offline", pp->mpp->alias, pp->dev); \
- else if (strlen(b)) \
- condlog(a, "%s: %s - %s", pp->mpp->alias, pp->dev, b); \
+#define LOG_MSG(lvl, verb, pp) \
+do { \
+ if (lvl <= verb) { \
+ if (pp->offline) \
+ condlog(lvl, "%s: %s - path offline", \
+ pp->mpp->alias, pp->dev); \
+ else { \
+ const char *__m = \
+ checker_message(&pp->checker); \
+ \
+ if (strlen(__m)) \
+ condlog(lvl, "%s: %s - %s checker%s", \
+ pp->mpp->alias, \
+ pp->dev, \
+ checker_name(&pp->checker), \
+ __m); \
+ } \
+ } \
} while(0)
struct mpath_event_param
pthread_mutex_unlock(&config_lock);
}
-void post_config_state(enum daemon_status state)
+static void __post_config_state(enum daemon_status state)
{
- pthread_mutex_lock(&config_lock);
- if (state != running_state) {
+ if (state != running_state && running_state != DAEMON_SHUTDOWN) {
enum daemon_status old_state = running_state;
running_state = state;
do_sd_notify(old_state);
#endif
}
- pthread_mutex_unlock(&config_lock);
+}
+
+void post_config_state(enum daemon_status state)
+{
+ pthread_mutex_lock(&config_lock);
+ pthread_cleanup_push(config_cleanup, NULL);
+ __post_config_state(state);
+ pthread_cleanup_pop(1);
}
int set_config_state(enum daemon_status state)
if (running_state != state) {
enum daemon_status old_state = running_state;
- if (running_state != DAEMON_IDLE) {
+ if (running_state == DAEMON_SHUTDOWN)
+ rc = EINVAL;
+ else if (running_state != DAEMON_IDLE) {
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
goto out;
}
if (strcmp(mpp->alias, alias)) {
- condlog(2, "%s: minor number mismatch (map %d, event %d)",
- mpp->alias, mpp->dmi->minor, minor);
+ condlog(2, "%s: map alias mismatch: have \"%s\", got \"%s\")",
+ uev->kernel, mpp->alias, alias);
goto out;
}
static void *
uxlsnrloop (void * ap)
{
+ long ux_sock;
+
+ pthread_cleanup_push(rcu_unregister, NULL);
+ rcu_register_thread();
+
+ ux_sock = ux_socket_listen(DEFAULT_SOCKET);
+ if (ux_sock == -1) {
+ condlog(1, "could not create uxsock: %d", errno);
+ exit_daemon();
+ goto out;
+ }
+ pthread_cleanup_push(uxsock_cleanup, (void *)ux_sock);
+
if (cli_init()) {
condlog(1, "Failed to init uxsock listener");
- return NULL;
+ exit_daemon();
+ goto out_sock;
}
- pthread_cleanup_push(rcu_unregister, NULL);
- rcu_register_thread();
+
+ /* Tell main thread that thread has started */
+ post_config_state(DAEMON_CONFIGURE);
+
set_handler_callback(LIST+PATHS, cli_list_paths);
set_handler_callback(LIST+PATHS+FMT, cli_list_paths_fmt);
set_handler_callback(LIST+PATHS+RAW+FMT, cli_list_paths_raw);
set_handler_callback(UNSETPRKEY+MAP, cli_unsetprkey);
umask(077);
- uxsock_listen(&uxsock_trigger, ap);
- pthread_cleanup_pop(1);
+ uxsock_listen(&uxsock_trigger, ux_sock, ap);
+
+out_sock:
+ pthread_cleanup_pop(1); /* uxsock_cleanup */
+out:
+ pthread_cleanup_pop(1); /* rcu_unregister */
return NULL;
}
int add_active;
int disable_reinstate = 0;
int oldchkrstate = pp->chkrstate;
- int retrigger_tries, checkint;
+ int retrigger_tries, checkint, max_checkint, verbosity;
struct config *conf;
int ret;
conf = get_multipath_config();
retrigger_tries = conf->retrigger_tries;
checkint = conf->checkint;
+ max_checkint = conf->max_checkint;
+ verbosity = conf->verbosity;
put_multipath_config(conf);
- if (!pp->mpp && pp->initialized == INIT_MISSING_UDEV &&
- pp->retriggers < retrigger_tries) {
- condlog(2, "%s: triggering change event to reinitialize",
- pp->dev);
- pp->initialized = INIT_REQUESTED_UDEV;
- pp->retriggers++;
- sysfs_attr_set_value(pp->udev, "uevent", "change",
- strlen("change"));
- return 0;
+
+ if (pp->checkint == CHECKINT_UNDEF) {
+ condlog(0, "%s: BUG: checkint is not set", pp->dev);
+ pp->checkint = checkint;
+ };
+
+ if (!pp->mpp && pp->initialized == INIT_MISSING_UDEV) {
+ if (pp->retriggers < retrigger_tries) {
+ condlog(2, "%s: triggering change event to reinitialize",
+ pp->dev);
+ pp->initialized = INIT_REQUESTED_UDEV;
+ pp->retriggers++;
+ sysfs_attr_set_value(pp->udev, "uevent", "change",
+ strlen("change"));
+ return 0;
+ } else {
+ condlog(1, "%s: not initialized after %d udev retriggers",
+ pp->dev, retrigger_tries);
+ /*
+ * Make sure that the "add missing path" code path
+ * below may reinstate the path later, if it ever
+ * comes up again.
+ * The WWID needs not be cleared; if it was set, the
+ * state hadn't been INIT_MISSING_UDEV in the first
+ * place.
+ */
+ pp->initialized = INIT_FAILED;
+ return 0;
+ }
}
/*
}
if (newstate == PATH_WILD || newstate == PATH_UNCHECKED) {
- condlog(2, "%s: unusable path", pp->dev);
+ condlog(2, "%s: unusable path - checker failed", pp->dev);
+ LOG_MSG(2, verbosity, pp);
conf = get_multipath_config();
pthread_cleanup_push(put_multipath_config, conf);
pathinfo(pp, conf, 0);
return 1;
}
if (!pp->mpp) {
- if (!strlen(pp->wwid) && pp->initialized != INIT_MISSING_UDEV &&
+ if (!strlen(pp->wwid) && pp->initialized == INIT_FAILED &&
(newstate == PATH_UP || newstate == PATH_GHOST)) {
condlog(2, "%s: add missing path", pp->dev);
conf = get_multipath_config();
pthread_cleanup_push(put_multipath_config, conf);
ret = pathinfo(pp, conf, DI_ALL | DI_BLACKLIST);
pthread_cleanup_pop(1);
- if (ret == PATHINFO_OK) {
+ /* INIT_OK implies ret == PATHINFO_OK */
+ if (pp->initialized == INIT_OK) {
ev_add_path(pp, vecs, 1);
pp->tick = 1;
- } else if (ret == PATHINFO_SKIPPED)
- return -1;
+ } else {
+ /*
+ * We failed multiple times to initialize this
+ * path properly. Don't re-check too often.
+ */
+ pp->checkint = max_checkint;
+ if (ret == PATHINFO_SKIPPED)
+ return -1;
+ }
}
return 0;
}
int oldstate = pp->state;
pp->state = newstate;
- LOG_MSG(1, checker_message(&pp->checker));
+ LOG_MSG(1, verbosity, pp);
/*
* upon state change, reset the checkint
pp->wait_checks = pp->mpp->delay_wait_checks;
pp->watch_checks = 0;
}
- }else
+ } else {
fail_path(pp, 0);
+ if (pp->wait_checks > 0)
+ pp->wait_checks =
+ pp->mpp->delay_wait_checks;
+ }
/*
* cancel scheduled failback
return 0;
}
} else {
- unsigned int max_checkint;
- LOG_MSG(4, checker_message(&pp->checker));
- conf = get_multipath_config();
- max_checkint = conf->max_checkint;
- put_multipath_config(conf);
+ LOG_MSG(4, verbosity, pp);
if (pp->checkint != max_checkint) {
/*
* double the next check delay.
log_checker_err = conf->log_checker_err;
put_multipath_config(conf);
if (log_checker_err == LOG_CHKR_ERR_ONCE)
- LOG_MSG(3, checker_message(&pp->checker));
+ LOG_MSG(3, verbosity, pp);
else
- LOG_MSG(2, checker_message(&pp->checker));
+ LOG_MSG(2, verbosity, pp);
}
}
return 1;
}
-static void init_path_check_interval(struct vectors *vecs)
-{
- struct config *conf;
- struct path *pp;
- unsigned int i;
-
- vector_foreach_slot (vecs->pathvec, pp, i) {
- conf = get_multipath_config();
- pp->checkint = conf->checkint;
- put_multipath_config(conf);
- }
-}
-
static void *
checkerloop (void *ap)
{
if (rc == ETIMEDOUT) {
condlog(4, "timeout waiting for DAEMON_IDLE");
continue;
- }
+ } else if (rc == EINVAL)
+ /* daemon shutdown */
+ break;
pthread_cleanup_push(cleanup_lock, &vecs->lock);
lock(&vecs->lock);
goto fail;
}
+ conf = get_multipath_config();
+ pthread_cleanup_push(put_multipath_config, conf);
vector_foreach_slot (vecs->pathvec, pp, i){
- conf = get_multipath_config();
- pthread_cleanup_push(put_multipath_config, conf);
if (filter_path(conf, pp) > 0){
vector_del_slot(vecs->pathvec, i);
free_path(pp);
i--;
}
- else
- pp->checkint = conf->checkint;
- pthread_cleanup_pop(1);
}
+ pthread_cleanup_pop(1);
+
if (map_discovery(vecs)) {
condlog(0, "configure failed at map discovery");
goto fail;
envp = getenv("LimitNOFILE");
- if (envp) {
+ if (envp)
condlog(2,"Using systemd provided open fds limit of %s", envp);
- } else if (conf->max_fds) {
- struct rlimit fd_limit;
-
- if (getrlimit(RLIMIT_NOFILE, &fd_limit) < 0) {
- condlog(0, "can't get open fds limit: %s",
- strerror(errno));
- fd_limit.rlim_cur = 0;
- fd_limit.rlim_max = 0;
- }
- if (fd_limit.rlim_cur < conf->max_fds) {
- fd_limit.rlim_cur = conf->max_fds;
- if (fd_limit.rlim_max < conf->max_fds)
- fd_limit.rlim_max = conf->max_fds;
- if (setrlimit(RLIMIT_NOFILE, &fd_limit) < 0) {
- condlog(0, "can't set open fds limit to "
- "%lu/%lu : %s",
- fd_limit.rlim_cur, fd_limit.rlim_max,
- strerror(errno));
- } else {
- condlog(3, "set open fds limit to %lu/%lu",
- fd_limit.rlim_cur, fd_limit.rlim_max);
- }
- }
-
- }
+ else
+ set_max_fds(conf->max_fds);
vecs = gvecs = init_vecs();
if (!vecs)
*/
conf = NULL;
- /*
- * Signal start of configuration
- */
- post_config_state(DAEMON_CONFIGURE);
+ pthread_cleanup_push(config_cleanup, NULL);
+ pthread_mutex_lock(&config_lock);
- init_path_check_interval(vecs);
+ __post_config_state(DAEMON_IDLE);
+ rc = pthread_create(&uxlsnr_thr, &misc_attr, uxlsnrloop, vecs);
+ if (!rc) {
+ /* Wait for uxlsnr startup */
+ while (running_state == DAEMON_IDLE)
+ pthread_cond_wait(&config_cond, &config_lock);
+ }
+ pthread_cleanup_pop(1);
+
+ if (rc) {
+ condlog(0, "failed to create cli listener: %d", rc);
+ goto failed;
+ }
+ else if (running_state != DAEMON_CONFIGURE) {
+ condlog(0, "cli listener failed to start");
+ goto failed;
+ }
if (poll_dmevents) {
if (init_dmevent_waiter(vecs)) {
goto failed;
}
pthread_attr_destroy(&uevent_attr);
- if ((rc = pthread_create(&uxlsnr_thr, &misc_attr, uxlsnrloop, vecs))) {
- condlog(0, "failed to create cli listener: %d", rc);
- goto failed;
- }
/*
* start threads
logsink = -1;
break;
case 'k':
+ logsink = 0;
conf = load_config(DEFAULT_CONFIGFILE);
if (!conf)
exit(1);
if (verbosity)
conf->verbosity = verbosity;
uxsock_timeout = conf->uxsock_timeout;
- uxclnt(optarg, uxsock_timeout + 100);
+ err = uxclnt(optarg, uxsock_timeout + 100);
free_config(conf);
- exit(0);
+ return err;
case 'B':
bindings_read_only = 1;
break;
char * s = cmd;
char * c = s;
+ logsink = 0;
conf = load_config(DEFAULT_CONFIGFILE);
if (!conf)
exit(1);
optind++;
}
c += snprintf(c, s + CMDSIZE - c, "\n");
- uxclnt(s, uxsock_timeout + 100);
+ err = uxclnt(s, uxsock_timeout + 100);
free_config(conf);
- exit(0);
+ return err;
}
if (foreground) {
}
}
-static void process_req(int fd, char * inbuf, unsigned int timeout)
+static int process_req(int fd, char * inbuf, unsigned int timeout)
{
char *reply;
int ret;
if (send_packet(fd, inbuf) != 0) {
printf("cannot send packet\n");
- return;
+ return 1;
}
ret = recv_packet(fd, &reply, timeout);
if (ret < 0) {
printf("timeout receiving packet\n");
else
printf("error %d receiving packet\n", ret);
+ return 1;
} else {
printf("%s", reply);
+ ret = (strcmp(reply, "fail\n") == 0);
FREE(reply);
+ return ret;
}
}
*/
int uxclnt(char * inbuf, unsigned int timeout)
{
- int fd;
+ int fd, ret = 0;
fd = mpath_connect();
if (fd == -1)
exit(1);
if (inbuf)
- process_req(fd, inbuf, timeout);
+ ret = process_req(fd, inbuf, timeout);
else
process(fd, timeout);
mpath_disconnect(fd);
- return 0;
+ return ret;
}
/*
* entry point
*/
-void * uxsock_listen(uxsock_trigger_fn uxsock_trigger, void * trigger_data)
+void * uxsock_listen(uxsock_trigger_fn uxsock_trigger, long ux_sock,
+ void * trigger_data)
{
- long ux_sock;
int rlen;
char *inbuf;
char *reply;
sigset_t mask;
int old_clients = MIN_POLLS;
- ux_sock = ux_socket_listen(DEFAULT_SOCKET);
-
- if (ux_sock == -1) {
- condlog(1, "could not create uxsock: %d", errno);
- exit_daemon();
- }
-
- pthread_cleanup_push(uxsock_cleanup, (void *)ux_sock);
-
condlog(3, "uxsock: startup listener");
polls = (struct pollfd *)MALLOC((MIN_POLLS + 1) * sizeof(struct pollfd));
if (!polls) {
}
}
- pthread_cleanup_pop(1);
return NULL;
}
typedef int (uxsock_trigger_fn)(char *, char **, int *, bool, void *);
-void * uxsock_listen(uxsock_trigger_fn uxsock_trigger,
- void * trigger_data);
+void uxsock_cleanup(void *arg);
+void *uxsock_listen(uxsock_trigger_fn uxsock_trigger, long ux_sock,
+ void * trigger_data);
#endif
skip();
remove_all_dm_device_events();
- assert_int_equal(add_dm_device_event("foo", 1, 5), 0);
+ assert_int_equal(add_dm_device_event("foo", 1, 5), 0);
will_return(__wrap_dm_geteventnr, 0);
assert_int_equal(watch_dmevents("foo"), 0);
dev_evt = find_dmevents("foo");