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

Commit 2aef6d5c authored by Hannes Reinecke's avatar Hannes Reinecke Committed by James Bottomley
Browse files

[SCSI] scsi_dh: Update hp_sw hardware handler



This patch updates the hp_sw device handler to properly
check the return codes etc.
And adds the 'correct' machine definitions.

Signed-off-by: default avatarHannes Reinecke <hare@suse.de>
Signed-off-by: default avatarChandra Seetharaman <sekharan@us.ibm.com>
Signed-off-by: default avatarJames Bottomley <James.Bottomley@HansenPartnership.com>
parent b6ff1b14
Loading
Loading
Loading
Loading
+234 −38
Original line number Diff line number Diff line
@@ -4,6 +4,7 @@
 *
 * Copyright (C) 2006 Red Hat, Inc.  All rights reserved.
 * Copyright (C) 2006 Mike Christie
 * Copyright (C) 2008 Hannes Reinecke <hare@suse.de>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
@@ -30,8 +31,13 @@
#define HP_SW_TIMEOUT			(60 * HZ)
#define HP_SW_RETRIES			3

#define HP_SW_PATH_UNINITIALIZED	-1
#define HP_SW_PATH_ACTIVE		0
#define HP_SW_PATH_PASSIVE		1

struct hp_sw_dh_data {
	unsigned char sense[SCSI_SENSE_BUFFERSIZE];
	int path_state;
	int retries;
};

@@ -42,51 +48,161 @@ static inline struct hp_sw_dh_data *get_hp_sw_data(struct scsi_device *sdev)
	return ((struct hp_sw_dh_data *) scsi_dh_data->buf);
}

static int hp_sw_done(struct scsi_device *sdev)
/*
 * tur_done - Handle TEST UNIT READY return status
 * @sdev: sdev the command has been sent to
 * @errors: blk error code
 *
 * Returns SCSI_DH_DEV_OFFLINED if the sdev is on the passive path
 */
