125184Sjkh// SPDX-License-Identifier: GPL-2.0
225184Sjkh/*
342621Shm * Serial Attached SCSI (SAS) Event processing
425184Sjkh *
525184Sjkh * Copyright (C) 2005 Adaptec, Inc.  All rights reserved.
625184Sjkh * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
725184Sjkh */
825184Sjkh
925184Sjkh#include <linux/export.h>
1025184Sjkh#include <scsi/scsi_host.h>
1125184Sjkh#include "sas_internal.h"
1225184Sjkh
1325184Sjkhbool sas_queue_work(struct sas_ha_struct *ha, struct sas_work *sw)
1425184Sjkh{
1525184Sjkh	if (!test_bit(SAS_HA_REGISTERED, &ha->state))
1625184Sjkh		return false;
1725184Sjkh
1825184Sjkh	if (test_bit(SAS_HA_DRAINING, &ha->state)) {
1925184Sjkh		/* add it to the defer list, if not already pending */
2025184Sjkh		if (list_empty(&sw->drain_node))
2125184Sjkh			list_add_tail(&sw->drain_node, &ha->defer_q);
2225184Sjkh		return true;
2325184Sjkh	}
2425184Sjkh
2525184Sjkh	return queue_work(ha->event_q, &sw->work);
2625184Sjkh}
2725184Sjkh
2840006Sphkstatic bool sas_queue_event(int event, struct sas_work *work,
2940006Sphk			    struct sas_ha_struct *ha)
3040006Sphk{
3140006Sphk	unsigned long flags;
3240006Sphk	bool rc;
3340006Sphk
3442621Shm	spin_lock_irqsave(&ha->lock, flags);
3542621Shm	rc = sas_queue_work(ha, work);
3642621Shm	spin_unlock_irqrestore(&ha->lock, flags);
3742621Shm
3842621Shm	return rc;
3925184Sjkh}
4025184Sjkh
4125184Sjkhvoid sas_queue_deferred_work(struct sas_ha_struct *ha)
4233682Sbrian{
4325184Sjkh	struct sas_work *sw, *_sw;
4425184Sjkh
4525184Sjkh	spin_lock_irq(&ha->lock);
4625184Sjkh	list_for_each_entry_safe(sw, _sw, &ha->defer_q, drain_node) {
4725184Sjkh		list_del_init(&sw->drain_node);
4825184Sjkh
4925184Sjkh		if (!sas_queue_work(ha, sw)) {
5025184Sjkh			pm_runtime_put(ha->dev);
5125184Sjkh			sas_free_event(to_asd_sas_event(&sw->work));
5225184Sjkh		}
5325184Sjkh	}
5425184Sjkh	spin_unlock_irq(&ha->lock);
5525184Sjkh}
5625184Sjkh
5725184Sjkhvoid __sas_drain_work(struct sas_ha_struct *ha)
5825184Sjkh{
5925184Sjkh	set_bit(SAS_HA_DRAINING, &ha->state);
6025184Sjkh	/* flush submitters */
6125184Sjkh	spin_lock_irq(&ha->lock);
6225184Sjkh	spin_unlock_irq(&ha->lock);
6325184Sjkh
6425184Sjkh	drain_workqueue(ha->event_q);
6525184Sjkh	drain_workqueue(ha->disco_q);
6625184Sjkh
6725184Sjkh	clear_bit(SAS_HA_DRAINING, &ha->state);
6829300Sdanny	sas_queue_deferred_work(ha);
6929300Sdanny}
7029300Sdanny
7129300Sdannyint sas_drain_work(struct sas_ha_struct *ha)
7232382Salex{
7332382Salex	int err;
7432382Salex
7529300Sdanny	err = mutex_lock_interruptible(&ha->drain_mutex);
7629300Sdanny	if (err)
7729300Sdanny		return err;
7829300Sdanny	if (test_bit(SAS_HA_REGISTERED, &ha->state))
7941077Speter		__sas_drain_work(ha);
8029300Sdanny	mutex_unlock(&ha->drain_mutex);
8129300Sdanny
8229300Sdanny	return 0;
8329300Sdanny}
8429300SdannyEXPORT_SYMBOL_GPL(sas_drain_work);
8529300Sdanny
8629300Sdannyvoid sas_disable_revalidation(struct sas_ha_struct *ha)
8729300Sdanny{
8829300Sdanny	mutex_lock(&ha->disco_mutex);
8929300Sdanny	set_bit(SAS_HA_ATA_EH_ACTIVE, &ha->state);
9029300Sdanny	mutex_unlock(&ha->disco_mutex);
9125364Sjkh}
9229300Sdanny
9329300Sdannyvoid sas_enable_revalidation(struct sas_ha_struct *ha)
9433337Salex{
9533337Salex	int i;
9633149Salex
9733149Salex	mutex_lock(&ha->disco_mutex);
9833149Salex	clear_bit(SAS_HA_ATA_EH_ACTIVE, &ha->state);
9933149Salex	for (i = 0; i < ha->num_phys; i++) {
10029300Sdanny		struct asd_sas_port *port = ha->sas_port[i];
10125184Sjkh		const int ev = DISCE_REVALIDATE_DOMAIN;
10225184Sjkh		struct sas_discovery *d = &port->disc;
10340006Sphk		struct asd_sas_phy *sas_phy;
10440006Sphk
10540006Sphk		if (!test_and_clear_bit(ev, &d->pending))
10640006Sphk			continue;
10740006Sphk
10829300Sdanny		spin_lock(&port->phy_list_lock);
10929300Sdanny		if (list_empty(&port->phy_list)) {
11025184Sjkh			spin_unlock(&port->phy_list_lock);
11125184Sjkh			continue;
11225184Sjkh		}
11325184Sjkh
11425184Sjkh		sas_phy = container_of(port->phy_list.next, struct asd_sas_phy,
11525184Sjkh				port_phy_el);
11625184Sjkh		spin_unlock(&port->phy_list_lock);
11725184Sjkh		sas_notify_port_event(sas_phy,
11825184Sjkh				PORTE_BROADCAST_RCVD, GFP_KERNEL);
11925184Sjkh	}
12025184Sjkh	mutex_unlock(&ha->disco_mutex);
12125184Sjkh}
12225184Sjkh
12325184Sjkh
12427218Spststatic void sas_port_event_worker(struct work_struct *work)
12527218Spst{
12627218Spst	struct asd_sas_event *ev = to_asd_sas_event(work);
12727218Spst	struct asd_sas_phy *phy = ev->phy;
12827218Spst	struct sas_ha_struct *ha = phy->ha;
12939267Sjkoshy
13039267Sjkoshy	sas_port_event_fns[ev->event](work);
13139267Sjkoshy	pm_runtime_put(ha->dev);
13239267Sjkoshy	sas_free_event(ev);
13339267Sjkoshy}
13425184Sjkh
13525365Sjkhstatic void sas_phy_event_worker(struct work_struct *work)
13625184Sjkh{
13725184Sjkh	struct asd_sas_event *ev = to_asd_sas_event(work);
13825184Sjkh	struct asd_sas_phy *phy = ev->phy;
13933439Sguido	struct sas_ha_struct *ha = phy->ha;
14033439Sguido
14133439Sguido	sas_phy_event_fns[ev->event](work);
14233439Sguido	pm_runtime_put(ha->dev);
14333439Sguido	sas_free_event(ev);
14433439Sguido}
14533439Sguido
14633439Sguido/* defer works of new phys during suspend */
14733439Sguidostatic bool sas_defer_event(struct asd_sas_phy *phy, struct asd_sas_event *ev)
14833439Sguido{
14925184Sjkh	struct sas_ha_struct *ha = phy->ha;
15025365Sjkh	unsigned long flags;
15125184Sjkh	bool deferred = false;
15225184Sjkh
15325184Sjkh	spin_lock_irqsave(&ha->lock, flags);
15436174Sjkh	if (test_bit(SAS_HA_RESUMING, &ha->state) && !phy->suspended) {
15536174Sjkh		struct sas_work *sw = &ev->work;
15636174Sjkh
15736174Sjkh		list_add_tail(&sw->drain_node, &ha->defer_q);
15836174Sjkh		deferred = true;
15936174Sjkh	}
16036174Sjkh	spin_unlock_irqrestore(&ha->lock, flags);
16136174Sjkh	return deferred;
16236174Sjkh}
16336174Sjkh
16436174Sjkhvoid sas_notify_port_event(struct asd_sas_phy *phy, enum port_event event,
16525184Sjkh			   gfp_t gfp_flags)
16636174Sjkh{
16725184Sjkh	struct sas_ha_struct *ha = phy->ha;
16825184Sjkh	struct asd_sas_event *ev;
16925765Sjkh
17036174Sjkh	BUG_ON(event >= PORT_NUM_EVENTS);
17136174Sjkh
17225765Sjkh	ev = sas_alloc_event(phy, gfp_flags);
17336174Sjkh	if (!ev)
17434395Sjkh		return;
17534395Sjkh
17634395Sjkh	/* Call pm_runtime_put() with pairs in sas_port_event_worker() */
17725184Sjkh	pm_runtime_get_noresume(ha->dev);
17825184Sjkh
17925184Sjkh	INIT_SAS_EVENT(ev, sas_port_event_worker, phy, event);
18025184Sjkh
18125184Sjkh	if (sas_defer_event(phy, ev))
18225184Sjkh		return;
18325184Sjkh
18432949Swollman	if (!sas_queue_event(event, &ev->work, ha)) {
18525184Sjkh		pm_runtime_put(ha->dev);
18625184Sjkh		sas_free_event(ev);
18731472Sobrien	}
18835787Sandreas}
18931472SobrienEXPORT_SYMBOL_GPL(sas_notify_port_event);
19025184Sjkh
19131472Sobrienvoid sas_notify_phy_event(struct asd_sas_phy *phy, enum phy_event event,
19235787Sandreas			  gfp_t gfp_flags)
19325184Sjkh{
19425184Sjkh	struct sas_ha_struct *ha = phy->ha;
19525184Sjkh	struct asd_sas_event *ev;
19625184Sjkh
19725184Sjkh	BUG_ON(event >= PHY_NUM_EVENTS);
19825184Sjkh
19925184Sjkh	ev = sas_alloc_event(phy, gfp_flags);
20025184Sjkh	if (!ev)
20125184Sjkh		return;
20225184Sjkh
20325184Sjkh	/* Call pm_runtime_put() with pairs in sas_phy_event_worker() */
20425184Sjkh	pm_runtime_get_noresume(ha->dev);
20525184Sjkh
20625184Sjkh	INIT_SAS_EVENT(ev, sas_phy_event_worker, phy, event);
20725184Sjkh
20825184Sjkh	if (sas_defer_event(phy, ev))
20925184Sjkh		return;
21025184Sjkh
21125184Sjkh	if (!sas_queue_event(event, &ev->work, ha)) {
21225184Sjkh		pm_runtime_put(ha->dev);
21325184Sjkh		sas_free_event(ev);
21425184Sjkh	}
21525184Sjkh}
21625184SjkhEXPORT_SYMBOL_GPL(sas_notify_phy_event);
21725184Sjkh