123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216 |
- // SPDX-License-Identifier: GPL-2.0
- /*
- * Serial Attached SCSI (SAS) Event processing
- *
- * Copyright (C) 2005 Adaptec, Inc. All rights reserved.
- * Copyright (C) 2005 Luben Tuikov <[email protected]>
- */
- #include <linux/export.h>
- #include <scsi/scsi_host.h>
- #include "sas_internal.h"
- bool sas_queue_work(struct sas_ha_struct *ha, struct sas_work *sw)
- {
- if (!test_bit(SAS_HA_REGISTERED, &ha->state))
- return false;
- if (test_bit(SAS_HA_DRAINING, &ha->state)) {
- /* add it to the defer list, if not already pending */
- if (list_empty(&sw->drain_node))
- list_add_tail(&sw->drain_node, &ha->defer_q);
- return true;
- }
- return queue_work(ha->event_q, &sw->work);
- }
- static bool sas_queue_event(int event, struct sas_work *work,
- struct sas_ha_struct *ha)
- {
- unsigned long flags;
- bool rc;
- spin_lock_irqsave(&ha->lock, flags);
- rc = sas_queue_work(ha, work);
- spin_unlock_irqrestore(&ha->lock, flags);
- return rc;
- }
- void sas_queue_deferred_work(struct sas_ha_struct *ha)
- {
- struct sas_work *sw, *_sw;
- spin_lock_irq(&ha->lock);
- list_for_each_entry_safe(sw, _sw, &ha->defer_q, drain_node) {
- list_del_init(&sw->drain_node);
- if (!sas_queue_work(ha, sw)) {
- 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);
- spin_unlock_irq(&ha->lock);
- drain_workqueue(ha->event_q);
- drain_workqueue(ha->disco_q);
- clear_bit(SAS_HA_DRAINING, &ha->state);
- sas_queue_deferred_work(ha);
- }
- int sas_drain_work(struct sas_ha_struct *ha)
- {
- int err;
- err = mutex_lock_interruptible(&ha->drain_mutex);
- if (err)
- return err;
- if (test_bit(SAS_HA_REGISTERED, &ha->state))
- __sas_drain_work(ha);
- mutex_unlock(&ha->drain_mutex);
- return 0;
- }
- EXPORT_SYMBOL_GPL(sas_drain_work);
- void sas_disable_revalidation(struct sas_ha_struct *ha)
- {
- mutex_lock(&ha->disco_mutex);
- set_bit(SAS_HA_ATA_EH_ACTIVE, &ha->state);
- mutex_unlock(&ha->disco_mutex);
- }
- void sas_enable_revalidation(struct sas_ha_struct *ha)
- {
- int i;
- mutex_lock(&ha->disco_mutex);
- clear_bit(SAS_HA_ATA_EH_ACTIVE, &ha->state);
- for (i = 0; i < ha->num_phys; i++) {
- struct asd_sas_port *port = ha->sas_port[i];
- const int ev = DISCE_REVALIDATE_DOMAIN;
- struct sas_discovery *d = &port->disc;
- struct asd_sas_phy *sas_phy;
- if (!test_and_clear_bit(ev, &d->pending))
- continue;
- 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);
- }
- mutex_unlock(&ha->disco_mutex);
- }
- 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;
- }
- void sas_notify_port_event(struct asd_sas_phy *phy, enum port_event event,
- gfp_t gfp_flags)
- {
- struct sas_ha_struct *ha = phy->ha;
- struct asd_sas_event *ev;
- BUG_ON(event >= PORT_NUM_EVENTS);
- ev = sas_alloc_event(phy, gfp_flags);
- if (!ev)
- return;
- /* 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;
- if (!sas_queue_event(event, &ev->work, ha)) {
- pm_runtime_put(ha->dev);
- sas_free_event(ev);
- }
- }
- EXPORT_SYMBOL_GPL(sas_notify_port_event);
- void sas_notify_phy_event(struct asd_sas_phy *phy, enum phy_event event,
- gfp_t gfp_flags)
- {
- struct sas_ha_struct *ha = phy->ha;
- struct asd_sas_event *ev;
- BUG_ON(event >= PHY_NUM_EVENTS);
- ev = sas_alloc_event(phy, gfp_flags);
- if (!ev)
- return;
- /* 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;
- if (!sas_queue_event(event, &ev->work, ha)) {
- pm_runtime_put(ha->dev);
- sas_free_event(ev);
- }
- }
- EXPORT_SYMBOL_GPL(sas_notify_phy_event);
|