diff options
Diffstat (limited to 'drivers/scsi/mpt2sas/mpt2sas_scsih.c')
| -rw-r--r-- | drivers/scsi/mpt2sas/mpt2sas_scsih.c | 266 | 
1 files changed, 241 insertions, 25 deletions
diff --git a/drivers/scsi/mpt2sas/mpt2sas_scsih.c b/drivers/scsi/mpt2sas/mpt2sas_scsih.c index efabea1a3ce4..c7ec3f174782 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_scsih.c +++ b/drivers/scsi/mpt2sas/mpt2sas_scsih.c @@ -52,6 +52,7 @@  #include <linux/delay.h>  #include <linux/pci.h>  #include <linux/interrupt.h> +#include <linux/raid_class.h>  #include "mpt2sas_base.h" @@ -133,6 +134,9 @@ struct fw_event_work {  	void			*event_data;  }; +/* raid transport support */ +static struct raid_template *mpt2sas_raid_template; +  /**   * struct _scsi_io_transfer - scsi io transfer   * @handle: sas device handle (assigned by firmware) @@ -1305,7 +1309,6 @@ _scsih_slave_alloc(struct scsi_device *sdev)  	struct MPT2SAS_DEVICE *sas_device_priv_data;  	struct scsi_target *starget;  	struct _raid_device *raid_device; -	struct _sas_device *sas_device;  	unsigned long flags;  	sas_device_priv_data = kzalloc(sizeof(struct scsi_device), GFP_KERNEL); @@ -1332,21 +1335,8 @@ _scsih_slave_alloc(struct scsi_device *sdev)  		if (raid_device)  			raid_device->sdev = sdev; /* raid is single lun */  		spin_unlock_irqrestore(&ioc->raid_device_lock, flags); -	} else { -		/* set TLR bit for SSP devices */ -		if (!(ioc->facts.IOCCapabilities & -		     MPI2_IOCFACTS_CAPABILITY_TLR)) -			goto out; -		spin_lock_irqsave(&ioc->sas_device_lock, flags); -		sas_device = mpt2sas_scsih_sas_device_find_by_sas_address(ioc, -		   sas_device_priv_data->sas_target->sas_address); -		spin_unlock_irqrestore(&ioc->sas_device_lock, flags); -		if (sas_device && sas_device->device_info & -		    MPI2_SAS_DEVICE_INFO_SSP_TARGET) -			sas_device_priv_data->flags |= MPT_DEVICE_TLR_ON;  	} - out:  	return 0;  } @@ -1419,6 +1409,140 @@ _scsih_display_sata_capabilities(struct MPT2SAS_ADAPTER *ioc,  }  /** + * _scsih_is_raid - return boolean indicating device is raid volume + * @dev the device struct object + */ +static int +_scsih_is_raid(struct device *dev) +{ +	struct scsi_device *sdev = to_scsi_device(dev); + +	return (sdev->channel == RAID_CHANNEL) ? 1 : 0; +} + +/** + * _scsih_get_resync - get raid volume resync percent complete + * @dev the device struct object + */ +static void +_scsih_get_resync(struct device *dev) +{ +	struct scsi_device *sdev = to_scsi_device(dev); +	struct MPT2SAS_ADAPTER *ioc = shost_priv(sdev->host); +	static struct _raid_device *raid_device; +	unsigned long flags; +	Mpi2RaidVolPage0_t vol_pg0; +	Mpi2ConfigReply_t mpi_reply; +	u32 volume_status_flags; +	u8 percent_complete = 0; + +	spin_lock_irqsave(&ioc->raid_device_lock, flags); +	raid_device = _scsih_raid_device_find_by_id(ioc, sdev->id, +	    sdev->channel); +	spin_unlock_irqrestore(&ioc->raid_device_lock, flags); + +	if (!raid_device) +		goto out; + +	if (mpt2sas_config_get_raid_volume_pg0(ioc, &mpi_reply, &vol_pg0, +	     MPI2_RAID_VOLUME_PGAD_FORM_HANDLE, raid_device->handle, +	     sizeof(Mpi2RaidVolPage0_t))) { +		printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", +		    ioc->name, __FILE__, __LINE__, __func__); +		goto out; +	} + +	volume_status_flags = le32_to_cpu(vol_pg0.VolumeStatusFlags); +	if (volume_status_flags & MPI2_RAIDVOL0_STATUS_FLAG_RESYNC_IN_PROGRESS) +		percent_complete = raid_device->percent_complete; + out: +	raid_set_resync(mpt2sas_raid_template, dev, percent_complete); +} + +/** + * _scsih_get_state - get raid volume level + * @dev the device struct object + */ +static void +_scsih_get_state(struct device *dev) +{ +	struct scsi_device *sdev = to_scsi_device(dev); +	struct MPT2SAS_ADAPTER *ioc = shost_priv(sdev->host); +	static struct _raid_device *raid_device; +	unsigned long flags; +	Mpi2RaidVolPage0_t vol_pg0; +	Mpi2ConfigReply_t mpi_reply; +	u32 volstate; +	enum raid_state state = RAID_STATE_UNKNOWN; + +	spin_lock_irqsave(&ioc->raid_device_lock, flags); +	raid_device = _scsih_raid_device_find_by_id(ioc, sdev->id, +	    sdev->channel); +	spin_unlock_irqrestore(&ioc->raid_device_lock, flags); + +	if (!raid_device) +		goto out; + +	if (mpt2sas_config_get_raid_volume_pg0(ioc, &mpi_reply, &vol_pg0, +	     MPI2_RAID_VOLUME_PGAD_FORM_HANDLE, raid_device->handle, +	     sizeof(Mpi2RaidVolPage0_t))) { +		printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", +		    ioc->name, __FILE__, __LINE__, __func__); +		goto out; +	} + +	volstate = le32_to_cpu(vol_pg0.VolumeStatusFlags); +	if (volstate & MPI2_RAIDVOL0_STATUS_FLAG_RESYNC_IN_PROGRESS) { +		state = RAID_STATE_RESYNCING; +		goto out; +	} + +	switch (vol_pg0.VolumeState) { +	case MPI2_RAID_VOL_STATE_OPTIMAL: +	case MPI2_RAID_VOL_STATE_ONLINE: +		state = RAID_STATE_ACTIVE; +		break; +	case  MPI2_RAID_VOL_STATE_DEGRADED: +		state = RAID_STATE_DEGRADED; +		break; +	case MPI2_RAID_VOL_STATE_FAILED: +	case MPI2_RAID_VOL_STATE_MISSING: +		state = RAID_STATE_OFFLINE; +		break; +	} + out: +	raid_set_state(mpt2sas_raid_template, dev, state); +} + +/** + * _scsih_set_level - set raid level + * @sdev: scsi device struct + * @raid_device: raid_device object + */ +static void +_scsih_set_level(struct scsi_device *sdev, struct _raid_device *raid_device) +{ +	enum raid_level level = RAID_LEVEL_UNKNOWN; + +	switch (raid_device->volume_type) { +	case MPI2_RAID_VOL_TYPE_RAID0: +		level = RAID_LEVEL_0; +		break; +	case MPI2_RAID_VOL_TYPE_RAID10: +		level = RAID_LEVEL_10; +		break; +	case MPI2_RAID_VOL_TYPE_RAID1E: +		level = RAID_LEVEL_1E; +		break; +	case MPI2_RAID_VOL_TYPE_RAID1: +		level = RAID_LEVEL_1; +		break; +	} + +	raid_set_level(mpt2sas_raid_template, &sdev->sdev_gendev, level); +} + +/**   * _scsih_get_volume_capabilities - volume capabilities   * @ioc: per adapter object   * @sas_device: the raid_device object @@ -1479,6 +1603,32 @@ _scsih_get_volume_capabilities(struct MPT2SAS_ADAPTER *ioc,  }  /** + * _scsih_enable_tlr - setting TLR flags + * @ioc: per adapter object + * @sdev: scsi device struct + * + * Enabling Transaction Layer Retries for tape devices when + * vpd page 0x90 is present + * + */ +static void +_scsih_enable_tlr(struct MPT2SAS_ADAPTER *ioc, struct scsi_device *sdev) +{ +	/* only for TAPE */ +	if (sdev->type != TYPE_TAPE) +		return; + +	if (!(ioc->facts.IOCCapabilities & MPI2_IOCFACTS_CAPABILITY_TLR)) +		return; + +	sas_enable_tlr(sdev); +	sdev_printk(KERN_INFO, sdev, "TLR %s\n", +	    sas_is_tlr_enabled(sdev) ? "Enabled" : "Disabled"); +	return; + +} + +/**   * _scsih_slave_configure - device configure routine.   * @sdev: scsi device struct   * @@ -1574,6 +1724,8 @@ _scsih_slave_configure(struct scsi_device *sdev)  		    (unsigned long long)raid_device->wwid,  		    raid_device->num_pds, ds);  		_scsih_change_queue_depth(sdev, qdepth, SCSI_QDEPTH_DEFAULT); +		/* raid transport support */ +		_scsih_set_level(sdev, raid_device);  		return 0;  	} @@ -1621,8 +1773,10 @@ _scsih_slave_configure(struct scsi_device *sdev)  	_scsih_change_queue_depth(sdev, qdepth, SCSI_QDEPTH_DEFAULT); -	if (ssp_target) +	if (ssp_target) {  		sas_read_port_mode_page(sdev); +		_scsih_enable_tlr(ioc, sdev); +	}  	return 0;  } @@ -2908,8 +3062,9 @@ _scsih_qcmd(struct scsi_cmnd *scmd, void (*done)(struct scsi_cmnd *))  	} else  		mpi_control |= MPI2_SCSIIO_CONTROL_SIMPLEQ; - -	if ((sas_device_priv_data->flags & MPT_DEVICE_TLR_ON)) +	/* Make sure Device is not raid volume */ +	if (!_scsih_is_raid(&scmd->device->sdev_gendev) && +	    sas_is_tlr_enabled(scmd->device))  		mpi_control |= MPI2_SCSIIO_CONTROL_TLR_ON;  	smid = mpt2sas_base_get_smid_scsiio(ioc, ioc->scsi_io_cb_idx, scmd); @@ -3298,10 +3453,12 @@ _scsih_io_done(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 msix_index, u32 reply)  		    le32_to_cpu(mpi_reply->ResponseInfo) & 0xFF;  	if (!sas_device_priv_data->tlr_snoop_check) {  		sas_device_priv_data->tlr_snoop_check++; -		if ((sas_device_priv_data->flags & MPT_DEVICE_TLR_ON) && -		    response_code == MPI2_SCSITASKMGMT_RSP_INVALID_FRAME) -			sas_device_priv_data->flags &= -			    ~MPT_DEVICE_TLR_ON; +	if (!_scsih_is_raid(&scmd->device->sdev_gendev) && +		sas_is_tlr_enabled(scmd->device) && +		    response_code == MPI2_SCSITASKMGMT_RSP_INVALID_FRAME) { +			sas_disable_tlr(scmd->device); +			sdev_printk(KERN_INFO, scmd->device, "TLR disabled\n"); +		}  	}  	xfer_cnt = le32_to_cpu(mpi_reply->TransferCount); @@ -5170,11 +5327,33 @@ static void  _scsih_sas_ir_operation_status_event(struct MPT2SAS_ADAPTER *ioc,      struct fw_event_work *fw_event)  { +	Mpi2EventDataIrOperationStatus_t *event_data = fw_event->event_data; +	static struct _raid_device *raid_device; +	unsigned long flags; +	u16 handle; +  #ifdef CONFIG_SCSI_MPT2SAS_LOGGING  	if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK)  		_scsih_sas_ir_operation_status_event_debug(ioc, -		     fw_event->event_data); +		     event_data);  #endif + +	/* code added for raid transport support */ +	if (event_data->RAIDOperation == MPI2_EVENT_IR_RAIDOP_RESYNC) { + +		handle = le16_to_cpu(event_data->VolDevHandle); + +		spin_lock_irqsave(&ioc->raid_device_lock, flags); +		raid_device = _scsih_raid_device_find_by_handle(ioc, handle); +		spin_unlock_irqrestore(&ioc->raid_device_lock, flags); + +		if (!raid_device) +			return; + +		if (event_data->RAIDOperation == MPI2_EVENT_IR_RAIDOP_RESYNC) +			raid_device->percent_complete = +			    event_data->PercentComplete; +	}  }  /** @@ -5998,6 +6177,8 @@ _scsih_remove(struct pci_dev *pdev)  	struct _sas_port *mpt2sas_port;  	struct _sas_device *sas_device;  	struct _sas_node *expander_sibling; +	struct _raid_device *raid_device, *next; +	struct MPT2SAS_TARGET *sas_target_priv_data;  	struct workqueue_struct	*wq;  	unsigned long flags; @@ -6011,6 +6192,21 @@ _scsih_remove(struct pci_dev *pdev)  	if (wq)  		destroy_workqueue(wq); +	/* release all the volumes */ +	list_for_each_entry_safe(raid_device, next, &ioc->raid_device_list, +	    list) { +		if (raid_device->starget) { +			sas_target_priv_data = +			    raid_device->starget->hostdata; +			sas_target_priv_data->deleted = 1; +			scsi_remove_target(&raid_device->starget->dev); +		} +		printk(MPT2SAS_INFO_FMT "removing handle(0x%04x), wwid" +		    "(0x%016llx)\n", ioc->name,  raid_device->handle, +		    (unsigned long long) raid_device->wwid); +		_scsih_raid_device_remove(ioc, raid_device); +	} +  	/* free ports attached to the sas_host */   retry_again:  	list_for_each_entry(mpt2sas_port, @@ -6373,6 +6569,13 @@ static struct pci_driver scsih_driver = {  #endif  }; +/* raid transport support */ +static struct raid_function_template mpt2sas_raid_functions = { +	.cookie		= &scsih_driver_template, +	.is_raid	= _scsih_is_raid, +	.get_resync	= _scsih_get_resync, +	.get_state	= _scsih_get_state, +};  /**   * _scsih_init - main entry point for this driver. @@ -6392,6 +6595,12 @@ _scsih_init(void)  	    sas_attach_transport(&mpt2sas_transport_functions);  	if (!mpt2sas_transport_template)  		return -ENODEV; +	/* raid transport support */ +	mpt2sas_raid_template = raid_class_attach(&mpt2sas_raid_functions); +	if (!mpt2sas_raid_template) { +		sas_release_transport(mpt2sas_transport_template); +		return -ENODEV; +	}  	mpt2sas_base_initialize_callback_handler(); @@ -6426,8 +6635,11 @@ _scsih_init(void)  	mpt2sas_ctl_init();  	error = pci_register_driver(&scsih_driver); -	if (error) +	if (error) { +		/* raid transport support */ +		raid_class_release(mpt2sas_raid_template);  		sas_release_transport(mpt2sas_transport_template); +	}  	return error;  } @@ -6445,7 +6657,8 @@ _scsih_exit(void)  	pci_unregister_driver(&scsih_driver); -	sas_release_transport(mpt2sas_transport_template); +	mpt2sas_ctl_exit(); +  	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); @@ -6457,7 +6670,10 @@ _scsih_exit(void)  	mpt2sas_base_release_callback_handler(tm_tr_cb_idx);  	mpt2sas_base_release_callback_handler(tm_sas_control_cb_idx); -	mpt2sas_ctl_exit(); +	/* raid transport support */ +	raid_class_release(mpt2sas_raid_template); +	sas_release_transport(mpt2sas_transport_template); +  }  module_init(_scsih_init);  |