diff options
Diffstat (limited to 'drivers/scsi/mpt2sas/mpt2sas_scsih.c')
| -rw-r--r-- | drivers/scsi/mpt2sas/mpt2sas_scsih.c | 634 | 
1 files changed, 503 insertions, 131 deletions
diff --git a/drivers/scsi/mpt2sas/mpt2sas_scsih.c b/drivers/scsi/mpt2sas/mpt2sas_scsih.c index 1da1aa1a11e..8889b1babca 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_scsih.c +++ b/drivers/scsi/mpt2sas/mpt2sas_scsih.c @@ -71,6 +71,9 @@ static void _firmware_event_work(struct work_struct *work);  static u8 _scsih_check_for_pending_tm(struct MPT2SAS_ADAPTER *ioc, u16 smid); +static void _scsih_scan_start(struct Scsi_Host *shost); +static int _scsih_scan_finished(struct Scsi_Host *shost, unsigned long time); +  /* global parameters */  LIST_HEAD(mpt2sas_ioc_list); @@ -79,6 +82,7 @@ static u8 scsi_io_cb_idx = -1;  static u8 tm_cb_idx = -1;  static u8 ctl_cb_idx = -1;  static u8 base_cb_idx = -1; +static u8 port_enable_cb_idx = -1;  static u8 transport_cb_idx = -1;  static u8 scsih_cb_idx = -1;  static u8 config_cb_idx = -1; @@ -103,6 +107,18 @@ static int max_lun = MPT2SAS_MAX_LUN;  module_param(max_lun, int, 0);  MODULE_PARM_DESC(max_lun, " max lun, default=16895 "); +/* diag_buffer_enable is bitwise + * bit 0 set = TRACE + * bit 1 set = SNAPSHOT + * bit 2 set = EXTENDED + * + * Either bit can be set, or both + */ +static int diag_buffer_enable = -1; +module_param(diag_buffer_enable, int, 0); +MODULE_PARM_DESC(diag_buffer_enable, " post diag buffers " +	"(TRACE=1/SNAPSHOT=2/EXTENDED=4/default=0)"); +  /**   * struct sense_info - common structure for obtaining sense keys   * @skey: sense key @@ -117,8 +133,8 @@ struct sense_info {  #define MPT2SAS_TURN_ON_FAULT_LED (0xFFFC) -#define MPT2SAS_RESCAN_AFTER_HOST_RESET (0xFFFF) - +#define MPT2SAS_PORT_ENABLE_COMPLETE (0xFFFD) +#define MPT2SAS_REMOVE_UNRESPONDING_DEVICES (0xFFFF)  /**   * struct fw_event_work - firmware event struct   * @list: link list framework @@ -372,31 +388,34 @@ _scsih_get_sas_address(struct MPT2SAS_ADAPTER *ioc, u16 handle,  	Mpi2SasDevicePage0_t sas_device_pg0;  	Mpi2ConfigReply_t mpi_reply;  	u32 ioc_status; +	*sas_address = 0;  	if (handle <= ioc->sas_hba.num_phys) {  		*sas_address = ioc->sas_hba.sas_address;  		return 0; -	} else -		*sas_address = 0; +	}  	if ((mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0,  	    MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle))) { -		printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", -		    ioc->name, __FILE__, __LINE__, __func__); +		printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", ioc->name, +		__FILE__, __LINE__, __func__);  		return -ENXIO;  	} -	ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & -	    MPI2_IOCSTATUS_MASK; -	if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { -		printk(MPT2SAS_ERR_FMT "handle(0x%04x), ioc_status(0x%04x)" -		    "\nfailure at %s:%d/%s()!\n", ioc->name, handle, ioc_status, -		     __FILE__, __LINE__, __func__); -		return -EIO; +	ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & MPI2_IOCSTATUS_MASK; +	if (ioc_status == MPI2_IOCSTATUS_SUCCESS) { +		*sas_address = le64_to_cpu(sas_device_pg0.SASAddress); +		return 0;  	} -	*sas_address = le64_to_cpu(sas_device_pg0.SASAddress); -	return 0; +	/* we hit this becuase the given parent handle doesn't exist */ +	if (ioc_status == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE) +		return -ENXIO; +	/* else error case */ +	printk(MPT2SAS_ERR_FMT "handle(0x%04x), ioc_status(0x%04x), " +	    "failure at %s:%d/%s()!\n", ioc->name, handle, ioc_status, +	     __FILE__, __LINE__, __func__); +	return -EIO;  }  /** @@ -424,7 +443,11 @@ _scsih_determine_boot_device(struct MPT2SAS_ADAPTER *ioc,  	u16 slot;  	 /* only process this function when driver loads */ -	if (!ioc->wait_for_port_enable_to_complete) +	if (!ioc->is_driver_loading) +		return; + +	 /* no Bios, return immediately */ +	if (!ioc->bios_pg3.BiosVersion)  		return;  	if (!is_raid) { @@ -587,8 +610,15 @@ _scsih_sas_device_add(struct MPT2SAS_ADAPTER *ioc,  	spin_unlock_irqrestore(&ioc->sas_device_lock, flags);  	if (!mpt2sas_transport_port_add(ioc, sas_device->handle, -	     sas_device->sas_address_parent)) +	     sas_device->sas_address_parent)) {  		_scsih_sas_device_remove(ioc, sas_device); +		} else if (!sas_device->starget) { +			if (!ioc->is_driver_loading) +				mpt2sas_transport_port_remove(ioc, +				sas_device->sas_address, +			    sas_device->sas_address_parent); +			_scsih_sas_device_remove(ioc, sas_device); +		}  }  /** @@ -1400,6 +1430,10 @@ _scsih_slave_destroy(struct scsi_device *sdev)  {  	struct MPT2SAS_TARGET *sas_target_priv_data;  	struct scsi_target *starget; +	struct Scsi_Host *shost; +	struct MPT2SAS_ADAPTER *ioc; +	struct _sas_device *sas_device; +	unsigned long flags;  	if (!sdev->hostdata)  		return; @@ -1407,6 +1441,19 @@ _scsih_slave_destroy(struct scsi_device *sdev)  	starget = scsi_target(sdev);  	sas_target_priv_data = starget->hostdata;  	sas_target_priv_data->num_luns--; + +	shost = dev_to_shost(&starget->dev); +	ioc = shost_priv(shost); + +	if (!(sas_target_priv_data->flags & MPT_TARGET_FLAGS_VOLUME)) { +		spin_lock_irqsave(&ioc->sas_device_lock, flags); +		sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc, +		   sas_target_priv_data->sas_address); +		if (sas_device) +			sas_device->starget = NULL; +		spin_unlock_irqrestore(&ioc->sas_device_lock, flags); +	} +  	kfree(sdev->hostdata);  	sdev->hostdata = NULL;  } @@ -1598,8 +1645,10 @@ _scsih_set_level(struct scsi_device *sdev, struct _raid_device *raid_device)   * _scsih_get_volume_capabilities - volume capabilities   * @ioc: per adapter object   * @sas_device: the raid_device object + * + * Returns 0 for success, else 1   */ -static void +static int  _scsih_get_volume_capabilities(struct MPT2SAS_ADAPTER *ioc,      struct _raid_device *raid_device)  { @@ -1612,9 +1661,10 @@ _scsih_get_volume_capabilities(struct MPT2SAS_ADAPTER *ioc,  	if ((mpt2sas_config_get_number_pds(ioc, raid_device->handle,  	    &num_pds)) || !num_pds) { -		printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", -		    ioc->name, __FILE__, __LINE__, __func__); -		return; +		dfailprintk(ioc, printk(MPT2SAS_WARN_FMT +		    "failure at %s:%d/%s()!\n", ioc->name, __FILE__, __LINE__, +		    __func__)); +		return 1;  	}  	raid_device->num_pds = num_pds; @@ -1622,17 +1672,19 @@ _scsih_get_volume_capabilities(struct MPT2SAS_ADAPTER *ioc,  	    sizeof(Mpi2RaidVol0PhysDisk_t));  	vol_pg0 = kzalloc(sz, GFP_KERNEL);  	if (!vol_pg0) { -		printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", -		    ioc->name, __FILE__, __LINE__, __func__); -		return; +		dfailprintk(ioc, printk(MPT2SAS_WARN_FMT +		    "failure at %s:%d/%s()!\n", ioc->name, __FILE__, __LINE__, +		    __func__)); +		return 1;  	}  	if ((mpt2sas_config_get_raid_volume_pg0(ioc, &mpi_reply, vol_pg0,  	     MPI2_RAID_VOLUME_PGAD_FORM_HANDLE, raid_device->handle, sz))) { -		printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", -		    ioc->name, __FILE__, __LINE__, __func__); +		dfailprintk(ioc, printk(MPT2SAS_WARN_FMT +		    "failure at %s:%d/%s()!\n", ioc->name, __FILE__, __LINE__, +		    __func__));  		kfree(vol_pg0); -		return; +		return 1;  	}  	raid_device->volume_type = vol_pg0->VolumeType; @@ -1652,6 +1704,7 @@ _scsih_get_volume_capabilities(struct MPT2SAS_ADAPTER *ioc,  	}  	kfree(vol_pg0); +	return 0;  }  /**   * _scsih_disable_ddio - Disable direct I/O for all the volumes @@ -1922,13 +1975,20 @@ _scsih_slave_configure(struct scsi_device *sdev)  		     sas_target_priv_data->handle);  		spin_unlock_irqrestore(&ioc->raid_device_lock, flags);  		if (!raid_device) { -			printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", -			    ioc->name, __FILE__, __LINE__, __func__); -			return 0; +			dfailprintk(ioc, printk(MPT2SAS_WARN_FMT +			    "failure at %s:%d/%s()!\n", ioc->name, __FILE__, +			    __LINE__, __func__)); +			return 1;  		}  		_scsih_get_volume_capabilities(ioc, raid_device); +		if (_scsih_get_volume_capabilities(ioc, raid_device)) { +			dfailprintk(ioc, printk(MPT2SAS_WARN_FMT +			    "failure at %s:%d/%s()!\n", ioc->name, __FILE__, +			    __LINE__, __func__)); +			return 1; +		}  		/*  		 * WARPDRIVE: Initialize the required data for Direct IO  		 */ @@ -2002,11 +2062,22 @@ _scsih_slave_configure(struct scsi_device *sdev)  	if (sas_device) {  		if (sas_target_priv_data->flags &  		    MPT_TARGET_FLAGS_RAID_COMPONENT) { -			mpt2sas_config_get_volume_handle(ioc, -			    sas_device->handle, &sas_device->volume_handle); -			mpt2sas_config_get_volume_wwid(ioc, +			if (mpt2sas_config_get_volume_handle(ioc, +			    sas_device->handle, &sas_device->volume_handle)) { +				dfailprintk(ioc, printk(MPT2SAS_WARN_FMT +				    "failure at %s:%d/%s()!\n", ioc->name, +				    __FILE__, __LINE__, __func__)); +				return 1; +			} +			if (sas_device->volume_handle && +			    mpt2sas_config_get_volume_wwid(ioc,  			    sas_device->volume_handle, -			    &sas_device->volume_wwid); +			    &sas_device->volume_wwid)) { +				dfailprintk(ioc, printk(MPT2SAS_WARN_FMT +				    "failure at %s:%d/%s()!\n", ioc->name, +				    __FILE__, __LINE__, __func__)); +				return 1; +			}  		}  		if (sas_device->device_info & MPI2_SAS_DEVICE_INFO_SSP_TARGET) {  			qdepth = MPT2SAS_SAS_QUEUE_DEPTH; @@ -2035,6 +2106,11 @@ _scsih_slave_configure(struct scsi_device *sdev)  		if (!ssp_target)  			_scsih_display_sata_capabilities(ioc, sas_device, sdev); +	} else { +		dfailprintk(ioc, printk(MPT2SAS_WARN_FMT +		    "failure at %s:%d/%s()!\n", ioc->name, __FILE__, __LINE__, +		    __func__)); +		return 1;  	}  	_scsih_change_queue_depth(sdev, qdepth, SCSI_QDEPTH_DEFAULT); @@ -2714,22 +2790,38 @@ _scsih_fw_event_free(struct MPT2SAS_ADAPTER *ioc, struct fw_event_work  /** - * _scsih_queue_rescan - queue a topology rescan from user context + * _scsih_error_recovery_delete_devices - remove devices not responding   * @ioc: per adapter object   *   * Return nothing.   */  static void -_scsih_queue_rescan(struct MPT2SAS_ADAPTER *ioc) +_scsih_error_recovery_delete_devices(struct MPT2SAS_ADAPTER *ioc)  {  	struct fw_event_work *fw_event; -	if (ioc->wait_for_port_enable_to_complete) +	if (ioc->is_driver_loading)  		return; +	fw_event->event = MPT2SAS_REMOVE_UNRESPONDING_DEVICES; +	fw_event->ioc = ioc; +	_scsih_fw_event_add(ioc, fw_event); +} + +/** + * mpt2sas_port_enable_complete - port enable completed (fake event) + * @ioc: per adapter object + * + * Return nothing. + */ +void +mpt2sas_port_enable_complete(struct MPT2SAS_ADAPTER *ioc) +{ +	struct fw_event_work *fw_event; +  	fw_event = kzalloc(sizeof(struct fw_event_work), GFP_ATOMIC);  	if (!fw_event)  		return; -	fw_event->event = MPT2SAS_RESCAN_AFTER_HOST_RESET; +	fw_event->event = MPT2SAS_PORT_ENABLE_COMPLETE;  	fw_event->ioc = ioc;  	_scsih_fw_event_add(ioc, fw_event);  } @@ -2977,14 +3069,27 @@ _scsih_tm_tr_send(struct MPT2SAS_ADAPTER *ioc, u16 handle)  	Mpi2SCSITaskManagementRequest_t *mpi_request;  	u16 smid;  	struct _sas_device *sas_device; -	struct MPT2SAS_TARGET *sas_target_priv_data; +	struct MPT2SAS_TARGET *sas_target_priv_data = NULL; +	u64 sas_address = 0;  	unsigned long flags;  	struct _tr_list *delayed_tr; +	u32 ioc_state; -	if (ioc->shost_recovery || ioc->remove_host || -	    ioc->pci_error_recovery) { -		dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: host reset in " -		   "progress!\n", __func__, ioc->name)); +	if (ioc->remove_host) { +		dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: host has been " +		    "removed: handle(0x%04x)\n", __func__, ioc->name, handle)); +		return; +	} else if (ioc->pci_error_recovery) { +		dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: host in pci " +		    "error recovery: handle(0x%04x)\n", __func__, ioc->name, +		    handle)); +		return; +	} +	ioc_state = mpt2sas_base_get_iocstate(ioc, 1); +	if (ioc_state != MPI2_IOC_STATE_OPERATIONAL) { +		dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: host is not " +		   "operational: handle(0x%04x)\n", __func__, ioc->name, +		   handle));  		return;  	} @@ -2998,13 +3103,18 @@ _scsih_tm_tr_send(struct MPT2SAS_ADAPTER *ioc, u16 handle)  	     sas_device->starget->hostdata) {  		sas_target_priv_data = sas_device->starget->hostdata;  		sas_target_priv_data->deleted = 1; -		dewtprintk(ioc, printk(MPT2SAS_INFO_FMT -		    "setting delete flag: handle(0x%04x), " -		    "sas_addr(0x%016llx)\n", ioc->name, handle, -		    (unsigned long long) sas_device->sas_address)); +		sas_address = sas_device->sas_address;  	}  	spin_unlock_irqrestore(&ioc->sas_device_lock, flags); +	if (sas_target_priv_data) { +		dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "setting delete flag: " +		"handle(0x%04x), sas_addr(0x%016llx)\n", ioc->name, handle, +			(unsigned long long)sas_address)); +		_scsih_ublock_io_device(ioc, handle); +		sas_target_priv_data->handle = MPT2SAS_INVALID_DEVICE_HANDLE; +	} +  	smid = mpt2sas_base_get_smid_hpr(ioc, ioc->tm_tr_cb_idx);  	if (!smid) {  		delayed_tr = kzalloc(sizeof(*delayed_tr), GFP_ATOMIC); @@ -3185,11 +3295,21 @@ _scsih_tm_tr_complete(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index,  	    mpt2sas_base_get_reply_virt_addr(ioc, reply);  	Mpi2SasIoUnitControlRequest_t *mpi_request;  	u16 smid_sas_ctrl; +	u32 ioc_state; -	if (ioc->shost_recovery || ioc->remove_host || -	    ioc->pci_error_recovery) { -		dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: host reset in " -		   "progress!\n", __func__, ioc->name)); +	if (ioc->remove_host) { +		dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: host has been " +		   "removed\n", __func__, ioc->name)); +		return 1; +	} else if (ioc->pci_error_recovery) { +		dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: host in pci " +		    "error recovery\n", __func__, ioc->name)); +		return 1; +	} +	ioc_state = mpt2sas_base_get_iocstate(ioc, 1); +	if (ioc_state != MPI2_IOC_STATE_OPERATIONAL) { +		dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "%s: host is not " +		    "operational\n", __func__, ioc->name));  		return 1;  	} @@ -5099,7 +5219,7 @@ _scsih_add_device(struct MPT2SAS_ADAPTER *ioc, u16 handle, u8 phy_num, u8 is_pd)  	/* get device name */  	sas_device->device_name = le64_to_cpu(sas_device_pg0.DeviceName); -	if (ioc->wait_for_port_enable_to_complete) +	if (ioc->wait_for_discovery_to_complete)  		_scsih_sas_device_init_add(ioc, sas_device);  	else  		_scsih_sas_device_add(ioc, sas_device); @@ -5135,6 +5255,9 @@ _scsih_remove_device(struct MPT2SAS_ADAPTER *ioc,  	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; +		_scsih_ublock_io_device(ioc, sas_device_backup.handle); +		sas_target_priv_data->handle = +		     MPT2SAS_INVALID_DEVICE_HANDLE;  	}  	_scsih_ublock_io_device(ioc, sas_device_backup.handle); @@ -5288,7 +5411,7 @@ _scsih_sas_topology_change_event(struct MPT2SAS_ADAPTER *ioc,  		_scsih_sas_topology_change_event_debug(ioc, event_data);  #endif -	if (ioc->shost_recovery || ioc->remove_host || ioc->pci_error_recovery) +	if (ioc->remove_host || ioc->pci_error_recovery)  		return;  	if (!ioc->sas_hba.num_phys) @@ -5349,6 +5472,9 @@ _scsih_sas_topology_change_event(struct MPT2SAS_ADAPTER *ioc,  		switch (reason_code) {  		case MPI2_EVENT_SAS_TOPO_RC_PHY_CHANGED: +			if (ioc->shost_recovery) +				break; +  			if (link_rate == prev_link_rate)  				break; @@ -5362,6 +5488,9 @@ _scsih_sas_topology_change_event(struct MPT2SAS_ADAPTER *ioc,  			break;  		case MPI2_EVENT_SAS_TOPO_RC_TARG_ADDED: +			if (ioc->shost_recovery) +				break; +  			mpt2sas_transport_update_links(ioc, sas_address,  			    handle, phy_number, link_rate); @@ -5622,7 +5751,7 @@ broadcast_aen_retry:  	termination_count = 0;  	query_count = 0;  	for (smid = 1; smid <= ioc->scsiio_depth; smid++) { -		if (ioc->ioc_reset_in_progress_status) +		if (ioc->shost_recovery)  			goto out;  		scmd = _scsih_scsi_lookup_get(ioc, smid);  		if (!scmd) @@ -5644,7 +5773,7 @@ broadcast_aen_retry:  		lun = sas_device_priv_data->lun;  		query_count++; -		if (ioc->ioc_reset_in_progress_status) +		if (ioc->shost_recovery)  			goto out;  		spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags); @@ -5686,7 +5815,7 @@ broadcast_aen_retry:  			goto broadcast_aen_retry;  		} -		if (ioc->ioc_reset_in_progress_status) +		if (ioc->shost_recovery)  			goto out_no_lock;  		r = mpt2sas_scsih_issue_tm(ioc, handle, sdev->channel, sdev->id, @@ -5725,7 +5854,7 @@ broadcast_aen_retry:  	    ioc->name, __func__, query_count, termination_count));  	ioc->broadcast_aen_busy = 0; -	if (!ioc->ioc_reset_in_progress_status) +	if (!ioc->shost_recovery)  		_scsih_ublock_io_all_device(ioc);  	mutex_unlock(&ioc->tm_cmds.mutex);  } @@ -5789,8 +5918,11 @@ _scsih_reprobe_lun(struct scsi_device *sdev, void *no_uld_attach)  static void  _scsih_reprobe_target(struct scsi_target *starget, int no_uld_attach)  { -	struct MPT2SAS_TARGET *sas_target_priv_data = starget->hostdata; +	struct MPT2SAS_TARGET *sas_target_priv_data; +	if (starget == NULL) +		return; +	sas_target_priv_data = starget->hostdata;  	if (no_uld_attach)  		sas_target_priv_data->flags |= MPT_TARGET_FLAGS_RAID_COMPONENT;  	else @@ -5845,7 +5977,7 @@ _scsih_sas_volume_add(struct MPT2SAS_ADAPTER *ioc,  	raid_device->handle = handle;  	raid_device->wwid = wwid;  	_scsih_raid_device_add(ioc, raid_device); -	if (!ioc->wait_for_port_enable_to_complete) { +	if (!ioc->wait_for_discovery_to_complete) {  		rc = scsi_add_device(ioc->shost, RAID_CHANNEL,  		    raid_device->id, 0);  		if (rc) @@ -6127,6 +6259,10 @@ _scsih_sas_ir_config_change_event(struct MPT2SAS_ADAPTER *ioc,  		_scsih_sas_ir_config_change_event_debug(ioc, event_data);  #endif + +	if (ioc->shost_recovery) +		return; +  	foreign_config = (le32_to_cpu(event_data->Flags) &  	    MPI2_EVENT_IR_CHANGE_FLAGS_FOREIGN_CONFIG) ? 1 : 0; @@ -6185,6 +6321,9 @@ _scsih_sas_ir_volume_event(struct MPT2SAS_ADAPTER *ioc,  	int rc;  	Mpi2EventDataIrVolume_t *event_data = fw_event->event_data; +	if (ioc->shost_recovery) +		return; +  	if (event_data->ReasonCode != MPI2_EVENT_IR_VOLUME_RC_STATE_CHANGED)  		return; @@ -6267,6 +6406,9 @@ _scsih_sas_ir_physical_disk_event(struct MPT2SAS_ADAPTER *ioc,  	Mpi2EventDataIrPhysicalDisk_t *event_data = fw_event->event_data;  	u64 sas_address; +	if (ioc->shost_recovery) +		return; +  	if (event_data->ReasonCode != MPI2_EVENT_IR_PHYSDISK_RC_STATE_CHANGED)  		return; @@ -6510,10 +6652,10 @@ _scsih_search_responding_sas_devices(struct MPT2SAS_ADAPTER *ioc)  	u32 device_info;  	u16 slot; -	printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, __func__); +	printk(MPT2SAS_INFO_FMT "search for end-devices: start\n", ioc->name);  	if (list_empty(&ioc->sas_device_list)) -		return; +		goto out;  	handle = 0xFFFF;  	while (!(mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, @@ -6532,6 +6674,9 @@ _scsih_search_responding_sas_devices(struct MPT2SAS_ADAPTER *ioc)  		_scsih_mark_responding_sas_device(ioc, sas_address, slot,  		    handle);  	} +out: +	printk(MPT2SAS_INFO_FMT "search for end-devices: complete\n", +	    ioc->name);  }  /** @@ -6607,10 +6752,14 @@ _scsih_search_responding_raid_devices(struct MPT2SAS_ADAPTER *ioc)  	u16 handle;  	u8 phys_disk_num; -	printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, __func__); +	if (!ioc->ir_firmware) +		return; + +	printk(MPT2SAS_INFO_FMT "search for raid volumes: start\n", +	    ioc->name);  	if (list_empty(&ioc->raid_device_list)) -		return; +		goto out;  	handle = 0xFFFF;  	while (!(mpt2sas_config_get_raid_volume_pg1(ioc, &mpi_reply, @@ -6649,6 +6798,9 @@ _scsih_search_responding_raid_devices(struct MPT2SAS_ADAPTER *ioc)  			set_bit(handle, ioc->pd_handles);  		}  	} +out: +	printk(MPT2SAS_INFO_FMT "search for responding raid volumes: " +	    "complete\n", ioc->name);  }  /** @@ -6708,10 +6860,10 @@ _scsih_search_responding_expanders(struct MPT2SAS_ADAPTER *ioc)  	u64 sas_address;  	u16 handle; -	printk(MPT2SAS_INFO_FMT "%s\n", ioc->name, __func__); +	printk(MPT2SAS_INFO_FMT "search for expanders: start\n", ioc->name);  	if (list_empty(&ioc->sas_expander_list)) -		return; +		goto out;  	handle = 0xFFFF;  	while (!(mpt2sas_config_get_expander_pg0(ioc, &mpi_reply, &expander_pg0, @@ -6730,6 +6882,8 @@ _scsih_search_responding_expanders(struct MPT2SAS_ADAPTER *ioc)  		_scsih_mark_responding_expander(ioc, sas_address, handle);  	} + out: +	printk(MPT2SAS_INFO_FMT "search for expanders: complete\n", ioc->name);  }  /** @@ -6745,6 +6899,8 @@ _scsih_remove_unresponding_sas_devices(struct MPT2SAS_ADAPTER *ioc)  	struct _sas_node *sas_expander;  	struct _raid_device *raid_device, *raid_device_next; +	printk(MPT2SAS_INFO_FMT "removing unresponding devices: start\n", +	    ioc->name);  	list_for_each_entry_safe(sas_device, sas_device_next,  	    &ioc->sas_device_list, list) { @@ -6764,6 +6920,9 @@ _scsih_remove_unresponding_sas_devices(struct MPT2SAS_ADAPTER *ioc)  		_scsih_remove_device(ioc, sas_device);  	} +	if (!ioc->ir_firmware) +		goto retry_expander_search; +  	list_for_each_entry_safe(raid_device, raid_device_next,  	    &ioc->raid_device_list, list) {  		if (raid_device->responding) { @@ -6790,52 +6949,170 @@ _scsih_remove_unresponding_sas_devices(struct MPT2SAS_ADAPTER *ioc)  		mpt2sas_expander_remove(ioc, sas_expander->sas_address);  		goto retry_expander_search;  	} +	printk(MPT2SAS_INFO_FMT "removing unresponding devices: complete\n", +	    ioc->name); +	/* unblock devices */ +	_scsih_ublock_io_all_device(ioc); +} + +static void +_scsih_refresh_expander_links(struct MPT2SAS_ADAPTER *ioc, +	struct _sas_node *sas_expander, u16 handle) +{ +	Mpi2ExpanderPage1_t expander_pg1; +	Mpi2ConfigReply_t mpi_reply; +	int i; + +	for (i = 0 ; i < sas_expander->num_phys ; i++) { +		if ((mpt2sas_config_get_expander_pg1(ioc, &mpi_reply, +		    &expander_pg1, i, handle))) { +			printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", +			    ioc->name, __FILE__, __LINE__, __func__); +			return; +		} + +		mpt2sas_transport_update_links(ioc, sas_expander->sas_address, +		    le16_to_cpu(expander_pg1.AttachedDevHandle), i, +		    expander_pg1.NegotiatedLinkRate >> 4); +	}  }  /** - * _scsih_hide_unhide_sas_devices - add/remove device to/from OS + * _scsih_scan_for_devices_after_reset - scan for devices after host reset   * @ioc: per adapter object   *   * Return nothing.   */  static void -_scsih_hide_unhide_sas_devices(struct MPT2SAS_ADAPTER *ioc) +_scsih_scan_for_devices_after_reset(struct MPT2SAS_ADAPTER *ioc)  { -	struct _sas_device *sas_device, *sas_device_next; +	Mpi2ExpanderPage0_t expander_pg0; +	Mpi2SasDevicePage0_t sas_device_pg0; +	Mpi2RaidVolPage1_t volume_pg1; +	Mpi2RaidVolPage0_t volume_pg0; +	Mpi2RaidPhysDiskPage0_t pd_pg0; +	Mpi2EventIrConfigElement_t element; +	Mpi2ConfigReply_t mpi_reply; +	u8 phys_disk_num; +	u16 ioc_status; +	u16 handle, parent_handle; +	u64 sas_address; +	struct _sas_device *sas_device; +	struct _sas_node *expander_device; +	static struct _raid_device *raid_device; -	if (!ioc->is_warpdrive || ioc->mfg_pg10_hide_flag != -	    MFG_PAGE10_HIDE_IF_VOL_PRESENT) -		return; +	printk(MPT2SAS_INFO_FMT "scan devices: start\n", ioc->name); -	if (ioc->hide_drives) { -		if (_scsih_get_num_volumes(ioc)) -			return; -		ioc->hide_drives = 0; -		list_for_each_entry_safe(sas_device, sas_device_next, -		    &ioc->sas_device_list, list) { -			if (!mpt2sas_transport_port_add(ioc, sas_device->handle, -				sas_device->sas_address_parent)) { -				_scsih_sas_device_remove(ioc, sas_device); -			} else if (!sas_device->starget) { -				mpt2sas_transport_port_remove(ioc, -				    sas_device->sas_address, -				    sas_device->sas_address_parent); -				_scsih_sas_device_remove(ioc, sas_device); -			} +	_scsih_sas_host_refresh(ioc); + +	/* expanders */ +	handle = 0xFFFF; +	while (!(mpt2sas_config_get_expander_pg0(ioc, &mpi_reply, &expander_pg0, +	    MPI2_SAS_EXPAND_PGAD_FORM_GET_NEXT_HNDL, handle))) { +		ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & +		    MPI2_IOCSTATUS_MASK; +		if (ioc_status == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE) +			break; +		handle = le16_to_cpu(expander_pg0.DevHandle); +		expander_device = mpt2sas_scsih_expander_find_by_sas_address( +		    ioc, le64_to_cpu(expander_pg0.SASAddress)); +		if (expander_device) +			_scsih_refresh_expander_links(ioc, expander_device, +			    handle); +		else +			_scsih_expander_add(ioc, handle); +	} + +	if (!ioc->ir_firmware) +		goto skip_to_sas; + +	/* phys disk */ +	phys_disk_num = 0xFF; +	while (!(mpt2sas_config_get_phys_disk_pg0(ioc, &mpi_reply, +	    &pd_pg0, MPI2_PHYSDISK_PGAD_FORM_GET_NEXT_PHYSDISKNUM, +	    phys_disk_num))) { +		ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & +		    MPI2_IOCSTATUS_MASK; +		if (ioc_status == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE) +			break; +		phys_disk_num = pd_pg0.PhysDiskNum; +		handle = le16_to_cpu(pd_pg0.DevHandle); +		sas_device = _scsih_sas_device_find_by_handle(ioc, handle); +		if (sas_device) +			continue; +		if (mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, +		    &sas_device_pg0, MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, +		    handle) != 0) +			continue; +		parent_handle = le16_to_cpu(sas_device_pg0.ParentDevHandle); +		if (!_scsih_get_sas_address(ioc, parent_handle, +		    &sas_address)) { +			mpt2sas_transport_update_links(ioc, sas_address, +			    handle, sas_device_pg0.PhyNum, +			    MPI2_SAS_NEG_LINK_RATE_1_5); +			set_bit(handle, ioc->pd_handles); +			_scsih_add_device(ioc, handle, 0, 1);  		} -	} else { -		if (!_scsih_get_num_volumes(ioc)) -			return; -		ioc->hide_drives = 1; -		list_for_each_entry_safe(sas_device, sas_device_next, -		    &ioc->sas_device_list, list) { -			mpt2sas_transport_port_remove(ioc, -			    sas_device->sas_address, -			    sas_device->sas_address_parent); +	} + +	/* volumes */ +	handle = 0xFFFF; +	while (!(mpt2sas_config_get_raid_volume_pg1(ioc, &mpi_reply, +	    &volume_pg1, MPI2_RAID_VOLUME_PGAD_FORM_GET_NEXT_HANDLE, handle))) { +		ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & +		    MPI2_IOCSTATUS_MASK; +		if (ioc_status == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE) +			break; +		handle = le16_to_cpu(volume_pg1.DevHandle); +		raid_device = _scsih_raid_device_find_by_wwid(ioc, +		    le64_to_cpu(volume_pg1.WWID)); +		if (raid_device) +			continue; +		if (mpt2sas_config_get_raid_volume_pg0(ioc, &mpi_reply, +		    &volume_pg0, MPI2_RAID_VOLUME_PGAD_FORM_HANDLE, handle, +		     sizeof(Mpi2RaidVolPage0_t))) +			continue; +		if (volume_pg0.VolumeState == MPI2_RAID_VOL_STATE_OPTIMAL || +		    volume_pg0.VolumeState == MPI2_RAID_VOL_STATE_ONLINE || +		    volume_pg0.VolumeState == MPI2_RAID_VOL_STATE_DEGRADED) { +			memset(&element, 0, sizeof(Mpi2EventIrConfigElement_t)); +			element.ReasonCode = MPI2_EVENT_IR_CHANGE_RC_ADDED; +			element.VolDevHandle = volume_pg1.DevHandle; +			_scsih_sas_volume_add(ioc, &element);  		}  	} + + skip_to_sas: + +	/* sas devices */ +	handle = 0xFFFF; +	while (!(mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, +	    &sas_device_pg0, MPI2_SAS_DEVICE_PGAD_FORM_GET_NEXT_HANDLE, +	    handle))) { +		ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & +		    MPI2_IOCSTATUS_MASK; +		if (ioc_status == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE) +			break; +		handle = le16_to_cpu(sas_device_pg0.DevHandle); +		if (!(_scsih_is_end_device( +		    le32_to_cpu(sas_device_pg0.DeviceInfo)))) +			continue; +		sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc, +		    le64_to_cpu(sas_device_pg0.SASAddress)); +		if (sas_device) +			continue; +		parent_handle = le16_to_cpu(sas_device_pg0.ParentDevHandle); +		if (!_scsih_get_sas_address(ioc, parent_handle, &sas_address)) { +			mpt2sas_transport_update_links(ioc, sas_address, handle, +			    sas_device_pg0.PhyNum, MPI2_SAS_NEG_LINK_RATE_1_5); +			_scsih_add_device(ioc, handle, 0, 0); +		} +	} + +	printk(MPT2SAS_INFO_FMT "scan devices: complete\n", ioc->name);  } +  /**   * mpt2sas_scsih_reset_handler - reset callback handler (for scsih)   * @ioc: per adapter object @@ -6871,7 +7148,6 @@ mpt2sas_scsih_reset_handler(struct MPT2SAS_ADAPTER *ioc, int reset_phase)  		}  		_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_INFO_FMT "%s: " @@ -6881,6 +7157,13 @@ mpt2sas_scsih_reset_handler(struct MPT2SAS_ADAPTER *ioc, int reset_phase)  		_scsih_search_responding_sas_devices(ioc);  		_scsih_search_responding_raid_devices(ioc);  		_scsih_search_responding_expanders(ioc); +		if (!ioc->is_driver_loading) { +			_scsih_prep_device_scan(ioc); +			_scsih_search_responding_sas_devices(ioc); +			_scsih_search_responding_raid_devices(ioc); +			_scsih_search_responding_expanders(ioc); +			_scsih_error_recovery_delete_devices(ioc); +		}  		break;  	}  } @@ -6898,7 +7181,6 @@ _firmware_event_work(struct work_struct *work)  {  	struct fw_event_work *fw_event = container_of(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 */ @@ -6908,23 +7190,21 @@ _firmware_event_work(struct work_struct *work)  		return;  	} -	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); +	switch (fw_event->event) { +	case MPT2SAS_REMOVE_UNRESPONDING_DEVICES: +		while (scsi_host_in_recovery(ioc->shost)) +			ssleep(1);  		_scsih_remove_unresponding_sas_devices(ioc); -		_scsih_hide_unhide_sas_devices(ioc); -		return; -	} +		_scsih_scan_for_devices_after_reset(ioc); +		break; +	case MPT2SAS_PORT_ENABLE_COMPLETE: +		ioc->start_scan = 0; -	switch (fw_event->event) { + + +		dewtprintk(ioc, printk(MPT2SAS_INFO_FMT "port enable: complete " +		    "from worker thread\n", ioc->name)); +		break;  	case MPT2SAS_TURN_ON_FAULT_LED:  		_scsih_turn_on_fault_led(ioc, fw_event->device_handle);  		break; @@ -7121,6 +7401,8 @@ static struct scsi_host_template scsih_driver_template = {  	.slave_configure		= _scsih_slave_configure,  	.target_destroy			= _scsih_target_destroy,  	.slave_destroy			= _scsih_slave_destroy, +	.scan_finished			= _scsih_scan_finished, +	.scan_start			= _scsih_scan_start,  	.change_queue_depth 		= _scsih_change_queue_depth,  	.change_queue_type		= _scsih_change_queue_type,  	.eh_abort_handler		= _scsih_abort, @@ -7381,7 +7663,12 @@ _scsih_probe_boot_devices(struct MPT2SAS_ADAPTER *ioc)  	unsigned long flags;  	int rc; +	 /* no Bios, return immediately */ +	if (!ioc->bios_pg3.BiosVersion) +		return; +  	device = NULL; +	is_raid = 0;  	if (ioc->req_boot_device.device) {  		device =  ioc->req_boot_device.device;  		is_raid = ioc->req_boot_device.is_raid; @@ -7417,8 +7704,9 @@ _scsih_probe_boot_devices(struct MPT2SAS_ADAPTER *ioc)  		    sas_device->sas_address_parent)) {  			_scsih_sas_device_remove(ioc, sas_device);  		} else if (!sas_device->starget) { -			mpt2sas_transport_port_remove(ioc, sas_address, -			    sas_address_parent); +			if (!ioc->is_driver_loading) +				mpt2sas_transport_port_remove(ioc, sas_address, +					sas_address_parent);  			_scsih_sas_device_remove(ioc, sas_device);  		}  	} @@ -7462,22 +7750,28 @@ _scsih_probe_sas(struct MPT2SAS_ADAPTER *ioc)  	/* SAS Device List */  	list_for_each_entry_safe(sas_device, next, &ioc->sas_device_init_list,  	    list) { -		spin_lock_irqsave(&ioc->sas_device_lock, flags); -		list_move_tail(&sas_device->list, &ioc->sas_device_list); -		spin_unlock_irqrestore(&ioc->sas_device_lock, flags);  		if (ioc->hide_drives)  			continue;  		if (!mpt2sas_transport_port_add(ioc, sas_device->handle,  		    sas_device->sas_address_parent)) { -			_scsih_sas_device_remove(ioc, sas_device); +			list_del(&sas_device->list); +			kfree(sas_device); +			continue;  		} else if (!sas_device->starget) { -			mpt2sas_transport_port_remove(ioc, -			    sas_device->sas_address, -			    sas_device->sas_address_parent); -			_scsih_sas_device_remove(ioc, sas_device); +			if (!ioc->is_driver_loading) +				mpt2sas_transport_port_remove(ioc, +					sas_device->sas_address, +					sas_device->sas_address_parent); +			list_del(&sas_device->list); +			kfree(sas_device); +			continue; +  		} +		spin_lock_irqsave(&ioc->sas_device_lock, flags); +		list_move_tail(&sas_device->list, &ioc->sas_device_list); +		spin_unlock_irqrestore(&ioc->sas_device_lock, flags);  	}  } @@ -7490,9 +7784,7 @@ _scsih_probe_sas(struct MPT2SAS_ADAPTER *ioc)  static void  _scsih_probe_devices(struct MPT2SAS_ADAPTER *ioc)  { -	u16 volume_mapping_flags = -	    le16_to_cpu(ioc->ioc_pg8.IRVolumeMappingFlags) & -	    MPI2_IOCPAGE8_IRFLAGS_MASK_VOLUME_MAPPING_MODE; +	u16 volume_mapping_flags;  	if (!(ioc->facts.ProtocolFlags & MPI2_IOCFACTS_PROTOCOL_SCSI_INITIATOR))  		return;  /* return when IOC doesn't support initiator mode */ @@ -7500,18 +7792,93 @@ _scsih_probe_devices(struct MPT2SAS_ADAPTER *ioc)  	_scsih_probe_boot_devices(ioc);  	if (ioc->ir_firmware) { -		if ((volume_mapping_flags & -		     MPI2_IOCPAGE8_IRFLAGS_HIGH_VOLUME_MAPPING)) { -			_scsih_probe_sas(ioc); +		volume_mapping_flags = +		    le16_to_cpu(ioc->ioc_pg8.IRVolumeMappingFlags) & +		    MPI2_IOCPAGE8_IRFLAGS_MASK_VOLUME_MAPPING_MODE; +		if (volume_mapping_flags == +		    MPI2_IOCPAGE8_IRFLAGS_LOW_VOLUME_MAPPING) {  			_scsih_probe_raid(ioc); +			_scsih_probe_sas(ioc);  		} else { -			_scsih_probe_raid(ioc);  			_scsih_probe_sas(ioc); +			_scsih_probe_raid(ioc);  		}  	} else  		_scsih_probe_sas(ioc);  } + +/** + * _scsih_scan_start - scsi lld callback for .scan_start + * @shost: SCSI host pointer + * + * The shost has the ability to discover targets on its own instead + * of scanning the entire bus.  In our implemention, we will kick off + * firmware discovery. + */ +static void +_scsih_scan_start(struct Scsi_Host *shost) +{ +	struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); +	int rc; + +	if (diag_buffer_enable != -1 && diag_buffer_enable != 0) +		mpt2sas_enable_diag_buffer(ioc, diag_buffer_enable); + +	ioc->start_scan = 1; +	rc = mpt2sas_port_enable(ioc); + +	if (rc != 0) +		printk(MPT2SAS_INFO_FMT "port enable: FAILED\n", ioc->name); +} + +/** + * _scsih_scan_finished - scsi lld callback for .scan_finished + * @shost: SCSI host pointer + * @time: elapsed time of the scan in jiffies + * + * This function will be called periodically until it returns 1 with the + * scsi_host and the elapsed time of the scan in jiffies. In our implemention, + * we wait for firmware discovery to complete, then return 1. + */ +static int +_scsih_scan_finished(struct Scsi_Host *shost, unsigned long time) +{ +	struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); + +	if (time >= (300 * HZ)) { +		ioc->base_cmds.status = MPT2_CMD_NOT_USED; +		printk(MPT2SAS_INFO_FMT "port enable: FAILED with timeout " +		    "(timeout=300s)\n", ioc->name); +		ioc->is_driver_loading = 0; +		return 1; +	} + +	if (ioc->start_scan) +		return 0; + +	if (ioc->start_scan_failed) { +		printk(MPT2SAS_INFO_FMT "port enable: FAILED with " +		    "(ioc_status=0x%08x)\n", ioc->name, ioc->start_scan_failed); +		ioc->is_driver_loading = 0; +		ioc->wait_for_discovery_to_complete = 0; +		ioc->remove_host = 1; +		return 1; +	} + +	printk(MPT2SAS_INFO_FMT "port enable: SUCCESS\n", ioc->name); +	ioc->base_cmds.status = MPT2_CMD_NOT_USED; + +	if (ioc->wait_for_discovery_to_complete) { +		ioc->wait_for_discovery_to_complete = 0; +		_scsih_probe_devices(ioc); +	} +	mpt2sas_base_start_watchdog(ioc); +	ioc->is_driver_loading = 0; +	return 1; +} + +  /**   * _scsih_probe - attach and add scsi host   * @pdev: PCI device struct @@ -7548,6 +7915,7 @@ _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id)  	ioc->tm_cb_idx = tm_cb_idx;  	ioc->ctl_cb_idx = ctl_cb_idx;  	ioc->base_cb_idx = base_cb_idx; +	ioc->port_enable_cb_idx = port_enable_cb_idx;  	ioc->transport_cb_idx = transport_cb_idx;  	ioc->scsih_cb_idx = scsih_cb_idx;  	ioc->config_cb_idx = config_cb_idx; @@ -7620,14 +7988,14 @@ _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id)  		goto out_thread_fail;  	} -	ioc->wait_for_port_enable_to_complete = 1; +	ioc->is_driver_loading = 1;  	if ((mpt2sas_base_attach(ioc))) {  		printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n",  		    ioc->name, __FILE__, __LINE__, __func__);  		goto out_attach_fail;  	} -	ioc->wait_for_port_enable_to_complete = 0; +	scsi_scan_host(shost);  	if (ioc->is_warpdrive) {  		if (ioc->mfg_pg10_hide_flag ==  MFG_PAGE10_EXPOSE_ALL_DISKS)  			ioc->hide_drives = 0; @@ -7650,6 +8018,7 @@ _scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id)   out_thread_fail:  	list_del(&ioc->list);  	scsi_remove_host(shost); +	scsi_host_put(shost);   out_add_shost_fail:  	return -ENODEV;  } @@ -7896,6 +8265,8 @@ _scsih_init(void)  	/* base internal commands callback handler */  	base_cb_idx = mpt2sas_base_register_callback_handler(mpt2sas_base_done); +	port_enable_cb_idx = mpt2sas_base_register_callback_handler( +		mpt2sas_port_enable_done);  	/* transport internal commands callback handler */  	transport_cb_idx = mpt2sas_base_register_callback_handler( @@ -7950,6 +8321,7 @@ _scsih_exit(void)  	mpt2sas_base_release_callback_handler(scsi_io_cb_idx);  	mpt2sas_base_release_callback_handler(tm_cb_idx);  	mpt2sas_base_release_callback_handler(base_cb_idx); +	mpt2sas_base_release_callback_handler(port_enable_cb_idx);  	mpt2sas_base_release_callback_handler(transport_cb_idx);  	mpt2sas_base_release_callback_handler(scsih_cb_idx);  	mpt2sas_base_release_callback_handler(config_cb_idx);  |