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

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

[SCSI] Handle MLQUEUE busy response in scsi_send_eh_cmnd



scsi_send_eh_cmnd() is calling queuecommand() directly, so
it needs to check the return value here.
The only valid return codes for queuecommand() are 'busy'
states, so we need to wait for a bit to allow the LLDD
to recover.

Based on an earlier patch from Wen Xiong.

[jejb: fix confusion between msec and jiffies values and other issues]
[bvanassche: correct stall_for interval]
Cc: Wen Xiong <wenxiong@linux.vnet.ibm.com>
Cc: Brian King <brking@linux.vnet.ibm.com>
Signed-off-by: default avatarHannes Reinecke <hare@suse.de>
Signed-off-by: default avatarJames Bottomley <JBottomley@Parallels.com>
parent d522844a
Loading
Loading
Loading
Loading
+27 −10
Original line number Original line Diff line number Diff line
@@ -25,6 +25,7 @@
#include <linux/interrupt.h>
#include <linux/interrupt.h>
#include <linux/blkdev.h>
#include <linux/blkdev.h>
#include <linux/delay.h>
#include <linux/delay.h>
#include <linux/jiffies.h>


#include <scsi/scsi.h>
#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_cmnd.h>
@@ -791,32 +792,48 @@ static int scsi_send_eh_cmnd(struct scsi_cmnd *scmd, unsigned char *cmnd,
	struct scsi_device *sdev = scmd->device;
	struct scsi_device *sdev = scmd->device;
	struct Scsi_Host *shost = sdev->host;
	struct Scsi_Host *shost = sdev->host;
	DECLARE_COMPLETION_ONSTACK(done);
	DECLARE_COMPLETION_ONSTACK(done);
	unsigned long timeleft;
	unsigned long timeleft = timeout;
	struct scsi_eh_save ses;
	struct scsi_eh_save ses;
	const unsigned long stall_for = msecs_to_jiffies(100);
	int rtn;
	int rtn;


retry:
	scsi_eh_prep_cmnd(scmd, &ses, cmnd, cmnd_size, sense_bytes);
	scsi_eh_prep_cmnd(scmd, &ses, cmnd, cmnd_size, sense_bytes);
	shost->eh_action = &done;
	shost->eh_action = &done;


	scsi_log_send(scmd);
	scsi_log_send(scmd);
	scmd->scsi_done = scsi_eh_done;
	scmd->scsi_done = scsi_eh_done;
	shost->hostt->queuecommand(shost, scmd);
	rtn = shost->hostt->queuecommand(shost, scmd);

	if (rtn) {
		if (timeleft > stall_for) {
			scsi_eh_restore_cmnd(scmd, &ses);
			timeleft -= stall_for;
			msleep(jiffies_to_msecs(stall_for));
			goto retry;
		}
		/* signal not to enter either branch of the if () below */
		timeleft = 0;
		rtn = NEEDS_RETRY;
	} else {
		timeleft = wait_for_completion_timeout(&done, timeout);
		timeleft = wait_for_completion_timeout(&done, timeout);
	}


	shost->eh_action = NULL;
	shost->eh_action = NULL;


	scsi_log_completion(scmd, SUCCESS);
	scsi_log_completion(scmd, rtn);


	SCSI_LOG_ERROR_RECOVERY(3,
	SCSI_LOG_ERROR_RECOVERY(3,
		printk("%s: scmd: %p, timeleft: %ld\n",
		printk("%s: scmd: %p, timeleft: %ld\n",
			__func__, scmd, timeleft));
			__func__, scmd, timeleft));


	/*
	/*
	 * If there is time left scsi_eh_done got called, and we will
	 * If there is time left scsi_eh_done got called, and we will examine
	 * examine the actual status codes to see whether the command
	 * the actual status codes to see whether the command actually did
	 * actually did complete normally, else tell the host to forget
	 * complete normally, else if we have a zero return and no time left,
	 * about this command.
	 * the command must still be pending, so abort it and return FAILED.
	 * If we never actually managed to issue the command, because
	 * ->queuecommand() kept returning non zero, use the rtn = FAILED
	 * value above (so don't execute either branch of the if)
	 */
	 */
	if (timeleft) {
	if (timeleft) {
		rtn = scsi_eh_completed_normally(scmd);
		rtn = scsi_eh_completed_normally(scmd);
@@ -837,7 +854,7 @@ static int scsi_send_eh_cmnd(struct scsi_cmnd *scmd, unsigned char *cmnd,
			rtn = FAILED;
			rtn = FAILED;
			break;
			break;
		}
		}
	} else {
	} else if (!rtn) {
		scsi_abort_eh_cmnd(scmd);
		scsi_abort_eh_cmnd(scmd);
		rtn = FAILED;
		rtn = FAILED;
	}
	}