Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 69723d17 authored by Hannes Reinecke's avatar Hannes Reinecke Committed by James Bottomley
Browse files

[SCSI] scsi_dh_alua: Handle all states correctly



For ALUA we should be handling all states, independent of whether
the mode is explicit or implicit. For 'Transitioning' we should retry
for a certain amount of time; after that we're setting the port
to 'Standby' and return SCSI_DH_RETRY to signal upper layers
a retry is in order here.

Signed-off-by: default avatarHannes Reinecke <hare@suse.de>
Acked-by: default avatarMike Snitzer <snitzer@redhat.com>
Signed-off-by: default avatarJames Bottomley <James.Bottomley@suse.de>
parent e27d6169
Loading
Loading
Loading
Loading
+38 −27
Original line number Diff line number Diff line
/*
 * Generic SCSI-3 ALUA SCSI Device Handler
 *
 * Copyright (C) 2007, 2008 Hannes Reinecke, SUSE Linux Products GmbH.
 * Copyright (C) 2007-2010 Hannes Reinecke, SUSE Linux Products GmbH.
 * All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
@@ -20,17 +20,19 @@
 *
 */
#include <linux/slab.h>
#include <linux/delay.h>
#include <scsi/scsi.h>
#include <scsi/scsi_eh.h>
#include <scsi/scsi_dh.h>

#define ALUA_DH_NAME "alua"
#define ALUA_DH_VER "1.2"
#define ALUA_DH_VER "1.3"

#define TPGS_STATE_OPTIMIZED		0x0
#define TPGS_STATE_NONOPTIMIZED		0x1
#define TPGS_STATE_STANDBY		0x2
#define TPGS_STATE_UNAVAILABLE		0x3
#define TPGS_STATE_LBA_DEPENDENT	0x4
#define TPGS_STATE_OFFLINE		0xe
#define TPGS_STATE_TRANSITIONING	0xf

@@ -39,6 +41,7 @@
#define TPGS_SUPPORT_NONOPTIMIZED	0x02
#define TPGS_SUPPORT_STANDBY		0x04
#define TPGS_SUPPORT_UNAVAILABLE	0x08
#define TPGS_SUPPORT_LBA_DEPENDENT	0x10
#define TPGS_SUPPORT_OFFLINE		0x40
#define TPGS_SUPPORT_TRANSITION		0x80

@@ -460,6 +463,8 @@ static char print_alua_state(int state)
		return 'S';
	case TPGS_STATE_UNAVAILABLE:
		return 'U';
	case TPGS_STATE_LBA_DEPENDENT:
		return 'L';
	case TPGS_STATE_OFFLINE:
		return 'O';
	case TPGS_STATE_TRANSITIONING:
@@ -542,7 +547,9 @@ static int alua_rtpg(struct scsi_device *sdev, struct alua_dh_data *h)
	int len, k, off, valid_states = 0;
	char *ucp;
	unsigned err;
	unsigned long expiry, interval = 10;

	expiry = round_jiffies_up(jiffies + ALUA_FAILOVER_TIMEOUT);
 retry:
	err = submit_rtpg(sdev, h);

@@ -553,7 +560,7 @@ static int alua_rtpg(struct scsi_device *sdev, struct alua_dh_data *h)
			return SCSI_DH_IO;

		err = alua_check_sense(sdev, &sense_hdr);
		if (err == ADD_TO_MLQUEUE)
		if (err == ADD_TO_MLQUEUE && time_before(jiffies, expiry))
			goto retry;
		sdev_printk(KERN_INFO, sdev,
			    "%s: rtpg sense code %02x/%02x/%02x\n",
@@ -587,38 +594,37 @@ static int alua_rtpg(struct scsi_device *sdev, struct alua_dh_data *h)
	}

	sdev_printk(KERN_INFO, sdev,
		    "%s: port group %02x state %c supports %c%c%c%c%c%c\n",
		    "%s: port group %02x state %c supports %c%c%c%c%c%c%c\n",
		    ALUA_DH_NAME, h->group_id, print_alua_state(h->state),
		    valid_states&TPGS_SUPPORT_TRANSITION?'T':'t',
		    valid_states&TPGS_SUPPORT_OFFLINE?'O':'o',
		    valid_states&TPGS_SUPPORT_LBA_DEPENDENT?'L':'l',
		    valid_states&TPGS_SUPPORT_UNAVAILABLE?'U':'u',
		    valid_states&TPGS_SUPPORT_STANDBY?'S':'s',
		    valid_states&TPGS_SUPPORT_NONOPTIMIZED?'N':'n',
		    valid_states&TPGS_SUPPORT_OPTIMIZED?'A':'a');

	if (h->tpgs & TPGS_MODE_EXPLICIT) {
	switch (h->state) {
	case TPGS_STATE_TRANSITIONING:
		if (time_before(jiffies, expiry)) {
			/* State transition, retry */
			interval *= 10;
			msleep(interval);
			goto retry;
		}
		/* Transitioning time exceeded, set port to standby */
		err = SCSI_DH_RETRY;
		h->state = TPGS_STATE_STANDBY;
		break;
	case TPGS_STATE_OFFLINE:
			/* Path is offline, fail */
	case TPGS_STATE_UNAVAILABLE:
		/* Path unuseable for unavailable/offline */
		err = SCSI_DH_DEV_OFFLINED;
		break;
	default:
			break;
		}
	} else {
		/* Only Implicit ALUA support */
		if (h->state == TPGS_STATE_OPTIMIZED ||
		    h->state == TPGS_STATE_NONOPTIMIZED ||
		    h->state == TPGS_STATE_STANDBY)
		/* Useable path if active */
		err = SCSI_DH_OK;
		else
			/* Path unuseable for unavailable/offline */
			err = SCSI_DH_DEV_OFFLINED;
		break;
	}
	return err;
}
@@ -672,7 +678,9 @@ static int alua_activate(struct scsi_device *sdev,
			goto out;
	}

	if (h->tpgs & TPGS_MODE_EXPLICIT && h->state != TPGS_STATE_OPTIMIZED) {
	if (h->tpgs & TPGS_MODE_EXPLICIT &&
	    h->state != TPGS_STATE_OPTIMIZED &&
	    h->state != TPGS_STATE_LBA_DEPENDENT) {
		h->callback_fn = fn;
		h->callback_data = data;
		err = submit_stpg(h);
@@ -698,8 +706,11 @@ static int alua_prep_fn(struct scsi_device *sdev, struct request *req)
	struct alua_dh_data *h = get_alua_data(sdev);
	int ret = BLKPREP_OK;

	if (h->state != TPGS_STATE_OPTIMIZED &&
	    h->state != TPGS_STATE_NONOPTIMIZED) {
	if (h->state == TPGS_STATE_TRANSITIONING)
		ret = BLKPREP_DEFER;
	else if (h->state != TPGS_STATE_OPTIMIZED &&
		 h->state != TPGS_STATE_NONOPTIMIZED &&
		 h->state != TPGS_STATE_LBA_DEPENDENT) {
		ret = BLKPREP_KILL;
		req->cmd_flags |= REQ_QUIET;
	}