diff options
Diffstat (limited to 'drivers/scsi/libsas/sas_event.c')
| -rw-r--r-- | drivers/scsi/libsas/sas_event.c | 77 | 
1 files changed, 63 insertions, 14 deletions
diff --git a/drivers/scsi/libsas/sas_event.c b/drivers/scsi/libsas/sas_event.c index f703115e7a25..3613b9b315bc 100644 --- a/drivers/scsi/libsas/sas_event.c +++ b/drivers/scsi/libsas/sas_event.c @@ -41,12 +41,25 @@ static int sas_queue_event(int event, struct sas_work *work,  	return rc;  } - -void __sas_drain_work(struct sas_ha_struct *ha) +void sas_queue_deferred_work(struct sas_ha_struct *ha)  {  	struct sas_work *sw, *_sw;  	int ret; +	spin_lock_irq(&ha->lock); +	list_for_each_entry_safe(sw, _sw, &ha->defer_q, drain_node) { +		list_del_init(&sw->drain_node); +		ret = sas_queue_work(ha, sw); +		if (ret != 1) { +			pm_runtime_put(ha->dev); +			sas_free_event(to_asd_sas_event(&sw->work)); +		} +	} +	spin_unlock_irq(&ha->lock); +} + +void __sas_drain_work(struct sas_ha_struct *ha) +{  	set_bit(SAS_HA_DRAINING, &ha->state);  	/* flush submitters */  	spin_lock_irq(&ha->lock); @@ -55,16 +68,8 @@ void __sas_drain_work(struct sas_ha_struct *ha)  	drain_workqueue(ha->event_q);  	drain_workqueue(ha->disco_q); -	spin_lock_irq(&ha->lock);  	clear_bit(SAS_HA_DRAINING, &ha->state); -	list_for_each_entry_safe(sw, _sw, &ha->defer_q, drain_node) { -		list_del_init(&sw->drain_node); -		ret = sas_queue_work(ha, sw); -		if (ret != 1) -			sas_free_event(to_asd_sas_event(&sw->work)); - -	} -	spin_unlock_irq(&ha->lock); +	sas_queue_deferred_work(ha);  }  int sas_drain_work(struct sas_ha_struct *ha) @@ -104,11 +109,15 @@ void sas_enable_revalidation(struct sas_ha_struct *ha)  		if (!test_and_clear_bit(ev, &d->pending))  			continue; -		if (list_empty(&port->phy_list)) +		spin_lock(&port->phy_list_lock); +		if (list_empty(&port->phy_list)) { +			spin_unlock(&port->phy_list_lock);  			continue; +		}  		sas_phy = container_of(port->phy_list.next, struct asd_sas_phy,  				port_phy_el); +		spin_unlock(&port->phy_list_lock);  		sas_notify_port_event(sas_phy,  				PORTE_BROADCAST_RCVD, GFP_KERNEL);  	} @@ -119,19 +128,43 @@ void sas_enable_revalidation(struct sas_ha_struct *ha)  static void sas_port_event_worker(struct work_struct *work)  {  	struct asd_sas_event *ev = to_asd_sas_event(work); +	struct asd_sas_phy *phy = ev->phy; +	struct sas_ha_struct *ha = phy->ha;  	sas_port_event_fns[ev->event](work); +	pm_runtime_put(ha->dev);  	sas_free_event(ev);  }  static void sas_phy_event_worker(struct work_struct *work)  {  	struct asd_sas_event *ev = to_asd_sas_event(work); +	struct asd_sas_phy *phy = ev->phy; +	struct sas_ha_struct *ha = phy->ha;  	sas_phy_event_fns[ev->event](work); +	pm_runtime_put(ha->dev);  	sas_free_event(ev);  } +/* defer works of new phys during suspend */ +static bool sas_defer_event(struct asd_sas_phy *phy, struct asd_sas_event *ev) +{ +	struct sas_ha_struct *ha = phy->ha; +	unsigned long flags; +	bool deferred = false; + +	spin_lock_irqsave(&ha->lock, flags); +	if (test_bit(SAS_HA_RESUMING, &ha->state) && !phy->suspended) { +		struct sas_work *sw = &ev->work; + +		list_add_tail(&sw->drain_node, &ha->defer_q); +		deferred = true; +	} +	spin_unlock_irqrestore(&ha->lock, flags); +	return deferred; +} +  int sas_notify_port_event(struct asd_sas_phy *phy, enum port_event event,  			  gfp_t gfp_flags)  { @@ -145,11 +178,19 @@ int sas_notify_port_event(struct asd_sas_phy *phy, enum port_event event,  	if (!ev)  		return -ENOMEM; +	/* Call pm_runtime_put() with pairs in sas_port_event_worker() */ +	pm_runtime_get_noresume(ha->dev); +  	INIT_SAS_EVENT(ev, sas_port_event_worker, phy, event); +	if (sas_defer_event(phy, ev)) +		return 0; +  	ret = sas_queue_event(event, &ev->work, ha); -	if (ret != 1) +	if (ret != 1) { +		pm_runtime_put(ha->dev);  		sas_free_event(ev); +	}  	return ret;  } @@ -168,11 +209,19 @@ int sas_notify_phy_event(struct asd_sas_phy *phy, enum phy_event event,  	if (!ev)  		return -ENOMEM; +	/* Call pm_runtime_put() with pairs in sas_phy_event_worker() */ +	pm_runtime_get_noresume(ha->dev); +  	INIT_SAS_EVENT(ev, sas_phy_event_worker, phy, event); +	if (sas_defer_event(phy, ev)) +		return 0; +  	ret = sas_queue_event(event, &ev->work, ha); -	if (ret != 1) +	if (ret != 1) { +		pm_runtime_put(ha->dev);  		sas_free_event(ev); +	}  	return ret;  }  |