Commit 39216033 authored by James Bottomley's avatar James Bottomley
Browse files

[SCSI] use scatter lists for all block pc requests and simplify hw handlers



Original From: Mike Christie <michaelc@cs.wisc.edu>

Add scsi_execute_req() as a replacement for scsi_wait_req()

Fixed up various pieces (added REQ_SPECIAL and caught req use after
free)

Signed-off-by: default avatarJames Bottomley <James.Bottomley@SteelEye.com>
parent 8e640118
Loading
Loading
Loading
Loading
+50 −1
Original line number Diff line number Diff line
@@ -246,7 +246,7 @@ void scsi_wait_req(struct scsi_request *sreq, const void *cmnd, void *buffer,
		   unsigned bufflen, int timeout, int retries)
{
	DECLARE_COMPLETION(wait);
	int write = sreq->sr_data_direction == DMA_TO_DEVICE;
	int write = (sreq->sr_data_direction == DMA_TO_DEVICE);
	struct request *req;

	req = blk_get_request(sreq->sr_device->request_queue, write,
@@ -281,6 +281,55 @@ void scsi_wait_req(struct scsi_request *sreq, const void *cmnd, void *buffer,

EXPORT_SYMBOL(scsi_wait_req);

/**
 * scsi_execute_req - insert request and wait for the result
 * @sdev:	scsi device
 * @cmd:	scsi command
 * @data_direction: data direction
 * @buffer:	data buffer
 * @bufflen:	len of buffer
 * @sense:	optional sense buffer
 * @timeout:	request timeout in seconds
 * @retries:	number of times to retry request
 *
 * scsi_execute_req returns the req->errors value which is the
 * the scsi_cmnd result field.
 **/
int scsi_execute_req(struct scsi_device *sdev, unsigned char *cmd,
		     int data_direction, void *buffer, unsigned bufflen,
		     unsigned char *sense, int timeout, int retries)
{
	struct request *req;
	int write = (data_direction == DMA_TO_DEVICE);
	int ret = DRIVER_ERROR << 24;

	req = blk_get_request(sdev->request_queue, write, __GFP_WAIT);

	if (bufflen &&	blk_rq_map_kern(sdev->request_queue, req,
					buffer, bufflen, __GFP_WAIT))
		goto out;

	req->cmd_len = COMMAND_SIZE(cmd[0]);
	memcpy(req->cmd, cmd, req->cmd_len);
	req->sense = sense;
	req->sense_len = 0;
	req->timeout = timeout;
	req->flags |= REQ_BLOCK_PC | REQ_SPECIAL;

	/*
	 * head injection *required* here otherwise quiesce won't work
	 */
	blk_execute_rq(req->q, NULL, req, 1);

	ret = req->errors;
 out:
	blk_put_request(req);

	return ret;
}

EXPORT_SYMBOL(scsi_execute_req);

/*
 * Function:    scsi_init_cmd_errh()
 *
+49 −63
Original line number Diff line number Diff line
@@ -111,15 +111,14 @@ MODULE_PARM_DESC(inq_timeout,

/**
 * scsi_unlock_floptical - unlock device via a special MODE SENSE command
 * @sreq:	used to send the command
 * @sdev:	scsi device to send command to
 * @result:	area to store the result of the MODE SENSE
 *
 * Description:
 *     Send a vendor specific MODE SENSE (not a MODE SELECT) command using
 *     @sreq to unlock a device, storing the (unused) results into result.
 *     Send a vendor specific MODE SENSE (not a MODE SELECT) command.
 *     Called for BLIST_KEY devices.
 **/
static void scsi_unlock_floptical(struct scsi_request *sreq,
static void scsi_unlock_floptical(struct scsi_device *sdev,
				  unsigned char *result)
{
	unsigned char scsi_cmd[MAX_COMMAND_SIZE];
@@ -131,9 +130,8 @@ static void scsi_unlock_floptical(struct scsi_request *sreq,
	scsi_cmd[3] = 0;
	scsi_cmd[4] = 0x2a;     /* size */
	scsi_cmd[5] = 0;
	sreq->sr_cmd_len = 0;
	sreq->sr_data_direction = DMA_FROM_DEVICE;
	scsi_wait_req(sreq, scsi_cmd, result, 0x2a /* size */, SCSI_TIMEOUT, 3);
	scsi_execute_req(sdev, scsi_cmd, DMA_FROM_DEVICE, result, 0x2a, NULL,
			 SCSI_TIMEOUT, 3);
}

/**
@@ -433,26 +431,26 @@ void scsi_target_reap(struct scsi_target *starget)

/**
 * scsi_probe_lun - probe a single LUN using a SCSI INQUIRY
 * @sreq:	used to send the INQUIRY
 * @sdev:	scsi_device to probe
 * @inq_result:	area to store the INQUIRY result
 * @result_len: len of inq_result
 * @bflags:	store any bflags found here
 *
 * Description:
 *     Probe the lun associated with @sreq using a standard SCSI INQUIRY;
 *     Probe the lun associated with @req using a standard SCSI INQUIRY;
 *
 *     If the INQUIRY is successful, sreq->sr_result is zero and: the
 *     If the INQUIRY is successful, zero is returned and the
 *     INQUIRY data is in @inq_result; the scsi_level and INQUIRY length
 *     are copied to the Scsi_Device at @sreq->sr_device (sdev);
 *     any flags value is stored in *@bflags.
 *     are copied to the Scsi_Device any flags value is stored in *@bflags.
 **/
static void scsi_probe_lun(struct scsi_request *sreq, char *inq_result,
			   int *bflags)
static int scsi_probe_lun(struct scsi_device *sdev, char *inq_result,
			  int result_len, int *bflags)
{
	struct scsi_device *sdev = sreq->sr_device;	/* a bit ugly */
	char sense[SCSI_SENSE_BUFFERSIZE];
	unsigned char scsi_cmd[MAX_COMMAND_SIZE];
	int first_inquiry_len, try_inquiry_len, next_inquiry_len;
	int response_len = 0;
	int pass, count;
	int pass, count, result;
	struct scsi_sense_hdr sshdr;

	*bflags = 0;
@@ -475,28 +473,28 @@ static void scsi_probe_lun(struct scsi_request *sreq, char *inq_result,
		memset(scsi_cmd, 0, 6);
		scsi_cmd[0] = INQUIRY;
		scsi_cmd[4] = (unsigned char) try_inquiry_len;
		sreq->sr_cmd_len = 0;
		sreq->sr_data_direction = DMA_FROM_DEVICE;

		memset(sense, 0, sizeof(sense));
		memset(inq_result, 0, try_inquiry_len);
		scsi_wait_req(sreq, (void *) scsi_cmd, (void *) inq_result,
				try_inquiry_len,

		result = scsi_execute_req(sdev,  scsi_cmd, DMA_FROM_DEVICE,
					  inq_result, try_inquiry_len, sense,
					  HZ / 2 + HZ * scsi_inq_timeout, 3);

		SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO "scsi scan: INQUIRY %s "
				"with code 0x%x\n",
				sreq->sr_result ? "failed" : "successful",
				sreq->sr_result));
				result ? "failed" : "successful", result));

		if (sreq->sr_result) {
		if (result) {
			/*
			 * not-ready to ready transition [asc/ascq=0x28/0x0]
			 * or power-on, reset [asc/ascq=0x29/0x0], continue.
			 * INQUIRY should not yield UNIT_ATTENTION
			 * but many buggy devices do so anyway. 
			 */
			if ((driver_byte(sreq->sr_result) & DRIVER_SENSE) &&
			    scsi_request_normalize_sense(sreq, &sshdr)) {
			if ((driver_byte(result) & DRIVER_SENSE) &&
			    scsi_normalize_sense(sense, sizeof(sense),
						 &sshdr)) {
				if ((sshdr.sense_key == UNIT_ATTENTION) &&
				    ((sshdr.asc == 0x28) ||
				     (sshdr.asc == 0x29)) &&
@@ -507,7 +505,7 @@ static void scsi_probe_lun(struct scsi_request *sreq, char *inq_result,
		break;
	}

	if (sreq->sr_result == 0) {
	if (result == 0) {
		response_len = (unsigned char) inq_result[4] + 5;
		if (response_len > 255)
			response_len = first_inquiry_len;	/* sanity */
@@ -556,8 +554,8 @@ static void scsi_probe_lun(struct scsi_request *sreq, char *inq_result,

	/* If the last transfer attempt got an error, assume the
	 * peripheral doesn't exist or is dead. */
	if (sreq->sr_result)
		return;
	if (result)
		return -EIO;

	/* Don't report any more data than the device says is valid */
	sdev->inquiry_len = min(try_inquiry_len, response_len);
@@ -593,7 +591,7 @@ static void scsi_probe_lun(struct scsi_request *sreq, char *inq_result,
	    (sdev->scsi_level == 1 && (inq_result[3] & 0x0f) == 1))
		sdev->scsi_level++;

	return;
	return 0;
}

/**
@@ -800,9 +798,8 @@ static int scsi_probe_and_add_lun(struct scsi_target *starget,
				  void *hostdata)
{
	struct scsi_device *sdev;
	struct scsi_request *sreq;
	unsigned char *result;
	int bflags, res = SCSI_SCAN_NO_RESPONSE;
	int bflags, res = SCSI_SCAN_NO_RESPONSE, result_len = 256;
	struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);

	/*
@@ -831,16 +828,13 @@ static int scsi_probe_and_add_lun(struct scsi_target *starget,
	sdev = scsi_alloc_sdev(starget, lun, hostdata);
	if (!sdev)
		goto out;
	sreq = scsi_allocate_request(sdev, GFP_ATOMIC);
	if (!sreq)
		goto out_free_sdev;
	result = kmalloc(256, GFP_ATOMIC |

	result = kmalloc(result_len, GFP_ATOMIC |
			((shost->unchecked_isa_dma) ? __GFP_DMA : 0));
	if (!result)
		goto out_free_sreq;
		goto out_free_sdev;

	scsi_probe_lun(sreq, result, &bflags);
	if (sreq->sr_result)
	if (scsi_probe_lun(sdev, result, result_len, &bflags))
		goto out_free_result;

	/*
@@ -868,7 +862,7 @@ static int scsi_probe_and_add_lun(struct scsi_target *starget,
	if (res == SCSI_SCAN_LUN_PRESENT) {
		if (bflags & BLIST_KEY) {
			sdev->lockable = 0;
			scsi_unlock_floptical(sreq, result);
			scsi_unlock_floptical(sdev, result);
		}
		if (bflagsp)
			*bflagsp = bflags;
@@ -876,8 +870,6 @@ static int scsi_probe_and_add_lun(struct scsi_target *starget,

 out_free_result:
	kfree(result);
 out_free_sreq:
	scsi_release_request(sreq);
 out_free_sdev:
	if (res == SCSI_SCAN_LUN_PRESENT) {
		if (sdevp) {
@@ -1065,13 +1057,14 @@ static int scsi_report_lun_scan(struct scsi_device *sdev, int bflags,
				int rescan)
{
	char devname[64];
	char sense[SCSI_SENSE_BUFFERSIZE];
	unsigned char scsi_cmd[MAX_COMMAND_SIZE];
	unsigned int length;
	unsigned int lun;
	unsigned int num_luns;
	unsigned int retries;
	int result;
	struct scsi_lun *lunp, *lun_data;
	struct scsi_request *sreq;
	u8 *data;
	struct scsi_sense_hdr sshdr;
	struct scsi_target *starget = scsi_target(sdev);
@@ -1089,10 +1082,6 @@ static int scsi_report_lun_scan(struct scsi_device *sdev, int bflags,
	if (bflags & BLIST_NOLUN)
		return 0;

	sreq = scsi_allocate_request(sdev, GFP_ATOMIC);
	if (!sreq)
		goto out;

	sprintf(devname, "host %d channel %d id %d",
		sdev->host->host_no, sdev->channel, sdev->id);

@@ -1110,7 +1099,7 @@ static int scsi_report_lun_scan(struct scsi_device *sdev, int bflags,
	lun_data = kmalloc(length, GFP_ATOMIC |
			   (sdev->host->unchecked_isa_dma ? __GFP_DMA : 0));
	if (!lun_data)
		goto out_release_request;
		goto out;

	scsi_cmd[0] = REPORT_LUNS;

@@ -1129,8 +1118,6 @@ static int scsi_report_lun_scan(struct scsi_device *sdev, int bflags,

	scsi_cmd[10] = 0;	/* reserved */
	scsi_cmd[11] = 0;	/* control */
	sreq->sr_cmd_len = 0;
	sreq->sr_data_direction = DMA_FROM_DEVICE;

	/*
	 * We can get a UNIT ATTENTION, for example a power on/reset, so
@@ -1146,29 +1133,30 @@ static int scsi_report_lun_scan(struct scsi_device *sdev, int bflags,
		SCSI_LOG_SCAN_BUS(3, printk (KERN_INFO "scsi scan: Sending"
				" REPORT LUNS to %s (try %d)\n", devname,
				retries));
		scsi_wait_req(sreq, scsi_cmd, lun_data, length,

		memset(sense, 0, sizeof(sense));
		result = scsi_execute_req(sdev, scsi_cmd, DMA_FROM_DEVICE,
					  lun_data, length, sense,
					  SCSI_TIMEOUT + 4 * HZ, 3);

		SCSI_LOG_SCAN_BUS(3, printk (KERN_INFO "scsi scan: REPORT LUNS"
				" %s (try %d) result 0x%x\n", sreq->sr_result
				?  "failed" : "successful", retries,
				sreq->sr_result));
		if (sreq->sr_result == 0)
				" %s (try %d) result 0x%x\n", result
				?  "failed" : "successful", retries, result));
		if (result == 0)
			break;
		else if (scsi_request_normalize_sense(sreq, &sshdr)) {
		else if (scsi_normalize_sense(sense, sizeof(sense), &sshdr)) {
			if (sshdr.sense_key != UNIT_ATTENTION)
				break;
		}
	}

	if (sreq->sr_result) {
	if (result) {
		/*
		 * The device probably does not support a REPORT LUN command
		 */
		kfree(lun_data);
		scsi_release_request(sreq);
		return 1;
	}
	scsi_release_request(sreq);

	/*
	 * Get the length from the first four bytes of lun_data.
@@ -1242,8 +1230,6 @@ static int scsi_report_lun_scan(struct scsi_device *sdev, int bflags,
	kfree(lun_data);
	return 0;

 out_release_request:
	scsi_release_request(sreq);
 out:
	/*
	 * We are out of memory, don't try scanning any further.
+3 −0
Original line number Diff line number Diff line
@@ -54,6 +54,9 @@ extern void scsi_do_req(struct scsi_request *, const void *cmnd,
			void *buffer, unsigned bufflen,
			void (*done) (struct scsi_cmnd *),
			int timeout, int retries);
extern int scsi_execute_req(struct scsi_device *sdev, unsigned char *cmd,
			    int data_direction, void *buffer, unsigned bufflen,
			    unsigned char *sense, int timeout, int retries);

struct scsi_mode_data {
	__u32	length;