static int tur_done(struct scsi_device *sdev, unsigned char *sense)
{
	struct hp_sw_dh_data *h = get_hp_sw_data(sdev);
	struct scsi_sense_hdr sshdr;
	int rc;

	sdev_printk(KERN_INFO, sdev, "hp_sw_done\n");
	int ret;

	rc = scsi_normalize_sense(h->sense, SCSI_SENSE_BUFFERSIZE, &sshdr);
	if (!rc)
	ret = scsi_normalize_sense(sense, SCSI_SENSE_BUFFERSIZE, &sshdr);
	if (!ret) {
		sdev_printk(KERN_WARNING, sdev,
			    "%s: sending tur failed, no sense available\n",
			    HP_SW_NAME);
		ret = SCSI_DH_IO;
		goto done;
	}
	switch (sshdr.sense_key) {
	case UNIT_ATTENTION:
		ret = SCSI_DH_IMM_RETRY;
		break;
	case NOT_READY:
		if ((sshdr.asc == 0x04) && (sshdr.ascq == 3)) {
			rc = SCSI_DH_RETRY;
			h->retries++;
		if ((sshdr.asc == 0x04) && (sshdr.ascq == 2)) {
			/*
			 * LUN not ready - Initialization command required
			 *
			 * This is the passive path
			 */
			ret = SCSI_DH_DEV_OFFLINED;
			break;
		}
		/* fall through */
		/* Fallthrough */
	default:
		h->retries++;
		rc = SCSI_DH_IMM_RETRY;
		sdev_printk(KERN_WARNING, sdev,
			   "%s: sending tur failed, sense %x/%x/%x\n",
			   HP_SW_NAME, sshdr.sense_key, sshdr.asc,
			   sshdr.ascq);
		break;
	}

done:
	if (rc == SCSI_DH_OK || rc == SCSI_DH_IO)
		h->retries = 0;
	else if (h->retries > HP_SW_RETRIES) {
		h->retries = 0;
	return ret;
}

/*
 * hp_sw_tur - Send TEST UNIT READY
 * @sdev: sdev command should be sent to
 *
 * Use the TEST UNIT READY command to determine
 * the path state.
 */
static int hp_sw_tur(struct scsi_device *sdev, struct hp_sw_dh_data *h)
{
	struct request *req;
	int ret;

	req = blk_get_request(sdev->request_queue, WRITE, GFP_NOIO);
	if (!req)
		return SCSI_DH_RES_TEMP_UNAVAIL;

	req->cmd_type = REQ_TYPE_BLOCK_PC;
	req->cmd_flags |= REQ_FAILFAST;
	req->cmd_len = COMMAND_SIZE(TEST_UNIT_READY);
	memset(req->cmd, 0, MAX_COMMAND_SIZE);
	req->cmd[0] = TEST_UNIT_READY;
	req->timeout = HP_SW_TIMEOUT;
	req->sense = h->sense;
	memset(req->sense, 0, SCSI_SENSE_BUFFERSIZE);
	req->sense_len = 0;

retry:
	ret = blk_execute_rq(req->q, NULL, req, 1);
	if (ret == -EIO) {
		if (req->sense_len > 0) {
			ret = tur_done(sdev, h->sense);
		} else {
			sdev_printk(KERN_WARNING, sdev,
				    "%s: sending tur failed with %x\n",
				    HP_SW_NAME, req->errors);
			ret = SCSI_DH_IO;
		}
	} else {
		h->path_state = HP_SW_PATH_ACTIVE;
		ret = SCSI_DH_OK;
	}
	if (ret == SCSI_DH_IMM_RETRY)
		goto retry;
	if (ret == SCSI_DH_DEV_OFFLINED) {
		h->path_state = HP_SW_PATH_PASSIVE;
		ret = SCSI_DH_OK;
	}

	blk_put_request(req);

	return ret;
}

/*
 * start_done - Handle START STOP UNIT return status
 * @sdev: sdev the command has been sent to
 * @errors: blk error code
 */
static int start_done(struct scsi_device *sdev, unsigned char *sense)
{
	struct scsi_sense_hdr sshdr;
	int rc;

	rc = scsi_normalize_sense(sense, SCSI_SENSE_BUFFERSIZE, &sshdr);
	if (!rc) {
		sdev_printk(KERN_WARNING, sdev,
			    "%s: sending start_stop_unit failed, "
			    "no sense available\n",
			    HP_SW_NAME);
		return SCSI_DH_IO;
	}
	switch (sshdr.sense_key) {
	case NOT_READY:
		if ((sshdr.asc == 0x04) && (sshdr.ascq == 3)) {
			/*
			 * LUN not ready - manual intervention required
			 *
			 * Switch-over in progress, retry.
			 */
			rc = SCSI_DH_RETRY;
			break;
		}
		/* fall through */
	default:
		sdev_printk(KERN_WARNING, sdev,
			   "%s: sending start_stop_unit failed, sense %x/%x/%x\n",
			   HP_SW_NAME, sshdr.sense_key, sshdr.asc,
			   sshdr.ascq);
		rc = SCSI_DH_IO;
	}

	return rc;
}

static int hp_sw_activate(struct scsi_device *sdev)
/*
 * hp_sw_start_stop - Send START STOP UNIT command
 * @sdev: sdev command should be sent to
 *
 * Sending START STOP UNIT activates the SP.
 */
static int hp_sw_start_stop(struct scsi_device *sdev, struct hp_sw_dh_data *h)
{
	struct hp_sw_dh_data *h = get_hp_sw_data(sdev);
	struct request *req;
	int ret = SCSI_DH_RES_TEMP_UNAVAIL;
	int ret, retry;

	req = blk_get_request(sdev->request_queue, WRITE, GFP_ATOMIC);
	req = blk_get_request(sdev->request_queue, WRITE, GFP_NOIO);
	if (!req)
		goto done;

	sdev_printk(KERN_INFO, sdev, "sending START_STOP.");
		return SCSI_DH_RES_TEMP_UNAVAIL;

	req->cmd_type = REQ_TYPE_BLOCK_PC;
	req->cmd_flags |= REQ_FAILFAST;
@@ -98,19 +214,78 @@ static int hp_sw_activate(struct scsi_device *sdev)
	req->sense = h->sense;
	memset(req->sense, 0, SCSI_SENSE_BUFFERSIZE);
	req->sense_len = 0;
	retry = h->retries;

retry:
	ret = blk_execute_rq(req->q, NULL, req, 1);
	if (!ret) /* SUCCESS */
		ret = hp_sw_done(sdev);
	else
	if (ret == -EIO) {
		if (req->sense_len > 0) {
			ret = start_done(sdev, h->sense);
		} else {
			sdev_printk(KERN_WARNING, sdev,
				    "%s: sending start_stop_unit failed with %x\n",
				    HP_SW_NAME, req->errors);
			ret = SCSI_DH_IO;
done:
		}
	} else
		ret = SCSI_DH_OK;

	if (ret == SCSI_DH_RETRY) {
		if (--retry)
			goto retry;
		ret = SCSI_DH_IO;
	}

	blk_put_request(req);

	return ret;
}

static int hp_sw_prep_fn(struct scsi_device *sdev, struct request *req)
{
	struct hp_sw_dh_data *h = get_hp_sw_data(sdev);
	int ret = BLKPREP_OK;

	if (h->path_state != HP_SW_PATH_ACTIVE) {
		ret = BLKPREP_KILL;
		req->cmd_flags |= REQ_QUIET;
	}
	return ret;

}

/*
 * hp_sw_activate - Activate a path
 * @sdev: sdev on the path to be activated
 *
 * The HP Active/Passive firmware is pretty simple;
 * the passive path reports NOT READY with sense codes
 * 0x04/0x02; a START STOP UNIT command will then
 * activate the passive path (and deactivate the
 * previously active one).
 */
static int hp_sw_activate(struct scsi_device *sdev)
{
	int ret = SCSI_DH_OK;
	struct hp_sw_dh_data *h = get_hp_sw_data(sdev);

	ret = hp_sw_tur(sdev, h);

	if (ret == SCSI_DH_OK && h->path_state == HP_SW_PATH_PASSIVE) {
		ret = hp_sw_start_stop(sdev, h);
		if (ret == SCSI_DH_OK)
			sdev_printk(KERN_INFO, sdev,
				    "%s: activated path\n",
				    HP_SW_NAME);
	}

	return ret;
}

const struct scsi_dh_devlist hp_sw_dh_data_list[] = {
	{"COMPAQ", "MSA"},
	{"HP", "HSV"},
	{"COMPAQ", "MSA1000 VOLUME"},
	{"COMPAQ", "HSV110"},
	{"HP", "HSV100"},
	{"DEC", "HSG80"},
	{NULL, NULL},
};
@@ -125,30 +300,51 @@ static struct scsi_device_handler hp_sw_dh = {
	.attach		= hp_sw_bus_attach,
	.detach		= hp_sw_bus_detach,
	.activate	= hp_sw_activate,
	.prep_fn	= hp_sw_prep_fn,
};

static int hp_sw_bus_attach(struct scsi_device *sdev)
{
	struct scsi_dh_data *scsi_dh_data;
	struct hp_sw_dh_data *h;
	unsigned long flags;
	int ret;

	scsi_dh_data = kzalloc(sizeof(struct scsi_device_handler *)
			       + sizeof(struct hp_sw_dh_data) , GFP_KERNEL);
	if (!scsi_dh_data) {
		sdev_printk(KERN_ERR, sdev, "Attach Failed %s.\n",
		sdev_printk(KERN_ERR, sdev, "%s: Attach Failed\n",
			    HP_SW_NAME);
		return 0;
	}

	scsi_dh_data->scsi_dh = &hp_sw_dh;
	h = (struct hp_sw_dh_data *) scsi_dh_data->buf;
	h->path_state = HP_SW_PATH_UNINITIALIZED;
	h->retries = HP_SW_RETRIES;

	ret = hp_sw_tur(sdev, h);
	if (ret != SCSI_DH_OK || h->path_state == HP_SW_PATH_UNINITIALIZED)
		goto failed;

	if (!try_module_get(THIS_MODULE))
		goto failed;

	spin_lock_irqsave(sdev->request_queue->queue_lock, flags);
	sdev->scsi_dh_data = scsi_dh_data;
	spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags);
	try_module_get(THIS_MODULE);

	sdev_printk(KERN_NOTICE, sdev, "Attached %s.\n", HP_SW_NAME);
	sdev_printk(KERN_INFO, sdev, "%s: attached to %s path\n",
		    HP_SW_NAME, h->path_state == HP_SW_PATH_ACTIVE?
		    "active":"passive");

	return 0;

failed:
	kfree(scsi_dh_data);
	sdev_printk(KERN_ERR, sdev, "%s: not attached\n",
		    HP_SW_NAME);
	return -EINVAL;
}

static void hp_sw_bus_detach( struct scsi_device *sdev )
@@ -162,7 +358,7 @@ static void hp_sw_bus_detach( struct scsi_device *sdev )
	spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags);
	module_put(THIS_MODULE);

	sdev_printk(KERN_NOTICE, sdev, "Detached %s\n", HP_SW_NAME);
	sdev_printk(KERN_NOTICE, sdev, "%s: Detached\n", HP_SW_NAME);

	kfree(scsi_dh_data);
}
@@ -180,6 +376,6 @@ static void __exit hp_sw_exit(void)
module_init(hp_sw_init);
module_exit(hp_sw_exit);

MODULE_DESCRIPTION("HP MSA 1000");
MODULE_DESCRIPTION("HP Active/Passive driver");
MODULE_AUTHOR("Mike Christie <michaelc@cs.wisc.edu");
MODULE_LICENSE("GPL");