struct udev_device *rport_dev = NULL;
char value[11];
char rport_id[32];
+ unsigned long long tmo;
sprintf(rport_id, "rport-%d:%d-%d",
pp->sg_id.host_no, pp->sg_id.channel, pp->sg_id.transport_id);
condlog(4, "target%d:%d:%d -> %s", pp->sg_id.host_no,
pp->sg_id.channel, pp->sg_id.scsi_id, rport_id);
- snprintf(value, 11, "%u", mpp->dev_loss);
- if (mpp->dev_loss &&
- sysfs_attr_set_value(rport_dev, "dev_loss_tmo", value, 11) <= 0) {
- if ((mpp->fast_io_fail == MP_FAST_IO_FAIL_UNSET ||
- mpp->fast_io_fail == MP_FAST_IO_FAIL_OFF)
- && mpp->dev_loss > 600) {
- condlog(3, "%s: limiting dev_loss_tmo to 600, since "
- "fast_io_fail is not set", mpp->alias);
- snprintf(value, 11, "%u", 600);
- if (sysfs_attr_set_value(rport_dev, "dev_loss_tmo",
- value, 11) <= 0)
- condlog(0, "%s failed to set dev_loss_tmo",
- mpp->alias);
+ /*
+ * This is tricky.
+ * dev_loss_tmo will be limited to 600 if fast_io_fail
+ * is _not_ set.
+ * fast_io_fail will be limited by the current dev_loss_tmo
+ * setting.
+ * So to get everything right we first need to increase
+ * dev_loss_tmo to the fast_io_fail setting (if present),
+ * then set fast_io_fail, and _then_ set dev_loss_tmo
+ * to the correct value.
+ */
+ value[0] = '\0';
+ if (mpp->fast_io_fail != MP_FAST_IO_FAIL_UNSET &&
+ mpp->fast_io_fail != MP_FAST_IO_FAIL_OFF) {
+ /* Check if we need to temporarily increase dev_loss_tmo */
+ if (sysfs_attr_get_value(rport_dev, "dev_loss_tmo",
+ value, 16) <= 0) {
+ condlog(0, "%s: failed to read dev_loss_tmo value, "
+ "error %d", pp->dev, errno);
+ goto out;
+ }
+ if (sscanf(value, "%llu\n", &tmo) != 1) {
+ condlog(0, "%s: Cannot parse dev_loss_tmo "
+ "attribute '%s'",pp->dev, value);
goto out;
}
+ if (mpp->fast_io_fail >= tmo) {
+ snprintf(value, 11, "%u", mpp->fast_io_fail);
+ } else {
+ tmo = 0;
+ }
+ } else if (mpp->dev_loss > 600) {
+ condlog(3, "%s: limiting dev_loss_tmo to 600, since "
+ "fast_io_fail is not set", pp->dev);
+ snprintf(value, 11, "%u", 600);
+ } else {
+ snprintf(value, 11, "%u", mpp->dev_loss);
}
- if (mpp->fast_io_fail != MP_FAST_IO_FAIL_UNSET){
+ if (strlen(value) &&
+ sysfs_attr_set_value(rport_dev, "dev_loss_tmo", value, 11) <= 0) {
+ condlog(0, "%s failed to set dev_loss_tmo",
+ mpp->alias);
+ goto out;
+ }
+ if (mpp->fast_io_fail != MP_FAST_IO_FAIL_UNSET) {
if (mpp->fast_io_fail == MP_FAST_IO_FAIL_OFF)
sprintf(value, "off");
else if (mpp->fast_io_fail == MP_FAST_IO_FAIL_ZERO)
mpp->alias);
}
}
+ if (tmo > 0) {
+ snprintf(value, 11, "%u", mpp->dev_loss);
+ if (sysfs_attr_set_value(rport_dev, "dev_loss_tmo",
+ value, 11) <= 0)
+ condlog(0, "%s failed to set dev_loss_tmo",
+ mpp->alias);
+ }
out:
udev_device_unref(rport_dev);
}
} else {
snprintf(value, 11, "%u", mpp->fast_io_fail);
if (sysfs_attr_set_value(session_dev, "recovery_tmo",
- value, 11)) {
+ value, 11) <= 0) {
condlog(3, "%s: Failed to set recovery_tmo, "
" error %d", pp->dev, errno);
}
#include "debug.h"
#include "devmapper.h"
+/*
+ * When we modify an attribute value we cannot rely on libudev for now,
+ * as libudev lacks the capability to update an attribute value.
+ * So for modified attributes we need to implement our own function.
+ */
+ssize_t sysfs_attr_get_value(struct udev_device *dev, const char *attr_name,
+ char * value, size_t value_len)
+{
+ char devpath[PATH_SIZE];
+ struct stat statbuf;
+ int fd;
+ ssize_t size = -1;
+
+ if (!dev || !attr_name || !value)
+ return 0;
+
+ snprintf(devpath, PATH_SIZE, "%s/%s", udev_device_get_syspath(dev),
+ attr_name);
+ condlog(4, "open '%s'", devpath);
+ if (stat(devpath, &statbuf) != 0) {
+ condlog(4, "stat '%s' failed: %s", devpath, strerror(errno));
+ return 0;
+ }
+
+ /* skip directories */
+ if (S_ISDIR(statbuf.st_mode)) {
+ condlog(4, "%s is a directory", devpath);
+ return 0;
+ }
+
+ /* skip non-writeable files */
+ if ((statbuf.st_mode & S_IRUSR) == 0) {
+ condlog(4, "%s is not readable", devpath);
+ return 0;
+ }
+
+ /* read attribute value */
+ fd = open(devpath, O_RDONLY);
+ if (fd < 0) {
+ condlog(4, "attribute '%s' can not be opened: %s",
+ devpath, strerror(errno));
+ return 0;
+ }
+ size = read(fd, value, value_len);
+ if (size < 0) {
+ condlog(4, "read from %s failed: %s", devpath, strerror(errno));
+ size = 0;
+ } else if (size == value_len) {
+ condlog(4, "overflow while reading from %s", devpath);
+ size = 0;
+ }
+
+ close(fd);
+ return size;
+}
+
ssize_t sysfs_attr_set_value(struct udev_device *dev, const char *attr_name,
char * value, size_t value_len)
{
}
/* skip directories */
- if (S_ISDIR(statbuf.st_mode))
+ if (S_ISDIR(statbuf.st_mode)) {
+ condlog(4, "%s is a directory", devpath);
return 0;
+ }
/* skip non-writeable files */
- if ((statbuf.st_mode & S_IWUSR) == 0)
+ if ((statbuf.st_mode & S_IWUSR) == 0) {
+ condlog(4, "%s is not writeable", devpath);
return 0;
+ }
/* write attribute value */
fd = open(devpath, O_WRONLY);