2006-04-02 13:51:53 +04:00
/*
* libata - eh . c - libata error handling
*
* Maintained by : Jeff Garzik < jgarzik @ pobox . com >
* Please ALWAYS copy linux - ide @ vger . kernel . org
* on emails .
*
* Copyright 2006 Tejun Heo < htejun @ gmail . com >
*
*
* 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 , 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 ; see the file COPYING . If not , write to
* the Free Software Foundation , 675 Mass Ave , Cambridge , MA 0213 9 ,
* USA .
*
*
* libata documentation is available via ' make { ps | pdf } docs ' ,
* as Documentation / DocBook / libata . *
*
* Hardware documentation available from http : //www.t13.org/ and
* http : //www.sata-io.org/
*
*/
# include <linux/config.h>
# include <linux/kernel.h>
# include <scsi/scsi.h>
# include <scsi/scsi_host.h>
# include <scsi/scsi_eh.h>
# include <scsi/scsi_device.h>
# include <scsi/scsi_cmnd.h>
# include <linux/libata.h>
# include "libata.h"
/**
* ata_scsi_timed_out - SCSI layer time out callback
* @ cmd : timed out SCSI command
*
* Handles SCSI layer timeout . We race with normal completion of
* the qc for @ cmd . If the qc is already gone , we lose and let
* the scsi command finish ( EH_HANDLED ) . Otherwise , the qc has
* timed out and EH should be invoked . Prevent ata_qc_complete ( )
* from finishing it by setting EH_SCHEDULED and return
* EH_NOT_HANDLED .
*
* LOCKING :
* Called from timer context
*
* RETURNS :
* EH_HANDLED or EH_NOT_HANDLED
*/
enum scsi_eh_timer_return ata_scsi_timed_out ( struct scsi_cmnd * cmd )
{
struct Scsi_Host * host = cmd - > device - > host ;
2006-04-11 21:12:34 +04:00
struct ata_port * ap = ata_shost_to_port ( host ) ;
2006-04-02 13:51:53 +04:00
unsigned long flags ;
struct ata_queued_cmd * qc ;
enum scsi_eh_timer_return ret = EH_HANDLED ;
DPRINTK ( " ENTER \n " ) ;
spin_lock_irqsave ( & ap - > host_set - > lock , flags ) ;
qc = ata_qc_from_tag ( ap , ap - > active_tag ) ;
if ( qc ) {
WARN_ON ( qc - > scsicmd ! = cmd ) ;
qc - > flags | = ATA_QCFLAG_EH_SCHEDULED ;
qc - > err_mask | = AC_ERR_TIMEOUT ;
ret = EH_NOT_HANDLED ;
}
spin_unlock_irqrestore ( & ap - > host_set - > lock , flags ) ;
DPRINTK ( " EXIT, ret=%d \n " , ret ) ;
return ret ;
}
/**
* ata_scsi_error - SCSI layer error handler callback
* @ host : SCSI host on which error occurred
*
* Handles SCSI - layer - thrown error events .
*
* LOCKING :
* Inherited from SCSI layer ( none , can sleep )
*
* RETURNS :
* Zero .
*/
2006-04-11 21:04:39 +04:00
void ata_scsi_error ( struct Scsi_Host * host )
2006-04-02 13:51:53 +04:00
{
2006-04-11 21:12:34 +04:00
struct ata_port * ap = ata_shost_to_port ( host ) ;
2006-04-02 13:51:53 +04:00
DPRINTK ( " ENTER \n " ) ;
/* synchronize with IRQ handler and port task */
spin_unlock_wait ( & ap - > host_set - > lock ) ;
ata_port_flush_task ( ap ) ;
WARN_ON ( ata_qc_from_tag ( ap , ap - > active_tag ) = = NULL ) ;
ap - > ops - > eng_timeout ( ap ) ;
WARN_ON ( host - > host_failed | | ! list_empty ( & host - > eh_cmd_q ) ) ;
scsi_eh_flush_done_q ( & ap - > eh_done_q ) ;
DPRINTK ( " EXIT \n " ) ;
}
/**
* ata_qc_timeout - Handle timeout of queued command
* @ qc : Command that timed out
*
* Some part of the kernel ( currently , only the SCSI layer )
* has noticed that the active command on port @ ap has not
* completed after a specified length of time . Handle this
* condition by disabling DMA ( if necessary ) and completing
* transactions , with error if necessary .
*
* This also handles the case of the " lost interrupt " , where
* for some reason ( possibly hardware bug , possibly driver bug )
* an interrupt was not delivered to the driver , even though the
* transaction completed successfully .
*
* LOCKING :
* Inherited from SCSI layer ( none , can sleep )
*/
static void ata_qc_timeout ( struct ata_queued_cmd * qc )
{
struct ata_port * ap = qc - > ap ;
struct ata_host_set * host_set = ap - > host_set ;
u8 host_stat = 0 , drv_stat ;
unsigned long flags ;
DPRINTK ( " ENTER \n " ) ;
ap - > hsm_task_state = HSM_ST_IDLE ;
spin_lock_irqsave ( & host_set - > lock , flags ) ;
switch ( qc - > tf . protocol ) {
case ATA_PROT_DMA :
case ATA_PROT_ATAPI_DMA :
host_stat = ap - > ops - > bmdma_status ( ap ) ;
/* before we do anything else, clear DMA-Start bit */
ap - > ops - > bmdma_stop ( qc ) ;
/* fall through */
default :
ata_altstatus ( ap ) ;
drv_stat = ata_chk_status ( ap ) ;
/* ack bmdma irq events */
ap - > ops - > irq_clear ( ap ) ;
2006-05-15 15:57:56 +04:00
ata_dev_printk ( qc - > dev , KERN_ERR , " command 0x%x timeout, "
" stat 0x%x host_stat 0x%x \n " ,
qc - > tf . command , drv_stat , host_stat ) ;
2006-04-02 13:51:53 +04:00
/* complete taskfile transaction */
qc - > err_mask | = ac_err_mask ( drv_stat ) ;
break ;
}
spin_unlock_irqrestore ( & host_set - > lock , flags ) ;
ata_eh_qc_complete ( qc ) ;
DPRINTK ( " EXIT \n " ) ;
}
/**
* ata_eng_timeout - Handle timeout of queued command
* @ ap : Port on which timed - out command is active
*
* Some part of the kernel ( currently , only the SCSI layer )
* has noticed that the active command on port @ ap has not
* completed after a specified length of time . Handle this
* condition by disabling DMA ( if necessary ) and completing
* transactions , with error if necessary .
*
* This also handles the case of the " lost interrupt " , where
* for some reason ( possibly hardware bug , possibly driver bug )
* an interrupt was not delivered to the driver , even though the
* transaction completed successfully .
*
* LOCKING :
* Inherited from SCSI layer ( none , can sleep )
*/
void ata_eng_timeout ( struct ata_port * ap )
{
DPRINTK ( " ENTER \n " ) ;
ata_qc_timeout ( ata_qc_from_tag ( ap , ap - > active_tag ) ) ;
DPRINTK ( " EXIT \n " ) ;
}
2006-05-15 15:58:05 +04:00
/**
* ata_qc_schedule_eh - schedule qc for error handling
* @ qc : command to schedule error handling for
*
* Schedule error handling for @ qc . EH will kick in as soon as
* other commands are drained .
*
* LOCKING :
* spin_lock_irqsave ( host_set lock )
*/
void ata_qc_schedule_eh ( struct ata_queued_cmd * qc )
{
struct ata_port * ap = qc - > ap ;
WARN_ON ( ! ap - > ops - > error_handler ) ;
qc - > flags | = ATA_QCFLAG_FAILED ;
qc - > ap - > flags | = ATA_FLAG_EH_PENDING ;
/* The following will fail if timeout has already expired.
* ata_scsi_error ( ) takes care of such scmds on EH entry .
* Note that ATA_QCFLAG_FAILED is unconditionally set after
* this function completes .
*/
scsi_req_abort_cmd ( qc - > scsicmd ) ;
}
2006-05-15 15:58:07 +04:00
/**
* ata_port_schedule_eh - schedule error handling without a qc
* @ ap : ATA port to schedule EH for
*
* Schedule error handling for @ ap . EH will kick in as soon as
* all commands are drained .
*
* LOCKING :
* spin_lock_irqsave ( host_set lock )
*/
void ata_port_schedule_eh ( struct ata_port * ap )
{
WARN_ON ( ! ap - > ops - > error_handler ) ;
ap - > flags | = ATA_FLAG_EH_PENDING ;
ata_schedule_scsi_eh ( ap - > host ) ;
DPRINTK ( " port EH scheduled \n " ) ;
}
/**
* ata_port_abort - abort all qc ' s on the port
* @ ap : ATA port to abort qc ' s for
*
* Abort all active qc ' s of @ ap and schedule EH .
*
* LOCKING :
* spin_lock_irqsave ( host_set lock )
*
* RETURNS :
* Number of aborted qc ' s .
*/
int ata_port_abort ( struct ata_port * ap )
{
int tag , nr_aborted = 0 ;
WARN_ON ( ! ap - > ops - > error_handler ) ;
for ( tag = 0 ; tag < ATA_MAX_QUEUE ; tag + + ) {
struct ata_queued_cmd * qc = ata_qc_from_tag ( ap , tag ) ;
if ( qc ) {
qc - > flags | = ATA_QCFLAG_FAILED ;
ata_qc_complete ( qc ) ;
nr_aborted + + ;
}
}
if ( ! nr_aborted )
ata_port_schedule_eh ( ap ) ;
return nr_aborted ;
}
2006-04-02 13:51:53 +04:00
static void ata_eh_scsidone ( struct scsi_cmnd * scmd )
{
/* nada */
}
static void __ata_eh_qc_complete ( struct ata_queued_cmd * qc )
{
struct ata_port * ap = qc - > ap ;
struct scsi_cmnd * scmd = qc - > scsicmd ;
unsigned long flags ;
spin_lock_irqsave ( & ap - > host_set - > lock , flags ) ;
qc - > scsidone = ata_eh_scsidone ;
__ata_qc_complete ( qc ) ;
WARN_ON ( ata_tag_valid ( qc - > tag ) ) ;
spin_unlock_irqrestore ( & ap - > host_set - > lock , flags ) ;
scsi_eh_finish_cmd ( scmd , & ap - > eh_done_q ) ;
}
/**
* ata_eh_qc_complete - Complete an active ATA command from EH
* @ qc : Command to complete
*
* Indicate to the mid and upper layers that an ATA command has
* completed . To be used from EH .
*/
void ata_eh_qc_complete ( struct ata_queued_cmd * qc )
{
struct scsi_cmnd * scmd = qc - > scsicmd ;
scmd - > retries = scmd - > allowed ;
__ata_eh_qc_complete ( qc ) ;
}
/**
* ata_eh_qc_retry - Tell midlayer to retry an ATA command after EH
* @ qc : Command to retry
*
* Indicate to the mid and upper layers that an ATA command
* should be retried . To be used from EH .
*
* SCSI midlayer limits the number of retries to scmd - > allowed .
* scmd - > retries is decremented for commands which get retried
* due to unrelated failures ( qc - > err_mask is zero ) .
*/
void ata_eh_qc_retry ( struct ata_queued_cmd * qc )
{
struct scsi_cmnd * scmd = qc - > scsicmd ;
if ( ! qc - > err_mask & & scmd - > retries )
scmd - > retries - - ;
__ata_eh_qc_complete ( qc ) ;
}