diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c
index 763f012..877efa8 100644
--- a/drivers/scsi/libsas/sas_ata.c
+++ b/drivers/scsi/libsas/sas_ata.c
@@ -619,32 +619,22 @@ static int sas_get_ata_command_set(struct domain_device *dev)
 	return ata_dev_classify(&tf);
 }
 
-void sas_probe_sata(struct asd_sas_port *port)
+void sas_probe_sata_device(struct domain_device *dev)
 {
-	struct domain_device *dev, *n;
-
-	mutex_lock(&port->ha->disco_mutex);
-	list_for_each_entry(dev, &port->disco_list, disco_list_node) {
-		if (!dev_is_sata(dev))
-			continue;
-
-		ata_sas_async_probe(dev->sata_dev.ap);
-	}
-	mutex_unlock(&port->ha->disco_mutex);
+	struct asd_sas_port *port = dev->port;
 
-	list_for_each_entry_safe(dev, n, &port->disco_list, disco_list_node) {
-		if (!dev_is_sata(dev))
-			continue;
+	if (!port || !port->ha || !dev_is_sata(dev))
+		return;
 
-		sas_ata_wait_eh(dev);
+	ata_sas_async_probe(dev->sata_dev.ap);
 
-		/* if libata could not bring the link up, don't surface
-		 * the device
-		 */
-		if (ata_dev_disabled(sas_to_ata_dev(dev)))
-			sas_fail_probe(dev, __func__, -ENODEV);
-	}
+	sas_ata_wait_eh(dev);
 
+	/* if libata could not bring the link up, don't surface
+	 * the device
+	 */
+	if (ata_dev_disabled(sas_to_ata_dev(dev)))
+		sas_fail_probe(dev, __func__, -ENODEV);
 }
 
 static void sas_ata_flush_pm_eh(struct asd_sas_port *port, const char *func)
@@ -729,7 +719,7 @@ int sas_discover_sata(struct domain_device *dev)
 	if (res)
 		return res;
 
-	sas_discover_event(dev->port, DISCE_PROBE);
+	sas_notify_device_event(dev, SAS_DEVICE_ADD);
 	return 0;
 }
 
diff --git a/drivers/scsi/libsas/sas_discover.c b/drivers/scsi/libsas/sas_discover.c
index 60de662..ea57c66 100644
--- a/drivers/scsi/libsas/sas_discover.c
+++ b/drivers/scsi/libsas/sas_discover.c
@@ -34,6 +34,12 @@
 #include <scsi/sas_ata.h>
 #include "../scsi_sas_internal.h"
 
+
+static void sas_unregister_common_dev(struct asd_sas_port *port,
+				struct domain_device *dev);
+static void sas_unregister_fail_dev(struct asd_sas_port *port,
+				struct domain_device *dev);
+
 /* ---------- Basic task processing for discovery purposes ---------- */
 
 void sas_init_dev(struct domain_device *dev)
@@ -158,11 +164,8 @@ static int sas_get_port_device(struct asd_sas_port *port)
 
 	if (dev_is_sata(dev) || dev->dev_type == SAS_END_DEVICE)
 		list_add_tail(&dev->disco_list_node, &port->disco_list);
-	else {
-		spin_lock_irq(&port->dev_list_lock);
-		list_add_tail(&dev->dev_list_node, &port->dev_list);
-		spin_unlock_irq(&port->dev_list_lock);
-	}
+	else
+		list_add_tail(&dev->dev_list_node, &port->expander_list);
 
 	spin_lock_irq(&port->phy_list_lock);
 	list_for_each_entry(phy, &port->phy_list, port_phy_el)
@@ -212,34 +215,83 @@ void sas_notify_lldd_dev_gone(struct domain_device *dev)
 	}
 }
 
