diff mbox

[1,24/25] hpsa: add in sas transport class

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

Commit Message

Don Brace Oct. 28, 2015, 10:06 p.m. UTC
From: Kevin Barnett <kevin.barnett@pmcs.com>

Reviewed-by: Scott Teel <scott.teel@pmcs.com>
Reviewed-by: Justin Lindley <justin.lindley@pmcs.com>
Reviewed-by: Kevin Barnett <kevin.barnett@pmcs.com>
Signed-off-by: Don Brace <don.brace@pmcs.com>
---
 drivers/scsi/hpsa.c     |  535 +++++++++++++++++++++++++++++++++++++++++++++--
 drivers/scsi/hpsa.h     |   27 ++
 drivers/scsi/hpsa_cmd.h |   14 +
 3 files changed, 555 insertions(+), 21 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 Oct. 30, 2015, 8:21 a.m. UTC | #1
On 10/28/2015 11:06 PM, Don Brace wrote:
> From: Kevin Barnett <kevin.barnett@pmcs.com>
> 
> Reviewed-by: Scott Teel <scott.teel@pmcs.com>
> Reviewed-by: Justin Lindley <justin.lindley@pmcs.com>
> Reviewed-by: Kevin Barnett <kevin.barnett@pmcs.com>
> Signed-off-by: Don Brace <don.brace@pmcs.com>
> ---
>  drivers/scsi/hpsa.c     |  535 +++++++++++++++++++++++++++++++++++++++++++++--
>  drivers/scsi/hpsa.h     |   27 ++
>  drivers/scsi/hpsa_cmd.h |   14 +
>  3 files changed, 555 insertions(+), 21 deletions(-)
> 
Reviewed-by: Hannes Reinecke <hare@suse.de>

Cheers,

Hannes
Tomas Henzl Oct. 30, 2015, 2:40 p.m. UTC | #2
On 28.10.2015 23:06, Don Brace wrote:
> From: Kevin Barnett <kevin.barnett@pmcs.com>
>
> Reviewed-by: Scott Teel <scott.teel@pmcs.com>
> Reviewed-by: Justin Lindley <justin.lindley@pmcs.com>
> Reviewed-by: Kevin Barnett <kevin.barnett@pmcs.com>
> Signed-off-by: Don Brace <don.brace@pmcs.com>

Reviewed-by: Tomas Henzl <thenzl@redhat.com>

Tomas

