2006-08-29 18:22:51 +04:00
/*
* Serial Attached SCSI ( SAS ) Event processing
*
* Copyright ( C ) 2005 Adaptec , Inc . All rights reserved .
* Copyright ( C ) 2005 Luben Tuikov < luben_tuikov @ adaptec . com >
*
* This file is licensed under GPLv2 .
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation ; either version 2 of the
* License , or ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful , but
* WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 51 Franklin St , Fifth Floor , Boston , MA 02110 - 1301 USA
*
*/
2011-12-20 04:42:34 +04:00
# include <linux/export.h>
2006-08-29 18:22:51 +04:00
# include <scsi/scsi_host.h>
# include "sas_internal.h"
# include "sas_dump.h"
2012-03-09 23:00:06 +04:00
void sas_queue_work ( struct sas_ha_struct * ha , struct sas_work * sw )
2011-12-20 04:42:34 +04:00
{
if ( ! test_bit ( SAS_HA_REGISTERED , & ha - > state ) )
return ;
2012-03-09 23:00:06 +04:00
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 ( & sw - > drain_node , & ha - > defer_q ) ;
} else
scsi_queue_work ( ha - > core . shost , & sw - > work ) ;
2011-12-20 04:42:34 +04:00
}
static void sas_queue_event ( int event , unsigned long * pending ,
2012-03-09 23:00:06 +04:00
struct sas_work * work ,
2011-12-20 04:42:34 +04:00
struct sas_ha_struct * ha )
{
if ( ! test_and_set_bit ( event , pending ) ) {
unsigned long flags ;
2012-06-22 10:25:27 +04:00
spin_lock_irqsave ( & ha - > lock , flags ) ;
2011-12-20 04:42:34 +04:00
sas_queue_work ( ha , work ) ;
2012-06-22 10:25:27 +04:00
spin_unlock_irqrestore ( & ha - > lock , flags ) ;
2011-12-20 04:42:34 +04:00
}
}
2012-01-12 23:47:24 +04:00
void __sas_drain_work ( struct sas_ha_struct * ha )
2011-12-20 04:42:34 +04:00
{
struct workqueue_struct * wq = ha - > core . shost - > work_q ;
2012-03-09 23:00:06 +04:00
struct sas_work * sw , * _sw ;
2011-12-20 04:42:34 +04:00
set_bit ( SAS_HA_DRAINING , & ha - > state ) ;
/* flush submitters */
2012-06-22 10:25:27 +04:00
spin_lock_irq ( & ha - > lock ) ;
spin_unlock_irq ( & ha - > lock ) ;
2011-12-20 04:42:34 +04:00
drain_workqueue ( wq ) ;
2012-06-22 10:25:27 +04:00
spin_lock_irq ( & ha - > lock ) ;
2011-12-20 04:42:34 +04:00
clear_bit ( SAS_HA_DRAINING , & ha - > state ) ;
2012-03-09 23:00:06 +04:00
list_for_each_entry_safe ( sw , _sw , & ha - > defer_q , drain_node ) {
list_del_init ( & sw - > drain_node ) ;
sas_queue_work ( ha , sw ) ;
2011-12-20 04:42:34 +04:00
}
2012-06-22 10:25:27 +04:00
spin_unlock_irq ( & ha - > lock ) ;
2012-01-12 23:47:24 +04:00
}
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 ) ;
2011-12-20 04:42:34 +04:00
mutex_unlock ( & ha - > drain_mutex ) ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( sas_drain_work ) ;
2011-11-18 05:59:51 +04:00
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 ;
if ( ! test_and_clear_bit ( ev , & d - > pending ) )
continue ;
sas_queue_event ( ev , & d - > pending , & d - > disc_work [ ev ] . work , ha ) ;
}
mutex_unlock ( & ha - > disco_mutex ) ;
}
2006-08-29 18:22:51 +04:00
static void notify_ha_event ( struct sas_ha_struct * sas_ha , enum ha_event event )
{
BUG_ON ( event > = HA_NUM_EVENTS ) ;
2011-11-18 05:59:49 +04:00
sas_queue_event ( event , & sas_ha - > pending ,
2007-01-12 01:15:43 +03:00
& sas_ha - > ha_events [ event ] . work , sas_ha ) ;
2006-08-29 18:22:51 +04:00
}
static void notify_port_event ( struct asd_sas_phy * phy , enum port_event event )
{
struct sas_ha_struct * ha = phy - > ha ;
BUG_ON ( event > = PORT_NUM_EVENTS ) ;
2011-11-18 05:59:49 +04:00
sas_queue_event ( event , & phy - > port_events_pending ,
2007-01-12 01:15:43 +03:00
& phy - > port_events [ event ] . work , ha ) ;
2006-08-29 18:22:51 +04:00
}
2012-06-22 10:41:51 +04:00
void sas_notify_phy_event ( struct asd_sas_phy * phy , enum phy_event event )
2006-08-29 18:22:51 +04:00
{
struct sas_ha_struct * ha = phy - > ha ;
BUG_ON ( event > = PHY_NUM_EVENTS ) ;
2011-11-18 05:59:49 +04:00
sas_queue_event ( event , & phy - > phy_events_pending ,
2007-01-12 01:15:43 +03:00
& phy - > phy_events [ event ] . work , ha ) ;
2006-08-29 18:22:51 +04:00
}
int sas_init_events ( struct sas_ha_struct * sas_ha )
{
2006-11-22 17:57:56 +03:00
static const work_func_t sas_ha_event_fns [ HA_NUM_EVENTS ] = {
2006-08-29 18:22:51 +04:00
[ HAE_RESET ] = sas_hae_reset ,
} ;
int i ;
2006-11-22 17:57:56 +03:00
for ( i = 0 ; i < HA_NUM_EVENTS ; i + + ) {
2012-03-09 23:00:06 +04:00
INIT_SAS_WORK ( & sas_ha - > ha_events [ i ] . work , sas_ha_event_fns [ i ] ) ;
2006-11-22 17:57:56 +03:00
sas_ha - > ha_events [ i ] . ha = sas_ha ;
}
2006-08-29 18:22:51 +04:00
sas_ha - > notify_ha_event = notify_ha_event ;
sas_ha - > notify_port_event = notify_port_event ;
2012-06-22 10:41:51 +04:00
sas_ha - > notify_phy_event = sas_notify_phy_event ;
2006-08-29 18:22:51 +04:00
return 0 ;
}