[RFC][PATCH 4/5] fix wide port hotplug issues

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



The sas layer does not support wide port hotplug, we modified it. For
plugging out, we un-register the device only when it is the last phy in the
port, for other phys in this port, we just delete it from the port.For
inserting, we do discovery when it is the first phy,for other phys in this
port, we add it to the port to
forming the wide-port.
					Jack
>From d683c649d0ed8de69b1ffda1b87383ae13fed164 Mon Sep 17 00:00:00 2001
From: Tom Peng <tom_peng@xxxxxxxxx>
Date: Wed, 1 Jul 2009 19:11:03 +0800
Subject: [PATCH 4/5] fix wide port hotplug issues

Signed-off-by: Tom Peng <tom_peng@xxxxxxxxx>
Signed-off-by: Jack Wang <jack_wang@xxxxxxxxx>
Signed-off-by: Lindar Liu <lindar_liu@xxxxxxxxx>
---
 drivers/scsi/libsas/sas_expander.c |  190
++++++++++++++++++++++++------------
 1 files changed, 126 insertions(+), 64 deletions(-)

diff --git a/drivers/scsi/libsas/sas_expander.c
b/drivers/scsi/libsas/sas_expander.c
index 54fa1e4..20fa5e4 100644
--- a/drivers/scsi/libsas/sas_expander.c
+++ b/drivers/scsi/libsas/sas_expander.c
@@ -764,8 +764,9 @@ static int sas_ex_join_wide_port(struct domain_device
*parent, int phy_id)
 			continue;
 
 		if (!memcmp(phy->attached_sas_addr, ephy->attached_sas_addr,
-			    SAS_ADDR_SIZE) && ephy->port) {
+			SAS_ADDR_SIZE) && ephy->port) {
 			sas_port_add_phy(ephy->port, phy->phy);
+			phy->port = ephy->port;
 			phy->phy_state = PHY_DEVICE_DISCOVERED;
 			return 0;
 		}
@@ -945,10 +946,17 @@ static int sas_ex_discover_dev(struct domain_device
*dev, int phy_id)
 			if (ex->ex_phy[i].phy_state == PHY_VACANT ||
 			    ex->ex_phy[i].phy_state == PHY_NOT_PRESENT)
 				continue;
-
+			/* in before discover process, it does not add the
phy
+			* to wide port, so we add the phy to the wide port
here.
+			*/
 			if (SAS_ADDR(ex->ex_phy[i].attached_sas_addr) ==
-			    SAS_ADDR(child->sas_addr))
+				SAS_ADDR(child->sas_addr)) {
 				ex->ex_phy[i].phy_state=
PHY_DEVICE_DISCOVERED;
+				res = sas_ex_join_wide_port(dev, i);
+				if (!res)
+					SAS_DPRINTK("Add ex phy %d to this "
+						"wide port.\n", i);
+			}
 		}
 	}
 