--
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
Matthew R. Ochs Oct. 30, 2015, 8:07 p.m. UTC | #3
> On Oct 28, 2015, at 5:06 PM, Don Brace <don.brace@pmcs.com> wrote:
> 
> From: Kevin Barnett <kevin.barnett@pmcs.com>
> 
> Reviewed-by: Scott Teel <scott.teel@pmcs.com>
> Reviewed-by: Justin Lindley <justin.lindley@pmcs.com>
> Reviewed-by: Kevin Barnett <kevin.barnett@pmcs.com>
> Signed-off-by: Don Brace <don.brace@pmcs.com>
> ---
> drivers/scsi/hpsa.c     |  535 +++++++++++++++++++++++++++++++++++++++++++++--
> drivers/scsi/hpsa.h     |   27 ++
> drivers/scsi/hpsa_cmd.h |   14 +
> 3 files changed, 555 insertions(+), 21 deletions(-)
> 
> diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c
> index 56526312..ca38a00 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)
> @@ -277,6 +288,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 void hpsa_disable_rld_caching(struct ctlr_info *h);
> +static inline int hpsa_scsi_do_report_phys_luns(struct ctlr_info *h,
> +	struct ReportExtendedLUNdata *buf, int bufsize);
> static int hpsa_luns_changed(struct ctlr_info *h);
> 
> static inline struct ctlr_info *sdev_to_hba(struct scsi_device *sdev)
> @@ -1696,8 +1709,12 @@ static int hpsa_add_device(struct ctlr_info *h, struct hpsa_scsi_dev_t *device)
> {
> 	int rc = 0;
> 
> -	rc = scsi_add_device(h->scsi_host, device->bus,
> +	if (is_logical_device(device)) /* RAID */
> +		rc = scsi_add_device(h->scsi_host, device->bus,
> 					device->target, device->lun);
> +	else /* HBA */
> +		rc = hpsa_add_sas_device(h->sas_host, device);
> +
> 	return rc;
> }
> 
> @@ -1706,21 +1723,23 @@ static void hpsa_remove_device(struct ctlr_info *h,
> {
> 	struct scsi_device *sdev = NULL;
> 
> -	sdev = scsi_device_lookup(h->scsi_host, device->bus,
> +	if (is_logical_device(device)) { /* RAID */
> +		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.
> -		 */
> -		hpsa_show_dev_msg(KERN_WARNING, h, device,
> +		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.
> +			 */
> +			hpsa_show_dev_msg(KERN_WARNING, h, device,
> 					"didn't find device for removal.");
> -	}
> +		}
> +	} else /* HBA */
> +		hpsa_remove_sas_device(device);
> }
> 
> static void adjust_hpsa_scsi_table(struct ctlr_info *h, int hostno,
> @@ -1915,11 +1934,24 @@ static int hpsa_slave_alloc(struct scsi_device *sdev)
> 
> 	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)) {
> +	if (sdev_channel(sdev) == HPSA_PHYSICAL_DEVICE_BUS) {
> +		struct scsi_target *starget;
> +		struct sas_rphy *rphy;
> +
> +		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->expose_device ? sd : NULL;
> +		sdev->hostdata = sd;
> 	} else
> 		sdev->hostdata = NULL;
> 	spin_unlock_irqrestore(&h->devlock, flags);
> @@ -3069,6 +3101,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_controller(struct ctlr_info *h,
> 	struct bmic_identify_controller *buf, size_t bufsize)
> {
> @@ -3097,7 +3161,6 @@ out:
> 	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)
> @@ -3124,9 +3187,64 @@ static int hpsa_bmic_id_physical_device(struct ctlr_info *h,
> 	}
> out:
> 	cmd_free(h, c);
> +
> 	return rc;
> }
> 
> +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]);

Don't you want to break out here if you found a match?

> +
> +	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;
> +
> +		ssi = kzalloc(sizeof(*ssi), GFP_KERNEL);

What happens if this allocation fails? If the I/O can succeed without the
DMA buffer then you will run into trouble when deriving sa.

