Commit 954b2b5a authored by Raghava Aditya Renukunta's avatar Raghava Aditya Renukunta Committed by Martin K. Petersen
Browse files

scsi: aacraid: Added support to abort cmd and reset lun



Added task management command support to abort any timed out commands
in case of a eh_abort call and to reset lun's in case of eh_reset call.

Signed-off-by: default avatarRaghava Aditya Renukunta <RaghavaAditya.Renukunta@microsemi.com>
Signed-off-by: default avatarDave Carroll <David.Carroll@microsemi.com>
Reviewed-by: default avatarJohannes Thumshirn <jthumshirn@suse.de>
Signed-off-by: default avatarMartin K. Petersen <martin.petersen@oracle.com>
parent ab5d129f
Loading
Loading
Loading
Loading
+48 −0
Original line number Diff line number Diff line
@@ -206,6 +206,53 @@ struct aac_hba_cmd_req {
	 */
};

/* Task Management Functions (TMF) */
#define HBA_TMF_ABORT_TASK	0x01
#define HBA_TMF_LUN_RESET	0x08

struct aac_hba_tm_req {
	u8	iu_type;	/* HBA information unit type */
	u8	reply_qid;	/* Host reply queue to post response to */
	u8	tmf;		/* Task management function */
	u8	reserved1;

	__le32	it_nexus;	/* Device handle for the command */

	u8	lun[8];		/* SCSI LUN */

	/* Used to hold sender context. */
	__le32	request_id;	/* Sender context */
	__le32	reserved2;

	/* Request identifier of managed task */
	__le32	managed_request_id;	/* Sender context being managed */
	__le32	reserved3;

	/* Lower 32-bits of reserved error data target location on the host */
	__le32	error_ptr_lo;
	/* Upper 32-bits of reserved error data target location on the host */
	__le32	error_ptr_hi;
	/* Length of reserved error data area on the host in bytes */
	__le32	error_length;
};

struct aac_hba_reset_req {
	u8	iu_type;	/* HBA information unit type */
	/* 0 - reset specified device, 1 - reset all devices */
	u8	reset_type;
	u8	reply_qid;	/* Host reply queue to post response to */
	u8	reserved1;

	__le32	it_nexus;	/* Device handle for the command */
	__le32	request_id;	/* Sender context */
	/* Lower 32-bits of reserved error data target location on the host */
	__le32	error_ptr_lo;
	/* Upper 32-bits of reserved error data target location on the host */
	__le32	error_ptr_hi;
	/* Length of reserved error data area on the host in bytes */
	__le32	error_length;
};

