1 /* 2 * Serial Attached SCSI (SAS) Event processing 3 * 4 * Copyright (C) 2005 Adaptec, Inc. All rights reserved. 5 * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com> 6 * 7 * This file is licensed under GPLv2. 8 * 9 * This program is free software; you can redistribute it and/or 10 * modify it under the terms of the GNU General Public License as 11 * published by the Free Software Foundation; either version 2 of the 12 * License, or (at your option) any later version. 13 * 14 * This program is distributed in the hope that it will be useful, but 15 * WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 * General Public License for more details. 18 * 19 * You should have received a copy of the GNU General Public License 20 * along with this program; if not, write to the Free Software 21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 22 * 23 */ 24 25 #include <linux/export.h> 26 #include <scsi/scsi_host.h> 27 #include "sas_internal.h" 28 #include "sas_dump.h" 29 30 void sas_queue_work(struct sas_ha_struct *ha, struct work_struct *work) 31 { 32 if (!test_bit(SAS_HA_REGISTERED, &ha->state)) 33 return; 34 35 if (test_bit(SAS_HA_DRAINING, &ha->state)) 36 list_add(&work->entry, &ha->defer_q); 37 else 38 scsi_queue_work(ha->core.shost, work); 39 } 40 41 static void sas_queue_event(int event, unsigned long *pending, 42 struct work_struct *work, 43 struct sas_ha_struct *ha) 44 { 45 if (!test_and_set_bit(event, pending)) { 46 unsigned long flags; 47 48 spin_lock_irqsave(&ha->state_lock, flags); 49 sas_queue_work(ha, work); 50 spin_unlock_irqrestore(&ha->state_lock, flags); 51 } 52 } 53 54 55 void __sas_drain_work(struct sas_ha_struct *ha) 56 { 57 struct workqueue_struct *wq = ha->core.shost->work_q; 58 struct work_struct *w, *_w; 59 60 set_bit(SAS_HA_DRAINING, &ha->state); 61 /* flush submitters */ 62 spin_lock_irq(&ha->state_lock); 63 spin_unlock_irq(&ha->state_lock); 64 65 drain_workqueue(wq); 66 67 spin_lock_irq(&ha->state_lock); 68 clear_bit(SAS_HA_DRAINING, &ha->state); 69 list_for_each_entry_safe(w, _w, &ha->defer_q, entry) { 70 list_del_init(&w->entry); 71 sas_queue_work(ha, w); 72 } 73 spin_unlock_irq(&ha->state_lock); 74 } 75 76 int sas_drain_work(struct sas_ha_struct *ha) 77 { 78 int err; 79 80 err = mutex_lock_interruptible(&ha->drain_mutex); 81 if (err) 82 return err; 83 if (test_bit(SAS_HA_REGISTERED, &ha->state)) 84 __sas_drain_work(ha); 85 mutex_unlock(&ha->drain_mutex); 86 87 return 0; 88 } 89 EXPORT_SYMBOL_GPL(sas_drain_work); 90 91 void sas_disable_revalidation(struct sas_ha_struct *ha) 92 { 93 mutex_lock(&ha->disco_mutex); 94 set_bit(SAS_HA_ATA_EH_ACTIVE, &ha->state); 95 mutex_unlock(&ha->disco_mutex); 96 } 97 98 void sas_enable_revalidation(struct sas_ha_struct *ha) 99 { 100 int i; 101 102 mutex_lock(&ha->disco_mutex); 103 clear_bit(SAS_HA_ATA_EH_ACTIVE, &ha->state); 104 for (i = 0; i < ha->num_phys; i++) { 105 struct asd_sas_port *port = ha->sas_port[i]; 106 const int ev = DISCE_REVALIDATE_DOMAIN; 107 struct sas_discovery *d = &port->disc; 108 109 if (!test_and_clear_bit(ev, &d->pending)) 110 continue; 111 112 sas_queue_event(ev, &d->pending, &d->disc_work[ev].work, ha); 113 } 114 mutex_unlock(&ha->disco_mutex); 115 } 116 117 static void notify_ha_event(struct sas_ha_struct *sas_ha, enum ha_event event) 118 { 119 BUG_ON(event >= HA_NUM_EVENTS); 120 121 sas_queue_event(event, &sas_ha->pending, 122 &sas_ha->ha_events[event].work, sas_ha); 123 } 124 125 static void notify_port_event(struct asd_sas_phy *phy, enum port_event event) 126 { 127 struct sas_ha_struct *ha = phy->ha; 128 129 BUG_ON(event >= PORT_NUM_EVENTS); 130 131 sas_queue_event(event, &phy->port_events_pending, 132 &phy->port_events[event].work, ha); 133 } 134 135 static void notify_phy_event(struct asd_sas_phy *phy, enum phy_event event) 136 { 137 struct sas_ha_struct *ha = phy->ha; 138 139 BUG_ON(event >= PHY_NUM_EVENTS); 140 141 sas_queue_event(event, &phy->phy_events_pending, 142 &phy->phy_events[event].work, ha); 143 } 144 145 int sas_init_events(struct sas_ha_struct *sas_ha) 146 { 147 static const work_func_t sas_ha_event_fns[HA_NUM_EVENTS] = { 148 [HAE_RESET] = sas_hae_reset, 149 }; 150 151 int i; 152 153 for (i = 0; i < HA_NUM_EVENTS; i++) { 154 INIT_WORK(&sas_ha->ha_events[i].work, sas_ha_event_fns[i]); 155 sas_ha->ha_events[i].ha = sas_ha; 156 } 157 158 sas_ha->notify_ha_event = notify_ha_event; 159 sas_ha->notify_port_event = notify_port_event; 160 sas_ha->notify_phy_event = notify_phy_event; 161 162 return 0; 163 } 164