2020-05-27 16:21:28 +01:00
// SPDX-License-Identifier: (BSD-3-Clause OR GPL-2.0-only)
/* Copyright(c) 2014 - 2020 Intel Corporation */
2014-06-05 13:42:39 -07:00
# include <linux/kernel.h>
# include <linux/pci.h>
# include <linux/aer.h>
# include <linux/completion.h>
# include <linux/workqueue.h>
# include <linux/delay.h>
# include "adf_accel_devices.h"
# include "adf_common_drv.h"
static struct workqueue_struct * device_reset_wq ;
static pci_ers_result_t adf_error_detected ( struct pci_dev * pdev ,
pci_channel_state_t state )
{
struct adf_accel_dev * accel_dev = adf_devmgr_pci_to_accel_dev ( pdev ) ;
2015-03-19 16:03:44 -07:00
dev_info ( & pdev - > dev , " Acceleration driver hardware error detected. \n " ) ;
2014-06-05 13:42:39 -07:00
if ( ! accel_dev ) {
2015-03-19 16:03:44 -07:00
dev_err ( & pdev - > dev , " Can't find acceleration device \n " ) ;
2014-06-05 13:42:39 -07:00
return PCI_ERS_RESULT_DISCONNECT ;
}
if ( state = = pci_channel_io_perm_failure ) {
2015-03-19 16:03:44 -07:00
dev_err ( & pdev - > dev , " Can't recover from device error \n " ) ;
2014-06-05 13:42:39 -07:00
return PCI_ERS_RESULT_DISCONNECT ;
}
return PCI_ERS_RESULT_NEED_RESET ;
}
/* reset dev data */
struct adf_reset_dev_data {
int mode ;
struct adf_accel_dev * accel_dev ;
struct completion compl ;
struct work_struct reset_work ;
} ;
2016-07-04 16:26:00 +01:00
void adf_reset_sbr ( struct adf_accel_dev * accel_dev )
2014-06-05 13:42:39 -07:00
{
struct pci_dev * pdev = accel_to_pci_dev ( accel_dev ) ;
struct pci_dev * parent = pdev - > bus - > self ;
2020-06-03 18:33:44 +01:00
u16 bridge_ctl = 0 ;
2014-06-05 13:42:39 -07:00
2015-08-07 11:34:25 -07:00
if ( ! parent )
parent = pdev ;
2015-01-09 11:55:14 -08:00
if ( ! pci_wait_for_pending_transaction ( pdev ) )
2015-03-19 16:03:44 -07:00
dev_info ( & GET_DEV ( accel_dev ) ,
" Transaction still in progress. Proceeding \n " ) ;
2014-06-05 13:42:39 -07:00
2016-07-04 16:26:00 +01:00
dev_info ( & GET_DEV ( accel_dev ) , " Secondary bus reset \n " ) ;
2014-06-05 13:42:39 -07:00
pci_read_config_word ( parent , PCI_BRIDGE_CONTROL , & bridge_ctl ) ;
bridge_ctl | = PCI_BRIDGE_CTL_BUS_RESET ;
pci_write_config_word ( parent , PCI_BRIDGE_CONTROL , bridge_ctl ) ;
msleep ( 100 ) ;
bridge_ctl & = ~ PCI_BRIDGE_CTL_BUS_RESET ;
pci_write_config_word ( parent , PCI_BRIDGE_CONTROL , bridge_ctl ) ;
msleep ( 100 ) ;
2016-07-04 16:26:00 +01:00
}
EXPORT_SYMBOL_GPL ( adf_reset_sbr ) ;
void adf_reset_flr ( struct adf_accel_dev * accel_dev )
{
2017-05-16 16:21:05 +02:00
pcie_flr ( accel_to_pci_dev ( accel_dev ) ) ;
2016-07-04 16:26:00 +01:00
}
EXPORT_SYMBOL_GPL ( adf_reset_flr ) ;
void adf_dev_restore ( struct adf_accel_dev * accel_dev )
{
struct adf_hw_device_data * hw_device = accel_dev - > hw_device ;
struct pci_dev * pdev = accel_to_pci_dev ( accel_dev ) ;
if ( hw_device - > reset_device ) {
dev_info ( & GET_DEV ( accel_dev ) , " Resetting device qat_dev%d \n " ,
accel_dev - > accel_id ) ;
hw_device - > reset_device ( accel_dev ) ;
pci_restore_state ( pdev ) ;
pci_save_state ( pdev ) ;
}
2014-06-05 13:42:39 -07:00
}
static void adf_device_reset_worker ( struct work_struct * work )
{
struct adf_reset_dev_data * reset_data =
container_of ( work , struct adf_reset_dev_data , reset_work ) ;
struct adf_accel_dev * accel_dev = reset_data - > accel_dev ;
adf_dev_restarting_notify ( accel_dev ) ;
adf_dev_stop ( accel_dev ) ;
crypto: qat - fix device reset flow
When the device needs a reset, e.g. when an uncorrectable PCIe AER event
occurs, various services/data structures need to be cleaned up, the
hardware reset and the services/data structures initialized and started.
The code to perform the cleanup and initialization was not performed when
a device reset was done.
This patch moves some of the initialization code out of the .probe entry-
point into a separate function that is now called during probe as well as
after the hardware has been reset. Similarly, a new function is added for
first cleaning up these services/data structures prior to resetting. The
new functions are adf_dev_init() and adf_dev_shutdown(), respectively, for
which there are already prototypes but no actual functions just yet and are
now called when the device is reset and during probe/cleanup of the driver.
The down and up flows via ioctl calls has similarly been updated.
In addition, there are two other bugs in the reset flow - one in the logic
for determining whether to schedule a device reset upon receiving an
uncorrectable AER event which prevents the reset flow from being initiated,
and another with clearing the status bit indicating a device is configured
(when resetting the device the configuration remains across the reset so
the bit should not be cleared, otherwise, the necessary services will not
be re-started in adf_dev_start() after the reset - clear the bit only when
actually deleting the configuration).
Signed-off-by: Bruce Allan <bruce.w.allan@intel.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
2015-01-09 11:54:58 -08:00
adf_dev_shutdown ( accel_dev ) ;
if ( adf_dev_init ( accel_dev ) | | adf_dev_start ( accel_dev ) ) {
2014-06-05 13:42:39 -07:00
/* The device hanged and we can't restart it so stop here */
dev_err ( & GET_DEV ( accel_dev ) , " Restart device failed \n " ) ;
kfree ( reset_data ) ;
WARN ( 1 , " QAT: device restart failed. Device is unusable \n " ) ;
return ;
}
adf_dev_restarted_notify ( accel_dev ) ;
clear_bit ( ADF_STATUS_RESTARTING , & accel_dev - > status ) ;
/* The dev is back alive. Notify the caller if in sync mode */
if ( reset_data - > mode = = ADF_DEV_RESET_SYNC )
complete ( & reset_data - > compl ) ;
else
kfree ( reset_data ) ;
}
static int adf_dev_aer_schedule_reset ( struct adf_accel_dev * accel_dev ,
enum adf_dev_reset_mode mode )
{
struct adf_reset_dev_data * reset_data ;
crypto: qat - fix device reset flow
When the device needs a reset, e.g. when an uncorrectable PCIe AER event
occurs, various services/data structures need to be cleaned up, the
hardware reset and the services/data structures initialized and started.
The code to perform the cleanup and initialization was not performed when
a device reset was done.
This patch moves some of the initialization code out of the .probe entry-
point into a separate function that is now called during probe as well as
after the hardware has been reset. Similarly, a new function is added for
first cleaning up these services/data structures prior to resetting. The
new functions are adf_dev_init() and adf_dev_shutdown(), respectively, for
which there are already prototypes but no actual functions just yet and are
now called when the device is reset and during probe/cleanup of the driver.
The down and up flows via ioctl calls has similarly been updated.
In addition, there are two other bugs in the reset flow - one in the logic
for determining whether to schedule a device reset upon receiving an
uncorrectable AER event which prevents the reset flow from being initiated,
and another with clearing the status bit indicating a device is configured
(when resetting the device the configuration remains across the reset so
the bit should not be cleared, otherwise, the necessary services will not
be re-started in adf_dev_start() after the reset - clear the bit only when
actually deleting the configuration).
Signed-off-by: Bruce Allan <bruce.w.allan@intel.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
2015-01-09 11:54:58 -08:00
if ( ! adf_dev_started ( accel_dev ) | |
test_bit ( ADF_STATUS_RESTARTING , & accel_dev - > status ) )
2014-06-05 13:42:39 -07:00
return 0 ;
set_bit ( ADF_STATUS_RESTARTING , & accel_dev - > status ) ;
2018-07-23 16:27:44 +08:00
reset_data = kzalloc ( sizeof ( * reset_data ) , GFP_KERNEL ) ;
2014-06-05 13:42:39 -07:00
if ( ! reset_data )
return - ENOMEM ;
reset_data - > accel_dev = accel_dev ;
init_completion ( & reset_data - > compl ) ;
reset_data - > mode = mode ;
INIT_WORK ( & reset_data - > reset_work , adf_device_reset_worker ) ;
queue_work ( device_reset_wq , & reset_data - > reset_work ) ;
/* If in sync mode wait for the result */
if ( mode = = ADF_DEV_RESET_SYNC ) {
int ret = 0 ;
/* Maximum device reset time is 10 seconds */
unsigned long wait_jiffies = msecs_to_jiffies ( 10000 ) ;
unsigned long timeout = wait_for_completion_timeout (
& reset_data - > compl , wait_jiffies ) ;
if ( ! timeout ) {
2015-03-19 16:03:44 -07:00
dev_err ( & GET_DEV ( accel_dev ) ,
" Reset device timeout expired \n " ) ;
2014-06-05 13:42:39 -07:00
ret = - EFAULT ;
}
kfree ( reset_data ) ;
return ret ;
}
return 0 ;
}
static pci_ers_result_t adf_slot_reset ( struct pci_dev * pdev )
{
struct adf_accel_dev * accel_dev = adf_devmgr_pci_to_accel_dev ( pdev ) ;
if ( ! accel_dev ) {
pr_err ( " QAT: Can't find acceleration device \n " ) ;
return PCI_ERS_RESULT_DISCONNECT ;
}
if ( adf_dev_aer_schedule_reset ( accel_dev , ADF_DEV_RESET_SYNC ) )
return PCI_ERS_RESULT_DISCONNECT ;
return PCI_ERS_RESULT_RECOVERED ;
}
static void adf_resume ( struct pci_dev * pdev )
{
2015-03-19 16:03:44 -07:00
dev_info ( & pdev - > dev , " Acceleration driver reset completed \n " ) ;
2017-06-26 20:41:03 +01:00
dev_info ( & pdev - > dev , " Device is up and running \n " ) ;
2014-06-05 13:42:39 -07:00
}
2015-11-14 11:06:59 +01:00
static const struct pci_error_handlers adf_err_handler = {
2014-06-05 13:42:39 -07:00
. error_detected = adf_error_detected ,
. slot_reset = adf_slot_reset ,
. resume = adf_resume ,
} ;
/**
* adf_enable_aer ( ) - Enable Advance Error Reporting for acceleration device
* @ accel_dev : Pointer to acceleration device .
* @ adf : PCI device driver owning the given acceleration device .
*
* Function enables PCI Advance Error Reporting for the
* QAT acceleration device accel_dev .
* To be used by QAT device specific drivers .
*
2015-07-24 13:18:26 -07:00
* Return : 0 on success , error code otherwise .
2014-06-05 13:42:39 -07:00
*/
int adf_enable_aer ( struct adf_accel_dev * accel_dev , struct pci_driver * adf )
{
struct pci_dev * pdev = accel_to_pci_dev ( accel_dev ) ;
adf - > err_handler = & adf_err_handler ;
pci_enable_pcie_error_reporting ( pdev ) ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( adf_enable_aer ) ;
/**
* adf_disable_aer ( ) - Enable Advance Error Reporting for acceleration device
* @ accel_dev : Pointer to acceleration device .
*
* Function disables PCI Advance Error Reporting for the
* QAT acceleration device accel_dev .
* To be used by QAT device specific drivers .
*
* Return : void
*/
void adf_disable_aer ( struct adf_accel_dev * accel_dev )
{
struct pci_dev * pdev = accel_to_pci_dev ( accel_dev ) ;
pci_disable_pcie_error_reporting ( pdev ) ;
}
EXPORT_SYMBOL_GPL ( adf_disable_aer ) ;
int adf_init_aer ( void )
{
2016-06-08 02:47:47 +05:30
device_reset_wq = alloc_workqueue ( " qat_device_reset_wq " ,
WQ_MEM_RECLAIM , 0 ) ;
2015-03-31 09:30:50 -07:00
return ! device_reset_wq ? - EFAULT : 0 ;
2014-06-05 13:42:39 -07:00
}
void adf_exit_aer ( void )
{
if ( device_reset_wq )
destroy_workqueue ( device_reset_wq ) ;
device_reset_wq = NULL ;
}