struct aac_hba_resp {
	u8	iu_type;		/* HBA information unit type */
	u8	reserved1[3];
@@ -223,6 +270,7 @@ struct aac_hba_resp {
struct aac_native_hba {
	union {
		struct aac_hba_cmd_req cmd;
		struct aac_hba_tm_req tmr;
		u8 cmd_bytes[AAC_MAX_NATIVE_SIZE-FW_ERROR_BUFFER_SIZE];
	} cmd;
	union {
+269 −84
Original line number Diff line number Diff line
@@ -574,12 +574,80 @@ static int aac_eh_abort(struct scsi_cmnd* cmd)
	struct scsi_device * dev = cmd->device;
	struct Scsi_Host * host = dev->host;
	struct aac_dev * aac = (struct aac_dev *)host->hostdata;
	int count;
	int count, found;
	u32 bus, cid;
	int ret = FAILED;

	printk(KERN_ERR "%s: Host adapter abort request (%d,%d,%d,%llu)\n",
	bus = aac_logical_to_phys(scmd_channel(cmd));
	cid = scmd_id(cmd);
	if (aac->hba_map[bus][cid].devtype == AAC_DEVTYPE_NATIVE_RAW) {
		struct fib *fib;
		struct aac_hba_tm_req *tmf;
		int status;
		u64 address;
		__le32 managed_request_id;

		pr_err("%s: Host adapter abort request (%d,%d,%d,%d)\n",
		 AAC_DRIVERNAME,
		host->host_no, sdev_channel(dev), sdev_id(dev), dev->lun);
		 host->host_no, sdev_channel(dev), sdev_id(dev), (int)dev->lun);

		found = 0;
		for (count = 0; count < (host->can_queue + AAC_NUM_MGT_FIB); ++count) {
			fib = &aac->fibs[count];
			if (*(u8 *)fib->hw_fib_va != 0 &&
				(fib->flags & FIB_CONTEXT_FLAG_NATIVE_HBA) &&
				(fib->callback_data == cmd)) {
				found = 1;
				managed_request_id = ((struct aac_hba_cmd_req *)
					fib->hw_fib_va)->request_id;
				break;
			}
		}
		if (!found)
			return ret;

		/* start a HBA_TMF_ABORT_TASK TMF request */
		fib = aac_fib_alloc(aac);
		if (!fib)
			return ret;

		tmf = (struct aac_hba_tm_req *)fib->hw_fib_va;
		memset(tmf, 0, sizeof(*tmf));
		tmf->tmf = HBA_TMF_ABORT_TASK;
		tmf->it_nexus = aac->hba_map[bus][cid].rmw_nexus;
		tmf->lun[1] = cmd->device->lun;

		address = (u64)fib->hw_error_pa;
		tmf->error_ptr_hi = cpu_to_le32((u32)(address >> 32));
		tmf->error_ptr_lo = cpu_to_le32((u32)(address & 0xffffffff));
		tmf->error_length = cpu_to_le32(FW_ERROR_BUFFER_SIZE);

		fib->hbacmd_size = sizeof(*tmf);
		cmd->SCp.sent_command = 0;

		status = aac_hba_send(HBA_IU_TYPE_SCSI_TM_REQ, fib,
				  (fib_callback) aac_hba_callback,
				  (void *) cmd);

		/* Wait up to 2 minutes for completion */
		for (count = 0; count < 120; ++count) {
			if (cmd->SCp.sent_command) {
				ret = SUCCESS;
				break;
			}
			msleep(1000);
		}

		if (ret != SUCCESS)
			pr_err("%s: Host adapter abort request timed out\n",
			AAC_DRIVERNAME);
	} else {
		pr_err(
			"%s: Host adapter abort request.\n"
			"%s: Outstanding commands on (%d,%d,%d,%d):\n",
			AAC_DRIVERNAME, AAC_DRIVERNAME,
			host->host_no, sdev_channel(dev), sdev_id(dev),
			(int)dev->lun);
		switch (cmd->cmnd[0]) {
		case SERVICE_ACTION_IN_16:
			if (!(aac->raw_io_interface) ||
@@ -588,33 +656,55 @@ static int aac_eh_abort(struct scsi_cmnd* cmd)
				break;
		case INQUIRY:
		case READ_CAPACITY:
		/* Mark associated FIB to not complete, eh handler does this */
		for (count = 0; count < (host->can_queue + AAC_NUM_MGT_FIB); ++count) {
			/*
			 * Mark associated FIB to not complete,
			 * eh handler does this
			 */
			for (count = 0;
				count < (host->can_queue + AAC_NUM_MGT_FIB);
				++count) {
				struct fib *fib = &aac->fibs[count];

				if (fib->hw_fib_va->header.XferState &&
				(fib->flags & FIB_CONTEXT_FLAG) &&
				(fib->callback_data == cmd)) {
				fib->flags |= FIB_CONTEXT_FLAG_TIMED_OUT;
				cmd->SCp.phase = AAC_OWNER_ERROR_HANDLER;
					fib->flags |=
						FIB_CONTEXT_FLAG_TIMED_OUT;
					cmd->SCp.phase =
						AAC_OWNER_ERROR_HANDLER;
					ret = SUCCESS;
				}
			}
			break;
		case TEST_UNIT_READY:
		/* Mark associated FIB to not complete, eh handler does this */
		for (count = 0; count < (host->can_queue + AAC_NUM_MGT_FIB); ++count) {
			/*
			 * Mark associated FIB to not complete,
			 * eh handler does this
			 */
			for (count = 0;
				count < (host->can_queue + AAC_NUM_MGT_FIB);
				++count) {
				struct scsi_cmnd *command;
				struct fib *fib = &aac->fibs[count];
			if ((fib->hw_fib_va->header.XferState & cpu_to_le32(Async | NoResponseExpected)) &&

				command = fib->callback_data;

				if ((fib->hw_fib_va->header.XferState &
					cpu_to_le32
					(Async | NoResponseExpected)) &&
					(fib->flags & FIB_CONTEXT_FLAG) &&
			  ((command = fib->callback_data)) &&
					((command)) &&
					(command->device == cmd->device)) {
				fib->flags |= FIB_CONTEXT_FLAG_TIMED_OUT;
				command->SCp.phase = AAC_OWNER_ERROR_HANDLER;
					fib->flags |=
						FIB_CONTEXT_FLAG_TIMED_OUT;
					command->SCp.phase =
						AAC_OWNER_ERROR_HANDLER;
					if (command == cmd)
						ret = SUCCESS;
				}
			}
			break;
		}
	}
	return ret;
}
@@ -628,14 +718,95 @@ static int aac_eh_reset(struct scsi_cmnd* cmd)
{
	struct scsi_device * dev = cmd->device;
	struct Scsi_Host * host = dev->host;
	struct scsi_cmnd * command;
	int count;
	struct aac_dev * aac = (struct aac_dev *)host->hostdata;
	int count;
	u32 bus, cid;
	int ret = FAILED;

	bus = aac_logical_to_phys(scmd_channel(cmd));
	cid = scmd_id(cmd);
	if (bus < AAC_MAX_BUSES && cid < AAC_MAX_TARGETS &&
		aac->hba_map[bus][cid].devtype == AAC_DEVTYPE_NATIVE_RAW) {
		struct fib *fib;
		int status;
		u64 address;
		u8 command;

		pr_err("%s: Host adapter reset request. SCSI hang ?\n",
			AAC_DRIVERNAME);

		fib = aac_fib_alloc(aac);
		if (!fib)
			return ret;


		if (aac->hba_map[bus][cid].reset_state == 0) {
			struct aac_hba_tm_req *tmf;

			/* start a HBA_TMF_LUN_RESET TMF request */
			tmf = (struct aac_hba_tm_req *)fib->hw_fib_va;
			memset(tmf, 0, sizeof(*tmf));
			tmf->tmf = HBA_TMF_LUN_RESET;
			tmf->it_nexus = aac->hba_map[bus][cid].rmw_nexus;
			tmf->lun[1] = cmd->device->lun;

			address = (u64)fib->hw_error_pa;
			tmf->error_ptr_hi = cpu_to_le32
					((u32)(address >> 32));
			tmf->error_ptr_lo = cpu_to_le32
					((u32)(address & 0xffffffff));
			tmf->error_length = cpu_to_le32(FW_ERROR_BUFFER_SIZE);
			fib->hbacmd_size = sizeof(*tmf);

			command = HBA_IU_TYPE_SCSI_TM_REQ;
			aac->hba_map[bus][cid].reset_state++;
		} else if (aac->hba_map[bus][cid].reset_state >= 1) {
			struct aac_hba_reset_req *rst;

			/* already tried, start a hard reset now */
			rst = (struct aac_hba_reset_req *)fib->hw_fib_va;
			memset(rst, 0, sizeof(*rst));
			/* reset_type is already zero... */
			rst->it_nexus = aac->hba_map[bus][cid].rmw_nexus;

			address = (u64)fib->hw_error_pa;
			rst->error_ptr_hi = cpu_to_le32((u32)(address >> 32));
			rst->error_ptr_lo = cpu_to_le32
				((u32)(address & 0xffffffff));
			rst->error_length = cpu_to_le32(FW_ERROR_BUFFER_SIZE);
			fib->hbacmd_size = sizeof(*rst);

			command = HBA_IU_TYPE_SATA_REQ;
			aac->hba_map[bus][cid].reset_state = 0;
		}
		cmd->SCp.sent_command = 0;

		status = aac_hba_send(command, fib,
				  (fib_callback) aac_hba_callback,
				  (void *) cmd);

		/* Wait up to 2 minutes for completion */
		for (count = 0; count < 120; ++count) {
			if (cmd->SCp.sent_command) {
				ret = SUCCESS;
				break;
			}
			msleep(1000);
		}

		if (ret != SUCCESS)
			pr_err("%s: Host adapter reset request timed out\n",
			AAC_DRIVERNAME);
	} else {
		struct scsi_cmnd *command;
		unsigned long flags;

	/* Mark the associated FIB to not complete, eh handler does this */
	for (count = 0; count < (host->can_queue + AAC_NUM_MGT_FIB); ++count) {
		/* Mark the assoc. FIB to not complete, eh handler does this */
		for (count = 0;
			count < (host->can_queue + AAC_NUM_MGT_FIB);
			++count) {
			struct fib *fib = &aac->fibs[count];

			if (fib->hw_fib_va->header.XferState &&
				(fib->flags & FIB_CONTEXT_FLAG) &&
				(fib->callback_data == cmd)) {
@@ -643,10 +814,12 @@ static int aac_eh_reset(struct scsi_cmnd* cmd)
				cmd->SCp.phase = AAC_OWNER_ERROR_HANDLER;
			}
		}
	printk(KERN_ERR "%s: Host adapter reset request. SCSI hang ?\n",

		pr_err("%s: Host adapter reset request. SCSI hang ?\n",
					AAC_DRIVERNAME);

	if ((count = aac_check_health(aac)))
		count = aac_check_health(aac);
		if (count)
			return count;
		/*
		 * Wait for all commands to complete to this specific
@@ -658,9 +831,11 @@ static int aac_eh_reset(struct scsi_cmnd* cmd)
			if (active == 0)
			__shost_for_each_device(dev, host) {
				spin_lock_irqsave(&dev->list_lock, flags);
			list_for_each_entry(command, &dev->cmd_list, list) {
				list_for_each_entry(command, &dev->cmd_list,
					list) {
					if ((command != cmd) &&
				    (command->SCp.phase == AAC_OWNER_FIRMWARE)) {
					(command->SCp.phase ==
					AAC_OWNER_FIRMWARE)) {
						active++;
						break;
					}
@@ -677,10 +852,12 @@ static int aac_eh_reset(struct scsi_cmnd* cmd)
				return SUCCESS;
			ssleep(1);
		}
	printk(KERN_ERR "%s: SCSI bus appears hung\n", AAC_DRIVERNAME);
		pr_err("%s: SCSI bus appears hung\n", AAC_DRIVERNAME);

		/*
	 * This adapter needs a blind reset, only do so for Adapters that
	 * support a register, instead of a commanded, reset.
		 * This adapter needs a blind reset, only do so for
		 * Adapters that support a register, instead of a commanded,
		 * reset.
		 */
		if (((aac->supplement_adapter_info.SupportedOptions2 &
			  AAC_OPTION_MU_RESET) ||
@@ -689,9 +866,17 @@ static int aac_eh_reset(struct scsi_cmnd* cmd)
			  aac_check_reset &&
			  ((aac_check_reset != 1) ||
			   !(aac->supplement_adapter_info.SupportedOptions2 &
	    AAC_OPTION_IGNORE_RESET)))
		aac_reset_adapter(aac, 2); /* Bypass wait for command quiesce */
	return SUCCESS; /* Cause an immediate retry of the command with a ten second delay after successful tur */
			    AAC_OPTION_IGNORE_RESET))) {
			/* Bypass wait for command quiesce */
			aac_reset_adapter(aac, 2);
		}
		ret = SUCCESS;
	}
	/*
	 * Cause an immediate retry of the command with a ten second delay
	 * after successful tur
	 */
	return ret;
}

/**
+29 −4
Original line number Diff line number Diff line
@@ -497,10 +497,35 @@ static int aac_src_deliver_message(struct fib *fib)
			vector_no = fib->vector_no;

		if (native_hba) {
			((struct aac_hba_cmd_req *)fib->hw_fib_va)->reply_qid
			if (fib->flags & FIB_CONTEXT_FLAG_NATIVE_HBA_TMF) {
				struct aac_hba_tm_req *tm_req;

				tm_req = (struct aac_hba_tm_req *)
						fib->hw_fib_va;
				if (tm_req->iu_type ==
					HBA_IU_TYPE_SCSI_TM_REQ) {
					((struct aac_hba_tm_req *)
						fib->hw_fib_va)->reply_qid
							= vector_no;
			((struct aac_hba_cmd_req *)fib->hw_fib_va)->request_id
					((struct aac_hba_tm_req *)
						fib->hw_fib_va)->request_id
							+= (vector_no << 16);
				} else {
					((struct aac_hba_reset_req *)
						fib->hw_fib_va)->reply_qid
							= vector_no;
					((struct aac_hba_reset_req *)
						fib->hw_fib_va)->request_id
							+= (vector_no << 16);
				}
			} else {
				((struct aac_hba_cmd_req *)
					fib->hw_fib_va)->reply_qid
						= vector_no;
				((struct aac_hba_cmd_req *)
					fib->hw_fib_va)->request_id
						+= (vector_no << 16);
			}
		} else {
			fib->hw_fib_va->header.Handle += (vector_no << 16);
		}