xref: /openbmc/linux/drivers/scsi/libsas/sas_event.c (revision a2a59faa)
186b89cb0SChristoph Hellwig // SPDX-License-Identifier: GPL-2.0
22908d778SJames Bottomley /*
32908d778SJames Bottomley  * Serial Attached SCSI (SAS) Event processing
42908d778SJames Bottomley  *
52908d778SJames Bottomley  * Copyright (C) 2005 Adaptec, Inc.  All rights reserved.
62908d778SJames Bottomley  * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
72908d778SJames Bottomley  */
82908d778SJames Bottomley 
9b1124cd3SDan Williams #include <linux/export.h>
102908d778SJames Bottomley #include <scsi/scsi_host.h>
112908d778SJames Bottomley #include "sas_internal.h"
122908d778SJames Bottomley 
sas_queue_work(struct sas_ha_struct * ha,struct sas_work * sw)13*a2a59faaSJohn Garry bool sas_queue_work(struct sas_ha_struct *ha, struct sas_work *sw)
14b1124cd3SDan Williams {
15b1124cd3SDan Williams 	if (!test_bit(SAS_HA_REGISTERED, &ha->state))
16*a2a59faaSJohn Garry 		return false;
17b1124cd3SDan Williams 
1822b9153fSDan Williams 	if (test_bit(SAS_HA_DRAINING, &ha->state)) {
1922b9153fSDan Williams 		/* add it to the defer list, if not already pending */
2022b9153fSDan Williams 		if (list_empty(&sw->drain_node))
217f6ab569Schenxiang 			list_add_tail(&sw->drain_node, &ha->defer_q);
22*a2a59faaSJohn Garry 		return true;
23b1124cd3SDan Williams 	}
24b1124cd3SDan Williams 
25*a2a59faaSJohn Garry 	return queue_work(ha->event_q, &sw->work);
26*a2a59faaSJohn Garry }
27*a2a59faaSJohn Garry 
sas_queue_event(int event,struct sas_work * work,struct sas_ha_struct * ha)28*a2a59faaSJohn Garry static bool sas_queue_event(int event, struct sas_work *work,
29b1124cd3SDan Williams 			    struct sas_ha_struct *ha)
30b1124cd3SDan Williams {
31b1124cd3SDan Williams 	unsigned long flags;
32*a2a59faaSJohn Garry 	bool rc;
33b1124cd3SDan Williams 
34e4a9c373SDan Williams 	spin_lock_irqsave(&ha->lock, flags);
356d311fa7SJohannes Thumshirn 	rc = sas_queue_work(ha, work);
36e4a9c373SDan Williams 	spin_unlock_irqrestore(&ha->lock, flags);
376d311fa7SJohannes Thumshirn 
386d311fa7SJohannes Thumshirn 	return rc;
39b1124cd3SDan Williams }
40b1124cd3SDan Williams 
sas_queue_deferred_work(struct sas_ha_struct * ha)411bc35475SXiang Chen void sas_queue_deferred_work(struct sas_ha_struct *ha)
42b1124cd3SDan Williams {
4322b9153fSDan Williams 	struct sas_work *sw, *_sw;
44b1124cd3SDan Williams 
451bc35475SXiang Chen 	spin_lock_irq(&ha->lock);
461bc35475SXiang Chen 	list_for_each_entry_safe(sw, _sw, &ha->defer_q, drain_node) {
471bc35475SXiang Chen 		list_del_init(&sw->drain_node);
48*a2a59faaSJohn Garry 
49*a2a59faaSJohn Garry 		if (!sas_queue_work(ha, sw)) {
50307d9f49SXiang Chen 			pm_runtime_put(ha->dev);
511bc35475SXiang Chen 			sas_free_event(to_asd_sas_event(&sw->work));
521bc35475SXiang Chen 		}
53307d9f49SXiang Chen 	}
541bc35475SXiang Chen 	spin_unlock_irq(&ha->lock);
551bc35475SXiang Chen }
561bc35475SXiang Chen 
__sas_drain_work(struct sas_ha_struct * ha)571bc35475SXiang Chen void __sas_drain_work(struct sas_ha_struct *ha)
581bc35475SXiang Chen {
59b1124cd3SDan Williams 	set_bit(SAS_HA_DRAINING, &ha->state);
60b1124cd3SDan Williams 	/* flush submitters */
61e4a9c373SDan Williams 	spin_lock_irq(&ha->lock);
62e4a9c373SDan Williams 	spin_unlock_irq(&ha->lock);
63b1124cd3SDan Williams 
6493bdbd06SJason Yan 	drain_workqueue(ha->event_q);
6593bdbd06SJason Yan 	drain_workqueue(ha->disco_q);
66b1124cd3SDan Williams 
67b1124cd3SDan Williams 	clear_bit(SAS_HA_DRAINING, &ha->state);
681bc35475SXiang Chen 	sas_queue_deferred_work(ha);
695d7f6d10SDan Williams }
705d7f6d10SDan Williams 
sas_drain_work(struct sas_ha_struct * ha)715d7f6d10SDan Williams int sas_drain_work(struct sas_ha_struct *ha)
725d7f6d10SDan Williams {
735d7f6d10SDan Williams 	int err;
745d7f6d10SDan Williams 
755d7f6d10SDan Williams 	err = mutex_lock_interruptible(&ha->drain_mutex);
765d7f6d10SDan Williams 	if (err)
775d7f6d10SDan Williams 		return err;
785d7f6d10SDan Williams 	if (test_bit(SAS_HA_REGISTERED, &ha->state))
795d7f6d10SDan Williams 		__sas_drain_work(ha);
80b1124cd3SDan Williams 	mutex_unlock(&ha->drain_mutex);
81b1124cd3SDan Williams 
82b1124cd3SDan Williams 	return 0;
83b1124cd3SDan Williams }
84b1124cd3SDan Williams EXPORT_SYMBOL_GPL(sas_drain_work);
85b1124cd3SDan Williams 
sas_disable_revalidation(struct sas_ha_struct * ha)8687c8331fSDan Williams void sas_disable_revalidation(struct sas_ha_struct *ha)
8787c8331fSDan Williams {
8887c8331fSDan Williams 	mutex_lock(&ha->disco_mutex);
8987c8331fSDan Williams 	set_bit(SAS_HA_ATA_EH_ACTIVE, &ha->state);
9087c8331fSDan Williams 	mutex_unlock(&ha->disco_mutex);
9187c8331fSDan Williams }
9287c8331fSDan Williams 
sas_enable_revalidation(struct sas_ha_struct * ha)9387c8331fSDan Williams void sas_enable_revalidation(struct sas_ha_struct *ha)
9487c8331fSDan Williams {
9587c8331fSDan Williams 	int i;
9687c8331fSDan Williams 
9787c8331fSDan Williams 	mutex_lock(&ha->disco_mutex);
9887c8331fSDan Williams 	clear_bit(SAS_HA_ATA_EH_ACTIVE, &ha->state);
9987c8331fSDan Williams 	for (i = 0; i < ha->num_phys; i++) {
10087c8331fSDan Williams 		struct asd_sas_port *port = ha->sas_port[i];
10187c8331fSDan Williams 		const int ev = DISCE_REVALIDATE_DOMAIN;
10287c8331fSDan Williams 		struct sas_discovery *d = &port->disc;
1031689c936SJason Yan 		struct asd_sas_phy *sas_phy;
10487c8331fSDan Williams 
10587c8331fSDan Williams 		if (!test_and_clear_bit(ev, &d->pending))
10687c8331fSDan Williams 			continue;
10787c8331fSDan Williams 
10842159d3cSXiang Chen 		spin_lock(&port->phy_list_lock);
10942159d3cSXiang Chen 		if (list_empty(&port->phy_list)) {
11042159d3cSXiang Chen 			spin_unlock(&port->phy_list_lock);
1111689c936SJason Yan 			continue;
11242159d3cSXiang Chen 		}
1131689c936SJason Yan 
1141689c936SJason Yan 		sas_phy = container_of(port->phy_list.next, struct asd_sas_phy,
1151689c936SJason Yan 				port_phy_el);
11642159d3cSXiang Chen 		spin_unlock(&port->phy_list_lock);
117f76d9f1aSAhmed S. Darwish 		sas_notify_port_event(sas_phy,
11819a39831SAhmed S. Darwish 				PORTE_BROADCAST_RCVD, GFP_KERNEL);
11987c8331fSDan Williams 	}
12087c8331fSDan Williams 	mutex_unlock(&ha->disco_mutex);
12187c8331fSDan Williams }
12287c8331fSDan Williams 
1231c393b97SJason Yan 
sas_port_event_worker(struct work_struct * work)1241c393b97SJason Yan static void sas_port_event_worker(struct work_struct *work)
1251c393b97SJason Yan {
1261c393b97SJason Yan 	struct asd_sas_event *ev = to_asd_sas_event(work);
127307d9f49SXiang Chen 	struct asd_sas_phy *phy = ev->phy;
128307d9f49SXiang Chen 	struct sas_ha_struct *ha = phy->ha;
1291c393b97SJason Yan 
1301c393b97SJason Yan 	sas_port_event_fns[ev->event](work);
131307d9f49SXiang Chen 	pm_runtime_put(ha->dev);
1321c393b97SJason Yan 	sas_free_event(ev);
1331c393b97SJason Yan }
1341c393b97SJason Yan 
sas_phy_event_worker(struct work_struct * work)1351c393b97SJason Yan static void sas_phy_event_worker(struct work_struct *work)
1361c393b97SJason Yan {
1371c393b97SJason Yan 	struct asd_sas_event *ev = to_asd_sas_event(work);
138307d9f49SXiang Chen 	struct asd_sas_phy *phy = ev->phy;
139307d9f49SXiang Chen 	struct sas_ha_struct *ha = phy->ha;
1401c393b97SJason Yan 
1411c393b97SJason Yan 	sas_phy_event_fns[ev->event](work);
142307d9f49SXiang Chen 	pm_runtime_put(ha->dev);
1431c393b97SJason Yan 	sas_free_event(ev);
1441c393b97SJason Yan }
1451c393b97SJason Yan 
146bf19aea4SXiang Chen /* defer works of new phys during suspend */
sas_defer_event(struct asd_sas_phy * phy,struct asd_sas_event * ev)147bf19aea4SXiang Chen static bool sas_defer_event(struct asd_sas_phy *phy, struct asd_sas_event *ev)
148bf19aea4SXiang Chen {
149bf19aea4SXiang Chen 	struct sas_ha_struct *ha = phy->ha;
150bf19aea4SXiang Chen 	unsigned long flags;
151bf19aea4SXiang Chen 	bool deferred = false;
152bf19aea4SXiang Chen 
153bf19aea4SXiang Chen 	spin_lock_irqsave(&ha->lock, flags);
154bf19aea4SXiang Chen 	if (test_bit(SAS_HA_RESUMING, &ha->state) && !phy->suspended) {
155bf19aea4SXiang Chen 		struct sas_work *sw = &ev->work;
156bf19aea4SXiang Chen 
157bf19aea4SXiang Chen 		list_add_tail(&sw->drain_node, &ha->defer_q);
158bf19aea4SXiang Chen 		deferred = true;
159bf19aea4SXiang Chen 	}
160bf19aea4SXiang Chen 	spin_unlock_irqrestore(&ha->lock, flags);
161bf19aea4SXiang Chen 	return deferred;
162bf19aea4SXiang Chen }
163bf19aea4SXiang Chen 
sas_notify_port_event(struct asd_sas_phy * phy,enum port_event event,gfp_t gfp_flags)164f1834fd1SJohn Garry void sas_notify_port_event(struct asd_sas_phy *phy, enum port_event event,
1655d6a75a1SAhmed S. Darwish 			   gfp_t gfp_flags)
1662908d778SJames Bottomley {
1672908d778SJames Bottomley 	struct sas_ha_struct *ha = phy->ha;
1685d6a75a1SAhmed S. Darwish 	struct asd_sas_event *ev;
1692908d778SJames Bottomley 
1702908d778SJames Bottomley 	BUG_ON(event >= PORT_NUM_EVENTS);
1712908d778SJames Bottomley 
172f76d9f1aSAhmed S. Darwish 	ev = sas_alloc_event(phy, gfp_flags);
1735d6a75a1SAhmed S. Darwish 	if (!ev)
174f1834fd1SJohn Garry 		return;
1755d6a75a1SAhmed S. Darwish 
176307d9f49SXiang Chen 	/* Call pm_runtime_put() with pairs in sas_port_event_worker() */
177307d9f49SXiang Chen 	pm_runtime_get_noresume(ha->dev);
178307d9f49SXiang Chen 
1791c393b97SJason Yan 	INIT_SAS_EVENT(ev, sas_port_event_worker, phy, event);
1801c393b97SJason Yan 
181bf19aea4SXiang Chen 	if (sas_defer_event(phy, ev))
182f1834fd1SJohn Garry 		return;
183bf19aea4SXiang Chen 
184*a2a59faaSJohn Garry 	if (!sas_queue_event(event, &ev->work, ha)) {
185307d9f49SXiang Chen 		pm_runtime_put(ha->dev);
1861c393b97SJason Yan 		sas_free_event(ev);
187307d9f49SXiang Chen 	}
1882908d778SJames Bottomley }
1895d6a75a1SAhmed S. Darwish EXPORT_SYMBOL_GPL(sas_notify_port_event);
1902908d778SJames Bottomley 
sas_notify_phy_event(struct asd_sas_phy * phy,enum phy_event event,gfp_t gfp_flags)191f1834fd1SJohn Garry void sas_notify_phy_event(struct asd_sas_phy *phy, enum phy_event event,
1925d6a75a1SAhmed S. Darwish 			  gfp_t gfp_flags)
1935d6a75a1SAhmed S. Darwish {
1945d6a75a1SAhmed S. Darwish 	struct sas_ha_struct *ha = phy->ha;
1951c393b97SJason Yan 	struct asd_sas_event *ev;
1965d6a75a1SAhmed S. Darwish 
1975d6a75a1SAhmed S. Darwish 	BUG_ON(event >= PHY_NUM_EVENTS);
1982908d778SJames Bottomley 
199f76d9f1aSAhmed S. Darwish 	ev = sas_alloc_event(phy, gfp_flags);
200c2d0f1a6SAhmed S. Darwish 	if (!ev)
201f1834fd1SJohn Garry 		return;
202c2d0f1a6SAhmed S. Darwish 
203307d9f49SXiang Chen 	/* Call pm_runtime_put() with pairs in sas_phy_event_worker() */
204307d9f49SXiang Chen 	pm_runtime_get_noresume(ha->dev);
205307d9f49SXiang Chen 
2061c393b97SJason Yan 	INIT_SAS_EVENT(ev, sas_phy_event_worker, phy, event);
2071c393b97SJason Yan 
208bf19aea4SXiang Chen 	if (sas_defer_event(phy, ev))
209f1834fd1SJohn Garry 		return;
210bf19aea4SXiang Chen 
211*a2a59faaSJohn Garry 	if (!sas_queue_event(event, &ev->work, ha)) {
212307d9f49SXiang Chen 		pm_runtime_put(ha->dev);
2131c393b97SJason Yan 		sas_free_event(ev);
214307d9f49SXiang Chen 	}
2152908d778SJames Bottomley }
2165d6a75a1SAhmed S. Darwish EXPORT_SYMBOL_GPL(sas_notify_phy_event);
217