Skip to content

Commit

Permalink
[SCSI] scsi_dh_alua: Handle all states correctly
Browse files Browse the repository at this point in the history
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: Hannes Reinecke <[email protected]>
Acked-by: Mike Snitzer <[email protected]>
Signed-off-by: James Bottomley <[email protected]>
  • Loading branch information
hreinecke authored and James Bottomley committed Oct 7, 2010
1 parent e27d616 commit 69723d1
Showing 1 changed file with 38 additions and 27 deletions.
65 changes: 38 additions & 27 deletions drivers/scsi/device_handler/scsi_dh_alua.c
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
* 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
Expand All @@ -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

Expand All @@ -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

Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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);

Expand All @@ -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",
Expand Down Expand Up @@ -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:
switch (h->state) {
case TPGS_STATE_TRANSITIONING:
if (time_before(jiffies, expiry)) {
/* State transition, retry */
interval *= 10;
msleep(interval);
goto retry;
break;
case TPGS_STATE_OFFLINE:
/* Path is offline, fail */
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;
/* Transitioning time exceeded, set port to standby */
err = SCSI_DH_RETRY;
h->state = TPGS_STATE_STANDBY;
break;
case TPGS_STATE_OFFLINE:
case TPGS_STATE_UNAVAILABLE:
/* Path unuseable for unavailable/offline */
err = SCSI_DH_DEV_OFFLINED;
break;
default:
/* Useable path if active */
err = SCSI_DH_OK;
break;
}
return err;
}
Expand Down Expand Up @@ -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);
Expand All @@ -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;
}
Expand Down

0 comments on commit 69723d1

Please sign in to comment.