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 29 int sas_queue_work(struct sas_ha_struct *ha, struct sas_work *sw) 30 { 31 /* it's added to the defer_q when draining so return succeed */ 32 int rc = 1; 33 34 if (!test_bit(SAS_HA_REGISTERED, &ha->state)) 35 return 0; 36 37 if (test_bit(SAS_HA_DRAINING, &ha->state)) { 38 /* add it to the defer list, if not already pending */ 39 if (list_empty(&sw->drain_node)) 40 list_add_tail(&sw->drain_node, &ha->defer_q); 41 } else 42 rc = queue_work(ha->event_q, &sw->work); 43 44 return rc; 45 } 46 47 static int sas_queue_event(int event, struct sas_work *work, 48 struct sas_ha_struct *ha) 49 { 50 unsigned long flags; 51 int rc; 52 53 spin_lock_irqsave(&ha->lock, flags); 54 rc = sas_queue_work(ha, work); 55 spin_unlock_irqrestore(&ha->lock, flags); 56 57 return rc; 58 } 59 60 61 void __sas_drain_work(struct sas_ha_struct *ha) 62 { 63 struct sas_work *sw, *_sw; 64 int ret; 65 66 set_bit(SAS_HA_DRAINING, &ha->state); 67 /* flush submitters */ 68 spin_lock_irq(&ha->lock); 69 spin_unlock_irq(&ha->lock); 70 71 drain_workqueue(ha->event_q); 72 drain_workqueue(ha->disco_q); 73 74 spin_lock_irq(&ha->lock); 75 clear_bit(SAS_HA_DRAINING, &ha->state); 76 list_for_each_entry_safe(sw, _sw, &ha->defer_q, drain_node) { 77 list_del_init(&sw->drain_node); 78 ret = sas_queue_work(ha, sw); 79 if (ret != 1) 80 sas_free_event(to_asd_sas_event(&sw->work)); 81 82 } 83 spin_unlock_irq(&ha->lock); 84 } 85 86 int sas_drain_work(struct sas_ha_struct *ha) 87 { 88 int err; 89 90 err = mutex_lock_interruptible(&ha->drain_mutex); 91 if (err) 92 return err; 93 if (test_bit(SAS_HA_REGISTERED, &ha->state)) 94 __sas_drain_work(ha); 95 mutex_unlock(&ha->drain_mutex); 96 97 return 0; 98 } 99 EXPORT_SYMBOL_GPL(sas_drain_work); 100 101 void sas_disable_revalidation(struct sas_ha_struct *ha) 102 { 103 mutex_lock(&ha->disco_mutex); 104 set_bit(SAS_HA_ATA_EH_ACTIVE, &ha->state); 105 mutex_unlock(&ha->disco_mutex); 106 } 107 108 void sas_enable_revalidation(struct sas_ha_struct *ha) 109 { 110 int i; 111 112 mutex_lock(&ha->disco_mutex); 113 clear_bit(SAS_HA_ATA_EH_ACTIVE, &ha->state); 114 for (i = 0; i < ha->num_phys; i++) { 115 struct asd_sas_port *port = ha->sas_port[i]; 116 const int ev = DISCE_REVALIDATE_DOMAIN; 117 struct sas_discovery *d = &port->disc; 118 struct asd_sas_phy *sas_phy; 119 120 if (!test_and_clear_bit(ev, &d->pending)) 121 continue; 122 123 if (list_empty(&port->phy_list)) 124 continue; 125 126 sas_phy = container_of(port->phy_list.next, struct asd_sas_phy, 127 port_phy_el); 128 ha->notify_port_event(sas_phy, PORTE_BROADCAST_RCVD); 129 } 130 mutex_unlock(&ha->disco_mutex); 131 } 132 133 134 static void sas_port_event_worker(struct work_struct *work) 135 { 136 struct asd_sas_event *ev = to_asd_sas_event(work); 137 138 sas_port_event_fns[ev->event](work); 139 sas_free_event(ev); 140 } 141 142 static void sas_phy_event_worker(struct work_struct *work) 143 { 144 struct asd_sas_event *ev = to_asd_sas_event(work); 145 146 sas_phy_event_fns[ev->event](work); 147 sas_free_event(ev); 148 } 149 150 static int sas_notify_port_event(struct asd_sas_phy *phy, enum port_event event) 151 { 152 struct asd_sas_event *ev; 153 struct sas_ha_struct *ha = phy->ha; 154 int ret; 155 156 BUG_ON(event >= PORT_NUM_EVENTS); 157 158 ev = sas_alloc_event(phy); 159 if (!ev) 160 return -ENOMEM; 161 162 INIT_SAS_EVENT(ev, sas_port_event_worker, phy, event); 163 164 ret = sas_queue_event(event, &ev->work, ha); 165 if (ret != 1) 166 sas_free_event(ev); 167 168 return ret; 169 } 170 171 int sas_notify_phy_event(struct asd_sas_phy *phy, enum phy_event event) 172 { 173 struct asd_sas_event *ev; 174 struct sas_ha_struct *ha = phy->ha; 175 int ret; 176 177 BUG_ON(event >= PHY_NUM_EVENTS); 178 179 ev = sas_alloc_event(phy); 180 if (!ev) 181 return -ENOMEM; 182 183 INIT_SAS_EVENT(ev, sas_phy_event_worker, phy, event); 184 185 ret = sas_queue_event(event, &ev->work, ha); 186 if (ret != 1) 187 sas_free_event(ev); 188 189 return ret; 190 } 191 192 int sas_init_events(struct sas_ha_struct *sas_ha) 193 { 194 sas_ha->notify_port_event = sas_notify_port_event; 195 sas_ha->notify_phy_event = sas_notify_phy_event; 196 197 return 0; 198 } 199