> +
> +		rc = hpsa_bmic_sense_subsystem_information(h,
> +					scsi3addr, 0, ssi, sizeof(*ssi));
> +		if (rc == 0) {
> +			sa = get_unaligned_be64(ssi->primary_world_wide_id);
> +			h->sas_address = sa;
> +		}
> +
> +		kfree(ssi);
> +	} 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_vpd_page_supported(struct ctlr_info *h,
> 	unsigned char scsi3addr[], u8 page)
> {
> @@ -3961,6 +4079,13 @@ static void hpsa_update_scsi_devices(struct ctlr_info *h, int hostno)
> 		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:
> 			/* We don't *really* support actual CD-ROM devices,
> @@ -4006,6 +4131,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);
> @@ -5137,6 +5266,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;
> @@ -6485,6 +6615,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;
> 		case BMIC_IDENTIFY_CONTROLLER:
> 			c->Request.CDBLen = 10;
> 			c->Request.type_attr_dir =
> @@ -6501,7 +6641,6 @@ static int fill_cmd(struct CommandList *c, u8 cmd, struct ctlr_info *h,
> 			c->Request.CDB[8] = (size >> 8) & 0XFF;
> 			c->Request.CDB[9] = 0;
> 			break;
> -
> 		default:
> 			dev_warn(&h->pdev->dev, "unknown command 0x%c\n", cmd);
> 			BUG();
> @@ -8618,6 +8757,9 @@ 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 */
> +
> +	hpsa_delete_sas_host(h);
> +
> 	kfree(h);					/* init_one 1 */
> }
> 
> @@ -9080,18 +9222,369 @@ 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;
> +
> +	sas_port_delete_phy(hpsa_sas_phy->parent_port->port, phy);
> +	sas_phy_free(phy);
> +	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)
> +		hpsa_free_sas_phy(hpsa_sas_phy);
> +
> +	sas_port_delete(hpsa_sas_port->port);
> +	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)
> +		hpsa_free_sas_port(hpsa_sas_port);
> +
> +	kfree(hpsa_sas_node);
> +}
> +
> +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)
> +{
> +	hpsa_free_sas_node(h->sas_host);
> +}
> +
> +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 6c82d3c..7847405 100644
> --- a/drivers/scsi/hpsa.h
> +++ b/drivers/scsi/hpsa.h
> @@ -33,6 +33,29 @@ 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 {
> 	unsigned int devtype;
> 	int bus, target, lun;		/* as presented to the OS */
> @@ -41,6 +64,7 @@ struct hpsa_scsi_dev_t {
> 	u8 expose_device;
> #define RAID_CTLR_LUNID "\0\0\0\0\0\0\0\0"
> 	unsigned char device_id[16];    /* from inquiry pg. 0x83 */
> +	u64 sas_address;
> 	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 */
> @@ -77,6 +101,7 @@ struct hpsa_scsi_dev_t {
> 	struct hpsa_scsi_dev_t *phys_disk[RAID_MAP_MAX_ENTRIES];
> 	int nphysical_disks;
> 	int supports_aborts;
> +	struct hpsa_sas_port *sas_port;
> 	int external;   /* 1-from external array 0-not <0-unknown */
> };
> 
> @@ -134,6 +159,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 */
> @@ -272,6 +298,7 @@ struct ctlr_info {
> 	wait_queue_head_t event_sync_wait_queue;
> 	struct mutex reset_mutex;
> 	u8 reset_in_progress;
> +	struct hpsa_sas_node *sas_host;
> };
> 
> struct offline_device_entry {
> diff --git a/drivers/scsi/hpsa_cmd.h b/drivers/scsi/hpsa_cmd.h
> index 4910344..d92ef0d 100644
> --- a/drivers/scsi/hpsa_cmd.h
> +++ b/drivers/scsi/hpsa_cmd.h
> @@ -290,6 +290,7 @@ struct SenseSubsystem_info {
> #define BMIC_SET_DIAG_OPTIONS 0xF4
> #define BMIC_SENSE_DIAG_OPTIONS 0xF5
> #define HPSA_DIAG_OPTS_DISABLE_RLD_CACHING 0x40000000
> +#define BMIC_SENSE_SUBSYSTEM_INFORMATION 0x66
> 
> /* Command List Structure */
> union SCSI3Addr {
> @@ -828,5 +829,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 */
> 
> --
> 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
> 

--
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
Don Brace Oct. 30, 2015, 10 p.m. UTC | #4
On 10/30/2015 03:07 PM, Matthew R. Ochs wrote:
>> On Oct 28, 2015, at 5:06 PM, Don Brace <don.brace@pmcs.com> wrote:
>>
>> From: Kevin Barnett <kevin.barnett@pmcs.com>
>>
>> Reviewed-by: Scott Teel <scott.teel@pmcs.com>
>> Reviewed-by: Justin Lindley <justin.lindley@pmcs.com>
>> Reviewed-by: Kevin Barnett <kevin.barnett@pmcs.com>
>> Signed-off-by: Don Brace <don.brace@pmcs.com>
>> ---
>> drivers/scsi/hpsa.c     |  535 +++++++++++++++++++++++++++++++++++++++++++++--
>> drivers/scsi/hpsa.h     |   27 ++
>> drivers/scsi/hpsa_cmd.h |   14 +
>> 3 files changed, 555 insertions(+), 21 deletions(-)
>>
>> diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c
>> index 56526312..ca38a00 100644
>> --- a/drivers/scsi/hpsa.c
>> +++ b/drivers/scsi/hpsa.c
>> @@ -41,6 +41,7 @@
>>
>> +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]);
> Don't you want to break out here if you found a match?
Agreed.
>
>> +
>> +	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;
>> +
>> +		ssi = kzalloc(sizeof(*ssi), GFP_KERNEL);
> What happens if this allocation fails? If the I/O can succeed without the
> DMA buffer then you will run into trouble when deriving sa.
Added check for NULL.
>
> --
> 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

--
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
Matthew R. Ochs Oct. 30, 2015, 11:23 p.m. UTC | #5
> On Oct 30, 2015, at 5:00 PM, Don Brace <brace77070@gmail.com> wrote:
> On 10/30/2015 03:07 PM, Matthew R. Ochs wrote:
>>> On Oct 28, 2015, at 5:06 PM, Don Brace <don.brace@pmcs.com> wrote:
>>> 
>>> From: Kevin Barnett <kevin.barnett@pmcs.com>
>>> 
>>> Reviewed-by: Scott Teel <scott.teel@pmcs.com>
>>> Reviewed-by: Justin Lindley <justin.lindley@pmcs.com>
>>> Reviewed-by: Kevin Barnett <kevin.barnett@pmcs.com>
>>> Signed-off-by: Don Brace <don.brace@pmcs.com>
>>> ---
>>> drivers/scsi/hpsa.c     |  535 +++++++++++++++++++++++++++++++++++++++++++++--
>>> drivers/scsi/hpsa.h     |   27 ++
>>> drivers/scsi/hpsa_cmd.h |   14 +
>>> 3 files changed, 555 insertions(+), 21 deletions(-)
>>> 
>>> diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c
>>> index 56526312..ca38a00 100644
>>> --- a/drivers/scsi/hpsa.c
>>> +++ b/drivers/scsi/hpsa.c
>>> @@ -41,6 +41,7 @@
>>> 
>>> +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]);
>> Don't you want to break out here if you found a match?
> Agreed.
>> 
>>> +
>>> +	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;
>>> +
>>> +		ssi = kzalloc(sizeof(*ssi), GFP_KERNEL);
>> What happens if this allocation fails? If the I/O can succeed without the
>> DMA buffer then you will run into trouble when deriving sa.
> Added check for NULL.

With these changes

Reviewed-by: Matthew R. Ochs <mrochs@linux.vnet.ibm.com>

--
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
kernel test robot Nov. 3, 2015, 12:40 a.m. UTC | #6
Hi Kevin,

[auto build test ERROR on scsi/for-next -- if it's inappropriate base, please suggest rules for selecting the more suitable base]

url:    https://github.com/0day-ci/linux/commits/Don-Brace/hpsa-updates/20151029-061230
config: i386-randconfig-b0-11030633 (attached as .config)
reproduce:
        # save the attached .config to linux build tree
        make ARCH=i386 

All errors (new ones prefixed by >>):

   drivers/built-in.o: In function `hpsa_free_sas_phy':
>> hpsa.c:(.text+0x11439a): undefined reference to `sas_port_delete_phy'
>> hpsa.c:(.text+0x1143a1): undefined reference to `sas_phy_free'
   drivers/built-in.o: In function `hpsa_free_sas_port':
>> hpsa.c:(.text+0x1143e5): undefined reference to `sas_port_delete'
   drivers/built-in.o: In function `hpsa_alloc_sas_port':
>> hpsa.c:(.text+0x11457a): undefined reference to `sas_port_alloc_num'
>> hpsa.c:(.text+0x114585): undefined reference to `sas_port_add'
>> hpsa.c:(.text+0x1145b0): undefined reference to `sas_port_free'
   drivers/built-in.o: In function `hpsa_scan_start':
>> hpsa.c:(.text+0x11a725): undefined reference to `sas_phy_alloc'
>> hpsa.c:(.text+0x11a7c2): undefined reference to `sas_phy_add'
>> hpsa.c:(.text+0x11a7d4): undefined reference to `sas_port_add_phy'
>> hpsa.c:(.text+0x11b2f9): undefined reference to `sas_end_device_alloc'
>> hpsa.c:(.text+0x11b331): undefined reference to `sas_rphy_add'
   drivers/built-in.o: In function `hpsa_init':
>> hpsa.c:(.init.text+0xb4b9): undefined reference to `sas_attach_transport'
>> hpsa.c:(.init.text+0xb4ea): undefined reference to `sas_release_transport'
   drivers/built-in.o: In function `hpsa_cleanup':
>> hpsa.c:(.exit.text+0xf72): undefined reference to `sas_release_transport'

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation
diff mbox

Patch

diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c
index 56526312..ca38a00 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)
@@ -277,6 +288,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 void hpsa_disable_rld_caching(struct ctlr_info *h);
+static inline int hpsa_scsi_do_report_phys_luns(struct ctlr_info *h,
+	struct ReportExtendedLUNdata *buf, int bufsize);
 static int hpsa_luns_changed(struct ctlr_info *h);
 
 static inline struct ctlr_info *sdev_to_hba(struct scsi_device *sdev)
@@ -1696,8 +1709,12 @@  static int hpsa_add_device(struct ctlr_info *h, struct hpsa_scsi_dev_t *device)
 {
 	int rc = 0;
 
-	rc = scsi_add_device(h->scsi_host, device->bus,
+	if (is_logical_device(device)) /* RAID */
+		rc = scsi_add_device(h->scsi_host, device->bus,
 					device->target, device->lun);
+	else /* HBA */
+		rc = hpsa_add_sas_device(h->sas_host, device);
+
 	return rc;
 }
 
@@ -1706,21 +1723,23 @@  static void hpsa_remove_device(struct ctlr_info *h,
 {
 	struct scsi_device *sdev = NULL;
 
-	sdev = scsi_device_lookup(h->scsi_host, device->bus,
+	if (is_logical_device(device)) { /* RAID */
+		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.
-		 */
-		hpsa_show_dev_msg(KERN_WARNING, h, device,
+		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.
+			 */
+			hpsa_show_dev_msg(KERN_WARNING, h, device,
 					"didn't find device for removal.");
-	}
+		}
+	} else /* HBA */
+		hpsa_remove_sas_device(device);
 }
 
 static void adjust_hpsa_scsi_table(struct ctlr_info *h, int hostno,
@@ -1915,11 +1934,24 @@  static int hpsa_slave_alloc(struct scsi_device *sdev)
 
 	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)) {
+	if (sdev_channel(sdev) == HPSA_PHYSICAL_DEVICE_BUS) {
+		struct scsi_target *starget;
+		struct sas_rphy *rphy;
+
+		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->expose_device ? sd : NULL;
+		sdev->hostdata = sd;
 	} else
 		sdev->hostdata = NULL;
 	spin_unlock_irqrestore(&h->devlock, flags);
@@ -3069,6 +3101,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_controller(struct ctlr_info *h,
 	struct bmic_identify_controller *buf, size_t bufsize)
 {
@@ -3097,7 +3161,6 @@  out:
 	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)
@@ -3124,9 +3187,64 @@  static int hpsa_bmic_id_physical_device(struct ctlr_info *h,
 	}
 out:
 	cmd_free(h, c);
+
 	return rc;
 }
 
+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;
+
+		ssi = kzalloc(sizeof(*ssi), GFP_KERNEL);
+
+		rc = hpsa_bmic_sense_subsystem_information(h,
+					scsi3addr, 0, ssi, sizeof(*ssi));
+		if (rc == 0) {
+			sa = get_unaligned_be64(ssi->primary_world_wide_id);
+			h->sas_address = sa;
+		}
+
+		kfree(ssi);
+	} 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_vpd_page_supported(struct ctlr_info *h,
 	unsigned char scsi3addr[], u8 page)
 {
@@ -3961,6 +4079,13 @@  static void hpsa_update_scsi_devices(struct ctlr_info *h, int hostno)
 		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:
 			/* We don't *really* support actual CD-ROM devices,
@@ -4006,6 +4131,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);
@@ -5137,6 +5266,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;
@@ -6485,6 +6615,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;
 		case BMIC_IDENTIFY_CONTROLLER:
 			c->Request.CDBLen = 10;
 			c->Request.type_attr_dir =
@@ -6501,7 +6641,6 @@  static int fill_cmd(struct CommandList *c, u8 cmd, struct ctlr_info *h,
 			c->Request.CDB[8] = (size >> 8) & 0XFF;
 			c->Request.CDB[9] = 0;
 			break;
-
 		default:
 			dev_warn(&h->pdev->dev, "unknown command 0x%c\n", cmd);
 			BUG();
@@ -8618,6 +8757,9 @@  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 */
+
+	hpsa_delete_sas_host(h);
+
 	kfree(h);					/* init_one 1 */
 }
 
@@ -9080,18 +9222,369 @@  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;
+
+	sas_port_delete_phy(hpsa_sas_phy->parent_port->port, phy);
+	sas_phy_free(phy);
+	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)
+		hpsa_free_sas_phy(hpsa_sas_phy);
+
+	sas_port_delete(hpsa_sas_port->port);
+	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)
+		hpsa_free_sas_port(hpsa_sas_port);
+
+	kfree(hpsa_sas_node);
+}
+
+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)
+{
+	hpsa_free_sas_node(h->sas_host);
+}
+
+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 6c82d3c..7847405 100644
--- a/drivers/scsi/hpsa.h
+++ b/drivers/scsi/hpsa.h
@@ -33,6 +33,29 @@  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 {
 	unsigned int devtype;
 	int bus, target, lun;		/* as presented to the OS */
@@ -41,6 +64,7 @@  struct hpsa_scsi_dev_t {
 	u8 expose_device;
 #define RAID_CTLR_LUNID "\0\0\0\0\0\0\0\0"
 	unsigned char device_id[16];    /* from inquiry pg. 0x83 */
+	u64 sas_address;
 	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 */
@@ -77,6 +101,7 @@  struct hpsa_scsi_dev_t {
 	struct hpsa_scsi_dev_t *phys_disk[RAID_MAP_MAX_ENTRIES];
 	int nphysical_disks;
 	int supports_aborts;
+	struct hpsa_sas_port *sas_port;
 	int external;   /* 1-from external array 0-not <0-unknown */
 };
 
@@ -134,6 +159,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 */
@@ -272,6 +298,7 @@  struct ctlr_info {
 	wait_queue_head_t event_sync_wait_queue;
 	struct mutex reset_mutex;
 	u8 reset_in_progress;
+	struct hpsa_sas_node *sas_host;
 };
 
 struct offline_device_entry {
diff --git a/drivers/scsi/hpsa_cmd.h b/drivers/scsi/hpsa_cmd.h
index 4910344..d92ef0d 100644
--- a/drivers/scsi/hpsa_cmd.h
+++ b/drivers/scsi/hpsa_cmd.h
@@ -290,6 +290,7 @@  struct SenseSubsystem_info {
 #define BMIC_SET_DIAG_OPTIONS 0xF4
 #define BMIC_SENSE_DIAG_OPTIONS 0xF5
 #define HPSA_DIAG_OPTS_DISABLE_RLD_CACHING 0x40000000
+#define BMIC_SENSE_SUBSYSTEM_INFORMATION 0x66
 
 /* Command List Structure */
 union SCSI3Addr {
@@ -828,5 +829,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 */