linux/drivers/scsi/libsas/sas_event.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Serial Attached SCSI (SAS) Event processing
   4 *
   5 * Copyright (C) 2005 Adaptec, Inc.  All rights reserved.
   6 * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
   7 */
   8
   9#include <linux/export.h>
  10#include <scsi/scsi_host.h>
  11#include "sas_internal.h"
  12
  13int sas_queue_work(struct sas_ha_struct *ha, struct sas_work *sw)
  14{
  15        /* it's added to the defer_q when draining so return succeed */
  16        int rc = 1;
  17
  18        if (!test_bit(SAS_HA_REGISTERED, &ha->state))
  19                return 0;
  20
  21        if (test_bit(SAS_HA_DRAINING, &ha->state)) {
  22                /* add it to the defer list, if not already pending */
  23                if (list_empty(&sw->drain_node))
  24                        list_add_tail(&sw->drain_node, &ha->defer_q);
  25        } else
  26                rc = queue_work(ha->event_q, &sw->work);
  27
  28        return rc;
  29}
  30
  31static int sas_queue_event(int event, struct sas_work *work,
  32                            struct sas_ha_struct *ha)
  33{
  34        unsigned long flags;
  35        int rc;
  36
  37        spin_lock_irqsave(&ha->lock, flags);
  38        rc = sas_queue_work(ha, work);
  39        spin_unlock_irqrestore(&ha->lock, flags);
  40
  41        return rc;
  42}
  43
  44
  45void __sas_drain_work(struct sas_ha_struct *ha)
  46{
  47        struct sas_work *sw, *_sw;
  48        int ret;
  49
  50        set_bit(SAS_HA_DRAINING, &ha->state);
  51        /* flush submitters */
  52        spin_lock_irq(&ha->lock);
  53        spin_unlock_irq(&ha->lock);
  54
  55        drain_workqueue(ha->event_q);
  56        drain_workqueue(ha->disco_q);
  57
  58        spin_lock_irq(&ha->lock);
  59        clear_bit(SAS_HA_DRAINING, &ha->state);
  60        list_for_each_entry_safe(sw, _sw, &ha->defer_q, drain_node) {
  61                list_del_init(&sw->drain_node);
  62                ret = sas_queue_work(ha, sw);
  63                if (ret != 1)
  64                        sas_free_event(to_asd_sas_event(&sw->work));
  65
  66        }
  67        spin_unlock_irq(&ha->lock);
  68}
  69
  70int sas_drain_work(struct sas_ha_struct *ha)
  71{
  72        int err;
  73
  74        err = mutex_lock_interruptible(&ha->drain_mutex);
  75        if (err)
  76                return err;
  77        if (test_bit(SAS_HA_REGISTERED, &ha->state))
  78                __sas_drain_work(ha);
  79        mutex_unlock(&ha->drain_mutex);
  80
  81        return 0;
  82}
  83EXPORT_SYMBOL_GPL(sas_drain_work);
  84
  85void sas_disable_revalidation(struct sas_ha_struct *ha)
  86{
  87        mutex_lock(&ha->disco_mutex);
  88        set_bit(SAS_HA_ATA_EH_ACTIVE, &ha->state);
  89        mutex_unlock(&ha->disco_mutex);
  90}
  91
  92void sas_enable_revalidation(struct sas_ha_struct *ha)
  93{
  94        int i;
  95
  96        mutex_lock(&ha->disco_mutex);
  97        clear_bit(SAS_HA_ATA_EH_ACTIVE, &ha->state);
  98        for (i = 0; i < ha->num_phys; i++) {
  99                struct asd_sas_port *port = ha->sas_port[i];
 100                const int ev = DISCE_REVALIDATE_DOMAIN;
 101                struct sas_discovery *d = &port->disc;
 102                struct asd_sas_phy *sas_phy;
 103
 104                if (!test_and_clear_bit(ev, &d->pending))
 105                        continue;
 106
 107                if (list_empty(&port->phy_list))
 108                        continue;
 109
 110                sas_phy = container_of(port->phy_list.next, struct asd_sas_phy,
 111                                port_phy_el);
 112                sas_notify_port_event(sas_phy,
 113                                PORTE_BROADCAST_RCVD, GFP_KERNEL);
 114        }
 115        mutex_unlock(&ha->disco_mutex);
 116}
 117
 118
 119static void sas_port_event_worker(struct work_struct *work)
 120{
 121        struct asd_sas_event *ev = to_asd_sas_event(work);
 122
 123        sas_port_event_fns[ev->event](work);
 124        sas_free_event(ev);
 125}
 126
 127static void sas_phy_event_worker(struct work_struct *work)
 128{
 129        struct asd_sas_event *ev = to_asd_sas_event(work);
 130
 131        sas_phy_event_fns[ev->event](work);
 132        sas_free_event(ev);
 133}
 134
 135int sas_notify_port_event(struct asd_sas_phy *phy, enum port_event event,
 136                          gfp_t gfp_flags)
 137{
 138        struct sas_ha_struct *ha = phy->ha;
 139        struct asd_sas_event *ev;
 140        int ret;
 141
 142        BUG_ON(event >= PORT_NUM_EVENTS);
 143
 144        ev = sas_alloc_event(phy, gfp_flags);
 145        if (!ev)
 146                return -ENOMEM;
 147
 148        INIT_SAS_EVENT(ev, sas_port_event_worker, phy, event);
 149
 150        ret = sas_queue_event(event, &ev->work, ha);
 151        if (ret != 1)
 152                sas_free_event(ev);
 153
 154        return ret;
 155}
 156EXPORT_SYMBOL_GPL(sas_notify_port_event);
 157
 158int sas_notify_phy_event(struct asd_sas_phy *phy, enum phy_event event,
 159                         gfp_t gfp_flags)
 160{
 161        struct sas_ha_struct *ha = phy->ha;
 162        struct asd_sas_event *ev;
 163        int ret;
 164
 165        BUG_ON(event >= PHY_NUM_EVENTS);
 166
 167        ev = sas_alloc_event(phy, gfp_flags);
 168        if (!ev)
 169                return -ENOMEM;
 170
 171        INIT_SAS_EVENT(ev, sas_phy_event_worker, phy, event);
 172
 173        ret = sas_queue_event(event, &ev->work, ha);
 174        if (ret != 1)
 175                sas_free_event(ev);
 176
 177        return ret;
 178}
 179EXPORT_SYMBOL_GPL(sas_notify_phy_event);
 180