2019-05-27 09:55:01 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2015-11-17 19:50:30 +03:00
/*
* Copyright ( c ) 2015 Linaro Ltd .
* Copyright ( c ) 2015 Hisilicon Limited .
*/
# include "hisi_sas.h"
# define DRV_NAME "hisi_sas"
2015-11-17 19:50:49 +03:00
# define DEV_IS_GONE(dev) \
( ( ! dev ) | | ( dev - > dev_type = = SAS_PHY_UNUSED ) )
2017-03-22 20:25:20 +03:00
static int hisi_sas_softreset_ata_disk ( struct domain_device * device ) ;
2017-12-08 20:16:45 +03:00
static int hisi_sas_control_phy ( struct asd_sas_phy * sas_phy , enum phy_func func ,
void * funcdata ) ;
2018-05-21 13:09:17 +03:00
static void hisi_sas_release_task ( struct hisi_hba * hisi_hba ,
struct domain_device * device ) ;
static void hisi_sas_dev_gone ( struct domain_device * device ) ;
2016-02-25 12:42:11 +03:00
2022-03-11 15:23:52 +03:00
struct hisi_sas_internal_abort_data {
bool rst_ha_timeout ; /* reset the HA for timeout */
} ;
2017-12-28 13:20:47 +03:00
u8 hisi_sas_get_ata_protocol ( struct host_to_dev_fis * fis , int direction )
2017-06-14 18:33:14 +03:00
{
2017-12-28 13:20:47 +03:00
switch ( fis - > command ) {
2017-06-14 18:33:14 +03:00
case ATA_CMD_FPDMA_WRITE :
case ATA_CMD_FPDMA_READ :
case ATA_CMD_FPDMA_RECV :
case ATA_CMD_FPDMA_SEND :
case ATA_CMD_NCQ_NON_DATA :
2018-03-07 15:25:12 +03:00
return HISI_SAS_SATA_PROTOCOL_FPDMA ;
2017-06-14 18:33:14 +03:00
case ATA_CMD_DOWNLOAD_MICRO :
case ATA_CMD_ID_ATA :
case ATA_CMD_PMP_READ :
case ATA_CMD_READ_LOG_EXT :
case ATA_CMD_PIO_READ :
case ATA_CMD_PIO_READ_EXT :
case ATA_CMD_PMP_WRITE :
case ATA_CMD_WRITE_LOG_EXT :
case ATA_CMD_PIO_WRITE :
case ATA_CMD_PIO_WRITE_EXT :
2018-03-07 15:25:12 +03:00
return HISI_SAS_SATA_PROTOCOL_PIO ;
2017-06-14 18:33:14 +03:00
case ATA_CMD_DSM :
case ATA_CMD_DOWNLOAD_MICRO_DMA :
case ATA_CMD_PMP_READ_DMA :
case ATA_CMD_PMP_WRITE_DMA :
case ATA_CMD_READ :
case ATA_CMD_READ_EXT :
case ATA_CMD_READ_LOG_DMA_EXT :
case ATA_CMD_READ_STREAM_DMA_EXT :
case ATA_CMD_TRUSTED_RCV_DMA :
case ATA_CMD_TRUSTED_SND_DMA :
case ATA_CMD_WRITE :
case ATA_CMD_WRITE_EXT :
case ATA_CMD_WRITE_FUA_EXT :
case ATA_CMD_WRITE_QUEUED :
case ATA_CMD_WRITE_LOG_DMA_EXT :
case ATA_CMD_WRITE_STREAM_DMA_EXT :
2017-08-10 19:09:34 +03:00
case ATA_CMD_ZAC_MGMT_IN :
2018-03-07 15:25:12 +03:00
return HISI_SAS_SATA_PROTOCOL_DMA ;
2017-06-14 18:33:14 +03:00
case ATA_CMD_CHK_POWER :
case ATA_CMD_DEV_RESET :
case ATA_CMD_EDD :
case ATA_CMD_FLUSH :
case ATA_CMD_FLUSH_EXT :
case ATA_CMD_VERIFY :
case ATA_CMD_VERIFY_EXT :
case ATA_CMD_SET_FEATURES :
case ATA_CMD_STANDBY :
case ATA_CMD_STANDBYNOW1 :
2017-08-10 19:09:34 +03:00
case ATA_CMD_ZAC_MGMT_OUT :
2018-03-07 15:25:12 +03:00
return HISI_SAS_SATA_PROTOCOL_NONDATA ;
2017-12-28 13:20:47 +03:00
2018-03-23 19:05:12 +03:00
case ATA_CMD_SET_MAX :
switch ( fis - > features ) {
case ATA_SET_MAX_PASSWD :
case ATA_SET_MAX_LOCK :
return HISI_SAS_SATA_PROTOCOL_PIO ;
2017-12-28 13:20:47 +03:00
2018-03-23 19:05:12 +03:00
case ATA_SET_MAX_PASSWD_DMA :
case ATA_SET_MAX_UNLOCK_DMA :
return HISI_SAS_SATA_PROTOCOL_DMA ;
default :
return HISI_SAS_SATA_PROTOCOL_NONDATA ;
2017-12-28 13:20:47 +03:00
}
2018-03-23 19:05:12 +03:00
default :
{
2017-06-14 18:33:14 +03:00
if ( direction = = DMA_NONE )
return HISI_SAS_SATA_PROTOCOL_NONDATA ;
return HISI_SAS_SATA_PROTOCOL_PIO ;
}
2017-12-28 13:20:47 +03:00
}
2017-06-14 18:33:14 +03:00
}
EXPORT_SYMBOL_GPL ( hisi_sas_get_ata_protocol ) ;
2017-06-14 18:33:15 +03:00
void hisi_sas_sata_done ( struct sas_task * task ,
struct hisi_sas_slot * slot )
{
struct task_status_struct * ts = & task - > task_status ;
struct ata_task_resp * resp = ( struct ata_task_resp * ) ts - > buf ;
2017-06-29 16:02:14 +03:00
struct hisi_sas_status_buffer * status_buf =
hisi_sas_status_buf_addr_mem ( slot ) ;
u8 * iu = & status_buf - > iu [ 0 ] ;
struct dev_to_host_fis * d2h = ( struct dev_to_host_fis * ) iu ;
2017-06-14 18:33:15 +03:00
resp - > frame_len = sizeof ( struct dev_to_host_fis ) ;
memcpy ( & resp - > ending_fis [ 0 ] , d2h , sizeof ( struct dev_to_host_fis ) ) ;
ts - > buf_valid_size = sizeof ( * resp ) ;
}
EXPORT_SYMBOL_GPL ( hisi_sas_sata_done ) ;
2018-05-02 18:56:30 +03:00
/*
* This function assumes linkrate mask fits in 8 bits , which it
* does for all HW versions supported .
*/
u8 hisi_sas_get_prog_phy_linkrate_mask ( enum sas_linkrate max )
{
2019-02-06 13:52:56 +03:00
u8 rate = 0 ;
2018-05-02 18:56:30 +03:00
int i ;
max - = SAS_LINK_RATE_1_5_GBPS ;
for ( i = 0 ; i < = max ; i + + )
rate | = 1 < < ( i * 2 ) ;
return rate ;
}
EXPORT_SYMBOL_GPL ( hisi_sas_get_prog_phy_linkrate_mask ) ;
2015-11-17 19:50:49 +03:00
static struct hisi_hba * dev_to_hisi_hba ( struct domain_device * device )
{
return device - > port - > ha - > lldd_ha ;
}
2017-03-22 20:25:17 +03:00
struct hisi_sas_port * to_hisi_sas_port ( struct asd_sas_port * sas_port )
{
return container_of ( sas_port , struct hisi_sas_port , sas_port ) ;
}
EXPORT_SYMBOL_GPL ( to_hisi_sas_port ) ;
2017-08-10 19:09:40 +03:00
void hisi_sas_stop_phys ( struct hisi_hba * hisi_hba )
{
int phy_no ;
for ( phy_no = 0 ; phy_no < hisi_hba - > n_phy ; phy_no + + )
2019-04-11 15:46:38 +03:00
hisi_sas_phy_enable ( hisi_hba , phy_no , 0 ) ;
2017-08-10 19:09:40 +03:00
}
EXPORT_SYMBOL_GPL ( hisi_sas_stop_phys ) ;
2015-11-17 19:50:36 +03:00
static void hisi_sas_slot_index_clear ( struct hisi_hba * hisi_hba , int slot_idx )
{
void * bitmap = hisi_hba - > slot_index_tags ;
2021-11-27 00:18:26 +03:00
__clear_bit ( slot_idx , bitmap ) ;
2015-11-17 19:50:36 +03:00
}
2015-11-17 19:50:49 +03:00
static void hisi_sas_slot_index_free ( struct hisi_hba * hisi_hba , int slot_idx )
{
2019-08-05 16:47:58 +03:00
if ( hisi_hba - > hw - > slot_index_alloc | |
2022-12-15 07:09:25 +03:00
slot_idx < HISI_SAS_RESERVED_IPTT ) {
2020-01-20 15:22:32 +03:00
spin_lock ( & hisi_hba - > lock ) ;
2018-09-24 18:06:33 +03:00
hisi_sas_slot_index_clear ( hisi_hba , slot_idx ) ;
2020-01-20 15:22:32 +03:00
spin_unlock ( & hisi_hba - > lock ) ;
2018-09-24 18:06:33 +03:00
}
2015-11-17 19:50:49 +03:00
}
static void hisi_sas_slot_index_set ( struct hisi_hba * hisi_hba , int slot_idx )
{
void * bitmap = hisi_hba - > slot_index_tags ;
2021-11-27 00:18:26 +03:00
__set_bit ( slot_idx , bitmap ) ;
2015-11-17 19:50:49 +03:00
}
2018-09-24 18:06:33 +03:00
static int hisi_sas_slot_index_alloc ( struct hisi_hba * hisi_hba ,
2022-10-18 14:15:58 +03:00
struct request * rq )
2015-11-17 19:50:49 +03:00
{
2018-09-24 18:06:33 +03:00
int index ;
2015-11-17 19:50:49 +03:00
void * bitmap = hisi_hba - > slot_index_tags ;
2022-10-18 14:15:58 +03:00
if ( rq )
2022-10-18 14:15:59 +03:00
return rq - > tag + HISI_SAS_RESERVED_IPTT ;
2018-09-24 18:06:33 +03:00
2020-01-20 15:22:32 +03:00
spin_lock ( & hisi_hba - > lock ) ;
2022-10-18 14:15:59 +03:00
index = find_next_zero_bit ( bitmap , HISI_SAS_RESERVED_IPTT ,
2018-09-24 18:06:33 +03:00
hisi_hba - > last_slot_index + 1 ) ;
2022-10-18 14:15:59 +03:00
if ( index > = HISI_SAS_RESERVED_IPTT ) {
2018-09-24 18:06:33 +03:00
index = find_next_zero_bit ( bitmap ,
2022-10-18 14:15:59 +03:00
HISI_SAS_RESERVED_IPTT ,
0 ) ;
if ( index > = HISI_SAS_RESERVED_IPTT ) {
2020-01-20 15:22:32 +03:00
spin_unlock ( & hisi_hba - > lock ) ;
2018-05-21 13:09:14 +03:00
return - SAS_QUEUE_FULL ;
2018-09-24 18:06:33 +03:00
}
2018-05-21 13:09:14 +03:00
}
2015-11-17 19:50:49 +03:00
hisi_sas_slot_index_set ( hisi_hba , index ) ;
2018-05-21 13:09:14 +03:00
hisi_hba - > last_slot_index = index ;
2020-01-20 15:22:32 +03:00
spin_unlock ( & hisi_hba - > lock ) ;
2018-05-21 13:09:14 +03:00
2018-09-24 18:06:33 +03:00
return index ;
2015-11-17 19:50:49 +03:00
}
2015-11-17 19:50:50 +03:00
void hisi_sas_slot_task_free ( struct hisi_hba * hisi_hba , struct sas_task * task ,
scsi: hisi_sas: Grab sas_dev lock when traversing the members of sas_dev.list
When freeing slots in function slot_complete_v3_hw(), it is possible that
sas_dev.list is being traversed elsewhere, and it may trigger a NULL
pointer exception, such as follows:
==>cq thread ==>scsi_eh_6
==>scsi_error_handler()
==>sas_eh_handle_sas_errors()
==>sas_scsi_find_task()
==>lldd_abort_task()
==>slot_complete_v3_hw() ==>hisi_sas_abort_task()
==>hisi_sas_slot_task_free() ==>dereg_device_v3_hw()
==>list_del_init() ==>list_for_each_entry_safe()
[ 7165.434918] sas: Enter sas_scsi_recover_host busy: 32 failed: 32
[ 7165.434926] sas: trying to find task 0x00000000769b5ba5
[ 7165.434927] sas: sas_scsi_find_task: aborting task 0x00000000769b5ba5
[ 7165.434940] hisi_sas_v3_hw 0000:b4:02.0: slot complete: task(00000000769b5ba5) aborted
[ 7165.434964] hisi_sas_v3_hw 0000:b4:02.0: slot complete: task(00000000c9f7aa07) ignored
[ 7165.434965] hisi_sas_v3_hw 0000:b4:02.0: slot complete: task(00000000e2a1cf01) ignored
[ 7165.434968] Unable to handle kernel NULL pointer dereference at virtual address 0000000000000000
[ 7165.434972] hisi_sas_v3_hw 0000:b4:02.0: slot complete: task(0000000022d52d93) ignored
[ 7165.434975] hisi_sas_v3_hw 0000:b4:02.0: slot complete: task(0000000066a7516c) ignored
[ 7165.434976] Mem abort info:
[ 7165.434982] ESR = 0x96000004
[ 7165.434991] Exception class = DABT (current EL), IL = 32 bits
[ 7165.434992] SET = 0, FnV = 0
[ 7165.434993] EA = 0, S1PTW = 0
[ 7165.434994] Data abort info:
[ 7165.434994] ISV = 0, ISS = 0x00000004
[ 7165.434995] CM = 0, WnR = 0
[ 7165.434997] user pgtable: 4k pages, 48-bit VAs, pgdp = 00000000f29543f2
[ 7165.434998] [0000000000000000] pgd=0000000000000000
[ 7165.435003] Internal error: Oops: 96000004 [#1] SMP
[ 7165.439863] Process scsi_eh_6 (pid: 4109, stack limit = 0x00000000c43818d5)
[ 7165.468862] pstate: 00c00009 (nzcv daif +PAN +UAO)
[ 7165.473637] pc : dereg_device_v3_hw+0x68/0xa8 [hisi_sas_v3_hw]
[ 7165.479443] lr : dereg_device_v3_hw+0x2c/0xa8 [hisi_sas_v3_hw]
[ 7165.485247] sp : ffff00001d623bc0
[ 7165.488546] x29: ffff00001d623bc0 x28: ffffa027d03b9508
[ 7165.493835] x27: ffff80278ed50af0 x26: ffffa027dd31e0a8
[ 7165.499123] x25: ffffa027d9b27f88 x24: ffffa027d9b209f8
[ 7165.504411] x23: ffffa027c45b0d60 x22: ffff80278ec07c00
[ 7165.509700] x21: 0000000000000008 x20: ffffa027d9b209f8
[ 7165.514988] x19: ffffa027d9b27f88 x18: ffffffffffffffff
[ 7165.520276] x17: 0000000000000000 x16: 0000000000000000
[ 7165.525564] x15: ffff0000091d9708 x14: ffff0000093b7dc8
[ 7165.530852] x13: ffff0000093b7a23 x12: 6e7265746e692067
[ 7165.536140] x11: 0000000000000000 x10: 0000000000000bb0
[ 7165.541429] x9 : ffff00001d6238f0 x8 : ffffa027d877af00
[ 7165.546718] x7 : ffffa027d6329600 x6 : ffff7e809f58ca00
[ 7165.552006] x5 : 0000000000001f8a x4 : 000000000000088e
[ 7165.557295] x3 : ffffa027d9b27fa8 x2 : 0000000000000000
[ 7165.562583] x1 : 0000000000000000 x0 : 000000003000188e
[ 7165.567872] Call trace:
[ 7165.570309] dereg_device_v3_hw+0x68/0xa8 [hisi_sas_v3_hw]
[ 7165.575775] hisi_sas_abort_task+0x248/0x358 [hisi_sas_main]
[ 7165.581415] sas_eh_handle_sas_errors+0x258/0x8e0 [libsas]
[ 7165.586876] sas_scsi_recover_host+0x134/0x458 [libsas]
[ 7165.592082] scsi_error_handler+0xb4/0x488
[ 7165.596163] kthread+0x134/0x138
[ 7165.599380] ret_from_fork+0x10/0x18
[ 7165.602940] Code: d5033e9f b9000040 aa0103e2 eb03003f (f9400021)
[ 7165.609004] kernel fault(0x1) notification starting on CPU 75
[ 7165.700728] ---[ end trace fc042cbbea224efc ]---
[ 7165.705326] Kernel panic - not syncing: Fatal exception
To fix the issue, grab sas_dev lock when traversing the members of
sas_dev.list in dereg_device_v3_hw() and hisi_sas_release_tasks() to avoid
concurrency of adding and deleting member. When function
hisi_sas_release_tasks() calls hisi_sas_do_release_task() to free slot, the
lock cannot be grabbed again in hisi_sas_slot_task_free(), then a bool
parameter need_lock is added.
Signed-off-by: Xingui Yang <yangxingui@huawei.com>
Signed-off-by: Xiang Chen <chenxiang66@hisilicon.com>
Link: https://lore.kernel.org/r/1679283265-115066-2-git-send-email-chenxiang66@hisilicon.com
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
2023-03-20 06:34:22 +03:00
struct hisi_sas_slot * slot , bool need_lock )
2015-11-17 19:50:50 +03:00
{
2019-02-06 13:52:55 +03:00
int device_id = slot - > device_id ;
struct hisi_sas_device * sas_dev = & hisi_hba - > devices [ device_id ] ;
2015-11-17 19:50:50 +03:00
2017-04-10 16:22:00 +03:00
if ( task ) {
2017-06-14 18:33:17 +03:00
struct device * dev = hisi_hba - > dev ;
2015-11-17 19:50:50 +03:00
2017-10-24 18:51:38 +03:00
if ( ! task - > lldd_task )
return ;
task - > lldd_task = NULL ;
2019-02-06 13:52:51 +03:00
if ( ! sas_protocol_ata ( task - > task_proto ) ) {
2022-07-14 21:23:20 +03:00
if ( slot - > n_elem ) {
2022-07-14 21:23:19 +03:00
if ( task - > task_proto & SAS_PROTOCOL_SSP )
dma_unmap_sg ( dev , task - > scatter ,
task - > num_scatter ,
task - > data_dir ) ;
2022-07-14 21:23:20 +03:00
else
dma_unmap_sg ( dev , & task - > smp_task . smp_req ,
1 , DMA_TO_DEVICE ) ;
}
2020-09-01 14:13:03 +03:00
if ( slot - > n_elem_dif ) {
struct sas_ssp_task * ssp_task = & task - > ssp_task ;
struct scsi_cmnd * scsi_cmnd = ssp_task - > cmd ;
2019-02-06 13:52:51 +03:00
dma_unmap_sg ( dev , scsi_prot_sglist ( scsi_cmnd ) ,
scsi_prot_sg_count ( scsi_cmnd ) ,
task - > data_dir ) ;
2020-09-01 14:13:03 +03:00
}
2019-02-06 13:52:51 +03:00
}
2017-04-10 16:22:00 +03:00
}
2015-11-17 19:50:50 +03:00
scsi: hisi_sas: Grab sas_dev lock when traversing the members of sas_dev.list
When freeing slots in function slot_complete_v3_hw(), it is possible that
sas_dev.list is being traversed elsewhere, and it may trigger a NULL
pointer exception, such as follows:
==>cq thread ==>scsi_eh_6
==>scsi_error_handler()
==>sas_eh_handle_sas_errors()
==>sas_scsi_find_task()
==>lldd_abort_task()
==>slot_complete_v3_hw() ==>hisi_sas_abort_task()
==>hisi_sas_slot_task_free() ==>dereg_device_v3_hw()
==>list_del_init() ==>list_for_each_entry_safe()
[ 7165.434918] sas: Enter sas_scsi_recover_host busy: 32 failed: 32
[ 7165.434926] sas: trying to find task 0x00000000769b5ba5
[ 7165.434927] sas: sas_scsi_find_task: aborting task 0x00000000769b5ba5
[ 7165.434940] hisi_sas_v3_hw 0000:b4:02.0: slot complete: task(00000000769b5ba5) aborted
[ 7165.434964] hisi_sas_v3_hw 0000:b4:02.0: slot complete: task(00000000c9f7aa07) ignored
[ 7165.434965] hisi_sas_v3_hw 0000:b4:02.0: slot complete: task(00000000e2a1cf01) ignored
[ 7165.434968] Unable to handle kernel NULL pointer dereference at virtual address 0000000000000000
[ 7165.434972] hisi_sas_v3_hw 0000:b4:02.0: slot complete: task(0000000022d52d93) ignored
[ 7165.434975] hisi_sas_v3_hw 0000:b4:02.0: slot complete: task(0000000066a7516c) ignored
[ 7165.434976] Mem abort info:
[ 7165.434982] ESR = 0x96000004
[ 7165.434991] Exception class = DABT (current EL), IL = 32 bits
[ 7165.434992] SET = 0, FnV = 0
[ 7165.434993] EA = 0, S1PTW = 0
[ 7165.434994] Data abort info:
[ 7165.434994] ISV = 0, ISS = 0x00000004
[ 7165.434995] CM = 0, WnR = 0
[ 7165.434997] user pgtable: 4k pages, 48-bit VAs, pgdp = 00000000f29543f2
[ 7165.434998] [0000000000000000] pgd=0000000000000000
[ 7165.435003] Internal error: Oops: 96000004 [#1] SMP
[ 7165.439863] Process scsi_eh_6 (pid: 4109, stack limit = 0x00000000c43818d5)
[ 7165.468862] pstate: 00c00009 (nzcv daif +PAN +UAO)
[ 7165.473637] pc : dereg_device_v3_hw+0x68/0xa8 [hisi_sas_v3_hw]
[ 7165.479443] lr : dereg_device_v3_hw+0x2c/0xa8 [hisi_sas_v3_hw]
[ 7165.485247] sp : ffff00001d623bc0
[ 7165.488546] x29: ffff00001d623bc0 x28: ffffa027d03b9508
[ 7165.493835] x27: ffff80278ed50af0 x26: ffffa027dd31e0a8
[ 7165.499123] x25: ffffa027d9b27f88 x24: ffffa027d9b209f8
[ 7165.504411] x23: ffffa027c45b0d60 x22: ffff80278ec07c00
[ 7165.509700] x21: 0000000000000008 x20: ffffa027d9b209f8
[ 7165.514988] x19: ffffa027d9b27f88 x18: ffffffffffffffff
[ 7165.520276] x17: 0000000000000000 x16: 0000000000000000
[ 7165.525564] x15: ffff0000091d9708 x14: ffff0000093b7dc8
[ 7165.530852] x13: ffff0000093b7a23 x12: 6e7265746e692067
[ 7165.536140] x11: 0000000000000000 x10: 0000000000000bb0
[ 7165.541429] x9 : ffff00001d6238f0 x8 : ffffa027d877af00
[ 7165.546718] x7 : ffffa027d6329600 x6 : ffff7e809f58ca00
[ 7165.552006] x5 : 0000000000001f8a x4 : 000000000000088e
[ 7165.557295] x3 : ffffa027d9b27fa8 x2 : 0000000000000000
[ 7165.562583] x1 : 0000000000000000 x0 : 000000003000188e
[ 7165.567872] Call trace:
[ 7165.570309] dereg_device_v3_hw+0x68/0xa8 [hisi_sas_v3_hw]
[ 7165.575775] hisi_sas_abort_task+0x248/0x358 [hisi_sas_main]
[ 7165.581415] sas_eh_handle_sas_errors+0x258/0x8e0 [libsas]
[ 7165.586876] sas_scsi_recover_host+0x134/0x458 [libsas]
[ 7165.592082] scsi_error_handler+0xb4/0x488
[ 7165.596163] kthread+0x134/0x138
[ 7165.599380] ret_from_fork+0x10/0x18
[ 7165.602940] Code: d5033e9f b9000040 aa0103e2 eb03003f (f9400021)
[ 7165.609004] kernel fault(0x1) notification starting on CPU 75
[ 7165.700728] ---[ end trace fc042cbbea224efc ]---
[ 7165.705326] Kernel panic - not syncing: Fatal exception
To fix the issue, grab sas_dev lock when traversing the members of
sas_dev.list in dereg_device_v3_hw() and hisi_sas_release_tasks() to avoid
concurrency of adding and deleting member. When function
hisi_sas_release_tasks() calls hisi_sas_do_release_task() to free slot, the
lock cannot be grabbed again in hisi_sas_slot_task_free(), then a bool
parameter need_lock is added.
Signed-off-by: Xingui Yang <yangxingui@huawei.com>
Signed-off-by: Xiang Chen <chenxiang66@hisilicon.com>
Link: https://lore.kernel.org/r/1679283265-115066-2-git-send-email-chenxiang66@hisilicon.com
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
2023-03-20 06:34:22 +03:00
if ( need_lock ) {
spin_lock ( & sas_dev - > lock ) ;
list_del_init ( & slot - > entry ) ;
spin_unlock ( & sas_dev - > lock ) ;
} else {
list_del_init ( & slot - > entry ) ;
}
scsi: hisi_sas: Pre-allocate slot DMA buffers
Currently the driver spends much time allocating and freeing the slot DMA
buffer for command delivery/completion. To boost the performance,
pre-allocate the buffers for all IPTT. The downside of this approach is
that we are reallocating all buffer memory upfront, so hog memory which we
may not need.
However, the current method - DMA buffer pool - also caches all buffers and
does not free them until the pool is destroyed, so is not exactly efficient
either.
On top of this, since the slot DMA buffer is slightly bigger than a 4K
page, we need to allocate 2x4K pages per buffer (for 4K page kernel), which
is quite wasteful. For 64K page size this is not such an issue.
So, for the 4K page case, in order to make memory usage more efficient,
pre-allocating larger blocks of DMA memory for the buffers can be more
efficient.
To make DMA memory usage most efficient, we would choose a single
contiguous DMA memory block, but this could use up all the DMA memory in
the system (when CMA enabled and no IOMMU), or we may just not be able to
allocate a DMA buffer large enough when no CMA or IOMMU.
To decide the block size we use the LCM (least common multiple) of the
buffer size and the page size. We roundup(64) to ensure the LCM is not too
large, even though a little memory may be wasted per block.
So, with this, the total memory requirement is about is about 17MB for 4096
max IPTT.
Previously (for 4K pages case), it would be 32MB (for all slots
allocated).
With this change, the relative increase of IOPS for bs=4K read when
PAGE_SIZE=4K and PAGE_SIZE=64K is as follows:
IODEPTH 4K PAGE_SIZE 64K PAGE_SIZE
32 56% 47%
64 53% 44%
128 64% 43%
256 67% 45%
Signed-off-by: Xiang Chen <chenxiang66@hisilicon.com>
Signed-off-by: John Garry <john.garry@huawei.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
2018-05-31 15:50:48 +03:00
memset ( slot , 0 , offsetof ( struct hisi_sas_slot , buf ) ) ;
2015-11-17 19:50:50 +03:00
hisi_sas_slot_index_free ( hisi_hba , slot - > idx ) ;
}
EXPORT_SYMBOL_GPL ( hisi_sas_slot_task_free ) ;
2018-05-09 18:10:46 +03:00
static void hisi_sas_task_prep_smp ( struct hisi_hba * hisi_hba ,
2015-11-17 19:50:54 +03:00
struct hisi_sas_slot * slot )
{
2018-05-09 18:10:46 +03:00
hisi_hba - > hw - > prep_smp ( hisi_hba , slot ) ;
2015-11-17 19:50:54 +03:00
}
2018-05-09 18:10:46 +03:00
static void hisi_sas_task_prep_ssp ( struct hisi_hba * hisi_hba ,
2018-05-21 13:09:21 +03:00
struct hisi_sas_slot * slot )
2015-11-17 19:50:49 +03:00
{
2018-05-21 13:09:21 +03:00
hisi_hba - > hw - > prep_ssp ( hisi_hba , slot ) ;
2015-11-17 19:50:49 +03:00
}
2018-05-09 18:10:46 +03:00
static void hisi_sas_task_prep_ata ( struct hisi_hba * hisi_hba ,
2016-01-25 21:47:20 +03:00
struct hisi_sas_slot * slot )
{
2018-05-09 18:10:46 +03:00
hisi_hba - > hw - > prep_stp ( hisi_hba , slot ) ;
2016-01-25 21:47:20 +03:00
}
2018-05-09 18:10:46 +03:00
static void hisi_sas_task_prep_abort ( struct hisi_hba * hisi_hba ,
2022-03-11 15:23:52 +03:00
struct hisi_sas_slot * slot )
2016-08-24 14:05:47 +03:00
{
2022-03-11 15:23:52 +03:00
hisi_hba - > hw - > prep_abort ( hisi_hba , slot ) ;
2016-08-24 14:05:47 +03:00
}
2018-12-06 16:34:41 +03:00
static void hisi_sas_dma_unmap ( struct hisi_hba * hisi_hba ,
2022-07-14 21:23:19 +03:00
struct sas_task * task , int n_elem )
2018-12-06 16:34:41 +03:00
{
struct device * dev = hisi_hba - > dev ;
2022-07-14 21:23:19 +03:00
if ( ! sas_protocol_ata ( task - > task_proto ) & & n_elem ) {
2018-12-06 16:34:41 +03:00
if ( task - > num_scatter ) {
2022-07-14 21:23:19 +03:00
dma_unmap_sg ( dev , task - > scatter , task - > num_scatter ,
task - > data_dir ) ;
2018-12-06 16:34:41 +03:00
} else if ( task - > task_proto & SAS_PROTOCOL_SMP ) {
2022-07-14 21:23:19 +03:00
dma_unmap_sg ( dev , & task - > smp_task . smp_req ,
1 , DMA_TO_DEVICE ) ;
2018-12-06 16:34:41 +03:00
}
}
}
static int hisi_sas_dma_map ( struct hisi_hba * hisi_hba ,
2022-07-14 21:23:19 +03:00
struct sas_task * task , int * n_elem )
2018-12-06 16:34:41 +03:00
{
struct device * dev = hisi_hba - > dev ;
int rc ;
if ( sas_protocol_ata ( task - > task_proto ) ) {
* n_elem = task - > num_scatter ;
} else {
2019-08-05 16:48:07 +03:00
unsigned int req_len ;
2018-12-06 16:34:41 +03:00
if ( task - > num_scatter ) {
* n_elem = dma_map_sg ( dev , task - > scatter ,
task - > num_scatter , task - > data_dir ) ;
if ( ! * n_elem ) {
rc = - ENOMEM ;
goto prep_out ;
}
} else if ( task - > task_proto & SAS_PROTOCOL_SMP ) {
2022-07-14 21:23:19 +03:00
* n_elem = dma_map_sg ( dev , & task - > smp_task . smp_req ,
1 , DMA_TO_DEVICE ) ;
if ( ! * n_elem ) {
2018-12-06 16:34:41 +03:00
rc = - ENOMEM ;
goto prep_out ;
}
req_len = sg_dma_len ( & task - > smp_task . smp_req ) ;
if ( req_len & 0x3 ) {
rc = - EINVAL ;
goto err_out_dma_unmap ;
}
}
}
if ( * n_elem > HISI_SAS_SGE_PAGE_CNT ) {
2020-09-01 14:13:09 +03:00
dev_err ( dev , " task prep: n_elem(%d) > HISI_SAS_SGE_PAGE_CNT \n " ,
2018-12-06 16:34:41 +03:00
* n_elem ) ;
rc = - EINVAL ;
goto err_out_dma_unmap ;
}
return 0 ;
err_out_dma_unmap :
/* It would be better to call dma_unmap_sg() here, but it's messy */
2022-07-14 21:23:19 +03:00
hisi_sas_dma_unmap ( hisi_hba , task , * n_elem ) ;
2018-12-06 16:34:41 +03:00
prep_out :
return rc ;
}
2019-02-06 13:52:51 +03:00
static void hisi_sas_dif_dma_unmap ( struct hisi_hba * hisi_hba ,
struct sas_task * task , int n_elem_dif )
{
struct device * dev = hisi_hba - > dev ;
if ( n_elem_dif ) {
struct sas_ssp_task * ssp_task = & task - > ssp_task ;
struct scsi_cmnd * scsi_cmnd = ssp_task - > cmd ;
dma_unmap_sg ( dev , scsi_prot_sglist ( scsi_cmnd ) ,
scsi_prot_sg_count ( scsi_cmnd ) ,
task - > data_dir ) ;
}
}
static int hisi_sas_dif_dma_map ( struct hisi_hba * hisi_hba ,
int * n_elem_dif , struct sas_task * task )
{
struct device * dev = hisi_hba - > dev ;
struct sas_ssp_task * ssp_task ;
struct scsi_cmnd * scsi_cmnd ;
int rc ;
if ( task - > num_scatter ) {
ssp_task = & task - > ssp_task ;
scsi_cmnd = ssp_task - > cmd ;
if ( scsi_prot_sg_count ( scsi_cmnd ) ) {
* n_elem_dif = dma_map_sg ( dev ,
scsi_prot_sglist ( scsi_cmnd ) ,
scsi_prot_sg_count ( scsi_cmnd ) ,
task - > data_dir ) ;
if ( ! * n_elem_dif )
return - ENOMEM ;
if ( * n_elem_dif > HISI_SAS_SGE_DIF_PAGE_CNT ) {
dev_err ( dev , " task prep: n_elem_dif(%d) too large \n " ,
* n_elem_dif ) ;
rc = - EINVAL ;
goto err_out_dif_dma_unmap ;
}
}
}
return 0 ;
err_out_dif_dma_unmap :
dma_unmap_sg ( dev , scsi_prot_sglist ( scsi_cmnd ) ,
scsi_prot_sg_count ( scsi_cmnd ) , task - > data_dir ) ;
return rc ;
}
2021-12-15 17:37:37 +03:00
static
void hisi_sas_task_deliver ( struct hisi_hba * hisi_hba ,
struct hisi_sas_slot * slot ,
struct hisi_sas_dq * dq ,
2022-03-11 15:23:52 +03:00
struct hisi_sas_device * sas_dev )
2015-11-17 19:50:49 +03:00
{
2021-12-15 17:37:37 +03:00
struct hisi_sas_cmd_hdr * cmd_hdr_base ;
int dlvry_queue_slot , dlvry_queue ;
struct sas_task * task = slot - > task ;
2018-05-09 18:10:48 +03:00
int wr_q_index ;
2015-11-17 19:50:49 +03:00
2020-01-20 15:22:32 +03:00
spin_lock ( & dq - > lock ) ;
2019-08-05 16:47:59 +03:00
wr_q_index = dq - > wr_point ;
dq - > wr_point = ( dq - > wr_point + 1 ) % HISI_SAS_QUEUE_SLOTS ;
2018-05-09 18:10:48 +03:00
list_add_tail ( & slot - > delivery , & dq - > list ) ;
2020-01-20 15:22:32 +03:00
spin_unlock ( & dq - > lock ) ;
spin_lock ( & sas_dev - > lock ) ;
2019-02-06 13:52:55 +03:00
list_add_tail ( & slot - > entry , & sas_dev - > list ) ;
2020-01-20 15:22:32 +03:00
spin_unlock ( & sas_dev - > lock ) ;
2015-11-17 19:50:49 +03:00
2017-06-14 18:33:13 +03:00
dlvry_queue = dq - > id ;
2018-05-09 18:10:48 +03:00
dlvry_queue_slot = wr_q_index ;
2015-11-17 19:50:49 +03:00
2019-02-06 13:52:55 +03:00
slot - > device_id = sas_dev - > device_id ;
2015-11-17 19:50:49 +03:00
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 ] ;
2021-12-15 17:37:37 +03:00
2015-11-17 19:50:49 +03:00
task - > lldd_task = slot ;
memset ( slot - > cmd_hdr , 0 , sizeof ( struct hisi_sas_cmd_hdr ) ) ;
2017-06-29 16:02:14 +03:00
memset ( hisi_sas_cmd_hdr_addr_mem ( slot ) , 0 , HISI_SAS_COMMAND_TABLE_SZ ) ;
2019-08-05 16:48:04 +03:00
memset ( hisi_sas_status_buf_addr_mem ( slot ) , 0 ,
sizeof ( struct hisi_sas_err_record ) ) ;
2015-11-17 19:50:49 +03:00
switch ( task - > task_proto ) {
2015-11-17 19:50:54 +03:00
case SAS_PROTOCOL_SMP :
2018-05-09 18:10:46 +03:00
hisi_sas_task_prep_smp ( hisi_hba , slot ) ;
2015-11-17 19:50:54 +03:00
break ;
2015-11-17 19:50:49 +03:00
case SAS_PROTOCOL_SSP :
2018-05-21 13:09:21 +03:00
hisi_sas_task_prep_ssp ( hisi_hba , slot ) ;
2015-11-17 19:50:49 +03:00
break ;
case SAS_PROTOCOL_SATA :
case SAS_PROTOCOL_STP :
2022-03-11 15:23:52 +03:00
case SAS_PROTOCOL_STP_ALL :
2018-05-09 18:10:46 +03:00
hisi_sas_task_prep_ata ( hisi_hba , slot ) ;
2016-01-25 21:47:20 +03:00
break ;
2022-03-11 15:23:52 +03:00
case SAS_PROTOCOL_INTERNAL_ABORT :
hisi_sas_task_prep_abort ( hisi_hba , slot ) ;
break ;
2015-11-17 19:50:49 +03:00
default :
2022-03-11 15:23:52 +03:00
return ;
2015-11-17 19:50:49 +03:00
}
2022-05-17 11:04:21 +03:00
/* Make slot memories observable before marking as ready */
smp_wmb ( ) ;
2018-07-18 17:14:32 +03:00
WRITE_ONCE ( slot - > ready , 1 ) ;
2015-11-17 19:50:49 +03:00
2021-12-15 17:37:34 +03:00
spin_lock ( & dq - > lock ) ;
hisi_hba - > hw - > start_delivery ( dq ) ;
spin_unlock ( & dq - > lock ) ;
2015-11-17 19:50:49 +03:00
}
2022-02-17 18:42:36 +03:00
static int hisi_sas_queue_command ( struct sas_task * task , gfp_t gfp_flags )
2015-11-17 19:50:49 +03:00
{
2022-07-14 21:23:19 +03:00
int n_elem = 0 , n_elem_dif = 0 ;
2018-11-09 17:06:35 +03:00
struct domain_device * device = task - > dev ;
struct asd_sas_port * sas_port = device - > port ;
2021-12-15 17:37:37 +03:00
struct hisi_sas_device * sas_dev = device - > lldd_dev ;
2022-03-11 15:23:52 +03:00
bool internal_abort = sas_is_internal_abort ( task ) ;
2018-05-09 18:10:50 +03:00
struct hisi_sas_dq * dq = NULL ;
2021-12-15 17:37:37 +03:00
struct hisi_sas_port * port ;
struct hisi_hba * hisi_hba ;
struct hisi_sas_slot * slot ;
2022-10-18 14:15:58 +03:00
struct request * rq = NULL ;
2021-12-15 17:37:37 +03:00
struct device * dev ;
int rc ;
2015-11-17 19:50:49 +03:00
2018-11-09 17:06:35 +03:00
if ( ! sas_port ) {
struct task_status_struct * ts = & task - > task_status ;
ts - > resp = SAS_TASK_UNDELIVERED ;
ts - > stat = SAS_PHY_DOWN ;
/*
* libsas will use dev - > port , should
* not call task_done for sata
*/
2022-03-11 15:23:52 +03:00
if ( device - > dev_type ! = SAS_SATA_DEV & & ! internal_abort )
2018-11-09 17:06:35 +03:00
task - > task_done ( task ) ;
return - ECOMM ;
}
hisi_hba = dev_to_hisi_hba ( device ) ;
dev = hisi_hba - > dev ;
2022-03-11 15:23:52 +03:00
switch ( task - > task_proto ) {
case SAS_PROTOCOL_SSP :
case SAS_PROTOCOL_SMP :
case SAS_PROTOCOL_SATA :
case SAS_PROTOCOL_STP :
case SAS_PROTOCOL_STP_ALL :
if ( unlikely ( test_bit ( HISI_SAS_REJECT_CMD_BIT , & hisi_hba - > flags ) ) ) {
if ( ! gfpflags_allow_blocking ( gfp_flags ) )
return - EINVAL ;
2018-05-31 15:50:45 +03:00
2022-03-11 15:23:52 +03:00
down ( & hisi_hba - > sem ) ;
up ( & hisi_hba - > sem ) ;
}
2017-03-22 20:25:18 +03:00
2022-03-11 15:23:52 +03:00
if ( DEV_IS_GONE ( sas_dev ) ) {
if ( sas_dev )
dev_info ( dev , " task prep: device %d not ready \n " ,
sas_dev - > device_id ) ;
else
dev_info ( dev , " task prep: device %016llx not ready \n " ,
SAS_ADDR ( device - > sas_addr ) ) ;
2021-12-15 17:37:37 +03:00
2022-03-11 15:23:52 +03:00
return - ECOMM ;
}
2021-12-15 17:37:37 +03:00
2022-03-11 15:23:52 +03:00
port = to_hisi_sas_port ( sas_port ) ;
if ( ! port - > port_attached ) {
dev_info ( dev , " task prep: %s port%d not attach device \n " ,
dev_is_sata ( device ) ? " SATA/STP " : " SAS " ,
device - > port - > id ) ;
2021-12-15 17:37:37 +03:00
2022-03-11 15:23:52 +03:00
return - ECOMM ;
2021-12-15 17:37:37 +03:00
}
2022-10-18 14:15:58 +03:00
rq = sas_task_find_rq ( task ) ;
if ( rq ) {
2022-03-11 15:23:52 +03:00
unsigned int dq_index ;
u32 blk_tag ;
2021-12-15 17:37:37 +03:00
2022-10-18 14:15:58 +03:00
blk_tag = blk_mq_unique_tag ( rq ) ;
2022-03-11 15:23:52 +03:00
dq_index = blk_mq_unique_tag_to_hwq ( blk_tag ) ;
dq = & hisi_hba - > dq [ dq_index ] ;
} else {
2023-03-07 09:09:13 +03:00
int queue ;
if ( hisi_hba - > iopoll_q_cnt ) {
/*
* Use interrupt queue ( queue 0 ) to deliver and complete
* internal IOs of libsas or libata when there is at least
* one iopoll queue
*/
queue = 0 ;
} else {
struct Scsi_Host * shost = hisi_hba - > shost ;
struct blk_mq_queue_map * qmap = & shost - > tag_set . map [ HCTX_TYPE_DEFAULT ] ;
2021-12-15 17:37:37 +03:00
2023-03-07 09:09:13 +03:00
queue = qmap - > mq_map [ raw_smp_processor_id ( ) ] ;
}
2022-03-11 15:23:52 +03:00
dq = & hisi_hba - > dq [ queue ] ;
}
break ;
case SAS_PROTOCOL_INTERNAL_ABORT :
if ( ! hisi_hba - > hw - > prep_abort )
return TMF_RESP_FUNC_FAILED ;
if ( test_bit ( HISI_SAS_HW_FAULT_BIT , & hisi_hba - > flags ) )
return - EIO ;
hisi_hba = dev_to_hisi_hba ( device ) ;
if ( unlikely ( test_bit ( HISI_SAS_REJECT_CMD_BIT , & hisi_hba - > flags ) ) )
return - EINVAL ;
port = to_hisi_sas_port ( sas_port ) ;
dq = & hisi_hba - > dq [ task - > abort_task . qid ] ;
break ;
default :
dev_err ( hisi_hba - > dev , " task prep: unknown/unsupported proto (0x%x) \n " ,
task - > task_proto ) ;
return - EINVAL ;
2021-12-15 17:37:37 +03:00
}
2022-07-14 21:23:19 +03:00
rc = hisi_sas_dma_map ( hisi_hba , task , & n_elem ) ;
2021-12-15 17:37:37 +03:00
if ( rc < 0 )
goto prep_out ;
if ( ! sas_protocol_ata ( task - > task_proto ) ) {
rc = hisi_sas_dif_dma_map ( hisi_hba , & n_elem_dif , task ) ;
if ( rc < 0 )
goto err_out_dma_unmap ;
}
2022-03-11 15:23:52 +03:00
if ( ! internal_abort & & hisi_hba - > hw - > slot_index_alloc )
2021-12-15 17:37:37 +03:00
rc = hisi_hba - > hw - > slot_index_alloc ( hisi_hba , device ) ;
else
2022-10-18 14:15:58 +03:00
rc = hisi_sas_slot_index_alloc ( hisi_hba , rq ) ;
2021-12-15 17:37:37 +03:00
if ( rc < 0 )
goto err_out_dif_dma_unmap ;
slot = & hisi_hba - > slot_info [ rc ] ;
slot - > n_elem = n_elem ;
slot - > n_elem_dif = n_elem_dif ;
slot - > task = task ;
slot - > port = port ;
2022-02-17 18:42:36 +03:00
slot - > tmf = task - > tmf ;
2022-03-11 15:23:52 +03:00
slot - > is_internal = ! ! task - > tmf | | internal_abort ;
2021-12-15 17:37:37 +03:00
2015-11-17 19:50:49 +03:00
/* protect task_prep and start_delivery sequence */
2022-03-11 15:23:52 +03:00
hisi_sas_task_deliver ( hisi_hba , slot , dq , sas_dev ) ;
2015-11-17 19:50:49 +03:00
2021-12-15 17:37:37 +03:00
return 0 ;
err_out_dif_dma_unmap :
if ( ! sas_protocol_ata ( task - > task_proto ) )
hisi_sas_dif_dma_unmap ( hisi_hba , task , n_elem_dif ) ;
err_out_dma_unmap :
2022-07-14 21:23:19 +03:00
hisi_sas_dma_unmap ( hisi_hba , task , n_elem ) ;
2021-12-15 17:37:37 +03:00
prep_out :
dev_err ( dev , " task exec: failed[%d]! \n " , rc ) ;
2015-11-17 19:50:49 +03:00
return rc ;
}
2015-11-17 19:50:36 +03:00
2021-01-18 13:09:47 +03:00
static void hisi_sas_bytes_dmaed ( struct hisi_hba * hisi_hba , int phy_no ,
gfp_t gfp_flags )
2015-11-17 19:50:48 +03:00
{
struct hisi_sas_phy * phy = & hisi_hba - > phy [ phy_no ] ;
struct asd_sas_phy * sas_phy = & phy - > sas_phy ;
if ( ! phy - > phy_attached )
return ;
2021-01-18 13:09:49 +03:00
sas_notify_phy_event ( sas_phy , PHYE_OOB_DONE , gfp_flags ) ;
2015-11-17 19:50:48 +03:00
if ( sas_phy - > phy ) {
struct sas_phy * sphy = sas_phy - > phy ;
sphy - > negotiated_linkrate = sas_phy - > linkrate ;
sphy - > minimum_linkrate_hw = SAS_LINK_RATE_1_5_GBPS ;
2016-11-07 15:48:40 +03:00
sphy - > maximum_linkrate_hw =
hisi_hba - > hw - > phy_get_max_linkrate ( ) ;
if ( sphy - > minimum_linkrate = = SAS_LINK_RATE_UNKNOWN )
sphy - > minimum_linkrate = phy - > minimum_linkrate ;
if ( sphy - > maximum_linkrate = = SAS_LINK_RATE_UNKNOWN )
sphy - > maximum_linkrate = phy - > maximum_linkrate ;
2015-11-17 19:50:48 +03:00
}
if ( phy - > phy_type & PORT_TYPE_SAS ) {
struct sas_identify_frame * id ;
id = ( struct sas_identify_frame * ) phy - > frame_rcvd ;
id - > dev_type = phy - > identify . device_type ;
id - > initiator_bits = SAS_PROTOCOL_ALL ;
id - > target_bits = phy - > identify . target_port_protocols ;
} else if ( phy - > phy_type & PORT_TYPE_SATA ) {
2019-04-11 15:46:44 +03:00
/* Nothing */
2015-11-17 19:50:48 +03:00
}
sas_phy - > frame_rcvd_size = phy - > frame_rcvd_size ;
2021-01-18 13:09:49 +03:00
sas_notify_port_event ( sas_phy , PORTE_BYTES_DMAED , gfp_flags ) ;
2015-11-17 19:50:48 +03:00
}
2015-11-17 19:50:51 +03:00
static struct hisi_sas_device * hisi_sas_alloc_dev ( struct domain_device * device )
{
struct hisi_hba * hisi_hba = dev_to_hisi_hba ( device ) ;
struct hisi_sas_device * sas_dev = NULL ;
2018-05-21 13:09:15 +03:00
int last = hisi_hba - > last_dev_id ;
int first = ( hisi_hba - > last_dev_id + 1 ) % HISI_SAS_MAX_DEVICES ;
2015-11-17 19:50:51 +03:00
int i ;
2020-01-20 15:22:32 +03:00
spin_lock ( & hisi_hba - > lock ) ;
2018-05-21 13:09:15 +03:00
for ( i = first ; i ! = last ; i % = HISI_SAS_MAX_DEVICES ) {
2015-11-17 19:50:51 +03:00
if ( hisi_hba - > devices [ i ] . dev_type = = SAS_PHY_UNUSED ) {
2017-06-14 18:33:13 +03:00
int queue = i % hisi_hba - > queue_count ;
struct hisi_sas_dq * dq = & hisi_hba - > dq [ queue ] ;
2015-11-17 19:50:51 +03:00
hisi_hba - > devices [ i ] . device_id = i ;
sas_dev = & hisi_hba - > devices [ i ] ;
2019-02-28 17:51:01 +03:00
sas_dev - > dev_status = HISI_SAS_DEV_INIT ;
2015-11-17 19:50:51 +03:00
sas_dev - > dev_type = device - > dev_type ;
sas_dev - > hisi_hba = hisi_hba ;
sas_dev - > sas_device = device ;
2017-06-14 18:33:13 +03:00
sas_dev - > dq = dq ;
2019-02-06 13:52:55 +03:00
spin_lock_init ( & sas_dev - > lock ) ;
2017-03-22 20:25:21 +03:00
INIT_LIST_HEAD ( & hisi_hba - > devices [ i ] . list ) ;
2015-11-17 19:50:51 +03:00
break ;
}
2018-05-21 13:09:15 +03:00
i + + ;
2015-11-17 19:50:51 +03:00
}
2018-05-21 13:09:15 +03:00
hisi_hba - > last_dev_id = i ;
2020-01-20 15:22:32 +03:00
spin_unlock ( & hisi_hba - > lock ) ;
2015-11-17 19:50:51 +03:00
return sas_dev ;
}
2023-03-07 09:09:14 +03:00
static void hisi_sas_sync_poll_cq ( struct hisi_sas_cq * cq )
{
/* make sure CQ entries being processed are processed to completion */
spin_lock ( & cq - > poll_lock ) ;
spin_unlock ( & cq - > poll_lock ) ;
}
static bool hisi_sas_queue_is_poll ( struct hisi_sas_cq * cq )
{
struct hisi_hba * hisi_hba = cq - > hisi_hba ;
if ( cq - > id < hisi_hba - > queue_count - hisi_hba - > iopoll_q_cnt )
return false ;
return true ;
}
static void hisi_sas_sync_cq ( struct hisi_sas_cq * cq )
{
if ( hisi_sas_queue_is_poll ( cq ) )
hisi_sas_sync_poll_cq ( cq ) ;
else
synchronize_irq ( cq - > irq_no ) ;
}
void hisi_sas_sync_poll_cqs ( struct hisi_hba * hisi_hba )
{
int i ;
for ( i = 0 ; i < hisi_hba - > queue_count ; i + + ) {
struct hisi_sas_cq * cq = & hisi_hba - > cq [ i ] ;
if ( hisi_sas_queue_is_poll ( cq ) )
hisi_sas_sync_poll_cq ( cq ) ;
}
}
EXPORT_SYMBOL_GPL ( hisi_sas_sync_poll_cqs ) ;
void hisi_sas_sync_cqs ( struct hisi_hba * hisi_hba )
{
int i ;
for ( i = 0 ; i < hisi_hba - > queue_count ; i + + ) {
struct hisi_sas_cq * cq = & hisi_hba - > cq [ i ] ;
hisi_sas_sync_cq ( cq ) ;
}
}
EXPORT_SYMBOL_GPL ( hisi_sas_sync_cqs ) ;
2022-02-17 18:42:40 +03:00
static void hisi_sas_tmf_aborted ( struct sas_task * task )
{
struct hisi_sas_slot * slot = task - > lldd_task ;
struct domain_device * device = task - > dev ;
struct hisi_sas_device * sas_dev = device - > lldd_dev ;
struct hisi_hba * hisi_hba = sas_dev - > hisi_hba ;
if ( slot ) {
struct hisi_sas_cq * cq =
& hisi_hba - > cq [ slot - > dlvry_queue ] ;
/*
2023-03-07 09:09:14 +03:00
* sync irq or poll queue to avoid free ' ing task
2022-02-17 18:42:40 +03:00
* before using task in IO completion
*/
2023-03-07 09:09:14 +03:00
hisi_sas_sync_cq ( cq ) ;
2022-02-17 18:42:40 +03:00
slot - > task = NULL ;
}
}
2019-09-06 15:55:29 +03:00
# define HISI_SAS_DISK_RECOVER_CNT 3
2018-05-21 13:09:17 +03:00
static int hisi_sas_init_device ( struct domain_device * device )
{
int rc = TMF_RESP_FUNC_COMPLETE ;
struct scsi_lun lun ;
2019-09-06 15:55:29 +03:00
int retry = HISI_SAS_DISK_RECOVER_CNT ;
2018-05-21 13:09:17 +03:00
struct hisi_hba * hisi_hba = dev_to_hisi_hba ( device ) ;
switch ( device - > dev_type ) {
case SAS_END_DEVICE :
int_to_scsilun ( 0 , & lun ) ;
2019-09-06 15:55:29 +03:00
while ( retry - - > 0 ) {
2023-01-04 07:03:19 +03:00
rc = sas_abort_task_set ( device , lun . scsi_lun ) ;
2019-09-06 15:55:29 +03:00
if ( rc = = TMF_RESP_FUNC_COMPLETE ) {
hisi_sas_release_task ( hisi_hba , device ) ;
break ;
}
}
2018-05-21 13:09:17 +03:00
break ;
case SAS_SATA_DEV :
case SAS_SATA_PM :
case SAS_SATA_PM_PORT :
case SAS_SATA_PENDING :
2019-02-28 17:51:01 +03:00
/*
2022-05-12 14:15:34 +03:00
* If an expander is swapped when a SATA disk is attached then
* we should issue a hard reset to clear previous affiliation
* of STP target port , see SPL ( chapter 6.19 .4 ) .
*
* However we don ' t need to issue a hard reset here for these
* reasons :
* a . When probing the device , libsas / libata already issues a
2023-07-31 17:34:17 +03:00
* hard reset in sas_probe_sata ( ) - > ata_port_probe ( ) .
2022-05-12 14:15:34 +03:00
* Note that in hisi_sas_debug_I_T_nexus_reset ( ) we take care
* to issue a hard reset by checking the dev status ( = = INIT ) .
* b . When resetting the controller , this is simply unnecessary .
2019-02-28 17:51:01 +03:00
*/
2018-05-21 13:09:17 +03:00
while ( retry - - > 0 ) {
rc = hisi_sas_softreset_ata_disk ( device ) ;
if ( ! rc )
break ;
}
break ;
default :
break ;
}
return rc ;
}
2021-10-12 15:26:25 +03:00
int hisi_sas_slave_alloc ( struct scsi_device * sdev )
{
2022-05-12 14:15:34 +03:00
struct domain_device * ddev = sdev_to_domain_dev ( sdev ) ;
struct hisi_sas_device * sas_dev = ddev - > lldd_dev ;
2021-10-12 15:26:25 +03:00
int rc ;
rc = sas_slave_alloc ( sdev ) ;
if ( rc )
return rc ;
2022-05-12 14:15:34 +03:00
rc = hisi_sas_init_device ( ddev ) ;
if ( rc )
return rc ;
sas_dev - > dev_status = HISI_SAS_DEV_NORMAL ;
return 0 ;
2021-10-12 15:26:25 +03:00
}
EXPORT_SYMBOL_GPL ( hisi_sas_slave_alloc ) ;
2015-11-17 19:50:51 +03:00
static int hisi_sas_dev_found ( struct domain_device * device )
{
struct hisi_hba * hisi_hba = dev_to_hisi_hba ( device ) ;
struct domain_device * parent_dev = device - > parent ;
struct hisi_sas_device * sas_dev ;
2017-06-14 18:33:17 +03:00
struct device * dev = hisi_hba - > dev ;
2018-05-21 13:09:17 +03:00
int rc ;
2015-11-17 19:50:51 +03:00
2016-04-15 16:36:36 +03:00
if ( hisi_hba - > hw - > alloc_dev )
sas_dev = hisi_hba - > hw - > alloc_dev ( device ) ;
else
sas_dev = hisi_sas_alloc_dev ( device ) ;
2015-11-17 19:50:51 +03:00
if ( ! sas_dev ) {
dev_err ( dev , " fail alloc dev: max support %d devices \n " ,
HISI_SAS_MAX_DEVICES ) ;
return - EINVAL ;
}
device - > lldd_dev = sas_dev ;
hisi_hba - > hw - > setup_itct ( hisi_hba , sas_dev ) ;
2019-06-10 15:41:41 +03:00
if ( parent_dev & & dev_is_expander ( parent_dev - > dev_type ) ) {
2015-11-17 19:50:51 +03:00
int phy_no ;
2022-09-28 10:01:27 +03:00
phy_no = sas_find_attached_phy_id ( & parent_dev - > ex_dev , device ) ;
if ( phy_no < 0 ) {
2015-11-17 19:50:51 +03:00
dev_info ( dev , " dev found: no attached "
" dev:%016llx at ex:%016llx \n " ,
SAS_ADDR ( device - > sas_addr ) ,
SAS_ADDR ( parent_dev - > sas_addr ) ) ;
2022-09-28 10:01:27 +03:00
rc = phy_no ;
2018-05-21 13:09:17 +03:00
goto err_out ;
2015-11-17 19:50:51 +03:00
}
}
2017-12-08 20:16:41 +03:00
dev_info ( dev , " dev[%d:%x] found \n " ,
sas_dev - > device_id , sas_dev - > dev_type ) ;
2015-11-17 19:50:51 +03:00
return 0 ;
2018-05-21 13:09:17 +03:00
err_out :
hisi_sas_dev_gone ( device ) ;
return rc ;
2015-11-17 19:50:51 +03:00
}
2018-05-21 13:09:18 +03:00
int hisi_sas_slave_configure ( struct scsi_device * sdev )
2016-02-25 12:42:14 +03:00
{
struct domain_device * dev = sdev_to_domain_dev ( sdev ) ;
int ret = sas_slave_configure ( sdev ) ;
if ( ret )
return ret ;
if ( ! dev_is_sata ( dev ) )
sas_change_queue_depth ( sdev , 64 ) ;
return 0 ;
}
2018-05-21 13:09:18 +03:00
EXPORT_SYMBOL_GPL ( hisi_sas_slave_configure ) ;
2016-02-25 12:42:14 +03:00
2018-05-21 13:09:18 +03:00
void hisi_sas_scan_start ( struct Scsi_Host * shost )
2015-11-17 19:50:55 +03:00
{
struct hisi_hba * hisi_hba = shost_priv ( shost ) ;
2017-03-22 20:25:19 +03:00
hisi_hba - > hw - > phys_init ( hisi_hba ) ;
2015-11-17 19:50:55 +03:00
}
2018-05-21 13:09:18 +03:00
EXPORT_SYMBOL_GPL ( hisi_sas_scan_start ) ;
2015-11-17 19:50:55 +03:00
2018-05-21 13:09:18 +03:00
int hisi_sas_scan_finished ( struct Scsi_Host * shost , unsigned long time )
2015-11-17 19:50:55 +03:00
{
struct hisi_hba * hisi_hba = shost_priv ( shost ) ;
struct sas_ha_struct * sha = & hisi_hba - > sha ;
2017-03-22 20:25:19 +03:00
/* Wait for PHY up interrupt to occur */
if ( time < HZ )
2015-11-17 19:50:55 +03:00
return 0 ;
sas_drain_work ( sha ) ;
return 1 ;
}
2018-05-21 13:09:18 +03:00
EXPORT_SYMBOL_GPL ( hisi_sas_scan_finished ) ;
2015-11-17 19:50:55 +03:00
2021-12-20 14:21:36 +03:00
static void hisi_sas_phyup_work_common ( struct work_struct * work ,
enum hisi_sas_phy_event event )
2015-11-17 19:50:48 +03:00
{
struct hisi_sas_phy * phy =
2021-12-20 14:21:36 +03:00
container_of ( work , typeof ( * phy ) , works [ event ] ) ;
2015-11-17 19:50:48 +03:00
struct hisi_hba * hisi_hba = phy - > hisi_hba ;
struct asd_sas_phy * sas_phy = & phy - > sas_phy ;
int phy_no = sas_phy - > id ;
2021-06-07 12:29:35 +03:00
phy - > wait_phyup_cnt = 0 ;
2019-01-25 17:22:30 +03:00
if ( phy - > identify . target_port_protocols = = SAS_PROTOCOL_SSP )
hisi_hba - > hw - > sl_notify_ssp ( hisi_hba , phy_no ) ;
2021-01-18 13:09:47 +03:00
hisi_sas_bytes_dmaed ( hisi_hba , phy_no , GFP_KERNEL ) ;
2015-11-17 19:50:48 +03:00
}
2015-11-17 19:50:42 +03:00
2021-12-20 14:21:36 +03:00
static void hisi_sas_phyup_work ( struct work_struct * work )
{
hisi_sas_phyup_work_common ( work , HISI_PHYE_PHY_UP ) ;
}
2017-12-08 20:16:45 +03:00
static void hisi_sas_linkreset_work ( struct work_struct * work )
{
struct hisi_sas_phy * phy =
container_of ( work , typeof ( * phy ) , works [ HISI_PHYE_LINK_RESET ] ) ;
struct asd_sas_phy * sas_phy = & phy - > sas_phy ;
hisi_sas_control_phy ( sas_phy , PHY_FUNC_LINK_RESET , NULL ) ;
}
2021-12-20 14:21:36 +03:00
static void hisi_sas_phyup_pm_work ( struct work_struct * work )
{
struct hisi_sas_phy * phy =
container_of ( work , typeof ( * phy ) , works [ HISI_PHYE_PHY_UP_PM ] ) ;
struct hisi_hba * hisi_hba = phy - > hisi_hba ;
struct device * dev = hisi_hba - > dev ;
hisi_sas_phyup_work_common ( work , HISI_PHYE_PHY_UP_PM ) ;
pm_runtime_put_sync ( dev ) ;
}
2017-12-08 20:16:44 +03:00
static const work_func_t hisi_sas_phye_fns [ HISI_PHYES_NUM ] = {
[ HISI_PHYE_PHY_UP ] = hisi_sas_phyup_work ,
2017-12-08 20:16:45 +03:00
[ HISI_PHYE_LINK_RESET ] = hisi_sas_linkreset_work ,
2021-12-20 14:21:36 +03:00
[ HISI_PHYE_PHY_UP_PM ] = hisi_sas_phyup_pm_work ,
2017-12-08 20:16:44 +03:00
} ;
bool hisi_sas_notify_phy_event ( struct hisi_sas_phy * phy ,
enum hisi_sas_phy_event event )
{
struct hisi_hba * hisi_hba = phy - > hisi_hba ;
if ( WARN_ON ( event > = HISI_PHYES_NUM ) )
return false ;
return queue_work ( hisi_hba - > wq , & phy - > works [ event ] ) ;
}
EXPORT_SYMBOL_GPL ( hisi_sas_notify_phy_event ) ;
2019-01-25 17:22:35 +03:00
static void hisi_sas_wait_phyup_timedout ( struct timer_list * t )
{
struct hisi_sas_phy * phy = from_timer ( phy , t , timer ) ;
struct hisi_hba * hisi_hba = phy - > hisi_hba ;
struct device * dev = hisi_hba - > dev ;
int phy_no = phy - > sas_phy . id ;
dev_warn ( dev , " phy%d wait phyup timeout, issuing link reset \n " , phy_no ) ;
hisi_sas_notify_phy_event ( phy , HISI_PHYE_LINK_RESET ) ;
}
2021-06-07 12:29:35 +03:00
# define HISI_SAS_WAIT_PHYUP_RETRIES 10
2019-01-25 17:22:35 +03:00
void hisi_sas_phy_oob_ready ( struct hisi_hba * hisi_hba , int phy_no )
{
struct hisi_sas_phy * phy = & hisi_hba - > phy [ phy_no ] ;
struct device * dev = hisi_hba - > dev ;
2021-12-15 17:37:40 +03:00
unsigned long flags ;
2019-01-25 17:22:35 +03:00
2020-05-15 17:13:42 +03:00
dev_dbg ( dev , " phy%d OOB ready \n " , phy_no ) ;
2021-12-15 17:37:40 +03:00
spin_lock_irqsave ( & phy - > lock , flags ) ;
if ( phy - > phy_attached ) {
spin_unlock_irqrestore ( & phy - > lock , flags ) ;
2020-05-15 17:13:42 +03:00
return ;
2021-12-15 17:37:40 +03:00
}
2020-05-15 17:13:42 +03:00
2019-01-25 17:22:35 +03:00
if ( ! timer_pending ( & phy - > timer ) ) {
2021-06-07 12:29:35 +03:00
if ( phy - > wait_phyup_cnt < HISI_SAS_WAIT_PHYUP_RETRIES ) {
phy - > wait_phyup_cnt + + ;
phy - > timer . expires = jiffies +
2021-06-07 12:29:37 +03:00
HISI_SAS_WAIT_PHYUP_TIMEOUT ;
2021-06-07 12:29:35 +03:00
add_timer ( & phy - > timer ) ;
2021-12-15 17:37:40 +03:00
spin_unlock_irqrestore ( & phy - > lock , flags ) ;
return ;
2021-06-07 12:29:35 +03:00
}
2021-12-15 17:37:40 +03:00
dev_warn ( dev , " phy%d failed to come up %d times, giving up \n " ,
phy_no , phy - > wait_phyup_cnt ) ;
phy - > wait_phyup_cnt = 0 ;
2019-01-25 17:22:35 +03:00
}
2021-12-15 17:37:40 +03:00
spin_unlock_irqrestore ( & phy - > lock , flags ) ;
2019-01-25 17:22:35 +03:00
}
2021-12-15 17:37:40 +03:00
2019-01-25 17:22:35 +03:00
EXPORT_SYMBOL_GPL ( hisi_sas_phy_oob_ready ) ;
2015-11-17 19:50:42 +03:00
static void hisi_sas_phy_init ( struct hisi_hba * hisi_hba , int phy_no )
{
struct hisi_sas_phy * phy = & hisi_hba - > phy [ phy_no ] ;
struct asd_sas_phy * sas_phy = & phy - > sas_phy ;
2017-12-08 20:16:44 +03:00
int i ;
2015-11-17 19:50:42 +03:00
phy - > hisi_hba = hisi_hba ;
phy - > port = NULL ;
2018-03-07 15:25:07 +03:00
phy - > minimum_linkrate = SAS_LINK_RATE_1_5_GBPS ;
phy - > maximum_linkrate = hisi_hba - > hw - > phy_get_max_linkrate ( ) ;
2015-11-17 19:50:42 +03:00
sas_phy - > enabled = ( phy_no < hisi_hba - > n_phy ) ? 1 : 0 ;
sas_phy - > iproto = SAS_PROTOCOL_ALL ;
sas_phy - > tproto = 0 ;
sas_phy - > role = PHY_ROLE_INITIATOR ;
sas_phy - > oob_mode = OOB_NOT_CONNECTED ;
sas_phy - > linkrate = SAS_LINK_RATE_UNKNOWN ;
sas_phy - > id = phy_no ;
sas_phy - > sas_addr = & hisi_hba - > sas_addr [ 0 ] ;
sas_phy - > frame_rcvd = & phy - > frame_rcvd [ 0 ] ;
sas_phy - > ha = ( struct sas_ha_struct * ) hisi_hba - > shost - > hostdata ;
sas_phy - > lldd_phy = phy ;
2015-11-17 19:50:48 +03:00
2017-12-08 20:16:44 +03:00
for ( i = 0 ; i < HISI_PHYES_NUM ; i + + )
INIT_WORK ( & phy - > works [ i ] , hisi_sas_phye_fns [ i ] ) ;
2018-05-31 15:50:49 +03:00
spin_lock_init ( & phy - > lock ) ;
2019-01-25 17:22:35 +03:00
timer_setup ( & phy - > timer , hisi_sas_wait_phyup_timedout , 0 ) ;
2015-11-17 19:50:42 +03:00
}
2019-04-11 15:46:38 +03:00
/* Wrapper to ensure we track hisi_sas_phy.enable properly */
void hisi_sas_phy_enable ( struct hisi_hba * hisi_hba , int phy_no , int enable )
{
struct hisi_sas_phy * phy = & hisi_hba - > phy [ phy_no ] ;
struct asd_sas_phy * aphy = & phy - > sas_phy ;
struct sas_phy * sphy = aphy - > phy ;
unsigned long flags ;
spin_lock_irqsave ( & phy - > lock , flags ) ;
if ( enable ) {
/* We may have been enabled already; if so, don't touch */
if ( ! phy - > enable )
sphy - > negotiated_linkrate = SAS_LINK_RATE_UNKNOWN ;
hisi_hba - > hw - > phy_start ( hisi_hba , phy_no ) ;
} else {
sphy - > negotiated_linkrate = SAS_PHY_DISABLED ;
hisi_hba - > hw - > phy_disable ( hisi_hba , phy_no ) ;
}
phy - > enable = enable ;
spin_unlock_irqrestore ( & phy - > lock , flags ) ;
}
EXPORT_SYMBOL_GPL ( hisi_sas_phy_enable ) ;
2015-11-17 19:50:52 +03:00
static void hisi_sas_port_notify_formed ( struct asd_sas_phy * sas_phy )
{
struct hisi_sas_phy * phy = sas_phy - > lldd_phy ;
struct asd_sas_port * sas_port = sas_phy - > port ;
2019-11-12 12:30:56 +03:00
struct hisi_sas_port * port ;
2015-11-17 19:50:52 +03:00
if ( ! sas_port )
return ;
2019-11-12 12:30:56 +03:00
port = to_hisi_sas_port ( sas_port ) ;
2015-11-17 19:50:52 +03:00
port - > port_attached = 1 ;
port - > id = phy - > port_id ;
phy - > port = port ;
sas_port - > lldd_port = port ;
}
2017-04-10 16:22:00 +03:00
static void hisi_sas_do_release_task ( struct hisi_hba * hisi_hba , struct sas_task * task ,
scsi: hisi_sas: Grab sas_dev lock when traversing the members of sas_dev.list
When freeing slots in function slot_complete_v3_hw(), it is possible that
sas_dev.list is being traversed elsewhere, and it may trigger a NULL
pointer exception, such as follows:
==>cq thread ==>scsi_eh_6
==>scsi_error_handler()
==>sas_eh_handle_sas_errors()
==>sas_scsi_find_task()
==>lldd_abort_task()
==>slot_complete_v3_hw() ==>hisi_sas_abort_task()
==>hisi_sas_slot_task_free() ==>dereg_device_v3_hw()
==>list_del_init() ==>list_for_each_entry_safe()
[ 7165.434918] sas: Enter sas_scsi_recover_host busy: 32 failed: 32
[ 7165.434926] sas: trying to find task 0x00000000769b5ba5
[ 7165.434927] sas: sas_scsi_find_task: aborting task 0x00000000769b5ba5
[ 7165.434940] hisi_sas_v3_hw 0000:b4:02.0: slot complete: task(00000000769b5ba5) aborted
[ 7165.434964] hisi_sas_v3_hw 0000:b4:02.0: slot complete: task(00000000c9f7aa07) ignored
[ 7165.434965] hisi_sas_v3_hw 0000:b4:02.0: slot complete: task(00000000e2a1cf01) ignored
[ 7165.434968] Unable to handle kernel NULL pointer dereference at virtual address 0000000000000000
[ 7165.434972] hisi_sas_v3_hw 0000:b4:02.0: slot complete: task(0000000022d52d93) ignored
[ 7165.434975] hisi_sas_v3_hw 0000:b4:02.0: slot complete: task(0000000066a7516c) ignored
[ 7165.434976] Mem abort info:
[ 7165.434982] ESR = 0x96000004
[ 7165.434991] Exception class = DABT (current EL), IL = 32 bits
[ 7165.434992] SET = 0, FnV = 0
[ 7165.434993] EA = 0, S1PTW = 0
[ 7165.434994] Data abort info:
[ 7165.434994] ISV = 0, ISS = 0x00000004
[ 7165.434995] CM = 0, WnR = 0
[ 7165.434997] user pgtable: 4k pages, 48-bit VAs, pgdp = 00000000f29543f2
[ 7165.434998] [0000000000000000] pgd=0000000000000000
[ 7165.435003] Internal error: Oops: 96000004 [#1] SMP
[ 7165.439863] Process scsi_eh_6 (pid: 4109, stack limit = 0x00000000c43818d5)
[ 7165.468862] pstate: 00c00009 (nzcv daif +PAN +UAO)
[ 7165.473637] pc : dereg_device_v3_hw+0x68/0xa8 [hisi_sas_v3_hw]
[ 7165.479443] lr : dereg_device_v3_hw+0x2c/0xa8 [hisi_sas_v3_hw]
[ 7165.485247] sp : ffff00001d623bc0
[ 7165.488546] x29: ffff00001d623bc0 x28: ffffa027d03b9508
[ 7165.493835] x27: ffff80278ed50af0 x26: ffffa027dd31e0a8
[ 7165.499123] x25: ffffa027d9b27f88 x24: ffffa027d9b209f8
[ 7165.504411] x23: ffffa027c45b0d60 x22: ffff80278ec07c00
[ 7165.509700] x21: 0000000000000008 x20: ffffa027d9b209f8
[ 7165.514988] x19: ffffa027d9b27f88 x18: ffffffffffffffff
[ 7165.520276] x17: 0000000000000000 x16: 0000000000000000
[ 7165.525564] x15: ffff0000091d9708 x14: ffff0000093b7dc8
[ 7165.530852] x13: ffff0000093b7a23 x12: 6e7265746e692067
[ 7165.536140] x11: 0000000000000000 x10: 0000000000000bb0
[ 7165.541429] x9 : ffff00001d6238f0 x8 : ffffa027d877af00
[ 7165.546718] x7 : ffffa027d6329600 x6 : ffff7e809f58ca00
[ 7165.552006] x5 : 0000000000001f8a x4 : 000000000000088e
[ 7165.557295] x3 : ffffa027d9b27fa8 x2 : 0000000000000000
[ 7165.562583] x1 : 0000000000000000 x0 : 000000003000188e
[ 7165.567872] Call trace:
[ 7165.570309] dereg_device_v3_hw+0x68/0xa8 [hisi_sas_v3_hw]
[ 7165.575775] hisi_sas_abort_task+0x248/0x358 [hisi_sas_main]
[ 7165.581415] sas_eh_handle_sas_errors+0x258/0x8e0 [libsas]
[ 7165.586876] sas_scsi_recover_host+0x134/0x458 [libsas]
[ 7165.592082] scsi_error_handler+0xb4/0x488
[ 7165.596163] kthread+0x134/0x138
[ 7165.599380] ret_from_fork+0x10/0x18
[ 7165.602940] Code: d5033e9f b9000040 aa0103e2 eb03003f (f9400021)
[ 7165.609004] kernel fault(0x1) notification starting on CPU 75
[ 7165.700728] ---[ end trace fc042cbbea224efc ]---
[ 7165.705326] Kernel panic - not syncing: Fatal exception
To fix the issue, grab sas_dev lock when traversing the members of
sas_dev.list in dereg_device_v3_hw() and hisi_sas_release_tasks() to avoid
concurrency of adding and deleting member. When function
hisi_sas_release_tasks() calls hisi_sas_do_release_task() to free slot, the
lock cannot be grabbed again in hisi_sas_slot_task_free(), then a bool
parameter need_lock is added.
Signed-off-by: Xingui Yang <yangxingui@huawei.com>
Signed-off-by: Xiang Chen <chenxiang66@hisilicon.com>
Link: https://lore.kernel.org/r/1679283265-115066-2-git-send-email-chenxiang66@hisilicon.com
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
2023-03-20 06:34:22 +03:00
struct hisi_sas_slot * slot , bool need_lock )
2015-11-17 19:50:52 +03:00
{
2017-04-10 16:22:00 +03:00
if ( task ) {
unsigned long flags ;
struct task_status_struct * ts ;
2015-11-17 19:50:52 +03:00
2017-04-10 16:22:00 +03:00
ts = & task - > task_status ;
2015-11-17 19:50:52 +03:00
2017-04-10 16:22:00 +03:00
ts - > resp = SAS_TASK_COMPLETE ;
ts - > stat = SAS_ABORTED_TASK ;
spin_lock_irqsave ( & task - > task_state_lock , flags ) ;
2022-02-10 13:43:24 +03:00
task - > task_state_flags & = ~ SAS_TASK_STATE_PENDING ;
2019-02-28 17:50:58 +03:00
if ( ! slot - > is_internal & & task - > task_proto ! = SAS_PROTOCOL_SMP )
task - > task_state_flags | = SAS_TASK_STATE_DONE ;
2017-04-10 16:22:00 +03:00
spin_unlock_irqrestore ( & task - > task_state_lock , flags ) ;
}
2015-11-17 19:50:52 +03:00
scsi: hisi_sas: Grab sas_dev lock when traversing the members of sas_dev.list
When freeing slots in function slot_complete_v3_hw(), it is possible that
sas_dev.list is being traversed elsewhere, and it may trigger a NULL
pointer exception, such as follows:
==>cq thread ==>scsi_eh_6
==>scsi_error_handler()
==>sas_eh_handle_sas_errors()
==>sas_scsi_find_task()
==>lldd_abort_task()
==>slot_complete_v3_hw() ==>hisi_sas_abort_task()
==>hisi_sas_slot_task_free() ==>dereg_device_v3_hw()
==>list_del_init() ==>list_for_each_entry_safe()
[ 7165.434918] sas: Enter sas_scsi_recover_host busy: 32 failed: 32
[ 7165.434926] sas: trying to find task 0x00000000769b5ba5
[ 7165.434927] sas: sas_scsi_find_task: aborting task 0x00000000769b5ba5
[ 7165.434940] hisi_sas_v3_hw 0000:b4:02.0: slot complete: task(00000000769b5ba5) aborted
[ 7165.434964] hisi_sas_v3_hw 0000:b4:02.0: slot complete: task(00000000c9f7aa07) ignored
[ 7165.434965] hisi_sas_v3_hw 0000:b4:02.0: slot complete: task(00000000e2a1cf01) ignored
[ 7165.434968] Unable to handle kernel NULL pointer dereference at virtual address 0000000000000000
[ 7165.434972] hisi_sas_v3_hw 0000:b4:02.0: slot complete: task(0000000022d52d93) ignored
[ 7165.434975] hisi_sas_v3_hw 0000:b4:02.0: slot complete: task(0000000066a7516c) ignored
[ 7165.434976] Mem abort info:
[ 7165.434982] ESR = 0x96000004
[ 7165.434991] Exception class = DABT (current EL), IL = 32 bits
[ 7165.434992] SET = 0, FnV = 0
[ 7165.434993] EA = 0, S1PTW = 0
[ 7165.434994] Data abort info:
[ 7165.434994] ISV = 0, ISS = 0x00000004
[ 7165.434995] CM = 0, WnR = 0
[ 7165.434997] user pgtable: 4k pages, 48-bit VAs, pgdp = 00000000f29543f2
[ 7165.434998] [0000000000000000] pgd=0000000000000000
[ 7165.435003] Internal error: Oops: 96000004 [#1] SMP
[ 7165.439863] Process scsi_eh_6 (pid: 4109, stack limit = 0x00000000c43818d5)
[ 7165.468862] pstate: 00c00009 (nzcv daif +PAN +UAO)
[ 7165.473637] pc : dereg_device_v3_hw+0x68/0xa8 [hisi_sas_v3_hw]
[ 7165.479443] lr : dereg_device_v3_hw+0x2c/0xa8 [hisi_sas_v3_hw]
[ 7165.485247] sp : ffff00001d623bc0
[ 7165.488546] x29: ffff00001d623bc0 x28: ffffa027d03b9508
[ 7165.493835] x27: ffff80278ed50af0 x26: ffffa027dd31e0a8
[ 7165.499123] x25: ffffa027d9b27f88 x24: ffffa027d9b209f8
[ 7165.504411] x23: ffffa027c45b0d60 x22: ffff80278ec07c00
[ 7165.509700] x21: 0000000000000008 x20: ffffa027d9b209f8
[ 7165.514988] x19: ffffa027d9b27f88 x18: ffffffffffffffff
[ 7165.520276] x17: 0000000000000000 x16: 0000000000000000
[ 7165.525564] x15: ffff0000091d9708 x14: ffff0000093b7dc8
[ 7165.530852] x13: ffff0000093b7a23 x12: 6e7265746e692067
[ 7165.536140] x11: 0000000000000000 x10: 0000000000000bb0
[ 7165.541429] x9 : ffff00001d6238f0 x8 : ffffa027d877af00
[ 7165.546718] x7 : ffffa027d6329600 x6 : ffff7e809f58ca00
[ 7165.552006] x5 : 0000000000001f8a x4 : 000000000000088e
[ 7165.557295] x3 : ffffa027d9b27fa8 x2 : 0000000000000000
[ 7165.562583] x1 : 0000000000000000 x0 : 000000003000188e
[ 7165.567872] Call trace:
[ 7165.570309] dereg_device_v3_hw+0x68/0xa8 [hisi_sas_v3_hw]
[ 7165.575775] hisi_sas_abort_task+0x248/0x358 [hisi_sas_main]
[ 7165.581415] sas_eh_handle_sas_errors+0x258/0x8e0 [libsas]
[ 7165.586876] sas_scsi_recover_host+0x134/0x458 [libsas]
[ 7165.592082] scsi_error_handler+0xb4/0x488
[ 7165.596163] kthread+0x134/0x138
[ 7165.599380] ret_from_fork+0x10/0x18
[ 7165.602940] Code: d5033e9f b9000040 aa0103e2 eb03003f (f9400021)
[ 7165.609004] kernel fault(0x1) notification starting on CPU 75
[ 7165.700728] ---[ end trace fc042cbbea224efc ]---
[ 7165.705326] Kernel panic - not syncing: Fatal exception
To fix the issue, grab sas_dev lock when traversing the members of
sas_dev.list in dereg_device_v3_hw() and hisi_sas_release_tasks() to avoid
concurrency of adding and deleting member. When function
hisi_sas_release_tasks() calls hisi_sas_do_release_task() to free slot, the
lock cannot be grabbed again in hisi_sas_slot_task_free(), then a bool
parameter need_lock is added.
Signed-off-by: Xingui Yang <yangxingui@huawei.com>
Signed-off-by: Xiang Chen <chenxiang66@hisilicon.com>
Link: https://lore.kernel.org/r/1679283265-115066-2-git-send-email-chenxiang66@hisilicon.com
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
2023-03-20 06:34:22 +03:00
hisi_sas_slot_task_free ( hisi_hba , task , slot , need_lock ) ;
2015-11-17 19:50:52 +03:00
}
static void hisi_sas_release_task ( struct hisi_hba * hisi_hba ,
struct domain_device * device )
{
2017-03-22 20:25:21 +03:00
struct hisi_sas_slot * slot , * slot2 ;
struct hisi_sas_device * sas_dev = device - > lldd_dev ;
2015-11-17 19:50:52 +03:00
scsi: hisi_sas: Grab sas_dev lock when traversing the members of sas_dev.list
When freeing slots in function slot_complete_v3_hw(), it is possible that
sas_dev.list is being traversed elsewhere, and it may trigger a NULL
pointer exception, such as follows:
==>cq thread ==>scsi_eh_6
==>scsi_error_handler()
==>sas_eh_handle_sas_errors()
==>sas_scsi_find_task()
==>lldd_abort_task()
==>slot_complete_v3_hw() ==>hisi_sas_abort_task()
==>hisi_sas_slot_task_free() ==>dereg_device_v3_hw()
==>list_del_init() ==>list_for_each_entry_safe()
[ 7165.434918] sas: Enter sas_scsi_recover_host busy: 32 failed: 32
[ 7165.434926] sas: trying to find task 0x00000000769b5ba5
[ 7165.434927] sas: sas_scsi_find_task: aborting task 0x00000000769b5ba5
[ 7165.434940] hisi_sas_v3_hw 0000:b4:02.0: slot complete: task(00000000769b5ba5) aborted
[ 7165.434964] hisi_sas_v3_hw 0000:b4:02.0: slot complete: task(00000000c9f7aa07) ignored
[ 7165.434965] hisi_sas_v3_hw 0000:b4:02.0: slot complete: task(00000000e2a1cf01) ignored
[ 7165.434968] Unable to handle kernel NULL pointer dereference at virtual address 0000000000000000
[ 7165.434972] hisi_sas_v3_hw 0000:b4:02.0: slot complete: task(0000000022d52d93) ignored
[ 7165.434975] hisi_sas_v3_hw 0000:b4:02.0: slot complete: task(0000000066a7516c) ignored
[ 7165.434976] Mem abort info:
[ 7165.434982] ESR = 0x96000004
[ 7165.434991] Exception class = DABT (current EL), IL = 32 bits
[ 7165.434992] SET = 0, FnV = 0
[ 7165.434993] EA = 0, S1PTW = 0
[ 7165.434994] Data abort info:
[ 7165.434994] ISV = 0, ISS = 0x00000004
[ 7165.434995] CM = 0, WnR = 0
[ 7165.434997] user pgtable: 4k pages, 48-bit VAs, pgdp = 00000000f29543f2
[ 7165.434998] [0000000000000000] pgd=0000000000000000
[ 7165.435003] Internal error: Oops: 96000004 [#1] SMP
[ 7165.439863] Process scsi_eh_6 (pid: 4109, stack limit = 0x00000000c43818d5)
[ 7165.468862] pstate: 00c00009 (nzcv daif +PAN +UAO)
[ 7165.473637] pc : dereg_device_v3_hw+0x68/0xa8 [hisi_sas_v3_hw]
[ 7165.479443] lr : dereg_device_v3_hw+0x2c/0xa8 [hisi_sas_v3_hw]
[ 7165.485247] sp : ffff00001d623bc0
[ 7165.488546] x29: ffff00001d623bc0 x28: ffffa027d03b9508
[ 7165.493835] x27: ffff80278ed50af0 x26: ffffa027dd31e0a8
[ 7165.499123] x25: ffffa027d9b27f88 x24: ffffa027d9b209f8
[ 7165.504411] x23: ffffa027c45b0d60 x22: ffff80278ec07c00
[ 7165.509700] x21: 0000000000000008 x20: ffffa027d9b209f8
[ 7165.514988] x19: ffffa027d9b27f88 x18: ffffffffffffffff
[ 7165.520276] x17: 0000000000000000 x16: 0000000000000000
[ 7165.525564] x15: ffff0000091d9708 x14: ffff0000093b7dc8
[ 7165.530852] x13: ffff0000093b7a23 x12: 6e7265746e692067
[ 7165.536140] x11: 0000000000000000 x10: 0000000000000bb0
[ 7165.541429] x9 : ffff00001d6238f0 x8 : ffffa027d877af00
[ 7165.546718] x7 : ffffa027d6329600 x6 : ffff7e809f58ca00
[ 7165.552006] x5 : 0000000000001f8a x4 : 000000000000088e
[ 7165.557295] x3 : ffffa027d9b27fa8 x2 : 0000000000000000
[ 7165.562583] x1 : 0000000000000000 x0 : 000000003000188e
[ 7165.567872] Call trace:
[ 7165.570309] dereg_device_v3_hw+0x68/0xa8 [hisi_sas_v3_hw]
[ 7165.575775] hisi_sas_abort_task+0x248/0x358 [hisi_sas_main]
[ 7165.581415] sas_eh_handle_sas_errors+0x258/0x8e0 [libsas]
[ 7165.586876] sas_scsi_recover_host+0x134/0x458 [libsas]
[ 7165.592082] scsi_error_handler+0xb4/0x488
[ 7165.596163] kthread+0x134/0x138
[ 7165.599380] ret_from_fork+0x10/0x18
[ 7165.602940] Code: d5033e9f b9000040 aa0103e2 eb03003f (f9400021)
[ 7165.609004] kernel fault(0x1) notification starting on CPU 75
[ 7165.700728] ---[ end trace fc042cbbea224efc ]---
[ 7165.705326] Kernel panic - not syncing: Fatal exception
To fix the issue, grab sas_dev lock when traversing the members of
sas_dev.list in dereg_device_v3_hw() and hisi_sas_release_tasks() to avoid
concurrency of adding and deleting member. When function
hisi_sas_release_tasks() calls hisi_sas_do_release_task() to free slot, the
lock cannot be grabbed again in hisi_sas_slot_task_free(), then a bool
parameter need_lock is added.
Signed-off-by: Xingui Yang <yangxingui@huawei.com>
Signed-off-by: Xiang Chen <chenxiang66@hisilicon.com>
Link: https://lore.kernel.org/r/1679283265-115066-2-git-send-email-chenxiang66@hisilicon.com
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
2023-03-20 06:34:22 +03:00
spin_lock ( & sas_dev - > lock ) ;
2017-03-22 20:25:21 +03:00
list_for_each_entry_safe ( slot , slot2 , & sas_dev - > list , entry )
scsi: hisi_sas: Grab sas_dev lock when traversing the members of sas_dev.list
When freeing slots in function slot_complete_v3_hw(), it is possible that
sas_dev.list is being traversed elsewhere, and it may trigger a NULL
pointer exception, such as follows:
==>cq thread ==>scsi_eh_6
==>scsi_error_handler()
==>sas_eh_handle_sas_errors()
==>sas_scsi_find_task()
==>lldd_abort_task()
==>slot_complete_v3_hw() ==>hisi_sas_abort_task()
==>hisi_sas_slot_task_free() ==>dereg_device_v3_hw()
==>list_del_init() ==>list_for_each_entry_safe()
[ 7165.434918] sas: Enter sas_scsi_recover_host busy: 32 failed: 32
[ 7165.434926] sas: trying to find task 0x00000000769b5ba5
[ 7165.434927] sas: sas_scsi_find_task: aborting task 0x00000000769b5ba5
[ 7165.434940] hisi_sas_v3_hw 0000:b4:02.0: slot complete: task(00000000769b5ba5) aborted
[ 7165.434964] hisi_sas_v3_hw 0000:b4:02.0: slot complete: task(00000000c9f7aa07) ignored
[ 7165.434965] hisi_sas_v3_hw 0000:b4:02.0: slot complete: task(00000000e2a1cf01) ignored
[ 7165.434968] Unable to handle kernel NULL pointer dereference at virtual address 0000000000000000
[ 7165.434972] hisi_sas_v3_hw 0000:b4:02.0: slot complete: task(0000000022d52d93) ignored
[ 7165.434975] hisi_sas_v3_hw 0000:b4:02.0: slot complete: task(0000000066a7516c) ignored
[ 7165.434976] Mem abort info:
[ 7165.434982] ESR = 0x96000004
[ 7165.434991] Exception class = DABT (current EL), IL = 32 bits
[ 7165.434992] SET = 0, FnV = 0
[ 7165.434993] EA = 0, S1PTW = 0
[ 7165.434994] Data abort info:
[ 7165.434994] ISV = 0, ISS = 0x00000004
[ 7165.434995] CM = 0, WnR = 0
[ 7165.434997] user pgtable: 4k pages, 48-bit VAs, pgdp = 00000000f29543f2
[ 7165.434998] [0000000000000000] pgd=0000000000000000
[ 7165.435003] Internal error: Oops: 96000004 [#1] SMP
[ 7165.439863] Process scsi_eh_6 (pid: 4109, stack limit = 0x00000000c43818d5)
[ 7165.468862] pstate: 00c00009 (nzcv daif +PAN +UAO)
[ 7165.473637] pc : dereg_device_v3_hw+0x68/0xa8 [hisi_sas_v3_hw]
[ 7165.479443] lr : dereg_device_v3_hw+0x2c/0xa8 [hisi_sas_v3_hw]
[ 7165.485247] sp : ffff00001d623bc0
[ 7165.488546] x29: ffff00001d623bc0 x28: ffffa027d03b9508
[ 7165.493835] x27: ffff80278ed50af0 x26: ffffa027dd31e0a8
[ 7165.499123] x25: ffffa027d9b27f88 x24: ffffa027d9b209f8
[ 7165.504411] x23: ffffa027c45b0d60 x22: ffff80278ec07c00
[ 7165.509700] x21: 0000000000000008 x20: ffffa027d9b209f8
[ 7165.514988] x19: ffffa027d9b27f88 x18: ffffffffffffffff
[ 7165.520276] x17: 0000000000000000 x16: 0000000000000000
[ 7165.525564] x15: ffff0000091d9708 x14: ffff0000093b7dc8
[ 7165.530852] x13: ffff0000093b7a23 x12: 6e7265746e692067
[ 7165.536140] x11: 0000000000000000 x10: 0000000000000bb0
[ 7165.541429] x9 : ffff00001d6238f0 x8 : ffffa027d877af00
[ 7165.546718] x7 : ffffa027d6329600 x6 : ffff7e809f58ca00
[ 7165.552006] x5 : 0000000000001f8a x4 : 000000000000088e
[ 7165.557295] x3 : ffffa027d9b27fa8 x2 : 0000000000000000
[ 7165.562583] x1 : 0000000000000000 x0 : 000000003000188e
[ 7165.567872] Call trace:
[ 7165.570309] dereg_device_v3_hw+0x68/0xa8 [hisi_sas_v3_hw]
[ 7165.575775] hisi_sas_abort_task+0x248/0x358 [hisi_sas_main]
[ 7165.581415] sas_eh_handle_sas_errors+0x258/0x8e0 [libsas]
[ 7165.586876] sas_scsi_recover_host+0x134/0x458 [libsas]
[ 7165.592082] scsi_error_handler+0xb4/0x488
[ 7165.596163] kthread+0x134/0x138
[ 7165.599380] ret_from_fork+0x10/0x18
[ 7165.602940] Code: d5033e9f b9000040 aa0103e2 eb03003f (f9400021)
[ 7165.609004] kernel fault(0x1) notification starting on CPU 75
[ 7165.700728] ---[ end trace fc042cbbea224efc ]---
[ 7165.705326] Kernel panic - not syncing: Fatal exception
To fix the issue, grab sas_dev lock when traversing the members of
sas_dev.list in dereg_device_v3_hw() and hisi_sas_release_tasks() to avoid
concurrency of adding and deleting member. When function
hisi_sas_release_tasks() calls hisi_sas_do_release_task() to free slot, the
lock cannot be grabbed again in hisi_sas_slot_task_free(), then a bool
parameter need_lock is added.
Signed-off-by: Xingui Yang <yangxingui@huawei.com>
Signed-off-by: Xiang Chen <chenxiang66@hisilicon.com>
Link: https://lore.kernel.org/r/1679283265-115066-2-git-send-email-chenxiang66@hisilicon.com
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
2023-03-20 06:34:22 +03:00
hisi_sas_do_release_task ( hisi_hba , slot - > task , slot , false ) ;
spin_unlock ( & sas_dev - > lock ) ;
2015-11-17 19:50:52 +03:00
}
2017-12-08 20:16:50 +03:00
void hisi_sas_release_tasks ( struct hisi_hba * hisi_hba )
2017-03-22 20:25:18 +03:00
{
2017-03-22 20:25:21 +03:00
struct hisi_sas_device * sas_dev ;
struct domain_device * device ;
2017-03-22 20:25:18 +03:00
int i ;
2017-03-22 20:25:21 +03:00
for ( i = 0 ; i < HISI_SAS_MAX_DEVICES ; i + + ) {
sas_dev = & hisi_hba - > devices [ i ] ;
device = sas_dev - > sas_device ;
2017-03-22 20:25:18 +03:00
2017-03-22 20:25:21 +03:00
if ( ( sas_dev - > dev_type = = SAS_PHY_UNUSED ) | |
! device )
2017-03-22 20:25:18 +03:00
continue ;
2017-03-22 20:25:21 +03:00
hisi_sas_release_task ( hisi_hba , device ) ;
2017-03-22 20:25:18 +03:00
}
}
2017-12-08 20:16:50 +03:00
EXPORT_SYMBOL_GPL ( hisi_sas_release_tasks ) ;
2017-03-22 20:25:18 +03:00
2017-06-14 18:33:32 +03:00
static void hisi_sas_dereg_device ( struct hisi_hba * hisi_hba ,
struct domain_device * device )
{
if ( hisi_hba - > hw - > dereg_device )
hisi_hba - > hw - > dereg_device ( hisi_hba , device ) ;
}
2022-03-11 15:23:52 +03:00
static int
hisi_sas_internal_task_abort_dev ( struct hisi_sas_device * sas_dev ,
bool rst_ha_timeout )
{
struct hisi_sas_internal_abort_data data = { rst_ha_timeout } ;
struct domain_device * device = sas_dev - > sas_device ;
struct hisi_hba * hisi_hba = sas_dev - > hisi_hba ;
int i , rc ;
for ( i = 0 ; i < hisi_hba - > cq_nvecs ; i + + ) {
struct hisi_sas_cq * cq = & hisi_hba - > cq [ i ] ;
const struct cpumask * mask = cq - > irq_mask ;
if ( mask & & ! cpumask_intersects ( cpu_online_mask , mask ) )
continue ;
rc = sas_execute_internal_abort_dev ( device , i , & data ) ;
if ( rc )
return rc ;
}
return 0 ;
}
2015-11-17 19:50:51 +03:00
static void hisi_sas_dev_gone ( struct domain_device * device )
{
struct hisi_sas_device * sas_dev = device - > lldd_dev ;
struct hisi_hba * hisi_hba = dev_to_hisi_hba ( device ) ;
2017-06-14 18:33:17 +03:00
struct device * dev = hisi_hba - > dev ;
2019-10-24 17:08:10 +03:00
int ret = 0 ;
2015-11-17 19:50:51 +03:00
2017-12-08 20:16:41 +03:00
dev_info ( dev , " dev[%d:%x] is gone \n " ,
2015-11-17 19:50:51 +03:00
sas_dev - > device_id , sas_dev - > dev_type ) ;
scsi: hisi_sas: Fix the conflict between device gone and host reset
When device gone, it will check whether it is during reset, if not, it will
send internal task abort. Before internal task abort returned, reset
begins, and it will check whether SAS_PHY_UNUSED is set, if not, it will
call hisi_sas_init_device(), but at that time domain_device may already be
freed or part of it is freed, so it may referenece null pointer in
hisi_sas_init_device(). It may occur as follows:
thread0 thread1
hisi_sas_dev_gone()
check whether in RESET(no)
internal task abort
reset prep
soft_reset
... (part of reset_done)
internal task abort failed
release resource anyway
clear_itct
device->lldd_dev=NULL
hisi_sas_reset_init_all_device
check sas_dev->dev_type is SAS_PHY_UNUSED and
!device
set dev_type SAS_PHY_UNUSED
sas_free_device
hisi_sas_init_device
...
Semaphore hisi_hba.sema is used to sync the processes of device gone and
host reset.
To solve the issue, expand the scope that semaphore protects and let them
never occur together.
And also some places will check whether domain_device is NULL to judge
whether the device is gone. So when device gone, need to clear
sas_dev->sas_device.
Link: https://lore.kernel.org/r/1567774537-20003-14-git-send-email-john.garry@huawei.com
Signed-off-by: Xiang Chen <chenxiang66@hisilicon.com>
Signed-off-by: John Garry <john.garry@huawei.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
2019-09-06 15:55:37 +03:00
down ( & hisi_hba - > sem ) ;
2021-08-24 13:00:58 +03:00
if ( ! test_bit ( HISI_SAS_RESETTING_BIT , & hisi_hba - > flags ) ) {
2022-03-11 15:23:52 +03:00
hisi_sas_internal_task_abort_dev ( sas_dev , true ) ;
2016-08-24 14:05:48 +03:00
2017-12-08 20:16:37 +03:00
hisi_sas_dereg_device ( hisi_hba , device ) ;
2019-10-24 17:08:10 +03:00
ret = hisi_hba - > hw - > clear_itct ( hisi_hba , sas_dev ) ;
2017-12-08 20:16:37 +03:00
device - > lldd_dev = NULL ;
}
2017-06-14 18:33:32 +03:00
2017-12-08 20:16:34 +03:00
if ( hisi_hba - > hw - > free_device )
hisi_hba - > hw - > free_device ( sas_dev ) ;
2019-10-24 17:08:10 +03:00
/* Don't mark it as SAS_PHY_UNUSED if failed to clear ITCT */
if ( ! ret )
sas_dev - > dev_type = SAS_PHY_UNUSED ;
scsi: hisi_sas: Fix the conflict between device gone and host reset
When device gone, it will check whether it is during reset, if not, it will
send internal task abort. Before internal task abort returned, reset
begins, and it will check whether SAS_PHY_UNUSED is set, if not, it will
call hisi_sas_init_device(), but at that time domain_device may already be
freed or part of it is freed, so it may referenece null pointer in
hisi_sas_init_device(). It may occur as follows:
thread0 thread1
hisi_sas_dev_gone()
check whether in RESET(no)
internal task abort
reset prep
soft_reset
... (part of reset_done)
internal task abort failed
release resource anyway
clear_itct
device->lldd_dev=NULL
hisi_sas_reset_init_all_device
check sas_dev->dev_type is SAS_PHY_UNUSED and
!device
set dev_type SAS_PHY_UNUSED
sas_free_device
hisi_sas_init_device
...
Semaphore hisi_hba.sema is used to sync the processes of device gone and
host reset.
To solve the issue, expand the scope that semaphore protects and let them
never occur together.
And also some places will check whether domain_device is NULL to judge
whether the device is gone. So when device gone, need to clear
sas_dev->sas_device.
Link: https://lore.kernel.org/r/1567774537-20003-14-git-send-email-john.garry@huawei.com
Signed-off-by: Xiang Chen <chenxiang66@hisilicon.com>
Signed-off-by: John Garry <john.garry@huawei.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
2019-09-06 15:55:37 +03:00
sas_dev - > sas_device = NULL ;
up ( & hisi_hba - > sem ) ;
2015-11-17 19:50:51 +03:00
}
2015-11-17 19:50:49 +03:00
2019-01-25 17:22:34 +03:00
static int hisi_sas_phy_set_linkrate ( struct hisi_hba * hisi_hba , int phy_no ,
2018-05-21 13:09:13 +03:00
struct sas_phy_linkrates * r )
{
struct sas_phy_linkrates _r ;
struct hisi_sas_phy * phy = & hisi_hba - > phy [ phy_no ] ;
struct asd_sas_phy * sas_phy = & phy - > sas_phy ;
enum sas_linkrate min , max ;
2019-01-25 17:22:34 +03:00
if ( r - > minimum_linkrate > SAS_LINK_RATE_1_5_GBPS )
return - EINVAL ;
2018-05-21 13:09:13 +03:00
if ( r - > maximum_linkrate = = SAS_LINK_RATE_UNKNOWN ) {
max = sas_phy - > phy - > maximum_linkrate ;
min = r - > minimum_linkrate ;
} else if ( r - > minimum_linkrate = = SAS_LINK_RATE_UNKNOWN ) {
max = r - > maximum_linkrate ;
min = sas_phy - > phy - > minimum_linkrate ;
} else
2019-01-25 17:22:34 +03:00
return - EINVAL ;
2018-05-21 13:09:13 +03:00
_r . maximum_linkrate = max ;
_r . minimum_linkrate = min ;
2018-09-24 18:06:28 +03:00
sas_phy - > phy - > maximum_linkrate = max ;
sas_phy - > phy - > minimum_linkrate = min ;
2019-04-11 15:46:38 +03:00
hisi_sas_phy_enable ( hisi_hba , phy_no , 0 ) ;
2018-05-21 13:09:13 +03:00
msleep ( 100 ) ;
hisi_hba - > hw - > phy_set_linkrate ( hisi_hba , phy_no , & _r ) ;
2019-04-11 15:46:38 +03:00
hisi_sas_phy_enable ( hisi_hba , phy_no , 1 ) ;
2019-01-25 17:22:34 +03:00
return 0 ;
2018-05-21 13:09:13 +03:00
}
2015-11-17 19:50:57 +03:00
static int hisi_sas_control_phy ( struct asd_sas_phy * sas_phy , enum phy_func func ,
void * funcdata )
{
2021-10-12 15:26:26 +03:00
struct hisi_sas_phy * phy = container_of ( sas_phy ,
struct hisi_sas_phy , sas_phy ) ;
2015-11-17 19:50:57 +03:00
struct sas_ha_struct * sas_ha = sas_phy - > ha ;
struct hisi_hba * hisi_hba = sas_ha - > lldd_ha ;
2021-10-12 15:26:26 +03:00
struct device * dev = hisi_hba - > dev ;
DECLARE_COMPLETION_ONSTACK ( completion ) ;
2015-11-17 19:50:57 +03:00
int phy_no = sas_phy - > id ;
2021-10-12 15:26:26 +03:00
u8 sts = phy - > phy_attached ;
int ret = 0 ;
2021-12-15 17:37:38 +03:00
down ( & hisi_hba - > sem ) ;
2021-10-12 15:26:26 +03:00
phy - > reset_completion = & completion ;
2015-11-17 19:50:57 +03:00
switch ( func ) {
case PHY_FUNC_HARD_RESET :
hisi_hba - > hw - > phy_hard_reset ( hisi_hba , phy_no ) ;
break ;
case PHY_FUNC_LINK_RESET :
2019-04-11 15:46:38 +03:00
hisi_sas_phy_enable ( hisi_hba , phy_no , 0 ) ;
2017-03-22 20:25:23 +03:00
msleep ( 100 ) ;
2019-04-11 15:46:38 +03:00
hisi_sas_phy_enable ( hisi_hba , phy_no , 1 ) ;
2015-11-17 19:50:57 +03:00
break ;
case PHY_FUNC_DISABLE :
2019-04-11 15:46:38 +03:00
hisi_sas_phy_enable ( hisi_hba , phy_no , 0 ) ;
2021-10-12 15:26:26 +03:00
goto out ;
2015-11-17 19:50:57 +03:00
case PHY_FUNC_SET_LINK_RATE :
2021-10-12 15:26:26 +03:00
ret = hisi_sas_phy_set_linkrate ( hisi_hba , phy_no , funcdata ) ;
break ;
2017-08-10 19:09:29 +03:00
case PHY_FUNC_GET_EVENTS :
if ( hisi_hba - > hw - > get_events ) {
hisi_hba - > hw - > get_events ( hisi_hba , phy_no ) ;
2021-10-12 15:26:26 +03:00
goto out ;
2017-08-10 19:09:29 +03:00
}
2020-08-24 01:36:59 +03:00
fallthrough ;
2015-11-17 19:50:57 +03:00
case PHY_FUNC_RELEASE_SPINUP_HOLD :
default :
2021-10-12 15:26:26 +03:00
ret = - EOPNOTSUPP ;
goto out ;
2015-11-17 19:50:57 +03:00
}
2021-10-12 15:26:26 +03:00
2022-02-24 14:51:25 +03:00
if ( sts & & ! wait_for_completion_timeout ( & completion ,
HISI_SAS_WAIT_PHYUP_TIMEOUT ) ) {
2021-10-12 15:26:26 +03:00
dev_warn ( dev , " phy%d wait phyup timed out for func %d \n " ,
phy_no , func ) ;
if ( phy - > in_reset )
ret = - ETIMEDOUT ;
}
out :
phy - > reset_completion = NULL ;
2021-12-15 17:37:38 +03:00
up ( & hisi_hba - > sem ) ;
2021-10-12 15:26:26 +03:00
return ret ;
2015-11-17 19:50:57 +03:00
}
2015-11-17 19:50:52 +03:00
2017-03-22 20:25:20 +03:00
static void hisi_sas_fill_ata_reset_cmd ( struct ata_device * dev ,
bool reset , int pmp , u8 * fis )
{
struct ata_taskfile tf ;
ata_tf_init ( dev , & tf ) ;
if ( reset )
tf . ctl | = ATA_SRST ;
else
tf . ctl & = ~ ATA_SRST ;
tf . command = ATA_CMD_DEV_RESET ;
ata_tf_to_fis ( & tf , pmp , 0 , fis ) ;
}
static int hisi_sas_softreset_ata_disk ( struct domain_device * device )
{
u8 fis [ 20 ] = { 0 } ;
struct ata_port * ap = device - > sata_dev . ap ;
struct ata_link * link ;
int rc = TMF_RESP_FUNC_FAILED ;
struct hisi_hba * hisi_hba = dev_to_hisi_hba ( device ) ;
2017-06-14 18:33:17 +03:00
struct device * dev = hisi_hba - > dev ;
2017-03-22 20:25:20 +03:00
ata_for_each_link ( link , ap , EDGE ) {
int pmp = sata_srst_pmp ( link ) ;
hisi_sas_fill_ata_reset_cmd ( link - > device , 1 , pmp , fis ) ;
2022-02-22 15:50:59 +03:00
rc = sas_execute_ata_cmd ( device , fis , - 1 ) ;
2017-03-22 20:25:20 +03:00
if ( rc ! = TMF_RESP_FUNC_COMPLETE )
break ;
}
if ( rc = = TMF_RESP_FUNC_COMPLETE ) {
ata_for_each_link ( link , ap , EDGE ) {
int pmp = sata_srst_pmp ( link ) ;
hisi_sas_fill_ata_reset_cmd ( link - > device , 0 , pmp , fis ) ;
2022-02-22 15:50:59 +03:00
rc = sas_execute_ata_cmd ( device , fis , - 1 ) ;
2017-03-22 20:25:20 +03:00
if ( rc ! = TMF_RESP_FUNC_COMPLETE )
2021-04-06 14:48:31 +03:00
dev_err ( dev , " ata disk %016llx de-reset failed \n " ,
SAS_ADDR ( device - > sas_addr ) ) ;
2017-03-22 20:25:20 +03:00
}
} else {
2021-04-06 14:48:31 +03:00
dev_err ( dev , " ata disk %016llx reset failed \n " ,
SAS_ADDR ( device - > sas_addr ) ) ;
2017-03-22 20:25:20 +03:00
}
2018-05-09 18:10:49 +03:00
if ( rc = = TMF_RESP_FUNC_COMPLETE )
2017-03-22 20:25:20 +03:00
hisi_sas_release_task ( hisi_hba , device ) ;
return rc ;
}
2017-12-08 20:16:35 +03:00
static void hisi_sas_refresh_port_id ( struct hisi_hba * hisi_hba )
2017-08-10 19:09:26 +03:00
{
2017-12-08 20:16:35 +03:00
u32 state = hisi_hba - > hw - > get_phys_state ( hisi_hba ) ;
2017-08-10 19:09:26 +03:00
int i ;
for ( i = 0 ; i < HISI_SAS_MAX_DEVICES ; i + + ) {
2017-12-08 20:16:35 +03:00
struct hisi_sas_device * sas_dev = & hisi_hba - > devices [ i ] ;
struct domain_device * device = sas_dev - > sas_device ;
struct asd_sas_port * sas_port ;
struct hisi_sas_port * port ;
struct hisi_sas_phy * phy = NULL ;
struct asd_sas_phy * sas_phy ;
2017-08-10 19:09:26 +03:00
if ( ( sas_dev - > dev_type = = SAS_PHY_UNUSED )
2017-12-08 20:16:35 +03:00
| | ! device | | ! device - > port )
2017-08-10 19:09:26 +03:00
continue ;
2017-12-08 20:16:35 +03:00
sas_port = device - > port ;
port = to_hisi_sas_port ( sas_port ) ;
2021-12-20 14:21:28 +03:00
spin_lock ( & sas_port - > phy_list_lock ) ;
2017-12-08 20:16:35 +03:00
list_for_each_entry ( sas_phy , & sas_port - > phy_list , port_phy_el )
if ( state & BIT ( sas_phy - > id ) ) {
phy = sas_phy - > lldd_phy ;
break ;
}
2021-12-20 14:21:28 +03:00
spin_unlock ( & sas_port - > phy_list_lock ) ;
2017-12-08 20:16:35 +03:00
if ( phy ) {
port - > id = phy - > port_id ;
2017-08-10 19:09:26 +03:00
2017-12-08 20:16:35 +03:00
/* Update linkrate of directly attached device. */
if ( ! device - > parent )
device - > linkrate = phy - > sas_phy . linkrate ;
2017-08-10 19:09:26 +03:00
2017-12-08 20:16:35 +03:00
hisi_hba - > hw - > setup_itct ( hisi_hba , sas_dev ) ;
2023-01-04 07:03:20 +03:00
} else if ( ! port - > port_attached )
2017-12-08 20:16:35 +03:00
port - > id = 0xff ;
2017-08-10 19:09:26 +03:00
}
}
2019-09-06 15:55:34 +03:00
static void hisi_sas_rescan_topology ( struct hisi_hba * hisi_hba , u32 state )
2017-08-10 19:09:26 +03:00
{
struct asd_sas_port * _sas_port = NULL ;
int phy_no ;
for ( phy_no = 0 ; phy_no < hisi_hba - > n_phy ; phy_no + + ) {
struct hisi_sas_phy * phy = & hisi_hba - > phy [ phy_no ] ;
struct asd_sas_phy * sas_phy = & phy - > sas_phy ;
struct asd_sas_port * sas_port = sas_phy - > port ;
2019-11-12 12:30:59 +03:00
bool do_port_check = _sas_port ! = sas_port ;
2017-08-10 19:09:26 +03:00
if ( ! sas_phy - > phy - > enabled )
continue ;
/* Report PHY state change to libsas */
2017-12-08 20:16:35 +03:00
if ( state & BIT ( phy_no ) ) {
if ( do_port_check & & sas_port & & sas_port - > port_dev ) {
2017-08-10 19:09:26 +03:00
struct domain_device * dev = sas_port - > port_dev ;
_sas_port = sas_port ;
2019-06-10 15:41:41 +03:00
if ( dev_is_expander ( dev - > dev_type ) )
2021-01-18 13:09:49 +03:00
sas_notify_port_event ( sas_phy ,
2021-01-18 13:09:47 +03:00
PORTE_BROADCAST_RCVD ,
GFP_KERNEL ) ;
2017-08-10 19:09:26 +03:00
}
2019-04-11 15:46:37 +03:00
} else {
2021-01-18 13:09:47 +03:00
hisi_sas_phy_down ( hisi_hba , phy_no , 0 , GFP_KERNEL ) ;
2019-04-11 15:46:37 +03:00
}
2017-08-10 19:09:26 +03:00
}
}
2018-05-21 13:09:19 +03:00
static void hisi_sas_reset_init_all_devices ( struct hisi_hba * hisi_hba )
{
struct hisi_sas_device * sas_dev ;
struct domain_device * device ;
int i ;
for ( i = 0 ; i < HISI_SAS_MAX_DEVICES ; i + + ) {
sas_dev = & hisi_hba - > devices [ i ] ;
device = sas_dev - > sas_device ;
if ( ( sas_dev - > dev_type = = SAS_PHY_UNUSED ) | | ! device )
continue ;
hisi_sas_init_device ( device ) ;
}
}
2018-05-21 13:09:23 +03:00
static void hisi_sas_send_ata_reset_each_phy ( struct hisi_hba * hisi_hba ,
struct asd_sas_port * sas_port ,
struct domain_device * device )
{
struct ata_port * ap = device - > sata_dev . ap ;
struct device * dev = hisi_hba - > dev ;
int rc = TMF_RESP_FUNC_FAILED ;
struct ata_link * link ;
u8 fis [ 20 ] = { 0 } ;
2021-12-20 14:21:28 +03:00
int i ;
2018-05-21 13:09:23 +03:00
2021-12-20 14:21:28 +03:00
for ( i = 0 ; i < hisi_hba - > n_phy ; i + + ) {
if ( ! ( sas_port - > phy_mask & BIT ( i ) ) )
continue ;
2018-05-21 13:09:23 +03:00
ata_for_each_link ( link , ap , EDGE ) {
int pmp = sata_srst_pmp ( link ) ;
hisi_sas_fill_ata_reset_cmd ( link - > device , 1 , pmp , fis ) ;
2022-02-22 15:50:59 +03:00
rc = sas_execute_ata_cmd ( device , fis , i ) ;
2018-05-21 13:09:23 +03:00
if ( rc ! = TMF_RESP_FUNC_COMPLETE ) {
dev_err ( dev , " phy%d ata reset failed rc=%d \n " ,
2021-12-20 14:21:28 +03:00
i , rc ) ;
2018-05-21 13:09:23 +03:00
break ;
}
}
}
}
static void hisi_sas_terminate_stp_reject ( struct hisi_hba * hisi_hba )
{
struct device * dev = hisi_hba - > dev ;
int port_no , rc , i ;
for ( i = 0 ; i < HISI_SAS_MAX_DEVICES ; i + + ) {
struct hisi_sas_device * sas_dev = & hisi_hba - > devices [ i ] ;
struct domain_device * device = sas_dev - > sas_device ;
if ( ( sas_dev - > dev_type = = SAS_PHY_UNUSED ) | | ! device )
continue ;
2022-03-11 15:23:52 +03:00
rc = hisi_sas_internal_task_abort_dev ( sas_dev , false ) ;
2018-05-21 13:09:23 +03:00
if ( rc < 0 )
dev_err ( dev , " STP reject: abort dev failed %d \n " , rc ) ;
}
for ( port_no = 0 ; port_no < hisi_hba - > n_phy ; port_no + + ) {
struct hisi_sas_port * port = & hisi_hba - > port [ port_no ] ;
struct asd_sas_port * sas_port = & port - > sas_port ;
struct domain_device * port_dev = sas_port - > port_dev ;
struct domain_device * device ;
2019-06-10 15:41:41 +03:00
if ( ! port_dev | | ! dev_is_expander ( port_dev - > dev_type ) )
2018-05-21 13:09:23 +03:00
continue ;
/* Try to find a SATA device */
list_for_each_entry ( device , & sas_port - > dev_list ,
dev_list_node ) {
if ( dev_is_sata ( device ) ) {
hisi_sas_send_ata_reset_each_phy ( hisi_hba ,
sas_port ,
device ) ;
break ;
}
}
}
}
2018-07-18 17:14:28 +03:00
void hisi_sas_controller_reset_prepare ( struct hisi_hba * hisi_hba )
2017-03-22 20:25:18 +03:00
{
2017-08-10 19:09:26 +03:00
struct Scsi_Host * shost = hisi_hba - > shost ;
2017-03-22 20:25:18 +03:00
2018-07-18 17:14:28 +03:00
hisi_hba - > phy_state = hisi_hba - > hw - > get_phys_state ( hisi_hba ) ;
2017-03-22 20:25:18 +03:00
2017-08-10 19:09:26 +03:00
scsi_block_requests ( shost ) ;
2018-05-21 13:09:20 +03:00
hisi_hba - > hw - > wait_cmds_complete_timeout ( hisi_hba , 100 , 5000 ) ;
2024-01-22 09:25:47 +03:00
/*
* hisi_hba - > timer is only used for v1 / v2 hw , and check hw - > sht
* which is also only used for v1 / v2 hw to skip it for v3 hw
*/
if ( hisi_hba - > hw - > sht )
del_timer_sync ( & hisi_hba - > timer ) ;
2018-05-02 18:56:29 +03:00
2017-08-10 19:09:26 +03:00
set_bit ( HISI_SAS_REJECT_CMD_BIT , & hisi_hba - > flags ) ;
2018-07-18 17:14:28 +03:00
}
EXPORT_SYMBOL_GPL ( hisi_sas_controller_reset_prepare ) ;
2023-03-20 06:34:24 +03:00
static void hisi_sas_async_init_wait_phyup ( void * data , async_cookie_t cookie )
{
struct hisi_sas_phy * phy = data ;
struct hisi_hba * hisi_hba = phy - > hisi_hba ;
struct device * dev = hisi_hba - > dev ;
DECLARE_COMPLETION_ONSTACK ( completion ) ;
int phy_no = phy - > sas_phy . id ;
phy - > reset_completion = & completion ;
hisi_sas_phy_enable ( hisi_hba , phy_no , 1 ) ;
if ( ! wait_for_completion_timeout ( & completion ,
HISI_SAS_WAIT_PHYUP_TIMEOUT ) )
dev_warn ( dev , " phy%d wait phyup timed out \n " , phy_no ) ;
phy - > reset_completion = NULL ;
}
2018-07-18 17:14:28 +03:00
void hisi_sas_controller_reset_done ( struct hisi_hba * hisi_hba )
{
struct Scsi_Host * shost = hisi_hba - > shost ;
2023-03-20 06:34:24 +03:00
ASYNC_DOMAIN_EXCLUSIVE ( async ) ;
int phy_no ;
2017-08-10 19:09:26 +03:00
/* Init and wait for PHYs to come up and all libsas event finished. */
2023-03-20 06:34:24 +03:00
for ( phy_no = 0 ; phy_no < hisi_hba - > n_phy ; phy_no + + ) {
struct hisi_sas_phy * phy = & hisi_hba - > phy [ phy_no ] ;
if ( ! ( hisi_hba - > phy_state & BIT ( phy_no ) ) )
continue ;
async_schedule_domain ( hisi_sas_async_init_wait_phyup ,
phy , & async ) ;
}
async_synchronize_full_domain ( & async ) ;
2017-12-08 20:16:35 +03:00
hisi_sas_refresh_port_id ( hisi_hba ) ;
2018-05-31 15:50:45 +03:00
clear_bit ( HISI_SAS_REJECT_CMD_BIT , & hisi_hba - > flags ) ;
2018-05-21 13:09:23 +03:00
if ( hisi_hba - > reject_stp_links_msk )
hisi_sas_terminate_stp_reject ( hisi_hba ) ;
2018-05-21 13:09:19 +03:00
hisi_sas_reset_init_all_devices ( hisi_hba ) ;
2017-12-08 20:16:36 +03:00
scsi_unblock_requests ( shost ) ;
2021-08-24 13:00:58 +03:00
clear_bit ( HISI_SAS_RESETTING_BIT , & hisi_hba - > flags ) ;
2021-12-15 17:37:39 +03:00
up ( & hisi_hba - > sem ) ;
2017-08-10 19:09:26 +03:00
2020-10-02 17:30:38 +03:00
hisi_sas_rescan_topology ( hisi_hba , hisi_hba - > phy_state ) ;
2018-07-18 17:14:28 +03:00
}
EXPORT_SYMBOL_GPL ( hisi_sas_controller_reset_done ) ;
2021-04-06 14:48:29 +03:00
static int hisi_sas_controller_prereset ( struct hisi_hba * hisi_hba )
2018-07-18 17:14:28 +03:00
{
if ( ! hisi_hba - > hw - > soft_reset )
2023-12-14 06:45:13 +03:00
return - ENOENT ;
2018-07-18 17:14:28 +03:00
2021-12-15 17:37:39 +03:00
down ( & hisi_hba - > sem ) ;
if ( test_and_set_bit ( HISI_SAS_RESETTING_BIT , & hisi_hba - > flags ) ) {
up ( & hisi_hba - > sem ) ;
2023-12-14 06:45:13 +03:00
return - EPERM ;
2021-12-15 17:37:39 +03:00
}
2018-07-18 17:14:28 +03:00
2024-01-22 09:25:45 +03:00
if ( hisi_sas_debugfs_enable )
2021-04-06 14:48:29 +03:00
hisi_hba - > hw - > debugfs_snapshot_regs ( hisi_hba ) ;
return 0 ;
}
static int hisi_sas_controller_reset ( struct hisi_hba * hisi_hba )
{
struct device * dev = hisi_hba - > dev ;
struct Scsi_Host * shost = hisi_hba - > shost ;
int rc ;
2018-07-18 17:14:28 +03:00
dev_info ( dev , " controller resetting... \n " ) ;
hisi_sas_controller_reset_prepare ( hisi_hba ) ;
rc = hisi_hba - > hw - > soft_reset ( hisi_hba ) ;
if ( rc ) {
dev_warn ( dev , " controller reset failed (%d) \n " , rc ) ;
clear_bit ( HISI_SAS_REJECT_CMD_BIT , & hisi_hba - > flags ) ;
up ( & hisi_hba - > sem ) ;
scsi_unblock_requests ( shost ) ;
2021-08-24 13:00:58 +03:00
clear_bit ( HISI_SAS_RESETTING_BIT , & hisi_hba - > flags ) ;
2018-07-18 17:14:28 +03:00
return rc ;
}
2022-09-05 14:48:46 +03:00
clear_bit ( HISI_SAS_HW_FAULT_BIT , & hisi_hba - > flags ) ;
2018-07-18 17:14:28 +03:00
hisi_sas_controller_reset_done ( hisi_hba ) ;
2017-12-08 20:16:36 +03:00
dev_info ( dev , " controller reset complete \n " ) ;
2017-03-22 20:25:18 +03:00
2018-05-31 15:50:45 +03:00
return 0 ;
2017-03-22 20:25:18 +03:00
}
2015-11-17 19:50:56 +03:00
static int hisi_sas_abort_task ( struct sas_task * task )
{
2022-03-11 15:23:52 +03:00
struct hisi_sas_internal_abort_data internal_abort_data = { false } ;
2015-11-17 19:50:56 +03:00
struct domain_device * device = task - > dev ;
struct hisi_sas_device * sas_dev = device - > lldd_dev ;
2022-10-17 12:20:29 +03:00
struct hisi_sas_slot * slot = task - > lldd_task ;
2018-05-02 18:56:28 +03:00
struct hisi_hba * hisi_hba ;
struct device * dev ;
2015-11-17 19:50:56 +03:00
int rc = TMF_RESP_FUNC_FAILED ;
unsigned long flags ;
2018-05-02 18:56:28 +03:00
if ( ! sas_dev )
2015-11-17 19:50:56 +03:00
return TMF_RESP_FUNC_FAILED ;
2018-05-02 18:56:28 +03:00
hisi_hba = dev_to_hisi_hba ( task - > dev ) ;
dev = hisi_hba - > dev ;
2015-11-17 19:50:56 +03:00
2018-05-02 18:56:25 +03:00
spin_lock_irqsave ( & task - > task_state_lock , flags ) ;
2015-11-17 19:50:56 +03:00
if ( task - > task_state_flags & SAS_TASK_STATE_DONE ) {
2018-09-24 18:06:30 +03:00
struct hisi_sas_cq * cq ;
if ( slot ) {
/*
2023-03-07 09:09:14 +03:00
* sync irq or poll queue to avoid free ' ing task
2018-09-24 18:06:30 +03:00
* before using task in IO completion
*/
cq = & hisi_hba - > cq [ slot - > dlvry_queue ] ;
2023-03-07 09:09:14 +03:00
hisi_sas_sync_cq ( cq ) ;
2018-09-24 18:06:30 +03:00
}
2018-05-02 18:56:25 +03:00
spin_unlock_irqrestore ( & task - > task_state_lock , flags ) ;
2015-11-17 19:50:56 +03:00
rc = TMF_RESP_FUNC_COMPLETE ;
goto out ;
}
2018-05-02 18:56:25 +03:00
task - > task_state_flags | = SAS_TASK_STATE_ABORTED ;
spin_unlock_irqrestore ( & task - > task_state_lock , flags ) ;
2015-11-17 19:50:56 +03:00
2023-12-14 06:45:14 +03:00
if ( ! slot )
goto out ;
if ( task - > task_proto & SAS_PROTOCOL_SSP ) {
2018-12-06 16:34:40 +03:00
u16 tag = slot - > idx ;
2017-03-22 20:25:25 +03:00
int rc2 ;
2015-11-17 19:50:56 +03:00
2022-02-17 18:42:45 +03:00
rc = sas_abort_task ( task , tag ) ;
2022-03-11 15:23:52 +03:00
rc2 = sas_execute_internal_abort_single ( device , tag ,
slot - > dlvry_queue , & internal_abort_data ) ;
scsi: hisi_sas: judge result of internal abort
Normally, hardware should ensure that internal abort timeout will never
happen. If happen, it would be an SoC failure. What's more, HW will not
process any other commands if an internal abort hasn't return CQ, and they
will time out also.
So, we should judge the result of internal abort in SCSI EH, if it is failed,
we should give up to do TMF/softreset and return failure to the upper layer
directly.
This patch do following things to achieve this:
1. When internal abort timeout happened, we set return value to -EIO in
hisi_sas_internal_task_abort().
2. If prep_abort() is not support, let hisi_sas_internal_task_abort() return
TMF_RESP_FUNC_FAILED.
3. If hisi_sas_internal_task_abort() return an negative number, it can be
thought that it not executed properly or internal abort timeout. Then we
won't do behind TMF or softreset, and return failure directly.
Signed-off-by: Xiaofei Tan <tanxiaofei@huawei.com>
Signed-off-by: John Garry <john.garry@huawei.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
2017-12-08 20:16:46 +03:00
if ( rc2 < 0 ) {
dev_err ( dev , " abort task: internal abort (%d) \n " , rc2 ) ;
return TMF_RESP_FUNC_FAILED ;
}
2017-03-22 20:25:25 +03:00
/*
* If the TMF finds that the IO is not in the device and also
* the internal abort does not succeed , then it is safe to
* free the slot .
* Note : if the internal abort succeeds then the slot
* will have already been completed
*/
if ( rc = = TMF_RESP_FUNC_COMPLETE & & rc2 ! = TMF_RESP_FUNC_SUCC ) {
2018-05-09 18:10:49 +03:00
if ( task - > lldd_task )
scsi: hisi_sas: Grab sas_dev lock when traversing the members of sas_dev.list
When freeing slots in function slot_complete_v3_hw(), it is possible that
sas_dev.list is being traversed elsewhere, and it may trigger a NULL
pointer exception, such as follows:
==>cq thread ==>scsi_eh_6
==>scsi_error_handler()
==>sas_eh_handle_sas_errors()
==>sas_scsi_find_task()
==>lldd_abort_task()
==>slot_complete_v3_hw() ==>hisi_sas_abort_task()
==>hisi_sas_slot_task_free() ==>dereg_device_v3_hw()
==>list_del_init() ==>list_for_each_entry_safe()
[ 7165.434918] sas: Enter sas_scsi_recover_host busy: 32 failed: 32
[ 7165.434926] sas: trying to find task 0x00000000769b5ba5
[ 7165.434927] sas: sas_scsi_find_task: aborting task 0x00000000769b5ba5
[ 7165.434940] hisi_sas_v3_hw 0000:b4:02.0: slot complete: task(00000000769b5ba5) aborted
[ 7165.434964] hisi_sas_v3_hw 0000:b4:02.0: slot complete: task(00000000c9f7aa07) ignored
[ 7165.434965] hisi_sas_v3_hw 0000:b4:02.0: slot complete: task(00000000e2a1cf01) ignored
[ 7165.434968] Unable to handle kernel NULL pointer dereference at virtual address 0000000000000000
[ 7165.434972] hisi_sas_v3_hw 0000:b4:02.0: slot complete: task(0000000022d52d93) ignored
[ 7165.434975] hisi_sas_v3_hw 0000:b4:02.0: slot complete: task(0000000066a7516c) ignored
[ 7165.434976] Mem abort info:
[ 7165.434982] ESR = 0x96000004
[ 7165.434991] Exception class = DABT (current EL), IL = 32 bits
[ 7165.434992] SET = 0, FnV = 0
[ 7165.434993] EA = 0, S1PTW = 0
[ 7165.434994] Data abort info:
[ 7165.434994] ISV = 0, ISS = 0x00000004
[ 7165.434995] CM = 0, WnR = 0
[ 7165.434997] user pgtable: 4k pages, 48-bit VAs, pgdp = 00000000f29543f2
[ 7165.434998] [0000000000000000] pgd=0000000000000000
[ 7165.435003] Internal error: Oops: 96000004 [#1] SMP
[ 7165.439863] Process scsi_eh_6 (pid: 4109, stack limit = 0x00000000c43818d5)
[ 7165.468862] pstate: 00c00009 (nzcv daif +PAN +UAO)
[ 7165.473637] pc : dereg_device_v3_hw+0x68/0xa8 [hisi_sas_v3_hw]
[ 7165.479443] lr : dereg_device_v3_hw+0x2c/0xa8 [hisi_sas_v3_hw]
[ 7165.485247] sp : ffff00001d623bc0
[ 7165.488546] x29: ffff00001d623bc0 x28: ffffa027d03b9508
[ 7165.493835] x27: ffff80278ed50af0 x26: ffffa027dd31e0a8
[ 7165.499123] x25: ffffa027d9b27f88 x24: ffffa027d9b209f8
[ 7165.504411] x23: ffffa027c45b0d60 x22: ffff80278ec07c00
[ 7165.509700] x21: 0000000000000008 x20: ffffa027d9b209f8
[ 7165.514988] x19: ffffa027d9b27f88 x18: ffffffffffffffff
[ 7165.520276] x17: 0000000000000000 x16: 0000000000000000
[ 7165.525564] x15: ffff0000091d9708 x14: ffff0000093b7dc8
[ 7165.530852] x13: ffff0000093b7a23 x12: 6e7265746e692067
[ 7165.536140] x11: 0000000000000000 x10: 0000000000000bb0
[ 7165.541429] x9 : ffff00001d6238f0 x8 : ffffa027d877af00
[ 7165.546718] x7 : ffffa027d6329600 x6 : ffff7e809f58ca00
[ 7165.552006] x5 : 0000000000001f8a x4 : 000000000000088e
[ 7165.557295] x3 : ffffa027d9b27fa8 x2 : 0000000000000000
[ 7165.562583] x1 : 0000000000000000 x0 : 000000003000188e
[ 7165.567872] Call trace:
[ 7165.570309] dereg_device_v3_hw+0x68/0xa8 [hisi_sas_v3_hw]
[ 7165.575775] hisi_sas_abort_task+0x248/0x358 [hisi_sas_main]
[ 7165.581415] sas_eh_handle_sas_errors+0x258/0x8e0 [libsas]
[ 7165.586876] sas_scsi_recover_host+0x134/0x458 [libsas]
[ 7165.592082] scsi_error_handler+0xb4/0x488
[ 7165.596163] kthread+0x134/0x138
[ 7165.599380] ret_from_fork+0x10/0x18
[ 7165.602940] Code: d5033e9f b9000040 aa0103e2 eb03003f (f9400021)
[ 7165.609004] kernel fault(0x1) notification starting on CPU 75
[ 7165.700728] ---[ end trace fc042cbbea224efc ]---
[ 7165.705326] Kernel panic - not syncing: Fatal exception
To fix the issue, grab sas_dev lock when traversing the members of
sas_dev.list in dereg_device_v3_hw() and hisi_sas_release_tasks() to avoid
concurrency of adding and deleting member. When function
hisi_sas_release_tasks() calls hisi_sas_do_release_task() to free slot, the
lock cannot be grabbed again in hisi_sas_slot_task_free(), then a bool
parameter need_lock is added.
Signed-off-by: Xingui Yang <yangxingui@huawei.com>
Signed-off-by: Xiang Chen <chenxiang66@hisilicon.com>
Link: https://lore.kernel.org/r/1679283265-115066-2-git-send-email-chenxiang66@hisilicon.com
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
2023-03-20 06:34:22 +03:00
hisi_sas_do_release_task ( hisi_hba , task , slot , true ) ;
2015-11-17 19:50:56 +03:00
}
} else if ( task - > task_proto & SAS_PROTOCOL_SATA | |
task - > task_proto & SAS_PROTOCOL_STP ) {
if ( task - > dev - > dev_type = = SAS_SATA_DEV ) {
2022-10-17 12:20:30 +03:00
struct ata_queued_cmd * qc = task - > uldd_task ;
2022-03-11 15:23:52 +03:00
rc = hisi_sas_internal_task_abort_dev ( sas_dev , false ) ;
scsi: hisi_sas: judge result of internal abort
Normally, hardware should ensure that internal abort timeout will never
happen. If happen, it would be an SoC failure. What's more, HW will not
process any other commands if an internal abort hasn't return CQ, and they
will time out also.
So, we should judge the result of internal abort in SCSI EH, if it is failed,
we should give up to do TMF/softreset and return failure to the upper layer
directly.
This patch do following things to achieve this:
1. When internal abort timeout happened, we set return value to -EIO in
hisi_sas_internal_task_abort().
2. If prep_abort() is not support, let hisi_sas_internal_task_abort() return
TMF_RESP_FUNC_FAILED.
3. If hisi_sas_internal_task_abort() return an negative number, it can be
thought that it not executed properly or internal abort timeout. Then we
won't do behind TMF or softreset, and return failure directly.
Signed-off-by: Xiaofei Tan <tanxiaofei@huawei.com>
Signed-off-by: John Garry <john.garry@huawei.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
2017-12-08 20:16:46 +03:00
if ( rc < 0 ) {
dev_err ( dev , " abort task: internal abort failed \n " ) ;
goto out ;
}
2017-06-14 18:33:32 +03:00
hisi_sas_dereg_device ( hisi_hba , device ) ;
2022-10-17 12:20:30 +03:00
/*
* If an ATA internal command times out in ATA EH , it
* need to execute soft reset , so check the scsicmd
*/
if ( ( sas_dev - > dev_status = = HISI_SAS_DEV_NCQ_ERR ) & &
qc & & qc - > scsicmd ) {
scsi: hisi_sas: Grab sas_dev lock when traversing the members of sas_dev.list
When freeing slots in function slot_complete_v3_hw(), it is possible that
sas_dev.list is being traversed elsewhere, and it may trigger a NULL
pointer exception, such as follows:
==>cq thread ==>scsi_eh_6
==>scsi_error_handler()
==>sas_eh_handle_sas_errors()
==>sas_scsi_find_task()
==>lldd_abort_task()
==>slot_complete_v3_hw() ==>hisi_sas_abort_task()
==>hisi_sas_slot_task_free() ==>dereg_device_v3_hw()
==>list_del_init() ==>list_for_each_entry_safe()
[ 7165.434918] sas: Enter sas_scsi_recover_host busy: 32 failed: 32
[ 7165.434926] sas: trying to find task 0x00000000769b5ba5
[ 7165.434927] sas: sas_scsi_find_task: aborting task 0x00000000769b5ba5
[ 7165.434940] hisi_sas_v3_hw 0000:b4:02.0: slot complete: task(00000000769b5ba5) aborted
[ 7165.434964] hisi_sas_v3_hw 0000:b4:02.0: slot complete: task(00000000c9f7aa07) ignored
[ 7165.434965] hisi_sas_v3_hw 0000:b4:02.0: slot complete: task(00000000e2a1cf01) ignored
[ 7165.434968] Unable to handle kernel NULL pointer dereference at virtual address 0000000000000000
[ 7165.434972] hisi_sas_v3_hw 0000:b4:02.0: slot complete: task(0000000022d52d93) ignored
[ 7165.434975] hisi_sas_v3_hw 0000:b4:02.0: slot complete: task(0000000066a7516c) ignored
[ 7165.434976] Mem abort info:
[ 7165.434982] ESR = 0x96000004
[ 7165.434991] Exception class = DABT (current EL), IL = 32 bits
[ 7165.434992] SET = 0, FnV = 0
[ 7165.434993] EA = 0, S1PTW = 0
[ 7165.434994] Data abort info:
[ 7165.434994] ISV = 0, ISS = 0x00000004
[ 7165.434995] CM = 0, WnR = 0
[ 7165.434997] user pgtable: 4k pages, 48-bit VAs, pgdp = 00000000f29543f2
[ 7165.434998] [0000000000000000] pgd=0000000000000000
[ 7165.435003] Internal error: Oops: 96000004 [#1] SMP
[ 7165.439863] Process scsi_eh_6 (pid: 4109, stack limit = 0x00000000c43818d5)
[ 7165.468862] pstate: 00c00009 (nzcv daif +PAN +UAO)
[ 7165.473637] pc : dereg_device_v3_hw+0x68/0xa8 [hisi_sas_v3_hw]
[ 7165.479443] lr : dereg_device_v3_hw+0x2c/0xa8 [hisi_sas_v3_hw]
[ 7165.485247] sp : ffff00001d623bc0
[ 7165.488546] x29: ffff00001d623bc0 x28: ffffa027d03b9508
[ 7165.493835] x27: ffff80278ed50af0 x26: ffffa027dd31e0a8
[ 7165.499123] x25: ffffa027d9b27f88 x24: ffffa027d9b209f8
[ 7165.504411] x23: ffffa027c45b0d60 x22: ffff80278ec07c00
[ 7165.509700] x21: 0000000000000008 x20: ffffa027d9b209f8
[ 7165.514988] x19: ffffa027d9b27f88 x18: ffffffffffffffff
[ 7165.520276] x17: 0000000000000000 x16: 0000000000000000
[ 7165.525564] x15: ffff0000091d9708 x14: ffff0000093b7dc8
[ 7165.530852] x13: ffff0000093b7a23 x12: 6e7265746e692067
[ 7165.536140] x11: 0000000000000000 x10: 0000000000000bb0
[ 7165.541429] x9 : ffff00001d6238f0 x8 : ffffa027d877af00
[ 7165.546718] x7 : ffffa027d6329600 x6 : ffff7e809f58ca00
[ 7165.552006] x5 : 0000000000001f8a x4 : 000000000000088e
[ 7165.557295] x3 : ffffa027d9b27fa8 x2 : 0000000000000000
[ 7165.562583] x1 : 0000000000000000 x0 : 000000003000188e
[ 7165.567872] Call trace:
[ 7165.570309] dereg_device_v3_hw+0x68/0xa8 [hisi_sas_v3_hw]
[ 7165.575775] hisi_sas_abort_task+0x248/0x358 [hisi_sas_main]
[ 7165.581415] sas_eh_handle_sas_errors+0x258/0x8e0 [libsas]
[ 7165.586876] sas_scsi_recover_host+0x134/0x458 [libsas]
[ 7165.592082] scsi_error_handler+0xb4/0x488
[ 7165.596163] kthread+0x134/0x138
[ 7165.599380] ret_from_fork+0x10/0x18
[ 7165.602940] Code: d5033e9f b9000040 aa0103e2 eb03003f (f9400021)
[ 7165.609004] kernel fault(0x1) notification starting on CPU 75
[ 7165.700728] ---[ end trace fc042cbbea224efc ]---
[ 7165.705326] Kernel panic - not syncing: Fatal exception
To fix the issue, grab sas_dev lock when traversing the members of
sas_dev.list in dereg_device_v3_hw() and hisi_sas_release_tasks() to avoid
concurrency of adding and deleting member. When function
hisi_sas_release_tasks() calls hisi_sas_do_release_task() to free slot, the
lock cannot be grabbed again in hisi_sas_slot_task_free(), then a bool
parameter need_lock is added.
Signed-off-by: Xingui Yang <yangxingui@huawei.com>
Signed-off-by: Xiang Chen <chenxiang66@hisilicon.com>
Link: https://lore.kernel.org/r/1679283265-115066-2-git-send-email-chenxiang66@hisilicon.com
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
2023-03-20 06:34:22 +03:00
hisi_sas_do_release_task ( hisi_hba , task , slot , true ) ;
2022-10-17 12:20:30 +03:00
rc = TMF_RESP_FUNC_COMPLETE ;
} else {
rc = hisi_sas_softreset_ata_disk ( device ) ;
}
2015-11-17 19:50:56 +03:00
}
2023-12-14 06:45:14 +03:00
} else if ( task - > task_proto & SAS_PROTOCOL_SMP ) {
2016-08-24 14:05:49 +03:00
/* SMP */
u32 tag = slot - > idx ;
2018-09-24 18:06:30 +03:00
struct hisi_sas_cq * cq = & hisi_hba - > cq [ slot - > dlvry_queue ] ;
2015-11-17 19:50:56 +03:00
2022-03-11 15:23:52 +03:00
rc = sas_execute_internal_abort_single ( device ,
tag , slot - > dlvry_queue ,
& internal_abort_data ) ;
scsi: hisi_sas: judge result of internal abort
Normally, hardware should ensure that internal abort timeout will never
happen. If happen, it would be an SoC failure. What's more, HW will not
process any other commands if an internal abort hasn't return CQ, and they
will time out also.
So, we should judge the result of internal abort in SCSI EH, if it is failed,
we should give up to do TMF/softreset and return failure to the upper layer
directly.
This patch do following things to achieve this:
1. When internal abort timeout happened, we set return value to -EIO in
hisi_sas_internal_task_abort().
2. If prep_abort() is not support, let hisi_sas_internal_task_abort() return
TMF_RESP_FUNC_FAILED.
3. If hisi_sas_internal_task_abort() return an negative number, it can be
thought that it not executed properly or internal abort timeout. Then we
won't do behind TMF or softreset, and return failure directly.
Signed-off-by: Xiaofei Tan <tanxiaofei@huawei.com>
Signed-off-by: John Garry <john.garry@huawei.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
2017-12-08 20:16:46 +03:00
if ( ( ( rc < 0 ) | | ( rc = = TMF_RESP_FUNC_FAILED ) ) & &
2018-09-24 18:06:30 +03:00
task - > lldd_task ) {
/*
2023-03-07 09:09:14 +03:00
* sync irq or poll queue to avoid free ' ing task
2018-09-24 18:06:30 +03:00
* before using task in IO completion
*/
2023-03-07 09:09:14 +03:00
hisi_sas_sync_cq ( cq ) ;
2018-09-24 18:06:30 +03:00
slot - > task = NULL ;
}
2015-11-17 19:50:56 +03:00
}
out :
if ( rc ! = TMF_RESP_FUNC_COMPLETE )
dev_notice ( dev , " abort task: rc=%d \n " , rc ) ;
return rc ;
}
static int hisi_sas_abort_task_set ( struct domain_device * device , u8 * lun )
{
2022-03-11 15:23:52 +03:00
struct hisi_sas_device * sas_dev = device - > lldd_dev ;
2017-12-08 20:16:47 +03:00
struct hisi_hba * hisi_hba = dev_to_hisi_hba ( device ) ;
struct device * dev = hisi_hba - > dev ;
2019-04-11 15:46:44 +03:00
int rc ;
2017-12-08 20:16:47 +03:00
2022-03-11 15:23:52 +03:00
rc = hisi_sas_internal_task_abort_dev ( sas_dev , false ) ;
2017-12-08 20:16:47 +03:00
if ( rc < 0 ) {
dev_err ( dev , " abort task set: internal abort rc=%d \n " , rc ) ;
return TMF_RESP_FUNC_FAILED ;
}
hisi_sas_dereg_device ( hisi_hba , device ) ;
2015-11-17 19:50:56 +03:00
2022-02-17 18:42:41 +03:00
rc = sas_abort_task_set ( device , lun ) ;
2018-05-09 18:10:49 +03:00
if ( rc = = TMF_RESP_FUNC_COMPLETE )
2017-12-08 20:16:47 +03:00
hisi_sas_release_task ( hisi_hba , device ) ;
2015-11-17 19:50:56 +03:00
return rc ;
}
static int hisi_sas_debug_I_T_nexus_reset ( struct domain_device * device )
{
2018-05-21 13:09:25 +03:00
struct sas_phy * local_phy = sas_get_local_phy ( device ) ;
2019-02-28 17:51:01 +03:00
struct hisi_sas_device * sas_dev = device - > lldd_dev ;
2018-05-21 13:09:25 +03:00
struct hisi_hba * hisi_hba = dev_to_hisi_hba ( device ) ;
struct sas_ha_struct * sas_ha = & hisi_hba - > sha ;
2019-02-28 17:51:01 +03:00
int rc , reset_type ;
2018-05-21 13:09:25 +03:00
2019-09-06 15:55:27 +03:00
if ( ! local_phy - > enabled ) {
sas_put_local_phy ( local_phy ) ;
return - ENODEV ;
}
2018-05-21 13:09:25 +03:00
if ( scsi_is_sas_phy_local ( local_phy ) ) {
2019-08-05 16:48:03 +03:00
struct asd_sas_phy * sas_phy =
sas_ha - > sas_phy [ local_phy - > number ] ;
struct hisi_sas_phy * phy =
container_of ( sas_phy , struct hisi_sas_phy , sas_phy ) ;
2021-10-12 15:26:26 +03:00
unsigned long flags ;
spin_lock_irqsave ( & phy - > lock , flags ) ;
2018-05-21 13:09:25 +03:00
phy - > in_reset = 1 ;
2021-10-12 15:26:26 +03:00
spin_unlock_irqrestore ( & phy - > lock , flags ) ;
2018-05-21 13:09:25 +03:00
}
2019-02-28 17:51:01 +03:00
reset_type = ( sas_dev - > dev_status = = HISI_SAS_DEV_INIT | |
2019-09-06 15:55:26 +03:00
! dev_is_sata ( device ) ) ? true : false ;
2019-02-28 17:51:01 +03:00
2018-05-21 13:09:25 +03:00
rc = sas_phy_reset ( local_phy , reset_type ) ;
sas_put_local_phy ( local_phy ) ;
if ( scsi_is_sas_phy_local ( local_phy ) ) {
2019-08-05 16:48:03 +03:00
struct asd_sas_phy * sas_phy =
sas_ha - > sas_phy [ local_phy - > number ] ;
struct hisi_sas_phy * phy =
container_of ( sas_phy , struct hisi_sas_phy , sas_phy ) ;
2018-05-21 13:09:25 +03:00
unsigned long flags ;
spin_lock_irqsave ( & phy - > lock , flags ) ;
phy - > in_reset = 0 ;
spin_unlock_irqrestore ( & phy - > lock , flags ) ;
/* report PHY down if timed out */
2021-10-12 15:26:26 +03:00
if ( rc = = - ETIMEDOUT )
2021-01-18 13:09:47 +03:00
hisi_sas_phy_down ( hisi_hba , sas_phy - > id , 0 , GFP_KERNEL ) ;
2022-05-12 14:15:33 +03:00
return rc ;
}
2022-11-18 11:37:13 +03:00
/* Remote phy */
2022-05-12 14:15:33 +03:00
if ( rc )
return rc ;
if ( dev_is_sata ( device ) ) {
2022-11-18 11:37:13 +03:00
struct ata_link * link = & device - > sata_dev . ap - > link ;
rc = ata_wait_after_reset ( link , HISI_SAS_WAIT_PHYUP_TIMEOUT ,
smp_ata_check_ready_type ) ;
2022-05-12 14:15:33 +03:00
} else {
msleep ( 2000 ) ;
2019-02-28 17:51:01 +03:00
}
2018-05-21 13:09:25 +03:00
2015-11-17 19:50:56 +03:00
return rc ;
}
static int hisi_sas_I_T_nexus_reset ( struct domain_device * device )
{
2022-03-11 15:23:52 +03:00
struct hisi_sas_device * sas_dev = device - > lldd_dev ;
2015-11-17 19:50:56 +03:00
struct hisi_hba * hisi_hba = dev_to_hisi_hba ( device ) ;
scsi: hisi_sas: judge result of internal abort
Normally, hardware should ensure that internal abort timeout will never
happen. If happen, it would be an SoC failure. What's more, HW will not
process any other commands if an internal abort hasn't return CQ, and they
will time out also.
So, we should judge the result of internal abort in SCSI EH, if it is failed,
we should give up to do TMF/softreset and return failure to the upper layer
directly.
This patch do following things to achieve this:
1. When internal abort timeout happened, we set return value to -EIO in
hisi_sas_internal_task_abort().
2. If prep_abort() is not support, let hisi_sas_internal_task_abort() return
TMF_RESP_FUNC_FAILED.
3. If hisi_sas_internal_task_abort() return an negative number, it can be
thought that it not executed properly or internal abort timeout. Then we
won't do behind TMF or softreset, and return failure directly.
Signed-off-by: Xiaofei Tan <tanxiaofei@huawei.com>
Signed-off-by: John Garry <john.garry@huawei.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
2017-12-08 20:16:46 +03:00
struct device * dev = hisi_hba - > dev ;
2019-02-06 13:52:56 +03:00
int rc ;
2015-11-17 19:50:56 +03:00
2022-10-17 12:20:30 +03:00
if ( sas_dev - > dev_status = = HISI_SAS_DEV_NCQ_ERR )
sas_dev - > dev_status = HISI_SAS_DEV_NORMAL ;
2022-03-11 15:23:52 +03:00
rc = hisi_sas_internal_task_abort_dev ( sas_dev , false ) ;
scsi: hisi_sas: judge result of internal abort
Normally, hardware should ensure that internal abort timeout will never
happen. If happen, it would be an SoC failure. What's more, HW will not
process any other commands if an internal abort hasn't return CQ, and they
will time out also.
So, we should judge the result of internal abort in SCSI EH, if it is failed,
we should give up to do TMF/softreset and return failure to the upper layer
directly.
This patch do following things to achieve this:
1. When internal abort timeout happened, we set return value to -EIO in
hisi_sas_internal_task_abort().
2. If prep_abort() is not support, let hisi_sas_internal_task_abort() return
TMF_RESP_FUNC_FAILED.
3. If hisi_sas_internal_task_abort() return an negative number, it can be
thought that it not executed properly or internal abort timeout. Then we
won't do behind TMF or softreset, and return failure directly.
Signed-off-by: Xiaofei Tan <tanxiaofei@huawei.com>
Signed-off-by: John Garry <john.garry@huawei.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
2017-12-08 20:16:46 +03:00
if ( rc < 0 ) {
dev_err ( dev , " I_T nexus reset: internal abort (%d) \n " , rc ) ;
return TMF_RESP_FUNC_FAILED ;
}
2017-06-14 18:33:32 +03:00
hisi_sas_dereg_device ( hisi_hba , device ) ;
2021-10-12 15:26:28 +03:00
rc = hisi_sas_debug_I_T_nexus_reset ( device ) ;
if ( rc = = TMF_RESP_FUNC_COMPLETE & & dev_is_sata ( device ) ) {
struct sas_phy * local_phy ;
2019-03-20 13:21:34 +03:00
rc = hisi_sas_softreset_ata_disk ( device ) ;
2021-10-12 15:26:28 +03:00
switch ( rc ) {
case - ECOMM :
rc = - ENODEV ;
break ;
case TMF_RESP_FUNC_FAILED :
case - EMSGSIZE :
case - EIO :
local_phy = sas_get_local_phy ( device ) ;
rc = sas_phy_enable ( local_phy , 0 ) ;
if ( ! rc ) {
local_phy - > enabled = 0 ;
dev_err ( dev , " Disabled local phy of ATA disk %016llx due to softreset fail (%d) \n " ,
SAS_ADDR ( device - > sas_addr ) , rc ) ;
rc = - ENODEV ;
}
sas_put_local_phy ( local_phy ) ;
break ;
default :
break ;
}
2019-03-20 13:21:34 +03:00
}
2018-05-09 18:10:49 +03:00
if ( ( rc = = TMF_RESP_FUNC_COMPLETE ) | | ( rc = = - ENODEV ) )
2017-03-22 20:25:28 +03:00
hisi_sas_release_task ( hisi_hba , device ) ;
2018-05-09 18:10:49 +03:00
2017-03-22 20:25:28 +03:00
return rc ;
2015-11-17 19:50:56 +03:00
}
static int hisi_sas_lu_reset ( struct domain_device * device , u8 * lun )
{
struct hisi_sas_device * sas_dev = device - > lldd_dev ;
struct hisi_hba * hisi_hba = dev_to_hisi_hba ( device ) ;
2017-06-14 18:33:17 +03:00
struct device * dev = hisi_hba - > dev ;
2015-11-17 19:50:56 +03:00
int rc = TMF_RESP_FUNC_FAILED ;
2019-08-05 16:48:12 +03:00
/* Clear internal IO and then lu reset */
2022-03-11 15:23:52 +03:00
rc = hisi_sas_internal_task_abort_dev ( sas_dev , false ) ;
2019-08-05 16:48:12 +03:00
if ( rc < 0 ) {
dev_err ( dev , " lu_reset: internal abort failed \n " ) ;
goto out ;
}
hisi_sas_dereg_device ( hisi_hba , device ) ;
2017-03-22 20:25:26 +03:00
if ( dev_is_sata ( device ) ) {
struct sas_phy * phy ;
phy = sas_get_local_phy ( device ) ;
2019-09-06 15:55:26 +03:00
rc = sas_phy_reset ( phy , true ) ;
2017-03-22 20:25:26 +03:00
2018-05-09 18:10:49 +03:00
if ( rc = = 0 )
2017-03-22 20:25:26 +03:00
hisi_sas_release_task ( hisi_hba , device ) ;
sas_put_local_phy ( phy ) ;
} else {
2022-02-17 18:42:43 +03:00
rc = sas_lu_reset ( device , lun ) ;
2018-05-09 18:10:49 +03:00
if ( rc = = TMF_RESP_FUNC_COMPLETE )
2017-03-22 20:25:26 +03:00
hisi_sas_release_task ( hisi_hba , device ) ;
}
out :
2017-03-22 20:25:37 +03:00
if ( rc ! = TMF_RESP_FUNC_COMPLETE )
2017-06-14 18:33:12 +03:00
dev_err ( dev , " lu_reset: for device[%d]:rc= %d \n " ,
2017-03-22 20:25:37 +03:00
sas_dev - > device_id , rc ) ;
2015-11-17 19:50:56 +03:00
return rc ;
}
2021-06-07 12:29:36 +03:00
static void hisi_sas_async_I_T_nexus_reset ( void * data , async_cookie_t cookie )
{
struct domain_device * device = data ;
struct hisi_hba * hisi_hba = dev_to_hisi_hba ( device ) ;
int rc ;
rc = hisi_sas_debug_I_T_nexus_reset ( device ) ;
if ( rc ! = TMF_RESP_FUNC_COMPLETE )
dev_info ( hisi_hba - > dev , " I_T_nexus reset fail for dev:%016llx rc=%d \n " ,
SAS_ADDR ( device - > sas_addr ) , rc ) ;
}
2017-03-22 20:25:35 +03:00
static int hisi_sas_clear_nexus_ha ( struct sas_ha_struct * sas_ha )
{
struct hisi_hba * hisi_hba = sas_ha - > lldd_ha ;
2017-12-08 20:16:38 +03:00
HISI_SAS_DECLARE_RST_WORK_ON_STACK ( r ) ;
2021-06-07 12:29:36 +03:00
ASYNC_DOMAIN_EXCLUSIVE ( async ) ;
2022-11-18 11:37:11 +03:00
int i ;
2017-03-22 20:25:35 +03:00
2017-12-08 20:16:38 +03:00
queue_work ( hisi_hba - > wq , & r . work ) ;
wait_for_completion ( r . completion ) ;
2022-11-18 11:37:11 +03:00
if ( ! r . done )
return TMF_RESP_FUNC_FAILED ;
2018-05-31 15:50:47 +03:00
for ( i = 0 ; i < HISI_SAS_MAX_DEVICES ; i + + ) {
struct hisi_sas_device * sas_dev = & hisi_hba - > devices [ i ] ;
struct domain_device * device = sas_dev - > sas_device ;
if ( ( sas_dev - > dev_type = = SAS_PHY_UNUSED ) | | ! device | |
2019-06-10 15:41:41 +03:00
dev_is_expander ( device - > dev_type ) )
2018-05-31 15:50:47 +03:00
continue ;
2021-06-07 12:29:36 +03:00
async_schedule_domain ( hisi_sas_async_I_T_nexus_reset ,
device , & async ) ;
2018-05-31 15:50:47 +03:00
}
2021-06-07 12:29:36 +03:00
async_synchronize_full_domain ( & async ) ;
2018-05-31 15:50:47 +03:00
hisi_sas_release_tasks ( hisi_hba ) ;
2017-12-08 20:16:38 +03:00
2022-11-18 11:37:11 +03:00
return TMF_RESP_FUNC_COMPLETE ;
2017-03-22 20:25:35 +03:00
}
2015-11-17 19:50:56 +03:00
static int hisi_sas_query_task ( struct sas_task * task )
{
int rc = TMF_RESP_FUNC_FAILED ;
if ( task - > lldd_task & & task - > task_proto & SAS_PROTOCOL_SSP ) {
struct hisi_sas_slot * slot = task - > lldd_task ;
u32 tag = slot - > idx ;
2022-02-17 18:42:44 +03:00
rc = sas_query_task ( task , tag ) ;
2015-11-17 19:50:56 +03:00
switch ( rc ) {
/* The task is still in Lun, release it then */
case TMF_RESP_FUNC_SUCC :
/* The task is not in Lun or failed, reset the phy */
case TMF_RESP_FUNC_FAILED :
case TMF_RESP_FUNC_COMPLETE :
break ;
2016-11-07 15:48:35 +03:00
default :
rc = TMF_RESP_FUNC_FAILED ;
break ;
2015-11-17 19:50:56 +03:00
}
}
return rc ;
}
2022-03-11 15:23:52 +03:00
static bool hisi_sas_internal_abort_timeout ( struct sas_task * task ,
void * data )
2016-08-24 14:05:47 +03:00
{
struct domain_device * device = task - > dev ;
2022-03-11 15:23:52 +03:00
struct hisi_hba * hisi_hba = dev_to_hisi_hba ( device ) ;
struct hisi_sas_internal_abort_data * timeout = data ;
2016-08-24 14:05:47 +03:00
2024-01-22 09:25:45 +03:00
if ( hisi_sas_debugfs_enable ) {
2024-01-22 09:25:44 +03:00
/*
* If timeout occurs in device gone scenario , to avoid
* circular dependency like :
* hisi_sas_dev_gone ( ) - > down ( ) - > . . . - >
* hisi_sas_internal_abort_timeout ( ) - > down ( ) .
*/
if ( ! timeout - > rst_ha_timeout )
down ( & hisi_hba - > sem ) ;
2023-09-13 05:15:26 +03:00
hisi_hba - > hw - > debugfs_snapshot_regs ( hisi_hba ) ;
2024-01-22 09:25:44 +03:00
if ( ! timeout - > rst_ha_timeout )
up ( & hisi_hba - > sem ) ;
2023-09-13 05:15:26 +03:00
}
2017-03-22 20:25:25 +03:00
2022-03-11 15:23:52 +03:00
if ( task - > task_state_flags & SAS_TASK_STATE_DONE ) {
pr_err ( " Internal abort: timeout %016llx \n " ,
SAS_ADDR ( device - > sas_addr ) ) ;
} else {
struct hisi_sas_slot * slot = task - > lldd_task ;
2016-08-24 14:05:47 +03:00
2022-03-11 15:23:52 +03:00
set_bit ( HISI_SAS_HW_FAULT_BIT , & hisi_hba - > flags ) ;
2016-08-24 14:05:47 +03:00
2022-03-11 15:23:52 +03:00
if ( slot ) {
struct hisi_sas_cq * cq =
& hisi_hba - > cq [ slot - > dlvry_queue ] ;
/*
2023-03-07 09:09:14 +03:00
* sync irq or poll queue to avoid free ' ing task
2022-03-11 15:23:52 +03:00
* before using task in IO completion
*/
2023-03-07 09:09:14 +03:00
hisi_sas_sync_cq ( cq ) ;
2022-03-11 15:23:52 +03:00
slot - > task = NULL ;
}
scsi: hisi_sas: Issue internal abort on all relevant queues
To support queue mapped to a CPU, it needs to be ensured that issuing an
internal abort is safe, in that it is guaranteed that an internal abort is
processed for a single IO or a device after all the relevant command(s)
which it is attempting to abort have been processed by the controller.
Currently we only deliver commands for any device on a single queue to
solve this problem, as we know that commands issued on the same queue will
be processed in order, and we will not have a scenario where the internal
abort is racing against a command(s) which it is trying to abort.
To enqueue commands on queue mapped to a CPU, choosing a queue for an
command is based on the associated queue for the current CPU, so this is
not safe for internal abort since it would definitely not be guaranteed
that commands for the command devices are issued on the same queue.
To solve this issue, we take a bludgeoning approach, and issue a separate
internal abort on any queue(s) relevant to the command or device, in that
we will be guaranteed that at least one of these internal aborts will be
received last in the controller.
So, for aborting a single command, we can just force the internal abort to
be issued on the same queue as the command which we are trying to abort.
For aborting all commands associated with a device, we issue a separate
internal abort on all relevant queues. Issuing multiple internal aborts in
this fashion would have not side affect.
Signed-off-by: John Garry <john.garry@huawei.com>
Signed-off-by: Xiang Chen <chenxiang66@hisilicon.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
2019-02-06 13:52:54 +03:00
2022-03-11 15:23:52 +03:00
if ( timeout - > rst_ha_timeout ) {
pr_err ( " Internal abort: timeout and not done %016llx. Queuing reset. \n " ,
SAS_ADDR ( device - > sas_addr ) ) ;
queue_work ( hisi_hba - > wq , & hisi_hba - > rst_work ) ;
} else {
pr_err ( " Internal abort: timeout and not done %016llx. \n " ,
SAS_ADDR ( device - > sas_addr ) ) ;
scsi: hisi_sas: Issue internal abort on all relevant queues
To support queue mapped to a CPU, it needs to be ensured that issuing an
internal abort is safe, in that it is guaranteed that an internal abort is
processed for a single IO or a device after all the relevant command(s)
which it is attempting to abort have been processed by the controller.
Currently we only deliver commands for any device on a single queue to
solve this problem, as we know that commands issued on the same queue will
be processed in order, and we will not have a scenario where the internal
abort is racing against a command(s) which it is trying to abort.
To enqueue commands on queue mapped to a CPU, choosing a queue for an
command is based on the associated queue for the current CPU, so this is
not safe for internal abort since it would definitely not be guaranteed
that commands for the command devices are issued on the same queue.
To solve this issue, we take a bludgeoning approach, and issue a separate
internal abort on any queue(s) relevant to the command or device, in that
we will be guaranteed that at least one of these internal aborts will be
received last in the controller.
So, for aborting a single command, we can just force the internal abort to
be issued on the same queue as the command which we are trying to abort.
For aborting all commands associated with a device, we issue a separate
internal abort on all relevant queues. Issuing multiple internal aborts in
this fashion would have not side affect.
Signed-off-by: John Garry <john.garry@huawei.com>
Signed-off-by: Xiang Chen <chenxiang66@hisilicon.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
2019-02-06 13:52:54 +03:00
}
2022-03-11 15:23:52 +03:00
return true ;
scsi: hisi_sas: Issue internal abort on all relevant queues
To support queue mapped to a CPU, it needs to be ensured that issuing an
internal abort is safe, in that it is guaranteed that an internal abort is
processed for a single IO or a device after all the relevant command(s)
which it is attempting to abort have been processed by the controller.
Currently we only deliver commands for any device on a single queue to
solve this problem, as we know that commands issued on the same queue will
be processed in order, and we will not have a scenario where the internal
abort is racing against a command(s) which it is trying to abort.
To enqueue commands on queue mapped to a CPU, choosing a queue for an
command is based on the associated queue for the current CPU, so this is
not safe for internal abort since it would definitely not be guaranteed
that commands for the command devices are issued on the same queue.
To solve this issue, we take a bludgeoning approach, and issue a separate
internal abort on any queue(s) relevant to the command or device, in that
we will be guaranteed that at least one of these internal aborts will be
received last in the controller.
So, for aborting a single command, we can just force the internal abort to
be issued on the same queue as the command which we are trying to abort.
For aborting all commands associated with a device, we issue a separate
internal abort on all relevant queues. Issuing multiple internal aborts in
this fashion would have not side affect.
Signed-off-by: John Garry <john.garry@huawei.com>
Signed-off-by: Xiang Chen <chenxiang66@hisilicon.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
2019-02-06 13:52:54 +03:00
}
2022-03-11 15:23:52 +03:00
return false ;
scsi: hisi_sas: Issue internal abort on all relevant queues
To support queue mapped to a CPU, it needs to be ensured that issuing an
internal abort is safe, in that it is guaranteed that an internal abort is
processed for a single IO or a device after all the relevant command(s)
which it is attempting to abort have been processed by the controller.
Currently we only deliver commands for any device on a single queue to
solve this problem, as we know that commands issued on the same queue will
be processed in order, and we will not have a scenario where the internal
abort is racing against a command(s) which it is trying to abort.
To enqueue commands on queue mapped to a CPU, choosing a queue for an
command is based on the associated queue for the current CPU, so this is
not safe for internal abort since it would definitely not be guaranteed
that commands for the command devices are issued on the same queue.
To solve this issue, we take a bludgeoning approach, and issue a separate
internal abort on any queue(s) relevant to the command or device, in that
we will be guaranteed that at least one of these internal aborts will be
received last in the controller.
So, for aborting a single command, we can just force the internal abort to
be issued on the same queue as the command which we are trying to abort.
For aborting all commands associated with a device, we issue a separate
internal abort on all relevant queues. Issuing multiple internal aborts in
this fashion would have not side affect.
Signed-off-by: John Garry <john.garry@huawei.com>
Signed-off-by: Xiang Chen <chenxiang66@hisilicon.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
2019-02-06 13:52:54 +03:00
}
2015-11-17 19:50:52 +03:00
static void hisi_sas_port_formed ( struct asd_sas_phy * sas_phy )
{
hisi_sas_port_notify_formed ( sas_phy ) ;
}
2018-01-17 19:46:53 +03:00
static int hisi_sas_write_gpio ( struct sas_ha_struct * sha , u8 reg_type ,
u8 reg_index , u8 reg_count , u8 * write_data )
{
struct hisi_hba * hisi_hba = sha - > lldd_ha ;
if ( ! hisi_hba - > hw - > write_gpio )
return - EOPNOTSUPP ;
return hisi_hba - > hw - > write_gpio ( hisi_hba , reg_type ,
reg_index , reg_count , write_data ) ;
}
2015-11-17 19:50:52 +03:00
static void hisi_sas_phy_disconnected ( struct hisi_sas_phy * phy )
{
scsi: hisi_sas: Set PHY linkrate when disconnected
When the PHY comes down, we currently do not set the negotiated linkrate:
root@(none)$ pwd
/sys/class/sas_phy/phy-0:0
root@(none)$ more enable
1
root@(none)$ more negotiated_linkrate
12.0 Gbit
root@(none)$ echo 0 > enable
root@(none)$ more negotiated_linkrate
12.0 Gbit
root@(none)$
This patch fixes the driver code to set it properly when the PHY comes
down.
If the PHY had been enabled, then set unknown; otherwise, flag as disabled.
The logical place to set the negotiated linkrate for this scenario is PHY
down routine, which is called from the PHY down ISR.
However, it is not possible to know if the PHY comes down due to PHY
disable or loss of link, as sas_phy.enabled member is not set until after
the transport disable routine is complete, which races with the PHY down
ISR.
As an imperfect solution, use sas_phy_data.enable as the flag to know if
the PHY is down due to disable. It's imperfect, as sas_phy_data is internal
to libsas.
I can't see another way without adding a new field to hisi_sas_phy and
managing it, or changing SCSI SAS transport.
Signed-off-by: John Garry <john.garry@huawei.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
2019-02-28 17:51:00 +03:00
struct asd_sas_phy * sas_phy = & phy - > sas_phy ;
struct sas_phy * sphy = sas_phy - > phy ;
2019-04-11 15:46:38 +03:00
unsigned long flags ;
scsi: hisi_sas: Set PHY linkrate when disconnected
When the PHY comes down, we currently do not set the negotiated linkrate:
root@(none)$ pwd
/sys/class/sas_phy/phy-0:0
root@(none)$ more enable
1
root@(none)$ more negotiated_linkrate
12.0 Gbit
root@(none)$ echo 0 > enable
root@(none)$ more negotiated_linkrate
12.0 Gbit
root@(none)$
This patch fixes the driver code to set it properly when the PHY comes
down.
If the PHY had been enabled, then set unknown; otherwise, flag as disabled.
The logical place to set the negotiated linkrate for this scenario is PHY
down routine, which is called from the PHY down ISR.
However, it is not possible to know if the PHY comes down due to PHY
disable or loss of link, as sas_phy.enabled member is not set until after
the transport disable routine is complete, which races with the PHY down
ISR.
As an imperfect solution, use sas_phy_data.enable as the flag to know if
the PHY is down due to disable. It's imperfect, as sas_phy_data is internal
to libsas.
I can't see another way without adding a new field to hisi_sas_phy and
managing it, or changing SCSI SAS transport.
Signed-off-by: John Garry <john.garry@huawei.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
2019-02-28 17:51:00 +03:00
2015-11-17 19:50:52 +03:00
phy - > phy_attached = 0 ;
phy - > phy_type = 0 ;
phy - > port = NULL ;
scsi: hisi_sas: Set PHY linkrate when disconnected
When the PHY comes down, we currently do not set the negotiated linkrate:
root@(none)$ pwd
/sys/class/sas_phy/phy-0:0
root@(none)$ more enable
1
root@(none)$ more negotiated_linkrate
12.0 Gbit
root@(none)$ echo 0 > enable
root@(none)$ more negotiated_linkrate
12.0 Gbit
root@(none)$
This patch fixes the driver code to set it properly when the PHY comes
down.
If the PHY had been enabled, then set unknown; otherwise, flag as disabled.
The logical place to set the negotiated linkrate for this scenario is PHY
down routine, which is called from the PHY down ISR.
However, it is not possible to know if the PHY comes down due to PHY
disable or loss of link, as sas_phy.enabled member is not set until after
the transport disable routine is complete, which races with the PHY down
ISR.
As an imperfect solution, use sas_phy_data.enable as the flag to know if
the PHY is down due to disable. It's imperfect, as sas_phy_data is internal
to libsas.
I can't see another way without adding a new field to hisi_sas_phy and
managing it, or changing SCSI SAS transport.
Signed-off-by: John Garry <john.garry@huawei.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
2019-02-28 17:51:00 +03:00
2019-04-11 15:46:38 +03:00
spin_lock_irqsave ( & phy - > lock , flags ) ;
if ( phy - > enable )
scsi: hisi_sas: Set PHY linkrate when disconnected
When the PHY comes down, we currently do not set the negotiated linkrate:
root@(none)$ pwd
/sys/class/sas_phy/phy-0:0
root@(none)$ more enable
1
root@(none)$ more negotiated_linkrate
12.0 Gbit
root@(none)$ echo 0 > enable
root@(none)$ more negotiated_linkrate
12.0 Gbit
root@(none)$
This patch fixes the driver code to set it properly when the PHY comes
down.
If the PHY had been enabled, then set unknown; otherwise, flag as disabled.
The logical place to set the negotiated linkrate for this scenario is PHY
down routine, which is called from the PHY down ISR.
However, it is not possible to know if the PHY comes down due to PHY
disable or loss of link, as sas_phy.enabled member is not set until after
the transport disable routine is complete, which races with the PHY down
ISR.
As an imperfect solution, use sas_phy_data.enable as the flag to know if
the PHY is down due to disable. It's imperfect, as sas_phy_data is internal
to libsas.
I can't see another way without adding a new field to hisi_sas_phy and
managing it, or changing SCSI SAS transport.
Signed-off-by: John Garry <john.garry@huawei.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
2019-02-28 17:51:00 +03:00
sphy - > negotiated_linkrate = SAS_LINK_RATE_UNKNOWN ;
else
sphy - > negotiated_linkrate = SAS_PHY_DISABLED ;
2019-04-11 15:46:38 +03:00
spin_unlock_irqrestore ( & phy - > lock , flags ) ;
2015-11-17 19:50:52 +03:00
}
2021-01-18 13:09:47 +03:00
void hisi_sas_phy_down ( struct hisi_hba * hisi_hba , int phy_no , int rdy ,
gfp_t gfp_flags )
2015-11-17 19:50:52 +03:00
{
struct hisi_sas_phy * phy = & hisi_hba - > phy [ phy_no ] ;
struct asd_sas_phy * sas_phy = & phy - > sas_phy ;
2018-05-21 13:09:25 +03:00
struct device * dev = hisi_hba - > dev ;
2015-11-17 19:50:52 +03:00
if ( rdy ) {
/* Phy down but ready */
2021-01-18 13:09:47 +03:00
hisi_sas_bytes_dmaed ( hisi_hba , phy_no , gfp_flags ) ;
2015-11-17 19:50:52 +03:00
hisi_sas_port_notify_formed ( sas_phy ) ;
} else {
struct hisi_sas_port * port = phy - > port ;
2021-08-24 13:00:58 +03:00
if ( test_bit ( HISI_SAS_RESETTING_BIT , & hisi_hba - > flags ) | |
2018-05-31 15:50:46 +03:00
phy - > in_reset ) {
2018-05-21 13:09:25 +03:00
dev_info ( dev , " ignore flutter phy%d down \n " , phy_no ) ;
return ;
}
2015-11-17 19:50:52 +03:00
/* Phy down and not ready */
2021-01-18 13:09:49 +03:00
sas_notify_phy_event ( sas_phy , PHYE_LOSS_OF_SIGNAL , gfp_flags ) ;
2015-11-17 19:50:52 +03:00
sas_phy_disconnected ( sas_phy ) ;
if ( port ) {
if ( phy - > phy_type & PORT_TYPE_SAS ) {
int port_id = port - > id ;
if ( ! hisi_hba - > hw - > get_wideport_bitmap ( hisi_hba ,
port_id ) )
port - > port_attached = 0 ;
} else if ( phy - > phy_type & PORT_TYPE_SATA )
port - > port_attached = 0 ;
}
hisi_sas_phy_disconnected ( phy ) ;
}
}
EXPORT_SYMBOL_GPL ( hisi_sas_phy_down ) ;
2022-09-05 14:48:48 +03:00
void hisi_sas_phy_bcast ( struct hisi_sas_phy * phy )
{
struct asd_sas_phy * sas_phy = & phy - > sas_phy ;
struct hisi_hba * hisi_hba = phy - > hisi_hba ;
if ( test_bit ( HISI_SAS_RESETTING_BIT , & hisi_hba - > flags ) )
return ;
sas_notify_port_event ( sas_phy , PORTE_BROADCAST_RCVD , GFP_ATOMIC ) ;
}
EXPORT_SYMBOL_GPL ( hisi_sas_phy_bcast ) ;
2019-04-11 15:46:36 +03:00
int hisi_sas_host_reset ( struct Scsi_Host * shost , int reset_type )
{
struct hisi_hba * hisi_hba = shost_priv ( shost ) ;
if ( reset_type ! = SCSI_ADAPTER_RESET )
return - EOPNOTSUPP ;
queue_work ( hisi_hba - > wq , & hisi_hba - > rst_work ) ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( hisi_sas_host_reset ) ;
2017-06-14 18:33:20 +03:00
struct scsi_transport_template * hisi_sas_stt ;
EXPORT_SYMBOL_GPL ( hisi_sas_stt ) ;
2015-11-17 19:50:30 +03:00
static struct sas_domain_function_template hisi_sas_transport_ops = {
2015-11-17 19:50:51 +03:00
. lldd_dev_found = hisi_sas_dev_found ,
. lldd_dev_gone = hisi_sas_dev_gone ,
2015-11-17 19:50:49 +03:00
. lldd_execute_task = hisi_sas_queue_command ,
2015-11-17 19:50:57 +03:00
. lldd_control_phy = hisi_sas_control_phy ,
2015-11-17 19:50:56 +03:00
. lldd_abort_task = hisi_sas_abort_task ,
. lldd_abort_task_set = hisi_sas_abort_task_set ,
. lldd_I_T_nexus_reset = hisi_sas_I_T_nexus_reset ,
. lldd_lu_reset = hisi_sas_lu_reset ,
. lldd_query_task = hisi_sas_query_task ,
2018-09-25 05:56:51 +03:00
. lldd_clear_nexus_ha = hisi_sas_clear_nexus_ha ,
2015-11-17 19:50:52 +03:00
. lldd_port_formed = hisi_sas_port_formed ,
2018-09-25 05:56:51 +03:00
. lldd_write_gpio = hisi_sas_write_gpio ,
2022-02-17 18:42:40 +03:00
. lldd_tmf_aborted = hisi_sas_tmf_aborted ,
2022-03-11 15:23:52 +03:00
. lldd_abort_timeout = hisi_sas_internal_abort_timeout ,
2015-11-17 19:50:30 +03:00
} ;
2017-03-22 20:25:18 +03:00
void hisi_sas_init_mem ( struct hisi_hba * hisi_hba )
{
2019-08-05 16:47:58 +03:00
int i , s , j , max_command_entries = HISI_SAS_MAX_COMMANDS ;
2019-01-25 17:22:37 +03:00
struct hisi_sas_breakpoint * sata_breakpoint = hisi_hba - > sata_breakpoint ;
2017-03-22 20:25:18 +03:00
for ( i = 0 ; i < hisi_hba - > queue_count ; i + + ) {
struct hisi_sas_cq * cq = & hisi_hba - > cq [ i ] ;
struct hisi_sas_dq * dq = & hisi_hba - > dq [ i ] ;
2019-01-25 17:22:37 +03:00
struct hisi_sas_cmd_hdr * cmd_hdr = hisi_hba - > cmd_hdr [ i ] ;
s = sizeof ( struct hisi_sas_cmd_hdr ) ;
for ( j = 0 ; j < HISI_SAS_QUEUE_SLOTS ; j + + )
memset ( & cmd_hdr [ j ] , 0 , s ) ;
2017-03-22 20:25:18 +03:00
dq - > wr_point = 0 ;
s = hisi_hba - > hw - > complete_hdr_size * HISI_SAS_QUEUE_SLOTS ;
memset ( hisi_hba - > complete_hdr [ i ] , 0 , s ) ;
cq - > rd_point = 0 ;
}
s = sizeof ( struct hisi_sas_initial_fis ) * hisi_hba - > n_phy ;
memset ( hisi_hba - > initial_fis , 0 , s ) ;
s = max_command_entries * sizeof ( struct hisi_sas_iost ) ;
memset ( hisi_hba - > iost , 0 , s ) ;
s = max_command_entries * sizeof ( struct hisi_sas_breakpoint ) ;
memset ( hisi_hba - > breakpoint , 0 , s ) ;
2019-01-25 17:22:37 +03:00
s = sizeof ( struct hisi_sas_sata_breakpoint ) ;
for ( j = 0 ; j < HISI_SAS_MAX_ITCT_ENTRIES ; j + + )
memset ( & sata_breakpoint [ j ] , 0 , s ) ;
2017-03-22 20:25:18 +03:00
}
EXPORT_SYMBOL_GPL ( hisi_sas_init_mem ) ;
2019-01-25 17:22:33 +03:00
int hisi_sas_alloc ( struct hisi_hba * hisi_hba )
2015-11-17 19:50:34 +03:00
{
2017-06-14 18:33:17 +03:00
struct device * dev = hisi_hba - > dev ;
2019-08-05 16:47:58 +03:00
int i , j , s , max_command_entries = HISI_SAS_MAX_COMMANDS ;
scsi: hisi_sas: Pre-allocate slot DMA buffers
Currently the driver spends much time allocating and freeing the slot DMA
buffer for command delivery/completion. To boost the performance,
pre-allocate the buffers for all IPTT. The downside of this approach is
that we are reallocating all buffer memory upfront, so hog memory which we
may not need.
However, the current method - DMA buffer pool - also caches all buffers and
does not free them until the pool is destroyed, so is not exactly efficient
either.
On top of this, since the slot DMA buffer is slightly bigger than a 4K
page, we need to allocate 2x4K pages per buffer (for 4K page kernel), which
is quite wasteful. For 64K page size this is not such an issue.
So, for the 4K page case, in order to make memory usage more efficient,
pre-allocating larger blocks of DMA memory for the buffers can be more
efficient.
To make DMA memory usage most efficient, we would choose a single
contiguous DMA memory block, but this could use up all the DMA memory in
the system (when CMA enabled and no IOMMU), or we may just not be able to
allocate a DMA buffer large enough when no CMA or IOMMU.
To decide the block size we use the LCM (least common multiple) of the
buffer size and the page size. We roundup(64) to ensure the LCM is not too
large, even though a little memory may be wasted per block.
So, with this, the total memory requirement is about is about 17MB for 4096
max IPTT.
Previously (for 4K pages case), it would be 32MB (for all slots
allocated).
With this change, the relative increase of IOPS for bs=4K read when
PAGE_SIZE=4K and PAGE_SIZE=64K is as follows:
IODEPTH 4K PAGE_SIZE 64K PAGE_SIZE
32 56% 47%
64 53% 44%
128 64% 43%
256 67% 45%
Signed-off-by: Xiang Chen <chenxiang66@hisilicon.com>
Signed-off-by: John Garry <john.garry@huawei.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
2018-05-31 15:50:48 +03:00
int max_command_entries_ru , sz_slot_buf_ru ;
int blk_cnt , slots_per_blk ;
2015-11-17 19:50:34 +03:00
2018-05-31 15:50:44 +03:00
sema_init ( & hisi_hba - > sem , 1 ) ;
2015-11-17 19:50:43 +03:00
spin_lock_init ( & hisi_hba - > lock ) ;
2015-11-17 19:50:42 +03:00
for ( i = 0 ; i < hisi_hba - > n_phy ; i + + ) {
hisi_sas_phy_init ( hisi_hba , i ) ;
hisi_hba - > port [ i ] . port_attached = 0 ;
hisi_hba - > port [ i ] . id = - 1 ;
}
2015-11-17 19:50:41 +03:00
for ( i = 0 ; i < HISI_SAS_MAX_DEVICES ; i + + ) {
hisi_hba - > devices [ i ] . dev_type = SAS_PHY_UNUSED ;
hisi_hba - > devices [ i ] . device_id = i ;
2019-02-28 17:51:01 +03:00
hisi_hba - > devices [ i ] . dev_status = HISI_SAS_DEV_INIT ;
2015-11-17 19:50:41 +03:00
}
2015-11-17 19:50:34 +03:00
for ( i = 0 ; i < hisi_hba - > queue_count ; i + + ) {
2015-11-17 19:50:37 +03:00
struct hisi_sas_cq * cq = & hisi_hba - > cq [ i ] ;
2016-09-06 18:36:12 +03:00
struct hisi_sas_dq * dq = & hisi_hba - > dq [ i ] ;
2015-11-17 19:50:37 +03:00
/* Completion queue structure */
cq - > id = i ;
cq - > hisi_hba = hisi_hba ;
2023-03-07 09:09:13 +03:00
spin_lock_init ( & cq - > poll_lock ) ;
2015-11-17 19:50:37 +03:00
2016-09-06 18:36:12 +03:00
/* Delivery queue structure */
2017-12-08 20:16:32 +03:00
spin_lock_init ( & dq - > lock ) ;
2018-05-09 18:10:48 +03:00
INIT_LIST_HEAD ( & dq - > list ) ;
2016-09-06 18:36:12 +03:00
dq - > id = i ;
dq - > hisi_hba = hisi_hba ;
2015-11-17 19:50:34 +03:00
/* Delivery queue */
s = sizeof ( struct hisi_sas_cmd_hdr ) * HISI_SAS_QUEUE_SLOTS ;
2018-05-31 15:50:42 +03:00
hisi_hba - > cmd_hdr [ i ] = dmam_alloc_coherent ( dev , s ,
& hisi_hba - > cmd_hdr_dma [ i ] ,
GFP_KERNEL ) ;
2015-11-17 19:50:34 +03:00
if ( ! hisi_hba - > cmd_hdr [ i ] )
goto err_out ;
/* Completion queue */
s = hisi_hba - > hw - > complete_hdr_size * HISI_SAS_QUEUE_SLOTS ;
2018-05-31 15:50:42 +03:00
hisi_hba - > complete_hdr [ i ] = dmam_alloc_coherent ( dev , s ,
& hisi_hba - > complete_hdr_dma [ i ] ,
GFP_KERNEL ) ;
2015-11-17 19:50:34 +03:00
if ( ! hisi_hba - > complete_hdr [ i ] )
goto err_out ;
}
s = HISI_SAS_MAX_ITCT_ENTRIES * sizeof ( struct hisi_sas_itct ) ;
2018-05-31 15:50:42 +03:00
hisi_hba - > itct = dmam_alloc_coherent ( dev , s , & hisi_hba - > itct_dma ,
2019-08-05 16:48:10 +03:00
GFP_KERNEL ) ;
2015-11-17 19:50:34 +03:00
if ( ! hisi_hba - > itct )
goto err_out ;
2016-01-25 21:47:03 +03:00
hisi_hba - > slot_info = devm_kcalloc ( dev , max_command_entries ,
2015-11-17 19:50:34 +03:00
sizeof ( struct hisi_sas_slot ) ,
GFP_KERNEL ) ;
if ( ! hisi_hba - > slot_info )
goto err_out ;
scsi: hisi_sas: Pre-allocate slot DMA buffers
Currently the driver spends much time allocating and freeing the slot DMA
buffer for command delivery/completion. To boost the performance,
pre-allocate the buffers for all IPTT. The downside of this approach is
that we are reallocating all buffer memory upfront, so hog memory which we
may not need.
However, the current method - DMA buffer pool - also caches all buffers and
does not free them until the pool is destroyed, so is not exactly efficient
either.
On top of this, since the slot DMA buffer is slightly bigger than a 4K
page, we need to allocate 2x4K pages per buffer (for 4K page kernel), which
is quite wasteful. For 64K page size this is not such an issue.
So, for the 4K page case, in order to make memory usage more efficient,
pre-allocating larger blocks of DMA memory for the buffers can be more
efficient.
To make DMA memory usage most efficient, we would choose a single
contiguous DMA memory block, but this could use up all the DMA memory in
the system (when CMA enabled and no IOMMU), or we may just not be able to
allocate a DMA buffer large enough when no CMA or IOMMU.
To decide the block size we use the LCM (least common multiple) of the
buffer size and the page size. We roundup(64) to ensure the LCM is not too
large, even though a little memory may be wasted per block.
So, with this, the total memory requirement is about is about 17MB for 4096
max IPTT.
Previously (for 4K pages case), it would be 32MB (for all slots
allocated).
With this change, the relative increase of IOPS for bs=4K read when
PAGE_SIZE=4K and PAGE_SIZE=64K is as follows:
IODEPTH 4K PAGE_SIZE 64K PAGE_SIZE
32 56% 47%
64 53% 44%
128 64% 43%
256 67% 45%
Signed-off-by: Xiang Chen <chenxiang66@hisilicon.com>
Signed-off-by: John Garry <john.garry@huawei.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
2018-05-31 15:50:48 +03:00
/* roundup to avoid overly large block size */
max_command_entries_ru = roundup ( max_command_entries , 64 ) ;
2019-02-06 13:52:51 +03:00
if ( hisi_hba - > prot_mask & HISI_SAS_DIX_PROT_MASK )
sz_slot_buf_ru = sizeof ( struct hisi_sas_slot_dif_buf_table ) ;
else
sz_slot_buf_ru = sizeof ( struct hisi_sas_slot_buf_table ) ;
sz_slot_buf_ru = roundup ( sz_slot_buf_ru , 64 ) ;
2019-08-05 16:48:05 +03:00
s = max ( lcm ( max_command_entries_ru , sz_slot_buf_ru ) , PAGE_SIZE ) ;
scsi: hisi_sas: Pre-allocate slot DMA buffers
Currently the driver spends much time allocating and freeing the slot DMA
buffer for command delivery/completion. To boost the performance,
pre-allocate the buffers for all IPTT. The downside of this approach is
that we are reallocating all buffer memory upfront, so hog memory which we
may not need.
However, the current method - DMA buffer pool - also caches all buffers and
does not free them until the pool is destroyed, so is not exactly efficient
either.
On top of this, since the slot DMA buffer is slightly bigger than a 4K
page, we need to allocate 2x4K pages per buffer (for 4K page kernel), which
is quite wasteful. For 64K page size this is not such an issue.
So, for the 4K page case, in order to make memory usage more efficient,
pre-allocating larger blocks of DMA memory for the buffers can be more
efficient.
To make DMA memory usage most efficient, we would choose a single
contiguous DMA memory block, but this could use up all the DMA memory in
the system (when CMA enabled and no IOMMU), or we may just not be able to
allocate a DMA buffer large enough when no CMA or IOMMU.
To decide the block size we use the LCM (least common multiple) of the
buffer size and the page size. We roundup(64) to ensure the LCM is not too
large, even though a little memory may be wasted per block.
So, with this, the total memory requirement is about is about 17MB for 4096
max IPTT.
Previously (for 4K pages case), it would be 32MB (for all slots
allocated).
With this change, the relative increase of IOPS for bs=4K read when
PAGE_SIZE=4K and PAGE_SIZE=64K is as follows:
IODEPTH 4K PAGE_SIZE 64K PAGE_SIZE
32 56% 47%
64 53% 44%
128 64% 43%
256 67% 45%
Signed-off-by: Xiang Chen <chenxiang66@hisilicon.com>
Signed-off-by: John Garry <john.garry@huawei.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
2018-05-31 15:50:48 +03:00
blk_cnt = ( max_command_entries_ru * sz_slot_buf_ru ) / s ;
slots_per_blk = s / sz_slot_buf_ru ;
2019-02-06 13:52:51 +03:00
scsi: hisi_sas: Pre-allocate slot DMA buffers
Currently the driver spends much time allocating and freeing the slot DMA
buffer for command delivery/completion. To boost the performance,
pre-allocate the buffers for all IPTT. The downside of this approach is
that we are reallocating all buffer memory upfront, so hog memory which we
may not need.
However, the current method - DMA buffer pool - also caches all buffers and
does not free them until the pool is destroyed, so is not exactly efficient
either.
On top of this, since the slot DMA buffer is slightly bigger than a 4K
page, we need to allocate 2x4K pages per buffer (for 4K page kernel), which
is quite wasteful. For 64K page size this is not such an issue.
So, for the 4K page case, in order to make memory usage more efficient,
pre-allocating larger blocks of DMA memory for the buffers can be more
efficient.
To make DMA memory usage most efficient, we would choose a single
contiguous DMA memory block, but this could use up all the DMA memory in
the system (when CMA enabled and no IOMMU), or we may just not be able to
allocate a DMA buffer large enough when no CMA or IOMMU.
To decide the block size we use the LCM (least common multiple) of the
buffer size and the page size. We roundup(64) to ensure the LCM is not too
large, even though a little memory may be wasted per block.
So, with this, the total memory requirement is about is about 17MB for 4096
max IPTT.
Previously (for 4K pages case), it would be 32MB (for all slots
allocated).
With this change, the relative increase of IOPS for bs=4K read when
PAGE_SIZE=4K and PAGE_SIZE=64K is as follows:
IODEPTH 4K PAGE_SIZE 64K PAGE_SIZE
32 56% 47%
64 53% 44%
128 64% 43%
256 67% 45%
Signed-off-by: Xiang Chen <chenxiang66@hisilicon.com>
Signed-off-by: John Garry <john.garry@huawei.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
2018-05-31 15:50:48 +03:00
for ( i = 0 ; i < blk_cnt ; i + + ) {
int slot_index = i * slots_per_blk ;
2019-02-06 13:52:51 +03:00
dma_addr_t buf_dma ;
void * buf ;
scsi: hisi_sas: Pre-allocate slot DMA buffers
Currently the driver spends much time allocating and freeing the slot DMA
buffer for command delivery/completion. To boost the performance,
pre-allocate the buffers for all IPTT. The downside of this approach is
that we are reallocating all buffer memory upfront, so hog memory which we
may not need.
However, the current method - DMA buffer pool - also caches all buffers and
does not free them until the pool is destroyed, so is not exactly efficient
either.
On top of this, since the slot DMA buffer is slightly bigger than a 4K
page, we need to allocate 2x4K pages per buffer (for 4K page kernel), which
is quite wasteful. For 64K page size this is not such an issue.
So, for the 4K page case, in order to make memory usage more efficient,
pre-allocating larger blocks of DMA memory for the buffers can be more
efficient.
To make DMA memory usage most efficient, we would choose a single
contiguous DMA memory block, but this could use up all the DMA memory in
the system (when CMA enabled and no IOMMU), or we may just not be able to
allocate a DMA buffer large enough when no CMA or IOMMU.
To decide the block size we use the LCM (least common multiple) of the
buffer size and the page size. We roundup(64) to ensure the LCM is not too
large, even though a little memory may be wasted per block.
So, with this, the total memory requirement is about is about 17MB for 4096
max IPTT.
Previously (for 4K pages case), it would be 32MB (for all slots
allocated).
With this change, the relative increase of IOPS for bs=4K read when
PAGE_SIZE=4K and PAGE_SIZE=64K is as follows:
IODEPTH 4K PAGE_SIZE 64K PAGE_SIZE
32 56% 47%
64 53% 44%
128 64% 43%
256 67% 45%
Signed-off-by: Xiang Chen <chenxiang66@hisilicon.com>
Signed-off-by: John Garry <john.garry@huawei.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
2018-05-31 15:50:48 +03:00
2019-02-06 13:52:51 +03:00
buf = dmam_alloc_coherent ( dev , s , & buf_dma ,
2019-08-05 16:48:10 +03:00
GFP_KERNEL ) ;
scsi: hisi_sas: Pre-allocate slot DMA buffers
Currently the driver spends much time allocating and freeing the slot DMA
buffer for command delivery/completion. To boost the performance,
pre-allocate the buffers for all IPTT. The downside of this approach is
that we are reallocating all buffer memory upfront, so hog memory which we
may not need.
However, the current method - DMA buffer pool - also caches all buffers and
does not free them until the pool is destroyed, so is not exactly efficient
either.
On top of this, since the slot DMA buffer is slightly bigger than a 4K
page, we need to allocate 2x4K pages per buffer (for 4K page kernel), which
is quite wasteful. For 64K page size this is not such an issue.
So, for the 4K page case, in order to make memory usage more efficient,
pre-allocating larger blocks of DMA memory for the buffers can be more
efficient.
To make DMA memory usage most efficient, we would choose a single
contiguous DMA memory block, but this could use up all the DMA memory in
the system (when CMA enabled and no IOMMU), or we may just not be able to
allocate a DMA buffer large enough when no CMA or IOMMU.
To decide the block size we use the LCM (least common multiple) of the
buffer size and the page size. We roundup(64) to ensure the LCM is not too
large, even though a little memory may be wasted per block.
So, with this, the total memory requirement is about is about 17MB for 4096
max IPTT.
Previously (for 4K pages case), it would be 32MB (for all slots
allocated).
With this change, the relative increase of IOPS for bs=4K read when
PAGE_SIZE=4K and PAGE_SIZE=64K is as follows:
IODEPTH 4K PAGE_SIZE 64K PAGE_SIZE
32 56% 47%
64 53% 44%
128 64% 43%
256 67% 45%
Signed-off-by: Xiang Chen <chenxiang66@hisilicon.com>
Signed-off-by: John Garry <john.garry@huawei.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
2018-05-31 15:50:48 +03:00
if ( ! buf )
goto err_out ;
for ( j = 0 ; j < slots_per_blk ; j + + , slot_index + + ) {
struct hisi_sas_slot * slot ;
slot = & hisi_hba - > slot_info [ slot_index ] ;
slot - > buf = buf ;
slot - > buf_dma = buf_dma ;
slot - > idx = slot_index ;
2019-02-06 13:52:51 +03:00
buf + = sz_slot_buf_ru ;
buf_dma + = sz_slot_buf_ru ;
scsi: hisi_sas: Pre-allocate slot DMA buffers
Currently the driver spends much time allocating and freeing the slot DMA
buffer for command delivery/completion. To boost the performance,
pre-allocate the buffers for all IPTT. The downside of this approach is
that we are reallocating all buffer memory upfront, so hog memory which we
may not need.
However, the current method - DMA buffer pool - also caches all buffers and
does not free them until the pool is destroyed, so is not exactly efficient
either.
On top of this, since the slot DMA buffer is slightly bigger than a 4K
page, we need to allocate 2x4K pages per buffer (for 4K page kernel), which
is quite wasteful. For 64K page size this is not such an issue.
So, for the 4K page case, in order to make memory usage more efficient,
pre-allocating larger blocks of DMA memory for the buffers can be more
efficient.
To make DMA memory usage most efficient, we would choose a single
contiguous DMA memory block, but this could use up all the DMA memory in
the system (when CMA enabled and no IOMMU), or we may just not be able to
allocate a DMA buffer large enough when no CMA or IOMMU.
To decide the block size we use the LCM (least common multiple) of the
buffer size and the page size. We roundup(64) to ensure the LCM is not too
large, even though a little memory may be wasted per block.
So, with this, the total memory requirement is about is about 17MB for 4096
max IPTT.
Previously (for 4K pages case), it would be 32MB (for all slots
allocated).
With this change, the relative increase of IOPS for bs=4K read when
PAGE_SIZE=4K and PAGE_SIZE=64K is as follows:
IODEPTH 4K PAGE_SIZE 64K PAGE_SIZE
32 56% 47%
64 53% 44%
128 64% 43%
256 67% 45%
Signed-off-by: Xiang Chen <chenxiang66@hisilicon.com>
Signed-off-by: John Garry <john.garry@huawei.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
2018-05-31 15:50:48 +03:00
}
}
2016-01-25 21:47:03 +03:00
s = max_command_entries * sizeof ( struct hisi_sas_iost ) ;
2018-05-31 15:50:42 +03:00
hisi_hba - > iost = dmam_alloc_coherent ( dev , s , & hisi_hba - > iost_dma ,
GFP_KERNEL ) ;
2015-11-17 19:50:34 +03:00
if ( ! hisi_hba - > iost )
goto err_out ;
2016-01-25 21:47:03 +03:00
s = max_command_entries * sizeof ( struct hisi_sas_breakpoint ) ;
2018-05-31 15:50:42 +03:00
hisi_hba - > breakpoint = dmam_alloc_coherent ( dev , s ,
& hisi_hba - > breakpoint_dma ,
GFP_KERNEL ) ;
2015-11-17 19:50:34 +03:00
if ( ! hisi_hba - > breakpoint )
goto err_out ;
2021-11-27 00:15:21 +03:00
s = hisi_hba - > slot_index_count = max_command_entries ;
hisi_hba - > slot_index_tags = devm_bitmap_zalloc ( dev , s , GFP_KERNEL ) ;
2015-11-17 19:50:36 +03:00
if ( ! hisi_hba - > slot_index_tags )
goto err_out ;
2015-11-17 19:50:34 +03:00
s = sizeof ( struct hisi_sas_initial_fis ) * HISI_SAS_MAX_PHYS ;
2018-05-31 15:50:42 +03:00
hisi_hba - > initial_fis = dmam_alloc_coherent ( dev , s ,
& hisi_hba - > initial_fis_dma ,
GFP_KERNEL ) ;
2015-11-17 19:50:34 +03:00
if ( ! hisi_hba - > initial_fis )
goto err_out ;
2017-10-24 18:51:35 +03:00
s = HISI_SAS_MAX_ITCT_ENTRIES * sizeof ( struct hisi_sas_sata_breakpoint ) ;
2018-05-31 15:50:42 +03:00
hisi_hba - > sata_breakpoint = dmam_alloc_coherent ( dev , s ,
& hisi_hba - > sata_breakpoint_dma ,
GFP_KERNEL ) ;
2015-11-17 19:50:34 +03:00
if ( ! hisi_hba - > sata_breakpoint )
goto err_out ;
2022-10-18 14:15:59 +03:00
hisi_hba - > last_slot_index = 0 ;
2015-11-17 19:50:36 +03:00
2015-11-17 19:50:40 +03:00
hisi_hba - > wq = create_singlethread_workqueue ( dev_name ( dev ) ) ;
if ( ! hisi_hba - > wq ) {
dev_err ( dev , " sas_alloc: failed to create workqueue \n " ) ;
goto err_out ;
}
2015-11-17 19:50:34 +03:00
return 0 ;
err_out :
return - ENOMEM ;
}
2017-06-14 18:33:20 +03:00
EXPORT_SYMBOL_GPL ( hisi_sas_alloc ) ;
2015-11-17 19:50:34 +03:00
2017-06-14 18:33:20 +03:00
void hisi_sas_free ( struct hisi_hba * hisi_hba )
2015-11-17 19:50:35 +03:00
{
2019-05-29 12:58:42 +03:00
int i ;
for ( i = 0 ; i < hisi_hba - > n_phy ; i + + ) {
struct hisi_sas_phy * phy = & hisi_hba - > phy [ i ] ;
del_timer_sync ( & phy - > timer ) ;
}
2015-11-17 19:50:40 +03:00
if ( hisi_hba - > wq )
destroy_workqueue ( hisi_hba - > wq ) ;
2015-11-17 19:50:35 +03:00
}
2017-06-14 18:33:20 +03:00
EXPORT_SYMBOL_GPL ( hisi_sas_free ) ;
2015-11-17 19:50:34 +03:00
2017-10-24 18:51:45 +03:00
void hisi_sas_rst_work_handler ( struct work_struct * work )
2017-03-22 20:25:18 +03:00
{
struct hisi_hba * hisi_hba =
container_of ( work , struct hisi_hba , rst_work ) ;
2021-04-06 14:48:29 +03:00
if ( hisi_sas_controller_prereset ( hisi_hba ) )
return ;
2017-03-22 20:25:18 +03:00
hisi_sas_controller_reset ( hisi_hba ) ;
}
2017-10-24 18:51:45 +03:00
EXPORT_SYMBOL_GPL ( hisi_sas_rst_work_handler ) ;
2017-03-22 20:25:18 +03:00
2017-12-08 20:16:38 +03:00
void hisi_sas_sync_rst_work_handler ( struct work_struct * work )
{
struct hisi_sas_rst * rst =
container_of ( work , struct hisi_sas_rst , work ) ;
2021-04-06 14:48:29 +03:00
if ( hisi_sas_controller_prereset ( rst - > hisi_hba ) )
goto rst_complete ;
2017-12-08 20:16:38 +03:00
if ( ! hisi_sas_controller_reset ( rst - > hisi_hba ) )
rst - > done = true ;
2021-04-06 14:48:29 +03:00
rst_complete :
2017-12-08 20:16:38 +03:00
complete ( rst - > completion ) ;
}
EXPORT_SYMBOL_GPL ( hisi_sas_sync_rst_work_handler ) ;
2017-06-14 18:33:18 +03:00
int hisi_sas_get_fw_info ( struct hisi_hba * hisi_hba )
2015-11-17 19:50:31 +03:00
{
2017-06-14 18:33:18 +03:00
struct device * dev = hisi_hba - > dev ;
struct platform_device * pdev = hisi_hba - > platform_dev ;
struct device_node * np = pdev ? pdev - > dev . of_node : NULL ;
2016-10-04 14:11:11 +03:00
struct clk * refclk ;
2015-11-17 19:50:31 +03:00
2016-02-03 21:26:08 +03:00
if ( device_property_read_u8_array ( dev , " sas-addr " , hisi_hba - > sas_addr ,
2017-06-14 18:33:18 +03:00
SAS_ADDR_SIZE ) ) {
dev_err ( dev , " could not get property sas-addr \n " ) ;
return - ENOENT ;
}
2015-11-17 19:50:32 +03:00
2016-02-03 21:26:08 +03:00
if ( np ) {
2017-06-14 18:33:18 +03:00
/*
* These properties are only required for platform device - based
* controller with DT firmware .
*/
2016-02-03 21:26:08 +03:00
hisi_hba - > ctrl = syscon_regmap_lookup_by_phandle ( np ,
" hisilicon,sas-syscon " ) ;
2017-06-14 18:33:18 +03:00
if ( IS_ERR ( hisi_hba - > ctrl ) ) {
dev_err ( dev , " could not get syscon \n " ) ;
return - ENOENT ;
}
2015-11-17 19:50:32 +03:00
2016-02-03 21:26:08 +03:00
if ( device_property_read_u32 ( dev , " ctrl-reset-reg " ,
2017-06-14 18:33:18 +03:00
& hisi_hba - > ctrl_reset_reg ) ) {
2019-04-11 15:46:44 +03:00
dev_err ( dev , " could not get property ctrl-reset-reg \n " ) ;
2017-06-14 18:33:18 +03:00
return - ENOENT ;
}
2015-11-17 19:50:32 +03:00
2016-02-03 21:26:08 +03:00
if ( device_property_read_u32 ( dev , " ctrl-reset-sts-reg " ,
2017-06-14 18:33:18 +03:00
& hisi_hba - > ctrl_reset_sts_reg ) ) {
2019-04-11 15:46:44 +03:00
dev_err ( dev , " could not get property ctrl-reset-sts-reg \n " ) ;
2017-06-14 18:33:18 +03:00
return - ENOENT ;
}
2015-11-17 19:50:32 +03:00
2016-02-03 21:26:08 +03:00
if ( device_property_read_u32 ( dev , " ctrl-clock-ena-reg " ,
2017-06-14 18:33:18 +03:00
& hisi_hba - > ctrl_clock_ena_reg ) ) {
2019-04-11 15:46:44 +03:00
dev_err ( dev , " could not get property ctrl-clock-ena-reg \n " ) ;
2017-06-14 18:33:18 +03:00
return - ENOENT ;
}
2016-02-03 21:26:08 +03:00
}
2017-06-14 18:33:18 +03:00
refclk = devm_clk_get ( dev , NULL ) ;
2016-10-04 14:11:11 +03:00
if ( IS_ERR ( refclk ) )
2017-01-20 15:45:20 +03:00
dev_dbg ( dev , " no ref clk property \n " ) ;
2016-10-04 14:11:11 +03:00
else
hisi_hba - > refclk_frequency_mhz = clk_get_rate ( refclk ) / 1000000 ;
2017-06-14 18:33:18 +03:00
if ( device_property_read_u32 ( dev , " phy-count " , & hisi_hba - > n_phy ) ) {
dev_err ( dev , " could not get property phy-count \n " ) ;
return - ENOENT ;
}
2015-11-17 19:50:32 +03:00
2016-02-03 21:26:08 +03:00
if ( device_property_read_u32 ( dev , " queue-count " ,
2017-06-14 18:33:18 +03:00
& hisi_hba - > queue_count ) ) {
dev_err ( dev , " could not get property queue-count \n " ) ;
return - ENOENT ;
}
return 0 ;
}
EXPORT_SYMBOL_GPL ( hisi_sas_get_fw_info ) ;
static struct Scsi_Host * hisi_sas_shost_alloc ( struct platform_device * pdev ,
const struct hisi_sas_hw * hw )
{
struct resource * res ;
struct Scsi_Host * shost ;
struct hisi_hba * hisi_hba ;
struct device * dev = & pdev - > dev ;
2019-02-18 10:34:25 +03:00
int error ;
2017-06-14 18:33:18 +03:00
2018-05-21 13:09:18 +03:00
shost = scsi_host_alloc ( hw - > sht , sizeof ( * hisi_hba ) ) ;
2017-06-14 18:33:18 +03:00
if ( ! shost ) {
dev_err ( dev , " scsi host alloc failed \n " ) ;
return NULL ;
}
hisi_hba = shost_priv ( shost ) ;
INIT_WORK ( & hisi_hba - > rst_work , hisi_sas_rst_work_handler ) ;
hisi_hba - > hw = hw ;
hisi_hba - > dev = dev ;
hisi_hba - > platform_dev = pdev ;
hisi_hba - > shost = shost ;
SHOST_TO_SAS_HA ( shost ) = & hisi_hba - > sha ;
2017-08-23 02:05:14 +03:00
timer_setup ( & hisi_hba - > timer , NULL , 0 ) ;
2017-06-14 18:33:18 +03:00
if ( hisi_sas_get_fw_info ( hisi_hba ) < 0 )
2015-11-17 19:50:32 +03:00
goto err_out ;
2019-02-18 10:34:25 +03:00
error = dma_set_mask_and_coherent ( dev , DMA_BIT_MASK ( 64 ) ) ;
if ( error ) {
2016-09-06 18:36:19 +03:00
dev_err ( dev , " No usable DMA addressing method \n " ) ;
goto err_out ;
}
2019-09-04 16:02:56 +03:00
hisi_hba - > regs = devm_platform_ioremap_resource ( pdev , 0 ) ;
2015-11-17 19:50:32 +03:00
if ( IS_ERR ( hisi_hba - > regs ) )
goto err_out ;
2018-01-17 19:46:53 +03:00
res = platform_get_resource ( pdev , IORESOURCE_MEM , 1 ) ;
if ( res ) {
hisi_hba - > sgpio_regs = devm_ioremap_resource ( dev , res ) ;
if ( IS_ERR ( hisi_hba - > sgpio_regs ) )
goto err_out ;
}
2019-01-25 17:22:33 +03:00
if ( hisi_sas_alloc ( hisi_hba ) ) {
2015-11-17 19:50:35 +03:00
hisi_sas_free ( hisi_hba ) ;
2015-11-17 19:50:34 +03:00
goto err_out ;
2015-11-17 19:50:35 +03:00
}
2015-11-17 19:50:34 +03:00
2015-11-17 19:50:31 +03:00
return shost ;
err_out :
2017-08-10 19:09:43 +03:00
scsi_host_put ( shost ) ;
2015-11-17 19:50:31 +03:00
dev_err ( dev , " shost alloc failed \n " ) ;
return NULL ;
}
2020-12-02 13:36:57 +03:00
static int hisi_sas_interrupt_preinit ( struct hisi_hba * hisi_hba )
{
if ( hisi_hba - > hw - > interrupt_preinit )
return hisi_hba - > hw - > interrupt_preinit ( hisi_hba ) ;
return 0 ;
}
2015-11-17 19:50:31 +03:00
int hisi_sas_probe ( struct platform_device * pdev ,
2018-05-21 13:09:18 +03:00
const struct hisi_sas_hw * hw )
2015-11-17 19:50:31 +03:00
{
struct Scsi_Host * shost ;
struct hisi_hba * hisi_hba ;
struct device * dev = & pdev - > dev ;
struct asd_sas_phy * * arr_phy ;
struct asd_sas_port * * arr_port ;
struct sas_ha_struct * sha ;
int rc , phy_nr , port_nr , i ;
shost = hisi_sas_shost_alloc ( pdev , hw ) ;
2016-11-29 18:45:57 +03:00
if ( ! shost )
return - ENOMEM ;
2015-11-17 19:50:31 +03:00
sha = SHOST_TO_SAS_HA ( shost ) ;
hisi_hba = shost_priv ( shost ) ;
platform_set_drvdata ( pdev , sha ) ;
2015-11-17 19:50:39 +03:00
2015-11-17 19:50:31 +03:00
phy_nr = port_nr = hisi_hba - > n_phy ;
arr_phy = devm_kcalloc ( dev , phy_nr , sizeof ( void * ) , GFP_KERNEL ) ;
arr_port = devm_kcalloc ( dev , port_nr , sizeof ( void * ) , GFP_KERNEL ) ;
2016-11-29 18:45:57 +03:00
if ( ! arr_phy | | ! arr_port ) {
rc = - ENOMEM ;
goto err_out_ha ;
}
2015-11-17 19:50:31 +03:00
sha - > sas_phy = arr_phy ;
sha - > sas_port = arr_port ;
sha - > lldd_ha = hisi_hba ;
shost - > transportt = hisi_sas_stt ;
shost - > max_id = HISI_SAS_MAX_DEVICES ;
shost - > max_lun = ~ 0 ;
shost - > max_channel = 1 ;
shost - > max_cmd_len = 16 ;
2018-09-24 18:06:33 +03:00
if ( hisi_hba - > hw - > slot_index_alloc ) {
2019-08-05 16:47:58 +03:00
shost - > can_queue = HISI_SAS_MAX_COMMANDS ;
shost - > cmd_per_lun = HISI_SAS_MAX_COMMANDS ;
2018-09-24 18:06:33 +03:00
} else {
2019-08-05 16:47:58 +03:00
shost - > can_queue = HISI_SAS_UNRESERVED_IPTT ;
shost - > cmd_per_lun = HISI_SAS_UNRESERVED_IPTT ;
2018-09-24 18:06:33 +03:00
}
2015-11-17 19:50:31 +03:00
sha - > sas_ha_name = DRV_NAME ;
2017-06-14 18:33:17 +03:00
sha - > dev = hisi_hba - > dev ;
2015-11-17 19:50:31 +03:00
sha - > sas_addr = & hisi_hba - > sas_addr [ 0 ] ;
sha - > num_phys = hisi_hba - > n_phy ;
2023-08-15 14:51:50 +03:00
sha - > shost = hisi_hba - > shost ;
2015-11-17 19:50:31 +03:00
for ( i = 0 ; i < hisi_hba - > n_phy ; i + + ) {
sha - > sas_phy [ i ] = & hisi_hba - > phy [ i ] . sas_phy ;
sha - > sas_port [ i ] = & hisi_hba - > port [ i ] . sas_port ;
}
2020-12-02 13:36:57 +03:00
rc = hisi_sas_interrupt_preinit ( hisi_hba ) ;
if ( rc )
goto err_out_ha ;
2015-11-17 19:50:31 +03:00
rc = scsi_add_host ( shost , & pdev - > dev ) ;
if ( rc )
goto err_out_ha ;
rc = sas_register_ha ( sha ) ;
if ( rc )
goto err_out_register_ha ;
2017-01-20 15:45:23 +03:00
rc = hisi_hba - > hw - > hw_init ( hisi_hba ) ;
if ( rc )
2021-04-06 14:48:28 +03:00
goto err_out_hw_init ;
2017-01-20 15:45:23 +03:00
2015-11-17 19:50:31 +03:00
scsi_scan_host ( shost ) ;
return 0 ;
2021-04-06 14:48:28 +03:00
err_out_hw_init :
sas_unregister_ha ( sha ) ;
2015-11-17 19:50:31 +03:00
err_out_register_ha :
scsi_remove_host ( shost ) ;
err_out_ha :
2016-11-29 18:45:57 +03:00
hisi_sas_free ( hisi_hba ) ;
2017-08-10 19:09:43 +03:00
scsi_host_put ( shost ) ;
2015-11-17 19:50:31 +03:00
return rc ;
}
EXPORT_SYMBOL_GPL ( hisi_sas_probe ) ;
2023-05-18 23:20:43 +03:00
void hisi_sas_remove ( struct platform_device * pdev )
2019-08-05 16:48:01 +03:00
{
2015-11-17 19:50:35 +03:00
struct sas_ha_struct * sha = platform_get_drvdata ( pdev ) ;
struct hisi_hba * hisi_hba = sha - > lldd_ha ;
2023-08-15 14:51:50 +03:00
struct Scsi_Host * shost = sha - > shost ;
2019-08-05 16:48:01 +03:00
2021-08-24 13:00:59 +03:00
del_timer_sync ( & hisi_hba - > timer ) ;
2019-08-05 16:48:01 +03:00
2015-11-17 19:50:35 +03:00
sas_unregister_ha ( sha ) ;
2023-08-15 14:51:50 +03:00
sas_remove_host ( shost ) ;
2019-08-05 16:48:01 +03:00
2015-11-17 19:50:35 +03:00
hisi_sas_free ( hisi_hba ) ;
2017-08-10 19:09:43 +03:00
scsi_host_put ( shost ) ;
2019-08-05 16:48:01 +03:00
}
2015-11-17 19:50:35 +03:00
EXPORT_SYMBOL_GPL ( hisi_sas_remove ) ;
2019-08-05 16:48:01 +03:00
2021-01-26 14:04:26 +03:00
# if IS_ENABLED(CONFIG_SCSI_HISI_SAS_DEBUGFS_DEFAULT_ENABLE)
# define DEBUGFS_ENABLE_DEFAULT "enabled"
bool hisi_sas_debugfs_enable = true ;
u32 hisi_sas_debugfs_dump_count = 50 ;
# else
# define DEBUGFS_ENABLE_DEFAULT "disabled"
2018-12-19 18:56:39 +03:00
bool hisi_sas_debugfs_enable ;
2021-01-26 14:04:26 +03:00
u32 hisi_sas_debugfs_dump_count = 1 ;
# endif
2018-12-19 18:56:39 +03:00
EXPORT_SYMBOL_GPL ( hisi_sas_debugfs_enable ) ;
module_param_named ( debugfs_enable , hisi_sas_debugfs_enable , bool , 0444 ) ;
2021-01-26 14:04:26 +03:00
MODULE_PARM_DESC ( hisi_sas_debugfs_enable ,
" Enable driver debugfs (default " DEBUGFS_ENABLE_DEFAULT " ) " ) ;
2018-12-19 18:56:41 +03:00
2019-10-24 17:08:22 +03:00
EXPORT_SYMBOL_GPL ( hisi_sas_debugfs_dump_count ) ;
module_param_named ( debugfs_dump_count , hisi_sas_debugfs_dump_count , uint , 0444 ) ;
MODULE_PARM_DESC ( hisi_sas_debugfs_dump_count , " Number of debugfs dumps to allow " ) ;
2019-02-06 13:52:52 +03:00
2020-11-24 11:46:34 +03:00
struct dentry * hisi_sas_debugfs_dir ;
EXPORT_SYMBOL_GPL ( hisi_sas_debugfs_dir ) ;
2019-10-24 17:08:22 +03:00
2015-11-17 19:50:30 +03:00
static __init int hisi_sas_init ( void )
{
hisi_sas_stt = sas_domain_attach_transport ( & hisi_sas_transport_ops ) ;
if ( ! hisi_sas_stt )
return - ENOMEM ;
2019-10-24 17:08:22 +03:00
if ( hisi_sas_debugfs_enable ) {
2018-12-19 18:56:39 +03:00
hisi_sas_debugfs_dir = debugfs_create_dir ( " hisi_sas " , NULL ) ;
2019-10-24 17:08:22 +03:00
if ( hisi_sas_debugfs_dump_count > HISI_SAS_MAX_DEBUGFS_DUMP ) {
pr_info ( " hisi_sas: Limiting debugfs dump count \n " ) ;
hisi_sas_debugfs_dump_count = HISI_SAS_MAX_DEBUGFS_DUMP ;
}
}
2018-12-19 18:56:39 +03:00
2015-11-17 19:50:30 +03:00
return 0 ;
}
static __exit void hisi_sas_exit ( void )
{
sas_release_transport ( hisi_sas_stt ) ;
2018-12-19 18:56:39 +03:00
2024-01-22 09:25:46 +03:00
if ( hisi_sas_debugfs_enable )
debugfs_remove ( hisi_sas_debugfs_dir ) ;
2015-11-17 19:50:30 +03:00
}
module_init ( hisi_sas_init ) ;
module_exit ( hisi_sas_exit ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " John Garry <john.garry@huawei.com> " ) ;
MODULE_DESCRIPTION ( " HISILICON SAS controller driver " ) ;
MODULE_ALIAS ( " platform: " DRV_NAME ) ;