diff options
Diffstat (limited to 'drivers/scsi/mpt2sas/mpt2sas_scsih.c')
| -rw-r--r-- | drivers/scsi/mpt2sas/mpt2sas_scsih.c | 1055 | 
1 files changed, 646 insertions, 409 deletions
| diff --git a/drivers/scsi/mpt2sas/mpt2sas_scsih.c b/drivers/scsi/mpt2sas/mpt2sas_scsih.c index be171ed682e..c5ff26a2a51 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_scsih.c +++ b/drivers/scsi/mpt2sas/mpt2sas_scsih.c @@ -2,7 +2,7 @@   * Scsi Host Layer for MPT (Message Passing Technology) based controllers   *   * This code is based on drivers/scsi/mpt2sas/mpt2_scsih.c - * Copyright (C) 2007-2009  LSI Corporation + * Copyright (C) 2007-2010  LSI Corporation   *  (mailto:DL-MPTFusionLinux@lsi.com)   *   * This program is free software; you can redistribute it and/or @@ -52,6 +52,7 @@  #include <linux/delay.h>  #include <linux/pci.h>  #include <linux/interrupt.h> +#include <linux/aer.h>  #include <linux/raid_class.h>  #include <linux/slab.h> @@ -109,14 +110,16 @@ struct sense_info {  }; +#define MPT2SAS_RESCAN_AFTER_HOST_RESET (0xFFFF) +  /**   * struct fw_event_work - firmware event struct   * @list: link list framework   * @work: work object (ioc->fault_reset_work_q) + * @cancel_pending_work: flag set during reset handling   * @ioc: per adapter object   * @VF_ID: virtual function id   * @VP_ID: virtual port id - * @host_reset_handling: handling events during host reset   * @ignore: flag meaning this event has been marked to ignore   * @event: firmware event MPI2_EVENT_XXX defined in mpt2_ioc.h   * @event_data: reply event data payload follows @@ -125,11 +128,11 @@ struct sense_info {   */  struct fw_event_work {  	struct list_head 	list; -	struct work_struct	work; +	u8			cancel_pending_work; +	struct delayed_work	delayed_work;  	struct MPT2SAS_ADAPTER *ioc;  	u8			VF_ID;  	u8			VP_ID; -	u8			host_reset_handling;  	u8			ignore;  	u16			event;  	void			*event_data; @@ -482,27 +485,17 @@ struct _sas_device *  mpt2sas_scsih_sas_device_find_by_sas_address(struct MPT2SAS_ADAPTER *ioc,      u64 sas_address)  { -	struct _sas_device *sas_device, *r; +	struct _sas_device *sas_device; -	r = NULL; -	/* check the sas_device_init_list */ -	list_for_each_entry(sas_device, &ioc->sas_device_init_list, -	    list) { -		if (sas_device->sas_address != sas_address) -			continue; -		r = sas_device; -		goto out; -	} +	list_for_each_entry(sas_device, &ioc->sas_device_list, list) +		if (sas_device->sas_address == sas_address) +			return sas_device; -	/* then check the sas_device_list */ -	list_for_each_entry(sas_device, &ioc->sas_device_list, list) { -		if (sas_device->sas_address != sas_address) -			continue; -		r = sas_device; -		goto out; -	} - out: -	return r; +	list_for_each_entry(sas_device, &ioc->sas_device_init_list, list) +		if (sas_device->sas_address == sas_address) +			return sas_device; + +	return NULL;  }  /** @@ -517,28 +510,17 @@ mpt2sas_scsih_sas_device_find_by_sas_address(struct MPT2SAS_ADAPTER *ioc,  static struct _sas_device *  _scsih_sas_device_find_by_handle(struct MPT2SAS_ADAPTER *ioc, u16 handle)  { -	struct _sas_device *sas_device, *r; +	struct _sas_device *sas_device; -	r = NULL; -	if (ioc->wait_for_port_enable_to_complete) { -		list_for_each_entry(sas_device, &ioc->sas_device_init_list, -		    list) { -			if (sas_device->handle != handle) -				continue; -			r = sas_device; -			goto out; -		} -	} else { -		list_for_each_entry(sas_device, &ioc->sas_device_list, list) { -			if (sas_device->handle != handle) -				continue; -			r = sas_device; -			goto out; -		} -	} +	list_for_each_entry(sas_device, &ioc->sas_device_list, list) +		if (sas_device->handle == handle) +			return sas_device; - out: -	return r; +	list_for_each_entry(sas_device, &ioc->sas_device_init_list, list) +		if (sas_device->handle == handle) +			return sas_device; + +	return NULL;  }  /** @@ -555,10 +537,15 @@ _scsih_sas_device_remove(struct MPT2SAS_ADAPTER *ioc,  {  	unsigned long flags; +	if (!sas_device) +		return; +  	spin_lock_irqsave(&ioc->sas_device_lock, flags); -	list_del(&sas_device->list); -	memset(sas_device, 0, sizeof(struct _sas_device)); -	kfree(sas_device); +	if (mpt2sas_scsih_sas_device_find_by_sas_address(ioc, +	    sas_device->sas_address)) { +		list_del(&sas_device->list); +		kfree(sas_device); +	}  	spin_unlock_irqrestore(&ioc->sas_device_lock, flags);  } @@ -988,7 +975,7 @@ _scsih_build_scatter_gather(struct MPT2SAS_ADAPTER *ioc,  	u32 chain_offset;  	u32 chain_length;  	u32 chain_flags; -	u32 sges_left; +	int sges_left;  	u32 sges_in_segment;  	u32 sgl_flags;  	u32 sgl_flags_last_element; @@ -1009,7 +996,7 @@ _scsih_build_scatter_gather(struct MPT2SAS_ADAPTER *ioc,  	sg_scmd = scsi_sglist(scmd);  	sges_left = scsi_dma_map(scmd); -	if (!sges_left) { +	if (sges_left < 0) {  		sdev_printk(KERN_ERR, scmd->device, "pci_map_sg"  		" failed: request for %d bytes!\n", scsi_bufflen(scmd));  		return -ENOMEM; @@ -1395,7 +1382,7 @@ _scsih_display_sata_capabilities(struct MPT2SAS_ADAPTER *ioc,  	}  	flags = le16_to_cpu(sas_device_pg0.Flags); -	device_info = le16_to_cpu(sas_device_pg0.DeviceInfo); +	device_info = le32_to_cpu(sas_device_pg0.DeviceInfo);  	sdev_printk(KERN_INFO, sdev,  	    "atapi(%s), ncq(%s), asyn_notify(%s), smart(%s), fua(%s), " @@ -1963,65 +1950,78 @@ mpt2sas_scsih_clear_tm_flag(struct MPT2SAS_ADAPTER *ioc, u16 handle)  	}  } +  /**   * mpt2sas_scsih_issue_tm - main routine for sending tm requests   * @ioc: per adapter struct   * @device_handle: device handle + * @channel: the channel assigned by the OS + * @id: the id assigned by the OS   * @lun: lun number   * @type: MPI2_SCSITASKMGMT_TASKTYPE__XXX (defined in mpi2_init.h)   * @smid_task: smid assigned to the task   * @timeout: timeout in seconds - * Context: The calling function needs to acquire the tm_cmds.mutex + * Context: user   *   * A generic API for sending task management requests to firmware.   * - * The ioc->tm_cmds.status flag should be MPT2_CMD_NOT_USED before calling - * this API. - *   * The callback index is set inside `ioc->tm_cb_idx`.   * - * Return nothing. + * Return SUCCESS or FAILED.   */ -void -mpt2sas_scsih_issue_tm(struct MPT2SAS_ADAPTER *ioc, u16 handle, uint lun, -    u8 type, u16 smid_task, ulong timeout) +int +mpt2sas_scsih_issue_tm(struct MPT2SAS_ADAPTER *ioc, u16 handle, uint channel, +    uint id, uint lun, u8 type, u16 smid_task, ulong timeout, +    struct scsi_cmnd *scmd)  {  	Mpi2SCSITaskManagementRequest_t *mpi_request;  	Mpi2SCSITaskManagementReply_t *mpi_reply;  	u16 smid = 0;  	u32 ioc_state;  	unsigned long timeleft; +	struct scsi_cmnd *scmd_lookup; +	int rc; +	mutex_lock(&ioc->tm_cmds.mutex);  	if (ioc->tm_cmds.status != MPT2_CMD_NOT_USED) {  		printk(MPT2SAS_INFO_FMT "%s: tm_cmd busy!!!\n",  		    __func__, ioc->name); -		return; +		rc = FAILED; +		goto err_out;  	} -	if (ioc->shost_recovery) { +	if (ioc->shost_recovery || ioc->remove_host) {  		printk(MPT2SAS_INFO_FMT "%s: host reset in progress!\n",  		    __func__, ioc->name); -		return; +		rc = FAILED; +		goto err_out;  	}  	ioc_state = mpt2sas_base_get_iocstate(ioc, 0);  	if (ioc_state & MPI2_DOORBELL_USED) {  		dhsprintk(ioc, printk(MPT2SAS_DEBUG_FMT "unexpected doorbell "  		    "active!\n", ioc->name)); -		goto issue_host_reset; +		mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, +		    FORCE_BIG_HAMMER); +		rc = SUCCESS; +		goto err_out;  	}  	if ((ioc_state & MPI2_IOC_STATE_MASK) == MPI2_IOC_STATE_FAULT) {  		mpt2sas_base_fault_info(ioc, ioc_state &  		    MPI2_DOORBELL_DATA_MASK); -		goto issue_host_reset; +		mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, +		    FORCE_BIG_HAMMER); +		rc = SUCCESS; +		goto err_out;  	}  	smid = mpt2sas_base_get_smid_hpr(ioc, ioc->tm_cb_idx);  	if (!smid) {  		printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n",  		    ioc->name, __func__); -		return; +		rc = FAILED; +		goto err_out;  	}  	dtmprintk(ioc, printk(MPT2SAS_INFO_FMT "sending tm: handle(0x%04x)," @@ -2035,21 +2035,24 @@ mpt2sas_scsih_issue_tm(struct MPT2SAS_ADAPTER *ioc, u16 handle, uint lun,  	mpi_request->DevHandle = cpu_to_le16(handle);  	mpi_request->TaskType = type;  	mpi_request->TaskMID = cpu_to_le16(smid_task); -	mpi_request->VP_ID = 0;  /* TODO */ -	mpi_request->VF_ID = 0;  	int_to_scsilun(lun, (struct scsi_lun *)mpi_request->LUN);  	mpt2sas_scsih_set_tm_flag(ioc, handle);  	init_completion(&ioc->tm_cmds.done);  	mpt2sas_base_put_smid_hi_priority(ioc, smid);  	timeleft = wait_for_completion_timeout(&ioc->tm_cmds.done, timeout*HZ); -	mpt2sas_scsih_clear_tm_flag(ioc, handle);  	if (!(ioc->tm_cmds.status & MPT2_CMD_COMPLETE)) {  		printk(MPT2SAS_ERR_FMT "%s: timeout\n",  		    ioc->name, __func__);  		_debug_dump_mf(mpi_request,  		    sizeof(Mpi2SCSITaskManagementRequest_t)/4); -		if (!(ioc->tm_cmds.status & MPT2_CMD_RESET)) -			goto issue_host_reset; +		if (!(ioc->tm_cmds.status & MPT2_CMD_RESET)) { +			mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, +			    FORCE_BIG_HAMMER); +			rc = SUCCESS; +			ioc->tm_cmds.status = MPT2_CMD_NOT_USED; +			mpt2sas_scsih_clear_tm_flag(ioc, handle); +			goto err_out; +		}  	}  	if (ioc->tm_cmds.status & MPT2_CMD_REPLY_VALID) { @@ -2059,12 +2062,57 @@ mpt2sas_scsih_issue_tm(struct MPT2SAS_ADAPTER *ioc, u16 handle, uint lun,  		    ioc->name, le16_to_cpu(mpi_reply->IOCStatus),  		    le32_to_cpu(mpi_reply->IOCLogInfo),  		    le32_to_cpu(mpi_reply->TerminationCount))); -		if (ioc->logging_level & MPT_DEBUG_TM) +		if (ioc->logging_level & MPT_DEBUG_TM) {  			_scsih_response_code(ioc, mpi_reply->ResponseCode); +			if (mpi_reply->IOCStatus) +				_debug_dump_mf(mpi_request, +				    sizeof(Mpi2SCSITaskManagementRequest_t)/4); +		}  	} -	return; - issue_host_reset: -	mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, FORCE_BIG_HAMMER); + +	/* sanity check: +	 * Check to see the commands were terminated. +	 * This is only needed for eh callbacks, hence the scmd check. +	 */ +	rc = FAILED; +	if (scmd == NULL) +		goto bypass_sanity_checks; +	switch (type) { +	case MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK: +		scmd_lookup = _scsih_scsi_lookup_get(ioc, smid_task); +		if (scmd_lookup && (scmd_lookup->serial_number == +		    scmd->serial_number)) +			rc = FAILED; +		else +			rc = SUCCESS; +		break; + +	case MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET: +		if (_scsih_scsi_lookup_find_by_target(ioc, id, channel)) +			rc = FAILED; +		else +			rc = SUCCESS; +		break; + +	case MPI2_SCSITASKMGMT_TASKTYPE_LOGICAL_UNIT_RESET: +		if (_scsih_scsi_lookup_find_by_lun(ioc, id, lun, channel)) +			rc = FAILED; +		else +			rc = SUCCESS; +		break; +	} + + bypass_sanity_checks: + +	mpt2sas_scsih_clear_tm_flag(ioc, handle); +	ioc->tm_cmds.status = MPT2_CMD_NOT_USED; +	mutex_unlock(&ioc->tm_cmds.mutex); + +	return rc; + + err_out: +	mutex_unlock(&ioc->tm_cmds.mutex); +	return rc;  }  /** @@ -2081,7 +2129,6 @@ _scsih_abort(struct scsi_cmnd *scmd)  	u16 smid;  	u16 handle;  	int r; -	struct scsi_cmnd *scmd_lookup;  	printk(MPT2SAS_INFO_FMT "attempting task abort! scmd(%p)\n",  	    ioc->name, scmd); @@ -2116,19 +2163,10 @@ _scsih_abort(struct scsi_cmnd *scmd)  	mpt2sas_halt_firmware(ioc); -	mutex_lock(&ioc->tm_cmds.mutex);  	handle = sas_device_priv_data->sas_target->handle; -	mpt2sas_scsih_issue_tm(ioc, handle, sas_device_priv_data->lun, -	    MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK, smid, 30); - -	/* sanity check - see whether command actually completed */ -	scmd_lookup = _scsih_scsi_lookup_get(ioc, smid); -	if (scmd_lookup && (scmd_lookup->serial_number == scmd->serial_number)) -		r = FAILED; -	else -		r = SUCCESS; -	ioc->tm_cmds.status = MPT2_CMD_NOT_USED; -	mutex_unlock(&ioc->tm_cmds.mutex); +	r = mpt2sas_scsih_issue_tm(ioc, handle, scmd->device->channel, +	    scmd->device->id, scmd->device->lun, +	    MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK, smid, 30, scmd);   out:  	printk(MPT2SAS_INFO_FMT "task abort: %s scmd(%p)\n", @@ -2185,22 +2223,9 @@ _scsih_dev_reset(struct scsi_cmnd *scmd)  		goto out;  	} -	mutex_lock(&ioc->tm_cmds.mutex); -	mpt2sas_scsih_issue_tm(ioc, handle, 0, -	    MPI2_SCSITASKMGMT_TASKTYPE_LOGICAL_UNIT_RESET, scmd->device->lun, -	    30); - -	/* -	 *  sanity check see whether all commands to this device been -	 *  completed -	 */ -	if (_scsih_scsi_lookup_find_by_lun(ioc, scmd->device->id, -	    scmd->device->lun, scmd->device->channel)) -		r = FAILED; -	else -		r = SUCCESS; -	ioc->tm_cmds.status = MPT2_CMD_NOT_USED; -	mutex_unlock(&ioc->tm_cmds.mutex); +	r = mpt2sas_scsih_issue_tm(ioc, handle, scmd->device->channel, +	    scmd->device->id, scmd->device->lun, +	    MPI2_SCSITASKMGMT_TASKTYPE_LOGICAL_UNIT_RESET, 0, 30, scmd);   out:  	printk(MPT2SAS_INFO_FMT "device reset: %s scmd(%p)\n", @@ -2257,21 +2282,9 @@ _scsih_target_reset(struct scsi_cmnd *scmd)  		goto out;  	} -	mutex_lock(&ioc->tm_cmds.mutex); -	mpt2sas_scsih_issue_tm(ioc, handle, 0, -	    MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET, 0, 30); - -	/* -	 *  sanity check see whether all commands to this target been -	 *  completed -	 */ -	if (_scsih_scsi_lookup_find_by_target(ioc, scmd->device->id, -	    scmd->device->channel)) -		r = FAILED; -	else -		r = SUCCESS; -	ioc->tm_cmds.status = MPT2_CMD_NOT_USED; -	mutex_unlock(&ioc->tm_cmds.mutex); +	r = mpt2sas_scsih_issue_tm(ioc, handle, scmd->device->channel, +	    scmd->device->id, 0, MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET, 0, +	    30, scmd);   out:  	printk(MPT2SAS_INFO_FMT "target reset: %s scmd(%p)\n", @@ -2325,8 +2338,9 @@ _scsih_fw_event_add(struct MPT2SAS_ADAPTER *ioc, struct fw_event_work *fw_event)  	spin_lock_irqsave(&ioc->fw_event_lock, flags);  	list_add_tail(&fw_event->list, &ioc->fw_event_list); -	INIT_WORK(&fw_event->work, _firmware_event_work); -	queue_work(ioc->firmware_event_thread, &fw_event->work); +	INIT_DELAYED_WORK(&fw_event->delayed_work, _firmware_event_work); +	queue_delayed_work(ioc->firmware_event_thread, +	    &fw_event->delayed_work, 0);  	spin_unlock_irqrestore(&ioc->fw_event_lock, flags);  } @@ -2353,61 +2367,53 @@ _scsih_fw_event_free(struct MPT2SAS_ADAPTER *ioc, struct fw_event_work  	spin_unlock_irqrestore(&ioc->fw_event_lock, flags);  } +  /** - * _scsih_fw_event_add - requeue an event + * _scsih_queue_rescan - queue a topology rescan from user context   * @ioc: per adapter object - * @fw_event: object describing the event - * Context: This function will acquire ioc->fw_event_lock.   *   * Return nothing.   */  static void -_scsih_fw_event_requeue(struct MPT2SAS_ADAPTER *ioc, struct fw_event_work -    *fw_event, unsigned long delay) +_scsih_queue_rescan(struct MPT2SAS_ADAPTER *ioc)  { -	unsigned long flags; -	if (ioc->firmware_event_thread == NULL) -		return; +	struct fw_event_work *fw_event; -	spin_lock_irqsave(&ioc->fw_event_lock, flags); -	queue_work(ioc->firmware_event_thread, &fw_event->work); -	spin_unlock_irqrestore(&ioc->fw_event_lock, flags); +	if (ioc->wait_for_port_enable_to_complete) +		return; +	fw_event = kzalloc(sizeof(struct fw_event_work), GFP_ATOMIC); +	if (!fw_event) +		return; +	fw_event->event = MPT2SAS_RESCAN_AFTER_HOST_RESET; +	fw_event->ioc = ioc; +	_scsih_fw_event_add(ioc, fw_event);  }  /** - * _scsih_fw_event_off - turn flag off preventing event handling + * _scsih_fw_event_cleanup_queue - cleanup event queue   * @ioc: per adapter object   * - * Used to prevent handling of firmware events during adapter reset - * driver unload. + * Walk the firmware event queue, either killing timers, or waiting + * for outstanding events to complete   *   * Return nothing.   */  static void -_scsih_fw_event_off(struct MPT2SAS_ADAPTER *ioc) +_scsih_fw_event_cleanup_queue(struct MPT2SAS_ADAPTER *ioc)  { -	unsigned long flags; +	struct fw_event_work *fw_event, *next; -	spin_lock_irqsave(&ioc->fw_event_lock, flags); -	ioc->fw_events_off = 1; -	spin_unlock_irqrestore(&ioc->fw_event_lock, flags); - -} - -/** - * _scsih_fw_event_on - turn flag on allowing firmware event handling - * @ioc: per adapter object - * - * Returns nothing. - */ -static void -_scsih_fw_event_on(struct MPT2SAS_ADAPTER *ioc) -{ -	unsigned long flags; +	if (list_empty(&ioc->fw_event_list) || +	     !ioc->firmware_event_thread || in_interrupt()) +		return; -	spin_lock_irqsave(&ioc->fw_event_lock, flags); -	ioc->fw_events_off = 0; -	spin_unlock_irqrestore(&ioc->fw_event_lock, flags); +	list_for_each_entry_safe(fw_event, next, &ioc->fw_event_list, list) { +		if (cancel_delayed_work(&fw_event->delayed_work)) { +			_scsih_fw_event_free(ioc, fw_event); +			continue; +		} +		fw_event->cancel_pending_work = 1; +	}  }  /** @@ -2571,25 +2577,24 @@ static void  _scsih_tm_tr_send(struct MPT2SAS_ADAPTER *ioc, u16 handle)  {  	Mpi2SCSITaskManagementRequest_t *mpi_request; -	struct MPT2SAS_TARGET *sas_target_priv_data;  	u16 smid;  	struct _sas_device *sas_device;  	unsigned long flags;  	struct _tr_list *delayed_tr; -	if (ioc->shost_recovery) { -		printk(MPT2SAS_INFO_FMT "%s: host reset in progress!\n", -		    __func__, ioc->name); +	if (ioc->shost_recovery || ioc->remove_host) { +		dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: host reset in " +		   "progress!\n", __func__, ioc->name));  		return;  	}  	spin_lock_irqsave(&ioc->sas_device_lock, flags);  	sas_device = _scsih_sas_device_find_by_handle(ioc, handle); -	spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - -	/* skip is hidden raid component */ -	if (sas_device && sas_device->hidden_raid_component) +	if (sas_device && sas_device->hidden_raid_component) { +		spin_unlock_irqrestore(&ioc->sas_device_lock, flags);  		return; +	} +	spin_unlock_irqrestore(&ioc->sas_device_lock, flags);  	smid = mpt2sas_base_get_smid_hpr(ioc, ioc->tm_tr_cb_idx);  	if (!smid) { @@ -2598,36 +2603,16 @@ _scsih_tm_tr_send(struct MPT2SAS_ADAPTER *ioc, u16 handle)  			return;  		INIT_LIST_HEAD(&delayed_tr->list);  		delayed_tr->handle = handle; -		delayed_tr->state = MPT2SAS_REQ_SAS_CNTRL; -		list_add_tail(&delayed_tr->list, -		    &ioc->delayed_tr_list); -		if (sas_device && sas_device->starget) { -			dewtprintk(ioc, starget_printk(KERN_INFO, -			    sas_device->starget, "DELAYED:tr:handle(0x%04x), " -			    "(open)\n", handle)); -		} else { -			dewtprintk(ioc, printk(MPT2SAS_INFO_FMT -			    "DELAYED:tr:handle(0x%04x), (open)\n", -			    ioc->name, handle)); -		} -		return; -	} - -	if (sas_device) { -		sas_device->state |= MPTSAS_STATE_TR_SEND; -		sas_device->state |= MPT2SAS_REQ_SAS_CNTRL; -		if (sas_device->starget && sas_device->starget->hostdata) { -			sas_target_priv_data = sas_device->starget->hostdata; -			sas_target_priv_data->tm_busy = 1; -			dewtprintk(ioc, starget_printk(KERN_INFO, -			    sas_device->starget, "tr:handle(0x%04x), (open)\n", -			    handle)); -		} -	} else { +		list_add_tail(&delayed_tr->list, &ioc->delayed_tr_list);  		dewtprintk(ioc, printk(MPT2SAS_INFO_FMT -		    "tr:handle(0x%04x), (open)\n", ioc->name, handle)); +		    "DELAYED:tr:handle(0x%04x), (open)\n", +		    ioc->name, handle)); +		return;  	} +	dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "tr_send:handle(0x%04x), " +	    "(open), smid(%d), cb(%d)\n", ioc->name, handle, smid, +	    ioc->tm_tr_cb_idx));  	mpi_request = mpt2sas_base_get_msg_frame(ioc, smid);  	memset(mpi_request, 0, sizeof(Mpi2SCSITaskManagementRequest_t));  	mpi_request->Function = MPI2_FUNCTION_SCSI_TASK_MGMT; @@ -2657,35 +2642,15 @@ static u8  _scsih_sas_control_complete(struct MPT2SAS_ADAPTER *ioc, u16 smid,      u8 msix_index, u32 reply)  { -	unsigned long flags; -	u16 handle; -	struct _sas_device *sas_device;  	Mpi2SasIoUnitControlReply_t *mpi_reply =  	    mpt2sas_base_get_reply_virt_addr(ioc, reply); -	handle = le16_to_cpu(mpi_reply->DevHandle); - -	spin_lock_irqsave(&ioc->sas_device_lock, flags); -	sas_device = _scsih_sas_device_find_by_handle(ioc, handle); -	spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - -	if (sas_device) { -		sas_device->state |= MPTSAS_STATE_CNTRL_COMPLETE; -		if (sas_device->starget) -			dewtprintk(ioc, starget_printk(KERN_INFO, -			    sas_device->starget, -			    "sc_complete:handle(0x%04x), " -			    "ioc_status(0x%04x), loginfo(0x%08x)\n", -			    handle, le16_to_cpu(mpi_reply->IOCStatus), -			    le32_to_cpu(mpi_reply->IOCLogInfo))); -	} else { -		dewtprintk(ioc, printk(MPT2SAS_INFO_FMT -		    "sc_complete:handle(0x%04x), " -		    "ioc_status(0x%04x), loginfo(0x%08x)\n", -		    ioc->name, handle, le16_to_cpu(mpi_reply->IOCStatus), -		    le32_to_cpu(mpi_reply->IOCLogInfo))); -	} - +	dewtprintk(ioc, printk(MPT2SAS_INFO_FMT +	    "sc_complete:handle(0x%04x), (open) " +	    "smid(%d), ioc_status(0x%04x), loginfo(0x%08x)\n", +	    ioc->name, le16_to_cpu(mpi_reply->DevHandle), smid, +	    le16_to_cpu(mpi_reply->IOCStatus), +	    le32_to_cpu(mpi_reply->IOCLogInfo)));  	return 1;  } @@ -2709,87 +2674,63 @@ static u8  _scsih_tm_tr_complete(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index,      u32 reply)  { -	unsigned long flags;  	u16 handle; -	struct _sas_device *sas_device; +	Mpi2SCSITaskManagementRequest_t *mpi_request_tm;  	Mpi2SCSITaskManagementReply_t *mpi_reply =  	    mpt2sas_base_get_reply_virt_addr(ioc, reply);  	Mpi2SasIoUnitControlRequest_t *mpi_request;  	u16 smid_sas_ctrl; -	struct MPT2SAS_TARGET *sas_target_priv_data;  	struct _tr_list *delayed_tr; -	u8 rc; -	handle = le16_to_cpu(mpi_reply->DevHandle); -	spin_lock_irqsave(&ioc->sas_device_lock, flags); -	sas_device = _scsih_sas_device_find_by_handle(ioc, handle); -	spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - -	if (sas_device) { -		sas_device->state |= MPTSAS_STATE_TR_COMPLETE; -		if (sas_device->starget) { -			dewtprintk(ioc, starget_printk(KERN_INFO, -			    sas_device->starget, "tr_complete:handle(0x%04x), " -			    "(%s) ioc_status(0x%04x), loginfo(0x%08x), " -			    "completed(%d)\n", sas_device->handle, -			    (sas_device->state & MPT2SAS_REQ_SAS_CNTRL) ? -			    "open" : "active", -			    le16_to_cpu(mpi_reply->IOCStatus), -			    le32_to_cpu(mpi_reply->IOCLogInfo), -			    le32_to_cpu(mpi_reply->TerminationCount))); -			if (sas_device->starget->hostdata) { -				sas_target_priv_data = -				    sas_device->starget->hostdata; -				sas_target_priv_data->tm_busy = 0; -			} -		} -	} else { -		dewtprintk(ioc, printk(MPT2SAS_INFO_FMT -		    "tr_complete:handle(0x%04x), (open) ioc_status(0x%04x), " -		    "loginfo(0x%08x), completed(%d)\n", ioc->name, -		    handle, le16_to_cpu(mpi_reply->IOCStatus), -		    le32_to_cpu(mpi_reply->IOCLogInfo), -		    le32_to_cpu(mpi_reply->TerminationCount))); +	if (ioc->shost_recovery || ioc->remove_host) { +		dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: host reset in " +		   "progress!\n", __func__, ioc->name)); +		return 1;  	} -	if (!list_empty(&ioc->delayed_tr_list)) { -		delayed_tr = list_entry(ioc->delayed_tr_list.next, -		    struct _tr_list, list); -		mpt2sas_base_free_smid(ioc, smid); -		if (delayed_tr->state & MPT2SAS_REQ_SAS_CNTRL) -			_scsih_tm_tr_send(ioc, delayed_tr->handle); -		list_del(&delayed_tr->list); -		kfree(delayed_tr); -		rc = 0; /* tells base_interrupt not to free mf */ -	} else -		rc = 1; - -	if (sas_device && !(sas_device->state & MPT2SAS_REQ_SAS_CNTRL)) -		return rc; - -	if (ioc->shost_recovery) { -		printk(MPT2SAS_INFO_FMT "%s: host reset in progress!\n", -		    __func__, ioc->name); -		return rc; +	mpi_request_tm = mpt2sas_base_get_msg_frame(ioc, smid); +	handle = le16_to_cpu(mpi_request_tm->DevHandle); +	if (handle != le16_to_cpu(mpi_reply->DevHandle)) { +		dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "spurious interrupt: " +		    "handle(0x%04x:0x%04x), smid(%d)!!!\n", ioc->name, handle, +		    le16_to_cpu(mpi_reply->DevHandle), smid)); +		return 0;  	} +	dewtprintk(ioc, printk(MPT2SAS_INFO_FMT +	    "tr_complete:handle(0x%04x), (open) smid(%d), ioc_status(0x%04x), " +	    "loginfo(0x%08x), completed(%d)\n", ioc->name, +	    handle, smid, le16_to_cpu(mpi_reply->IOCStatus), +	    le32_to_cpu(mpi_reply->IOCLogInfo), +	    le32_to_cpu(mpi_reply->TerminationCount))); +  	smid_sas_ctrl = mpt2sas_base_get_smid(ioc, ioc->tm_sas_control_cb_idx);  	if (!smid_sas_ctrl) {  		printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n",  		    ioc->name, __func__); -		return rc; +		return 1;  	} -	if (sas_device) -		sas_device->state |= MPTSAS_STATE_CNTRL_SEND; - +	dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "sc_send:handle(0x%04x), " +	    "(open), smid(%d), cb(%d)\n", ioc->name, handle, smid_sas_ctrl, +	    ioc->tm_sas_control_cb_idx));  	mpi_request = mpt2sas_base_get_msg_frame(ioc, smid_sas_ctrl);  	memset(mpi_request, 0, sizeof(Mpi2SasIoUnitControlRequest_t));  	mpi_request->Function = MPI2_FUNCTION_SAS_IO_UNIT_CONTROL;  	mpi_request->Operation = MPI2_SAS_OP_REMOVE_DEVICE; -	mpi_request->DevHandle = mpi_reply->DevHandle; +	mpi_request->DevHandle = mpi_request_tm->DevHandle;  	mpt2sas_base_put_smid_default(ioc, smid_sas_ctrl); -	return rc; + +	if (!list_empty(&ioc->delayed_tr_list)) { +		delayed_tr = list_entry(ioc->delayed_tr_list.next, +		    struct _tr_list, list); +		mpt2sas_base_free_smid(ioc, smid); +		_scsih_tm_tr_send(ioc, delayed_tr->handle); +		list_del(&delayed_tr->list); +		kfree(delayed_tr); +		return 0; /* tells base_interrupt not to free mf */ +	} +	return 1;  }  /** @@ -3021,25 +2962,32 @@ _scsih_qcmd(struct scsi_cmnd *scmd, void (*done)(struct scsi_cmnd *))  	scmd->scsi_done = done;  	sas_device_priv_data = scmd->device->hostdata; -	if (!sas_device_priv_data) { +	if (!sas_device_priv_data || !sas_device_priv_data->sas_target) {  		scmd->result = DID_NO_CONNECT << 16;  		scmd->scsi_done(scmd);  		return 0;  	}  	sas_target_priv_data = sas_device_priv_data->sas_target; -	if (!sas_target_priv_data || sas_target_priv_data->handle == -	    MPT2SAS_INVALID_DEVICE_HANDLE || sas_target_priv_data->deleted) { +	/* invalid device handle */ +	if (sas_target_priv_data->handle == MPT2SAS_INVALID_DEVICE_HANDLE) {  		scmd->result = DID_NO_CONNECT << 16;  		scmd->scsi_done(scmd);  		return 0;  	} -	/* see if we are busy with task managment stuff */ -	if (sas_device_priv_data->block || sas_target_priv_data->tm_busy) -		return SCSI_MLQUEUE_DEVICE_BUSY; -	else if (ioc->shost_recovery || ioc->ioc_link_reset_in_progress) +	/* host recovery or link resets sent via IOCTLs */ +	if (ioc->shost_recovery || ioc->ioc_link_reset_in_progress)  		return SCSI_MLQUEUE_HOST_BUSY; +	/* device busy with task managment */ +	else if (sas_device_priv_data->block || sas_target_priv_data->tm_busy) +		return SCSI_MLQUEUE_DEVICE_BUSY; +	/* device has been deleted */ +	else if (sas_target_priv_data->deleted) { +		scmd->result = DID_NO_CONNECT << 16; +		scmd->scsi_done(scmd); +		return 0; +	}  	if (scmd->sc_data_direction == DMA_FROM_DEVICE)  		mpi_control = MPI2_SCSIIO_CONTROL_READ; @@ -3110,8 +3058,11 @@ _scsih_qcmd(struct scsi_cmnd *scmd, void (*done)(struct scsi_cmnd *))  		}  	} -	mpt2sas_base_put_smid_scsi_io(ioc, smid, -	    sas_device_priv_data->sas_target->handle); +	if (likely(mpi_request->Function == MPI2_FUNCTION_SCSI_IO_REQUEST)) +		mpt2sas_base_put_smid_scsi_io(ioc, smid, +		    sas_device_priv_data->sas_target->handle); +	else +		mpt2sas_base_put_smid_default(ioc, smid);  	return 0;   out: @@ -3301,8 +3252,8 @@ _scsih_scsi_ioc_info(struct MPT2SAS_ADAPTER *ioc, struct scsi_cmnd *scmd,  		struct sense_info data;  		_scsih_normalize_sense(scmd->sense_buffer, &data);  		printk(MPT2SAS_WARN_FMT "\t[sense_key,asc,ascq]: " -		    "[0x%02x,0x%02x,0x%02x]\n", ioc->name, data.skey, -		    data.asc, data.ascq); +		    "[0x%02x,0x%02x,0x%02x], count(%d)\n", ioc->name, data.skey, +		    data.asc, data.ascq, le32_to_cpu(mpi_reply->SenseCount));  	}  	if (scsi_state & MPI2_SCSI_STATE_RESPONSE_INFO_VALID) { @@ -3356,7 +3307,7 @@ _scsih_smart_predicted_fault(struct MPT2SAS_ADAPTER *ioc, u16 handle)  		mpi_request.Function = MPI2_FUNCTION_SCSI_ENCLOSURE_PROCESSOR;  		mpi_request.Action = MPI2_SEP_REQ_ACTION_WRITE_STATUS;  		mpi_request.SlotStatus = -		    MPI2_SEP_REQ_SLOTSTATUS_PREDICTED_FAULT; +		    cpu_to_le32(MPI2_SEP_REQ_SLOTSTATUS_PREDICTED_FAULT);  		mpi_request.DevHandle = cpu_to_le16(handle);  		mpi_request.Flags = MPI2_SEP_REQ_FLAGS_DEVHANDLE_ADDRESS;  		if ((mpt2sas_base_scsi_enclosure_processor(ioc, &mpi_reply, @@ -4008,6 +3959,134 @@ _scsih_expander_remove(struct MPT2SAS_ADAPTER *ioc, u64 sas_address)  }  /** + * _scsih_check_access_status - check access flags + * @ioc: per adapter object + * @sas_address: sas address + * @handle: sas device handle + * @access_flags: errors returned during discovery of the device + * + * Return 0 for success, else failure + */ +static u8 +_scsih_check_access_status(struct MPT2SAS_ADAPTER *ioc, u64 sas_address, +   u16 handle, u8 access_status) +{ +	u8 rc = 1; +	char *desc = NULL; + +	switch (access_status) { +	case MPI2_SAS_DEVICE0_ASTATUS_NO_ERRORS: +	case MPI2_SAS_DEVICE0_ASTATUS_SATA_NEEDS_INITIALIZATION: +		rc = 0; +		break; +	case MPI2_SAS_DEVICE0_ASTATUS_SATA_CAPABILITY_FAILED: +		desc = "sata capability failed"; +		break; +	case MPI2_SAS_DEVICE0_ASTATUS_SATA_AFFILIATION_CONFLICT: +		desc = "sata affiliation conflict"; +		break; +	case MPI2_SAS_DEVICE0_ASTATUS_ROUTE_NOT_ADDRESSABLE: +		desc = "route not addressable"; +		break; +	case MPI2_SAS_DEVICE0_ASTATUS_SMP_ERROR_NOT_ADDRESSABLE: +		desc = "smp error not addressable"; +		break; +	case MPI2_SAS_DEVICE0_ASTATUS_DEVICE_BLOCKED: +		desc = "device blocked"; +		break; +	case MPI2_SAS_DEVICE0_ASTATUS_SATA_INIT_FAILED: +	case MPI2_SAS_DEVICE0_ASTATUS_SIF_UNKNOWN: +	case MPI2_SAS_DEVICE0_ASTATUS_SIF_AFFILIATION_CONFLICT: +	case MPI2_SAS_DEVICE0_ASTATUS_SIF_DIAG: +	case MPI2_SAS_DEVICE0_ASTATUS_SIF_IDENTIFICATION: +	case MPI2_SAS_DEVICE0_ASTATUS_SIF_CHECK_POWER: +	case MPI2_SAS_DEVICE0_ASTATUS_SIF_PIO_SN: +	case MPI2_SAS_DEVICE0_ASTATUS_SIF_MDMA_SN: +	case MPI2_SAS_DEVICE0_ASTATUS_SIF_UDMA_SN: +	case MPI2_SAS_DEVICE0_ASTATUS_SIF_ZONING_VIOLATION: +	case MPI2_SAS_DEVICE0_ASTATUS_SIF_NOT_ADDRESSABLE: +	case MPI2_SAS_DEVICE0_ASTATUS_SIF_MAX: +		desc = "sata initialization failed"; +		break; +	default: +		desc = "unknown"; +		break; +	} + +	if (!rc) +		return 0; + +	printk(MPT2SAS_ERR_FMT "discovery errors(%s): sas_address(0x%016llx), " +	    "handle(0x%04x)\n", ioc->name, desc, +	    (unsigned long long)sas_address, handle); +	return rc; +} + +static void +_scsih_check_device(struct MPT2SAS_ADAPTER *ioc, u16 handle) +{ +	Mpi2ConfigReply_t mpi_reply; +	Mpi2SasDevicePage0_t sas_device_pg0; +	struct _sas_device *sas_device; +	u32 ioc_status; +	unsigned long flags; +	u64 sas_address; +	struct scsi_target *starget; +	struct MPT2SAS_TARGET *sas_target_priv_data; +	u32 device_info; + +	if ((mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0, +	    MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle))) +		return; + +	ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & MPI2_IOCSTATUS_MASK; +	if (ioc_status != MPI2_IOCSTATUS_SUCCESS) +		return; + +	/* check if this is end device */ +	device_info = le32_to_cpu(sas_device_pg0.DeviceInfo); +	if (!(_scsih_is_end_device(device_info))) +		return; + +	spin_lock_irqsave(&ioc->sas_device_lock, flags); +	sas_address = le64_to_cpu(sas_device_pg0.SASAddress); +	sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc, +	    sas_address); + +	if (!sas_device) { +		printk(MPT2SAS_ERR_FMT "device is not present " +		    "handle(0x%04x), no sas_device!!!\n", ioc->name, handle); +		spin_unlock_irqrestore(&ioc->sas_device_lock, flags); +		return; +	} + +	if (unlikely(sas_device->handle != handle)) { +		starget = sas_device->starget; +		sas_target_priv_data = starget->hostdata; +		starget_printk(KERN_INFO, starget, "handle changed from(0x%04x)" +		   " to (0x%04x)!!!\n", sas_device->handle, handle); +		sas_target_priv_data->handle = handle; +		sas_device->handle = handle; +	} +	spin_unlock_irqrestore(&ioc->sas_device_lock, flags); + +	/* check if device is present */ +	if (!(le16_to_cpu(sas_device_pg0.Flags) & +	    MPI2_SAS_DEVICE0_FLAGS_DEVICE_PRESENT)) { +		printk(MPT2SAS_ERR_FMT "device is not present " +		    "handle(0x%04x), flags!!!\n", ioc->name, handle); +		return; +	} + +	/* check if there were any issues with discovery */ +	if (_scsih_check_access_status(ioc, sas_address, handle, +	    sas_device_pg0.AccessStatus)) +		return; +	_scsih_ublock_io_device(ioc, handle); + +} + +/**   * _scsih_add_device -  creating sas device object   * @ioc: per adapter object   * @handle: sas device handle @@ -4045,6 +4124,8 @@ _scsih_add_device(struct MPT2SAS_ADAPTER *ioc, u16 handle, u8 phy_num, u8 is_pd)  		return -1;  	} +	sas_address = le64_to_cpu(sas_device_pg0.SASAddress); +  	/* check if device is present */  	if (!(le16_to_cpu(sas_device_pg0.Flags) &  	    MPI2_SAS_DEVICE0_FLAGS_DEVICE_PRESENT)) { @@ -4055,15 +4136,10 @@ _scsih_add_device(struct MPT2SAS_ADAPTER *ioc, u16 handle, u8 phy_num, u8 is_pd)  		return -1;  	} -	/* check if there were any issus with discovery */ -	if (sas_device_pg0.AccessStatus == -	    MPI2_SAS_DEVICE0_ASTATUS_SATA_INIT_FAILED) { -		printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", -		    ioc->name, __FILE__, __LINE__, __func__); -		printk(MPT2SAS_ERR_FMT "AccessStatus = 0x%02x\n", -		    ioc->name, sas_device_pg0.AccessStatus); +	/* check if there were any issues with discovery */ +	if (_scsih_check_access_status(ioc, sas_address, handle, +	    sas_device_pg0.AccessStatus))  		return -1; -	}  	/* check if this is end device */  	device_info = le32_to_cpu(sas_device_pg0.DeviceInfo); @@ -4073,17 +4149,14 @@ _scsih_add_device(struct MPT2SAS_ADAPTER *ioc, u16 handle, u8 phy_num, u8 is_pd)  		return -1;  	} -	sas_address = le64_to_cpu(sas_device_pg0.SASAddress);  	spin_lock_irqsave(&ioc->sas_device_lock, flags);  	sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc,  	    sas_address);  	spin_unlock_irqrestore(&ioc->sas_device_lock, flags); -	if (sas_device) { -		_scsih_ublock_io_device(ioc, handle); +	if (sas_device)  		return 0; -	}  	sas_device = kzalloc(sizeof(struct _sas_device),  	    GFP_KERNEL); @@ -4126,67 +4199,38 @@ _scsih_add_device(struct MPT2SAS_ADAPTER *ioc, u16 handle, u8 phy_num, u8 is_pd)  }  /** - * _scsih_remove_device -  removing sas device object + * _scsih_remove_pd_device -  removing sas device pd object   * @ioc: per adapter object - * @sas_device: the sas_device object + * @sas_device_delete: the sas_device object   * + * For hidden raid components, we do driver-fw handshake from + * hotplug work threads.   * Return nothing.   */  static void -_scsih_remove_device(struct MPT2SAS_ADAPTER *ioc, struct _sas_device -    *sas_device) +_scsih_remove_pd_device(struct MPT2SAS_ADAPTER *ioc, struct _sas_device +    sas_device)  { -	struct MPT2SAS_TARGET *sas_target_priv_data;  	Mpi2SasIoUnitControlReply_t mpi_reply;  	Mpi2SasIoUnitControlRequest_t mpi_request; -	u16 device_handle, handle; - -	if (!sas_device) -		return; +	u16 vol_handle, handle; -	handle = sas_device->handle; +	handle = sas_device.handle;  	dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: enter: handle(0x%04x),"  	    " sas_addr(0x%016llx)\n", ioc->name, __func__, handle, -	    (unsigned long long) sas_device->sas_address)); - -	if (sas_device->starget && sas_device->starget->hostdata) { -		sas_target_priv_data = sas_device->starget->hostdata; -		sas_target_priv_data->deleted = 1; -	} - -	if (ioc->remove_host || ioc->shost_recovery || !handle) -		goto out; +	    (unsigned long long) sas_device.sas_address)); -	if ((sas_device->state & MPTSAS_STATE_TR_COMPLETE)) { -		dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "\tskip " -		   "target_reset handle(0x%04x)\n", ioc->name, -		   handle)); -		goto skip_tr; -	} - -	/* Target Reset to flush out all the outstanding IO */ -	device_handle = (sas_device->hidden_raid_component) ? -	    sas_device->volume_handle : handle; -	if (device_handle) { -		dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "issue target reset: " -		    "handle(0x%04x)\n", ioc->name, device_handle)); -		mutex_lock(&ioc->tm_cmds.mutex); -		mpt2sas_scsih_issue_tm(ioc, device_handle, 0, -		    MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET, 0, 10); -		ioc->tm_cmds.status = MPT2_CMD_NOT_USED; -		mutex_unlock(&ioc->tm_cmds.mutex); -		dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "issue target reset " -		    "done: handle(0x%04x)\n", ioc->name, device_handle)); -		if (ioc->shost_recovery) -			goto out; -	} - skip_tr: - -	if ((sas_device->state & MPTSAS_STATE_CNTRL_COMPLETE)) { -		dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "\tskip " -		   "sas_cntrl handle(0x%04x)\n", ioc->name, handle)); -		goto out; -	} +	vol_handle = sas_device.volume_handle; +	if (!vol_handle) +		return; +	dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "issue target reset: " +	    "handle(0x%04x)\n", ioc->name, vol_handle)); +	mpt2sas_scsih_issue_tm(ioc, vol_handle, 0, 0, 0, +	    MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET, 0, 30, NULL); +	dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "issue target reset " +	    "done: handle(0x%04x)\n", ioc->name, vol_handle)); +	if (ioc->shost_recovery) +		return;  	/* SAS_IO_UNIT_CNTR - send REMOVE_DEVICE */  	dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "sas_iounit: handle" @@ -4194,34 +4238,68 @@ _scsih_remove_device(struct MPT2SAS_ADAPTER *ioc, struct _sas_device  	memset(&mpi_request, 0, sizeof(Mpi2SasIoUnitControlRequest_t));  	mpi_request.Function = MPI2_FUNCTION_SAS_IO_UNIT_CONTROL;  	mpi_request.Operation = MPI2_SAS_OP_REMOVE_DEVICE; -	mpi_request.DevHandle = handle; -	mpi_request.VF_ID = 0; /* TODO */ -	mpi_request.VP_ID = 0; +	mpi_request.DevHandle = cpu_to_le16(handle);  	if ((mpt2sas_base_sas_iounit_control(ioc, &mpi_reply, -	    &mpi_request)) != 0) { +	    &mpi_request)) != 0)  		printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",  		    ioc->name, __FILE__, __LINE__, __func__); -	}  	dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "sas_iounit: ioc_status"  	    "(0x%04x), loginfo(0x%08x)\n", ioc->name,  	    le16_to_cpu(mpi_reply.IOCStatus),  	    le32_to_cpu(mpi_reply.IOCLogInfo))); - out: +	dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: exit: handle(0x%04x)," +	    " sas_addr(0x%016llx)\n", ioc->name, __func__, handle, +	    (unsigned long long) sas_device.sas_address)); +} -	_scsih_ublock_io_device(ioc, handle); +/** + * _scsih_remove_device -  removing sas device object + * @ioc: per adapter object + * @sas_device_delete: the sas_device object + * + * Return nothing. + */ +static void +_scsih_remove_device(struct MPT2SAS_ADAPTER *ioc, +    struct _sas_device *sas_device) +{ +	struct _sas_device sas_device_backup; +	struct MPT2SAS_TARGET *sas_target_priv_data; -	mpt2sas_transport_port_remove(ioc, sas_device->sas_address, -	    sas_device->sas_address_parent); +	if (!sas_device) +		return; -	printk(MPT2SAS_INFO_FMT "removing handle(0x%04x), sas_addr" -	    "(0x%016llx)\n", ioc->name, handle, -	    (unsigned long long) sas_device->sas_address); +	memcpy(&sas_device_backup, sas_device, sizeof(struct _sas_device));  	_scsih_sas_device_remove(ioc, sas_device); -	dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: exit: handle" -	    "(0x%04x)\n", ioc->name, __func__, handle)); +	dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: enter: " +	    "handle(0x%04x), sas_addr(0x%016llx)\n", ioc->name, __func__, +	    sas_device_backup.handle, (unsigned long long) +	    sas_device_backup.sas_address)); + +	if (sas_device_backup.starget && sas_device_backup.starget->hostdata) { +		sas_target_priv_data = sas_device_backup.starget->hostdata; +		sas_target_priv_data->deleted = 1; +	} + +	if (sas_device_backup.hidden_raid_component) +		_scsih_remove_pd_device(ioc, sas_device_backup); + +	_scsih_ublock_io_device(ioc, sas_device_backup.handle); + +	mpt2sas_transport_port_remove(ioc, sas_device_backup.sas_address, +	    sas_device_backup.sas_address_parent); + +	printk(MPT2SAS_INFO_FMT "removing handle(0x%04x), sas_addr" +	    "(0x%016llx)\n", ioc->name, sas_device_backup.handle, +	    (unsigned long long) sas_device_backup.sas_address); + +	dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: exit: " +	    "handle(0x%04x), sas_addr(0x%016llx)\n", ioc->name, __func__, +	    sas_device_backup.handle, (unsigned long long) +	    sas_device_backup.sas_address));  }  #ifdef CONFIG_SCSI_MPT2SAS_LOGGING @@ -4331,7 +4409,7 @@ _scsih_sas_topology_change_event(struct MPT2SAS_ADAPTER *ioc,  		_scsih_sas_topology_change_event_debug(ioc, event_data);  #endif -	if (ioc->shost_recovery) +	if (ioc->shost_recovery || ioc->remove_host)  		return;  	if (!ioc->sas_hba.num_phys) @@ -4370,7 +4448,7 @@ _scsih_sas_topology_change_event(struct MPT2SAS_ADAPTER *ioc,  			    "expander event\n", ioc->name));  			return;  		} -		if (ioc->shost_recovery) +		if (ioc->shost_recovery || ioc->remove_host)  			return;  		phy_number = event_data->StartPhyNum + i;  		reason_code = event_data->PHY[i].PhyStatus & @@ -4393,8 +4471,10 @@ _scsih_sas_topology_change_event(struct MPT2SAS_ADAPTER *ioc,  			mpt2sas_transport_update_links(ioc, sas_address,  			    handle, phy_number, link_rate); -			if (link_rate >= MPI2_SAS_NEG_LINK_RATE_1_5) -				_scsih_ublock_io_device(ioc, handle); +			if (link_rate < MPI2_SAS_NEG_LINK_RATE_1_5) +				break; + +			_scsih_check_device(ioc, handle);  			break;  		case MPI2_EVENT_SAS_TOPO_RC_TARG_ADDED: @@ -4520,10 +4600,10 @@ _scsih_sas_device_status_change_event(struct MPT2SAS_ADAPTER *ioc,  		     event_data);  #endif -	if (!(event_data->ReasonCode == +	if (event_data->ReasonCode !=  	    MPI2_EVENT_SAS_DEV_STAT_RC_INTERNAL_DEVICE_RESET && -	   event_data->ReasonCode == -	    MPI2_EVENT_SAS_DEV_STAT_RC_CMP_INTERNAL_DEV_RESET)) +	   event_data->ReasonCode != +	    MPI2_EVENT_SAS_DEV_STAT_RC_CMP_INTERNAL_DEV_RESET)  		return;  	spin_lock_irqsave(&ioc->sas_device_lock, flags); @@ -4630,7 +4710,6 @@ _scsih_sas_broadcast_primative_event(struct MPT2SAS_ADAPTER *ioc,  	dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: enter\n", ioc->name,  	    __func__)); -	mutex_lock(&ioc->tm_cmds.mutex);  	termination_count = 0;  	query_count = 0;  	mpi_reply = ioc->tm_cmds.reply; @@ -4654,8 +4733,8 @@ _scsih_sas_broadcast_primative_event(struct MPT2SAS_ADAPTER *ioc,  		lun = sas_device_priv_data->lun;  		query_count++; -		mpt2sas_scsih_issue_tm(ioc, handle, lun, -		    MPI2_SCSITASKMGMT_TASKTYPE_QUERY_TASK, smid, 30); +		mpt2sas_scsih_issue_tm(ioc, handle, 0, 0, lun, +		    MPI2_SCSITASKMGMT_TASKTYPE_QUERY_TASK, smid, 30, NULL);  		ioc->tm_cmds.status = MPT2_CMD_NOT_USED;  		ioc_status = le16_to_cpu(mpi_reply->IOCStatus)  		    & MPI2_IOCSTATUS_MASK; @@ -4666,13 +4745,11 @@ _scsih_sas_broadcast_primative_event(struct MPT2SAS_ADAPTER *ioc,  		     MPI2_SCSITASKMGMT_RSP_IO_QUEUED_ON_IOC))  			continue; -		mpt2sas_scsih_issue_tm(ioc, handle, lun, -		    MPI2_SCSITASKMGMT_TASKTYPE_ABRT_TASK_SET, 0, 30); -		ioc->tm_cmds.status = MPT2_CMD_NOT_USED; +		mpt2sas_scsih_issue_tm(ioc, handle, 0, 0, lun, +		    MPI2_SCSITASKMGMT_TASKTYPE_ABRT_TASK_SET, 0, 30, NULL);  		termination_count += le32_to_cpu(mpi_reply->TerminationCount);  	}  	ioc->broadcast_aen_busy = 0; -	mutex_unlock(&ioc->tm_cmds.mutex);  	dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT  	    "%s - exit, query_count = %d termination_count = %d\n", @@ -5442,6 +5519,26 @@ _scsih_task_set_full(struct MPT2SAS_ADAPTER *ioc, struct fw_event_work  }  /** + * _scsih_prep_device_scan - initialize parameters prior to device scan + * @ioc: per adapter object + * + * Set the deleted flag prior to device scan.  If the device is found during + * the scan, then we clear the deleted flag. + */ +static void +_scsih_prep_device_scan(struct MPT2SAS_ADAPTER *ioc) +{ +	struct MPT2SAS_DEVICE *sas_device_priv_data; +	struct scsi_device *sdev; + +	shost_for_each_device(sdev, ioc->shost) { +		sas_device_priv_data = sdev->hostdata; +		if (sas_device_priv_data && sas_device_priv_data->sas_target) +			sas_device_priv_data->sas_target->deleted = 1; +	} +} + +/**   * _scsih_mark_responding_sas_device - mark a sas_devices as responding   * @ioc: per adapter object   * @sas_address: sas address @@ -5467,10 +5564,13 @@ _scsih_mark_responding_sas_device(struct MPT2SAS_ADAPTER *ioc, u64 sas_address,  		if (sas_device->sas_address == sas_address &&  		    sas_device->slot == slot && sas_device->starget) {  			sas_device->responding = 1; -			sas_device->state = 0;  			starget = sas_device->starget; -			sas_target_priv_data = starget->hostdata; -			sas_target_priv_data->tm_busy = 0; +			if (starget && starget->hostdata) { +				sas_target_priv_data = starget->hostdata; +				sas_target_priv_data->tm_busy = 0; +				sas_target_priv_data->deleted = 0; +			} else +				sas_target_priv_data = NULL;  			starget_printk(KERN_INFO, sas_device->starget,  			    "handle(0x%04x), sas_addr(0x%016llx), enclosure "  			    "logical id(0x%016llx), slot(%d)\n", handle, @@ -5483,7 +5583,8 @@ _scsih_mark_responding_sas_device(struct MPT2SAS_ADAPTER *ioc, u64 sas_address,  			printk(KERN_INFO "\thandle changed from(0x%04x)!!!\n",  			    sas_device->handle);  			sas_device->handle = handle; -			sas_target_priv_data->handle = handle; +			if (sas_target_priv_data) +				sas_target_priv_data->handle = handle;  			goto out;  		}  	} @@ -5558,6 +5659,12 @@ _scsih_mark_responding_raid_device(struct MPT2SAS_ADAPTER *ioc, u64 wwid,  	spin_lock_irqsave(&ioc->raid_device_lock, flags);  	list_for_each_entry(raid_device, &ioc->raid_device_list, list) {  		if (raid_device->wwid == wwid && raid_device->starget) { +			starget = raid_device->starget; +			if (starget && starget->hostdata) { +				sas_target_priv_data = starget->hostdata; +				sas_target_priv_data->deleted = 0; +			} else +				sas_target_priv_data = NULL;  			raid_device->responding = 1;  			starget_printk(KERN_INFO, raid_device->starget,  			    "handle(0x%04x), wwid(0x%016llx)\n", handle, @@ -5567,9 +5674,8 @@ _scsih_mark_responding_raid_device(struct MPT2SAS_ADAPTER *ioc, u64 wwid,  			printk(KERN_INFO "\thandle changed from(0x%04x)!!!\n",  			    raid_device->handle);  			raid_device->handle = handle; -			starget = raid_device->starget; -			sas_target_priv_data = starget->hostdata; -			sas_target_priv_data->handle = handle; +			if (sas_target_priv_data) +				sas_target_priv_data->handle = handle;  			goto out;  		}  	} @@ -5694,13 +5800,13 @@ _scsih_search_responding_expanders(struct MPT2SAS_ADAPTER *ioc)  }  /** - * _scsih_remove_unresponding_devices - removing unresponding devices + * _scsih_remove_unresponding_sas_devices - removing unresponding devices   * @ioc: per adapter object   *   * Return nothing.   */  static void -_scsih_remove_unresponding_devices(struct MPT2SAS_ADAPTER *ioc) +_scsih_remove_unresponding_sas_devices(struct MPT2SAS_ADAPTER *ioc)  {  	struct _sas_device *sas_device, *sas_device_next;  	struct _sas_node *sas_expander; @@ -5722,8 +5828,6 @@ _scsih_remove_unresponding_devices(struct MPT2SAS_ADAPTER *ioc)  			    (unsigned long long)  			    sas_device->enclosure_logical_id,  			    sas_device->slot); -		/* invalidate the device handle */ -		sas_device->handle = 0;  		_scsih_remove_device(ioc, sas_device);  	} @@ -5774,32 +5878,33 @@ mpt2sas_scsih_reset_handler(struct MPT2SAS_ADAPTER *ioc, int reset_phase)  	case MPT2_IOC_PRE_RESET:  		dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: "  		    "MPT2_IOC_PRE_RESET\n", ioc->name, __func__)); -		_scsih_fw_event_off(ioc);  		break;  	case MPT2_IOC_AFTER_RESET:  		dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: "  		    "MPT2_IOC_AFTER_RESET\n", ioc->name, __func__)); +		if (ioc->scsih_cmds.status & MPT2_CMD_PENDING) { +			ioc->scsih_cmds.status |= MPT2_CMD_RESET; +			mpt2sas_base_free_smid(ioc, ioc->scsih_cmds.smid); +			complete(&ioc->scsih_cmds.done); +		}  		if (ioc->tm_cmds.status & MPT2_CMD_PENDING) {  			ioc->tm_cmds.status |= MPT2_CMD_RESET;  			mpt2sas_base_free_smid(ioc, ioc->tm_cmds.smid);  			complete(&ioc->tm_cmds.done);  		} -		_scsih_fw_event_on(ioc); +		_scsih_fw_event_cleanup_queue(ioc);  		_scsih_flush_running_cmds(ioc); +		_scsih_queue_rescan(ioc);  		break;  	case MPT2_IOC_DONE_RESET:  		dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: "  		    "MPT2_IOC_DONE_RESET\n", ioc->name, __func__));  		_scsih_sas_host_refresh(ioc); +		_scsih_prep_device_scan(ioc);  		_scsih_search_responding_sas_devices(ioc);  		_scsih_search_responding_raid_devices(ioc);  		_scsih_search_responding_expanders(ioc);  		break; -	case MPT2_IOC_RUNNING: -		dtmprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s: " -		    "MPT2_IOC_RUNNING\n", ioc->name, __func__)); -		_scsih_remove_unresponding_devices(ioc); -		break;  	}  } @@ -5815,21 +5920,28 @@ static void  _firmware_event_work(struct work_struct *work)  {  	struct fw_event_work *fw_event = container_of(work, -	    struct fw_event_work, work); +	    struct fw_event_work, delayed_work.work);  	unsigned long flags;  	struct MPT2SAS_ADAPTER *ioc = fw_event->ioc;  	/* the queue is being flushed so ignore this event */ -	spin_lock_irqsave(&ioc->fw_event_lock, flags); -	if (ioc->fw_events_off || ioc->remove_host) { -		spin_unlock_irqrestore(&ioc->fw_event_lock, flags); +	if (ioc->remove_host || fw_event->cancel_pending_work) {  		_scsih_fw_event_free(ioc, fw_event);  		return;  	} -	spin_unlock_irqrestore(&ioc->fw_event_lock, flags); -	if (ioc->shost_recovery) { -		_scsih_fw_event_requeue(ioc, fw_event, 1000); +	if (fw_event->event == MPT2SAS_RESCAN_AFTER_HOST_RESET) { +		_scsih_fw_event_free(ioc, fw_event); +		spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags); +		if (ioc->shost_recovery) { +			init_completion(&ioc->shost_recovery_done); +			spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, +			    flags); +			wait_for_completion(&ioc->shost_recovery_done); +		} else +			spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, +			    flags); +		_scsih_remove_unresponding_sas_devices(ioc);  		return;  	} @@ -5891,16 +6003,12 @@ mpt2sas_scsih_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 msix_index,  {  	struct fw_event_work *fw_event;  	Mpi2EventNotificationReply_t *mpi_reply; -	unsigned long flags;  	u16 event; +	u16 sz;  	/* events turned off due to host reset or driver unloading */ -	spin_lock_irqsave(&ioc->fw_event_lock, flags); -	if (ioc->fw_events_off || ioc->remove_host) { -		spin_unlock_irqrestore(&ioc->fw_event_lock, flags); +	if (ioc->remove_host)  		return 1; -	} -	spin_unlock_irqrestore(&ioc->fw_event_lock, flags);  	mpi_reply = mpt2sas_base_get_reply_virt_addr(ioc, reply);  	event = le16_to_cpu(mpi_reply->Event); @@ -5947,8 +6055,8 @@ mpt2sas_scsih_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 msix_index,  		    ioc->name, __FILE__, __LINE__, __func__);  		return 1;  	} -	fw_event->event_data = -	    kzalloc(mpi_reply->EventDataLength*4, GFP_ATOMIC); +	sz = le16_to_cpu(mpi_reply->EventDataLength) * 4; +	fw_event->event_data = kzalloc(sz, GFP_ATOMIC);  	if (!fw_event->event_data) {  		printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",  		    ioc->name, __FILE__, __LINE__, __func__); @@ -5957,7 +6065,7 @@ mpt2sas_scsih_event_callback(struct MPT2SAS_ADAPTER *ioc, u8 msix_index,  	}  	memcpy(fw_event->event_data, mpi_reply->EventData, -	    mpi_reply->EventDataLength*4); +	    sz);  	fw_event->ioc = ioc;  	fw_event->VF_ID = mpi_reply->VF_ID;  	fw_event->VP_ID = mpi_reply->VP_ID; @@ -6158,6 +6266,18 @@ _scsih_shutdown(struct pci_dev *pdev)  {  	struct Scsi_Host *shost = pci_get_drvdata(pdev);  	struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); +	struct workqueue_struct	*wq; +	unsigned long flags; + +	ioc->remove_host = 1; +	_scsih_fw_event_cleanup_queue(ioc); + +	spin_lock_irqsave(&ioc->fw_event_lock, flags); +	wq = ioc->firmware_event_thread; +	ioc->firmware_event_thread = NULL; +	spin_unlock_irqrestore(&ioc->fw_event_lock, flags); +	if (wq) +		destroy_workqueue(wq);  	_scsih_ir_shutdown(ioc);  	mpt2sas_base_detach(ioc); @@ -6184,7 +6304,7 @@ _scsih_remove(struct pci_dev *pdev)  	unsigned long flags;  	ioc->remove_host = 1; -	_scsih_fw_event_off(ioc); +	_scsih_fw_event_cleanup_queue(ioc);  	spin_lock_irqsave(&ioc->fw_event_lock, flags);  	wq = ioc->firmware_event_thread; @@ -6557,6 +6677,122 @@ _scsih_resume(struct pci_dev *pdev)  }  #endif /* CONFIG_PM */ +/** + * _scsih_pci_error_detected - Called when a PCI error is detected. + * @pdev: PCI device struct + * @state: PCI channel state + * + * Description: Called when a PCI error is detected. + * + * Return value: + *      PCI_ERS_RESULT_NEED_RESET or PCI_ERS_RESULT_DISCONNECT + */ +static pci_ers_result_t +_scsih_pci_error_detected(struct pci_dev *pdev, pci_channel_state_t state) +{ +	struct Scsi_Host *shost = pci_get_drvdata(pdev); +	struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); + +	printk(MPT2SAS_INFO_FMT "PCI error: detected callback, state(%d)!!\n", +	    ioc->name, state); + +	switch (state) { +	case pci_channel_io_normal: +		return PCI_ERS_RESULT_CAN_RECOVER; +	case pci_channel_io_frozen: +		scsi_block_requests(ioc->shost); +		mpt2sas_base_stop_watchdog(ioc); +		mpt2sas_base_free_resources(ioc); +		return PCI_ERS_RESULT_NEED_RESET; +	case pci_channel_io_perm_failure: +		_scsih_remove(pdev); +		return PCI_ERS_RESULT_DISCONNECT; +	} +	return PCI_ERS_RESULT_NEED_RESET; +} + +/** + * _scsih_pci_slot_reset - Called when PCI slot has been reset. + * @pdev: PCI device struct + * + * Description: This routine is called by the pci error recovery + * code after the PCI slot has been reset, just before we + * should resume normal operations. + */ +static pci_ers_result_t +_scsih_pci_slot_reset(struct pci_dev *pdev) +{ +	struct Scsi_Host *shost = pci_get_drvdata(pdev); +	struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); +	int rc; + +	printk(MPT2SAS_INFO_FMT "PCI error: slot reset callback!!\n", +		ioc->name); + +	ioc->pdev = pdev; +	rc = mpt2sas_base_map_resources(ioc); +	if (rc) +		return PCI_ERS_RESULT_DISCONNECT; + + +	rc = mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, +	    FORCE_BIG_HAMMER); + +	printk(MPT2SAS_WARN_FMT "hard reset: %s\n", ioc->name, +	    (rc == 0) ? "success" : "failed"); + +	if (!rc) +		return PCI_ERS_RESULT_RECOVERED; +	else +		return PCI_ERS_RESULT_DISCONNECT; +} + +/** + * _scsih_pci_resume() - resume normal ops after PCI reset + * @pdev: pointer to PCI device + * + * Called when the error recovery driver tells us that its + * OK to resume normal operation. Use completion to allow + * halted scsi ops to resume. + */ +static void +_scsih_pci_resume(struct pci_dev *pdev) +{ +	struct Scsi_Host *shost = pci_get_drvdata(pdev); +	struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); + +	printk(MPT2SAS_INFO_FMT "PCI error: resume callback!!\n", ioc->name); + +	pci_cleanup_aer_uncorrect_error_status(pdev); +	mpt2sas_base_start_watchdog(ioc); +	scsi_unblock_requests(ioc->shost); +} + +/** + * _scsih_pci_mmio_enabled - Enable MMIO and dump debug registers + * @pdev: pointer to PCI device + */ +static pci_ers_result_t +_scsih_pci_mmio_enabled(struct pci_dev *pdev) +{ +	struct Scsi_Host *shost = pci_get_drvdata(pdev); +	struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); + +	printk(MPT2SAS_INFO_FMT "PCI error: mmio enabled callback!!\n", +	    ioc->name); + +	/* TODO - dump whatever for debugging purposes */ + +	/* Request a slot reset. */ +	return PCI_ERS_RESULT_NEED_RESET; +} + +static struct pci_error_handlers _scsih_err_handler = { +	.error_detected = _scsih_pci_error_detected, +	.mmio_enabled = _scsih_pci_mmio_enabled, +	.slot_reset =	_scsih_pci_slot_reset, +	.resume =	_scsih_pci_resume, +};  static struct pci_driver scsih_driver = {  	.name		= MPT2SAS_DRIVER_NAME, @@ -6564,6 +6800,7 @@ static struct pci_driver scsih_driver = {  	.probe		= _scsih_probe,  	.remove		= __devexit_p(_scsih_remove),  	.shutdown	= _scsih_shutdown, +	.err_handler	= &_scsih_err_handler,  #ifdef CONFIG_PM  	.suspend	= _scsih_suspend,  	.resume		= _scsih_resume, | 
