@ -17,6 +17,10 @@
static int hisi_sas_debug_issue_ssp_tmf ( struct domain_device * device ,
static int hisi_sas_debug_issue_ssp_tmf ( struct domain_device * device ,
u8 * lun , struct hisi_sas_tmf_task * tmf ) ;
u8 * lun , struct hisi_sas_tmf_task * tmf ) ;
static int
hisi_sas_internal_task_abort ( struct hisi_hba * hisi_hba ,
struct domain_device * device ,
int abort_flag , int tag ) ;
static struct hisi_hba * dev_to_hisi_hba ( struct domain_device * device )
static struct hisi_hba * dev_to_hisi_hba ( struct domain_device * device )
{
{
@ -116,6 +120,14 @@ static int hisi_sas_task_prep_ata(struct hisi_hba *hisi_hba,
return hisi_hba - > hw - > prep_stp ( hisi_hba , slot ) ;
return hisi_hba - > hw - > prep_stp ( hisi_hba , slot ) ;
}
}
static int hisi_sas_task_prep_abort ( struct hisi_hba * hisi_hba ,
struct hisi_sas_slot * slot ,
int device_id , int abort_flag , int tag_to_abort )
{
return hisi_hba - > hw - > prep_abort ( hisi_hba , slot ,
device_id , abort_flag , tag_to_abort ) ;
}
/*
/*
* This function will issue an abort TMF regardless of whether the
* This function will issue an abort TMF regardless of whether the
* task is in the sdev or not . Then it will do the task complete
* task is in the sdev or not . Then it will do the task complete
@ -954,6 +966,157 @@ static int hisi_sas_query_task(struct sas_task *task)
return rc ;
return rc ;
}
}
static int
hisi_sas_internal_abort_task_exec ( struct hisi_hba * hisi_hba , u64 device_id ,
struct sas_task * task , int abort_flag ,
int task_tag )
{
struct domain_device * device = task - > dev ;
struct hisi_sas_device * sas_dev = device - > lldd_dev ;
struct device * dev = & hisi_hba - > pdev - > dev ;
struct hisi_sas_port * port ;
struct hisi_sas_slot * slot ;
struct hisi_sas_cmd_hdr * cmd_hdr_base ;
int dlvry_queue_slot , dlvry_queue , n_elem = 0 , rc , slot_idx ;
if ( ! device - > port )
return - 1 ;
port = device - > port - > lldd_port ;
/* simply get a slot and send abort command */
rc = hisi_sas_slot_index_alloc ( hisi_hba , & slot_idx ) ;
if ( rc )
goto err_out ;
rc = hisi_hba - > hw - > get_free_slot ( hisi_hba , & dlvry_queue ,
& dlvry_queue_slot ) ;
if ( rc )
goto err_out_tag ;
slot = & hisi_hba - > slot_info [ slot_idx ] ;
memset ( slot , 0 , sizeof ( struct hisi_sas_slot ) ) ;
slot - > idx = slot_idx ;
slot - > n_elem = n_elem ;
slot - > dlvry_queue = dlvry_queue ;
slot - > dlvry_queue_slot = dlvry_queue_slot ;
cmd_hdr_base = hisi_hba - > cmd_hdr [ dlvry_queue ] ;
slot - > cmd_hdr = & cmd_hdr_base [ dlvry_queue_slot ] ;
slot - > task = task ;
slot - > port = port ;
task - > lldd_task = slot ;
memset ( slot - > cmd_hdr , 0 , sizeof ( struct hisi_sas_cmd_hdr ) ) ;
rc = hisi_sas_task_prep_abort ( hisi_hba , slot , device_id ,
abort_flag , task_tag ) ;
if ( rc )
goto err_out_tag ;
/* Port structure is static for the HBA, so
* even if the port is deformed it is ok
* to reference .
*/
list_add_tail ( & slot - > entry , & port - > list ) ;
spin_lock ( & task - > task_state_lock ) ;
task - > task_state_flags | = SAS_TASK_AT_INITIATOR ;
spin_unlock ( & task - > task_state_lock ) ;
hisi_hba - > slot_prep = slot ;
sas_dev - > running_req + + ;
/* send abort command to our chip */
hisi_hba - > hw - > start_delivery ( hisi_hba ) ;
return 0 ;
err_out_tag :
hisi_sas_slot_index_free ( hisi_hba , slot_idx ) ;
err_out :
dev_err ( dev , " internal abort task prep: failed[%d]! \n " , rc ) ;
return rc ;
}
/**
* hisi_sas_internal_task_abort - - execute an internal
* abort command for single IO command or a device
* @ hisi_hba : host controller struct
* @ device : domain device
* @ abort_flag : mode of operation , device or single IO
* @ tag : tag of IO to be aborted ( only relevant to single
* IO mode )
*/
static int
hisi_sas_internal_task_abort ( struct hisi_hba * hisi_hba ,
struct domain_device * device ,
int abort_flag , int tag )
{
struct sas_task * task ;
struct hisi_sas_device * sas_dev = device - > lldd_dev ;
struct device * dev = & hisi_hba - > pdev - > dev ;
int res ;
unsigned long flags ;
if ( ! hisi_hba - > hw - > prep_abort )
return - EOPNOTSUPP ;
task = sas_alloc_slow_task ( GFP_KERNEL ) ;
if ( ! task )
return - ENOMEM ;
task - > dev = device ;
task - > task_proto = device - > tproto ;
task - > task_done = hisi_sas_task_done ;
task - > slow_task - > timer . data = ( unsigned long ) task ;
task - > slow_task - > timer . function = hisi_sas_tmf_timedout ;
task - > slow_task - > timer . expires = jiffies + 20 * HZ ;
add_timer ( & task - > slow_task - > timer ) ;
/* Lock as we are alloc'ing a slot, which cannot be interrupted */
spin_lock_irqsave ( & hisi_hba - > lock , flags ) ;
res = hisi_sas_internal_abort_task_exec ( hisi_hba , sas_dev - > device_id ,
task , abort_flag , tag ) ;
spin_unlock_irqrestore ( & hisi_hba - > lock , flags ) ;
if ( res ) {
del_timer ( & task - > slow_task - > timer ) ;
dev_err ( dev , " internal task abort: executing internal task failed: %d \n " ,
res ) ;
goto exit ;
}
wait_for_completion ( & task - > slow_task - > completion ) ;
res = TMF_RESP_FUNC_FAILED ;
if ( task - > task_status . resp = = SAS_TASK_COMPLETE & &
task - > task_status . stat = = TMF_RESP_FUNC_COMPLETE ) {
res = TMF_RESP_FUNC_COMPLETE ;
goto exit ;
}
/* TMF timed out, return direct. */
if ( ( task - > task_state_flags & SAS_TASK_STATE_ABORTED ) ) {
if ( ! ( task - > task_state_flags & SAS_TASK_STATE_DONE ) ) {
dev_err ( dev , " internal task abort: timeout. \n " ) ;
if ( task - > lldd_task ) {
struct hisi_sas_slot * slot = task - > lldd_task ;
hisi_sas_slot_task_free ( hisi_hba , task , slot ) ;
}
}
}
exit :
dev_info ( dev , " internal task abort: task to dev %016llx task=%p "
" resp: 0x%x sts 0x%x \n " ,
SAS_ADDR ( device - > sas_addr ) ,
task ,
task - > task_status . resp , /* 0 is complete, -1 is undelivered */
task - > task_status . stat ) ;
sas_free_task ( task ) ;
return res ;
}
static void hisi_sas_port_formed ( struct asd_sas_phy * sas_phy )
static void hisi_sas_port_formed ( struct asd_sas_phy * sas_phy )
{
{
hisi_sas_port_notify_formed ( sas_phy ) ;
hisi_sas_port_notify_formed ( sas_phy ) ;