Hi Christophe, I've implemented yet another path checker, based on O_DIRECT. Rationale: On S/390 it's possible to activate multipath support for DASD devices (called Parallel Access Volume (PAV)). As these are no SCSI devices the readsector0 path checker wouldn't work. And as this checker is quite generic it'll work for just about anybody. Cheers, Hannes -- Dr. Hannes Reinecke hare@xxxxxxx SuSE Linux Products GmbH S390 & zSeries Maxfeldstraße 5 +49 911 74053 688 90409 Nürnberg http://www.suse.de
diff -purN multipath-tools-0.4.4.orig/libcheckers/checkers.h multipath-tools-0.4.4/libcheckers/checkers.h --- multipath-tools-0.4.4.orig/libcheckers/checkers.h 2005-03-29 16:21:40.000000000 +0200 +++ multipath-tools-0.4.4/libcheckers/checkers.h 2005-08-04 15:20:52.000000000 +0200 @@ -9,6 +9,7 @@ enum checkers { CHECKER_RESERVED, TUR, READSECTOR0, + DIRECTIO, EMC_CLARIION, HP_SW }; @@ -20,6 +21,7 @@ void *get_checker_addr (int); int get_checker_name (char *, int); int emc_clariion (int fd, char * msg, void ** ctxt); +int directio (int fd, char * msg, void ** ctxt); int readsector0 (int fd, char * msg, void ** ctxt); int tur (int fd, char * msg, void ** ctxt); diff -purN multipath-tools-0.4.4.orig/libcheckers/directio.c multipath-tools-0.4.4/libcheckers/directio.c --- multipath-tools-0.4.4.orig/libcheckers/directio.c 1970-01-01 01:00:00.000000000 +0100 +++ multipath-tools-0.4.4/libcheckers/directio.c 2005-08-04 15:19:10.000000000 +0200 @@ -0,0 +1,165 @@ +#define _GNU_SOURCE +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <linux/fs.h> +#include <errno.h> + +#include "path_state.h" +#include "checkers.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" + +struct readsector0_checker_context { + void * dummy; +}; + +static int +direct_read (int fd, unsigned char * buff, int size) +{ + long flags; + int reset_flags = 0; + int res, retval; + + flags = fcntl(fd,F_GETFL); + + if (flags < 0) { + return PATH_UNCHECKED; + } + + if (!(flags & O_DIRECT)) { + flags |= O_DIRECT; + if (fcntl(fd,F_SETFL,flags) < 0) { + return PATH_UNCHECKED; + } + reset_flags = 1; + } + + while ( (res = read(fd,buff,size)) < 0 && errno == EINTR ); + if (res < 0) { + if (errno == EINVAL) { + /* O_DIRECT is not available */ + retval = PATH_UNCHECKED; + } else if (errno == ENOMEM) { + retval = PATH_UP; + } else { + retval = PATH_DOWN; + } + } else { + retval = PATH_UP; + } + + if (reset_flags) { + flags &= ~O_DIRECT; + /* No point in checking for errors */ + fcntl(fd,F_SETFL,flags); + } + + return retval; +} + +extern int +directio (int fd, char *msg, void **context) +{ + unsigned char *buf, *ptr; + struct readsector0_checker_context * ctxt = NULL; + unsigned long pgsize, numsect; + int ret, blksize; + + pgsize = getpagesize(); + + /* + * caller passed in a context : use its address + */ + if (context) + ctxt = (struct readsector0_checker_context *) (*context); + + /* + * passed in context is uninitialized or volatile context : + * initialize it + */ + if (!ctxt) { + ctxt = malloc(sizeof(struct readsector0_checker_context)); + memset(ctxt, 0, sizeof(struct readsector0_checker_context)); + + if (!ctxt) { + MSG("cannot allocate context"); + return -1; + } + if (context) + *context = ctxt; + } + if (fd <= 0) { + MSG("no usable fd"); + ret = -1; + goto out; + } + + if (ioctl(fd, BLKGETSIZE, &numsect) < 0) { + MSG("cannot get number of sectors, set default"); + numsect = 0; + } + + if (ioctl(fd, BLKBSZGET, &blksize) < 0) { + MSG("cannot get blocksize, set default"); + blksize = 512; + } + + if (blksize > 4096) { + /* + * Sanity check for DASD; BSZGET is broken + */ + blksize = 4096; + } + + if (!blksize) { + /* + * Blocksize is 0, assume we can't write + * to this device. + */ + MSG(MSG_DIRECTIO_DOWN); + ret = PATH_DOWN; + goto out; + } + + buf = (unsigned char *)malloc(blksize + pgsize); + if (!buf){ + goto out; + } + ptr = (char *)(((unsigned long)buf + pgsize - 1) & + (~(pgsize - 1))); + ret = direct_read(fd, ptr, blksize); + + switch (ret) + { + case PATH_UNCHECKED: + MSG(MSG_DIRECTIO_UNKNOWN); + break; + case PATH_DOWN: + MSG(MSG_DIRECTIO_DOWN); + break; + case PATH_UP: + MSG(MSG_DIRECTIO_UP); + break; + default: + break; + } + free(buf); + +out: + /* + * caller told us he doesn't want to keep the context : + * free it + */ + if (!context) + free(ctxt); + + return ret; +} diff -purN multipath-tools-0.4.4.orig/libcheckers/Makefile multipath-tools-0.4.4/libcheckers/Makefile --- multipath-tools-0.4.4.orig/libcheckers/Makefile 2005-04-04 12:09:58.000000000 +0200 +++ multipath-tools-0.4.4/libcheckers/Makefile 2005-08-04 15:19:20.000000000 +0200 @@ -6,7 +6,7 @@ BUILD = glibc include ../Makefile.inc -OBJS = readsector0.o tur.o selector.o emc_clariion.o hp_sw.o +OBJS = readsector0.o tur.o selector.o directio.o emc_clariion.o hp_sw.o all: $(BUILD) diff -purN multipath-tools-0.4.4.orig/libcheckers/selector.c multipath-tools-0.4.4/libcheckers/selector.c --- multipath-tools-0.4.4.orig/libcheckers/selector.c 2005-03-29 16:27:46.000000000 +0200 +++ multipath-tools-0.4.4/libcheckers/selector.c 2005-08-04 15:20:27.000000000 +0200 @@ -10,6 +10,8 @@ get_checker_id (char * str) return TUR; if (0 == strncmp(str, "readsector0", 11)) return READSECTOR0; + if (0 == strncmp(str, "directio", 8)) + return DIRECTIO; if (0 == strncmp(str, "emc_clariion", 12)) return EMC_CLARIION; return -1; @@ -27,6 +29,9 @@ get_checker_addr (int id) case READSECTOR0: checker = &readsector0; break; + case DIRECTIO: + checker = &directio; + break; case EMC_CLARIION: checker = &emc_clariion; break; @@ -49,6 +54,9 @@ get_checker_name (char * str, int id) case READSECTOR0: s = "readsector0"; break; + case DIRECTIO: + s = "directio"; + break; case EMC_CLARIION: s = "emc_clariion"; break;