@@ -1589,16 +1597,14 @@ static int sas_get_phy_attached_sas_addr(struct
domain_device *dev,
 
 	res = sas_get_phy_discover(dev, phy_id, disc_resp);
 	if (!res) {
-
memcpy(attached_sas_addr,disc_resp->disc.attached_sas_addr,8);
-		if (dr->attached_dev_type == 0)
-			memset(attached_sas_addr, 0, 8);
+		memcpy(attached_sas_addr, disc_resp->disc.attached_sas_addr,
8);
 	}
 	kfree(disc_resp);
 	return res;
 }
 
 static int sas_find_bcast_phy(struct domain_device *dev, int *phy_id,
-			      int from_phy)
+			      int from_phy, bool update)
 {
 	struct expander_device *ex = &dev->ex_dev;
 	int res = 0;
@@ -1611,7 +1617,9 @@ static int sas_find_bcast_phy(struct domain_device
*dev, int *phy_id,
 		if (res)
 			goto out;
 		else if (phy_change_count != ex->ex_phy[i].phy_change_count)
{
-			ex->ex_phy[i].phy_change_count = phy_change_count;
+			if (update)
+				ex->ex_phy[i].phy_change_count =
+					phy_change_count;
 			*phy_id = i;
 			return 0;
 		}
@@ -1653,31 +1661,50 @@ out:
 	kfree(rg_req);
 	return res;
 }
-
+/**
+ * sas_find_bcast_dev -  in before version, it does not consider
+ * SAS self-configuration expander. Suppose two expander cascading,
+ * while the first level expander is self-configuring, hotplug the disks in
+ * second level expander, BROADCAST(CHANGE) will not only be originated
+ * in the second level expander, but also be originated in the first level
+ * expander (see SAS protocol SAS 2r-14, 7.11 for detail), it is to say,
+ * expander changed count in two level expanders will all increment at
least
+ * once, but the phy which chang count has changed is the source device
which
+ * we concerned.
+ * @dev:domain device to be detect.
+ * @src_dev: the device which originated BROADCAST(CHANGE).
+ */
 static int sas_find_bcast_dev(struct domain_device *dev,
 			      struct domain_device **src_dev)
 {
 	struct expander_device *ex = &dev->ex_dev;
 	int ex_change_count = -1;
-	int res;
+	int phy_id = -1;
+	int res, i = 0;
+	struct domain_device *ch;
 
 	res = sas_get_ex_change_count(dev, &ex_change_count);
 	if (res)
 		goto out;
-	if (ex_change_count != -1 &&
-	    ex_change_count != ex->ex_change_count) {
-		*src_dev = dev;
-		ex->ex_change_count = ex_change_count;
-	} else {
-		struct domain_device *ch;
-
-		list_for_each_entry(ch, &ex->children, siblings) {
-			if (ch->dev_type == EDGE_DEV ||
-			    ch->dev_type == FANOUT_DEV) {
-				res = sas_find_bcast_dev(ch, src_dev);
-				if (src_dev)
-					return res;
-			}
+	if (ex_change_count != -1 && ex_change_count != ex->ex_change_count)
{
+		/* Just detect if this expander phys phy change count
changed,
+		* in order to determine if this expander originate
BROADCAST,
+		* and do not update phy change count field in our structure.
+		*/
+		res = sas_find_bcast_phy(dev, &phy_id, i, FALSE);
+		if (phy_id != -1) {
+			*src_dev = dev;
+			ex->ex_change_count = ex_change_count;
+			SAS_DPRINTK("Expander phy change count has changed
\n");
+			return res;
+		} else
+			SAS_DPRINTK("Expander phys DID NOT changed \n");
+	}
+	list_for_each_entry(ch, &ex->children, siblings) {
+		if (ch->dev_type == EDGE_DEV || ch->dev_type == FANOUT_DEV)
{
+			res = sas_find_bcast_dev(ch, src_dev);
+			if (src_dev)
+				return res;
 		}
 	}
 out:
@@ -1700,24 +1727,26 @@ static void sas_unregister_ex_tree(struct
domain_device *dev)
 }
 
 static void sas_unregister_devs_sas_addr(struct domain_device *parent,
-					 int phy_id)
+					int phy_id, bool last)
 {
 	struct expander_device *ex_dev = &parent->ex_dev;
 	struct ex_phy *phy = &ex_dev->ex_phy[phy_id];
 	struct domain_device *child, *n;
-
-	list_for_each_entry_safe(child, n, &ex_dev->children, siblings) {
-		if (SAS_ADDR(child->sas_addr) ==
-		    SAS_ADDR(phy->attached_sas_addr)) {
-			if (child->dev_type == EDGE_DEV ||
-			    child->dev_type == FANOUT_DEV)
-				sas_unregister_ex_tree(child);
-			else
-				sas_unregister_dev(child);
-			break;
+	if (last) {
+		list_for_each_entry_safe(child, n,
+			&ex_dev->children, siblings) {
+			if (SAS_ADDR(child->sas_addr) ==
+				SAS_ADDR(phy->attached_sas_addr)) {
+				if (child->dev_type == EDGE_DEV ||
+					child->dev_type == FANOUT_DEV)
+					sas_unregister_ex_tree(child);
+				else
+					sas_unregister_dev(child);
+				break;
+			}
 		}
+		sas_disable_routing(parent, phy->attached_sas_addr);
 	}
-	sas_disable_routing(parent, phy->attached_sas_addr);
 	memset(phy->attached_sas_addr, 0, SAS_ADDR_SIZE);
 	sas_port_delete_phy(phy->port, phy->phy);
 	if (phy->port->num_phys == 0)
@@ -1770,21 +1799,37 @@ static int sas_discover_new(struct domain_device
*dev, int phy_id)
 {
 	struct ex_phy *ex_phy = &dev->ex_dev.ex_phy[phy_id];
 	struct domain_device *child;
-	int res;
+	bool found = FALSE;
+	int res, i = 0;
 
 	SAS_DPRINTK("ex %016llx phy%d new device attached\n",
-		    SAS_ADDR(dev->sas_addr), phy_id);
+		SAS_ADDR(dev->sas_addr), phy_id);
 	res = sas_ex_phy_discover(dev, phy_id);
 	if (res)
 		goto out;
+	/* to support the wide port inserted */
+	for (i = 0; i < dev->ex_dev.num_phys; i++) {
+		struct ex_phy *ex_phy_temp = &dev->ex_dev.ex_phy[i];
+		if (i == phy_id)
+			continue;
+		if (SAS_ADDR(ex_phy_temp->attached_sas_addr) ==
+			SAS_ADDR(ex_phy->attached_sas_addr)) {
+			found = TRUE;
+			break;
+		}
+	}
+	if (found == TRUE) {
+		sas_ex_join_wide_port(dev, phy_id);
+		return 0;
+	}
 	res = sas_ex_discover_devices(dev, phy_id);
-	if (res)
+	if (!res)
 		goto out;
 	list_for_each_entry(child, &dev->ex_dev.children, siblings) {
 		if (SAS_ADDR(child->sas_addr) ==
-		    SAS_ADDR(ex_phy->attached_sas_addr)) {
+			SAS_ADDR(ex_phy->attached_sas_addr)) {
 			if (child->dev_type == EDGE_DEV ||
-			    child->dev_type == FANOUT_DEV)
+				child->dev_type == FANOUT_DEV)
 				res = sas_discover_bfs_by_root(child);
 			break;
 		}
@@ -1793,7 +1838,8 @@ out:
 	return res;
 }
 
-static int sas_rediscover_dev(struct domain_device *dev, int phy_id)
+static int sas_rediscover_dev(struct domain_device *dev, int phy_id,
+				bool last)
 {
 	struct expander_device *ex = &dev->ex_dev;
 	struct ex_phy *phy = &ex->ex_phy[phy_id];
@@ -1804,11 +1850,11 @@ static int sas_rediscover_dev(struct domain_device
*dev, int phy_id)
 	switch (res) {
 	case SMP_RESP_NO_PHY:
 		phy->phy_state = PHY_NOT_PRESENT;
-		sas_unregister_devs_sas_addr(dev, phy_id);
+		sas_unregister_devs_sas_addr(dev, phy_id, last);
 		goto out; break;
 	case SMP_RESP_PHY_VACANT:
 		phy->phy_state = PHY_VACANT;
-		sas_unregister_devs_sas_addr(dev, phy_id);
+		sas_unregister_devs_sas_addr(dev, phy_id, last);
 		goto out; break;
 	case SMP_RESP_FUNC_ACC:
 		break;
@@ -1816,11 +1862,11 @@ static int sas_rediscover_dev(struct domain_device
*dev, int phy_id)
 
 	if (SAS_ADDR(attached_sas_addr) == 0) {
 		phy->phy_state = PHY_EMPTY;
-		sas_unregister_devs_sas_addr(dev, phy_id);
+		sas_unregister_devs_sas_addr(dev, phy_id, last);
 	} else if (SAS_ADDR(attached_sas_addr) ==
-		   SAS_ADDR(phy->attached_sas_addr)) {
+			SAS_ADDR(phy->attached_sas_addr)) {
 		SAS_DPRINTK("ex %016llx phy 0x%x broadcast flutter\n",
-			    SAS_ADDR(dev->sas_addr), phy_id);
+			SAS_ADDR(dev->sas_addr), phy_id);
 		sas_ex_phy_discover(dev, phy_id);
 	} else
 		res = sas_discover_new(dev, phy_id);
@@ -1828,33 +1874,50 @@ out:
 	return res;
 }
 
+/**
+ * sas_rediscover -- revalidate the domain. The before version
+ * sas layer does not support wide port hotplug, we modified it. For
+ * plugging out, we un-register the device only when it is the last phy
+ * in the port, for other phys in this port, we just delete it
+ * from the port.For inserting, we do discovery when it is the
+ * first phy,for other phys in this port, we add it to the port to
+ * forming the wide-port.
+ * @dev:domain device to be detect.
+ * @phy_id: the phy id will be detected.
+ * NOTE: this process _must_ quit (return) as soon as any connection
+ * errors are encountered.  Connection recovery is done elsewhere.
+ * Discover process only interrogates devices in order to discover the
+ * domain.
+ */
 static int sas_rediscover(struct domain_device *dev, const int phy_id)
 {
 	struct expander_device *ex = &dev->ex_dev;
 	struct ex_phy *changed_phy = &ex->ex_phy[phy_id];
 	int res = 0;
 	int i;
+	/* To check whether the last phy of a port determining whether or
not
+	delete devices*/
+	bool last = TRUE;
 
 	SAS_DPRINTK("ex %016llx phy%d originated BROADCAST(CHANGE)\n",
-		    SAS_ADDR(dev->sas_addr), phy_id);
+		SAS_ADDR(dev->sas_addr), phy_id);
 
 	if (SAS_ADDR(changed_phy->attached_sas_addr) != 0) {
 		for (i = 0; i < ex->num_phys; i++) {
 			struct ex_phy *phy = &ex->ex_phy[i];
-
 			if (i == phy_id)
 				continue;
 			if (SAS_ADDR(phy->attached_sas_addr) ==
-			    SAS_ADDR(changed_phy->attached_sas_addr)) {
+				SAS_ADDR(changed_phy->attached_sas_addr)) {
 				SAS_DPRINTK("phy%d part of wide port with "
-					    "phy%d\n", phy_id, i);
-				goto out;
+					"phy%d\n", phy_id, i);
+				last = FALSE;
+				break;
 			}
 		}
-		res = sas_rediscover_dev(dev, phy_id);
+		res = sas_rediscover_dev(dev, phy_id, last);
 	} else
 		res = sas_discover_new(dev, phy_id);
-out:
 	return res;
 }
 
@@ -1871,7 +1934,6 @@ int sas_ex_revalidate_domain(struct domain_device
*port_dev)
 {
 	int res;
 	struct domain_device *dev = NULL;
-
 	res = sas_find_bcast_dev(port_dev, &dev);
 	if (res)
 		goto out;
@@ -1881,7 +1943,7 @@ int sas_ex_revalidate_domain(struct domain_device
*port_dev)
 
 		do {
 			phy_id = -1;
-			res = sas_find_bcast_phy(dev, &phy_id, i);
+			res = sas_find_bcast_phy(dev, &phy_id, i, TRUE);
 			if (phy_id == -1)
 				break;
 			res = sas_rediscover(dev, phy_id);
@@ -1927,21 +1989,21 @@ int sas_smp_handler(struct Scsi_Host *shost, struct
sas_rphy *rphy,
 	/* do we need to support multiple segments? */
 	if (req->bio->bi_vcnt > 1 || rsp->bio->bi_vcnt > 1) {
 		printk("%s: multiple segments req %u %u, rsp %u %u\n",
-		       __func__, req->bio->bi_vcnt, blk_rq_bytes(req),
-		       rsp->bio->bi_vcnt, blk_rq_bytes(rsp));
+		       __func__, req->bio->bi_vcnt, req->data_len,
+		       rsp->bio->bi_vcnt, rsp->data_len);
 		return -EINVAL;
 	}
 
-	ret = smp_execute_task(dev, bio_data(req->bio), blk_rq_bytes(req),
-			       bio_data(rsp->bio), blk_rq_bytes(rsp));
+	ret = smp_execute_task(dev, bio_data(req->bio), req->data_len,
+			       bio_data(rsp->bio), rsp->data_len);
 	if (ret > 0) {
 		/* positive number is the untransferred residual */
-		rsp->resid_len = ret;
-		req->resid_len = 0;
+		rsp->data_len = ret;
+		req->data_len = 0;
 		ret = 0;
 	} else if (ret == 0) {
-		rsp->resid_len = 0;
-		req->resid_len = 0;
+		rsp->data_len = 0;
+		req->data_len = 0;
 	}
 
 	return ret;
-- 


Attachment: 0004-fix-wide-port-hotplug-issues.patch
Description: Binary data


[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [SCSI Target Devel]     [Linux SCSI Target Infrastructure]     [Kernel Newbies]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Linux IIO]     [Samba]     [Device Mapper]
  Powered by Linux