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

Commit b2460756 authored by Hannes Reinecke's avatar Hannes Reinecke Committed by Martin K. Petersen
Browse files

scsi_dh_alua: Make stpg synchronous



The 'activate_complete' function needs to be executed after
stpg has finished, so we can as well execute stpg synchronously
and call the function directly.

Reviewed-by: default avatarBart Van Assche <bart.vanassche@sandisk.com>
Reviewed-by: default avatarChristoph Hellwig <hch@lst.de>
Signed-off-by: default avatarHannes Reinecke <hare@suse.de>
Signed-off-by: default avatarMartin K. Petersen <martin.petersen@oracle.com>
parent f2ecf13a
Loading
Loading
Loading
Loading
+54 −104
Original line number Original line Diff line number Diff line
@@ -169,81 +169,28 @@ static unsigned submit_rtpg(struct scsi_device *sdev, unsigned char *buff,
}
}


/*
/*
 * stpg_endio - Evaluate SET TARGET GROUP STATES
 * submit_stpg - Issue a SET TARGET PORT GROUP command
 * @sdev: the device to be evaluated
 * @state: the new target group state
 *
 * Evaluate a SET TARGET GROUP STATES command response.
 */
static void stpg_endio(struct request *req, int error)
{
	struct alua_dh_data *h = req->end_io_data;
	struct scsi_sense_hdr sense_hdr;
	unsigned err = SCSI_DH_OK;

	if (host_byte(req->errors) != DID_OK ||
	    msg_byte(req->errors) != COMMAND_COMPLETE) {
		err = SCSI_DH_IO;
		goto done;
	}

	if (scsi_normalize_sense(h->sense, SCSI_SENSE_BUFFERSIZE,
				 &sense_hdr)) {
		if (sense_hdr.sense_key == NOT_READY &&
		    sense_hdr.asc == 0x04 && sense_hdr.ascq == 0x0a) {
			/* ALUA state transition already in progress */
			err = SCSI_DH_OK;
			goto done;
		}
		if (sense_hdr.sense_key == UNIT_ATTENTION) {
			err = SCSI_DH_RETRY;
			goto done;
		}
		sdev_printk(KERN_INFO, h->sdev, "%s: stpg failed\n",
			    ALUA_DH_NAME);
		scsi_print_sense_hdr(h->sdev, ALUA_DH_NAME, &sense_hdr);
		err = SCSI_DH_IO;
	} else if (error)
		err = SCSI_DH_IO;

	if (err == SCSI_DH_OK) {
		h->state = TPGS_STATE_OPTIMIZED;
		sdev_printk(KERN_INFO, h->sdev,
			    "%s: port group %02x switched to state %c\n",
			    ALUA_DH_NAME, h->group_id,
			    print_alua_state(h->state));
	}
done:
	req->end_io_data = NULL;
	__blk_put_request(req->q, req);
	if (h->callback_fn) {
		h->callback_fn(h->callback_data, err);
		h->callback_fn = h->callback_data = NULL;
	}
	return;
}

/*
 * submit_stpg - Issue a SET TARGET GROUP STATES command
 *
 *
 * Currently we're only setting the current target port group state
 * Currently we're only setting the current target port group state
 * to 'active/optimized' and let the array firmware figure out
 * to 'active/optimized' and let the array firmware figure out
 * the states of the remaining groups.
 * the states of the remaining groups.
 */
 */
static unsigned submit_stpg(struct alua_dh_data *h)
static unsigned submit_stpg(struct scsi_device *sdev, int group_id,
			    unsigned char *sense)
{
{
	struct request *rq;
	struct request *rq;
	unsigned char stpg_data[8];
	int stpg_len = 8;
	int stpg_len = 8;
	struct scsi_device *sdev = h->sdev;
	int err = 0;


	/* Prepare the data buffer */
	/* Prepare the data buffer */
	memset(h->buff, 0, stpg_len);
	memset(stpg_data, 0, stpg_len);
	h->buff[4] = TPGS_STATE_OPTIMIZED & 0x0f;
	stpg_data[4] = TPGS_STATE_OPTIMIZED & 0x0f;
	put_unaligned_be16(h->group_id, &h->buff[6]);
	put_unaligned_be16(group_id, &stpg_data[6]);


	rq = get_alua_req(sdev, h->buff, stpg_len, WRITE);
	rq = get_alua_req(sdev, stpg_data, stpg_len, WRITE);
	if (!rq)
	if (!rq)
		return SCSI_DH_RES_TEMP_UNAVAIL;
		return DRIVER_BUSY << 24;


	/* Prepare the command. */
	/* Prepare the command. */
	rq->cmd[0] = MAINTENANCE_OUT;
	rq->cmd[0] = MAINTENANCE_OUT;
@@ -251,13 +198,17 @@ static unsigned submit_stpg(struct alua_dh_data *h)
	put_unaligned_be32(stpg_len, &rq->cmd[6]);
	put_unaligned_be32(stpg_len, &rq->cmd[6]);
	rq->cmd_len = COMMAND_SIZE(MAINTENANCE_OUT);
	rq->cmd_len = COMMAND_SIZE(MAINTENANCE_OUT);


	rq->sense = h->sense;
	rq->sense = sense;
	memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE);
	memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE);
	rq->sense_len = 0;
	rq->sense_len = 0;
	rq->end_io_data = h;


	blk_execute_rq_nowait(rq->q, NULL, rq, 1, stpg_endio);
	blk_execute_rq(rq->q, NULL, rq, 1);
	return SCSI_DH_OK;
	if (rq->errors)
		err = rq->errors;

	blk_put_request(rq);

	return err;
}
}


