diff mbox

hpsa: add in sas transport

Message ID 20150929222154.6433.60590.stgit@brunhilda (mailing list archive)
State Superseded, archived
Headers show

Commit Message

Don Brace Sept. 29, 2015, 10:21 p.m. UTC
From: Kevin Barnett <kevin.barnett@pmcs.com>

customers want lsscsi -t to show sas addresses when
enumerating sas devices. The sas addresses are used
mainly to light drive LEDs for location.

Signed-off-by: Don Brace <don.brace@pmcs.com>
---
 drivers/scsi/hpsa.c     |  704 ++++++++++++++++++++++++++++++++++++++++++-----
 drivers/scsi/hpsa.h     |   37 ++
 drivers/scsi/hpsa_cmd.h |   14 +
 3 files changed, 677 insertions(+), 78 deletions(-)


--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Comments

Hannes Reinecke Sept. 30, 2015, 7:49 a.m. UTC | #1
On 09/30/2015 12:21 AM, Don Brace wrote:
> From: Kevin Barnett <kevin.barnett@pmcs.com>
> 
> customers want lsscsi -t to show sas addresses when
> enumerating sas devices. The sas addresses are used
> mainly to light drive LEDs for location.
> 
> Signed-off-by: Don Brace <don.brace@pmcs.com>
> ---
>  drivers/scsi/hpsa.c     |  704 ++++++++++++++++++++++++++++++++++++++++++-----
>  drivers/scsi/hpsa.h     |   37 ++
>  drivers/scsi/hpsa_cmd.h |   14 +
>  3 files changed, 677 insertions(+), 78 deletions(-)
> 
> diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c
> index 3b35de0..ae811a7 100644
> --- a/drivers/scsi/hpsa.c
> +++ b/drivers/scsi/hpsa.c
> @@ -41,6 +41,7 @@
>  #include <scsi/scsi_host.h>
>  #include <scsi/scsi_tcq.h>
>  #include <scsi/scsi_eh.h>
> +#include <scsi/scsi_transport_sas.h>
>  #include <scsi/scsi_dbg.h>
>  #include <linux/cciss_ioctl.h>
>  #include <linux/string.h>
> @@ -205,6 +206,16 @@ static struct board_type products[] = {
>  	{0xFFFF103C, "Unknown Smart Array", &SA5_access},
>  };
>  
> +static struct scsi_transport_template *hpsa_sas_transport_template;
> +static int hpsa_add_sas_host(struct ctlr_info *h);
> +static void hpsa_delete_sas_host(struct ctlr_info *h);
> +static int hpsa_add_sas_device(struct hpsa_sas_node *hpsa_sas_node,
> +			struct hpsa_scsi_dev_t *device);
> +static void hpsa_remove_sas_device(struct hpsa_scsi_dev_t *device);
> +static struct hpsa_scsi_dev_t
> +	*hpsa_find_device_by_sas_rphy(struct ctlr_info *h,
> +		struct sas_rphy *rphy);
> +
>  #define SCSI_CMD_BUSY ((struct scsi_cmnd *)&hpsa_cmd_busy)
>  static const struct scsi_cmnd hpsa_cmd_busy;
>  #define SCSI_CMD_IDLE ((struct scsi_cmnd *)&hpsa_cmd_idle)
> @@ -275,6 +286,8 @@ static void hpsa_command_resubmit_worker(struct work_struct *work);
>  static u32 lockup_detected(struct ctlr_info *h);
>  static int detect_controller_lockup(struct ctlr_info *h);
>  static int is_ext_target(struct ctlr_info *h, struct hpsa_scsi_dev_t *device);
> +static inline int hpsa_scsi_do_report_phys_luns(struct ctlr_info *h,
> +	struct ReportExtendedLUNdata *buf, int bufsize);
>  
>  static inline struct ctlr_info *sdev_to_hba(struct scsi_device *sdev)
>  {
> @@ -617,6 +630,11 @@ static const char * const raid_label[] = { "0", "4", "1(+0)", "5", "5+1", "6",
>  #define HPSA_RAID_ADM	6	/* also used for RAID 1+0 ADM */
>  #define RAID_UNKNOWN (ARRAY_SIZE(raid_label) - 1)
>  
> +static inline bool is_logical_device(struct hpsa_scsi_dev_t *device)
> +{
> +	return !device->physical_device;
> +}
> +
>  static ssize_t raid_level_show(struct device *dev,
>  	     struct device_attribute *attr, char *buf)
>  {
> @@ -637,7 +655,7 @@ static ssize_t raid_level_show(struct device *dev,
>  	}
>  
>  	/* Is this even a logical drive? */
> -	if (!is_logical_dev_addr_mode(hdev->scsi3addr)) {
> +	if (!is_logical_device(hdev)) {
>  		spin_unlock_irqrestore(&h->lock, flags);
>  		l = snprintf(buf, PAGE_SIZE, "N/A\n");
>  		return l;
Can you make this a separate patch? It's not related to the
transport class modifications, and it would reduce the size of the
individual patches.

> @@ -771,8 +789,8 @@ static ssize_t path_info_show(struct device *dev,
>  				scsi_device_type(hdev->devtype));
>  
>  		if (is_ext_target(h, hdev) ||
> -			(hdev->devtype == TYPE_RAID) ||
> -			is_logical_dev_addr_mode(hdev->scsi3addr)) {
> +			hdev->devtype == TYPE_RAID ||
> +			is_logical_device(hdev)) {
>  			output_len += snprintf(path[i] + output_len,
>  						PATH_STRING_LEN, "%s\n",
>  						active);
> @@ -791,8 +809,7 @@ static ssize_t path_info_show(struct device *dev,
>  				PATH_STRING_LEN,
>  				"PORT: %.2s ",
>  				phys_connector);
> -		if (hdev->devtype == TYPE_DISK &&
> -			hdev->expose_state != HPSA_DO_NOT_EXPOSE) {
> +		if (hdev->devtype == TYPE_DISK && hdev->expose_device) {
>  			if (box == 0 || box == 0xFF) {
>  				output_len += snprintf(path[i] + output_len,
>  					PATH_STRING_LEN,
> @@ -1148,7 +1165,7 @@ static inline void hpsa_show_dev_msg(const char *level, struct ctlr_info *h,
>  				"RAID-?" : raid_label[dev->raid_level],
>  			dev->offload_config ? '+' : '-',
>  			dev->offload_enabled ? '+' : '-',
> -			dev->expose_state);
> +			dev->expose_device);
>  }
>  
>  /* Add an entry into h->dev[] array. */
> @@ -1221,7 +1238,7 @@ lun_assigned:
>  	added[*nadded] = device;
>  	(*nadded)++;
>  	hpsa_show_dev_msg(KERN_INFO, h, device,
> -		device->expose_state & HPSA_SCSI_ADD ? "added" : "masked");
> +		device->expose_device ? "added" : "masked");
>  	device->offload_to_be_enabled = device->offload_enabled;
>  	device->offload_enabled = 0;
>  	return 0;
Same goes for the move from 'expose_state' to 'expose_device'.

> @@ -1579,7 +1596,7 @@ static void hpsa_figure_phys_disk_ptrs(struct ctlr_info *h,
>  		for (j = 0; j < ndevices; j++) {
>  			if (dev[j]->devtype != TYPE_DISK)
>  				continue;
> -			if (is_logical_dev_addr_mode(dev[j]->scsi3addr))
> +			if (is_logical_device(dev[j]))
>  				continue;
>  			if (dev[j]->ioaccel_handle != dd[i].ioaccel_handle)
>  				continue;
> @@ -1622,7 +1639,7 @@ static void hpsa_update_log_drive_phys_drive_ptrs(struct ctlr_info *h,
>  	for (i = 0; i < ndevices; i++) {
>  		if (dev[i]->devtype != TYPE_DISK)
>  			continue;
> -		if (!is_logical_dev_addr_mode(dev[i]->scsi3addr))
> +		if (!is_logical_device(dev[i]))
>  			continue;
>  
>  		/*
> @@ -1638,6 +1655,45 @@ static void hpsa_update_log_drive_phys_drive_ptrs(struct ctlr_info *h,
>  	}
>  }
>  
> +static int hpsa_add_device(struct ctlr_info *h, struct hpsa_scsi_dev_t *device)
> +{
> +	int rc;
> +
> +	if (is_logical_device(device))
> +		rc = scsi_add_device(h->scsi_host, device->bus,
> +			device->target, device->lun);
> +	else
> +		rc = hpsa_add_sas_device(h->sas_host, device);
> +
> +	return rc;
> +}
> +
> +static void hpsa_remove_device(struct ctlr_info *h,
> +			struct hpsa_scsi_dev_t *device)
> +{
> +	if (is_logical_device(device)) {
> +		struct scsi_device *sdev =
> +			scsi_device_lookup(h->scsi_host, device->bus,
> +				device->target, device->lun);
> +		if (sdev) {
> +			scsi_remove_device(sdev);
> +			scsi_device_put(sdev);
> +		} else {
> +			/*
> +			 * We don't expect to get here.  Future commands
> +			 * to this device will get a selection timeout as
> +			 * if the device were gone.
> +			 */
> +			dev_warn(&h->pdev->dev,
> +				"didn't find scsi %d:%d:%d:%d for removal.",
> +				h->scsi_host->host_no, device->bus,
> +				device->target, device->lun);
> +		}
> +	} else {
> +		hpsa_remove_sas_device(device);
> +	}
> +}
> +
>  static void adjust_hpsa_scsi_table(struct ctlr_info *h, int hostno,
>  	struct hpsa_scsi_dev_t *sd[], int nsds)
>  {
> @@ -1650,14 +1706,13 @@ static void adjust_hpsa_scsi_table(struct ctlr_info *h, int hostno,
>  	unsigned long flags;
>  	struct hpsa_scsi_dev_t **added, **removed;
>  	int nadded, nremoved;
> -	struct Scsi_Host *sh = NULL;
>  
>  	added = kzalloc(sizeof(*added) * HPSA_MAX_DEVICES, GFP_KERNEL);
>  	removed = kzalloc(sizeof(*removed) * HPSA_MAX_DEVICES, GFP_KERNEL);
>  
>  	if (!added || !removed) {
> -		dev_warn(&h->pdev->dev, "out of memory in "
> -			"adjust_hpsa_scsi_table\n");
> +		dev_warn(&h->pdev->dev, "out of memory in %s\n",
> +			__func__);
>  		goto free_and_out;
>  	}
>  
> @@ -1758,36 +1813,19 @@ static void adjust_hpsa_scsi_table(struct ctlr_info *h, int hostno,
>  	if (hostno == -1 || !changes)
>  		goto free_and_out;
>  
> -	sh = h->scsi_host;
>  	/* Notify scsi mid layer of any removed devices */
>  	for (i = 0; i < nremoved; i++) {
> -		if (removed[i]->expose_state & HPSA_SCSI_ADD) {
> -			struct scsi_device *sdev =
> -				scsi_device_lookup(sh, removed[i]->bus,
> -					removed[i]->target, removed[i]->lun);
> -			if (sdev != NULL) {
> -				scsi_remove_device(sdev);
> -				scsi_device_put(sdev);
> -			} else {
> -				/*
> -				 * We don't expect to get here.
> -				 * future cmds to this device will get selection
> -				 * timeout as if the device was gone.
> -				 */
> -				hpsa_show_dev_msg(KERN_WARNING, h, removed[i],
> -					"didn't find device for removal.");
> -			}
> -		}
> +		if (removed[i]->expose_device)
> +			hpsa_remove_device(h, removed[i]);
>  		kfree(removed[i]);
>  		removed[i] = NULL;
>  	}
>  
>  	/* Notify scsi mid layer of any added devices */
>  	for (i = 0; i < nadded; i++) {
> -		if (!(added[i]->expose_state & HPSA_SCSI_ADD))
> +		if (!(added[i]->expose_device))
>  			continue;
> -		if (scsi_add_device(sh, added[i]->bus,
> -			added[i]->target, added[i]->lun) == 0)
> +		if (hpsa_add_device(h, added[i]) == 0)
>  			continue;
>  		hpsa_show_dev_msg(KERN_WARNING, h, added[i],
>  					"addition failed, device not added.");
Sigh. Here we go again.

I've probably asked you (or Mike Miller :-) about this several times
now, but couldn't you use the _real_ LUN addresses?

Especially as you're now exposing 'real' devices, where is the point
of creating an internal LUN mapping table?

If you were expose the devices with the actual LUN address (by eg
arranging the target/RAID controller on bus '0', the RAID devices on
bus '1', and the exposed devices on bus '2') you could remove the
internal LUN mapping table and quite some complexity would go away ...

Cheers,

Hannes
Don Brace Oct. 20, 2015, 8:07 p.m. UTC | #2
PiAtLS0tLU9yaWdpbmFsIE1lc3NhZ2UtLS0tLQ0KPiBGcm9tOiBIYW5uZXMgUmVpbmVja2UgW21h
aWx0bzpoYXJlQHN1c2UuZGVdDQo+IFNlbnQ6IFdlZG5lc2RheSwgU2VwdGVtYmVyIDMwLCAyMDE1
IDI6NTAgQU0NCj4gVG86IERvbiBCcmFjZTsgbGludXgtc2NzaUB2Z2VyLmtlcm5lbC5vcmcNCj4g
U3ViamVjdDogUmU6IFtQQVRDSF0gaHBzYTogYWRkIGluIHNhcyB0cmFuc3BvcnQNCj4gDQo+IE9u
IDA5LzMwLzIwMTUgMTI6MjEgQU0sIERvbiBCcmFjZSB3cm90ZToNCj4gPiBGcm9tOiBLZXZpbiBC
YXJuZXR0IDxrZXZpbi5iYXJuZXR0QHBtY3MuY29tPg0KPiA+DQo+ID4gY3VzdG9tZXJzIHdhbnQg
bHNzY3NpIC10IHRvIHNob3cgc2FzIGFkZHJlc3NlcyB3aGVuDQo+ID4gZW51bWVyYXRpbmcgc2Fz
IGRldmljZXMuIFRoZSBzYXMgYWRkcmVzc2VzIGFyZSB1c2VkDQo+ID4gbWFpbmx5IHRvIGxpZ2h0
IGRyaXZlIExFRHMgZm9yIGxvY2F0aW9uLg0KPiA+DQo+ID4gU2lnbmVkLW9mZi1ieTogRG9uIEJy
YWNlIDxkb24uYnJhY2VAcG1jcy5jb20+DQo+ID4gLS0tDQo+ID4gIGRyaXZlcnMvc2NzaS9ocHNh
LmMgICAgIHwgIDcwNA0KPiArKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysr
KystLS0tLQ0KPiA+ICBkcml2ZXJzL3Njc2kvaHBzYS5oICAgICB8ICAgMzcgKysNCj4gPiAgZHJp
dmVycy9zY3NpL2hwc2FfY21kLmggfCAgIDE0ICsNCj4gPiAgMyBmaWxlcyBjaGFuZ2VkLCA2Nzcg
aW5zZXJ0aW9ucygrKSwgNzggZGVsZXRpb25zKC0pDQo+ID4NCj4gPiBkaWZmIC0tZ2l0IGEvZHJp
dmVycy9zY3NpL2hwc2EuYyBiL2RyaXZlcnMvc2NzaS9ocHNhLmMNCj4gPiBpbmRleCAzYjM1ZGUw
Li5hZTgxMWE3IDEwMDY0NA0KPiA+ICAJLyogTm90aWZ5IHNjc2kgbWlkIGxheWVyIG9mIGFueSBh
ZGRlZCBkZXZpY2VzICovDQo+ID4gIAlmb3IgKGkgPSAwOyBpIDwgbmFkZGVkOyBpKyspIHsNCj4g
PiAtCQlpZiAoIShhZGRlZFtpXS0+ZXhwb3NlX3N0YXRlICYgSFBTQV9TQ1NJX0FERCkpDQo+ID4g
KwkJaWYgKCEoYWRkZWRbaV0tPmV4cG9zZV9kZXZpY2UpKQ0KPiA+ICAJCQljb250aW51ZTsNCj4g
PiAtCQlpZiAoc2NzaV9hZGRfZGV2aWNlKHNoLCBhZGRlZFtpXS0+YnVzLA0KPiA+IC0JCQlhZGRl
ZFtpXS0+dGFyZ2V0LCBhZGRlZFtpXS0+bHVuKSA9PSAwKQ0KPiA+ICsJCWlmIChocHNhX2FkZF9k
ZXZpY2UoaCwgYWRkZWRbaV0pID09IDApDQo+ID4gIAkJCWNvbnRpbnVlOw0KPiA+ICAJCWhwc2Ff
c2hvd19kZXZfbXNnKEtFUk5fV0FSTklORywgaCwgYWRkZWRbaV0sDQo+ID4gIAkJCQkJImFkZGl0
aW9uIGZhaWxlZCwgZGV2aWNlIG5vdCBhZGRlZC4iKTsNCj4gU2lnaC4gSGVyZSB3ZSBnbyBhZ2Fp
bi4NCj4gDQo+IEkndmUgcHJvYmFibHkgYXNrZWQgeW91IChvciBNaWtlIE1pbGxlciA6LSkgYWJv
dXQgdGhpcyBzZXZlcmFsIHRpbWVzDQo+IG5vdywgYnV0IGNvdWxkbid0IHlvdSB1c2UgdGhlIF9y
ZWFsXyBMVU4gYWRkcmVzc2VzPw0KPiANCj4gRXNwZWNpYWxseSBhcyB5b3UncmUgbm93IGV4cG9z
aW5nICdyZWFsJyBkZXZpY2VzLCB3aGVyZSBpcyB0aGUgcG9pbnQNCj4gb2YgY3JlYXRpbmcgYW4g
aW50ZXJuYWwgTFVOIG1hcHBpbmcgdGFibGU/DQo+IA0KPiBJZiB5b3Ugd2VyZSBleHBvc2UgdGhl
IGRldmljZXMgd2l0aCB0aGUgYWN0dWFsIExVTiBhZGRyZXNzIChieSBlZw0KPiBhcnJhbmdpbmcg
dGhlIHRhcmdldC9SQUlEIGNvbnRyb2xsZXIgb24gYnVzICcwJywgdGhlIFJBSUQgZGV2aWNlcyBv
bg0KPiBidXMgJzEnLCBhbmQgdGhlIGV4cG9zZWQgZGV2aWNlcyBvbiBidXMgJzInKSB5b3UgY291
bGQgcmVtb3ZlIHRoZQ0KPiBpbnRlcm5hbCBMVU4gbWFwcGluZyB0YWJsZSBhbmQgcXVpdGUgc29t
ZSBjb21wbGV4aXR5IHdvdWxkIGdvIGF3YXkgLi4uDQo+IA0KDQpUaGlzIGNhbiBiZSBkb25lLg0K
DQpIb3dldmVyLCBhdCB0aGlzIHBvaW50LCBJIGRvIG5vdCB3YW50IHRvIGNoYW5nZSBocHNhIHdp
dGhvdXQgbW9yZSB0ZXN0aW5nLiANCkknbGwgbWFrZSBhbm90aGVyIHBhdGNoIGFuZCBzZW5kIGl0
IHVwIGFmdGVyIGl0IGhhcyBhIGNoYW5jZSB0byBiZSB0ZXN0ZWQgaW4gb3VyIGxhYnMuDQpIb3Bl
IHRoaXMgaXMgT0suDQoNCkkgc3BsaXQgb3V0IHRoZSBvdGhlciBjaGFuZ2VzIHRvIHNlcGFyYXRl
IHBhdGNoZXMgYXMgeW91IHJlY29tbWVuZGVkLg0K
--
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Hannes Reinecke Oct. 21, 2015, 6:01 a.m. UTC | #3
On 10/20/2015 10:07 PM, Don Brace wrote:
>> -----Original Message-----
>> From: Hannes Reinecke [mailto:hare@suse.de]
>> Sent: Wednesday, September 30, 2015 2:50 AM
>> To: Don Brace; linux-scsi@vger.kernel.org
>> Subject: Re: [PATCH] hpsa: add in sas transport
>>
>> On 09/30/2015 12:21 AM, Don Brace wrote:
>>> From: Kevin Barnett <kevin.barnett@pmcs.com>
>>>
>>> customers want lsscsi -t to show sas addresses when
>>> enumerating sas devices. The sas addresses are used
>>> mainly to light drive LEDs for location.
>>>
>>> Signed-off-by: Don Brace <don.brace@pmcs.com>
>>> ---
>>>  drivers/scsi/hpsa.c     |  704
>> ++++++++++++++++++++++++++++++++++++++++++-----
>>>  drivers/scsi/hpsa.h     |   37 ++
>>>  drivers/scsi/hpsa_cmd.h |   14 +
>>>  3 files changed, 677 insertions(+), 78 deletions(-)
>>>
>>> diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c
>>> index 3b35de0..ae811a7 100644
>>>  	/* Notify scsi mid layer of any added devices */
>>>  	for (i = 0; i < nadded; i++) {
>>> -		if (!(added[i]->expose_state & HPSA_SCSI_ADD))
>>> +		if (!(added[i]->expose_device))
>>>  			continue;
>>> -		if (scsi_add_device(sh, added[i]->bus,
>>> -			added[i]->target, added[i]->lun) == 0)
>>> +		if (hpsa_add_device(h, added[i]) == 0)
>>>  			continue;
>>>  		hpsa_show_dev_msg(KERN_WARNING, h, added[i],
>>>  					"addition failed, device not added.");
>> Sigh. Here we go again.
>>
>> I've probably asked you (or Mike Miller :-) about this several times
>> now, but couldn't you use the _real_ LUN addresses?
>>
>> Especially as you're now exposing 'real' devices, where is the point
>> of creating an internal LUN mapping table?
>>
>> If you were expose the devices with the actual LUN address (by eg
>> arranging the target/RAID controller on bus '0', the RAID devices on
>> bus '1', and the exposed devices on bus '2') you could remove the
>> internal LUN mapping table and quite some complexity would go away ...
>>
> 
> This can be done.
> 
> However, at this point, I do not want to change hpsa without more testing. 
> I'll make another patch and send it up after it has a chance to be tested in our labs.
> Hope this is OK.
> 
Sure. Just wanted to raise this (again :-), as I think it'd be a
good cleanup.

> I split out the other changes to separate patches as you recommended.
> 
Please, go ahead.

Cheers,

Hannes
diff mbox

Patch

diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c
index 3b35de0..ae811a7 100644
--- a/drivers/scsi/hpsa.c
+++ b/drivers/scsi/hpsa.c
@@ -41,6 +41,7 @@ 
 #include <scsi/scsi_host.h>
 #include <scsi/scsi_tcq.h>
 #include <scsi/scsi_eh.h>
+#include <scsi/scsi_transport_sas.h>
 #include <scsi/scsi_dbg.h>
 #include <linux/cciss_ioctl.h>
 #include <linux/string.h>
@@ -205,6 +206,16 @@  static struct board_type products[] = {
 	{0xFFFF103C, "Unknown Smart Array", &SA5_access},
 };
 
+static struct scsi_transport_template *hpsa_sas_transport_template;
+static int hpsa_add_sas_host(struct ctlr_info *h);
+static void hpsa_delete_sas_host(struct ctlr_info *h);
+static int hpsa_add_sas_device(struct hpsa_sas_node *hpsa_sas_node,
+			struct hpsa_scsi_dev_t *device);
+static void hpsa_remove_sas_device(struct hpsa_scsi_dev_t *device);
+static struct hpsa_scsi_dev_t
+	*hpsa_find_device_by_sas_rphy(struct ctlr_info *h,
+		struct sas_rphy *rphy);
+
 #define SCSI_CMD_BUSY ((struct scsi_cmnd *)&hpsa_cmd_busy)
 static const struct scsi_cmnd hpsa_cmd_busy;
 #define SCSI_CMD_IDLE ((struct scsi_cmnd *)&hpsa_cmd_idle)
@@ -275,6 +286,8 @@  static void hpsa_command_resubmit_worker(struct work_struct *work);
 static u32 lockup_detected(struct ctlr_info *h);
 static int detect_controller_lockup(struct ctlr_info *h);
 static int is_ext_target(struct ctlr_info *h, struct hpsa_scsi_dev_t *device);
+static inline int hpsa_scsi_do_report_phys_luns(struct ctlr_info *h,
+	struct ReportExtendedLUNdata *buf, int bufsize);
 
 static inline struct ctlr_info *sdev_to_hba(struct scsi_device *sdev)
 {
@@ -617,6 +630,11 @@  static const char * const raid_label[] = { "0", "4", "1(+0)", "5", "5+1", "6",
 #define HPSA_RAID_ADM	6	/* also used for RAID 1+0 ADM */
 #define RAID_UNKNOWN (ARRAY_SIZE(raid_label) - 1)
 
+static inline bool is_logical_device(struct hpsa_scsi_dev_t *device)
+{
+	return !device->physical_device;
+}
+
 static ssize_t raid_level_show(struct device *dev,
 	     struct device_attribute *attr, char *buf)
 {
@@ -637,7 +655,7 @@  static ssize_t raid_level_show(struct device *dev,
 	}
 
 	/* Is this even a logical drive? */
-	if (!is_logical_dev_addr_mode(hdev->scsi3addr)) {
+	if (!is_logical_device(hdev)) {
 		spin_unlock_irqrestore(&h->lock, flags);
 		l = snprintf(buf, PAGE_SIZE, "N/A\n");
 		return l;
@@ -771,8 +789,8 @@  static ssize_t path_info_show(struct device *dev,
 				scsi_device_type(hdev->devtype));
 
 		if (is_ext_target(h, hdev) ||
-			(hdev->devtype == TYPE_RAID) ||
-			is_logical_dev_addr_mode(hdev->scsi3addr)) {
+			hdev->devtype == TYPE_RAID ||
+			is_logical_device(hdev)) {
 			output_len += snprintf(path[i] + output_len,
 						PATH_STRING_LEN, "%s\n",
 						active);
@@ -791,8 +809,7 @@  static ssize_t path_info_show(struct device *dev,
 				PATH_STRING_LEN,
 				"PORT: %.2s ",
 				phys_connector);
-		if (hdev->devtype == TYPE_DISK &&
-			hdev->expose_state != HPSA_DO_NOT_EXPOSE) {
+		if (hdev->devtype == TYPE_DISK && hdev->expose_device) {
 			if (box == 0 || box == 0xFF) {
 				output_len += snprintf(path[i] + output_len,
 					PATH_STRING_LEN,
@@ -1148,7 +1165,7 @@  static inline void hpsa_show_dev_msg(const char *level, struct ctlr_info *h,
 				"RAID-?" : raid_label[dev->raid_level],
 			dev->offload_config ? '+' : '-',
 			dev->offload_enabled ? '+' : '-',
-			dev->expose_state);
+			dev->expose_device);
 }
 
 /* Add an entry into h->dev[] array. */
@@ -1221,7 +1238,7 @@  lun_assigned:
 	added[*nadded] = device;
 	(*nadded)++;
 	hpsa_show_dev_msg(KERN_INFO, h, device,
-		device->expose_state & HPSA_SCSI_ADD ? "added" : "masked");
+		device->expose_device ? "added" : "masked");
 	device->offload_to_be_enabled = device->offload_enabled;
 	device->offload_enabled = 0;
 	return 0;
@@ -1579,7 +1596,7 @@  static void hpsa_figure_phys_disk_ptrs(struct ctlr_info *h,
 		for (j = 0; j < ndevices; j++) {
 			if (dev[j]->devtype != TYPE_DISK)
 				continue;
-			if (is_logical_dev_addr_mode(dev[j]->scsi3addr))
+			if (is_logical_device(dev[j]))
 				continue;
 			if (dev[j]->ioaccel_handle != dd[i].ioaccel_handle)
 				continue;
@@ -1622,7 +1639,7 @@  static void hpsa_update_log_drive_phys_drive_ptrs(struct ctlr_info *h,
 	for (i = 0; i < ndevices; i++) {
 		if (dev[i]->devtype != TYPE_DISK)
 			continue;
-		if (!is_logical_dev_addr_mode(dev[i]->scsi3addr))
+		if (!is_logical_device(dev[i]))
 			continue;
 
 		/*
@@ -1638,6 +1655,45 @@  static void hpsa_update_log_drive_phys_drive_ptrs(struct ctlr_info *h,
 	}
 }
 
+static int hpsa_add_device(struct ctlr_info *h, struct hpsa_scsi_dev_t *device)
+{
+	int rc;
+
+	if (is_logical_device(device))
+		rc = scsi_add_device(h->scsi_host, device->bus,
+			device->target, device->lun);
+	else
+		rc = hpsa_add_sas_device(h->sas_host, device);
+
+	return rc;
+}
+
+static void hpsa_remove_device(struct ctlr_info *h,
+			struct hpsa_scsi_dev_t *device)
+{
+	if (is_logical_device(device)) {
+		struct scsi_device *sdev =
+			scsi_device_lookup(h->scsi_host, device->bus,
+				device->target, device->lun);
+		if (sdev) {
+			scsi_remove_device(sdev);
+			scsi_device_put(sdev);
+		} else {
+			/*
+			 * We don't expect to get here.  Future commands
+			 * to this device will get a selection timeout as
+			 * if the device were gone.
+			 */
+			dev_warn(&h->pdev->dev,
+				"didn't find scsi %d:%d:%d:%d for removal.",
+				h->scsi_host->host_no, device->bus,
+				device->target, device->lun);
+		}
+	} else {
+		hpsa_remove_sas_device(device);
+	}
+}
+
 static void adjust_hpsa_scsi_table(struct ctlr_info *h, int hostno,
 	struct hpsa_scsi_dev_t *sd[], int nsds)
 {
@@ -1650,14 +1706,13 @@  static void adjust_hpsa_scsi_table(struct ctlr_info *h, int hostno,
 	unsigned long flags;
 	struct hpsa_scsi_dev_t **added, **removed;
 	int nadded, nremoved;
-	struct Scsi_Host *sh = NULL;
 
 	added = kzalloc(sizeof(*added) * HPSA_MAX_DEVICES, GFP_KERNEL);
 	removed = kzalloc(sizeof(*removed) * HPSA_MAX_DEVICES, GFP_KERNEL);
 
 	if (!added || !removed) {
-		dev_warn(&h->pdev->dev, "out of memory in "
-			"adjust_hpsa_scsi_table\n");
+		dev_warn(&h->pdev->dev, "out of memory in %s\n",
+			__func__);
 		goto free_and_out;
 	}
 
@@ -1758,36 +1813,19 @@  static void adjust_hpsa_scsi_table(struct ctlr_info *h, int hostno,
 	if (hostno == -1 || !changes)
 		goto free_and_out;
 
-	sh = h->scsi_host;
 	/* Notify scsi mid layer of any removed devices */
 	for (i = 0; i < nremoved; i++) {
-		if (removed[i]->expose_state & HPSA_SCSI_ADD) {
-			struct scsi_device *sdev =
-				scsi_device_lookup(sh, removed[i]->bus,
-					removed[i]->target, removed[i]->lun);
-			if (sdev != NULL) {
-				scsi_remove_device(sdev);
-				scsi_device_put(sdev);
-			} else {
-				/*
-				 * We don't expect to get here.
-				 * future cmds to this device will get selection
-				 * timeout as if the device was gone.
-				 */
-				hpsa_show_dev_msg(KERN_WARNING, h, removed[i],
-					"didn't find device for removal.");
-			}
-		}
+		if (removed[i]->expose_device)
+			hpsa_remove_device(h, removed[i]);
 		kfree(removed[i]);
 		removed[i] = NULL;
 	}
 
 	/* Notify scsi mid layer of any added devices */
 	for (i = 0; i < nadded; i++) {
-		if (!(added[i]->expose_state & HPSA_SCSI_ADD))
+		if (!(added[i]->expose_device))
 			continue;
-		if (scsi_add_device(sh, added[i]->bus,
-			added[i]->target, added[i]->lun) == 0)
+		if (hpsa_add_device(h, added[i]) == 0)
 			continue;
 		hpsa_show_dev_msg(KERN_WARNING, h, added[i],
 					"addition failed, device not added.");
@@ -1826,15 +1864,28 @@  static int hpsa_slave_alloc(struct scsi_device *sdev)
 	struct hpsa_scsi_dev_t *sd;
 	unsigned long flags;
 	struct ctlr_info *h;
+	struct scsi_target *starget;
+	struct sas_rphy *rphy;
 
 	h = sdev_to_hba(sdev);
 	spin_lock_irqsave(&h->devlock, flags);
-	sd = lookup_hpsa_scsi_dev(h, sdev_channel(sdev),
-		sdev_id(sdev), sdev->lun);
-	if (likely(sd)) {
-		atomic_set(&sd->ioaccel_cmds_out, 0);
-		sdev->hostdata = (sd->expose_state & HPSA_SCSI_ADD) ? sd : NULL;
+
+	if (sdev_channel(sdev) == HPSA_PHYSICAL_DEVICE_BUS) {
+		starget = scsi_target(sdev);
+		rphy = target_to_rphy(starget);
+		sd = hpsa_find_device_by_sas_rphy(h, rphy);
+		if (sd) {
+			sd->target = sdev_id(sdev);
+			sd->lun = sdev->lun;
+		}
 	} else
+		sd = lookup_hpsa_scsi_dev(h, sdev_channel(sdev),
+			sdev_id(sdev), sdev->lun);
+
+	if (sd && sd->expose_device) {
+		atomic_set(&sd->ioaccel_cmds_out, 0);
+		sdev->hostdata = sd;
+	} else if (sd)
 		sdev->hostdata = NULL;
 	spin_unlock_irqrestore(&h->devlock, flags);
 	return 0;
@@ -1847,7 +1898,7 @@  static int hpsa_slave_configure(struct scsi_device *sdev)
 	int queue_depth;
 
 	sd = sdev->hostdata;
-	sdev->no_uld_attach = !sd || !(sd->expose_state & HPSA_ULD_ATTACH);
+	sdev->no_uld_attach = !sd || !sd->expose_device;
 
 	if (sd)
 		queue_depth = sd->queue_depth != 0 ?
@@ -2210,7 +2261,7 @@  static void process_ioaccel2_completion(struct ctlr_info *h,
 	 * the normal I/O path so the controller can handle whatever's
 	 * wrong.
 	 */
-	if (is_logical_dev_addr_mode(dev->scsi3addr) &&
+	if (is_logical_device(dev) &&
 		c2->error_data.serv_response ==
 			IOACCEL2_SERV_RESPONSE_FAILURE) {
 		if (c2->error_data.status ==
@@ -2330,7 +2381,7 @@  static void complete_scsi_command(struct CommandList *cp)
 		 * the normal I/O path so the controller can handle whatever's
 		 * wrong.
 		 */
-		if (is_logical_dev_addr_mode(dev->scsi3addr)) {
+		if (is_logical_device(dev)) {
 			if (ei->CommandStatus == CMD_IOACCEL_DISABLED)
 				dev->offload_enabled = 0;
 			return hpsa_retry_cmd(h, cp);
@@ -2984,6 +3035,38 @@  out:
 	return rc;
 }
 
+static int hpsa_bmic_sense_subsystem_information(struct ctlr_info *h,
+		unsigned char scsi3addr[], u16 bmic_device_index,
+		struct bmic_sense_subsystem_info *buf, size_t bufsize)
+{
+	int rc = IO_OK;
+	struct CommandList *c;
+	struct ErrorInfo *ei;
+
+	c = cmd_alloc(h);
+
+	rc = fill_cmd(c, BMIC_SENSE_SUBSYSTEM_INFORMATION, h, buf, bufsize,
+		0, RAID_CTLR_LUNID, TYPE_CMD);
+	if (rc)
+		goto out;
+
+	c->Request.CDB[2] = bmic_device_index & 0xff;
+	c->Request.CDB[9] = (bmic_device_index >> 8) & 0xff;
+
+	rc = hpsa_scsi_do_simple_cmd_with_retry(h, c,
+				PCI_DMA_FROMDEVICE, NO_TIMEOUT);
+	if (rc)
+		goto out;
+	ei = c->err_info;
+	if (ei->CommandStatus != 0 && ei->CommandStatus != CMD_DATA_UNDERRUN) {
+		hpsa_scsi_interpret_error(h, c);
+		rc = -1;
+	}
+out:
+	cmd_free(h, c);
+	return rc;
+}
+
 static int hpsa_bmic_id_physical_device(struct ctlr_info *h,
 		unsigned char scsi3addr[], u16 bmic_device_index,
 		struct bmic_identify_physical_device *buf, size_t bufsize)
@@ -3010,6 +3093,7 @@  static int hpsa_bmic_id_physical_device(struct ctlr_info *h,
 	}
 out:
 	cmd_free(h, c);
+
 	return rc;
 }
 
@@ -3095,9 +3179,60 @@  out:
 	return;
 }
 
-/* Get the device id from inquiry page 0x83 */
+static u64 hpsa_get_sas_address_from_report_physical(struct ctlr_info *h,
+	unsigned char *scsi3addr)
+{
+	struct ReportExtendedLUNdata *physdev;
+	u32 nphysicals;
+	u64 sa = 0;
+	int i;
+
+	physdev = kzalloc(sizeof(*physdev), GFP_KERNEL);
+	if (!physdev)
+		return 0;
+
+	if (hpsa_scsi_do_report_phys_luns(h, physdev, sizeof(*physdev))) {
+		dev_err(&h->pdev->dev, "report physical LUNs failed.\n");
+		kfree(physdev);
+		return 0;
+	}
+	nphysicals = get_unaligned_be32(physdev->LUNListLength) / 24;
+
+	for (i = 0; i < nphysicals; i++) {
+		if (!memcmp(&physdev->LUN[i].lunid[0], scsi3addr, 8))
+			sa = get_unaligned_be64(&physdev->LUN[i].wwid[0]);
+	}
+
+	kfree(physdev);
+
+	return sa;
+}
+
+static void hpsa_get_sas_address(struct ctlr_info *h, unsigned char *scsi3addr,
+				struct hpsa_scsi_dev_t *dev)
+{
+	int rc;
+	u64 sa = 0;
+
+	if (is_hba_lunid(scsi3addr)) {
+		struct bmic_sense_subsystem_info ssi;
+
+		rc = hpsa_bmic_sense_subsystem_information(h,
+					scsi3addr, 0, &ssi, sizeof(ssi));
+		if (rc == 0) {
+			sa = get_unaligned_be64(&ssi.primary_world_wide_id[0]);
+			h->sas_address = sa;
+		}
+	} else {
+		sa = hpsa_get_sas_address_from_report_physical(h, scsi3addr);
+	}
+
+	dev->sas_address = sa;
+}
+
+/* Get a device id from inquiry page 0x83 */
 static int hpsa_get_device_id(struct ctlr_info *h, unsigned char *scsi3addr,
-	unsigned char *device_id, int buflen)
+	unsigned char *device_id, int index, int buflen)
 {
 	int rc;
 	unsigned char *buf;
@@ -3109,8 +3244,10 @@  static int hpsa_get_device_id(struct ctlr_info *h, unsigned char *scsi3addr,
 		return -ENOMEM;
 	rc = hpsa_scsi_do_inquiry(h, scsi3addr, VPD_PAGE | 0x83, buf, 64);
 	if (rc == 0)
-		memcpy(device_id, &buf[8], buflen);
+		memcpy(device_id, &buf[index], buflen);
+
 	kfree(buf);
+
 	return rc != 0;
 }
 
@@ -3339,6 +3476,18 @@  static int hpsa_device_supports_aborts(struct ctlr_info *h,
 	return rc;
 }
 
+static void sanitize_inquiry_string(unsigned char *s, int len)
+{
+	bool terminated = false;
+
+	for (; len > 0; (--len, ++s)) {
+		if (*s == 0)
+			terminated = true;
+		if (terminated || *s < 0x20 || *s > 0x7e)
+			*s = ' ';
+	}
+}
+
 static int hpsa_update_device_info(struct ctlr_info *h,
 	unsigned char scsi3addr[], struct hpsa_scsi_dev_t *this_device,
 	unsigned char *is_OBDR_device)
@@ -3365,6 +3514,9 @@  static int hpsa_update_device_info(struct ctlr_info *h,
 		goto bail_out;
 	}
 
+	sanitize_inquiry_string(&inq_buff[8], 8);
+	sanitize_inquiry_string(&inq_buff[16], 16);
+
 	this_device->devtype = (inq_buff[0] & 0x1f);
 	memcpy(this_device->scsi3addr, scsi3addr, 8);
 	memcpy(this_device->vendor, &inq_buff[8],
@@ -3373,7 +3525,7 @@  static int hpsa_update_device_info(struct ctlr_info *h,
 		sizeof(this_device->model));
 	memset(this_device->device_id, 0,
 		sizeof(this_device->device_id));
-	hpsa_get_device_id(h, scsi3addr, this_device->device_id,
+	hpsa_get_device_id(h, scsi3addr, this_device->device_id, 8,
 		sizeof(this_device->device_id));
 
 	if (this_device->devtype == TYPE_DISK &&
@@ -3460,38 +3612,37 @@  static int is_ext_target(struct ctlr_info *h, struct hpsa_scsi_dev_t *device)
 	return 0;
 }
 
-/* Helper function to assign bus, target, lun mapping of devices.
- * Puts non-external target logical volumes on bus 0, external target logical
- * volumes on bus 1, physical devices on bus 2. and the hba on bus 3.
+/*
+ * Helper function to assign bus, target, lun mapping of devices.
  * Logical drive target and lun are assigned at this time, but
  * physical device lun and target assignment are deferred (assigned
  * in hpsa_find_target_lun, called by hpsa_scsi_add_entry.)
- */
+*/
 static void figure_bus_target_lun(struct ctlr_info *h,
 	u8 *lunaddrbytes, struct hpsa_scsi_dev_t *device)
 {
-	u32 lunid = le32_to_cpu(*((__le32 *) lunaddrbytes));
+	u32 lunid = le32_to_cpu(*((__le32 *)lunaddrbytes));
 
 	if (!is_logical_dev_addr_mode(lunaddrbytes)) {
 		/* physical device, target and lun filled in later */
 		if (is_hba_lunid(lunaddrbytes))
-			hpsa_set_bus_target_lun(device, 3, 0, lunid & 0x3fff);
+			hpsa_set_bus_target_lun(device,
+					HPSA_HBA_BUS, 0, lunid & 0x3fff);
 		else
 			/* defer target, lun assignment for physical devices */
-			hpsa_set_bus_target_lun(device, 2, -1, -1);
+			hpsa_set_bus_target_lun(device,
+					HPSA_PHYSICAL_DEVICE_BUS, -1, -1);
 		return;
 	}
 	/* It's a logical device */
 	if (is_ext_target(h, device)) {
-		/* external target way, put logicals on bus 1
-		 * and match target/lun numbers box
-		 * reports, other smart array, bus 0, target 0, match lunid
-		 */
 		hpsa_set_bus_target_lun(device,
-			1, (lunid >> 16) & 0x3fff, lunid & 0x00ff);
+			HPSA_EXTERNAL_RAID_VOLUME_BUS, (lunid >> 16) & 0x3fff,
+			lunid & 0x00ff);
 		return;
 	}
-	hpsa_set_bus_target_lun(device, 0, 0, lunid & 0x3fff);
+	hpsa_set_bus_target_lun(device, HPSA_RAID_VOLUME_BUS,
+				0, lunid & 0x3fff);
 }
 
 /*
@@ -3620,6 +3771,11 @@  static int hpsa_gather_lun_info(struct ctlr_info *h,
 	return 0;
 }
 
+/*
+ * Helper function that figures out where the LUN ID info is coming from
+ * given index i, lists of physical and logical devices, where in
+ * the list the raid controller is supposed to appear (first or last).
+ */
 static u8 *figure_lunaddrbytes(struct ctlr_info *h, int raid_ctlr_position,
 	int i, int nphysicals, int nlogicals,
 	struct ReportExtendedLUNdata *physdev_list,
@@ -3724,6 +3880,7 @@  static void hpsa_update_scsi_devices(struct ctlr_info *h, int hostno)
 	int ncurrent = 0;
 	int i, n_ext_target_devs, ndevs_to_allocate;
 	int raid_ctlr_position;
+	bool physical_device;
 	DECLARE_BITMAP(lunzerobits, MAX_EXT_TARGETS);
 
 	currentsd = kzalloc(sizeof(*currentsd) * HPSA_MAX_DEVICES, GFP_KERNEL);
@@ -3778,14 +3935,15 @@  static void hpsa_update_scsi_devices(struct ctlr_info *h, int hostno)
 		u8 *lunaddrbytes, is_OBDR = 0;
 		int phys_dev_index = i - (raid_ctlr_position == 0);
 
+		physical_device = i < nphysicals + (raid_ctlr_position == 0);
+
 		/* Figure out where the LUN ID info is coming from */
 		lunaddrbytes = figure_lunaddrbytes(h, raid_ctlr_position,
 			i, nphysicals, nlogicals, physdev_list, logdev_list);
 
 		/* skip masked non-disk devices */
-		if (MASKED_DEVICE(lunaddrbytes))
-			if (i < nphysicals + (raid_ctlr_position == 0) &&
-				(physdev_list->LUN[phys_dev_index].device_flags & 0x01))
+		if (MASKED_DEVICE(lunaddrbytes) && physical_device &&
+			(physdev_list->LUN[phys_dev_index].device_flags & 0x01))
 				continue;
 
 		/* Get device type, vendor, model, device id */
@@ -3800,26 +3958,34 @@  static void hpsa_update_scsi_devices(struct ctlr_info *h, int hostno)
 		 * For external target devices, we have to insert a LUN 0 which
 		 * doesn't show up in CCISS_REPORT_PHYSICAL data, but there
 		 * is nonetheless an enclosure device there.  We have to
-		 * present that otherwise linux won't find anything if
-		 * there is no lun 0.
+		 * present that otherwise Linux won't find anything if
+		 * there is no LUN 0.
 		 */
 		if (add_ext_target_dev(h, tmpdevice, this_device,
-				lunaddrbytes, lunzerobits,
-				&n_ext_target_devs)) {
+			lunaddrbytes, lunzerobits,
+			&n_ext_target_devs)) {
 			ncurrent++;
 			this_device = currentsd[ncurrent];
 		}
 
 		*this_device = *tmpdevice;
+		this_device->physical_device = physical_device;
 
-		/* do not expose masked devices */
-		if (MASKED_DEVICE(lunaddrbytes) &&
-			i < nphysicals + (raid_ctlr_position == 0)) {
-			this_device->expose_state = HPSA_DO_NOT_EXPOSE;
-		} else {
-			this_device->expose_state =
-					HPSA_SG_ATTACH | HPSA_ULD_ATTACH;
-		}
+		/*
+		 * Expose all devices except for physical devices that
+		 * are masked.
+		 */
+		if (this_device->physical_device &&
+			MASKED_DEVICE(lunaddrbytes))
+				this_device->expose_device = 0;
+		else
+			this_device->expose_device = 1;
+
+		/*
+		 * Get the SAS address for physical devices that are exposed.
+		 */
+		if (this_device->physical_device && this_device->expose_device)
+			hpsa_get_sas_address(h, lunaddrbytes, this_device);
 
 		switch (this_device->devtype) {
 		case TYPE_ROM:
@@ -3834,7 +4000,7 @@  static void hpsa_update_scsi_devices(struct ctlr_info *h, int hostno)
 				ncurrent++;
 			break;
 		case TYPE_DISK:
-			if (i < nphysicals + (raid_ctlr_position == 0)) {
+			if (this_device->physical_device) {
 				/* The disk is in HBA mode. */
 				/* Never use RAID mapper in HBA mode. */
 				this_device->offload_enabled = 0;
@@ -3849,8 +4015,6 @@  static void hpsa_update_scsi_devices(struct ctlr_info *h, int hostno)
 		case TYPE_MEDIUM_CHANGER:
 		case TYPE_ENCLOSURE:
 			ncurrent++;
-			this_device->expose_state =
-					HPSA_SG_ATTACH | HPSA_ULD_ATTACH;
 			break;
 		case TYPE_RAID:
 			/* Only present the Smartarray HBA as a RAID controller.
@@ -3868,6 +4032,10 @@  static void hpsa_update_scsi_devices(struct ctlr_info *h, int hostno)
 		if (ncurrent >= HPSA_MAX_DEVICES)
 			break;
 	}
+
+	if (h->sas_host == NULL)
+		hpsa_add_sas_host(h);
+
 	adjust_hpsa_scsi_table(h, hostno, currentsd, ncurrent);
 out:
 	kfree(tmpdevice);
@@ -4998,6 +5166,7 @@  static int hpsa_scsi_host_alloc(struct ctlr_info *h)
 	sh->can_queue = h->nr_cmds - HPSA_NRESERVED_CMDS;
 	sh->cmd_per_lun = sh->can_queue;
 	sh->sg_tablesize = h->maxsgentries;
+	sh->transportt = hpsa_sas_transport_template;
 	sh->hostdata[0] = (unsigned long) h;
 	sh->irq = h->intr[h->intr_mode];
 	sh->unique_id = sh->irq;
@@ -6325,6 +6494,16 @@  static int fill_cmd(struct CommandList *c, u8 cmd, struct ctlr_info *h,
 			c->Request.CDB[7] = (size >> 16) & 0xFF;
 			c->Request.CDB[8] = (size >> 8) & 0XFF;
 			break;
+		case BMIC_SENSE_SUBSYSTEM_INFORMATION:
+			c->Request.CDBLen = 10;
+			c->Request.type_attr_dir =
+				TYPE_ATTR_DIR(cmd_type, ATTR_SIMPLE, XFER_READ);
+			c->Request.Timeout = 0;
+			c->Request.CDB[0] = BMIC_READ;
+			c->Request.CDB[6] = BMIC_SENSE_SUBSYSTEM_INFORMATION;
+			c->Request.CDB[7] = (size >> 16) & 0xFF;
+			c->Request.CDB[8] = (size >> 8) & 0XFF;
+			break;
 		default:
 			dev_warn(&h->pdev->dev, "unknown command 0x%c\n", cmd);
 			BUG();
@@ -8324,6 +8503,11 @@  static void hpsa_remove_one(struct pci_dev *pdev)
 	free_percpu(h->lockup_detected);		/* init_one 2 */
 	h->lockup_detected = NULL;			/* init_one 2 */
 	/* (void) pci_disable_pcie_error_reporting(pdev); */	/* init_one 1 */
+
+	printk("%s: Calling hpsa_delete_sas_host\n", __func__);
+	hpsa_delete_sas_host(h);
+	printk("%s: Called hpsa_delete_sas_host\n", __func__);
+
 	kfree(h);					/* init_one 1 */
 }
 
@@ -8786,18 +8970,384 @@  static void hpsa_drain_accel_commands(struct ctlr_info *h)
 	} while (1);
 }
 
+static struct hpsa_sas_phy *hpsa_alloc_sas_phy(
+				struct hpsa_sas_port *hpsa_sas_port)
+{
+	struct hpsa_sas_phy *hpsa_sas_phy;
+	struct sas_phy *phy;
+
+	hpsa_sas_phy = kzalloc(sizeof(*hpsa_sas_phy), GFP_KERNEL);
+	if (!hpsa_sas_phy)
+		return NULL;
+
+	phy = sas_phy_alloc(hpsa_sas_port->parent_node->parent_dev,
+		hpsa_sas_port->next_phy_index);
+	if (!phy) {
+		kfree(hpsa_sas_phy);
+		return NULL;
+	}
+
+	hpsa_sas_port->next_phy_index++;
+	hpsa_sas_phy->phy = phy;
+	hpsa_sas_phy->parent_port = hpsa_sas_port;
+
+	return hpsa_sas_phy;
+}
+
+static void hpsa_free_sas_phy(struct hpsa_sas_phy *hpsa_sas_phy)
+{
+	struct sas_phy *phy = hpsa_sas_phy->phy;
+
+	printk("%s: Calling sas_port_delete_phy\n", __func__);
+	sas_port_delete_phy(hpsa_sas_phy->parent_port->port, phy);
+	printk("%s: Called sas_port_delete_phy\n", __func__);
+	sas_phy_free(phy);
+	printk("%s: Called sas_phy_free\n", __func__);
+	if (hpsa_sas_phy->added_to_port)
+		list_del(&hpsa_sas_phy->phy_list_entry);
+	kfree(hpsa_sas_phy);
+}
+
+static int hpsa_sas_port_add_phy(struct hpsa_sas_phy *hpsa_sas_phy)
+{
+	int rc;
+	struct hpsa_sas_port *hpsa_sas_port;
+	struct sas_phy *phy;
+	struct sas_identify *identify;
+
+	hpsa_sas_port = hpsa_sas_phy->parent_port;
+	phy = hpsa_sas_phy->phy;
+
+	identify = &phy->identify;
+	memset(identify, 0, sizeof(*identify));
+	identify->sas_address = hpsa_sas_port->sas_address;
+	identify->device_type = SAS_END_DEVICE;
+	identify->initiator_port_protocols = SAS_PROTOCOL_STP;
+	identify->target_port_protocols = SAS_PROTOCOL_STP;
+	phy->minimum_linkrate_hw = SAS_LINK_RATE_UNKNOWN;
+	phy->maximum_linkrate_hw = SAS_LINK_RATE_UNKNOWN;
+	phy->minimum_linkrate = SAS_LINK_RATE_UNKNOWN;
+	phy->maximum_linkrate = SAS_LINK_RATE_UNKNOWN;
+	phy->negotiated_linkrate = SAS_LINK_RATE_UNKNOWN;
+
+	rc = sas_phy_add(hpsa_sas_phy->phy);
+	if (rc)
+		return rc;
+
+	sas_port_add_phy(hpsa_sas_port->port, hpsa_sas_phy->phy);
+	list_add_tail(&hpsa_sas_phy->phy_list_entry,
+			&hpsa_sas_port->phy_list_head);
+	hpsa_sas_phy->added_to_port = true;
+
+	return 0;
+}
+
+static int
+	hpsa_sas_port_add_rphy(struct hpsa_sas_port *hpsa_sas_port,
+				struct sas_rphy *rphy)
+{
+	struct sas_identify *identify;
+
+	identify = &rphy->identify;
+	identify->sas_address = hpsa_sas_port->sas_address;
+	identify->initiator_port_protocols = SAS_PROTOCOL_STP;
+	identify->target_port_protocols = SAS_PROTOCOL_STP;
+
+	return sas_rphy_add(rphy);
+}
+
+static struct hpsa_sas_port
+	*hpsa_alloc_sas_port(struct hpsa_sas_node *hpsa_sas_node,
+				u64 sas_address)
+{
+	int rc;
+	struct hpsa_sas_port *hpsa_sas_port;
+	struct sas_port *port;
+
+	hpsa_sas_port = kzalloc(sizeof(*hpsa_sas_port), GFP_KERNEL);
+	if (!hpsa_sas_port)
+		return NULL;
+
+	INIT_LIST_HEAD(&hpsa_sas_port->phy_list_head);
+	hpsa_sas_port->parent_node = hpsa_sas_node;
+
+	port = sas_port_alloc_num(hpsa_sas_node->parent_dev);
+	if (!port)
+		goto free_hpsa_port;
+
+	rc = sas_port_add(port);
+	if (rc)
+		goto free_sas_port;
+
+	hpsa_sas_port->port = port;
+	hpsa_sas_port->sas_address = sas_address;
+	list_add_tail(&hpsa_sas_port->port_list_entry,
+			&hpsa_sas_node->port_list_head);
+
+	return hpsa_sas_port;
+
+free_sas_port:
+	sas_port_free(port);
+free_hpsa_port:
+	kfree(hpsa_sas_port);
+
+	return NULL;
+}
+
+static void hpsa_free_sas_port(struct hpsa_sas_port *hpsa_sas_port)
+{
+	struct hpsa_sas_phy *hpsa_sas_phy;
+	struct hpsa_sas_phy *next;
+
+	list_for_each_entry_safe(hpsa_sas_phy, next,
+			&hpsa_sas_port->phy_list_head, phy_list_entry) {
+		printk("%s: Calling hpsa_free_sas_phy\n", __func__);
+		hpsa_free_sas_phy(hpsa_sas_phy);
+		printk("%s: Called hpsa_free_sas_phy\n", __func__);
+	}
+
+	printk("%s: Calling sas_port_delete\n", __func__);
+	sas_port_delete(hpsa_sas_port->port);
+	printk("%s: Called sas_port_delete\n", __func__);
+	list_del(&hpsa_sas_port->port_list_entry);
+	kfree(hpsa_sas_port);
+}
+
+static struct hpsa_sas_node *hpsa_alloc_sas_node(struct device *parent_dev)
+{
+	struct hpsa_sas_node *hpsa_sas_node;
+
+	hpsa_sas_node = kzalloc(sizeof(*hpsa_sas_node), GFP_KERNEL);
+	if (hpsa_sas_node) {
+		hpsa_sas_node->parent_dev = parent_dev;
+		INIT_LIST_HEAD(&hpsa_sas_node->port_list_head);
+	}
+
+	return hpsa_sas_node;
+}
+
+static void hpsa_free_sas_node(struct hpsa_sas_node *hpsa_sas_node)
+{
+	struct hpsa_sas_port *hpsa_sas_port;
+	struct hpsa_sas_port *next;
+
+	if (!hpsa_sas_node)
+		return;
+
+	list_for_each_entry_safe(hpsa_sas_port, next,
+			&hpsa_sas_node->port_list_head, port_list_entry) {
+		printk("%s: Calling hpsa_free_sas_port\n", __func__);
+		hpsa_free_sas_port(hpsa_sas_port);
+		printk("%s: Called hpsa_free_sas_port\n", __func__);
+	}
+
+	printk("%s: Calling kfree hpsa_sas_node\n", __func__);
+	kfree(hpsa_sas_node);
+	printk("%s: Called kfree hpsa_sas_node\n", __func__);
+}
+
+static struct hpsa_scsi_dev_t
+	*hpsa_find_device_by_sas_rphy(struct ctlr_info *h,
+					struct sas_rphy *rphy)
+{
+	int i;
+	struct hpsa_scsi_dev_t *device;
+
+	for (i = 0; i < h->ndevices; i++) {
+		device = h->dev[i];
+		if (!device->sas_port)
+			continue;
+		if (device->sas_port->rphy == rphy)
+			return device;
+	}
+
+	return NULL;
+}
+
+static int hpsa_add_sas_host(struct ctlr_info *h)
+{
+	int rc;
+	struct device *parent_dev;
+	struct hpsa_sas_node *hpsa_sas_node;
+	struct hpsa_sas_port *hpsa_sas_port;
+	struct hpsa_sas_phy *hpsa_sas_phy;
+
+	parent_dev = &h->scsi_host->shost_gendev;
+
+	hpsa_sas_node = hpsa_alloc_sas_node(parent_dev);
+	if (!hpsa_sas_node)
+		return -ENOMEM;
+
+	hpsa_sas_port = hpsa_alloc_sas_port(hpsa_sas_node, h->sas_address);
+	if (!hpsa_sas_port) {
+		rc = -ENODEV;
+		goto free_sas_node;
+	}
+
+	hpsa_sas_phy = hpsa_alloc_sas_phy(hpsa_sas_port);
+	if (!hpsa_sas_phy) {
+		rc = -ENODEV;
+		goto free_sas_port;
+	}
+
+	rc = hpsa_sas_port_add_phy(hpsa_sas_phy);
+	if (rc)
+		goto free_sas_phy;
+
+	h->sas_host = hpsa_sas_node;
+
+	return 0;
+
+free_sas_phy:
+	hpsa_free_sas_phy(hpsa_sas_phy);
+free_sas_port:
+	hpsa_free_sas_port(hpsa_sas_port);
+free_sas_node:
+	hpsa_free_sas_node(hpsa_sas_node);
+
+	return rc;
+}
+
+static void hpsa_delete_sas_host(struct ctlr_info *h)
+{
+	printk("%s: Calling hpsa_free_sas_node\n", __func__);
+	hpsa_free_sas_node(h->sas_host);
+	printk("%s: Called hpsa_free_sas_node\n", __func__);
+}
+
+static int hpsa_add_sas_device(struct hpsa_sas_node *hpsa_sas_node,
+				struct hpsa_scsi_dev_t *device)
+{
+	int rc;
+	struct hpsa_sas_port *hpsa_sas_port;
+	struct sas_rphy *rphy;
+
+	hpsa_sas_port = hpsa_alloc_sas_port(hpsa_sas_node, device->sas_address);
+	if (!hpsa_sas_port)
+		return -ENOMEM;
+
+	rphy = sas_end_device_alloc(hpsa_sas_port->port);
+	if (!rphy) {
+		rc = -ENODEV;
+		goto free_sas_port;
+	}
+
+	hpsa_sas_port->rphy = rphy;
+	device->sas_port = hpsa_sas_port;
+
+	rc = hpsa_sas_port_add_rphy(hpsa_sas_port, rphy);
+	if (rc)
+		goto free_sas_port;
+
+	return 0;
+
+free_sas_port:
+	hpsa_free_sas_port(hpsa_sas_port);
+	device->sas_port = NULL;
+
+	return rc;
+}
+
+static void hpsa_remove_sas_device(struct hpsa_scsi_dev_t *device)
+{
+	if (device->sas_port) {
+		hpsa_free_sas_port(device->sas_port);
+		device->sas_port = NULL;
+	}
+}
+
+static int
+hpsa_sas_get_linkerrors(struct sas_phy *phy)
+{
+	return 0;
+}
+
+static int
+hpsa_sas_get_enclosure_identifier(struct sas_rphy *rphy, u64 *identifier)
+{
+	return 0;
+}
+
+static int
+hpsa_sas_get_bay_identifier(struct sas_rphy *rphy)
+{
+	return -ENXIO;
+}
+
+static int
+hpsa_sas_phy_reset(struct sas_phy *phy, int hard_reset)
+{
+	return 0;
+}
+
+static int
+hpsa_sas_phy_enable(struct sas_phy *phy, int enable)
+{
+	return 0;
+}
+
+static int
+hpsa_sas_phy_setup(struct sas_phy *phy)
+{
+	return 0;
+}
+
+static void
+hpsa_sas_phy_release(struct sas_phy *phy)
+{
+}
+
+static int
+hpsa_sas_phy_speed(struct sas_phy *phy, struct sas_phy_linkrates *rates)
+{
+	return -EINVAL;
+}
+
+/* SMP = Serial Management Protocol */
+static int
+hpsa_sas_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy,
+struct request *req)
+{
+	return -EINVAL;
+}
+
+static struct sas_function_template hpsa_sas_transport_functions = {
+	.get_linkerrors = hpsa_sas_get_linkerrors,
+	.get_enclosure_identifier = hpsa_sas_get_enclosure_identifier,
+	.get_bay_identifier = hpsa_sas_get_bay_identifier,
+	.phy_reset = hpsa_sas_phy_reset,
+	.phy_enable = hpsa_sas_phy_enable,
+	.phy_setup = hpsa_sas_phy_setup,
+	.phy_release = hpsa_sas_phy_release,
+	.set_phy_speed = hpsa_sas_phy_speed,
+	.smp_handler = hpsa_sas_smp_handler,
+};
+
 /*
  *  This is it.  Register the PCI driver information for the cards we control
  *  the OS will call our registered routines when it finds one of our cards.
  */
 static int __init hpsa_init(void)
 {
-	return pci_register_driver(&hpsa_pci_driver);
+	int rc;
+
+	hpsa_sas_transport_template =
+		sas_attach_transport(&hpsa_sas_transport_functions);
+	if (!hpsa_sas_transport_template)
+		return -ENODEV;
+
+	rc = pci_register_driver(&hpsa_pci_driver);
+
+	if (rc)
+		sas_release_transport(hpsa_sas_transport_template);
+
+	return rc;
 }
 
 static void __exit hpsa_cleanup(void)
 {
 	pci_unregister_driver(&hpsa_pci_driver);
+	sas_release_transport(hpsa_sas_transport_template);
 }
 
 static void __attribute__((unused)) verify_offsets(void)
diff --git a/drivers/scsi/hpsa.h b/drivers/scsi/hpsa.h
index 27fcd4c..c110e6c 100644
--- a/drivers/scsi/hpsa.h
+++ b/drivers/scsi/hpsa.h
@@ -33,12 +33,38 @@  struct access_method {
 	unsigned long (*command_completed)(struct ctlr_info *h, u8 q);
 };
 
+/* for SAS hosts and SAS expanders */
+struct hpsa_sas_node {
+	struct device *parent_dev;
+	struct list_head port_list_head;
+};
+
+struct hpsa_sas_port {
+	struct list_head port_list_entry;
+	u64 sas_address;
+	struct sas_port *port;
+	int	next_phy_index;
+	struct list_head phy_list_head;
+	struct hpsa_sas_node *parent_node;
+	struct sas_rphy *rphy;
+};
+
+struct hpsa_sas_phy {
+	struct list_head phy_list_entry;
+	struct sas_phy *phy;
+	struct hpsa_sas_port *parent_port;
+	bool	added_to_port;
+};
+
 struct hpsa_scsi_dev_t {
-	int devtype;
+	int devtype;			/* as reported by INQUIRY commmand */
 	int bus, target, lun;		/* as presented to the OS */
 	unsigned char scsi3addr[8];	/* as presented to the HW */
+	u8 physical_device : 1;
+	u8 expose_device : 1;
 #define RAID_CTLR_LUNID "\0\0\0\0\0\0\0\0"
 	unsigned char device_id[16];    /* from inquiry pg. 0x83 */
+	u64 sas_address;                /* from inquiry pg. 0x83 */
 	unsigned char vendor[8];        /* bytes 8-15 of inquiry data */
 	unsigned char model[16];        /* bytes 16-31 of inquiry data */
 	unsigned char raid_level;	/* from inquiry page 0xC1 */
@@ -80,6 +106,7 @@  struct hpsa_scsi_dev_t {
 #define HPSA_ULD_ATTACH		0x2
 #define HPSA_SCSI_ADD		(HPSA_SG_ATTACH | HPSA_ULD_ATTACH)
 	u8 expose_state;
+	struct hpsa_sas_port *sas_port;
 };
 
 struct reply_queue_buffer {
@@ -136,6 +163,7 @@  struct ctlr_info {
 	char    *product_name;
 	struct pci_dev *pdev;
 	u32	board_id;
+	u64	sas_address;
 	void __iomem *vaddr;
 	unsigned long paddr;
 	int 	nr_cmds; /* Number of commands allowed on this controller */
@@ -270,6 +298,7 @@  struct ctlr_info {
 	wait_queue_head_t abort_cmd_wait_queue;
 	wait_queue_head_t event_sync_wait_queue;
 	struct mutex reset_mutex;
+	struct hpsa_sas_node *sas_host;
 };
 
 struct offline_device_entry {
@@ -368,6 +397,12 @@  struct offline_device_entry {
 #define IOACCEL2_INBOUND_POSTQ_64_LOW	0xd0
 #define IOACCEL2_INBOUND_POSTQ_64_HI	0xd4
 
+#define HPSA_PHYSICAL_DEVICE_BUS       0
+#define HPSA_RAID_VOLUME_BUS           1
+#define HPSA_EXTERNAL_RAID_VOLUME_BUS  2
+#define HPSA_HBA_BUS                   3
+
+
 /*
 	Send the command to the hardware
 */
diff --git a/drivers/scsi/hpsa_cmd.h b/drivers/scsi/hpsa_cmd.h
index c2c0737..1721aec 100644
--- a/drivers/scsi/hpsa_cmd.h
+++ b/drivers/scsi/hpsa_cmd.h
@@ -286,6 +286,7 @@  struct SenseSubsystem_info {
 #define BMIC_FLASH_FIRMWARE 0xF7
 #define BMIC_SENSE_CONTROLLER_PARAMETERS 0x64
 #define BMIC_IDENTIFY_PHYSICAL_DEVICE 0x15
+#define BMIC_SENSE_SUBSYSTEM_INFORMATION 0x66
 
 /* Command List Structure */
 union SCSI3Addr {
@@ -814,5 +815,18 @@  struct bmic_identify_physical_device {
 	u8     padding[112];
 };
 
+struct bmic_sense_subsystem_info {
+	u8	primary_slot_number;
+	u8	reserved[3];
+	u8	chasis_serial_number[32];
+	u8	primary_world_wide_id[8];
+	u8	primary_array_serial_number[32]; /* NULL terminated */
+	u8	primary_cache_serial_number[32]; /* NULL terminated */
+	u8	reserved_2[8];
+	u8	secondary_array_serial_number[32];
+	u8	secondary_cache_serial_number[32];
+	u8	pad[332];
+};
+
 #pragma pack()
 #endif /* HPSA_CMD_H */