-static void sas_probe_devices(struct work_struct *work)
+static void sas_add_device(struct work_struct *work)
 {
-	struct domain_device *dev, *n;
-	struct sas_discovery_event *ev = to_sas_discovery_event(work);
-	struct asd_sas_port *port = ev->port;
+	int err;
+	struct sas_device_event *ev = to_sas_device_event(work);
+	struct domain_device *dev = ev->device;
+	struct asd_sas_port *port = dev->port;
 
-	clear_bit(DISCE_PROBE, &port->disc.pending);
+	/* if device is not on disco_list, BUG! */
+	BUG_ON(list_empty(&dev->disco_list_node));
 
-	/* devices must be domain members before link recovery and probe */
-	list_for_each_entry(dev, &port->disco_list, disco_list_node) {
-		spin_lock_irq(&port->dev_list_lock);
-		list_add_tail(&dev->dev_list_node, &port->dev_list);
-		spin_unlock_irq(&port->dev_list_lock);
-	}
+	/* avoid add a device that has gone */
+	if (test_bit(SAS_DEV_DESTROY, &dev->state))
+		goto out;
 
-	sas_probe_sata(port);
+	mutex_lock(&port->ha->disco_mutex);
+	spin_lock_irq(&port->dev_list_lock);
+	list_add_tail(&dev->dev_list_node, &port->dev_list);
+	spin_unlock_irq(&port->dev_list_lock);
+	mutex_unlock(&port->ha->disco_mutex);
+
+	sas_probe_sata_device(dev);
 
-	list_for_each_entry_safe(dev, n, &port->disco_list, disco_list_node) {
-		int err;
+	if (!test_bit(SAS_DEV_PROBE_FAIL, &dev->state)) {
 
 		err = sas_rphy_add(dev->rphy);
 		if (err)
 			sas_fail_probe(dev, __func__, err);
-		else
-			list_del_init(&dev->disco_list_node);
 	}
+
+out:
+	/* race with discovery */
+	mutex_lock(&port->ha->disco_mutex);
+	list_del_init(&dev->disco_list_node);
+	mutex_unlock(&port->ha->disco_mutex);
+
+	kfree(ev);
 }
 
+static void sas_del_device(struct work_struct *work)
+{
+	struct sas_device_event *ev = to_sas_device_event(work);
+	struct domain_device *dev = ev->device;
+	struct asd_sas_port *port = dev->port;
+
+	struct sas_port *sas_port = dev_to_sas_port(dev->rphy->dev.parent);
+
+	if (dev->dev_type == SAS_EDGE_EXPANDER_DEVICE
+			|| dev->dev_type == SAS_FANOUT_EXPANDER_DEVICE)
+		sas_del_parent_port(dev);
+
+	/* expander can not come to this branch */
+	if (list_empty(&dev->dev_list_node)) {
+		sas_rphy_free(dev->rphy);
+		sas_unregister_fail_dev(port, dev);
+		goto out;
+	}
+
+	if (test_and_clear_bit(SAS_DEV_PROBE_FAIL, &dev->state)) {
+		/* this rphy never saw sas_rphy_add */
+		sas_rphy_free(dev->rphy);
+		sas_unregister_common_dev(port, dev);
+
+		goto out;
+	}
+
+	sas_remove_children(&dev->rphy->dev);
+	sas_rphy_delete(dev->rphy);
+	sas_unregister_common_dev(port, dev);
+
+out:
+	if (!sas_port->num_phys)
+		sas_port_delete(sas_port);
+
+	kfree(ev);
+ }
+ 
+
 static void sas_suspend_devices(struct work_struct *work)
 {
 	struct asd_sas_phy *phy;
@@ -260,6 +312,9 @@ static void sas_suspend_devices(struct work_struct *work)
 	list_for_each_entry(dev, &port->dev_list, dev_list_node)
 		sas_notify_lldd_dev_gone(dev);
 
+	list_for_each_entry(dev, &port->expander_list, dev_list_node)
+		sas_notify_lldd_dev_gone(dev);
+
 	/* we are suspending, so we know events are disabled and
 	 * phy_list is not being mutated
 	 */
@@ -281,6 +336,29 @@ static void sas_resume_devices(struct work_struct *work)
 	sas_resume_sata(port);
 }
 
+const work_func_t sas_dev_event_fns[SAS_DEVICE_NUM_EVENTS] = {
+		[SAS_DEVICE_ADD] = sas_add_device,
+		[SAS_DEVICE_DEL] = sas_del_device,
+	};
+
+int sas_notify_device_event(struct domain_device *dev, enum sas_device_event_type ev)
+{
+	struct sas_device_event *evt;
+
+	BUG_ON(ev >= SAS_DEVICE_NUM_EVENTS);
+
+	evt = kmalloc(sizeof(*evt), GFP_KERNEL);
+	if (!evt)
+		return 0;
+
+	INIT_WORK(&evt->work, sas_dev_event_fns[ev]);
+	evt->device = dev;
+	evt->event = ev;
+
+	return queue_work(sas_dev_wq, &evt->work);
+}
+
+
 /**
  * sas_discover_end_dev -- discover an end device (SSP, etc)
  * @end: pointer to domain device of interest
@@ -294,7 +372,7 @@ int sas_discover_end_dev(struct domain_device *dev)
 	res = sas_notify_lldd_dev_found(dev);
 	if (res)
 		return res;
-	sas_discover_event(dev->port, DISCE_PROBE);
+	sas_notify_device_event(dev, SAS_DEVICE_ADD);
 
 	return 0;
 }
@@ -326,21 +404,49 @@ void sas_free_device(struct kref *kref)
 	kfree(dev);
 }
 
+static void sas_unregister_fail_dev(struct asd_sas_port *port,
+				struct domain_device *dev)
+{
+	sas_notify_lldd_dev_gone(dev);
+
+	/* race with discovery */
+	mutex_lock(&port->ha->disco_mutex);
+	if (!dev->parent)
+		dev->port->port_dev = NULL;
+	else
+		list_del_init(&dev->siblings);
+
+	mutex_unlock(&port->ha->disco_mutex);
+
+	sas_put_device(dev);
+}
+
+
 static void sas_unregister_common_dev(struct asd_sas_port *port, struct domain_device *dev)
 {
 	struct sas_ha_struct *ha = port->ha;
 
 	sas_notify_lldd_dev_gone(dev);
+
+	/* race with discovery */
+	mutex_lock(&port->ha->disco_mutex);
+
 	if (!dev->parent)
 		dev->port->port_dev = NULL;
 	else
 		list_del_init(&dev->siblings);
 
-	spin_lock_irq(&port->dev_list_lock);
-	list_del_init(&dev->dev_list_node);
-	if (dev_is_sata(dev))
-		sas_ata_end_eh(dev->sata_dev.ap);
-	spin_unlock_irq(&port->dev_list_lock);
+	if (dev->dev_type == SAS_EDGE_EXPANDER_DEVICE
+		|| dev->dev_type == SAS_FANOUT_EXPANDER_DEVICE) {
+		list_del_init(&dev->dev_list_node);
+	} else {
+		spin_lock_irq(&port->dev_list_lock);
+		list_del_init(&dev->dev_list_node);
+		if (dev_is_sata(dev))
+			sas_ata_end_eh(dev->sata_dev.ap);
+		spin_unlock_irq(&port->dev_list_lock);
+	}
+	mutex_unlock(&port->ha->disco_mutex);
 
 	spin_lock_irq(&ha->lock);
 	if (dev->dev_type == SAS_END_DEVICE &&
@@ -353,53 +459,36 @@ static void sas_unregister_common_dev(struct asd_sas_port *port, struct domain_d
 	sas_put_device(dev);
 }
 
-static void sas_destruct_devices(struct work_struct *work)
-{
-	struct domain_device *dev, *n;
-	struct sas_discovery_event *ev = to_sas_discovery_event(work);
-	struct asd_sas_port *port = ev->port;
-
-	clear_bit(DISCE_DESTRUCT, &port->disc.pending);
-
-	list_for_each_entry_safe(dev, n, &port->destroy_list, disco_list_node) {
-		list_del_init(&dev->disco_list_node);
-
-		sas_remove_children(&dev->rphy->dev);
-		sas_rphy_delete(dev->rphy);
-		sas_unregister_common_dev(port, dev);
-	}
-}
-
 void sas_unregister_dev(struct asd_sas_port *port, struct domain_device *dev)
 {
-	if (!test_bit(SAS_DEV_DESTROY, &dev->state) &&
-	    !list_empty(&dev->disco_list_node)) {
-		/* this rphy never saw sas_rphy_add */
-		list_del_init(&dev->disco_list_node);
-		sas_rphy_free(dev->rphy);
-		sas_unregister_common_dev(port, dev);
-		return;
-	}
-
-	if (!test_and_set_bit(SAS_DEV_DESTROY, &dev->state)) {
-		sas_rphy_unlink(dev->rphy);
-		list_move_tail(&dev->disco_list_node, &port->destroy_list);
-		sas_discover_event(dev->port, DISCE_DESTRUCT);
-	}
+	if (!test_and_set_bit(SAS_DEV_DESTROY, &dev->state))
+		sas_notify_device_event(dev, SAS_DEVICE_DEL);
 }
 
 void sas_unregister_domain_devices(struct asd_sas_port *port, int gone)
 {
 	struct domain_device *dev, *n;
 
+	/* race with device add or device delete */
+	mutex_lock(&port->ha->disco_mutex);
+
+	list_for_each_entry_safe(dev, n, &port->disco_list, disco_list_node)
+		sas_unregister_dev(port, dev);
+
 	list_for_each_entry_safe_reverse(dev, n, &port->dev_list, dev_list_node) {
 		if (gone)
 			set_bit(SAS_DEV_GONE, &dev->state);
 		sas_unregister_dev(port, dev);
 	}
 
-	list_for_each_entry_safe(dev, n, &port->disco_list, disco_list_node)
+	list_for_each_entry_safe_reverse(dev, n,
+				&port->expander_list, dev_list_node) {
+		if (gone)
+			set_bit(SAS_DEV_GONE, &dev->state);
+
 		sas_unregister_dev(port, dev);
+	}
+	mutex_unlock(&port->ha->disco_mutex);
 
 	port->port->rphy = NULL;
 
@@ -427,6 +516,8 @@ void sas_device_set_phy(struct domain_device *dev, struct sas_port *port)
 
 /* ---------- Discovery and Revalidation ---------- */
 
+#define SAS_MAX_WAIT_RESCOURCE_CLEAR_TIME (3 * 60)
+
 /**
  * sas_discover_domain -- discover the domain
  * @port: port to the domain of interest
@@ -445,9 +536,36 @@ static void sas_discover_domain(struct work_struct *work)
 
 	clear_bit(DISCE_DISCOVER_DOMAIN, &port->disc.pending);
 
-	if (port->port_dev)
-		return;
+	/* if port_dev still valid, may another hot-remove is
+	 * processing, max wait time is 3 minutes */
+	if (port->port_dev) {
+		int cnt = 0;
+
+		while (1) {
+			msleep(100);
+
+			mutex_lock(&port->ha->disco_mutex);
+			if (list_empty(&port->dev_list)
+				&& list_empty(&port->expander_list)
+				&& list_empty(&port->disco_list)) {
+				mutex_unlock(&port->ha->disco_mutex);
+				break;
+			}
+			mutex_unlock(&port->ha->disco_mutex);
+
+			cnt++;
+			if (cnt > SAS_MAX_WAIT_RESCOURCE_CLEAR_TIME * 10) {
+				SAS_DPRINTK(
+				"Timeout for wait port %d clear, pid:%d\n",
+					port->id,
+					task_pid_nr(current));
+				cnt = 0;
+				return;
+			}
+		}
+	}
 
+	mutex_lock(&port->ha->disco_mutex);
 	error = sas_get_port_device(port);
 	if (error)
 		return;
@@ -482,14 +600,13 @@ static void sas_discover_domain(struct work_struct *work)
 	if (error) {
 		sas_rphy_free(dev->rphy);
 		list_del_init(&dev->disco_list_node);
-		spin_lock_irq(&port->dev_list_lock);
 		list_del_init(&dev->dev_list_node);
-		spin_unlock_irq(&port->dev_list_lock);
 
 		sas_put_device(dev);
 		port->port_dev = NULL;
 	}
 
+	mutex_unlock(&port->ha->disco_mutex);
 	SAS_DPRINTK("DONE DISCOVERY on port %d, pid:%d, result:%d\n", port->id,
 		    task_pid_nr(current), error);
 }
@@ -578,10 +695,8 @@ void sas_init_disc(struct sas_discovery *disc, struct asd_sas_port *port)
 	static const work_func_t sas_event_fns[DISC_NUM_EVENTS] = {
 		[DISCE_DISCOVER_DOMAIN] = sas_discover_domain,
 		[DISCE_REVALIDATE_DOMAIN] = sas_revalidate_domain,
-		[DISCE_PROBE] = sas_probe_devices,
 		[DISCE_SUSPEND] = sas_suspend_devices,
 		[DISCE_RESUME] = sas_resume_devices,
-		[DISCE_DESTRUCT] = sas_destruct_devices,
 	};
 
 	disc->pending = 0;
diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c
index 022bb6e..eb00331 100644
--- a/drivers/scsi/libsas/sas_expander.c
+++ b/drivers/scsi/libsas/sas_expander.c
@@ -643,9 +643,25 @@ static int sas_dev_present_in_domain(struct asd_sas_port *port,
 	if (SAS_ADDR(port->sas_addr) == SAS_ADDR(sas_addr))
 		return 1;
 	list_for_each_entry(dev, &port->dev_list, dev_list_node) {
-		if (SAS_ADDR(dev->sas_addr) == SAS_ADDR(sas_addr))
+		if (SAS_ADDR(dev->sas_addr) == SAS_ADDR(sas_addr)) {
+			/* ignore the device that has been gone */
+			if (sas_dev_gone(dev))
+				continue;
+
+			return 1;
+		}
+	}
+
+	list_for_each_entry(dev, &port->expander_list, dev_list_node) {
+		if (SAS_ADDR(dev->sas_addr) == SAS_ADDR(sas_addr)) {
+			/* ignore the device that has been gone */
+			if (sas_dev_gone(dev))
+				continue;
+
 			return 1;
+		}
 	}
+
 	return 0;
 }
 
@@ -659,6 +675,9 @@ int sas_smp_get_phy_events(struct sas_phy *phy)
 	struct sas_rphy *rphy = dev_to_rphy(phy->dev.parent);
 	struct domain_device *dev = sas_find_dev_by_rphy(rphy);
 
+	if (!dev)
+		return -ENODEV;
+
 	req = alloc_smp_req(RPEL_REQ_SIZE);
 	if (!req)
 		return -ENOMEM;
@@ -792,7 +811,7 @@ static struct domain_device *sas_ex_discover_end_dev(
 	memcpy(child->sas_addr, phy->attached_sas_addr, SAS_ADDR_SIZE);
 	sas_hash_addr(child->hashed_sas_addr, child->sas_addr);
 	if (!phy->port) {
-		phy->port = sas_port_alloc(&parent->rphy->dev, phy_id);
+		phy->port = sas_port_alloc_num(&parent->rphy->dev);
 		if (unlikely(!phy->port))
 			goto out_err;
 		if (unlikely(sas_port_add(phy->port) != 0)) {
@@ -868,9 +887,6 @@ static struct domain_device *sas_ex_discover_end_dev(
  out_list_del:
 	sas_rphy_free(child->rphy);
 	list_del(&child->disco_list_node);
-	spin_lock_irq(&parent->port->dev_list_lock);
-	list_del(&child->dev_list_node);
-	spin_unlock_irq(&parent->port->dev_list_lock);
  out_free:
 	sas_port_delete(phy->port);
  out_err:
@@ -926,7 +942,7 @@ static struct domain_device *sas_ex_discover_expander(
 	if (!child)
 		return NULL;
 
-	phy->port = sas_port_alloc(&parent->rphy->dev, phy_id);
+	phy->port = sas_port_alloc_num(&parent->rphy->dev);
 	/* FIXME: better error handling */
 	BUG_ON(sas_port_add(phy->port) != 0);
 
@@ -964,16 +980,12 @@ static struct domain_device *sas_ex_discover_expander(
 	sas_fill_in_rphy(child, rphy);
 	sas_rphy_add(rphy);
 
-	spin_lock_irq(&parent->port->dev_list_lock);
-	list_add_tail(&child->dev_list_node, &parent->port->dev_list);
-	spin_unlock_irq(&parent->port->dev_list_lock);
+	list_add_tail(&child->dev_list_node, &parent->port->expander_list);
 
 	res = sas_discover_expander(child);
 	if (res) {
 		sas_rphy_delete(rphy);
-		spin_lock_irq(&parent->port->dev_list_lock);
 		list_del(&child->dev_list_node);
-		spin_unlock_irq(&parent->port->dev_list_lock);
 		sas_put_device(child);
 		return NULL;
 	}
@@ -1130,6 +1142,9 @@ static int sas_check_level_subtractive_boundary(struct domain_device *dev)
 	u8 sub_addr[8] = {0, };
 
 	list_for_each_entry(child, &ex->children, siblings) {
+		if (sas_dev_gone(child))
+			continue;
+
 		if (child->dev_type != SAS_EDGE_EXPANDER_DEVICE &&
 		    child->dev_type != SAS_FANOUT_EXPANDER_DEVICE)
 			continue;
@@ -1618,7 +1633,7 @@ static int sas_ex_level_discovery(struct asd_sas_port *port, const int level)
 	int res = 0;
 	struct domain_device *dev;
 
-	list_for_each_entry(dev, &port->dev_list, dev_list_node) {
+	list_for_each_entry(dev, &port->expander_list, dev_list_node) {
 		if (dev->dev_type == SAS_EDGE_EXPANDER_DEVICE ||
 		    dev->dev_type == SAS_FANOUT_EXPANDER_DEVICE) {
 			struct sas_expander_device *ex =
@@ -1849,6 +1864,9 @@ static int sas_find_bcast_dev(struct domain_device *dev,
 			SAS_DPRINTK("Expander phys DID NOT change\n");
 	}
 	list_for_each_entry(ch, &ex->children, siblings) {
+		if (sas_dev_gone(ch))
+			continue;
+
 		if (ch->dev_type == SAS_EDGE_EXPANDER_DEVICE || ch->dev_type == SAS_FANOUT_EXPANDER_DEVICE) {
 			res = sas_find_bcast_dev(ch, src_dev);
 			if (*src_dev)
@@ -1865,6 +1883,9 @@ static void sas_unregister_ex_tree(struct asd_sas_port *port, struct domain_devi
 	struct domain_device *child, *n;
 
 	list_for_each_entry_safe(child, n, &ex->children, siblings) {
+		if (sas_dev_gone(child))
+			continue;
+
 		set_bit(SAS_DEV_GONE, &child->state);
 		if (child->dev_type == SAS_EDGE_EXPANDER_DEVICE ||
 		    child->dev_type == SAS_FANOUT_EXPANDER_DEVICE)
@@ -1884,6 +1905,9 @@ static void sas_unregister_devs_sas_addr(struct domain_device *parent,
 	if (last) {
 		list_for_each_entry_safe(child, n,
 			&ex_dev->children, siblings) {
+			if (sas_dev_gone(child))
+				continue;
+
 			if (SAS_ADDR(child->sas_addr) ==
 			    SAS_ADDR(phy->attached_sas_addr)) {
 				set_bit(SAS_DEV_GONE, &child->state);
@@ -1916,6 +1940,9 @@ static int sas_discover_bfs_by_root_level(struct domain_device *root,
 	int res = 0;
 
 	list_for_each_entry(child, &ex_root->children, siblings) {
+		if (sas_dev_gone(child))
+			continue;
+
 		if (child->dev_type == SAS_EDGE_EXPANDER_DEVICE ||
 		    child->dev_type == SAS_FANOUT_EXPANDER_DEVICE) {
 			struct sas_expander_device *ex =
@@ -1968,6 +1995,9 @@ static int sas_discover_new(struct domain_device *dev, int phy_id)
 	if (res)
 		return res;
 	list_for_each_entry(child, &dev->ex_dev.children, siblings) {
+		if (sas_dev_gone(child))
+			continue;
+
 		if (SAS_ADDR(child->sas_addr) ==
 		    SAS_ADDR(ex_phy->attached_sas_addr)) {
 			if (child->dev_type == SAS_EDGE_EXPANDER_DEVICE ||
diff --git a/drivers/scsi/libsas/sas_init.c b/drivers/scsi/libsas/sas_init.c
index 301d6ce..fa2a98a 100644
--- a/drivers/scsi/libsas/sas_init.c
+++ b/drivers/scsi/libsas/sas_init.c
@@ -39,6 +39,7 @@
 #include "../scsi_sas_internal.h"
 
 static struct kmem_cache *sas_task_cache;
+struct workqueue_struct *sas_dev_wq;
 
 struct sas_task *sas_alloc_task(gfp_t flags)
 {
@@ -131,6 +132,12 @@ int sas_register_ha(struct sas_ha_struct *sas_ha)
 	INIT_LIST_HEAD(&sas_ha->defer_q);
 	INIT_LIST_HEAD(&sas_ha->eh_dev_q);
 
+	sas_dev_wq = create_singlethread_workqueue("sas_dev_wq");
+	if (!sas_dev_wq) {
+		error = -EINVAL;
+		return error;
+	}
+
 	error = sas_register_phys(sas_ha);
 	if (error) {
 		printk(KERN_NOTICE "couldn't register sas phys:%d\n", error);
@@ -184,6 +191,7 @@ int sas_unregister_ha(struct sas_ha_struct *sas_ha)
 	__sas_drain_work(sas_ha);
 	mutex_unlock(&sas_ha->drain_mutex);
 
+	destroy_workqueue(sas_dev_wq);
 	return 0;
 }
 
@@ -251,7 +259,12 @@ static int transport_sas_phy_reset(struct sas_phy *phy, int hard_reset)
 	} else {
 		struct sas_rphy *rphy = dev_to_rphy(phy->dev.parent);
 		struct domain_device *ddev = sas_find_dev_by_rphy(rphy);
-		struct domain_device *ata_dev = sas_ex_to_ata(ddev, phy->number);
+		struct domain_device *ata_dev;
+
+		if (!ddev)
+			return -ENODEV;
+
+		ata_dev = sas_ex_to_ata(ddev, phy->number);
 
 		if (ata_dev && !hard_reset) {
 			sas_ata_schedule_reset(ata_dev);
@@ -287,6 +300,9 @@ static int sas_phy_enable(struct sas_phy *phy, int enable)
 		struct sas_rphy *rphy = dev_to_rphy(phy->dev.parent);
 		struct domain_device *ddev = sas_find_dev_by_rphy(rphy);
 
+		if (!ddev)
+			return -ENODEV;
+
 		if (enable)
 			ret = transport_sas_phy_reset(phy, 0);
 		else
@@ -319,6 +335,10 @@ int sas_phy_reset(struct sas_phy *phy, int hard_reset)
 	} else {
 		struct sas_rphy *rphy = dev_to_rphy(phy->dev.parent);
 		struct domain_device *ddev = sas_find_dev_by_rphy(rphy);
+
+		if (!ddev)
+			return -ENODEV;
+
 		ret = sas_smp_phy_control(ddev, phy->number, reset_type, NULL);
 	}
 	return ret;
@@ -355,6 +375,10 @@ int sas_set_phy_speed(struct sas_phy *phy,
 	} else {
 		struct sas_rphy *rphy = dev_to_rphy(phy->dev.parent);
 		struct domain_device *ddev = sas_find_dev_by_rphy(rphy);
+
+		if (!ddev)
+			return -ENODEV;
+
 		ret = sas_smp_phy_control(ddev, phy->number,
 					  PHY_FUNC_LINK_RESET, rates);
 
diff --git a/drivers/scsi/libsas/sas_internal.h b/drivers/scsi/libsas/sas_internal.h
index 79c7c48..23a00ca 100644
--- a/drivers/scsi/libsas/sas_internal.h
+++ b/drivers/scsi/libsas/sas_internal.h
@@ -51,6 +51,29 @@ struct sas_phy_data {
 	struct sas_work enable_work;
 };
 
+enum sas_device_event_type {
+	SAS_DEVICE_ADD   = 0U,
+	SAS_DEVICE_DEL = 1,
+	SAS_DEVICE_NUM_EVENTS
+};
+
+struct sas_device_event {
+	struct work_struct work;
+	struct domain_device *device;
+	enum sas_device_event_type event;
+};
+
+static inline struct sas_device_event *to_sas_device_event(struct work_struct *work)
+{
+	struct sas_device_event *ev = container_of(work, typeof(*ev), work);
+
+	return ev;
+}
+
+extern struct workqueue_struct *sas_dev_wq;
+extern const work_func_t sas_phy_event_fns[PHY_NUM_EVENTS];
+extern const work_func_t sas_port_event_fns[PORT_NUM_EVENTS];
+
 void sas_scsi_recover_host(struct Scsi_Host *shost);
 
 int sas_show_class(enum sas_class class, char *buf);
@@ -99,8 +122,8 @@ void sas_hae_reset(struct work_struct *work);
 
 void sas_free_device(struct kref *kref);
 
-extern const work_func_t sas_phy_event_fns[PHY_NUM_EVENTS];
-extern const work_func_t sas_port_event_fns[PORT_NUM_EVENTS];
+int sas_notify_device_event(struct domain_device *dev, 
+	enum sas_device_event_type ev);
 
 #ifdef CONFIG_SCSI_SAS_HOST_SMP
 extern int sas_smp_host_handler(struct Scsi_Host *shost, struct request *req,
@@ -122,6 +145,7 @@ static inline void sas_fail_probe(struct domain_device *dev, const char *func, i
 		    func, dev->parent ? "exp-attached" :
 					    "direct-attached",
 		    SAS_ADDR(dev->sas_addr), err);
+	set_bit(SAS_DEV_PROBE_FAIL, &dev->state);
 	sas_unregister_dev(dev->port, dev);
 }
 
@@ -172,7 +196,7 @@ static inline void sas_add_parent_port(struct domain_device *dev, int phy_id)
 	struct ex_phy *ex_phy = &ex->ex_phy[phy_id];
 
 	if (!ex->parent_port) {
-		ex->parent_port = sas_port_alloc(&dev->rphy->dev, phy_id);
+		ex->parent_port = sas_port_alloc_num(&dev->rphy->dev);
 		/* FIXME: error handling */
 		BUG_ON(!ex->parent_port);
 		BUG_ON(sas_port_add(ex->parent_port));
@@ -181,6 +205,15 @@ static inline void sas_add_parent_port(struct domain_device *dev, int phy_id)
 	sas_port_add_phy(ex->parent_port, ex_phy->phy);
 }
 
+static inline void sas_del_parent_port(struct domain_device *dev)
+{
+	struct expander_device *ex = &dev->ex_dev;
+
+	if (ex->parent_port)
+		sas_port_delete(ex->parent_port);
+}
+
+
 static inline struct domain_device *sas_alloc_device(void)
 {
 	struct domain_device *dev = kzalloc(sizeof(*dev), GFP_KERNEL);
@@ -200,4 +233,11 @@ static inline void sas_put_device(struct domain_device *dev)
 	kref_put(&dev->kref, sas_free_device);
 }
 
+static inline int sas_dev_gone(struct domain_device *dev)
+{
+	return (test_bit(SAS_DEV_GONE, &dev->state) 
+			|| test_bit(SAS_DEV_PROBE_FAIL, &dev->state)
+			|| test_bit(SAS_DEV_DESTROY, &dev->state));
+}
+
 #endif /* _SAS_INTERNAL_H_ */
diff --git a/drivers/scsi/libsas/sas_port.c b/drivers/scsi/libsas/sas_port.c
index 6364346..ef4fdc9 100644
--- a/drivers/scsi/libsas/sas_port.c
+++ b/drivers/scsi/libsas/sas_port.c
@@ -61,6 +61,14 @@ static void sas_resume_port(struct asd_sas_phy *phy)
 	 * 2/ force the next revalidation to check all expander phys
 	 */
 	list_for_each_entry(dev, &port->dev_list, dev_list_node) {
+		int rc;
+
+		rc = sas_notify_lldd_dev_found(dev);
+		if (rc)
+			sas_unregister_dev(port, dev);
+	}
+
+	list_for_each_entry(dev, &port->expander_list, dev_list_node) {
 		int i, rc;
 
 		rc = sas_notify_lldd_dev_found(dev);
@@ -173,7 +181,7 @@ static void sas_form_port(struct asd_sas_phy *phy)
 	spin_unlock_irqrestore(&sas_ha->phy_port_lock, flags);
 
 	if (!port->port) {
-		port->port = sas_port_alloc(phy->phy->dev.parent, port->id);
+		port->port = sas_port_alloc_num(phy->phy->dev.parent);
 		BUG_ON(!port->port);
 		sas_port_add(port->port);
 	}
@@ -317,7 +325,7 @@ static void sas_init_port(struct asd_sas_port *port,
 	port->id = i;
 	INIT_LIST_HEAD(&port->dev_list);
 	INIT_LIST_HEAD(&port->disco_list);
-	INIT_LIST_HEAD(&port->destroy_list);
+	INIT_LIST_HEAD(&port->expander_list);
 	spin_lock_init(&port->phy_list_lock);
 	INIT_LIST_HEAD(&port->phy_list);
 	port->ha = sas_ha;
diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c
index 519dac4..e3ce60f 100644
--- a/drivers/scsi/libsas/sas_scsi_host.c
+++ b/drivers/scsi/libsas/sas_scsi_host.c
@@ -836,12 +836,35 @@ struct domain_device *sas_find_dev_by_rphy(struct sas_rphy *rphy)
 		spin_lock(&port->dev_list_lock);
 		list_for_each_entry(dev, &port->dev_list, dev_list_node) {
 			if (rphy == dev->rphy) {
+				if (sas_dev_gone(dev)) {
+					SAS_DPRINTK(
+					"%s, ignore device %llx state %lx\n",
+					 __func__, SAS_ADDR(dev->sas_addr),
+					 dev->state);
+					continue;
+				}
+
 				found_dev = dev;
 				spin_unlock(&port->dev_list_lock);
 				goto found;
 			}
 		}
 		spin_unlock(&port->dev_list_lock);
+
+		list_for_each_entry(dev, &port->expander_list, dev_list_node) {
+			if (rphy == dev->rphy) {
+				if (sas_dev_gone(dev)) {
+					SAS_DPRINTK(
+					"%s, ignore device %llx state %lx\n",
+					 __func__, SAS_ADDR(dev->sas_addr),
+					 dev->state);
+					continue;
+				}
+
+				found_dev = dev;
+				goto found;
+			}
+		}
 	}
  found:
 	spin_unlock_irqrestore(&ha->phy_port_lock, flags);
diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h
index 6c25173..334d512 100644
--- a/include/scsi/libsas.h
+++ b/include/scsi/libsas.h
@@ -181,7 +181,8 @@ struct ssp_device {
 enum {
 	SAS_DEV_GONE,
 	SAS_DEV_FOUND, /* device notified to lldd */
-	SAS_DEV_DESTROY,
+	SAS_DEV_PROBE_FAIL,/* device probe fail */
+	SAS_DEV_DESTROY, /* device will be destroyed from system */
 	SAS_DEV_EH_PENDING,
 	SAS_DEV_LU_RESET,
 	SAS_DEV_RESET,
@@ -268,7 +269,7 @@ struct asd_sas_port {
 	spinlock_t dev_list_lock;
 	struct list_head dev_list;
 	struct list_head disco_list;
-	struct list_head destroy_list;
+	struct list_head expander_list;
 	enum   sas_linkrate linkrate;
 
 	struct sas_work work;
diff --git a/include/scsi/sas_ata.h b/include/scsi/sas_ata.h
index 00f41ae..a3104a1 100644
--- a/include/scsi/sas_ata.h
+++ b/include/scsi/sas_ata.h
@@ -44,7 +44,7 @@ void sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q,
 		struct list_head *done_q);
 void sas_ata_schedule_reset(struct domain_device *dev);
 void sas_ata_wait_eh(struct domain_device *dev);
-void sas_probe_sata(struct asd_sas_port *port);
+void sas_probe_sata_device(struct domain_device *dev);
 void sas_suspend_sata(struct asd_sas_port *port);
 void sas_resume_sata(struct asd_sas_port *port);
 void sas_ata_end_eh(struct ata_port *ap);
@@ -80,7 +80,7 @@ static inline void sas_ata_wait_eh(struct domain_device *dev)
 {
 }
 
-static inline void sas_probe_sata(struct asd_sas_port *port)
+static inline void sas_probe_sata_device(struct domain_device *dev)
 {
 }
 