/*
/*
@@ -582,59 +533,59 @@ static int alua_rtpg(struct scsi_device *sdev, struct alua_dh_data *h, int wait_
 * alua_stpg - Issue a SET TARGET PORT GROUP command
 * alua_stpg - Issue a SET TARGET PORT GROUP command
 *
 *
 * Issue a SET TARGET PORT GROUP command and evaluate the
 * Issue a SET TARGET PORT GROUP command and evaluate the
 * response. Returns SCSI_DH_RETRY if the target port group
 * response. Returns SCSI_DH_RETRY per default to trigger
 * state is found to be in 'transitioning'.
 * a re-evaluation of the target group state or SCSI_DH_OK
 * If SCSI_DH_OK is returned the passed-in 'fn' function
 * if no further action needs to be taken.
 * this function will take care of executing 'fn'.
 * Otherwise 'fn' should be executed by the caller with the
 * returned error code.
 */
 */
static unsigned alua_stpg(struct scsi_device *sdev, struct alua_dh_data *h,
static unsigned alua_stpg(struct scsi_device *sdev, struct alua_dh_data *h)
			  activate_complete fn, void *data)
{
{
	int err = SCSI_DH_OK;
	int retval;
	int stpg = 0;
	struct scsi_sense_hdr sense_hdr;

	if (!(h->tpgs & TPGS_MODE_EXPLICIT))
		/* Only implicit ALUA supported */
		goto out;


	if (!(h->tpgs & TPGS_MODE_EXPLICIT)) {
		/* Only implicit ALUA supported, retry */
		return SCSI_DH_RETRY;
	}
	switch (h->state) {
	switch (h->state) {
	case TPGS_STATE_OPTIMIZED:
		return SCSI_DH_OK;
	case TPGS_STATE_NONOPTIMIZED:
	case TPGS_STATE_NONOPTIMIZED:
		stpg = 1;
		if ((h->flags & ALUA_OPTIMIZE_STPG) &&
		if ((h->flags & ALUA_OPTIMIZE_STPG) &&
		    !h->pref &&
		    !h->pref &&
		    (h->tpgs & TPGS_MODE_IMPLICIT))
		    (h->tpgs & TPGS_MODE_IMPLICIT))
			stpg = 0;
			return SCSI_DH_OK;
		break;
		break;
	case TPGS_STATE_STANDBY:
	case TPGS_STATE_STANDBY:
	case TPGS_STATE_UNAVAILABLE:
	case TPGS_STATE_UNAVAILABLE:
		stpg = 1;
		break;
		break;
	case TPGS_STATE_OFFLINE:
	case TPGS_STATE_OFFLINE:
		err = SCSI_DH_IO;
		return SCSI_DH_IO;
		break;
	case TPGS_STATE_TRANSITIONING:
	case TPGS_STATE_TRANSITIONING:
		err = SCSI_DH_RETRY;
		break;
		break;
	default:
	default:
		break;
		sdev_printk(KERN_INFO, sdev,
			    "%s: stpg failed, unhandled TPGS state %d",
			    ALUA_DH_NAME, h->state);
		return SCSI_DH_NOSYS;
	}
	}
	retval = submit_stpg(sdev, h->group_id, h->sense);


	if (stpg) {
	if (retval) {
		h->callback_fn = fn;
		if (!scsi_normalize_sense(h->sense, SCSI_SENSE_BUFFERSIZE,
		h->callback_data = data;
					  &sense_hdr)) {
		err = submit_stpg(h);
			sdev_printk(KERN_INFO, sdev,
		if (err != SCSI_DH_OK)
				    "%s: stpg failed, result %d",
			h->callback_fn = h->callback_data = NULL;
				    ALUA_DH_NAME, retval);
		else
			if (driver_byte(retval) == DRIVER_BUSY)
			fn = NULL;
				return SCSI_DH_DEV_TEMP_BUSY;
		} else {
			sdev_printk(KERN_INFO, h->sdev, "%s: stpg failed\n",
				    ALUA_DH_NAME);
			scsi_print_sense_hdr(sdev, ALUA_DH_NAME, &sense_hdr);
		}
		}
out:
	}
	if (fn)
	/* Retry RTPG */
		fn(data, err);
	return SCSI_DH_RETRY;

	return err;
}
}


/*
/*
@@ -722,10 +673,9 @@ static int alua_activate(struct scsi_device *sdev,
	if (optimize_stpg)
	if (optimize_stpg)
		h->flags |= ALUA_OPTIMIZE_STPG;
		h->flags |= ALUA_OPTIMIZE_STPG;


	err = alua_stpg(sdev, h, fn, data);
	err = alua_stpg(sdev, h);

out:
out:
	if (err != SCSI_DH_OK && fn)
	if (fn)
		fn(data, err);
		fn(data, err);
	return 0;
	return 0;
}
}