diff --git a/anolis/configs/L1-RECOMMEND/default/CONFIG_SCSI_PS3STOR b/anolis/configs/L1-RECOMMEND/default/CONFIG_SCSI_PS3STOR new file mode 100644 index 0000000000000000000000000000000000000000..fb326232da781c5cd2608a96e41ed2a2aa5c5a73 --- /dev/null +++ b/anolis/configs/L1-RECOMMEND/default/CONFIG_SCSI_PS3STOR @@ -0,0 +1 @@ +CONFIG_SCSI_PS3STOR=m diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index cd658370af46f8f1d80e3acaeadc08e5f8f38cf4..48e40b67e9789bf4a019657e8631896f9a0f42ef 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -488,6 +488,7 @@ config SCSI_ARCMSR source "drivers/scsi/esas2r/Kconfig" source "drivers/scsi/megaraid/Kconfig.megaraid" source "drivers/scsi/mpt3sas/Kconfig" +source "drivers/scsi/linkdata/Kconfig" source "drivers/scsi/mpi3mr/Kconfig" source "drivers/scsi/leapioraid/Kconfig" source "drivers/scsi/smartpqi/Kconfig" diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index ab9ce4a6bae528375d6a6d697a1cf52a38f53fa6..224e1bdc7e495a996c35450fd4dabbc3876957ab 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -99,6 +99,7 @@ obj-$(CONFIG_MEGARAID_LEGACY) += megaraid.o obj-$(CONFIG_MEGARAID_NEWGEN) += megaraid/ obj-$(CONFIG_MEGARAID_SAS) += megaraid/ obj-$(CONFIG_SCSI_MPT3SAS) += mpt3sas/ +obj-$(CONFIG_SCSI_PS3STOR) += linkdata/ obj-$(CONFIG_SCSI_MPI3MR) += mpi3mr/ obj-$(CONFIG_SCSI_LEAPIORAID) += leapioraid/ obj-$(CONFIG_SCSI_ACARD) += atp870u.o diff --git a/drivers/scsi/linkdata/Kconfig b/drivers/scsi/linkdata/Kconfig new file mode 100644 index 0000000000000000000000000000000000000000..67d670e959e495ba97c7238f0109cba0f248e5ae --- /dev/null +++ b/drivers/scsi/linkdata/Kconfig @@ -0,0 +1 @@ +source "drivers/scsi/linkdata/ps3stor/Kconfig" diff --git a/drivers/scsi/linkdata/Makefile b/drivers/scsi/linkdata/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..7fe24f27878d27eac79673c438ccde64bf1de9da --- /dev/null +++ b/drivers/scsi/linkdata/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_SCSI_PS3STOR) += ps3stor/ diff --git a/drivers/scsi/linkdata/ps3stor/Kconfig b/drivers/scsi/linkdata/ps3stor/Kconfig new file mode 100644 index 0000000000000000000000000000000000000000..1d68d9d96d71e551518b1ca2a89146942e0aecd0 --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/Kconfig @@ -0,0 +1,9 @@ +config SCSI_PS3STOR + tristate "Linkdata ps3stor Storage Controller Device Driver" + depends on PCI && SCSI + select SCSI_SAS_ATTRS + select RAID_ATTRS + select IRQ_POLL + default m + help + This driver supports Linkdata HBA & RAID controllers. diff --git a/drivers/scsi/linkdata/ps3stor/Makefile b/drivers/scsi/linkdata/ps3stor/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..e8f7f225844ce0b35b4b512df083d31aca74b078 --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/Makefile @@ -0,0 +1,56 @@ +# SPDX-License-Identifier: GPL-2.0 + +obj-$(CONFIG_SCSI_PS3STOR) += ps3stor.o + +ps3stor-objs := \ + ps3_cmd_channel.o \ + ps3_device_update.o \ + ps3_ioc_manager.o \ + ps3_mgr_channel.o \ + ps3_platform_utils.o \ + ps3_scsi_cmd_err.o \ + ps3_cmd_complete.o \ + ps3_ioc_state.o \ + ps3_mgr_cmd.o \ + ps3_qos.o \ + ps3_scsih.o \ + ps3_cmd_statistics.o \ + ps3_event.o \ + ps3_ioctl.o \ + ps3_mgr_cmd_err.o \ + ps3_r1x_write_lock.o \ + ps3_scsih_cmd_parse.o \ + ps3_debug.o \ + ps3_io_trace.o \ + ps3_module_para.o \ + ps3_rb_tree.o \ + ps3_scsih_raid_engine.o \ + ps3_device_manager.o \ + ps3_instance_manager.o \ + ps3_irq.o \ + ps3_nvme_resp_to_scsi.o \ + ps3_recovery.o \ + ps3_trace_id_alloc.o \ + ps3_device_manager_sas.o \ + ps3_ioc_adp.o \ + ps3_load.o \ + ps3_pci.o \ + ps3_sas_transport.o \ + ps3_watchdog.o \ + ./linux/ps3_base.o \ + ./linux/ps3_cli.o \ + ./linux/ps3_cli_debug.o \ + ./linux/ps3_driver_log.o \ + ./linux/ps3_dump.o \ + ./linux/ps3_pcie_err_handle.o + +ccflags-y += -DPS3_CFG_RELEASE +ccflags-y += -DPS3_HARDWARE_ASIC +ccflags-y += -DPS3_MODEL_V200 +ccflags-y += -Werror +ccflags-y += $(call cc-option, -Wmaybe-uninitialized) +ccflags-y += \ + -I$(srctree)/drivers/scsi/linkdata/ps3stor \ + -I$(srctree)/drivers/scsi/linkdata/ps3stor/linux \ + -I$(srctree)/drivers/scsi/linkdata/ps3stor/include \ + -I$(srctree)/drivers/scsi/linkdata/ps3stor/include/htp_v200 \ No newline at end of file diff --git a/drivers/scsi/linkdata/ps3stor/include/htp_v200/ps3_htp.h b/drivers/scsi/linkdata/ps3stor/include/htp_v200/ps3_htp.h new file mode 100644 index 0000000000000000000000000000000000000000..d0bfa404f1b01aa4377fe2130dddee6ae60218b5 --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/include/htp_v200/ps3_htp.h @@ -0,0 +1,278 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _PS3_HTP_H_ +#define _PS3_HTP_H_ + +#include "ps3_htp_def.h" +#include "ps3_htp_dev.h" +#include "ps3_htp_req_frame_hw.h" + +#define PS3_DUMP_CTRL_COPY_FINISH 0x1 +#define PS3_DUMP_CTRL_DUMP_ABORT 0x2 +#define PS3_DUMP_CTRL_DUMP_FW_LOG 0x3 +#define PS3_DUMP_CTRL_DUMP_BAR_DATA 0x4 +#define PS3_DUMP_CTRL_DUMP_CORE_FILE 0x5 +#define PS3_DUMP_CTRL_DUMP_END 0x6 +#define PS3_DUMP_CTRL_DUMP_INT_READY 0x7 +#define PS3_DUMP_DMA_DONE (0x1) +#define PS3_DUMP_DMA_ABORT (0x1 << 6) +#define PS3_DUMP_DATA_UNIT_SIZE (0x400) +#define PS3_DREICT_SENSE_DATA_BUF_SIZE 72 + +#define PS3_ATU_FLAG_LOW_BITS_MASK (0x0000FFFF) +#define PS3_ATU_FLAG_HIGH_BITS_MASK (0xFFFFFFFFFFFF0000) +#define PS3_ATU_FLAG_DRIVER_SET (0xC0DE) + +#define PS3_IOCTL_VERSION (0x2000000) + +enum { + HIL_MODEL_SW = 0, + HIL_MODEL_HW, + HIL_MODEL_HW_ENHANCED, + HIL_MODEL_SW_ASSIST, +}; + +enum Ps3DumpType { + PS3_DUMP_TYPE_UNKNOWN = 0, + PS3_DUMP_TYPE_CRASH = 1, + PS3_DUMP_TYPE_FW_LOG = 2, + PS3_DUMP_TYPE_BAR_DATA = 3, +}; + +enum Ps3DumpState { + PS3_DUMP_STATE_INVALID = 0, + PS3_DUMP_STATE_PRE_ABORT, + PS3_DUMP_STATE_ABORTED, + PS3_DUMP_STATE_START, + PS3_DUMP_STATE_COPYING, + PS3_DUMP_STATE_COPY_DONE, + PS3_DUMP_STATE_READY = 7, +}; +enum Ps3CtrlSecurityState { + PS3_CTRL_SECURITY_STATE_DECRYPT = 0, + PS3_CTRL_SECURITY_STATE_ENCRYPT, +}; + +struct Ps3DumpNotifyInfo { + int dumpType; +}; + + +struct PS3LinkErrInfo { + unsigned int invalidDwordCount; + unsigned int runningDisparityErrCount; + unsigned int lossOfDwordSyncCount; + unsigned int phyResetProblemCount; +}; + +enum PhyCtrl { + PS3_SAS_CTRL_UNKNOWN = 0, + PS3_SAS_CTRL_RESET = 1, + PS3_SAS_CTRL_RESET_HARD = 2, + PS3_SAS_CTRL_DISABLE = 3 +}; + + +enum { + PS3_UNLOAD_SUB_TYPE_RESERVED = 0, + PS3_UNLOAD_SUB_TYPE_REMOVE = 1, + PS3_UNLOAD_SUB_TYPE_SHUTDOWN = 2, + PS3_UNLOAD_SUB_TYPE_SUSPEND = 3, +}; + + +enum { + PS3_SUSPEND_TYPE_NONE = 0, + PS3_SUSPEND_TYPE_SLEEP = 1, + PS3_SUSPEND_TYPE_HIBERNATE = 2, +}; + +static inline const char *namePhyCtrl(enum PhyCtrl e) +{ + static const char * const myNames[] = { + [PS3_SAS_CTRL_UNKNOWN] = "PS3_SAS_CTRL_UNKNOWN", + [PS3_SAS_CTRL_RESET] = "PS3_SAS_CTRL_RESET", + [PS3_SAS_CTRL_RESET_HARD] = "PS3_SAS_CTRL_RESET_HARD", + [PS3_SAS_CTRL_DISABLE] = "PS3_SAS_CTRL_DISABLE" + }; + + return myNames[e]; +} + + +struct PS3InitCmdWord { + union { + struct { + unsigned int type : 2; + unsigned int reserved1 : 1; + unsigned int direct : 2; + unsigned int reserved2 : 27; + }; + unsigned int lowAddr; + }; + + unsigned int highAddr; +}; + +struct PS3CmdWord { + unsigned short type : 2; + unsigned short reserved1 : 2; + unsigned short direct : 2; + unsigned short isrSN : 8; + unsigned short reserved2 : 2; + unsigned short cmdFrameID : 13; + unsigned short reserved3 : 3; + unsigned short phyDiskID : 12; + unsigned short reserved4 : 4; + unsigned short virtDiskID : 8; + unsigned short reserved5 : 4; + unsigned short qMask : 4; +}; + + +struct PS3CmdWordSw { + unsigned int type : 2; + unsigned int noReplyWord : 1; + unsigned int cmdFrameID : 13; + unsigned int isrSN : 8; + unsigned int cmdIndex : 8; +}; + + +union PS3CmdWordU32 { + struct PS3CmdWordSw cmdWord; + unsigned int val; +}; + + +union PS3DefaultCmdWord { + struct PS3CmdWord cmdWord; + union { + struct { + unsigned int low; + unsigned int high; + } u; + unsigned long long words; + }; +}; + +enum { + PS3_ISR_ACC_MODE_LATENCY = 0, + PS3_ISR_ACC_MODE_SSD_IOPS, + PS3_ISR_ACC_MODE_HDD_IOPS, + PS3_ISR_ACC_MODE_IOPS_VER0 = 2, + PS3_ISR_ACC_MODE_DEV_IOPS, + PS3_ISR_ACC_MODE_MAX, +}; +struct PS3ReplyFifoDesc { + unsigned long long ReplyFifoBaseAddr; + unsigned int irqNo; + unsigned short depthReplyFifo; + unsigned char isrAccMode; + unsigned char reserved; +}; + +struct PS3ReplyWord { + unsigned short type : 2; + unsigned short diskType : 1; + unsigned short reserved1 : 1; + unsigned short mode : 2; + unsigned short reserved2 : 10; + unsigned short cmdFrameID : 13; + unsigned short reserved3 : 2; + unsigned short reserved4 : 1; + unsigned short retStatus : 15; + unsigned short retType : 1; + unsigned short reserved5 : 12; + unsigned short qMask : 4; +}; + + +struct PS3MgrTaskRespInfo { + unsigned char iocStatus; + unsigned char reserved1; + unsigned short iocLogInfo; + unsigned int terminationCnt; + unsigned int respInfo; + unsigned int reserved2; +}; + + +struct PS3MgrCmdReplyRespInfo { + unsigned char cmdReplyStatus; + unsigned char reserved[15]; +}; + + +union PS3RespDetails { + unsigned int xfer_cnt; + unsigned int respData[4]; + struct PS3MgrTaskRespInfo taskMgrRespInfo; + struct PS3MgrCmdReplyRespInfo replyCmdRespInfo; +}; + + +struct PS3SasDirectRespStatus { + unsigned int status : 8; + unsigned int dataPres : 2; + unsigned int reserved : 22; +}; + + +struct Ps3SasDirectRespFrameIU { + union { + unsigned char reserved0[8]; + unsigned long long mediumErrorLba; + }; + unsigned char reserved1[2]; + unsigned char dataPres; + unsigned char status; + union { + unsigned int reserved2; + unsigned int xfer_cnt; + }; + unsigned int senseDataLen; + unsigned int respDataLen; + unsigned char data[PS3_SENSE_BUFFER_SIZE]; + unsigned char reserved3[8]; +}; + + +struct PS3NormalRespFrame { + union PS3RespDetails respDetail; + unsigned char reserved1[8]; + unsigned char sense[PS3_SENSE_BUFFER_SIZE]; + unsigned char type; + unsigned char reserved2[3]; + unsigned char respStatus; + unsigned char dataPre; + unsigned char reserved3[2]; +}; + +union PS3RespFrame { + struct Ps3SasDirectRespFrameIU sasRespFrame; + struct PS3NormalRespFrame normalRespFrame; +}; + + +struct Ps3DebugMemEntry { + unsigned long long debugMemAddr; + unsigned int debugMemSize; + unsigned int reserved; +}; + + +struct PS3NvmeCmdStatus { + union { + struct { + unsigned short sc : 8; + unsigned short sct : 3; + unsigned short crd : 2; + unsigned short m : 1; + unsigned short dnr : 1; + unsigned short p : 1; + }; + unsigned short cmdStatus; + }; +}; + +#endif diff --git a/drivers/scsi/linkdata/ps3stor/include/htp_v200/ps3_htp_def.h b/drivers/scsi/linkdata/ps3stor/include/htp_v200/ps3_htp_def.h new file mode 100644 index 0000000000000000000000000000000000000000..a6ea2539d68309ce79850869e83544ae48c33389 --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/include/htp_v200/ps3_htp_def.h @@ -0,0 +1,561 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _PS3_HTP_DEF_H_ +#define _PS3_HTP_DEF_H_ + + + + +#define PCIE_DMA_HOST_ADDR_BIT_POS (44) + +#define PCIE_DMA_HOST_ADDR_BIT_POS_SET(addr) \ + (((1ULL) << (PCIE_DMA_HOST_ADDR_BIT_POS)) | (addr)) + +#define PCIE_DMA_HOST_ADDR_BIT_POS_CLEAR(addr) \ + ((~((1ULL) << (PCIE_DMA_HOST_ADDR_BIT_POS))) & (addr)) + +#define PCIE_DMA_HOST_ADDR_BIT_POS_F0 (54) +#define PCIE_DMA_HOST_ADDR_BIT_POS_F1 (53) +#define PCIE_DMA_HOST_ADDR_BIT_POS_VALID (52) + +#define PCIE_DMA_HOST_ADDR_BIT_POS_SET_NEW(bit_pos, addr) \ + ((addr) + ((1ULL) << (bit_pos))) + +#define PCIE_DMA_HOST_ADDR_BIT_POS_CLEAR_NEW(bit_pos, addr) \ + ((addr) - ((1ULL) << (bit_pos))) + + +enum PS3FWDiagKey { + PS3_FW_DIAG_FLUSH = 0X00, + PS3_FW_DIAG_1ST_KEY = 0x52, + PS3_FW_DIAG_2ND_KEY = 0x5F, + PS3_FW_DIAG_3RD_KEY = 0x55, + PS3_FW_DIAG_4TH_KEY = 0x5F, + PS3_FW_DIAG_5TH_KEY = 0x52, + PS3_FW_DIAG_6TH_KEY = 0x45, + PS3_FW_DIAG_7TH_KEY = 0x41, + PS3_FW_DIAG_8TH_KEY = 0x44, + PS3_FW_DIAG_9TH_KEY = 0x59, +}; + + +enum PS3FWStateAct { + PS3_FW_STATE_ACT_INIT_READY = 0X00000001, +}; + + +enum PS3RegDoorBellType { + PS3_REG_DOORBELL_STATE_TO_READY = 1, + PS3_REG_DOORBELL_STATE_TO_FAULT = 2, + PS3_REG_DOORBELL_STATE_TO_HALT = 3, +}; + + +enum PS3FWSoftResetAct { + PS3_FW_STATE_ACT_SHALLOW_SOFT_RESET = 0X00000001, + PS3_FW_STATE_ACT_DEEP_SOFT_RESET = 0X00000002, +}; + +enum PS3PerfModeType { + PS3_PERF_MODE_BALANCE = 0, + PS3_PERF_MODE_IOPS = 1, + PS3_PERF_MODE_LATENCY = 2, +}; + + +enum PS3BitPos { + PS3_BIT_POS_DEFAULT = 0, + PS3_BIT_POS_44 = 1, + PS3_BIT_POS_45 = 2, + PS3_BIT_POS_46 = 3, + PS3_BIT_POS_47 = 4, + PS3_BIT_POS_48 = 5, + PS3_BIT_POS_49 = 6, + PS3_BIT_POS_50 = 7, + PS3_BIT_POS_51 = 8, + PS3_BIT_POS_52 = 9, + PS3_BIT_POS_53 = 10, + PS3_BIT_POS_54 = 11, +}; + + +enum PS3CmdType { + PS3_CMD_INIT_IOC = 0x0, + PS3_CMD_VD_SCSI_IO_RW = 0x1, + PS3_CMD_VD_SCSI_IO_NORW = 0x2, + PS3_CMD_PD_SCSI_IO_RW = 0x3, + PS3_CMD_PD_SCSI_IO_NORW = 0x4, + PS3_CMD_MANAGEMENT = 0x5, + PS3_CMD_SCSI_TASK_MANAGEMENT = 0x6, + PS3_CMD_IOCTL = 0x7, + PS3_CMD_SAS_MANAGEMENT = 0x8, + PS3_CMD_COUNT = 0x9, + PS3_CMD_INVALID = 0xff, +}; + +enum { + PS3_SGL = 0x0, + PS3_PRP = 0x1, +}; + +enum { + PS3_CMD_OPERATOR_TYPE_HOST = 0x0, + PS3_CMD_OPERATOR_TYPE_UEFI = 0x1, + PS3_CMD_OPERATOR_TYPE_IOC = 0x2, +}; + + +enum { + PS3_PCI_IRQ_LEGACY = 0, + PS3_PCI_IRQ_MSI = 1, + PS3_PCI_IRQ_MSIX = 2, +}; + +#define PS3_DRV_SYSTEM_ID_MAX_LEN 64 +#define PS3_SENSE_BUFFER_SIZE (96) +#define PS3_RESP_FRAME_BUFFER_SIZE (128) + + +#define PS3_REQUEST_CONTROL_DIR_NONE 0x00 +#define PS3_REQUEST_CONTROL_DIR_READ 0x01 +#define PS3_REQUEST_CONTROL_DIR_WRITE 0x02 +#define PS3_REQUEST_CONTROL_DIR_BOTH 0x03 + + +#define PS3_MAX_PD_COUNT_IN_SPAN 32 +#define PS3_MAX_SPAN_IN_VD 8 + +#define PS3_IOC_INIT_STATE_MASK 0xFFFF +#define PS3_IOC_RECOVERY_COUNT_MASK 0xFFFFFFFF + + +#define PS3_START_STATE_SPACE 0x0100 + + +#define PS3_DRV_MGR_FLUSH_RETRY_MAX_COUNT 1 + + +#define MAX_MGR_CMD_TOTAL_COUNT (16) + +#define PS3_HOT_RESET_OFFSET \ + (HIL_REG1_PS3_REGISTER_F_PS3_DEBUG10_ADDR - \ + HIL_REG1_PS3_REGISTER_F_BASEADDR) + +union HilRegPs3RegisterHotReset { + unsigned long long val; + struct { + + unsigned char isHotReset : 1; + unsigned char reserved0 : 7; + unsigned char reserved1[7]; + } reg; +}; + + +#define PS3_ATU_SUPPORT_OFFSET \ + (HIL_REG1_PS3_REGISTER_F_PS3_DEBUG8_ADDR - \ + HIL_REG1_PS3_REGISTER_F_BASEADDR) + +union HilRegPs3RegisterFPs3AtuSupport { + unsigned long long val; + struct { + + unsigned char bitPos; + unsigned char reserved[7]; + } reg; +}; + + +#define PS3_CAN_HARD_RESET_OFFSET \ + (HIL_REG1_PS3_REGISTER_F_PS3_DEBUG9_ADDR - \ + HIL_REG1_PS3_REGISTER_F_BASEADDR) + +union HilRegPs3RegisterFPs3CanHardReset { + unsigned long long val; + struct { + + unsigned char canHardReset : 1; + unsigned char reserved0 : 7; + unsigned char reserved1[7]; + } reg; +}; + + +enum PS3FWRunState { + PS3_FW_STATE_UNDEFINED = 0x00, + PS3_FW_STATE_START = 0x01, + PS3_FW_STATE_READY = 0x02, + PS3_FW_STATE_WAIT = 0x03, + PS3_FW_STATE_RUNNING = 0x04, + PS3_FW_STATE_FLUSHING = 0x05, + PS3_FW_STATE_RESET = 0x06, + PS3_FW_STATE_CRITICAL = 0x09, + PS3_FW_STATE_FAULT = 0xE0, + PS3_FW_STATE_HALT = 0xF0, + PS3_FW_STATE_END, + PS3_FW_STATE_MASK = 0xFF, + PS3_FW_STATE_WDT_MASK = 0xFF0000FF, +}; + + +enum PS3FWStartState { + PS3_START_STATE_UNDEFINED = 0x0000, + PS3_START_STATE_INIT_BASE = 0x0100, + PS3_START_STATE_INIT_HARDWARE = 0x0200, + PS3_START_STATE_INIT_SOFTWARE = 0x0300, + PS3_START_STATE_INIT_DATAPATH = 0x0400, + PS3_START_STATE_INIT_THREAD = 0x0500, + PS3_START_STATE_SCAN_DEVICE = 0x0600, + PS3_START_STATE_FLUSH_CACHE = 0x0700, + PS3_START_STATE_INIT_RESET = 0x0800, + PS3_START_STATE_FINISHED = 0x0900, + PS3_START_STATE_MASK = 0xFF00, + PS3_START_STATE_WDT_MASK = 0xFF00FF00, +}; + + +#define PS3_FW_RESET_FLAG (0X00000001) +#define PS3_FW_DIAG_ENABLE (0X00000001) +#define PS3_FW_HARD_RESET_ACT (0X00000001) + + +#define PS3_FW_MAX_CMD_MASK (0X0000FFFF) +#define PS3_FW_MAX_MSIX_VECTORS_MASK (0X0000FFFF) +#define PS3_FW_MAX_CHAIN_SIZE_MASK (0XFFFFFFFF) +#define PS3_FW_MAX_RAID_MAP_SIZE_MASK (0XFFFFFFFF) +#define PS3_FW_MAX_NVME_PAGE_SIZE_MASK (0xFFFFFFFF) + + +#define PS3_FW_INTERRUPT_STATUS_MASK (0X00000001) +#define PS3_FW_INTERRUPT_CMD_INTR_CAP_MASK (0X00000004) +#define PS3_FW_INTERRUPT_CMD_MSI_CAP_MASK (0X00000002) +#define PS3_FW_INTERRUPT_CMD_MSIX_CAP_MASK (0X00000001) +#define PS3_FW_INTERRUPT_CLEAR_MASK (0X00000001) + + +enum PS3FWFeatureSupportMask { + PS3_FW_FEATURE_SUPPORT_SYNC_CACHE = 0X00000001, + PS3_FW_FEATURE_SUPPORT_DMA64 = 0X00000002, +}; + + +enum PS3FWCtrlMask { + PS3_FW_CTRL_CMD_TRIGGER_SNAPSHOT = 0X00000001, + PS3_FW_CTRL_CMD_CRASHDUMP_COLLECTION_DONE = 0X00000002, + PS3_FW_CTRL_CMD_CRASHDUMP_DMA_CLEAR = 0X00000004, +}; + + +enum PS3FWCtrlStatusMask { + PS3_FW_CTRL_STATUS_CRASHDUMP_DONE = 0X00000001, + PS3_FW_CTRL_STATUS_RSVR = 0X00000002, + PS3_FW_CTRL_STATUS_CRASHDUMP_MAP = 0X00000004, +}; + + +enum PS3CmdTrigger { + PS3_CMD_TRIGGER_UNLOAD = 0X0001, + PS3_CMD_TRIGGER_UNLOAD_SUSPEND = 0X0002, +}; + + +enum PS3RegCmdState { + PS3_DOORBELL_DONE = 0X0001, +}; + + +enum PS3Debug12Mask { + PS3_DEBUG12__HOT_RESET = 0X00000001, +}; + + +enum PS3MgrControlFlag { + PS3_REQUEST_CONTROL_SKIP_REFIRE = 0x0, + PS3_REQUEST_CONTROL_SENSE32 = 0x1, + PS3_REQUEST_CONTROL_SENSE64 = 0x2, +}; + + +enum PS3TaskCmdSubType { + PS3_TASK_CMD_SCSI_TASK_ABORT, + PS3_TASK_CMD_SCSI_TASK_RESET, + PS3_TASK_CMD_COUNT, + + PS3_TASK_CMD_INVALID = 0xffff, +}; + +enum PS3MgrCmdSubType { + PS3_MGR_CMD_GET_CTRL_INFO = 0x0, + PS3_MGR_CMD_UNLOAD, + PS3_MGR_CMD_SUBSCRIBE_EVENT, + PS3_MGR_CMD_GET_VD_LIST, + PS3_MGR_CMD_GET_PD_LIST, + PS3_MGR_CMD_GET_VD_INFO, + PS3_MGR_CMD_GET_PD_INFO, + PS3_MGR_CMD_GET_BOOTDRIVE_INFO, + PS3_MGR_CMD_GET_BIOS_INFO, + + PS3_MGR_CMD_GET_SNAPSHOT_ATTR, + PS3_MGR_CMD_SET_CRASH_DUMP, + PS3_MGR_CMD_CANCEL, + PS3_MGR_CMD_ABORT, + PS3_MGR_CMD_DEV_ADD_ACK, + PS3_MGR_CMD_DEV_DEL_DONE, + + PS3_SAS_SMP_REQUEST, + PS3_SAS_GET_LINK_ERR, + PS3_SAS_PHY_CTRL, + PS3_SAS_GET_EXPANDERS, + PS3_SAS_GET_PHY_INFO, + PS3_SAS_GET_EXPANDER_INFO, + + PS3_MGR_CMD_AUTODUMP_NOTIFY, + + PS3_MGR_CMD_SECURITY_RANDOM_GET, + PS3_MGR_CMD_SECURITY_PASSWORD, + PS3_MGR_CMD_WEBSUBSCRIBE_EVENT, + + PS3_MGR_CMD_PRESERVED_INFO_GET, + PS3_MGR_CMD_GET_PD_SN_LIST, + PS3_MGR_CMD_PD_REF_CLEAR, + PS3_MGR_CMD_COUNT, + PS3_MGR_CMD_INVALID = 0xff +}; + +static inline const char *namePS3MgrCmdSubType(enum PS3MgrCmdSubType type) +{ + static const char * const myNames[] = { + [PS3_MGR_CMD_GET_CTRL_INFO] = "PS3_MGR_CMD_GET_CTRL_INFO", + [PS3_MGR_CMD_UNLOAD] = "PS3_MGR_CMD_UNLOAD", + [PS3_MGR_CMD_SUBSCRIBE_EVENT] = "PS3_MGR_CMD_SUBSCRIBE_EVENT", + [PS3_MGR_CMD_GET_VD_LIST] = "PS3_MGR_CMD_GET_VD_LIST", + [PS3_MGR_CMD_GET_PD_LIST] = "PS3_MGR_CMD_GET_PD_LIST", + [PS3_MGR_CMD_GET_VD_INFO] = "PS3_MGR_CMD_GET_VD_INFO", + [PS3_MGR_CMD_GET_PD_INFO] = "PS3_MGR_CMD_GET_PD_INFO", + [PS3_MGR_CMD_GET_BOOTDRIVE_INFO] = + "PS3_MGR_CMD_GET_BOOTDRIVE_INFO", + [PS3_MGR_CMD_GET_BIOS_INFO] = "PS3_MGR_CMD_GET_BIOS_INFO", + + [PS3_MGR_CMD_GET_SNAPSHOT_ATTR] = + "PS3_MGR_CMD_GET_SNAPSHOT_ATTR", + [PS3_MGR_CMD_SET_CRASH_DUMP] = "PS3_MGR_CMD_SET_CRASH_DUMP", + [PS3_MGR_CMD_CANCEL] = "PS3_MGR_CMD_CANCEL", + [PS3_MGR_CMD_ABORT] = "PS3_MGR_CMD_ABORT", + [PS3_MGR_CMD_DEV_ADD_ACK] = "PS3_MGR_CMD_DEV_ADD_ACK", + [PS3_MGR_CMD_DEV_DEL_DONE] = "PS3_MGR_CMD_DEV_DEL_DONE", + + [PS3_SAS_SMP_REQUEST] = "PS3_SAS_SMP_REQUEST", + [PS3_SAS_GET_LINK_ERR] = "PS3_SAS_GET_LINK_ERR", + [PS3_SAS_PHY_CTRL] = "PS3_SAS_PHY_CTRL", + [PS3_SAS_GET_EXPANDERS] = "PS3_SAS_GET_EXPANDERS", + [PS3_SAS_GET_PHY_INFO] = "PS3_SAS_GET_PHY_INFO", + [PS3_SAS_GET_EXPANDER_INFO] = "PS3_SAS_GET_EXPANDER_INFO", + [PS3_MGR_CMD_AUTODUMP_NOTIFY] = "PS3_MGR_CMD_AUTODUMP_NOTIFY", + + [PS3_MGR_CMD_SECURITY_RANDOM_GET] = + "PS3_MGR_CMD_SECURITY_RANDOM_GET", + [PS3_MGR_CMD_SECURITY_PASSWORD] = + "PS3_MGR_CMD_SECURITY_PASSWORD", + [PS3_MGR_CMD_WEBSUBSCRIBE_EVENT] = + "PS3_MGR_CMD_WEBSUBSCRIBE_EVENT", + + [PS3_MGR_CMD_PRESERVED_INFO_GET] = + "PS3_MGR_CMD_PRESERVED_INFO_GET", + [PS3_MGR_CMD_GET_PD_SN_LIST] = "PS3_MGR_CMD_GET_PD_SN_LIST", + [PS3_MGR_CMD_PD_REF_CLEAR] = "PS3_MGR_CMD_PD_REF_CLEAR", + [PS3_MGR_CMD_COUNT] = "PS3_MGR_CMD_INVALID", + }; + + if (type < PS3_MGR_CMD_COUNT && type < ARRAY_SIZE(myNames)) + return myNames[type]; + + return "PS3_MGR_CMD_INVALID"; +} + + +enum PS3CmdIocErrCode { + PS3_IOC_ERR_CODE_OK = 0x00, + PS3_IOC_ERR_CODE_ERR = 0x01, + PS3_IOC_STATE_INVALID_STATUS = 0xFFFF, +}; + +enum PS3CmdStatusCode { + SCSI_STATUS_GOOD = 0x00, + SCSI_STATUS_CHECK_CONDITION = 0x02, + SCSI_STATUS_CONDITION_MET = 0x04, + SCSI_STATUS_BUSY = 0x08, + SCSI_STATUS_RESERVATION_CONFLICT = 0x18, + SCSI_STATUS_TASK_SET_FULL = 0x28, + SCSI_STATUS_ACA_ACTIVE = 0x30, + SCSI_STATUS_TASK_ABORTED = 0x40, + + PS3_STATUS_DEVICE_NOT_FOUND = 0x80, + PS3_STATUS_IO_ABORTED = 0x81, + PS3_STATUS_REQ_ILLEGAL = 0x82, + PS3_STATUS_RESET_FAIL = 0x83, + PS3_STATUS_VD_OFFLINE = 0x84, + PS3_STATUS_ACCESS_BLOCK = 0x85, + PS3_STATUS_INTERNAL_SOFT_ERR = 0x86, + PS3_STATUS_INTERNAL_ERR = 0x87, + PS3_STATUS_HOST_NOT_FOUND = 0x88, + PS3_STATUS_HOST_RESET = 0x89, + PS3_STATUS_PCI_RECOVERY = 0x8A, + PS3_STATUS_VD_MEMBER_OFFLINE = 0x8B, + PS3_STATUS_UNDERRUN = 0x8C, + PS3_STATUS_OVERRUN = 0x8D, + PS3_STATUS_DIF_GRD_ERROR = 0x8E, + PS3_STATUS_DIF_REF_ERROR = 0x8F, + PS3_STATUS_DIF_APP_ERROR = 0x90, + PS3_STATUS_ACCESS_RO = 0x91, +}; + + +enum PS3CmdWordType { + PS3_CMDWORD_TYPE_INIT = 0x00, + PS3_CMDWORD_TYPE_ABORT = 0x00, + PS3_CMDWORD_TYPE_MGR = 0x01, + PS3_CMDWORD_TYPE_READ = 0x02, + PS3_CMDWORD_TYPE_WRITE = 0x03, +}; + +#define PS3_CMD_TYPE_IS_RW(type) \ + ((type) == PS3_CMDWORD_TYPE_READ || (type) == PS3_CMDWORD_TYPE_WRITE) + +enum PS3ReqFrameCtrl { + PS3_DATA_BUF_SGL = 0x00, + + PS3_DATA_BUF_PRP = 0x02, +}; + +enum PS3CmdWordDirect { + PS3_CMDWORD_DIRECT_NORMAL = 0x00, + PS3_CMDWORD_DIRECT_RESERVE = 0x01, + PS3_CMDWORD_DIRECT_OK = 0x02, + PS3_CMDWORD_DIRECT_ADVICE = 0x03, +}; + +enum PS3CmdWordPort { + PS3_CMDWORD_PORT_SAS0 = 0x00, + PS3_CMDWORD_PORT_SAS1 = 0x01, + PS3_CMDWORD_PORT_NVME = 0x02, + PS3_CMDWORD_PORT_RESERVE = 0x03, +}; + +enum PS3CmdWordFormat { + PS3_CMDWORD_FORMAT_FRONTEND = 0x00, + PS3_CMDWORD_FORMAT_HARDWARE = 0x01, +}; + +enum PS3ReplyWordFlag { + PS3_REPLY_WORD_FLAG_SUCCESS = 0x00, + PS3_REPLY_WORD_FLAG_FAIL = 0X01, + PS3_REPLY_WORD_FLAG_REPEAT_REPLY = 0x0F, + PS3_REPLY_WORD_FLAG_INVALID = 0X7FFF, +}; + +enum PS3ReplyWordMask { + PS3_REPLY_WORD_MASK_FLAG = 0X7FFF, +}; + +enum PS3ReplyWordMode { + PS3_REPLY_WORD_MODE_NORMAL = 0x00, + PS3_REPLY_WORD_MODE_DIRECT_ADVICE_TO_NORMAL = 0X01, + PS3_REPLY_WORD_MODE_DIRECT_OK = 0X02, + PS3_REPLY_WORD_MODE_DIRECT_ADVICE_TO_DIRECT = 0X03, +}; + +enum PS3RetType { + PS3_NOT_HARD_RET = 0x00, + PS3_HARD_RET = 0X01, +}; + +enum PS3CmdWordVer { + PS3_CMDWORD_VER_0 = 0x0, + PS3_CMDWORD_VER_1 = 0x1, + PS3_CMDWORD_VER_UPDATING = 0x2, + PS3_CMDWORD_VER_INVALID = 0x3, +}; + +enum PS3CmdWordNoReplyWord { + PS3_CMD_WORD_NEED_REPLY_WORD = 0, + PS3_CMD_WORD_NO_REPLY_WORD = 1, +}; + +enum PS3ChannelType { + PS3_CHAN_TYPE_UNKNOWN = 0, + PS3_CHAN_TYPE_VD = 1, + PS3_CHAN_TYPE_PD = 2, +}; + +enum PS3DiskType { + PS3_DISK_TYPE_UNKNOWN = 0, + PS3_DISK_TYPE_VD = 1, + PS3_DISK_TYPE_PD = 2, +}; + +#define PS3_CONTROL_PAGE_TYPE_BIT_OFFSET (0x1) +#define PS3_CONTROL_PAGE_TYPE_BIT_NUM (0x1) +#define PS3_CONTROL_PAGE_TYPE_MASK (0x1) + +enum PS3PageType { + PS3_CONTROL_PAGE_TYPE_OF_SGE = 0, + PS3_CONTROL_PAGE_TYPE_OF_PRP = 1, + PS3_CONTROL_PAGE_TYPE_MAX = 2, +}; +static inline const char *namePS3DiskType(enum PS3DiskType e) +{ + static const char * const myNames[] = { + [PS3_DISK_TYPE_UNKNOWN] = "PS3_DISK_TYPE_UNKNOWN", + [PS3_DISK_TYPE_VD] = "PS3_DISK_TYPE_VD", + [PS3_DISK_TYPE_PD] = "PS3_DISK_TYPE_PD" + }; + + return myNames[e]; +} + +static inline const char *namePS3ChannelType(enum PS3ChannelType e) +{ + static const char * const myNames[] = { + [PS3_CHAN_TYPE_UNKNOWN] = "PS3_CHAN_TYPE_UNKNOWN", + [PS3_CHAN_TYPE_VD] = "PS3_CHAN_TYPE_VD", + [PS3_CHAN_TYPE_PD] = "PS3_CHAN_TYPE_PD" + }; + + return myNames[e]; +} + +enum PS3DrvMgrErrorCode { + PS3_DRV_MGR_TIMEOUT = 1, + PS3_DRV_MGR_UNRUNING, + PS3_DRV_MGR_INVAL_CMD, + PS3_DRV_MGR_NORESOURCE, + PS3_DRV_MGR_INVAL_PARAM, + PS3_DRV_MGR_DEV_NOEXIST, + PS3_DRV_MGR_DEV_DATA_ERR, + PS3_DRV_MGR_BUSY, + PS3_DRV_MGR_EVT_REPEAT, + PS3_DRV_MGR_EVT_CANCEL_ERR, + PS3_DRV_MGR_FLUSH_FAILED, + PS3_DRV_MGR_SMP_BACKEND_ERR, + PS3_DRV_MGR_LINK_GET_BACKEND_ERR, + PS3_DRV_MGR_PHY_CTL_BACKEND_ERR, + PS3_DRV_MGR_RESTART_COMMAND_RSP, + PS3_DRV_MGR_TM_FAILED, +}; + +enum PS3IoctlRetCode { + PS3_IOCTL_STATUS_OK = 0, + PS3_IOCTL_STATUS_INVALID_REQ = 1, + PS3_IOCTL_STATUS_NO_HBA = 2, + PS3_IOCTL_STATUS_BUSY = 3, + PS3_IOCTL_STATUS_NOT_READY = 4, + PS3_IOCTL_STATUS_INVALIED_PARAM = 5, + PS3_IOCTL_STATUS_REQ_ERR = 6, + PS3_IOCTL_STATUS_NEED_RETRY = 7, +}; + +enum PS3HwVdMaxIOSize { + PS3_ENUM_HW_VD_MAX_IO_SIZE_1M = 0, + PS3_ENUM_HW_VD_MAX_IO_SIZE_OTHER, +}; + +#endif diff --git a/drivers/scsi/linkdata/ps3stor/include/htp_v200/ps3_htp_dev.h b/drivers/scsi/linkdata/ps3stor/include/htp_v200/ps3_htp_dev.h new file mode 100644 index 0000000000000000000000000000000000000000..989017297e9099bfba91da6f333cb197b96b59d4 --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/include/htp_v200/ps3_htp_dev.h @@ -0,0 +1,328 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _PS3_HTP_DEV_H_ +#define _PS3_HTP_DEV_H_ + +#include "ps3_htp_def.h" + +#define PS3_MAX_CHANNEL_NUM 15 +#define PS3_MAX_RANDOM_NUM 32 +#define PS3_MAX_IV_NUM 16 +#define PS3_SECURITY_CIPHER_NUM_MAX 2 +#define PS3_STABLE_WRITES_MASK (0x1) + + +struct PS3IocCtrlProp { + unsigned int enableSnapshot : 1; + unsigned int enableSoftReset : 1; + unsigned int reserved1 : 30; + unsigned int reserved2; +}; + + +struct PS3IocCtrlCapable { + unsigned int supportUnevenSpans : 1; + unsigned int supportJbodSecure : 1; + unsigned int supportNvmePassthru : 1; + unsigned int supportDirectCmd : 1; + unsigned int supportAcceleration : 1; + unsigned int supportSataDirectCmd : 1; + unsigned int supportSataNcq : 1; + unsigned int reserved1 : 25; + unsigned int reserved2[3]; +}; + +#define PS3_IOC_CLUSTER_SERIAL_NO_SIZE 16 + +struct PS3ChannelAttr { + unsigned short channelType : 4; + unsigned short maxDevNum : 12; +}; + +struct PS3ChannelInfo { + unsigned char channelNum; + unsigned char reserved; + struct PS3ChannelAttr channels[PS3_MAX_CHANNEL_NUM]; +}; + +struct PS3QosInfo { + unsigned short tfifoDepth; + unsigned short sataHddQuota; + unsigned short sataSsdQuota; + unsigned short sasHddQuota; + unsigned short sasSsdQuota; + unsigned short nvmeVdQuota; + unsigned short nvmeDirectQuota; + unsigned short nvmeNormalQuota; +}; + + +struct PS3IocCtrlInfo { + + unsigned short maxVdCount; + + unsigned short maxPdCount; + + unsigned int maxSectors; + struct PS3IocCtrlProp properties; + struct PS3IocCtrlCapable capabilities; + + unsigned char scsiTaskAbortTimeout; + unsigned char scsiTaskResetTimeout; + + unsigned short offsetOfVDID; + unsigned char reserved1[2]; + + unsigned short cancelTimeOut; + + unsigned int vdIOThreshold; + + unsigned char iocPerfMode; + + unsigned char vdQueueNum; + + unsigned char ioTimeOut; + unsigned char hwVdMaxIOSize : 4; + unsigned char reserved2 : 4; + + struct PS3ChannelInfo channelInfo; + + struct PS3QosInfo qosInfo; + unsigned short isotoneTimeOut; + unsigned char reserved3[2]; + + unsigned char reserved4[32]; + +}; + +struct PS3Dev { + union { + unsigned short phyDiskID; + unsigned short virtDiskID; + }; + unsigned short softChan : 4; + unsigned short devID : 12; +}; + +union PS3DiskDev { + unsigned int diskID; + struct PS3Dev ps3Dev; +}; + + +struct PS3DiskDevPos { + union { + struct { + unsigned char checkSum; + unsigned char enclId; + unsigned char phyId; + }; + unsigned int diskMagicNum; + }; + union PS3DiskDev diskDev; +}; + +struct PS3PhyDevice { + struct PS3DiskDevPos diskPos; + unsigned char diskState; + unsigned char configFlag; + unsigned char driverType : 4; + unsigned char mediumType : 4; + unsigned char reserved; + unsigned char reserved1[4]; +}; + +struct PS3VirtDevice { + struct PS3DiskDevPos diskPos; + unsigned char accessPolicy; + unsigned char isHidden; + unsigned char diskState; + unsigned char reserved; + unsigned char reserved1[4]; +}; + +union PS3Device { + struct PS3PhyDevice pd; + struct PS3VirtDevice vd; +}; + +struct PS3DevList { + unsigned short count; + unsigned char reserved[6]; + union PS3Device devs[]; +}; + +struct PS3PDInfo { + struct PS3DiskDevPos diskPos; + unsigned char diskState; + unsigned char configFlag; + unsigned char driverType : 4; + unsigned char mediumType : 4; + unsigned char scsiInterfaceType; + unsigned char taskAbortTimeout; + unsigned char taskResetTimeout; + union { + struct { + unsigned char supportNCQ : 1; + unsigned char protect : 1; + unsigned char isDirectDisable : 1; + unsigned char reserved : 5; + }; + unsigned char pdFlags; + }; + unsigned char reserved1; + unsigned short sectorSize; + unsigned char reserved2[2]; + unsigned char enclId; + unsigned char phyId; + unsigned char dmaAddrAlignShift; + unsigned char dmaLenAlignShift; + unsigned char reserved3[4]; + unsigned int maxIOSize; + unsigned int devQueDepth; + unsigned short normalQuota; + unsigned short directQuota; + unsigned char reserved4[20]; +}; + + +struct PS3Extent { + union PS3DiskDev phyDiskID; + unsigned char state; + unsigned char reserved[3]; +}; + +struct PS3Span { + unsigned int spanStripeDataSize; + unsigned char spanState; + unsigned char spanPdNum; + unsigned char reserved[2]; + struct PS3Extent extent[PS3_MAX_PD_COUNT_IN_SPAN]; +}; + +struct PS3VDEntry { + struct PS3DiskDevPos diskPos; + unsigned short sectorSize; + unsigned short stripSize; + unsigned int stripeDataSize; + unsigned short physDrvCnt; + unsigned short diskGrpId; + unsigned char accessPolicy; + + unsigned char reserved1; + unsigned char dmaAddrAlignShift; + unsigned char dmaLenAlignShift; + unsigned char isDirectEnable : 1; + unsigned char isHidden : 1; + unsigned char isNvme : 1; + unsigned char isSsd : 1; + unsigned char bdev_bdi_cap : 2; + unsigned char isWriteDirectEnable : 1; + unsigned char reserved2 : 1; + unsigned char raidLevel; + unsigned char spanCount; + unsigned char diskState; + unsigned short umapBlkDescCnt : 3; + unsigned short umapNumblk : 13; + unsigned short dev_busy_scale; + unsigned long long startLBA; + unsigned long long extentSize; + unsigned long long mapBlock; + unsigned long long capacity; + unsigned char isTaskMgmtEnable; + unsigned char taskAbortTimeout; + unsigned char taskResetTimeout; + unsigned char mapBlockVer; + unsigned int maxIOSize; + unsigned int devQueDepth; + unsigned short virtDiskSeq; + unsigned short normalQuota; + unsigned short directQuota; + unsigned short reserved4[21]; + struct PS3Span span[PS3_MAX_SPAN_IN_VD]; + +}; + +struct PS3VDInfo { + unsigned short count; + unsigned char reserved[6]; + struct PS3VDEntry vds[]; +}; + +struct PS3DrvSysInfo { + unsigned char version; + unsigned char systemIDLen; + unsigned char reserved[6]; + unsigned char systemID[PS3_DRV_SYSTEM_ID_MAX_LEN]; +}; + + +struct PS3PhyInfo { + unsigned long long sasAddr; + unsigned long long attachedSasAddr; + unsigned char phyId; + unsigned char negLinkRate; + unsigned char slotId; + unsigned char attachDevType; + unsigned char initiatorPortProtocol : 4; + unsigned char targetPortProtocols : 4; + unsigned char attachInitiatorPortProtocol : 4; + unsigned char attachTargetPortProtocols : 4; + unsigned char minLinkRateHw : 4; + unsigned char maxLinkRateHw : 4; + unsigned char minLinkRate : 4; + unsigned char maxLinkRate : 4; + unsigned char enable : 1; + unsigned char reserve : 7; + unsigned char reserved[7]; +}; + + +struct PS3ExpanderInfo { + unsigned long long sasAddr; + unsigned long long parentSasAddr; + unsigned char parentId; + unsigned char enclID; + unsigned char devType; + unsigned char phyCount; + unsigned char reserved[4]; +}; + + +struct PS3Expanders { + unsigned char count; + unsigned char reserved[7]; + unsigned long long hbaSasAddr[3]; + struct PS3ExpanderInfo expanders[]; +}; + +struct PS3BiosInfo { + unsigned char biosState; + unsigned char biosMode; + + unsigned char biosAbs; + unsigned char devMaxNum; +}; + +struct PS3BootDriveInfo { + unsigned char hasBootDrive : 1; + unsigned char isPD : 1; + unsigned char reserved_9 : 6; + unsigned char enclID; + unsigned short slotID; + unsigned short vdID; + unsigned char pad[2]; +}; + +struct PS3RandomInfo { + unsigned char randomNum[PS3_MAX_RANDOM_NUM]; + unsigned char iv[PS3_MAX_IV_NUM]; +}; + +struct PS3SecurityPwHead { + unsigned char cipherNum; + unsigned int cipherLegth[PS3_SECURITY_CIPHER_NUM_MAX]; + unsigned int cipherOffset[PS3_SECURITY_CIPHER_NUM_MAX]; + unsigned char iv[PS3_MAX_IV_NUM]; +}; + +#endif diff --git a/drivers/scsi/linkdata/ps3stor/include/htp_v200/ps3_htp_event.h b/drivers/scsi/linkdata/ps3stor/include/htp_v200/ps3_htp_event.h new file mode 100644 index 0000000000000000000000000000000000000000..5c97717b6e75cc0cf877ce6a124fdcfd9d775d7f --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/include/htp_v200/ps3_htp_event.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _PS3_HTP_EVENT_H_ +#define _PS3_HTP_EVENT_H_ + +#include "ps3_htp_def.h" +#include "ps3_htp_dev.h" +#include "ps3_htp_mgr_evt.h" +#include "ps3_evtcode_trans.h" +#define PS3_EVENT_DETAIL_BUF_MAX (20) + + +enum PS3EventLevel { + PS3_EVENT_LEVEL_INFO, + PS3_EVENT_LEVEL_WARN, + PS3_EVENT_LEVEL_CRITICAL, +}; + + +struct PS3EventDetail { + unsigned int eventCode; + unsigned int timestamp; + enum MgrEvtType eventType; + union { + struct PS3DiskDevPos devicePos; + unsigned char EnclId; + }; +}; + +struct PS3EventInfo { + unsigned int eventTypeMap; + unsigned int eventCount; + + + struct PS3EventDetail eventDetail[PS3_EVENT_DETAIL_BUF_MAX]; + unsigned char reserved[8]; +}; +#endif diff --git a/drivers/scsi/linkdata/ps3stor/include/htp_v200/ps3_htp_ioctl.h b/drivers/scsi/linkdata/ps3stor/include/htp_v200/ps3_htp_ioctl.h new file mode 100644 index 0000000000000000000000000000000000000000..033450f75b5b2c539e3d4e7ec64940ac0dcf0f9b --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/include/htp_v200/ps3_htp_ioctl.h @@ -0,0 +1,112 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _PS3_HTP_IOCTL_H_ +#define _PS3_HTP_IOCTL_H_ + +#include "ps3_htp.h" +#include "ps3_htp_reqframe.h" + +#define PS3_MAX_IOCTL_SGE_NUM 16 +#define PS3_IOCTL_SENSE_SIZE 96 +#define PS3_IOCTL_MAX_FRAME_SIZE 128 + +#ifndef PS3_SCSI_HOST_PROC_NAME +#define PS3_SCSI_HOST_PROC_NAME "ps3stor" +#endif + +#define PS3_PRODUCT_MODEL "ps3stor" + +#ifndef PS3_SCSI_HOST_PROC_NAME_V100 +#define PS3_SCSI_HOST_PROC_NAME_V100 "ps3" +#endif +#define PS3_PRODUCT_MODEL_V100 "ps3" + +#define PS3_PSW_PRODUCT_MODEL "psw" + + +struct PS3CmdIoctlHeader { + unsigned char cmdType; + unsigned char version; + unsigned short deviceId; + unsigned short cmdSubType; + unsigned char cmdResult; + unsigned char sglOffset; + unsigned short index; + unsigned short control; + unsigned int sgeCount; + unsigned short timeout; + unsigned char sglChainOffset; + unsigned char syncFlag; + unsigned int abortCmdFrameId; +}; +union PS3IoctlFrame { + unsigned char value[PS3_IOCTL_MAX_FRAME_SIZE]; + struct PS3CmdIoctlHeader header; +}; +#ifdef _WINDOWS + +#define PS3_IOCTL_SIG "ps3stor" +#define PS3_IOCTL_FUNCTION 0x801 +#define PS3_DEBUG_CLI_FUNCTION 0x802 + +#define PS3_CTL_CODE \ + CTL_CODE(FILE_DEVICE_MASS_STORAGE, PS3_IOCTL_FUNCTION, \ + METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) +#define PS3_DBG_CLI_CODE \ + CTL_CODE(FILE_DEVICE_MASS_STORAGE, PS3_DEBUG_CLI_FUNCTION, \ + METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) + +struct PS3IoctlSyncCmd { + unsigned short hostId; + unsigned short sglOffset; + unsigned short sgeCount; + unsigned short reserved; + unsigned long long traceId; + struct PS3Sge Sgl[PS3_MAX_IOCTL_SGE_NUM]; + unsigned char data[]; +}; + +struct _PS3_IO_CONTROL { + SRB_IO_CONTROL SrbHeader; + unsigned long reserved; + struct PS3IoctlSyncCmd ps3Ioctl; +}; +#else + +#define PS3_CMD_IOCTL_SYNC_CMD _IOWR('M', 1, struct PS3IoctlSyncCmd) + +#ifndef __WIN32__ +#define PS3_EVENT_NOTICE_SIG (SIGRTMIN + 7) +enum { + PS3_IOCTL_CMD_NORMAL = 0, + PS3_IOCTL_CMD_WEB_SUBSCRIBE, +}; + +struct PS3IoctlSyncCmd { + unsigned short hostId; + unsigned short sglOffset; + unsigned short sgeCount; + unsigned short reserved1; + unsigned int resultCode; + unsigned char reserved2[4]; + unsigned char sense[PS3_IOCTL_SENSE_SIZE]; + unsigned long long traceId; + unsigned char reserved3[120]; + union PS3IoctlFrame msg; + struct PS3Sge sgl[PS3_MAX_IOCTL_SGE_NUM]; +}; +#endif + +struct PS3IoctlAsynCmd { + unsigned short hostId; + unsigned short reserved1; + + unsigned int seqNum; + + unsigned short eventLevel; + + unsigned short eventType; + unsigned char reserved2[4]; +}; + +#endif +#endif diff --git a/drivers/scsi/linkdata/ps3stor/include/htp_v200/ps3_htp_pci.h b/drivers/scsi/linkdata/ps3stor/include/htp_v200/ps3_htp_pci.h new file mode 100644 index 0000000000000000000000000000000000000000..1d5787f90691892d5d08552c751ae165bb4a0808 --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/include/htp_v200/ps3_htp_pci.h @@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _PS3_HTP_PCI_H_ +#define _PS3_HTP_PCI_H_ + +#define PCI_DEVICE_ID 0x02 + +enum { + PCI_VENDOR_ID_STARS = 0x1eb6, + PCI_VENDOR_ID_PS3_FPGA = 0x1eb6, + PCI_VENDOR_ID_PS3_SWITCH_FPGA = 0x1eb6, + PCI_VENDOR_ID_PS3_ASIC = 0x1666, + PCI_VENDOR_ID_PS3 = 0x1ff2, + PCI_DEVICE_ID_PS3_RAID = 0x0001, + PCI_DEVICE_ID_PS3_HBA = 0x0002, + PCI_DEVICE_ID_PS3_SWITCH = 0x0003, + PCI_DEVICE_ID_PS3_SWITCH_FPGA = 0x6004, + PCI_DEVICE_ID_PS3_RAID_FPGA = 0xabcd, + PCI_DEVICE_ID_STARS_HBA_2120_16i = 0x20a1, + PCI_DEVICE_ID_STARS_IOC_2020_18i = 0x20a2, + PCI_DEVICE_ID_STARS_IOC_2213_16i = 0x20a3, + PCI_DEVICE_ID_STARS_ROC_2020_10i = 0x30a2, + PCI_SUBVERDOR_PS3_SWITCH_FPGA = 0x1eb6, + PCI_SUBSYSTEM_PS3_SWITCH_FPGA = 0x6004, + PCI_CLASS_CODE_PS3_SWITCH_FPGA = 0x058000, +}; + +static inline const char *namePciDevType(unsigned short e) +{ + switch (e) { + case PCI_DEVICE_ID_PS3_RAID: + return "PCI_DEVICE_ID_PS3_RAID"; + case PCI_DEVICE_ID_PS3_RAID_FPGA: + return "PCI_DEVICE_ID_PS3_RAID_FPGA"; + case PCI_DEVICE_ID_PS3_HBA: + return "PCI_DEVICE_ID_PS3_HBA"; + case PCI_DEVICE_ID_PS3_SWITCH: + return "PCI_DEVICE_ID_PS3_SWITCH"; + case PCI_DEVICE_ID_PS3_SWITCH_FPGA: + return "PCI_DEVICE_ID_PS3_SWITCH_FPGA"; + case PCI_DEVICE_ID_STARS_HBA_2120_16i: + return "PCI_DEVICE_ID_STARS_HBA_2120_16i"; + case PCI_DEVICE_ID_STARS_IOC_2020_18i: + return "PCI_DEVICE_ID_STARS_IOC_2020_18i"; + case PCI_DEVICE_ID_STARS_ROC_2020_10i: + return "PCI_DEVICE_ID_STARS_ROC_2020_10i"; + case PCI_DEVICE_ID_STARS_IOC_2213_16i: + return "PCI_DEVICE_ID_STARS_IOC_2213_16i"; + default: + return "PCI_DEVICE_ID_INVALID"; + } +} + +#endif diff --git a/drivers/scsi/linkdata/ps3stor/include/htp_v200/ps3_htp_req_frame_hw.h b/drivers/scsi/linkdata/ps3stor/include/htp_v200/ps3_htp_req_frame_hw.h new file mode 100644 index 0000000000000000000000000000000000000000..9903b6a2d79d398a14d9a8c49eb69e2d0003e2cf --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/include/htp_v200/ps3_htp_req_frame_hw.h @@ -0,0 +1,98 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _PS3_HTP_REQ_FRAME_HW_H_ +#define _PS3_HTP_REQ_FRAME_HW_H_ + +#include "ps3_htp_def.h" + +#define ENCODE_CCS_XFERLEN(x) (((unsigned int)(x) >> 2)) +#define DECODE_CCS_XFERLEN(x) (((unsigned int)(x) << 2)) + +#ifdef _WINDOWS +#define __attribute__(x) +#pragma pack(push, 1) +#endif + +struct PS3NvmeSglDesc { + unsigned long long addr; + union { + struct { + unsigned char reserved[7]; + unsigned char subtype : 4; + unsigned char type : 4; + } generic; + + struct { + unsigned int length; + unsigned char reserved[3]; + unsigned char subtype : 4; + unsigned char type : 4; + } unkeyed; + + struct { + unsigned long long length : 24; + unsigned long long key : 32; + unsigned long long subtype : 4; + unsigned long long type : 4; + } keyed; + }; +}; + +struct PS3NvmeCmdDw0_9 { + unsigned short opcode : 8; + unsigned short fuse : 2; + unsigned short reserved1 : 4; + unsigned short psdt : 2; + unsigned short cID; + + unsigned int nsID; + + unsigned int reserved2; + unsigned int reserved3; + + unsigned long long mPtr; + + + union { + struct { + unsigned long long prp1; + unsigned long long prp2; + } prp; + struct PS3NvmeSglDesc sgl1; + + } dPtr; +}; + + +struct PS3NvmeCommonCmd { + struct PS3NvmeCmdDw0_9 cDW0_9; + + unsigned int cDW10; + unsigned int cDW11; + unsigned int cDW12; + unsigned int cDW13; + unsigned int cDW14; + unsigned int cDW15; +}; + +struct PS3NvmeRWCmd { + struct PS3NvmeCmdDw0_9 cDW0_9; + + unsigned int sLbaLo; + unsigned int sLbaHi; + unsigned int numLba; + unsigned int cDW13; + unsigned int cDW14; + unsigned int cDW15; +}; + + +union PS3NvmeReqFrame { + struct PS3NvmeCommonCmd commonReqFrame; + struct PS3NvmeRWCmd rwReqFrame; +}; + +#ifdef _WINDOWS +#pragma pack(pop) +#endif + +#endif diff --git a/drivers/scsi/linkdata/ps3stor/include/htp_v200/ps3_htp_reqframe.h b/drivers/scsi/linkdata/ps3stor/include/htp_v200/ps3_htp_reqframe.h new file mode 100644 index 0000000000000000000000000000000000000000..f10321c66df1059e3bb988f3c4378e0ec1595f68 --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/include/htp_v200/ps3_htp_reqframe.h @@ -0,0 +1,295 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _PS3_HTP_REQFRAME_H_ +#define _PS3_HTP_REQFRAME_H_ + +#include "ps3_htp_def.h" +#include "ps3_htp_dev.h" +#include "ps3_htp_sas.h" +#include "ps3_htp_req_frame_hw.h" + +enum { + PS3_FRAME_SGE_BUFSIZE = 4096, + PS3_FRAME_SGE_SHIFT = 12, + PS3_FRAME_REQ_SGE_NUM_FE = 8, + PS3_FRAME_REQ_PRP_NUM_FE = 2, + PS3_FRAME_REQ_SGE_NUM_HW = 8, + PS3_FRAME_REQ_PRP_NUM_HW = 2, + PS3_FRAME_REQ_SGE_NUM_MGR = 11, + PS3_FRAME_REQ_EXT_SGE_MIN = 2, + PS3_FRAME_CDB_BUFLEN = 32, + PS3_FRAME_LUN_BUFLEN = 8, + PS3_DEBUG_MEM_ARRAY_MAX_NUM = 16, + PS3_MAX_DMA_MEM_SIZE = 4096, + PS3_MAX_DEBUG_MEM_SIZE_PARA = 65536, + PS3_DRV_NAME_MAX_LEN = 32, + PS3_DRV_VERSION_MAX_LEN = 24, +}; + +enum { + PS3_DATA_DIRECTION_WRITE = 0, + PS3_DATA_DIRECTION_READ = 1, +}; + +enum { + PS3_REQFRAME_FORMAT_FRONTEND = 0, + PS3_REQFRAME_FORMAT_SAS = 1, + PS3_REQFRAME_FORMAT_SATA = 2, + PS3_REQFRAME_FORMAT_NVME = 3, +}; + +enum { + PS3_LINUX_FRAME = 0, + PS3_WINDOWS_FRAME = 1, +}; + +enum { + PS3_COMPAT_VER_DEFAULT = 0, + PS3_COMPAT_VER_1 = 1, + PS3_COMPAT_VER_MAX = 0xffff, +}; + +struct PS3Sge { + unsigned long long addr; + unsigned int length; + unsigned int reserved1 : 30; + unsigned int lastSge : 1; + unsigned int ext : 1; +}; + +struct PS3Prp { + unsigned long long prp1; + unsigned long long prp2; +}; + + + +struct PS3SoftwareZone { + unsigned long long virtDiskLba; + unsigned int numBlocks; + unsigned char opcode; + unsigned char sglOffset; + unsigned char sglFormat : 2; + unsigned char isResendCmd : 1; + unsigned char reserved1 : 5; + unsigned char reserved2; + unsigned short subOpcode; + unsigned short sgeCount : 9; + unsigned short reserved3 : 7; + unsigned char reserved4[4]; +}; + +struct PS3ReqFrameHead { + unsigned char cmdType; + unsigned char cmdSubType; + unsigned short cmdFrameID; + union { + struct { + unsigned int noReplyWord : 1; + unsigned int dataFormat : 1; + unsigned int reqFrameFormat : 2; + unsigned int mapBlockVer : 2; + unsigned int isWrite : 1; + unsigned int isStream1 : 1; + unsigned int reserved : 24; + }; + unsigned int control; + }; + union PS3DiskDev devID; + unsigned short timeout; + unsigned short virtDiskSeq; + unsigned short reserved1[4]; + unsigned long long traceID; +}; + +struct PS3HwReqFrame { + struct PS3ReqFrameHead reqHead; + struct PS3SoftwareZone softwareZone; + unsigned char reserved[8]; + union { + struct IODT_V1 sasReqFrame; + union PS3NvmeReqFrame nvmeReqFrame; + }; + struct PS3Sge sgl[PS3_FRAME_REQ_SGE_NUM_FE]; +}; + +struct PS3VDAccAttr { + unsigned long long firstPdStartLba; + unsigned char firstSpanNo; + unsigned char fisrtSeqInSpan; + unsigned char secondSeqInSapn; + unsigned char thirdSeqInSapn; + unsigned char clineCount; + unsigned char isAccActive : 1; + unsigned char isStream : 1; + unsigned char reserved1 : 6; + unsigned short ioOutStandingCnt; + unsigned char reserved2[16]; +}; + + +struct PS3FrontEndReqFrame { + struct PS3ReqFrameHead reqHead; + unsigned char cdb[PS3_FRAME_CDB_BUFLEN]; + struct PS3VDAccAttr vdAccAttr; + unsigned int dataXferLen; + unsigned char reserved[25]; + unsigned char sgeOffset; + unsigned short sgeCount; + union { + struct PS3Sge sgl[PS3_FRAME_REQ_SGE_NUM_FE]; + struct PS3Prp prp; + }; +}; + +struct PS3MgrDev { + struct PS3DiskDevPos devID; + unsigned short num; + unsigned char devType; + unsigned char reserved[17]; +}; + +struct PS3MgrEvent { + unsigned int eventTypeMap; + unsigned int eventTypeMapProcResult; + unsigned int eventLevel; + unsigned char reserved[20]; +}; + + +struct PS3SasMgr { + unsigned long long sasAddr; + unsigned char enclID; + unsigned char startPhyID; + unsigned char phyCount; + unsigned char reserved1; + unsigned short reqLen; + unsigned char reserved2[2]; +}; + + +struct PS3SasPhySet { + unsigned long long sasAddr; + unsigned char phyID; + unsigned char minLinkRate; + unsigned char maxLinkRate; + unsigned char phyCtrl; + unsigned char reserved[3]; +}; + +union PS3MgrReqDiffValue { + unsigned char word[32]; + unsigned short originalCmdFrameID; + unsigned char eventStart; + struct PS3MgrDev dev; + struct PS3MgrEvent event; + struct PS3SasMgr sasMgr; + struct PS3SasPhySet phySet; + unsigned char unLoadType; + int isRetry; +}; + + +struct PS3MgrReqFrame { + struct PS3ReqFrameHead reqHead; + unsigned short sgeCount; + unsigned char sgeOffset; + unsigned char syncFlag; + unsigned short timeout; + unsigned char abortFlag; + unsigned char pendingFlag; + union PS3MgrReqDiffValue value; + unsigned char osType; + unsigned char suspend_type; + unsigned char reserved[6]; + struct PS3Sge sgl[PS3_FRAME_REQ_SGE_NUM_MGR]; +}; + + +struct PS3MgrTaskReqFrame { + struct PS3ReqFrameHead reqHead; + unsigned short taskID; + unsigned char lun[PS3_FRAME_LUN_BUFLEN]; + unsigned char abortedCmdType; + unsigned char reserved[5]; +}; + + +union PS3ReqFrame { + struct PS3MgrTaskReqFrame taskReq; + struct PS3MgrReqFrame mgrReq; + struct PS3FrontEndReqFrame frontendReq; + struct PS3HwReqFrame hwReq; + unsigned char word[256]; +}; + + +struct PS3DrvInfo { + char drvName[PS3_DRV_NAME_MAX_LEN]; + char drvVersion[PS3_DRV_VERSION_MAX_LEN]; + unsigned long long bus; + unsigned char dev : 5; + unsigned char func : 3; + unsigned char domain_support : 1; + unsigned char reserved : 7; + unsigned short compatVer; + unsigned int domain; + unsigned char reserved1[56]; +}; + + +enum { + PS3_MEM_TYPE_UNKNOWN = 0, + PS3_MEM_TYPE_SO = 1, + PS3_MEM_TYPE_RO = 2, +}; + + +struct PS3HostMemInfo { + unsigned long long startAddr; + unsigned long long endAddr; + unsigned char type; + unsigned char reserved[7]; +}; + +struct PS3InitReqFrame { + struct PS3ReqFrameHead reqHead; + unsigned char ver; + unsigned char reserved0; + unsigned short length; + unsigned char operater; + unsigned char pageSize; + unsigned char pciIrqType; + unsigned char osType; + unsigned char reserved1[6]; + unsigned short msixVector; + unsigned long long timeStamp; + unsigned long long reqFrameBufBaseAddr; + unsigned long long hostMemInfoBaseAddr; + unsigned int hostMemInfoNum; + unsigned char reserved2[20]; + + unsigned long long replyFifoDescBaseAddr; + + unsigned long long respFrameBaseAddr; + unsigned int eventTypeMap; + unsigned short reqFrameMaxNum; + unsigned short respFrameMaxNum; + unsigned long long filterTableAddr; + unsigned int filterTableLen; + unsigned short bufSizePerRespFrame; + unsigned char hilMode; + unsigned char reserved3[33]; + unsigned long long systemInfoBufAddr; + unsigned long long debugMemArrayAddr; + unsigned int debugMemArrayNum; + unsigned int dumpDmaBufLen; + unsigned long long dumpDmaBufAddr; + unsigned int dumpIsrSN; + unsigned short drvInfoBufLen; + unsigned char reserverd4[2]; + unsigned long long drvInfoBufAddr; + unsigned char reserved5[36]; + unsigned int respStatus; +}; + +#endif diff --git a/drivers/scsi/linkdata/ps3stor/include/htp_v200/ps3_htp_sas.h b/drivers/scsi/linkdata/ps3stor/include/htp_v200/ps3_htp_sas.h new file mode 100644 index 0000000000000000000000000000000000000000..2dce4a9e41c51e7f4e6cdd002c5c5d5b33f634dc --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/include/htp_v200/ps3_htp_sas.h @@ -0,0 +1,324 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _PS3_IODT_H_ +#define _PS3_IODT_H_ + +#include "ps3_htp_def.h" + +enum IodtProtocolType { + PROTOCOL_SMP = 0b000, + PROTOCOL_SSP = 0b001, + PROTOCOL_STP = 0b010, + PROTOCOL_DIRT = 0b111, +}; + +enum IodtFrameType { + FRAMETYPE_SSP_CMD = 0b001, + FRAMETYPE_SSP_SMP = 0b001, + FRAMETYPE_SATA_NONDATA = 0b001, + + FRAMETYPE_SSP_TASK = 0b010, + FRAMETYPE_SATA_PIO = 0b010, + + FRAMETYPE_SATA_DMA = 0b011, + + FRAMETYPE_SATA_FPDMA = 0b100, + + FRAMETYPE_SATA_ATAPI = 0b101, + FRAMETYPE_DIRECT = 0b111, +}; + +enum IodtIuSrc { + IU_SRC_MEM = 0b00, + IU_SRC_IODT = 0b01, + IU_SRC_TUPLE = 0b10, + IU_SRC_SATA = 0b11, +}; + +enum IodtSgeMode { + IODT_SGEMODE_DIRECT = 0x0, + IODT_SGEMODE_SGL = 0x1, +}; + +enum IodtEedpMode { + EEDP_MODE_CHECK = 0x0, + EEDP_MODE_INSERT = 0x1, + EEDP_MODE_REPLACE = 0x2, + EEDP_MODE_RMV = 0x3, +}; + +enum AbortCtrl { + + ABT_SASSATA_TASK = 0b00, + ABT_SAS_TASKSET = 0b01, + ABT_LOCAL_BY_DISK = 0b10, + ABT_LOCAL_BY_PORT = 0b11, + + ABT_MGT_IO = 0b00, + ABT_SOFTRESET = 0b01, + ABT_READ_NCQ_ERR_LOG = 0b10, + ABT_SMP = 0b11, +}; + +enum DirectFlag { + DIRECT_FLAG_NORMAL = 0b00, + DIRECT_FLAG_DIRECT = 0b10, +}; + +enum CmdWordType { + CMD_WORD_TYPE_ABORT = 0b00, + CMD_WORD_TYPE_MGT = 0b01, + CMD_WORD_TYPE_READ = 0b10, + CMD_WORD_TYPE_WRITE = 0b11, +}; + +enum SmpFrameType { + SMP_REQ = 0x40, + SMP_RESP = 0x41, +}; + +enum eedpMode { + EEDP_NONE = 0b0000, + EEDP_CHK = 0b0001, + EEDP_INST = 0b0010, + EEDP_REPL = 0b0100, + EEDP_RMV = 0b1000, +}; + +union IoDmaCfg { + struct { + unsigned char eedpEn : 1; + unsigned char eedpSgMod : 1; + unsigned char twoSglMod : 1; + unsigned char sgMode : 1; + unsigned char eedpMode : 4; + }; + unsigned char byte; +}; + + +struct __packed SspTaskFrameIu { + unsigned long long LUN; + unsigned short reserved0; + unsigned char function; + unsigned char reserved1; + unsigned short manageTag; + unsigned char reserved2[14]; +}; + + +union __packed SmpFrameIu { + struct { + unsigned char frameType; + unsigned char reqestBytes[31]; + }; + unsigned char smpIURaw[32]; +}; + + +struct __packed DfifoWordCommon { + + union { + struct { + unsigned short type : 2; + unsigned short rsv1 : 2; + unsigned short direct : 2; + unsigned short rFifoID : 4; + unsigned short rsv2 : 6; + }; + unsigned short WD0; + }; + + + union { + + struct { + union { + + struct { + unsigned short darID : 13; + unsigned short rsv3 : 2; + + unsigned short function : 1; + }; + + struct { + unsigned short manageIptt : 13; + }; + }; + }; + + struct { + union { + + struct { + unsigned short reqFrameID : 13; + }; + + struct { + unsigned short manageReqFrameID : 13; + }; + }; + }; + unsigned short WD1; + }; + + union { + struct { + unsigned short phyDiskID : 12; + unsigned short rsv4 : 2; + unsigned short abortCtrl : 2; + }; + unsigned short WD2; + }; +}; + +struct __packed IODT_V1 { + union { + + struct __packed { + union { + struct { + union { + struct { + unsigned char + protocolType : 3; + unsigned char + frameType : 3; + unsigned char iuSrc : 2; + }; + unsigned char byte0; + }; + + union IoDmaCfg dmaCfg; + }; + unsigned short config; + }; + + + + unsigned short cmdLen : 9; + unsigned short rsv0 : 7; + + union { + struct { + unsigned int taskDarID : 13; + unsigned int resv0 : 19; + }; + struct { + unsigned int dataBufLenDWAlign : 24; + + unsigned int rsvd0 : 1; + unsigned int cmdDir : 1; + unsigned int refTagEn : 1; + unsigned int appTagEn : 1; + unsigned int guardTagEn : 1; + unsigned int refTagInc : 1; + unsigned int rsv1 : 1; + unsigned int aborted : 1; + }; + }; + }; + unsigned long long QW0; + }; + + + union { + struct __packed { + struct DfifoWordCommon commonWord; + unsigned short rsv2 : 1; + unsigned short sataCtl : 1; + unsigned short rsv3 : 2; + unsigned short sasCtl : 1; + unsigned short sataByteBlock0 : 1; + unsigned short sataByteBlock1 : 1; + unsigned short rsv4 : 9; + }; + unsigned long long QW1; + }; + + + union { + unsigned long long dataBaseAddr; + unsigned long long QW2; + }; + + + union { + unsigned long long eedpBaseAddr; + unsigned long long QW3; + }; + + + union { + + struct { + unsigned long long cmdIUAddr; + unsigned long long rsv9; + + unsigned long long refTag : 32; + unsigned long long appTag : 16; + unsigned long long rsv10 : 16; + + unsigned long long rsv11; + } A; + + union { + unsigned char cdb[32]; + struct SspTaskFrameIu taskIU; + union SmpFrameIu smpIU; + } B; + + + struct { + unsigned long long opCode : 8; + unsigned long long rsv12 : 56; + + unsigned long long lba : 48; + unsigned long long rsv13 : 16; + + unsigned long long refTag : 32; + unsigned long long appTag : 16; + unsigned long long rsv14 : 16; + + unsigned long long rsv15; + } C; + + struct { + unsigned int ataCmd : 8; + unsigned int ataDev : 8; + unsigned int ataCtl : 8; + unsigned int ataIcc : 8; + unsigned int ataSecCnt : 16; + unsigned int ataFeature : 16; + + union { + struct { + unsigned long long ataLba : 48; + unsigned long long rsv16 : 16; + }; + struct { + unsigned char lba0; + unsigned char lba1; + unsigned char lba2; + unsigned char lba3; + unsigned char lba4; + unsigned char lba5; + }; + unsigned char lba[6]; + }; + + unsigned int ataAuxiliary; + unsigned int rsv17; + + unsigned long long rsv18; + } D; + }; +}; + +enum { + CMD_LEN_THR = 32, + CMD_LEN_S = 7, + CMD_LEN_L = 11, +}; + +#endif diff --git a/drivers/scsi/linkdata/ps3stor/include/hwapi/include_v200/s1861_regs/s1861_global_baseaddr.h b/drivers/scsi/linkdata/ps3stor/include/hwapi/include_v200/s1861_regs/s1861_global_baseaddr.h new file mode 100644 index 0000000000000000000000000000000000000000..7c908d71c5494eca911b8d9d7f1673a9337e09c3 --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/include/hwapi/include_v200/s1861_regs/s1861_global_baseaddr.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __S1861_GLOBAL_BASEADDR_REG_H__ +#define __S1861_GLOBAL_BASEADDR_REG_H__ +#define HIL_REG0_PS3_REGISTER_F_BASEADDR 0x24000000UL +#define HIL_REG0_PS3_REGISTER_F_SIZE 0x10000UL +#define HIL_REG0_PS3_REQUEST_QUEUE_BASEADDR 0x24010000UL +#define HIL_REG0_PS3_REQUEST_QUEUE_SIZE 0x200UL +#define HIL_REG0_PS3_REGISTER_S_SIZE 0x1FE00UL +#endif diff --git a/drivers/scsi/linkdata/ps3stor/include/hwapi/include_v200/s1861_regs/s1861_hil_reg0_ps3_register_f_reg.h b/drivers/scsi/linkdata/ps3stor/include/hwapi/include_v200/s1861_regs/s1861_hil_reg0_ps3_register_f_reg.h new file mode 100644 index 0000000000000000000000000000000000000000..82baa94e2ae06a9a7f1014c9749f28861f1f6bdc --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/include/hwapi/include_v200/s1861_regs/s1861_hil_reg0_ps3_register_f_reg.h @@ -0,0 +1,1078 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __S1861_HIL_REG0_PS3_REGISTER_F_REG_H__ +#define __S1861_HIL_REG0_PS3_REGISTER_F_REG_H__ +#include "s1861_global_baseaddr.h" +#ifndef __S1861_HIL_REG0_PS3_REGISTER_F_REG_MACRO__ +#define HIL_REG0_PS3_REGISTER_F_PS3_DOORBELL_ADDR \ + (HIL_REG0_PS3_REGISTER_F_BASEADDR + 0x40) +#define HIL_REG0_PS3_REGISTER_F_PS3_DOORBELL_RST (0x0000000000000000) +#define HIL_REG0_PS3_REGISTER_F_PS3_DOORBELL_IRQ_CLEAR_ADDR \ + (HIL_REG0_PS3_REGISTER_F_BASEADDR + 0x48) +#define HIL_REG0_PS3_REGISTER_F_PS3_DOORBELL_IRQ_CLEAR_RST (0x0000000000000000) +#define HIL_REG0_PS3_REGISTER_F_PS3_DOORBELL_IRQ_MASK_ADDR \ + (HIL_REG0_PS3_REGISTER_F_BASEADDR + 0x50) +#define HIL_REG0_PS3_REGISTER_F_PS3_DOORBELL_IRQ_MASK_RST (0x0000000000000000) +#define HIL_REG0_PS3_REGISTER_F_PS3_IRQ_CONTROL_ADDR \ + (HIL_REG0_PS3_REGISTER_F_BASEADDR + 0x58) +#define HIL_REG0_PS3_REGISTER_F_PS3_IRQ_CONTROL_RST (0x0000000000000000) +#define HIL_REG0_PS3_REGISTER_F_PS3_SOFTRESET_KEY_ADDR \ + (HIL_REG0_PS3_REGISTER_F_BASEADDR + 0x100) +#define HIL_REG0_PS3_REGISTER_F_PS3_SOFTRESET_KEY_RST (0x0000000000000000) +#define HIL_REG0_PS3_REGISTER_F_PS3_SOFTRESET_STATE_ADDR \ + (HIL_REG0_PS3_REGISTER_F_BASEADDR + 0x108) +#define HIL_REG0_PS3_REGISTER_F_PS3_SOFTRESET_STATE_RST (0x0000000000000000) +#define HIL_REG0_PS3_REGISTER_F_PS3_SOFTRESET_ADDR \ + (HIL_REG0_PS3_REGISTER_F_BASEADDR + 0x110) +#define HIL_REG0_PS3_REGISTER_F_PS3_SOFTRESET_RST (0x0000000000000000) +#define HIL_REG0_PS3_REGISTER_F_PS3_SOFTRESET_IRQ_CLEAR_ADDR \ + (HIL_REG0_PS3_REGISTER_F_BASEADDR + 0x118) +#define HIL_REG0_PS3_REGISTER_F_PS3_SOFTRESET_IRQ_CLEAR_RST (0x0000000000000000) +#define HIL_REG0_PS3_REGISTER_F_PS3_SOFTRESET_IRQ_MASK_ADDR \ + (HIL_REG0_PS3_REGISTER_F_BASEADDR + 0x120) +#define HIL_REG0_PS3_REGISTER_F_PS3_SOFTRESET_IRQ_MASK_RST (0x0000000000000000) +#define HIL_REG0_PS3_REGISTER_F_PS3_SOFTRESET_KEY_SHIFT_REG_LOW_ADDR \ + (HIL_REG0_PS3_REGISTER_F_BASEADDR + 0x128) +#define HIL_REG0_PS3_REGISTER_F_PS3_SOFTRESET_KEY_SHIFT_REG_LOW_RST \ + (0x0000000000000000) +#define HIL_REG0_PS3_REGISTER_F_PS3_SOFTRESET_KEY_SHIFT_REG_HIGH_ADDR \ + (HIL_REG0_PS3_REGISTER_F_BASEADDR + 0x130) +#define HIL_REG0_PS3_REGISTER_F_PS3_SOFTRESET_KEY_SHIFT_REG_HIGH_RST \ + (0x0000000000000000) +#define HIL_REG0_PS3_REGISTER_F_PS3_SOFTRESET_TIME_CNT_ADDR \ + (HIL_REG0_PS3_REGISTER_F_BASEADDR + 0x138) +#define HIL_REG0_PS3_REGISTER_F_PS3_SOFTRESET_TIME_CNT_RST (0x0000000000000000) +#define HIL_REG0_PS3_REGISTER_F_PS3_SOFTRESET_TIME_OUT_EN_ADDR \ + (HIL_REG0_PS3_REGISTER_F_BASEADDR + 0x140) +#define HIL_REG0_PS3_REGISTER_F_PS3_SOFTRESET_TIME_OUT_EN_RST \ + (0x0000000000000000) +#define HIL_REG0_PS3_REGISTER_F_PS3_HARDRESET_KEY_ADDR \ + (HIL_REG0_PS3_REGISTER_F_BASEADDR + 0x200) +#define HIL_REG0_PS3_REGISTER_F_PS3_HARDRESET_KEY_RST (0x0000000000000000) +#define HIL_REG0_PS3_REGISTER_F_PS3_HARDRESET_STATE_ADDR \ + (HIL_REG0_PS3_REGISTER_F_BASEADDR + 0x208) +#define HIL_REG0_PS3_REGISTER_F_PS3_HARDRESET_STATE_RST (0x0000000000000000) +#define HIL_REG0_PS3_REGISTER_F_PS3_HARDRESET_ADDR \ + (HIL_REG0_PS3_REGISTER_F_BASEADDR + 0x210) +#define HIL_REG0_PS3_REGISTER_F_PS3_HARDRESET_RST (0x0000000000000000) +#define HIL_REG0_PS3_REGISTER_F_PS3_HARDRESET_KEY_SHIFT_REG_LOW_ADDR \ + (HIL_REG0_PS3_REGISTER_F_BASEADDR + 0x218) +#define HIL_REG0_PS3_REGISTER_F_PS3_HARDRESET_KEY_SHIFT_REG_LOW_RST \ + (0x0000000000000000) +#define HIL_REG0_PS3_REGISTER_F_PS3_HARDRESET_KEY_SHIFT_REG_HIGH_ADDR \ + (HIL_REG0_PS3_REGISTER_F_BASEADDR + 0x220) +#define HIL_REG0_PS3_REGISTER_F_PS3_HARDRESET_KEY_SHIFT_REG_HIGH_RST \ + (0x0000000000000000) +#define HIL_REG0_PS3_REGISTER_F_PS3_HARDRESET_TIME_CNT_ADDR \ + (HIL_REG0_PS3_REGISTER_F_BASEADDR + 0x228) +#define HIL_REG0_PS3_REGISTER_F_PS3_HARDRESET_TIME_CNT_RST (0x0000000000000000) +#define HIL_REG0_PS3_REGISTER_F_PS3_HARDRESET_TIME_OUT_EN_ADDR \ + (HIL_REG0_PS3_REGISTER_F_BASEADDR + 0x230) +#define HIL_REG0_PS3_REGISTER_F_PS3_HARDRESET_TIME_OUT_EN_RST \ + (0x0000000000000000) +#define HIL_REG0_PS3_REGISTER_F_PS3_KEY_GAP_CFG_ADDR \ + (HIL_REG0_PS3_REGISTER_F_BASEADDR + 0x238) +#define HIL_REG0_PS3_REGISTER_F_PS3_KEY_GAP_CFG_RST (0x0000000002FAF080) +#define HIL_REG0_PS3_REGISTER_F_PS3_HARDRESET_IRQ_CLEAR_ADDR \ + (HIL_REG0_PS3_REGISTER_F_BASEADDR + 0x240) +#define HIL_REG0_PS3_REGISTER_F_PS3_HARDRESET_IRQ_CLEAR_RST (0x0000000000000000) +#define HIL_REG0_PS3_REGISTER_F_PS3_HARDRESET_IRQ_MASK_ADDR \ + (HIL_REG0_PS3_REGISTER_F_BASEADDR + 0x248) +#define HIL_REG0_PS3_REGISTER_F_PS3_HARDRESET_IRQ_MASK_RST (0x0000000000000001) +#define HIL_REG0_PS3_REGISTER_F_PS3_SOC_FW_STATE_ADDR \ + (HIL_REG0_PS3_REGISTER_F_BASEADDR + 0x300) +#define HIL_REG0_PS3_REGISTER_F_PS3_SOC_FW_STATE_RST (0x0000000000000000) +#define HIL_REG0_PS3_REGISTER_F_PS3_MAX_FW_CMD_ADDR \ + (HIL_REG0_PS3_REGISTER_F_BASEADDR + 0x308) +#define HIL_REG0_PS3_REGISTER_F_PS3_MAX_FW_CMD_RST (0x0000000000001FFF) +#define HIL_REG0_PS3_REGISTER_F_PS3_MAX_CHAIN_SIZE_ADDR \ + (HIL_REG0_PS3_REGISTER_F_BASEADDR + 0x310) +#define HIL_REG0_PS3_REGISTER_F_PS3_MAX_CHAIN_SIZE_RST (0x0000000000000FFF) +#define HIL_REG0_PS3_REGISTER_F_PS3_MAX_VD_INFO_SIZE_ADDR \ + (HIL_REG0_PS3_REGISTER_F_BASEADDR + 0x318) +#define HIL_REG0_PS3_REGISTER_F_PS3_MAX_VD_INFO_SIZE_RST (0x0000000000000000) +#define HIL_REG0_PS3_REGISTER_F_PS3_MAX_NVME_PAGE_SIZE_ADDR \ + (HIL_REG0_PS3_REGISTER_F_BASEADDR + 0x320) +#define HIL_REG0_PS3_REGISTER_F_PS3_MAX_NVME_PAGE_SIZE_RST (0x0000000000000000) +#define HIL_REG0_PS3_REGISTER_F_PS3_FEATURE_SUPPORT_ADDR \ + (HIL_REG0_PS3_REGISTER_F_BASEADDR + 0x328) +#define HIL_REG0_PS3_REGISTER_F_PS3_FEATURE_SUPPORT_RST (0x0000000000000007) +#define HIL_REG0_PS3_REGISTER_F_PS3_FIRMWARE_VERSION_ADDR \ + (HIL_REG0_PS3_REGISTER_F_BASEADDR + 0x330) +#define HIL_REG0_PS3_REGISTER_F_PS3_FIRMWARE_VERSION_RST (0x0000000000000000) +#define HIL_REG0_PS3_REGISTER_F_PS3_MAX_REPLYQUE_ADDR \ + (HIL_REG0_PS3_REGISTER_F_BASEADDR + 0x338) +#define HIL_REG0_PS3_REGISTER_F_PS3_MAX_REPLYQUE_RST (0x000000000000007F) +#define HIL_REG0_PS3_REGISTER_F_PS3_HARDWARE_VERSION_ADDR \ + (HIL_REG0_PS3_REGISTER_F_BASEADDR + 0x340) +#define HIL_REG0_PS3_REGISTER_F_PS3_HARDWARE_VERSION_RST (0x0000000000000000) +#define HIL_REG0_PS3_REGISTER_F_PS3_MGR_QUEUE_DEPTH_ADDR \ + (HIL_REG0_PS3_REGISTER_F_BASEADDR + 0x348) +#define HIL_REG0_PS3_REGISTER_F_PS3_MGR_QUEUE_DEPTH_RST (0x0000000000000400) +#define HIL_REG0_PS3_REGISTER_F_PS3_CMD_QUEUE_DEPTH_ADDR \ + (HIL_REG0_PS3_REGISTER_F_BASEADDR + 0x350) +#define HIL_REG0_PS3_REGISTER_F_PS3_CMD_QUEUE_DEPTH_RST (0x0000000000001000) +#define HIL_REG0_PS3_REGISTER_F_PS3_TFIFO_DEPTH_ADDR \ + (HIL_REG0_PS3_REGISTER_F_BASEADDR + 0x358) +#define HIL_REG0_PS3_REGISTER_F_PS3_TFIFO_DEPTH_RST (0x0000000000000400) +#define HIL_REG0_PS3_REGISTER_F_PS3_MAX_SEC_R1X_CMDS_ADDR \ + (HIL_REG0_PS3_REGISTER_F_BASEADDR + 0x360) +#define HIL_REG0_PS3_REGISTER_F_PS3_MAX_SEC_R1X_CMDS_RST (0x0000000000000000) +#define HIL_REG0_PS3_REGISTER_F_PS3_HIL_ADVICE2DIRECT_CNT0_ADDR \ + (HIL_REG0_PS3_REGISTER_F_BASEADDR + 0x400) +#define HIL_REG0_PS3_REGISTER_F_PS3_HIL_ADVICE2DIRECT_CNT0_RST \ + (0x0000000000000000) +#define HIL_REG0_PS3_REGISTER_F_PS3_HIL_ADVICE2DIRECT_CNT1_ADDR \ + (HIL_REG0_PS3_REGISTER_F_BASEADDR + 0x408) +#define HIL_REG0_PS3_REGISTER_F_PS3_HIL_ADVICE2DIRECT_CNT1_RST \ + (0x0000000000000000) +#define HIL_REG0_PS3_REGISTER_F_PS3_HIL_ADVICE2DIRECT_CNT2_ADDR \ + (HIL_REG0_PS3_REGISTER_F_BASEADDR + 0x410) +#define HIL_REG0_PS3_REGISTER_F_PS3_HIL_ADVICE2DIRECT_CNT2_RST \ + (0x0000000000000000) +#define HIL_REG0_PS3_REGISTER_F_PS3_HIL_ADVICE2DIRECT_CNT3_ADDR \ + (HIL_REG0_PS3_REGISTER_F_BASEADDR + 0x418) +#define HIL_REG0_PS3_REGISTER_F_PS3_HIL_ADVICE2DIRECT_CNT3_RST \ + (0x0000000000000000) +#define HIL_REG0_PS3_REGISTER_F_PS3_HIL_ADVICE2DIRECT_CNT_ALL_ADDR \ + (HIL_REG0_PS3_REGISTER_F_BASEADDR + 0x420) +#define HIL_REG0_PS3_REGISTER_F_PS3_HIL_ADVICE2DIRECT_CNT_ALL_RST \ + (0x0000000000000000) +#define HIL_REG0_PS3_REGISTER_F_PS3_IRQ_STATUS_RPT_ADDR \ + (HIL_REG0_PS3_REGISTER_F_BASEADDR + 0x440) +#define HIL_REG0_PS3_REGISTER_F_PS3_IRQ_STATUS_RPT_RST (0x0000000000000000) +#define HIL_REG0_PS3_REGISTER_F_PS3_DUMP_CTRL_ADDR \ + (HIL_REG0_PS3_REGISTER_F_BASEADDR + 0x500) +#define HIL_REG0_PS3_REGISTER_F_PS3_DUMP_CTRL_RST (0x0000000000000000) +#define HIL_REG0_PS3_REGISTER_F_PS3_DUMP_CTRL_IRQ_CLEAR_ADDR \ + (HIL_REG0_PS3_REGISTER_F_BASEADDR + 0x508) +#define HIL_REG0_PS3_REGISTER_F_PS3_DUMP_CTRL_IRQ_CLEAR_RST (0x0000000000000000) +#define HIL_REG0_PS3_REGISTER_F_PS3_DUMP_CTRL_IRQ_MASK_ADDR \ + (HIL_REG0_PS3_REGISTER_F_BASEADDR + 0x510) +#define HIL_REG0_PS3_REGISTER_F_PS3_DUMP_CTRL_IRQ_MASK_RST (0x0000000000000000) +#define HIL_REG0_PS3_REGISTER_F_PS3_DUMP_STATUS_ADDR \ + (HIL_REG0_PS3_REGISTER_F_BASEADDR + 0x518) +#define HIL_REG0_PS3_REGISTER_F_PS3_DUMP_STATUS_RST (0x0000000000000000) +#define HIL_REG0_PS3_REGISTER_F_PS3_DUMP_DATA_SIZE_ADDR \ + (HIL_REG0_PS3_REGISTER_F_BASEADDR + 0x520) +#define HIL_REG0_PS3_REGISTER_F_PS3_DUMP_DATA_SIZE_RST (0x0000000000000000) +#define HIL_REG0_PS3_REGISTER_F_PS3_CMD_TRIGGER_ADDR \ + (HIL_REG0_PS3_REGISTER_F_BASEADDR + 0x600) +#define HIL_REG0_PS3_REGISTER_F_PS3_CMD_TRIGGER_RST (0x0000000000000000) +#define HIL_REG0_PS3_REGISTER_F_PS3_CMD_TRIGGER_IRQ_CLEAR_ADDR \ + (HIL_REG0_PS3_REGISTER_F_BASEADDR + 0x608) +#define HIL_REG0_PS3_REGISTER_F_PS3_CMD_TRIGGER_IRQ_CLEAR_RST \ + (0x0000000000000000) +#define HIL_REG0_PS3_REGISTER_F_PS3_CMD_TRIGGER_IRQ_MASK_ADDR \ + (HIL_REG0_PS3_REGISTER_F_BASEADDR + 0x610) +#define HIL_REG0_PS3_REGISTER_F_PS3_CMD_TRIGGER_IRQ_MASK_RST \ + (0x0000000000000000) +#define HIL_REG0_PS3_REGISTER_F_PS3_SOFTRESET_COUNTER_ADDR \ + (HIL_REG0_PS3_REGISTER_F_BASEADDR + 0x618) +#define HIL_REG0_PS3_REGISTER_F_PS3_SOFTRESET_COUNTER_RST (0x0000000000000000) +#define HIL_REG0_PS3_REGISTER_F_PS3_REG_CMD_STATE_ADDR \ + (HIL_REG0_PS3_REGISTER_F_BASEADDR + 0x620) +#define HIL_REG0_PS3_REGISTER_F_PS3_REG_CMD_STATE_RST (0x0000000000000000) +#define HIL_REG0_PS3_REGISTER_F_PS3_DEBUG0_ADDR \ + (HIL_REG0_PS3_REGISTER_F_BASEADDR + 0x628) +#define HIL_REG0_PS3_REGISTER_F_PS3_DEBUG0_RST (0x0000000000000000) +#define HIL_REG0_PS3_REGISTER_F_PS3_DEBUG0_IRQ_CLEAR_ADDR \ + (HIL_REG0_PS3_REGISTER_F_BASEADDR + 0x630) +#define HIL_REG0_PS3_REGISTER_F_PS3_DEBUG0_IRQ_CLEAR_RST (0x0000000000000000) +#define HIL_REG0_PS3_REGISTER_F_PS3_DEBUG0_IRQ_MASK_ADDR \ + (HIL_REG0_PS3_REGISTER_F_BASEADDR + 0x638) +#define HIL_REG0_PS3_REGISTER_F_PS3_DEBUG0_IRQ_MASK_RST (0x0000000000000000) +#define HIL_REG0_PS3_REGISTER_F_PS3_DEBUG1_ADDR \ + (HIL_REG0_PS3_REGISTER_F_BASEADDR + 0x640) +#define HIL_REG0_PS3_REGISTER_F_PS3_DEBUG1_RST (0x0000000000000000) +#define HIL_REG0_PS3_REGISTER_F_PS3_DEBUG1_IRQ_CLEAR_ADDR \ + (HIL_REG0_PS3_REGISTER_F_BASEADDR + 0x648) +#define HIL_REG0_PS3_REGISTER_F_PS3_DEBUG1_IRQ_CLEAR_RST (0x0000000000000000) +#define HIL_REG0_PS3_REGISTER_F_PS3_DEBUG1_IRQ_MASK_ADDR \ + (HIL_REG0_PS3_REGISTER_F_BASEADDR + 0x650) +#define HIL_REG0_PS3_REGISTER_F_PS3_DEBUG1_IRQ_MASK_RST (0x0000000000000000) +#define HIL_REG0_PS3_REGISTER_F_PS3_DEBUG2_ADDR \ + (HIL_REG0_PS3_REGISTER_F_BASEADDR + 0x658) +#define HIL_REG0_PS3_REGISTER_F_PS3_DEBUG2_RST (0x0000000000000000) +#define HIL_REG0_PS3_REGISTER_F_PS3_DEBUG2_IRQ_CLEAR_ADDR \ + (HIL_REG0_PS3_REGISTER_F_BASEADDR + 0x660) +#define HIL_REG0_PS3_REGISTER_F_PS3_DEBUG2_IRQ_CLEAR_RST (0x0000000000000000) +#define HIL_REG0_PS3_REGISTER_F_PS3_DEBUG2_IRQ_MASK_ADDR \ + (HIL_REG0_PS3_REGISTER_F_BASEADDR + 0x668) +#define HIL_REG0_PS3_REGISTER_F_PS3_DEBUG2_IRQ_MASK_RST (0x0000000000000000) +#define HIL_REG0_PS3_REGISTER_F_PS3_DEBUG3_ADDR \ + (HIL_REG0_PS3_REGISTER_F_BASEADDR + 0x670) +#define HIL_REG0_PS3_REGISTER_F_PS3_DEBUG3_RST (0x0000000000000000) +#define HIL_REG0_PS3_REGISTER_F_PS3_DEBUG3_IRQ_CLEAR_ADDR \ + (HIL_REG0_PS3_REGISTER_F_BASEADDR + 0x678) +#define HIL_REG0_PS3_REGISTER_F_PS3_DEBUG3_IRQ_CLEAR_RST (0x0000000000000000) +#define HIL_REG0_PS3_REGISTER_F_PS3_DEBUG3_IRQ_MASK_ADDR \ + (HIL_REG0_PS3_REGISTER_F_BASEADDR + 0x680) +#define HIL_REG0_PS3_REGISTER_F_PS3_DEBUG3_IRQ_MASK_RST (0x0000000000000000) +#define HIL_REG0_PS3_REGISTER_F_PS3_DEBUG4_ADDR \ + (HIL_REG0_PS3_REGISTER_F_BASEADDR + 0x688) +#define HIL_REG0_PS3_REGISTER_F_PS3_DEBUG4_RST (0x0000000000000000) +#define HIL_REG0_PS3_REGISTER_F_PS3_DEBUG4_IRQ_CLEAR_ADDR \ + (HIL_REG0_PS3_REGISTER_F_BASEADDR + 0x690) +#define HIL_REG0_PS3_REGISTER_F_PS3_DEBUG4_IRQ_CLEAR_RST (0x0000000000000000) +#define HIL_REG0_PS3_REGISTER_F_PS3_DEBUG4_IRQ_MASK_ADDR \ + (HIL_REG0_PS3_REGISTER_F_BASEADDR + 0x698) +#define HIL_REG0_PS3_REGISTER_F_PS3_DEBUG4_IRQ_MASK_RST (0x0000000000000000) +#define HIL_REG0_PS3_REGISTER_F_PS3_DEBUG5_ADDR \ + (HIL_REG0_PS3_REGISTER_F_BASEADDR + 0x6a0) +#define HIL_REG0_PS3_REGISTER_F_PS3_DEBUG5_RST (0x0000000000000000) +#define HIL_REG0_PS3_REGISTER_F_PS3_DEBUG6_ADDR \ + (HIL_REG0_PS3_REGISTER_F_BASEADDR + 0x6a8) +#define HIL_REG0_PS3_REGISTER_F_PS3_DEBUG6_RST (0x0000000000000000) +#define HIL_REG0_PS3_REGISTER_F_PS3_DEBUG7_ADDR \ + (HIL_REG0_PS3_REGISTER_F_BASEADDR + 0x6b0) +#define HIL_REG0_PS3_REGISTER_F_PS3_DEBUG7_RST (0x0000000000000000) +#define HIL_REG0_PS3_REGISTER_F_PS3_DEBUG8_ADDR \ + (HIL_REG0_PS3_REGISTER_F_BASEADDR + 0x6b8) +#define HIL_REG0_PS3_REGISTER_F_PS3_DEBUG8_RST (0x0000000000000000) +#define HIL_REG0_PS3_REGISTER_F_PS3_DEBUG9_ADDR \ + (HIL_REG0_PS3_REGISTER_F_BASEADDR + 0x6c0) +#define HIL_REG0_PS3_REGISTER_F_PS3_DEBUG9_RST (0x0000000000000000) +#define HIL_REG0_PS3_REGISTER_F_PS3_DEBUG10_ADDR \ + (HIL_REG0_PS3_REGISTER_F_BASEADDR + 0x6c8) +#define HIL_REG0_PS3_REGISTER_F_PS3_DEBUG10_RST (0x0000000000000000) +#define HIL_REG0_PS3_REGISTER_F_PS3_DEBUG11_ADDR \ + (HIL_REG0_PS3_REGISTER_F_BASEADDR + 0x6d0) +#define HIL_REG0_PS3_REGISTER_F_PS3_DEBUG11_RST (0x0000000000000000) +#define HIL_REG0_PS3_REGISTER_F_PS3_DEBUG12_ADDR \ + (HIL_REG0_PS3_REGISTER_F_BASEADDR + 0x6d8) +#define HIL_REG0_PS3_REGISTER_F_PS3_DEBUG12_RST (0x0000000000000000) +#define HIL_REG0_PS3_REGISTER_F_PS3_SESSIONCMD_ADDR_ADDR \ + (HIL_REG0_PS3_REGISTER_F_BASEADDR + 0x700) +#define HIL_REG0_PS3_REGISTER_F_PS3_SESSIONCMD_ADDR_RST (0xFFFFFFFFFFFFFFFF) +#define HIL_REG0_PS3_REGISTER_F_PS3_SESSIONCMD_ADDR_IRQ_CLEAR_ADDR \ + (HIL_REG0_PS3_REGISTER_F_BASEADDR + 0x708) +#define HIL_REG0_PS3_REGISTER_F_PS3_SESSIONCMD_ADDR_IRQ_CLEAR_RST \ + (0x0000000000000000) +#define HIL_REG0_PS3_REGISTER_F_PS3_SESSIONCMD_ADDR_IRQ_MASK_ADDR \ + (HIL_REG0_PS3_REGISTER_F_BASEADDR + 0x710) +#define HIL_REG0_PS3_REGISTER_F_PS3_SESSIONCMD_ADDR_IRQ_MASK_RST \ + (0x0000000000000000) +#endif + +#ifndef __S1861_HIL_REG0_PS3_REGISTER_F_REG_STRUCT__ +union HilReg0Ps3RegisterFPs3Doorbell { + unsigned long long val; + struct { + + unsigned long long cmd : 16; + unsigned long long reserved1 : 48; + } reg; +}; + +union HilReg0Ps3RegisterFPs3DoorbellIrqClear { + unsigned long long val; + struct { + + unsigned long long pulse : 1; + unsigned long long reserved1 : 63; + } reg; +}; + +union HilReg0Ps3RegisterFPs3DoorbellIrqMask { + unsigned long long val; + struct { + + unsigned long long level : 1; + unsigned long long reserved1 : 63; + } reg; +}; + +union HilReg0Ps3RegisterFPs3IrqControl { + unsigned long long val; + struct { + + unsigned long long global : 1; + unsigned long long fwState : 1; + unsigned long long tbd : 30; + unsigned long long reserved3 : 32; + } reg; +}; + +union HilReg0Ps3RegisterFPs3SoftresetKey { + unsigned long long val; + struct { + + unsigned long long ps3SoftresetKey : 8; + unsigned long long reserved1 : 56; + } reg; +}; + +union HilReg0Ps3RegisterFPs3SoftresetState { + unsigned long long val; + struct { + + unsigned long long rpt : 1; + unsigned long long reserved1 : 63; + } reg; +}; + +union HilReg0Ps3RegisterFPs3Softreset { + unsigned long long val; + struct { + + unsigned long long cmd : 8; + unsigned long long reserved1 : 56; + } reg; +}; + +union HilReg0Ps3RegisterFPs3SoftresetIrqClear { + unsigned long long val; + struct { + + unsigned long long pulse : 1; + unsigned long long reserved1 : 63; + } reg; +}; + +union HilReg0Ps3RegisterFPs3SoftresetIrqMask { + unsigned long long val; + struct { + + unsigned long long level : 1; + unsigned long long reserved1 : 63; + } reg; +}; + +union HilReg0Ps3RegisterFPs3SoftresetKeyShiftRegLow { + unsigned long long val; + struct { + + unsigned long long rpt : 64; + } reg; +}; + +union HilReg0Ps3RegisterFPs3SoftresetKeyShiftRegHigh { + unsigned long long val; + struct { + + unsigned long long rpt : 8; + unsigned long long reserved1 : 56; + } reg; +}; + +union HilReg0Ps3RegisterFPs3SoftresetTimeCnt { + unsigned long long val; + struct { + + unsigned long long rpt : 64; + } reg; +}; + +union HilReg0Ps3RegisterFPs3SoftresetTimeOutEn { + unsigned long long val; + struct { + + unsigned long long rpt : 1; + unsigned long long reserved1 : 63; + } reg; +}; + +union HilReg0Ps3RegisterFPs3HardresetKey { + unsigned long long val; + struct { + + unsigned long long ps3HardresetKey : 8; + unsigned long long reserved1 : 56; + } reg; +}; + +union HilReg0Ps3RegisterFPs3HardresetState { + unsigned long long val; + struct { + + unsigned long long rpt : 1; + unsigned long long reserved1 : 63; + } reg; +}; + +union HilReg0Ps3RegisterFPs3Hardreset { + unsigned long long val; + struct { + + unsigned long long config : 8; + unsigned long long reserved1 : 56; + } reg; +}; + +union HilReg0Ps3RegisterFPs3HardresetKeyShiftRegLow { + unsigned long long val; + struct { + + unsigned long long rpt : 64; + } reg; +}; + +union HilReg0Ps3RegisterFPs3HardresetKeyShiftRegHigh { + unsigned long long val; + struct { + + unsigned long long rpt : 8; + unsigned long long reserved1 : 56; + } reg; +}; + +union HilReg0Ps3RegisterFPs3HardresetTimeCnt { + unsigned long long val; + struct { + + unsigned long long rpt : 64; + } reg; +}; + +union HilReg0Ps3RegisterFPs3HardresetTimeOutEn { + unsigned long long val; + struct { + + unsigned long long rpt : 1; + unsigned long long reserved1 : 63; + } reg; +}; + +union HilReg0Ps3RegisterFPs3KeyGapCfg { + unsigned long long val; + struct { + + unsigned long long ps3KeyGapCfg : 64; + } reg; +}; + +union HilReg0Ps3RegisterFPs3HardresetIrqClear { + unsigned long long val; + struct { + + unsigned long long pulse : 1; + unsigned long long reserved1 : 63; + } reg; +}; + +union HilReg0Ps3RegisterFPs3HardresetIrqMask { + unsigned long long val; + struct { + + unsigned long long level : 1; + unsigned long long reserved1 : 63; + } reg; +}; + +union HilReg0Ps3RegisterFPs3SocFwState { + unsigned long long val; + struct { + + unsigned long long ps3SocFwState : 8; + unsigned long long ps3SocFwStartState : 8; + unsigned long long ps3SocBootState : 8; + unsigned long long tbd : 8; + unsigned long long reserved4 : 32; + } reg; +}; + +union HilReg0Ps3RegisterFPs3MaxFwCmd { + unsigned long long val; + struct { + + unsigned long long ps3MaxFwCmd : 16; + unsigned long long reserved1 : 48; + } reg; +}; + +union HilReg0Ps3RegisterFPs3MaxChainSize { + unsigned long long val; + struct { + + unsigned long long ps3MaxChainSize : 32; + unsigned long long reserved1 : 32; + } reg; +}; + +union HilReg0Ps3RegisterFPs3MaxVdInfoSize { + unsigned long long val; + struct { + + unsigned long long ps3MaxVdInfoSize : 32; + unsigned long long reserved1 : 32; + } reg; +}; + +union HilReg0Ps3RegisterFPs3MaxNvmePageSize { + unsigned long long val; + struct { + + unsigned long long ps3MaxNvmePageSize : 32; + unsigned long long reserved1 : 32; + } reg; +}; + +union HilReg0Ps3RegisterFPs3FeatureSupport { + unsigned long long val; + struct { + + unsigned long long multiDevfnSupport : 1; + unsigned long long dmaBit64Support : 1; + unsigned long long debugOcmSupport : 1; + unsigned long long tbd1 : 13; + unsigned long long fwHaltSupport : 1; + unsigned long long sglModeSupport : 1; + unsigned long long dumpCrashSupport : 1; + unsigned long long shallowSoftRecoverySupport : 1; + unsigned long long deepSoftRecoverySupport : 1; + unsigned long long hardRecoverySupport : 1; + unsigned long long tbd2 : 42; + } reg; +}; + +union HilReg0Ps3RegisterFPs3FirmwareVersion { + unsigned long long val; + struct { + + unsigned long long ps3FmVer : 8; + unsigned long long tbd : 24; + unsigned long long reserved2 : 32; + } reg; +}; + +union HilReg0Ps3RegisterFPs3MaxReplyque { + unsigned long long val; + struct { + + unsigned long long ps3MaxReplyque : 16; + unsigned long long reserved1 : 48; + } reg; +}; + +union HilReg0Ps3RegisterFPs3HardwareVersion { + unsigned long long val; + struct { + + unsigned long long chipId : 64; + } reg; +}; + +union HilReg0Ps3RegisterFPs3MgrQueueDepth { + unsigned long long val; + struct { + + unsigned long long ps3MgrQueueDepth : 16; + unsigned long long reserved1 : 48; + } reg; +}; + +union HilReg0Ps3RegisterFPs3CmdQueueDepth { + unsigned long long val; + struct { + + unsigned long long ps3CmdQueueDepth : 16; + unsigned long long reserved1 : 48; + } reg; +}; + +union HilReg0Ps3RegisterFPs3TfifoDepth { + unsigned long long val; + struct { + + unsigned long long ps3TfifoDepth : 16; + unsigned long long reserved1 : 48; + } reg; +}; + +union HilReg0Ps3RegisterFPs3MaxSecR1xCmds { + unsigned long long val; + struct { + + unsigned long long ps3MaxSecR1xCmds : 16; + unsigned long long reserved1 : 48; + } reg; +}; + +union HilReg0Ps3RegisterFPs3HilAdvice2directCnt0 { + unsigned long long val; + struct { + + unsigned long long rpt : 32; + unsigned long long reserved1 : 32; + } reg; +}; + +union HilReg0Ps3RegisterFPs3HilAdvice2directCnt1 { + unsigned long long val; + struct { + + unsigned long long rpt : 32; + unsigned long long reserved1 : 32; + } reg; +}; + +union HilReg0Ps3RegisterFPs3HilAdvice2directCnt2 { + unsigned long long val; + struct { + + unsigned long long rpt : 32; + unsigned long long reserved1 : 32; + } reg; +}; + +union HilReg0Ps3RegisterFPs3HilAdvice2directCnt3 { + unsigned long long val; + struct { + + unsigned long long rpt : 32; + unsigned long long reserved1 : 32; + } reg; +}; + +union HilReg0Ps3RegisterFPs3HilAdvice2directCntAll { + unsigned long long val; + struct { + + unsigned long long rpt : 32; + unsigned long long reserved1 : 32; + } reg; +}; + +union HilReg0Ps3RegisterFPs3IrqStatusRpt { + unsigned long long val; + struct { + + unsigned long long doorbell : 1; + unsigned long long reserved1 : 3; + unsigned long long softreset : 1; + unsigned long long reserved3 : 3; + unsigned long long dumpCtrl : 1; + unsigned long long reserved5 : 3; + unsigned long long debug0 : 1; + unsigned long long reserved7 : 3; + unsigned long long debug1 : 1; + unsigned long long reserved9 : 3; + unsigned long long debug2 : 1; + unsigned long long reserved11 : 3; + unsigned long long debug3 : 1; + unsigned long long reserved13 : 3; + unsigned long long debug4 : 1; + unsigned long long reserved15 : 3; + unsigned long long cmdTrigger : 1; + unsigned long long reserved17 : 3; + unsigned long long hardreset : 1; + unsigned long long reserved19 : 3; + unsigned long long sessioncmdAddr : 1; + unsigned long long reserved21 : 23; + } reg; +}; + +union HilReg0Ps3RegisterFPs3DumpCtrl { + unsigned long long val; + struct { + + unsigned long long cmd : 16; + unsigned long long reserved1 : 48; + } reg; +}; + +union HilReg0Ps3RegisterFPs3DumpCtrlIrqClear { + unsigned long long val; + struct { + + unsigned long long pulse : 1; + unsigned long long reserved1 : 63; + } reg; +}; + +union HilReg0Ps3RegisterFPs3DumpCtrlIrqMask { + unsigned long long val; + struct { + + unsigned long long level : 1; + unsigned long long reserved1 : 63; + } reg; +}; + +union HilReg0Ps3RegisterFPs3DumpStatus { + unsigned long long val; + struct { + + unsigned long long dmaFinish : 1; + unsigned long long hasCrashDump : 1; + unsigned long long hasFwDump : 1; + unsigned long long hasBarDump : 1; + unsigned long long hasAutoDump : 2; + unsigned long long tbd : 10; + unsigned long long reserved6 : 48; + } reg; +}; + +union HilReg0Ps3RegisterFPs3DumpDataSize { + unsigned long long val; + struct { + + unsigned long long ps3DumpDataSize : 32; + unsigned long long reserved1 : 32; + } reg; +}; + +union HilReg0Ps3RegisterFPs3CmdTrigger { + unsigned long long val; + struct { + + unsigned long long cmd : 64; + } reg; +}; + +union HilReg0Ps3RegisterFPs3CmdTriggerIrqClear { + unsigned long long val; + struct { + + unsigned long long pulse : 1; + unsigned long long reserved1 : 63; + } reg; +}; + +union HilReg0Ps3RegisterFPs3CmdTriggerIrqMask { + unsigned long long val; + struct { + + unsigned long long level : 1; + unsigned long long reserved1 : 63; + } reg; +}; + +union HilReg0Ps3RegisterFPs3SoftresetCounter { + unsigned long long val; + struct { + + unsigned long long rpt : 32; + unsigned long long tbd : 32; + } reg; +}; + +union HilReg0Ps3RegisterFPs3RegCmdState { + unsigned long long val; + struct { + + unsigned long long cmd : 64; + } reg; +}; + +union HilReg0Ps3RegisterFPs3Debug0 { + unsigned long long val; + struct { + + unsigned long long cmd : 64; + } reg; +}; + +union HilReg0Ps3RegisterFPs3Debug0IrqClear { + unsigned long long val; + struct { + + unsigned long long pulse : 1; + unsigned long long reserved1 : 63; + } reg; +}; + +union HilReg0Ps3RegisterFPs3Debug0IrqMask { + unsigned long long val; + struct { + + unsigned long long level : 1; + unsigned long long reserved1 : 63; + } reg; +}; + +union HilReg0Ps3RegisterFPs3Debug1 { + unsigned long long val; + struct { + + unsigned long long cmd : 64; + } reg; +}; + +union HilReg0Ps3RegisterFPs3Debug1IrqClear { + unsigned long long val; + struct { + + unsigned long long pulse : 1; + unsigned long long reserved1 : 63; + } reg; +}; + +union HilReg0Ps3RegisterFPs3Debug1IrqMask { + unsigned long long val; + struct { + + unsigned long long level : 1; + unsigned long long reserved1 : 63; + } reg; +}; + +union HilReg0Ps3RegisterFPs3Debug2 { + unsigned long long val; + struct { + + unsigned long long cmd : 64; + } reg; +}; + +union HilReg0Ps3RegisterFPs3Debug2IrqClear { + unsigned long long val; + struct { + + unsigned long long pulse : 1; + unsigned long long reserved1 : 63; + } reg; +}; + +union HilReg0Ps3RegisterFPs3Debug2IrqMask { + unsigned long long val; + struct { + + unsigned long long level : 1; + unsigned long long reserved1 : 63; + } reg; +}; + +union HilReg0Ps3RegisterFPs3Debug3 { + unsigned long long val; + struct { + + unsigned long long cmd : 64; + } reg; +}; + +union HilReg0Ps3RegisterFPs3Debug3IrqClear { + unsigned long long val; + struct { + + unsigned long long pulse : 1; + unsigned long long reserved1 : 63; + } reg; +}; + +union HilReg0Ps3RegisterFPs3Debug3IrqMask { + unsigned long long val; + struct { + + unsigned long long level : 1; + unsigned long long reserved1 : 63; + } reg; +}; + +union HilReg0Ps3RegisterFPs3Debug4 { + unsigned long long val; + struct { + + unsigned long long cmd : 64; + } reg; +}; + +union HilReg0Ps3RegisterFPs3Debug4IrqClear { + unsigned long long val; + struct { + + unsigned long long pulse : 1; + unsigned long long reserved1 : 63; + } reg; +}; + +union HilReg0Ps3RegisterFPs3Debug4IrqMask { + unsigned long long val; + struct { + + unsigned long long level : 1; + unsigned long long reserved1 : 63; + } reg; +}; + +union HilReg0Ps3RegisterFPs3Debug5 { + unsigned long long val; + struct { + + unsigned long long cmd : 64; + } reg; +}; + +union HilReg0Ps3RegisterFPs3Debug6 { + unsigned long long val; + struct { + + unsigned long long cmd : 64; + } reg; +}; + +union HilReg0Ps3RegisterFPs3Debug7 { + unsigned long long val; + struct { + + unsigned long long cmd : 64; + } reg; +}; + +union HilReg0Ps3RegisterFPs3Debug8 { + unsigned long long val; + struct { + + unsigned long long cmd : 64; + } reg; +}; + +union HilReg0Ps3RegisterFPs3Debug9 { + unsigned long long val; + struct { + + unsigned long long cmd : 64; + } reg; +}; + +union HilReg0Ps3RegisterFPs3Debug10 { + unsigned long long val; + struct { + + unsigned long long cmd : 64; + } reg; +}; + +union HilReg0Ps3RegisterFPs3Debug11 { + unsigned long long val; + struct { + + unsigned long long cmd : 64; + } reg; +}; + +union HilReg0Ps3RegisterFPs3Debug12 { + unsigned long long val; + struct { + + unsigned long long cmd : 64; + } reg; +}; + +union HilReg0Ps3RegisterFPs3SessioncmdAddr { + unsigned long long val; + struct { + + unsigned long long cmd : 64; + } reg; +}; + +union HilReg0Ps3RegisterFPs3SessioncmdAddrIrqClear { + unsigned long long val; + struct { + + unsigned long long pulse : 1; + unsigned long long reserved1 : 63; + } reg; +}; + +union HilReg0Ps3RegisterFPs3SessioncmdAddrIrqMask { + unsigned long long val; + struct { + + unsigned long long level : 1; + unsigned long long reserved1 : 63; + } reg; +}; + +struct HilReg0Ps3RegisterF { + + unsigned long long reserved0[8]; + union HilReg0Ps3RegisterFPs3Doorbell ps3Doorbell; + union HilReg0Ps3RegisterFPs3DoorbellIrqClear ps3DoorbellIrqClear; + union HilReg0Ps3RegisterFPs3DoorbellIrqMask ps3DoorbellIrqMask; + union HilReg0Ps3RegisterFPs3IrqControl ps3IrqControl; + unsigned long long reserved1[20]; + union HilReg0Ps3RegisterFPs3SoftresetKey ps3SoftresetKey; + union HilReg0Ps3RegisterFPs3SoftresetState ps3SoftresetState; + union HilReg0Ps3RegisterFPs3Softreset ps3Softreset; + union HilReg0Ps3RegisterFPs3SoftresetIrqClear ps3SoftresetIrqClear; + union HilReg0Ps3RegisterFPs3SoftresetIrqMask ps3SoftresetIrqMask; + + union HilReg0Ps3RegisterFPs3SoftresetKeyShiftRegLow + ps3SoftresetKeyShiftRegLow; + union HilReg0Ps3RegisterFPs3SoftresetKeyShiftRegHigh + ps3SoftresetKeyShiftRegHigh; + union HilReg0Ps3RegisterFPs3SoftresetTimeCnt ps3SoftresetTimeCnt; + union HilReg0Ps3RegisterFPs3SoftresetTimeOutEn ps3SoftresetTimeOutEn; + unsigned long long reserved2[23]; + union HilReg0Ps3RegisterFPs3HardresetKey ps3HardresetKey; + union HilReg0Ps3RegisterFPs3HardresetState ps3HardresetState; + union HilReg0Ps3RegisterFPs3Hardreset ps3Hardreset; + + union HilReg0Ps3RegisterFPs3HardresetKeyShiftRegLow + ps3HardresetKeyShiftRegLow; + union HilReg0Ps3RegisterFPs3HardresetKeyShiftRegHigh + ps3HardresetKeyShiftRegHigh; + union HilReg0Ps3RegisterFPs3HardresetTimeCnt ps3HardresetTimeCnt; + union HilReg0Ps3RegisterFPs3HardresetTimeOutEn ps3HardresetTimeOutEn; + union HilReg0Ps3RegisterFPs3KeyGapCfg ps3KeyGapCfg; + union HilReg0Ps3RegisterFPs3HardresetIrqClear ps3HardresetIrqClear; + union HilReg0Ps3RegisterFPs3HardresetIrqMask ps3HardresetIrqMask; + unsigned long long reserved3[22]; + union HilReg0Ps3RegisterFPs3SocFwState ps3SocFwState; + union HilReg0Ps3RegisterFPs3MaxFwCmd ps3MaxFwCmd; + union HilReg0Ps3RegisterFPs3MaxChainSize ps3MaxChainSize; + union HilReg0Ps3RegisterFPs3MaxVdInfoSize ps3MaxVdInfoSize; + union HilReg0Ps3RegisterFPs3MaxNvmePageSize ps3MaxNvmePageSize; + union HilReg0Ps3RegisterFPs3FeatureSupport ps3FeatureSupport; + union HilReg0Ps3RegisterFPs3FirmwareVersion ps3FirmwareVersion; + union HilReg0Ps3RegisterFPs3MaxReplyque ps3MaxReplyque; + union HilReg0Ps3RegisterFPs3HardwareVersion ps3HardwareVersion; + union HilReg0Ps3RegisterFPs3MgrQueueDepth ps3MgrQueueDepth; + union HilReg0Ps3RegisterFPs3CmdQueueDepth ps3CmdQueueDepth; + union HilReg0Ps3RegisterFPs3TfifoDepth ps3TfifoDepth; + union HilReg0Ps3RegisterFPs3MaxSecR1xCmds ps3MaxSecR1xCmds; + unsigned long long reserved4[19]; + union HilReg0Ps3RegisterFPs3HilAdvice2directCnt0 ps3HilAdvice2directCnt0; + union HilReg0Ps3RegisterFPs3HilAdvice2directCnt1 ps3HilAdvice2directCnt1; + union HilReg0Ps3RegisterFPs3HilAdvice2directCnt2 ps3HilAdvice2directCnt2; + union HilReg0Ps3RegisterFPs3HilAdvice2directCnt3 ps3HilAdvice2directCnt3; + union HilReg0Ps3RegisterFPs3HilAdvice2directCntAll + ps3HilAdvice2directCntAll; + unsigned long long reserved5[3]; + union HilReg0Ps3RegisterFPs3IrqStatusRpt ps3IrqStatusRpt; + unsigned long long reserved6[23]; + union HilReg0Ps3RegisterFPs3DumpCtrl ps3DumpCtrl; + union HilReg0Ps3RegisterFPs3DumpCtrlIrqClear ps3DumpCtrlIrqClear; + union HilReg0Ps3RegisterFPs3DumpCtrlIrqMask ps3DumpCtrlIrqMask; + union HilReg0Ps3RegisterFPs3DumpStatus ps3DumpStatus; + union HilReg0Ps3RegisterFPs3DumpDataSize ps3DumpDataSize; + unsigned long long reserved7[27]; + union HilReg0Ps3RegisterFPs3CmdTrigger ps3CmdTrigger; + union HilReg0Ps3RegisterFPs3CmdTriggerIrqClear ps3CmdTriggerIrqClear; + union HilReg0Ps3RegisterFPs3CmdTriggerIrqMask ps3CmdTriggerIrqMask; + union HilReg0Ps3RegisterFPs3SoftresetCounter ps3SoftresetCounter; + union HilReg0Ps3RegisterFPs3RegCmdState ps3RegCmdState; + union HilReg0Ps3RegisterFPs3Debug0 ps3Debug0; + union HilReg0Ps3RegisterFPs3Debug0IrqClear ps3Debug0IrqClear; + union HilReg0Ps3RegisterFPs3Debug0IrqMask ps3Debug0IrqMask; + union HilReg0Ps3RegisterFPs3Debug1 ps3Debug1; + union HilReg0Ps3RegisterFPs3Debug1IrqClear ps3Debug1IrqClear; + union HilReg0Ps3RegisterFPs3Debug1IrqMask ps3Debug1IrqMask; + union HilReg0Ps3RegisterFPs3Debug2 ps3Debug2; + union HilReg0Ps3RegisterFPs3Debug2IrqClear ps3Debug2IrqClear; + union HilReg0Ps3RegisterFPs3Debug2IrqMask ps3Debug2IrqMask; + union HilReg0Ps3RegisterFPs3Debug3 ps3Debug3; + union HilReg0Ps3RegisterFPs3Debug3IrqClear ps3Debug3IrqClear; + union HilReg0Ps3RegisterFPs3Debug3IrqMask ps3Debug3IrqMask; + union HilReg0Ps3RegisterFPs3Debug4 ps3Debug4; + union HilReg0Ps3RegisterFPs3Debug4IrqClear ps3Debug4IrqClear; + union HilReg0Ps3RegisterFPs3Debug4IrqMask ps3Debug4IrqMask; + union HilReg0Ps3RegisterFPs3Debug5 ps3Debug5; + union HilReg0Ps3RegisterFPs3Debug6 ps3Debug6; + union HilReg0Ps3RegisterFPs3Debug7 ps3Debug7; + union HilReg0Ps3RegisterFPs3Debug8 ps3Debug8; + union HilReg0Ps3RegisterFPs3Debug9 ps3Debug9; + union HilReg0Ps3RegisterFPs3Debug10 ps3Debug10; + union HilReg0Ps3RegisterFPs3Debug11 ps3Debug11; + union HilReg0Ps3RegisterFPs3Debug12 ps3Debug12; + unsigned long long reserved8[4]; + union HilReg0Ps3RegisterFPs3SessioncmdAddr ps3SessioncmdAddr; + union HilReg0Ps3RegisterFPs3SessioncmdAddrIrqClear + ps3SessioncmdAddrIrqClear; + union HilReg0Ps3RegisterFPs3SessioncmdAddrIrqMask + ps3SessioncmdAddrIrqMask; +}; +#endif +#endif diff --git a/drivers/scsi/linkdata/ps3stor/include/hwapi/include_v200/s1861_regs/s1861_hil_reg0_ps3_register_s_reg.h b/drivers/scsi/linkdata/ps3stor/include/hwapi/include_v200/s1861_regs/s1861_hil_reg0_ps3_register_s_reg.h new file mode 100644 index 0000000000000000000000000000000000000000..7744917493beca154cb40ffe684d39a9ea7dcd63 --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/include/hwapi/include_v200/s1861_regs/s1861_hil_reg0_ps3_register_s_reg.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __S1861_HIL_REG0_PS3_REGISTER_S_REG_H__ +#define __S1861_HIL_REG0_PS3_REGISTER_S_REG_H__ +#include "s1861_global_baseaddr.h" +#ifndef __S1861_HIL_REG0_PS3_REGISTER_S_REG_MACRO__ +#define HIL_REG0_PS3_REGISTER_S_PS3_FUNCTION_LOCK_ADDR \ + (HIL_REG0_PS3_REGISTER_S_BASEADDR + 0x0) +#define HIL_REG0_PS3_REGISTER_S_PS3_FUNCTION_LOCK_RST (0x0000000000000000) +#define HIL_REG0_PS3_REGISTER_S_PS3_FUNCTION_LOCK_OWNER_ADDR \ + (HIL_REG0_PS3_REGISTER_S_BASEADDR + 0x8) +#define HIL_REG0_PS3_REGISTER_S_PS3_FUNCTION_LOCK_OWNER_RST (0x0000000000000003) +#endif + +#ifndef __S1861_HIL_REG0_PS3_REGISTER_S_REG_STRUCT__ +union HilReg0Ps3RegisterSPs3FucntionLock { + unsigned long long val; + struct { + + unsigned long long lock : 1; + unsigned long long reserved1 : 63; + } reg; +}; + +union HilReg0Ps3RegisterSPs3FunctionLockOwner { + unsigned long long val; + struct { + + unsigned long long display : 2; + unsigned long long reserved1 : 62; + } reg; +}; + +struct HilReg0Ps3RegisterS { + + union HilReg0Ps3RegisterSPs3FucntionLock ps3FucntionLock; + union HilReg0Ps3RegisterSPs3FunctionLockOwner ps3FunctionLockOwner; +}; +#endif +#endif diff --git a/drivers/scsi/linkdata/ps3stor/include/hwapi/include_v200/s1861_regs/s1861_hil_reg0_ps3_request_queue_reg.h b/drivers/scsi/linkdata/ps3stor/include/hwapi/include_v200/s1861_regs/s1861_hil_reg0_ps3_request_queue_reg.h new file mode 100644 index 0000000000000000000000000000000000000000..8f03eb43676ef95378c7d8f3a5b1e7eaf098c68a --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/include/hwapi/include_v200/s1861_regs/s1861_hil_reg0_ps3_request_queue_reg.h @@ -0,0 +1,424 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __S1861_HIL_REG0_PS3_REQUEST_QUEUE_REG_H__ +#define __S1861_HIL_REG0_PS3_REQUEST_QUEUE_REG_H__ +#include "s1861_global_baseaddr.h" +#ifndef __S1861_HIL_REG0_PS3_REQUEST_QUEUE_REG_MACRO__ +#define HIL_REG0_PS3_REQUEST_QUEUE_PS3_REQUEST_QUEUE_ADDR \ + (HIL_REG0_PS3_REQUEST_QUEUE_BASEADDR + 0x0) +#define HIL_REG0_PS3_REQUEST_QUEUE_PS3_REQUEST_QUEUE_RST (0xFFFFFFFFFFFFFFFF) +#define HIL_REG0_PS3_REQUEST_QUEUE_FIFOERRCNT_ADDR \ + (HIL_REG0_PS3_REQUEST_QUEUE_BASEADDR + 0x8) +#define HIL_REG0_PS3_REQUEST_QUEUE_FIFOERRCNT_RST (0x0000000000000000) +#define HIL_REG0_PS3_REQUEST_QUEUE_FIFOSTATUS_ADDR \ + (HIL_REG0_PS3_REQUEST_QUEUE_BASEADDR + 0x10) +#define HIL_REG0_PS3_REQUEST_QUEUE_FIFOSTATUS_RST (0x0000000C1FFF0000) +#define HIL_REG0_PS3_REQUEST_QUEUE_FIFOLEVELCONFIG_ADDR \ + (HIL_REG0_PS3_REQUEST_QUEUE_BASEADDR + 0x18) +#define HIL_REG0_PS3_REQUEST_QUEUE_FIFOLEVELCONFIG_RST (0x0000000300000000) +#define HIL_REG0_PS3_REQUEST_QUEUE_FIFORST_ADDR \ + (HIL_REG0_PS3_REQUEST_QUEUE_BASEADDR + 0x20) +#define HIL_REG0_PS3_REQUEST_QUEUE_FIFORST_RST (0x0000000000000000) +#define HIL_REG0_PS3_REQUEST_QUEUE_FIFOIOCNT_ADDR \ + (HIL_REG0_PS3_REQUEST_QUEUE_BASEADDR + 0x28) +#define HIL_REG0_PS3_REQUEST_QUEUE_FIFOIOCNT_RST (0x0000000000000000) +#define HIL_REG0_PS3_REQUEST_QUEUE_FIFOFLOWCNT_ADDR \ + (HIL_REG0_PS3_REQUEST_QUEUE_BASEADDR + 0x30) +#define HIL_REG0_PS3_REQUEST_QUEUE_FIFOFLOWCNT_RST (0x0000000000000000) +#define HIL_REG0_PS3_REQUEST_QUEUE_FIFO_INT_STATUS_ADDR \ + (HIL_REG0_PS3_REQUEST_QUEUE_BASEADDR + 0x38) +#define HIL_REG0_PS3_REQUEST_QUEUE_FIFO_INT_STATUS_RST (0x0000000000000000) +#define HIL_REG0_PS3_REQUEST_QUEUE_FIFO_INT_SET_ADDR \ + (HIL_REG0_PS3_REQUEST_QUEUE_BASEADDR + 0x40) +#define HIL_REG0_PS3_REQUEST_QUEUE_FIFO_INT_SET_RST (0x0000000000000000) +#define HIL_REG0_PS3_REQUEST_QUEUE_FIFO_INT_CLR_ADDR \ + (HIL_REG0_PS3_REQUEST_QUEUE_BASEADDR + 0x48) +#define HIL_REG0_PS3_REQUEST_QUEUE_FIFO_INT_CLR_RST (0x0000000000000000) +#define HIL_REG0_PS3_REQUEST_QUEUE_FIFO_INT_MASK_ADDR \ + (HIL_REG0_PS3_REQUEST_QUEUE_BASEADDR + 0x50) +#define HIL_REG0_PS3_REQUEST_QUEUE_FIFO_INT_MASK_RST (0x0000000000000000) +#define HIL_REG0_PS3_REQUEST_QUEUE_FIFO_CNT_CLR_ADDR \ + (HIL_REG0_PS3_REQUEST_QUEUE_BASEADDR + 0x58) +#define HIL_REG0_PS3_REQUEST_QUEUE_FIFO_CNT_CLR_RST (0x0000000000000000) +#define HIL_REG0_PS3_REQUEST_QUEUE_FIFOORDERERROR_ADDR \ + (HIL_REG0_PS3_REQUEST_QUEUE_BASEADDR + 0x60) +#define HIL_REG0_PS3_REQUEST_QUEUE_FIFOORDERERROR_RST (0x0000000000000000) +#define HIL_REG0_PS3_REQUEST_QUEUE_FIFODINSHIFT_ADDR(_n) \ + (HIL_REG0_PS3_REQUEST_QUEUE_BASEADDR + 0x68 + (_n) * 0x8) +#define HIL_REG0_PS3_REQUEST_QUEUE_FIFODINSHIFT_RST (0x0000000000000000) +#define HIL_REG0_PS3_REQUEST_QUEUE_FIFODOUTSHIFT_ADDR(_n) \ + (HIL_REG0_PS3_REQUEST_QUEUE_BASEADDR + 0x88 + (_n) * 0x8) +#define HIL_REG0_PS3_REQUEST_QUEUE_FIFODOUTSHIFT_RST (0x0000000000000000) +#define HIL_REG0_PS3_REQUEST_QUEUE_FIFOSTATUS_MAXLEVEL_ADDR \ + (HIL_REG0_PS3_REQUEST_QUEUE_BASEADDR + 0xa8) +#define HIL_REG0_PS3_REQUEST_QUEUE_FIFOSTATUS_MAXLEVEL_RST (0x0000000000000000) +#define HIL_REG0_PS3_REQUEST_QUEUE_FIFOINIT_ADDR \ + (HIL_REG0_PS3_REQUEST_QUEUE_BASEADDR + 0xb0) +#define HIL_REG0_PS3_REQUEST_QUEUE_FIFOINIT_RST (0x0000000000000002) +#define HIL_REG0_PS3_REQUEST_QUEUE_FIFOINIT_EN_ADDR \ + (HIL_REG0_PS3_REQUEST_QUEUE_BASEADDR + 0xb8) +#define HIL_REG0_PS3_REQUEST_QUEUE_FIFOINIT_EN_RST (0x0000000000000000) +#define HIL_REG0_PS3_REQUEST_QUEUE_FIFOINIT_MAX_ADDR \ + (HIL_REG0_PS3_REQUEST_QUEUE_BASEADDR + 0xc0) +#define HIL_REG0_PS3_REQUEST_QUEUE_FIFOINIT_MAX_RST (0x0000000000000000) +#define HIL_REG0_PS3_REQUEST_QUEUE_FIFOSTATUS_ECC_CNT_ADDR \ + (HIL_REG0_PS3_REQUEST_QUEUE_BASEADDR + 0xc8) +#define HIL_REG0_PS3_REQUEST_QUEUE_FIFOSTATUS_ECC_CNT_RST (0x0000000000000000) +#define HIL_REG0_PS3_REQUEST_QUEUE_FIFOSTATUS_ECC_ADDR_ADDR \ + (HIL_REG0_PS3_REQUEST_QUEUE_BASEADDR + 0xd0) +#define HIL_REG0_PS3_REQUEST_QUEUE_FIFOSTATUS_ECC_ADDR_RST (0x0000000000000000) +#define HIL_REG0_PS3_REQUEST_QUEUE_FIFO_DECODER_OVERFLOW_ADDR \ + (HIL_REG0_PS3_REQUEST_QUEUE_BASEADDR + 0xd8) +#define HIL_REG0_PS3_REQUEST_QUEUE_FIFO_DECODER_OVERFLOW_RST \ + (0x000000000000003F) +#define HIL_REG0_PS3_REQUEST_QUEUE_FIFO_ECC_BAD_PROJECT_ADDR \ + (HIL_REG0_PS3_REQUEST_QUEUE_BASEADDR + 0xe0) +#define HIL_REG0_PS3_REQUEST_QUEUE_FIFO_ECC_BAD_PROJECT_RST (0x0000000000000001) +#define HIL_REG0_PS3_REQUEST_QUEUE_FIFOOVERFLOW_WORD_ADDR \ + (HIL_REG0_PS3_REQUEST_QUEUE_BASEADDR + 0xe8) +#define HIL_REG0_PS3_REQUEST_QUEUE_FIFOOVERFLOW_WORD_RST (0x0000000000000000) +#define HIL_REG0_PS3_REQUEST_QUEUE_FIFOLEVELMONITORCTL_ADDR \ + (HIL_REG0_PS3_REQUEST_QUEUE_BASEADDR + 0xf0) +#define HIL_REG0_PS3_REQUEST_QUEUE_FIFOLEVELMONITORCTL_RST (0x0000000000000000) +#define HIL_REG0_PS3_REQUEST_QUEUE_FIFOLEVELMONITORCNTCLR_ADDR \ + (HIL_REG0_PS3_REQUEST_QUEUE_BASEADDR + 0xf8) +#define HIL_REG0_PS3_REQUEST_QUEUE_FIFOLEVELMONITORCNTCLR_RST \ + (0x0000000000000000) +#define HIL_REG0_PS3_REQUEST_QUEUE_FIFOLEVELMONITORLOW_ADDR \ + (HIL_REG0_PS3_REQUEST_QUEUE_BASEADDR + 0x100) +#define HIL_REG0_PS3_REQUEST_QUEUE_FIFOLEVELMONITORLOW_RST (0x0000000000000000) +#define HIL_REG0_PS3_REQUEST_QUEUE_FIFOLEVELMONITORMID_ADDR \ + (HIL_REG0_PS3_REQUEST_QUEUE_BASEADDR + 0x108) +#define HIL_REG0_PS3_REQUEST_QUEUE_FIFOLEVELMONITORMID_RST (0x0000000000000000) +#define HIL_REG0_PS3_REQUEST_QUEUE_FIFOLEVELMONITORHIGH_ADDR \ + (HIL_REG0_PS3_REQUEST_QUEUE_BASEADDR + 0x110) +#define HIL_REG0_PS3_REQUEST_QUEUE_FIFOLEVELMONITORHIGH_RST (0x0000000000000000) +#endif + +#ifndef __S1861_HIL_REG0_PS3_REQUEST_QUEUE_REG_STRUCT__ +union HilReg0Ps3RequestQueuePs3RequestQueue { + unsigned long long val; + struct { + + unsigned long long port : 64; + } reg; +}; + +union HilReg0Ps3RequestQueueFifoErrCnt { + unsigned long long val; + struct { + + unsigned long long waddrerr : 32; + unsigned long long reserved1 : 32; + } reg; +}; + +union HilReg0Ps3RequestQueueFifoStatus { + unsigned long long val; + struct { + + unsigned long long filled : 16; + unsigned long long fifoDepth : 16; + unsigned long long almostfull : 1; + unsigned long long full : 1; + unsigned long long almostempty : 1; + unsigned long long empty : 1; + unsigned long long reserved6 : 28; + } reg; +}; + +union HilReg0Ps3RequestQueueFifoLevelConfig { + unsigned long long val; + struct { + + unsigned long long cfgAempty : 16; + unsigned long long cfgAfull : 16; + unsigned long long emptyProtect : 1; + unsigned long long fullProtect : 1; + unsigned long long reserved4 : 30; + } reg; +}; + +union HilReg0Ps3RequestQueueFifoRst { + unsigned long long val; + struct { + + unsigned long long resetPls : 1; + unsigned long long reserved1 : 63; + } reg; +}; + +union HilReg0Ps3RequestQueueFifoIOCnt { + unsigned long long val; + struct { + + unsigned long long wr : 32; + unsigned long long rd : 32; + } reg; +}; + +union HilReg0Ps3RequestQueueFifoFlowCnt { + unsigned long long val; + struct { + + unsigned long long overflow : 32; + unsigned long long underflow : 32; + } reg; +}; + +union HilReg0Ps3RequestQueueFifoIntStatus { + unsigned long long val; + struct { + + unsigned long long overflowStatus : 1; + unsigned long long underflowStatus : 1; + unsigned long long nemptyStatus : 1; + unsigned long long eccBadStatus : 1; + unsigned long long reserved4 : 60; + } reg; +}; + +union HilReg0Ps3RequestQueueFifoIntSet { + unsigned long long val; + struct { + + unsigned long long overflowSet : 1; + unsigned long long underflowSet : 1; + unsigned long long nemptySet : 1; + unsigned long long eccBadSet : 1; + unsigned long long reserved4 : 60; + } reg; +}; + +union HilReg0Ps3RequestQueueFifoIntClr { + unsigned long long val; + struct { + + unsigned long long overflowClr : 1; + unsigned long long underflowClr : 1; + unsigned long long nemptyClr : 1; + unsigned long long eccBadClr : 1; + unsigned long long reserved4 : 60; + } reg; +}; + +union HilReg0Ps3RequestQueueFifoIntMask { + unsigned long long val; + struct { + + unsigned long long overflowMask : 1; + unsigned long long underflowMask : 1; + unsigned long long nemptyMask : 1; + unsigned long long eccBadMask : 1; + unsigned long long reserved4 : 60; + } reg; +}; + +union HilReg0Ps3RequestQueueFifoCntClr { + unsigned long long val; + struct { + + unsigned long long fifowrcntClr : 1; + unsigned long long fifordcntClr : 1; + unsigned long long fifoerrcntClr : 1; + unsigned long long fifoordererrwrcntClr : 1; + unsigned long long fifoordererrrdcntClr : 1; + unsigned long long fifobit1errcntClr : 1; + unsigned long long fifobit2errcntClr : 1; + unsigned long long reserved7 : 57; + } reg; +}; + +union HilReg0Ps3RequestQueueFifoOrderError { + unsigned long long val; + struct { + + unsigned long long wrcnt : 32; + unsigned long long rdcnt : 32; + } reg; +}; + +union HilReg0Ps3RequestQueueFifoDinShift { + unsigned long long val; + struct { + + unsigned long long val : 64; + } reg; +}; + +union HilReg0Ps3RequestQueueFifoDoutShift { + unsigned long long val; + struct { + + unsigned long long val : 64; + } reg; +}; + +union HilReg0Ps3RequestQueueFifostatusMaxlevel { + unsigned long long val; + struct { + + unsigned long long val : 16; + unsigned long long reserved1 : 48; + } reg; +}; + +union HilReg0Ps3RequestQueueFifoInit { + unsigned long long val; + struct { + + unsigned long long stat : 2; + unsigned long long reserved1 : 62; + } reg; +}; + +union HilReg0Ps3RequestQueueFifoinitEn { + unsigned long long val; + struct { + + unsigned long long start : 1; + unsigned long long reserved1 : 63; + } reg; +}; + +union HilReg0Ps3RequestQueueFifoinitMax { + unsigned long long val; + struct { + + unsigned long long num : 16; + unsigned long long reserved1 : 48; + } reg; +}; + +union HilReg0Ps3RequestQueueFifostatusEccCnt { + unsigned long long val; + struct { + + unsigned long long bit1Err : 32; + unsigned long long bit2Err : 32; + } reg; +}; + +union HilReg0Ps3RequestQueueFifostatusEccAddr { + unsigned long long val; + struct { + + unsigned long long errPoint : 64; + } reg; +}; + +union HilReg0Ps3RequestQueueFifoDecoderOverflow { + unsigned long long val; + struct { + + unsigned long long rCmdwordEmpty : 1; + unsigned long long rPortindexEmpty : 1; + unsigned long long rCmdbackEmpty : 1; + unsigned long long wCmdwordEmpty : 1; + unsigned long long wPortindexEmpty : 1; + unsigned long long wCmdbackEmpty : 1; + unsigned long long rCmdwordFull : 1; + unsigned long long rPortindexFull : 1; + unsigned long long rCmdbackFull : 1; + unsigned long long wCmdwordFull : 1; + unsigned long long wPortindexFull : 1; + unsigned long long wCmdbackFull : 1; + unsigned long long reserved12 : 52; + } reg; +}; + +union HilReg0Ps3RequestQueueFifoEccBadProject { + unsigned long long val; + struct { + + unsigned long long en : 1; + unsigned long long reserved1 : 63; + } reg; +}; + +union HilReg0Ps3RequestQueueFifooverflowWord { + unsigned long long val; + struct { + + unsigned long long record : 64; + } reg; +}; + +union HilReg0Ps3RequestQueueFifoLevelMonitorCtl { + unsigned long long val; + struct { + + unsigned long long low : 16; + unsigned long long high : 16; + unsigned long long en : 1; + unsigned long long reserved3 : 31; + } reg; +}; + +union HilReg0Ps3RequestQueueFifoLevelMonitorCntClr { + unsigned long long val; + struct { + + unsigned long long en : 1; + unsigned long long reserved1 : 63; + } reg; +}; + +union HilReg0Ps3RequestQueueFifoLevelMonitorLow { + unsigned long long val; + struct { + + unsigned long long cnt : 64; + } reg; +}; + +union HilReg0Ps3RequestQueueFifoLevelMonitorMid { + unsigned long long val; + struct { + + unsigned long long cnt : 64; + } reg; +}; + +union HilReg0Ps3RequestQueueFifoLevelMonitorHigh { + unsigned long long val; + struct { + + unsigned long long cnt : 64; + } reg; +}; + +struct HilReg0Ps3RequestQueue { + + union HilReg0Ps3RequestQueuePs3RequestQueue ps3RequestQueue; + union HilReg0Ps3RequestQueueFifoErrCnt fifoErrCnt; + union HilReg0Ps3RequestQueueFifoStatus fifoStatus; + union HilReg0Ps3RequestQueueFifoLevelConfig fifoLevelConfig; + union HilReg0Ps3RequestQueueFifoRst fifoRst; + union HilReg0Ps3RequestQueueFifoIOCnt fifoIOCnt; + union HilReg0Ps3RequestQueueFifoFlowCnt fifoFlowCnt; + union HilReg0Ps3RequestQueueFifoIntStatus fifoIntStatus; + union HilReg0Ps3RequestQueueFifoIntSet fifoIntSet; + union HilReg0Ps3RequestQueueFifoIntClr fifoIntClr; + union HilReg0Ps3RequestQueueFifoIntMask fifoIntMask; + union HilReg0Ps3RequestQueueFifoCntClr fifoCntClr; + union HilReg0Ps3RequestQueueFifoOrderError fifoOrderError; + union HilReg0Ps3RequestQueueFifoDinShift fifoDinShift[4]; + union HilReg0Ps3RequestQueueFifoDoutShift fifoDoutShift[4]; + union HilReg0Ps3RequestQueueFifostatusMaxlevel fifoStatusMaxLevel; + union HilReg0Ps3RequestQueueFifoInit fifoInit; + union HilReg0Ps3RequestQueueFifoinitEn fifoinitEn; + union HilReg0Ps3RequestQueueFifoinitMax fifoinitMax; + union HilReg0Ps3RequestQueueFifostatusEccCnt fifoStatusEccCnt; + union HilReg0Ps3RequestQueueFifostatusEccAddr fifoStatusEccAddr; + union HilReg0Ps3RequestQueueFifoDecoderOverflow fifoDecoderOverflow; + union HilReg0Ps3RequestQueueFifoEccBadProject fifoEccBadProject; + union HilReg0Ps3RequestQueueFifooverflowWord fifoOverFlowWord; + union HilReg0Ps3RequestQueueFifoLevelMonitorCtl fifoLevelMonitorCtl; + union HilReg0Ps3RequestQueueFifoLevelMonitorCntClr + fifoLevelMonitorCntClr; + union HilReg0Ps3RequestQueueFifoLevelMonitorLow fifoLevelMonitorLow; + union HilReg0Ps3RequestQueueFifoLevelMonitorMid fifoLevelMonitorMid; + union HilReg0Ps3RequestQueueFifoLevelMonitorHigh fifoLevelMonitorHigh; +}; +#endif +#endif diff --git a/drivers/scsi/linkdata/ps3stor/include/ps3_evtcode_trans.h b/drivers/scsi/linkdata/ps3stor/include/ps3_evtcode_trans.h new file mode 100644 index 0000000000000000000000000000000000000000..38662e99c51595b0384190ceee7f0572347703fc --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/include/ps3_evtcode_trans.h @@ -0,0 +1,507 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __PS3_EVTCODE_TRANS_H__ +#define __PS3_EVTCODE_TRANS_H__ + +#if !(defined(PS3_PRODUCT_EXPANDER) || defined(PS3_PRODUCT_SWITCH)) +#include "ps3_htp_mgr_evt_raidhba.h" + +#define PS3_EVT_EXT_NR (PS3_EVT_MAX_TYPE_LOCAL - MGR_EVT_EXTEND_TYPE_START) + +static inline char const *mgrEvtCodeTrans(unsigned int opCode) +{ + unsigned int typeIndex = 0; + unsigned int codeIndex = 0; + const char *pEvtTransStr = NULL; + static const char *pEvtCodeInfo[MGR_EVT_EXTEND_TYPE_START][MGR_EVT_TYPE_OFFSET] = { + { + "MGR_EVT_SAS_START", + "MGR_EVT_SAS_EXPANDER_IN", + "MGR_EVT_SAS_EXPANDER_OUT", + "MGR_EVT_SAS_EXPANDER_UPDATE", + "MGR_EVT_SAS_EXPANDER_CHANGE", + "MGR_EVT_ENCL_TEMP_NORMAL", + "MGR_EVT_ENCL_TEMP_WARNING", + "MGR_EVT_ENCL_TEMP_CRITICAL", + "MGR_EVT_TOPO_LOOP", + "MGR_EVT_LOOP_RESOLUTION", + "MGR_EVT_CASCADED_SCAN_SMP_FAIL", + "MGR_EVT_ENCL_EXCEED", + "MGR_EVT_TRI_MODE_SWITCH", + "MGR_EVT_CHANGE_PHY_BY_USER", + "MGR_EVT_REV_HIBERNATE_CMD", + "MGR_EVT_PHY_CHANGE", + "MGR_EVT_INQUIRY_INFO", + "MGR_EVT_SAS_SATA_LINK_SPEED_NOMATCH", + "MGR_EVT_SAS_SATA_LN_EXCEPTION", + "MGR_EVT_SAS_SATA_DRIVER_INFO", + }, + { + "MGR_EVT_PD_COUNT_START", + "MGR_EVT_DEVM_DISK_IN", + "MGR_EVT_DEVM_DISK_OUT", + "MGR_EVT_MULITPILE_PD_IN", + "MGR_EVT_MULITPILE_PD_OUT", + "MGR_EVT_DEVM_JBOD", + "MGR_EVT_DEVM_READY", + "MGR_EVT_MULITPILE_JBOD", + "MGR_EVT_MULITPILE_READY", + "MGR_EVT_BACKPLANE_ON", + "MGR_EVT_BACKPLANE_OFF", + "MGR_EVT_MULITPILE_PD_STATE_CHANGE", + "MGR_EVT_DEVM_DISK_CHANGE", + "MGR_EVT_DEFAULT_UNUSED", + "MGR_EVT_PD_PRE_READY", + }, + { + "MGR_EVT_VD_COUNT_START", + "MGR_EVT_VD_OPTIMAL", + "MGR_EVT_VD_PARTIAL_DEGRADE", + "MGR_EVT_VD_DEGRADE", + "MGR_EVT_VD_CREATED", + "MGR_EVT_VD_DELETED", + "MGR_EVT_VD_HIDDEN_CHANGE", + "MGR_EVT_MULITPILE_VD_IN", + "MGR_EVT_MULITPILE_VD_OUT", + "MGR_EVT_VD_UNLOCK", + "MGR_EVT_VD_OFFLINE", + }, + { + "MGR_EVT_CTRL_INFO_START", + "MGR_EVT_CTRL_REBOOT", + "MGR_EVT_CTRL_SHUTDOWN", + "MGR_EVT_CTRL_TIME_CHANGE", + "MGR_EVT_CTRL_EVENT_LOG_CLEARED", + "MGR_EVT_CTRL_RBLD_RATE_CHANGED", + "MGR_EVT_CTRL_ENABLEMOVEBACK_CHANGED", + "MGR_EVT_CTRL_ENABLENCQ_CHANGED", + "MGR_EVT_CTRL_AUTO_REBUILD_CHANGED", + "MGR_EVT_CTRL_EGHS_READY_CHANGED", + "MGR_EVT_CTRL_EGHS_SPARE_CHANGED", + "MGR_EVT_CTRL_BGI_RATE_CHANGED", + "MGR_EVT_CTRL_MIGRATE_RATE_CHANGED", + "MGR_EVT_CTRL_CC_RATE_CHANGED", + "MGR_EVT_CTRL_DIRECT_CHANGE", + "MGR_EVT_CTRL_PR_START", + "MGR_EVT_CTRL_PR_PAUSE", + "MGR_EVT_CTRL_PR_REMINDER_PAUSE", + "MGR_EVT_CTRL_PR_RESUME", + "MGR_EVT_CTRL_PR_DONE", + "MGR_EVT_CTRL_PR_RATE_CHANGED", + "MGR_EVT_CTRL_PR_CANT_START", + "MGR_EVT_CTRL_PR_PROP_CHANGED", + "MGR_EVT_CTRL_PARAM_CHANGE", + "MGR_EVT_CTRL_AUTO_CC_PARAM_CHANGE", + "MGR_EVT_CTRL_RECOVERY_FACTORY", + "MGR_EVT_CTRL_PROFILEID_CHANGED", + "MGR_EVT_CTRL_SANPSHOT_CREATE", + "MGR_EVT_CTRL_SANPSHOT_DELETE", + "MGR_EVT_CTRL_ENABLEPDM_CHANGED", + "MGR_EVT_CTRL_PDMSUPPORTREADYPD", + "MGR_EVT_CTRL_PDMTIMERINTERVAL", + "MGR_EVT_CTRL_SECURITY_KEY_CHANGE", + "MGR_EVT_CTRL_SECURITY_KEY_CREATE", + "MGR_EVT_CTRL_SECURITY_KEY_DESTROY", + "MGR_EVT_CTRL_SECURITY_KEY_ESCROW", + "MGR_EVT_CTRL_SECURITY_KEY_FAILED", + "MGR_EVT_CTRL_SECURITY_KEY_INVALID", + "MGR_EVT_AUTOCONFIG", + "MGR_EVT_CTRL_FOREIGN_IMPORTED_ALL", + "MGR_EVT_CTRL_FOREIGN_CLEAR", + "MGR_EVT_CTRL_FOREIGN_IMPORT_FAIL", + "MGR_EVT_CTRL_FOREIGN_IMPORT_PART_FAIL", + "MGR_EVT_CTRL_FOREIGN_IMPORT_FAIL_PDS", + "MGR_EVT_CTRL_FOREIGN_PD_DETECTED", + "MGR_EVT_PD_INSERT_TO_DG", + "MGR_EVT_PD_IMPORT_TO_DG", + "MGR_EVT_HARD_RESET", + "MGR_EVT_DPF_CFG_MODIFY", + "MGR_EVT_SPOR", + "MGR_EVT_ONF_ABNORMAL", + "MGR_EVT_CTRL_LOADED", + "MGR_EVT_CACHE_PROTECTION_CMPT_READY", + "MGR_EVT_CACHE_PROTECTION_CMPT_NOT_READY", + "MGR_EVT_CACHE_PROTECTION_CMPT_INSTABLE", + "MGR_EVT_MAIN_MEDIUM_NOT_AVAILABLE", + "MGR_EVT_BACKUP_MEDIUM_NOT_AVAILABLE", + "MGR_EVT_MEDIUM_DATA_RECOVER", + "MGR_EVT_INIT_FAIL", + "MGR_EVT_CTRL_POWER_MODE_CHANGED", + }, + { + "MGR_EVT_PD_ATTR_START", + "MGR_EVT_PD_INFO_CHANGE", + "MGR_EVT_PD_STATE_CHANGE", + "MGR_EVT_PD_MARKED_JBOD", + "MGR_EVT_PD_MARKED_READY", + "MGR_EVT_PD_MARKED_ONLINE", + "MGR_EVT_PD_MARKED_OFFLINE", + "MGR_EVT_PD_MARKED_FAILED", + "MGR_EVT_PD_MARKED_MISSING", + "MGR_EVT_PD_MARKED_REBUILD", + "MGR_EVT_PD_MARKED_REPLACE", + "MGR_EVT_PD_MARKED_UNCONFIGUREDBAD", + "MGR_EVT_PD_MARKED_FOREIGN", + "MGR_EVT_PD_NR_GLOBAL_SPARE_ADDED", + "MGR_EVT_PD_NR_GLOBAL_SPARE_DELETED", + "MGR_EVT_PD_NR_DEDICATED_SPARE_ADDED", + "MGR_EVT_PD_NR_DEDICATED_SPARE_DELETED", + "MGR_EVT_PD_R_GLOBAL_SPARE_ADDED", + "MGR_EVT_PD_R_GLOBAL_SPARE_DELETED", + "MGR_EVT_PD_R_DEDICATED_SPARE_ADDED", + "MGR_EVT_PD_R_DEDICATED_SPARE_DELETED", + "MGR_EVT_PD_RBLD_ABORT_BY_USER", + "MGR_EVT_PD_RBLD_DONE_PD", + "MGR_EVT_PD_RBLD_FAILED_BAD_SOURCE", + "MGR_EVT_PD_RBLD_FAILED_BAD_TARGET", + "MGR_EVT_PD_RBLD_PROGRESS", + "MGR_EVT_PD_RBLD_SUSPENDED_REMINDER", + "MGR_EVT_PD_RBLD_SUSPENDED", + "MGR_EVT_PD_RBLD_RESUME", + "MGR_EVT_PD_RBLD_START", + "MGR_EVT_PD_RBLD_START_AUTO", + "MGR_EVT_PD_RBLD_MEDIUM_ERROR", + "MGR_EVT_PD_EMERGENCY_SPARE", + "MGR_EVT_PD_RECOVER_MEDIUM_ERROR", + "MGR_EVT_PD_MOVEBACK_START", + "MGR_EVT_PD_MOVEBACK_ABORT", + "MGR_EVT_PD_MOVEBACK_ABORT_FOR_REBUILD", + "MGR_EVT_PD_MOVEBACK_DONE", + "MGR_EVT_PD_MOVEBACK_PROGRESS", + "MGR_EVT_PD_MOVEBACK_SUSPENDED", + "MGR_EVT_PD_MOVEBACK_SUSPENDED_REMINDER", + "MGR_EVT_PD_MOVEBACK_RESUME", + "MGR_EVT_PD_MOVEBACK_START_AUTO", + "MGR_EVT_PD_MOVEBACK_FAILED_BAD_SOURCE", + "MGR_EVT_PD_MOVEBACK_FAILED_BAD_TARGET", + "MGR_EVT_PD_MOVEBACK_ABORT_BY_USER", + "MGR_EVT_PD_MOVEBACK_MEDIUM_ERROR", + "MGR_EVT_PD_FGI_START", + "MGR_EVT_PD_FGI_COMPLETE", + "MGR_EVT_PD_FGI_FAILED", + "MGR_EVT_PD_FGI_PROGRESS", + "MGR_EVT_PD_FGI_ABORT", + "MGR_EVT_PD_PR_ABORTED", + "MGR_EVT_PD_PR_CORRECTED", + "MGR_EVT_PD_PR_UNCORRECTABLE", + "MGR_EVT_PD_PR_FOUND_MEDIUM_ERROR", + "MGR_EVT_PD_PR_PROGRESS", + "MGR_EVT_PD_SET_BOOT_DRIVE", + "MGR_EVT_PD_RESET_BOOT_DRIVE", + "MGR_EVT_PD_RESET_BOOT_DRIVE_WITH_DISK_OUT", + "MGR_EVT_PD_SET_BOOT_DRIVE_WITH_DISK_IN", + "MGR_EVT_PD_POWERSAVE_TO_ON", + "MGR_EVT_PD_POWERSAVE_TO_TRANSITION", + "MGR_EVT_PD_TRANSITION_TO_ON", + "MGR_EVT_PD_TRANSITION_TO_POWERSAVE", + "MGR_EVT_PD_ON_TO_POWERSAVE", + "MGR_EVT_PD_START_LOCATE", + "MGR_EVT_PD_STOP_LOCATE", + "MGR_EVT_PD_PFA_ERROR", + "MGR_EVT_PD_NOT_PRESENT", + "MGR_EVT_DEVM_DISK_FAULT", + "MGR_EVT_PHY_FLASHOFFLINE", + "MGR_EVT_PHY_FLASHONLINE", + "MGR_EVT_NFR_DONE", + "MGR_EVT_NFR_REPAIRED", + "MGR_EVT_NFR_REPORT", + "MGR_EVT_NFR_STOP", + "MGR_EVT_PD_DOWNLOAD_DONE", + "MGR_EVT_PD_DOWNLOAD_START", + "MGR_EVT_NVME_PHY_BAD", + }, + { + "MGR_EVT_VD_ATTR_START", + "MGR_EVT_MULITPILE_VD_STATE_CHANGE", + "MGR_EVT_VD_OFFLINE_OLD", + "MGR_EVT_VD_LOCK", + "MGR_EVT_VD_PASSWD_CHANGED", + "MGR_EVT_VD_SETTINGS_CHANGE", + "MGR_EVT_VD_PD_CHANGE", + "MGR_EVT_VD_STATE_CHANGE", + "MGR_EVT_PD_RBLD_DONE_LD", + "MGR_EVT_VD_FGI_START", + "MGR_EVT_VD_FGI_COMPLETE", + "MGR_EVT_VD_FGI_FAILED", + "MGR_EVT_VD_FGI_PROGRESS", + "MGR_EVT_VD_FGI_ABORT", + "MGR_EVT_VD_BGI_START", + "MGR_EVT_VD_BGI_COMPLETE", + "MGR_EVT_VD_BGI_FAILED", + "MGR_EVT_VD_BGI_PROGRESS", + "MGR_EVT_VD_BGI_ABORT", + "MGR_EVT_VD_BGI_PAUSE", + "MGR_EVT_VD_BGI_REMINDER_PAUSE", + "MGR_EVT_VD_BGI_RESUME", + "MGR_EVT_VD_BGI_CORRECTABLE_ERROR", + "MGR_EVT_VD_BGI_UNCORRECTABLE_ERROR", + "MGR_EVT_VD_ALTER_ATTR_BY_MIGRATION", + "MGR_EVT_VD_BBM_CLEARED", + "MGR_EVT_VD_BBM_PERCENT_FULL", + "MGR_EVT_VD_BBM_100_FULL", + "MGR_EVT_VD_BBM_LOG_FULL", + "MGR_EVT_VD_BBM_LOG_RCT", + "MGR_EVT_VD_BBM_LOG_WCT", + "MGR_EVT_VD_BBM_DEL_RCT", + "MGR_EVT_VD_CC_ABORTED", + "MGR_EVT_VD_CC_CORRECTED_MEDIUM_ERROR", + "MGR_EVT_VD_CC_DOUBLE_MEDIUM_ERRORS", + "MGR_EVT_VD_CC_DONE", + "MGR_EVT_VD_CC_DONE_INCON", + "MGR_EVT_VD_CC_FAILED", + "MGR_EVT_VD_CC_FAILED_UNCOR", + "MGR_EVT_VD_CC_INCONSISTENT_PARITY", + "MGR_EVT_VD_CC_INCONSISTENT_PARITY_LOGGING_DISABLED", + "MGR_EVT_VD_CC_PROGRESS", + "MGR_EVT_VD_CC_START", + "MGR_EVT_VD_CC_PAUSE", + "MGR_EVT_VD_CC_REMINDER_PAUSE", + "MGR_EVT_VD_CC_RESUME", + "MGR_EVT_VD_RW_RAID1X_WRITE_CARDBUSY", + "MGR_EVT_VD_SET_BOOT_DRIVE", + "MGR_EVT_VD_RESET_BOOT_DRIVE", + "MGR_EVT_VD_RESET_BOOT_DRIVE_WITH_DISK_OUT", + "MGR_EVT_VD_SET_BOOT_DRIVE_WITH_DISK_IN", + "MGR_EVT_ALL_CONFIGS_MISSING", + "MGR_EVT_PDS_MISSING", + "MGR_EVT_NVSRAM_RECOVER_FAIL", + "MGR_EVT_NVSRAM_UPDATE_FAIL", + "MGR_EVT_VD_ERASE_ABORT", + "MGR_EVT_VD_ERASE_COMPLETE", + "MGR_EVT_VD_ERASE_DISK_FAILED", + "MGR_EVT_VD_ERASE_FAILED", + "MGR_EVT_VD_ERASE_PROGRESS", + "MGR_EVT_VD_ERASE_START", + "MGR_EVT_VD_EXPAND_FINISH", + "MGR_EVT_VD_EXPAND_START", + "MGR_EVT_VD_FLUSH_CLEAR_FAILED", + "MGR_EVT_VD_FLUSH_RECORD_FAILED", + "MGR_EVT_VD_FLUSH_SET_INCONSIST", + "MGR_EVT_VDS_MISSING", + "MGR_EVT_VD_BBM_UNCORRECTABLE_LOGGED", + "MGR_EVT_VD_FLUSH_CACHE_PINNED", + "MGR_EVT_VD_FLUSH_OCR_CACHE_PINNED", + "MGR_EVT_VD_FLUSH_CACHE_DISCARDED", + "MGR_EVT_VD_FLUSH_CACHE_DISCARDED_DEL", + "MGR_EVT_VD_FLUSH_CACHE_DESTAGED", + "MGR_EVT_VD_CM_RECOVERY_CRC_FAIL", + "MGR_EVT_VD_BGI_COMPLETE_UNCOR", + }, + { + "MGR_EVT_DG_INFO_START", + "MGR_EVT_DG_CREATED", + "MGR_EVT_DG_DELETED", + "MGR_EVT_DG_SETTING_CHANGE", + "MGR_EVT_DG_MIGRATION_START", + "MGR_EVT_DG_MIGRATION_RECOVER", + "MGR_EVT_DG_MIGRATION_SUCCESS", + "MGR_EVT_DG_MIGRATION_ALLOC_RESOURCE_FAILED", + "MGR_EVT_DG_MIGRATION_FAILED", + "MGR_EVT_DG_MIGRATION_RECOVER_FAILED", + "MGR_EVT_DG_MIGRATION_PROGRESS", + "MGR_EVT_DG_MIGRATION_INTERNAL_WARNING", + "MGR_EVT_DG_PRSWITCH_CHANGE", + "MGR_EVT_DG_PR_ABORTED_WM_FAILED", + "MGR_EVT_DG_PR_ABORTED_SPINUP_FAILED", + }, + { + "MGR_EVT_BBU_START", + "MGR_EVT_BBU_PRESENT", + "MGR_EVT_BBU_NOT_PRESENT", + "MGR_EVT_BBU_GOOD", + "MGR_EVT_BBU_BAD", + "MGR_EVT_BBU_CAP_BELOW_THRESHOLD", + "MGR_EVT_BBU_CAP_ABOVE_THRESHOLD", + "MGR_EVT_BBU_CHARGE_STATUS", + "MGR_EVT_BBU_CHARGE_COMPLETE", + "MGR_EVT_BBU_INSERT", + "MGR_EVT_BBU_ABSENT", + "MGR_EVT_BBU_TEMPERATURE_NORMAL", + "MGR_EVT_BBU_TEMPERATURE_HIGH", + "MGR_EVT_BBU_VOLTAGE_NORMAL", + "MGR_EVT_BBU_VOLTAGE_HIGH", + "MGR_EVT_BBU_CURRENT_NORMAL", + "MGR_EVT_BBU_CURRENT_HIGH", + "MGR_EVT_BBU_LOAD_NORMAL", + "MGR_EVT_BBU_VOLTAGE_LOW", + "MGR_EVT_BBU_BATTERY_CAP_ABOVE_SOH_THRESHOLD", + "MGR_EVT_BBU_BATTERY_CAP_BELOW_SOH_THRESHOLD", + "MGR_EVT_BBU_LEARN_STAGE", + "MGR_EVT_BBU_LEARN_REQUESTED", + "MGR_EVT_BBU_LEARN_POSTPONED", + "MGR_EVT_BBU_LEARN_TIMEOUT", + "MGR_EVT_BBU_LEARN_MANUAL", + "MGR_EVT_BBU_LEARN_RESCHEDULED", + "MGR_EVT_BBU_PROPERTIES_CHANGED", + }, + { + "MGR_EVT_CONFIG_START", + "MGR_EVT_CONFIG_INFO", + "MGR_EVT_CONFIG_FLASH_READ_FAIL", + "MGR_EVT_CONFIG_FLASH_WRITE_FAIL", + "MGR_EVT_CONFIG_NVSRAM_READ_FAIL", + "MGR_EVT_CONFIG_NVSRAM_WRITE_FAIL", + "MGR_EVT_CONFIG_VERSION_MISMATCH", + "MGR_EVT_CONFIG_MEDIA_VALUE_CHECK_FAIL", + "MGR_EVT_CONFIG_ERASE_NVSRAM", + "MGR_EVT_PHY_LINKSPEED_CFG_CHANGE", + }, + { + "MGR_EVT_IO_START", + "MGR_EVT_IO_TEST", + "MGR_EVT_IO_SENSE_DATA", + "MGR_EVT_DEVICE_RESET", + "MGR_EVT_NVME_DEVICE_RESET", + "MGR_EVT_DEVICE_ABNORMAL", + "MGR_EVT_IO_SENSE", + }, + { + "MGR_EVT_UKEY_START", + "MGR_EVT_UKEY_INSERT", + "MGR_EVT_UKEY_DEGRADE", + "MGR_EVT_RAID_KEY_OVERCURRENT", + "MGR_EVT_RAID_KEY_OVERCURRENT_RECOVER", + }, + { + "MGR_EVT_HWR_START", + "MGR_EVT_HWR_HAC_RESET_BEGIN", + "MGR_EVT_HWR_HAC_RESET_END", + "MGR_EVT_REPORT_HAC_ECC", + }, + { + "MGR_EVT_ALARM_START", + "MGR_EVT_ALARM_ENABLED", + "MGR_EVT_ALARM_DISABLED", + }, + { + "MGR_EVT_ECC_START", + "MGR_EVT_ECC_CNT_EXCEED", + "MGR_EVT_ECC_ERR_INTR", + "MGR_EVT_ECC_CNT_CLEAR", + "MGR_EVT_ECC_CLI_CHANGE", + "MGR_EVT_ECC_FACTORY_CHANGE", + }, + { + "MGR_EVT_UPGRADE_START", + "MGR_EVT_IMAGE_DOWNLOAD_ERROR", + "MGR_EVT_IMAGE_AUTHENTICATION_FAIL", + "MGR_EVT_IMAGE_VERSION_CHECK_FAIL", + "MGR_EVT_IMAGE_CHECKSUM_CHECK_FAIL", + "MGR_EVT_IMAGE_FROM_BACKUP", + "MGR_EVT_DEV_OPEN_ERROR", + "MGR_EVT_DEV_FLASH_ERROR", + "MGR_EVT_DEV_ERASE_ERROR", + "MGR_EVT_DEV_CLOSE_ERROR", + "MGR_EVT_FLASH_GENERAL_ERROR", + "MGR_EVT_FLASH_TIMEOUT", + "MGR_EVT_UPGRADE_SUCCESS", + "MGR_EVT_UPGRADE_SYNC_START", + "MGR_EVT_UPGRADE_SYNC_FAILED", + "MGR_EVT_UPGRADE_SYNC_SUCCESS", + }, + { + "MGR_EVT_TEMP_START", + "MGR_EVT_TEMP_WITHIN_OPTIMAL_RANGE", + "MGR_EVT_TEMP_ABOVE_OPTIMAL_RANGE", + "MGR_EVT_TEMP_WARNING", + "MGR_EVT_TEMP_CRITICAL", + }, + }; + + static const char *pEvtCodeExtInfo[PS3_EVT_EXT_NR][MGR_EVT_TYPE_EXTEND_OFFSET] = { + { + "MGR_EVT_PD_ATTR_EXTEND_START_START", + "MGR_EVT_PD_ERASE_ABORT", + "MGR_EVT_PD_ERASE_COMPLETE", + "MGR_EVT_PD_ERASE_FAILED", + "MGR_EVT_PD_ERASE_PROGRESS", + "MGR_EVT_PD_ERASE_START", + "MGR_EVT_PD_PDM_ABORT_BY_USER", + "MGR_EVT_PD_PDM_ABORT_FOR_REBUILD", + "MGR_EVT_PD_PDM_ABORT", + "MGR_EVT_PD_PDM_DONE", + "MGR_EVT_PD_PDM_FAILED_BAD_SOURCE", + "MGR_EVT_PD_PDM_FAILED_BAD_TARGET", + "MGR_EVT_PD_PDM_MEDIUM_ERROR", + "MGR_EVT_PD_PDM_PROGRESS", + "MGR_EVT_PD_PDM_REPLACED_SOURCE", + "MGR_EVT_PD_PDM_RESUME", + "MGR_EVT_PD_PDM_START_AUTO", + "MGR_EVT_PD_PDM_START", + "MGR_EVT_PD_PDM_SUSPENDED_REMINDER", + "MGR_EVT_PD_PDM_SUSPENDED", + "MGR_EVT_PD_PFA_ERROR_CLEAR", + "MGR_EVT_PD_SANITIZE_START", + "MGR_EVT_PD_FORMAT_START", + "MGR_EVT_PD_SANITIZE_PROGRESS", + "MGR_EVT_PD_SANITIZE_DONE", + "MGR_EVT_PD_FORMAT_DONE", + "MGR_EVT_PD_DPF_ERROR", + "MGR_EVT_PD_MARKED_SHIELD", + "MGR_EVT_PD_BBM_CORRECTED", + "MGR_EVT_PD_BBM_UNRECOVERABLE", + "MGR_EVT_PD_BBM_PUNCTURING", + "MGR_EVT_PD_BBM_REASSIGN_WR_ERROR", + "MGR_EVT_PD_UNABLE_ACCESS", + "MGR_EVT_MULITPILE_PD_UNABLE_ACCESS", + "MGR_EVT_PD_INSERT_FAIL", + "MGR_EVT_PHY_SATA_D2H_FAIL", + "MGR_EVT_PD_NOT_INSERTED", + "MGR_EVT_PD_NOT_SUPPORT", + "MGR_EVT_MULITPILE_PD_NOT_SUPPORT", + "MGR_EVT_NVDATA_INVALID", + "MGR_EVT_PD_RBLD_NON_UNMAP_PD", + "MGR_EVT_PD_MOVEBACK_NON_UNMAP_PD", + "MGR_EVT_PD_PDM_NON_UNMAP_PD", + "MGR_EVT_PD_RBLD_FAILED", + "MGR_EVT_PD_SANITIZE_FAILED", + "MGR_EVT_PD_SPIN_FAIL", + "MGR_EVT_PD_RBLD_MEDIA_MIX_NOT_SUPPORT", + "MGR_EVT_PD_RBLD_DRIVE_MIX_NOT_SUPPORT", + "MGR_EVT_PD_PDM_MEDIA_MIX_NOT_SUPPORT", + "MGR_EVT_PD_PDM_DRIVE_MIX_NOT_SUPPORT", + "MGR_EVT_PD_MOVEBACK_MEDIA_MIX_NOT_SUPPORT", + "MGR_EVT_PD_MOVEBACK_DRIVE_MIX_NOT_SUPPORT", + "MGR_EVT_NFR_FAILED", + "MGR_EVT_PD_RBLD_SMALL_SIZE", + "MGR_EVT_PD_MOVEBACK_SMALL_SIZE", + "MGR_EVT_NEGO_LOWER_LINK_SPEED", + "MGR_EVT_PD_NVME_CMD_ERR", + "MGR_EVT_PD_NVME_CMD_TO", + "MGR_EVT_PD_NVME_INSERT_FAIL", + "MGR_EVT_PCIE_LINK_NEGO_ABNORMAL", + "MGR_EVT_PD_ISOLATION", + "MGR_EVT_PD_REPAIR_FAILED", + "MGR_EVT_PD_PCIE_LINK_UP_WITHOUT_I2C", + "MGR_EVT_PD_PCIE_ENUMERATE_FAILED", + "MGR_EVT_PD_MISS_LIGHTING", + }, + }; + + if (opCode >= ((MGR_EVT_EXTEND_TYPE_START - 1) * MGR_EVT_TYPE_OFFSET) + + (PS3_EVT_MAX_TYPE_LOCAL - MGR_EVT_EXTEND_TYPE_START) + * MGR_EVT_TYPE_EXTEND_OFFSET) { + goto end; + }; + if (opCode < ((MGR_EVT_EXTEND_TYPE_START - 1) * MGR_EVT_TYPE_OFFSET)) { + typeIndex = opCode / MGR_EVT_TYPE_OFFSET; + codeIndex = opCode % MGR_EVT_TYPE_OFFSET; + pEvtTransStr = pEvtCodeInfo[typeIndex][codeIndex]; + } else { + typeIndex = (opCode - ((MGR_EVT_EXTEND_TYPE_START - 1) * MGR_EVT_TYPE_OFFSET)) + / MGR_EVT_TYPE_EXTEND_OFFSET; + codeIndex = (opCode - ((MGR_EVT_EXTEND_TYPE_START - 1) * MGR_EVT_TYPE_OFFSET)) + % MGR_EVT_TYPE_EXTEND_OFFSET; + pEvtTransStr = pEvtCodeExtInfo[typeIndex][codeIndex]; +}; +end: + return pEvtTransStr; +} + +#else +static inline char const *mgrEvtCodeTrans(unsigned int opCode) +{ + const char * const pEvtTransStr = NULL; + return pEvtTransStr; +} +#endif +#endif diff --git a/drivers/scsi/linkdata/ps3stor/include/ps3_htp_dev_info.h b/drivers/scsi/linkdata/ps3stor/include/ps3_htp_dev_info.h new file mode 100644 index 0000000000000000000000000000000000000000..431d00120b776c774759428a372dd23c388451d0 --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/include/ps3_htp_dev_info.h @@ -0,0 +1,87 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __PS3_HTP_DEV_INFO_H__ +#define __PS3_HTP_DEV_INFO_H__ + + +enum DriverType { + DRIVER_TYPE_UNKNOWN = 0, + DRIVER_TYPE_SAS, + DRIVER_TYPE_SATA, + DRIVER_TYPE_SES, + DRIVER_TYPE_NVME, + DRIVER_TYPE_EXP_SAS, + DRIVER_TYPE_VEP, + DRIVER_TYPE_CHASSIS, + DRIVER_TYPE_BACKPLANE, + DRIVER_TYPE_MAX, +}; + +static inline const char *getDriverTypeName(enum DriverType driverType) +{ + static const char * const driverTypeName[] = { + [DRIVER_TYPE_UNKNOWN] = "DRIVER_TYPE_UNKNOWN", + [DRIVER_TYPE_SAS] = "DRIVER_TYPE_SAS", + [DRIVER_TYPE_SATA] = "DRIVER_TYPE_SATA", + [DRIVER_TYPE_SES] = "DRIVER_TYPE_SES", + [DRIVER_TYPE_NVME] = "DRIVER_TYPE_NVME", + [DRIVER_TYPE_EXP_SAS] = "DRIVER_TYPE_EXP_SAS", + [DRIVER_TYPE_VEP] = "DRIVER_TYPE_VEP", + [DRIVER_TYPE_CHASSIS] = "DRIVER_TYPE_CHASSIS", + [DRIVER_TYPE_BACKPLANE] = "DRIVER_TYPE_BACKPLANE", + [DRIVER_TYPE_MAX] = "DRIVER_TYPE_MAX", + }; + + return driverTypeName[driverType]; +} + + +enum MediumType { + DEVICE_TYPE_UNKNOWN = 0, + DEVICE_TYPE_HDD, + DEVICE_TYPE_SSD, + DEVICE_TYPE_ENCLOSURE, + DEVICE_TYPE_MAX, +}; + +static inline const char *getMediumTypeName(enum MediumType mediumType) +{ + static const char * const mediumTypeName[] = { + [DEVICE_TYPE_UNKNOWN] = "DEVICE_TYPE_UNKNOWN", + [DEVICE_TYPE_HDD] = "DEVICE_TYPE_HDD", + [DEVICE_TYPE_SSD] = "DEVICE_TYPE_SSD", + [DEVICE_TYPE_ENCLOSURE] = "DEVICE_TYPE_ENCLOSURE", + [DEVICE_TYPE_MAX] = "DEVICE_TYPE_MAX", + }; + + return mediumTypeName[mediumType]; +} + + +enum DeviceState { + DEVICE_STATE_FREE = 0x0, + DEVICE_STATE_INSERTING, + DEVICE_STATE_ONLINE, + DEVICE_STATE_WAIT, + DEVICE_STATE_RECOVER, + DEVICE_STATE_PREONLINE, + DEVICE_STATE_OUTING, + DEVICE_STATE_MAX, +}; + +static inline const char *getDeviceStateName(enum DeviceState pdState) +{ + static const char * const pdStateName[] = { + [DEVICE_STATE_FREE] = "DEVICE_STATE_FREE", + [DEVICE_STATE_INSERTING] = "DEVICE_STATE_INSERTING", + [DEVICE_STATE_ONLINE] = "DEVICE_STATE_ONLINE", + [DEVICE_STATE_WAIT] = "DEVICE_STATE_WAIT", + [DEVICE_STATE_RECOVER] = "DEVICE_STATE_RECOVER", + [DEVICE_STATE_PREONLINE] = "DEVICE_STATE_PREONLINE", + [DEVICE_STATE_OUTING] = "DEVICE_STATE_OUTING", + [DEVICE_STATE_MAX] = "DEVICE_STATE_MAX", + }; + + return pdStateName[pdState]; +} + +#endif diff --git a/drivers/scsi/linkdata/ps3stor/include/ps3_htp_meta.h b/drivers/scsi/linkdata/ps3stor/include/ps3_htp_meta.h new file mode 100644 index 0000000000000000000000000000000000000000..b22bfdb6865c3f1463b7fb4b10c34307bb84302c --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/include/ps3_htp_meta.h @@ -0,0 +1,126 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __PS3_HTP_META_H__ +#define __PS3_HTP_META_H__ + +enum MicPdState { + MIC_PD_STATE_UNKNOWN = 0, + MIC_PD_STATE_READY = 1, + MIC_PD_STATE_UBAD = 2, + MIC_PD_STATE_DSPARE = 3, + MIC_PD_STATE_GSPARE = 4, + MIC_PD_STATE_OFFLINE = 5, + MIC_PD_STATE_ONLINE = 6, + MIC_PD_STATE_MISSING = 7, + MIC_PD_STATE_FAILED = 8, + MIC_PD_STATE_REBUILD = 9, + MIC_PD_STATE_REPLACE = 10, + MIC_PD_STATE_FOREIGN = 11, + MIC_PD_STATE_JBOD = 12, + MIC_PD_STATE_UNSUPPORT = 13, + MIC_PD_STATE_PDM = 14, + MIC_PD_STATE_CFSHLD = 15, + MIC_PD_STATE_HSPSHLD = 16, + MIC_PD_STATE_RUNSP = 17, + MIC_PD_STATE_UBUNSP = 18, + MIC_PD_STATE_MAX +}; + + +static inline const char *getPdStateName(enum MicPdState pdSate, + unsigned char isRaid) +{ + static const char * const raidPdName[] = { + [MIC_PD_STATE_UNKNOWN] = "UNKNOWN", + [MIC_PD_STATE_READY] = "UGOOD", + [MIC_PD_STATE_UBAD] = "UBAD", + [MIC_PD_STATE_DSPARE] = "DSPARE", + [MIC_PD_STATE_GSPARE] = "GSPARE", + [MIC_PD_STATE_OFFLINE] = "OFFLINE", + [MIC_PD_STATE_ONLINE] = "ONLINE", + [MIC_PD_STATE_MISSING] = "MISSING", + [MIC_PD_STATE_FAILED] = "FAILED", + [MIC_PD_STATE_REBUILD] = "REBUILD", + [MIC_PD_STATE_REPLACE] = "REPLACE", + [MIC_PD_STATE_FOREIGN] = "FOREIGN", + [MIC_PD_STATE_JBOD] = "JBOD", + [MIC_PD_STATE_UNSUPPORT] = "UNSUPPORT", + [MIC_PD_STATE_PDM] = "PDM", + [MIC_PD_STATE_CFSHLD] = "CFSHLD", + [MIC_PD_STATE_HSPSHLD] = "HSPSHLD", + [MIC_PD_STATE_RUNSP] = "UGUNSP", + [MIC_PD_STATE_UBUNSP] = "UBUNSP", + }; + + static const char * const hbaPdName[] = { + [MIC_PD_STATE_UNKNOWN] = "UNKNOWN", + [MIC_PD_STATE_READY] = "READY", + [MIC_PD_STATE_UBAD] = "UBAD", + [MIC_PD_STATE_DSPARE] = "DSPARE", + [MIC_PD_STATE_GSPARE] = "GSPARE", + [MIC_PD_STATE_OFFLINE] = "OFFLINE", + [MIC_PD_STATE_ONLINE] = "ONLINE", + [MIC_PD_STATE_MISSING] = "MISSING", + [MIC_PD_STATE_FAILED] = "FAILED", + [MIC_PD_STATE_REBUILD] = "REBUILD", + [MIC_PD_STATE_REPLACE] = "REPLACE", + [MIC_PD_STATE_FOREIGN] = "FOREIGN", + [MIC_PD_STATE_JBOD] = "JBOD", + [MIC_PD_STATE_UNSUPPORT] = "UNSUPPORT", + [MIC_PD_STATE_PDM] = "PDM", + [MIC_PD_STATE_CFSHLD] = "CFSHLD", + [MIC_PD_STATE_HSPSHLD] = "HSPSHLD", + [MIC_PD_STATE_RUNSP] = "RUNSP", + [MIC_PD_STATE_UBUNSP] = "UBUNSP", + }; + + if (isRaid) + return raidPdName[pdSate]; + else + return hbaPdName[pdSate]; +} + + +enum MicVdState { + MIC_VD_STATE_UNKNOWN = 0, + MIC_VD_STATE_OFFLINE, + MIC_VD_STATE_OPTIMAL, + MIC_VD_STATE_PARTIAL_DEGRADE, + MIC_VD_STATE_DEGRADE +}; + +static inline const char *getVdStateName(enum MicVdState vdSate) +{ + static const char * const vdStateName[] = { [MIC_VD_STATE_UNKNOWN] = "UNKNOWN", + [MIC_VD_STATE_OFFLINE] = "OFFLINE", + [MIC_VD_STATE_OPTIMAL] = "OPTIMAL", + [MIC_VD_STATE_PARTIAL_DEGRADE] = + "PARTIALLY DEGRADED", + [MIC_VD_STATE_DEGRADE] = + "DEGRADED" }; + + return vdStateName[vdSate]; +} + + +enum RaidLevel { + RAID0 = 0x00, + RAID1 = 0x01, + RAID5 = 0x05, + RAID6 = 0x06, + JBOD = 0x0A, + RAID10 = 0x10, + RAID1E = 0x11, + RAID00 = 0x20, + RAID50 = 0x50, + RAID60 = 0x60, + RAID_UNKNOWN = 0xFF +}; + +enum VDAccessPolicy { + VD_ACCESS_POLICY_READ_WRITE = 0, + VD_ACCESS_POLICY_READ_ONLY, + VD_ACCESS_POLICY_BLOCK, + VD_ACCESS_POLICY_REMOVE_ACCESS +}; +#endif diff --git a/drivers/scsi/linkdata/ps3stor/include/ps3_htp_mgr_evt.h b/drivers/scsi/linkdata/ps3stor/include/ps3_htp_mgr_evt.h new file mode 100644 index 0000000000000000000000000000000000000000..a7322b33d68b53d7eb567b08f57050927fc3582f --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/include/ps3_htp_mgr_evt.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __PS3_HTP_MGR_EVT_H__ +#define __PS3_HTP_MGR_EVT_H__ + +#include "ps3_htp_mgr_evt_raidhba.h" + +struct PS3EventFilter { + unsigned char eventType; + unsigned char eventCodeCnt; + unsigned char reserved[6]; + unsigned short eventCodeTable[]; +}; + +#endif diff --git a/drivers/scsi/linkdata/ps3stor/include/ps3_htp_mgr_evt_raidhba.h b/drivers/scsi/linkdata/ps3stor/include/ps3_htp_mgr_evt_raidhba.h new file mode 100644 index 0000000000000000000000000000000000000000..b5af1831ba841525d0cafbeca503a336f8f5ccb2 --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/include/ps3_htp_mgr_evt_raidhba.h @@ -0,0 +1,1865 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __PS3_HTP_MGR_EVT_RAIDHBA_H__ +#define __PS3_HTP_MGR_EVT_RAIDHBA_H__ +#include "ps3_mgr_evt_common.h" + +enum MgrEvtType { + PS3_EVT_ILLEGAL_TYPE = 0, + PS3_EVT_SAS_INFO = 0x1, + PS3_EVT_PD_COUNT = 0x2, + PS3_EVT_VD_COUNT = 0x4, + PS3_EVT_CTRL_INFO = 0x8, + PS3_EVT_PD_ATTR = 0x10, + PS3_EVT_VD_ATTR = 0x20, + PS3_EVT_DG_INFO = 0x40, + PS3_EVT_BBU_INFO = 0x80, + PS3_EVT_CONFIG = 0x100, + PS3_EVT_IO_INFO = 0x200, + PS3_EVT_UKEY_INFO = 0x400, + PS3_EVT_HWR_INFO = 0x800, + PS3_EVT_ALARM_INFO = 0x1000, + PS3_EVT_ECC_INFO = 0x2000, + PS3_EVT_UPGRADE_INFO = 0x4000, + PS3_EVT_TEMP_INFO = 0x8000, + PS3_EVT_PD_ATTR_EXTEND = 0x10000, +}; + +static inline const char *nameMgrEvtType(unsigned int e) +{ + switch (e) { + case PS3_EVT_SAS_INFO: + return "PS3_EVT_SAS_INFO"; + case PS3_EVT_PD_COUNT: + return "PS3_EVT_PD_COUNT"; + case PS3_EVT_VD_COUNT: + return "PS3_EVT_VD_COUNT"; + case PS3_EVT_CTRL_INFO: + return "PS3_EVT_CTRL_INFO"; + case PS3_EVT_PD_ATTR: + return "PS3_EVT_PD_ATTR"; + case PS3_EVT_VD_ATTR: + return "PS3_EVT_VD_ATTR"; + case PS3_EVT_DG_INFO: + return "PS3_EVT_DG_INFO"; + case PS3_EVT_BBU_INFO: + return "PS3_EVT_BBU_INFO"; + case PS3_EVT_CONFIG: + return "PS3_EVT_CONFIG"; + case PS3_EVT_IO_INFO: + return "PS3_EVT_IO_INFO"; + case PS3_EVT_UKEY_INFO: + return "PS3_EVT_UKEY_INFO"; + case PS3_EVT_HWR_INFO: + return "PS3_EVT_HWR_INFO"; + case PS3_EVT_ALARM_INFO: + return "PS3_EVT_ALARM_INFO"; + case PS3_EVT_ECC_INFO: + return "PS3_EVT_ECC_INFO"; + case PS3_EVT_UPGRADE_INFO: + return "PS3_EVT_UPGRADE_INFO"; + case PS3_EVT_TEMP_INFO: + return "PS3_EVT_TEMP_INFO"; + case PS3_EVT_PD_ATTR_EXTEND: + return "PS3_EVT_PD_ATTR_EXTEND"; + default: + return "PS3_EVT_ILLEGAL_TYPE"; + } +} + +enum { + PS3_EVT_SAS_INFO_LOCAL = 1, + PS3_EVT_PD_COUNT_LOCAL = 2, + PS3_EVT_VD_COUNT_LOCAL = 3, + PS3_EVT_CTRL_INFO_LOCAL = 4, + PS3_EVT_PD_ATTR_LOCAL = 5, + PS3_EVT_VD_ATTR_LOCAL = 6, + PS3_EVT_DG_INFO_LOCAL = 7, + PS3_EVT_BBU_INFO_LOCAL = 8, + PS3_EVT_CONFIG_LOCAL = 9, + PS3_EVT_IO_INFO_LOCAL = 10, + PS3_EVT_UKEY_INFO_LOCAL = 11, + PS3_EVT_HWR_INFO_LOCAL = 12, + PS3_EVT_ALARM_INFO_LOCAL = 13, + PS3_EVT_ECC_INFO_LOCAL = 14, + PS3_EVT_UPGRADE_INFO_LOCAL = 15, + PS3_EVT_TEMP_INFO_LOCAL = 16, + PS3_EVT_PD_ATTR_EXTEND_LOCAL = 17, + PS3_EVT_DEFAULT_UNUSED_LOCAL, + PS3_EVT_MAX_TYPE_LOCAL, +}; + +static inline const char *nameMgrEvtLocalType(unsigned int e) +{ + switch (e) { + case PS3_EVT_SAS_INFO_LOCAL: + return "PS3_EVT_SAS_INFO_LOCAL"; + case PS3_EVT_PD_COUNT_LOCAL: + return "PS3_EVT_PD_COUNT_LOCAL"; + case PS3_EVT_VD_COUNT_LOCAL: + return "PS3_EVT_VD_COUNT_LOCAL"; + case PS3_EVT_CTRL_INFO_LOCAL: + return "PS3_EVT_CTRL_INFO_LOCAL"; + case PS3_EVT_PD_ATTR_LOCAL: + return "PS3_EVT_PD_ATTR_LOCAL"; + case PS3_EVT_VD_ATTR_LOCAL: + return "PS3_EVT_VD_ATTR_LOCAL"; + case PS3_EVT_DG_INFO_LOCAL: + return "PS3_EVT_DG_INFO_LOCAL"; + case PS3_EVT_BBU_INFO_LOCAL: + return "PS3_EVT_BBU_INFO_LOCAL"; + case PS3_EVT_CONFIG_LOCAL: + return "PS3_EVT_CONFIG_LOCAL"; + case PS3_EVT_IO_INFO_LOCAL: + return "PS3_EVT_IO_INFO_LOCAL"; + case PS3_EVT_UKEY_INFO_LOCAL: + return "PS3_EVT_UKEY_INFO_LOCAL"; + case PS3_EVT_HWR_INFO_LOCAL: + return "PS3_EVT_HWR_INFO_LOCAL"; + case PS3_EVT_ALARM_INFO_LOCAL: + return "PS3_EVT_ALARM_INFO_LOCAL"; + case PS3_EVT_ECC_INFO_LOCAL: + return "PS3_EVT_ECC_INFO_LOCAL"; + case PS3_EVT_UPGRADE_INFO_LOCAL: + return "PS3_EVT_UPGRADE_INFO_LOCAL"; + case PS3_EVT_TEMP_INFO_LOCAL: + return "PS3_EVT_TEMP_INFO_LOCAL"; + case PS3_EVT_PD_ATTR_EXTEND_LOCAL: + return "PS3_EVT_PD_ATTR_EXTEND_LOCAL"; + default: + return "PS3_EVT_ILLEGAL_TYPE"; + } +} + +enum MgrEvtSASInfoCode { + MGR_EVT_SAS_START = MGR_EVT_TYPE_BASE_LOCAL(PS3_EVT_SAS_INFO_LOCAL), + MGR_EVT_SAS_EXPANDER_IN = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_SAS_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_SAS_START + 0x1), + MGR_EVT_SAS_EXPANDER_OUT = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_SAS_INFO_LOCAL, PS3_EVT_CLASS_WARNING, + MGR_EVT_SAS_START + 0x2), + MGR_EVT_SAS_EXPANDER_UPDATE = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_SAS_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_SAS_START + 0x3), + MGR_EVT_SAS_EXPANDER_CHANGE = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_SAS_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_SAS_START + 0x4), + MGR_EVT_ENCL_TEMP_NORMAL = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_SAS_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_SAS_START + 0x5), + MGR_EVT_ENCL_TEMP_WARNING = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_SAS_INFO_LOCAL, PS3_EVT_CLASS_WARNING, + MGR_EVT_SAS_START + 0x6), + MGR_EVT_ENCL_TEMP_CRITICAL = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_SAS_INFO_LOCAL, PS3_EVT_CLASS_CRITICAL, + MGR_EVT_SAS_START + 0x7), + MGR_EVT_TOPO_LOOP = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_SAS_INFO_LOCAL, PS3_EVT_CLASS_WARNING, + MGR_EVT_SAS_START + 0x8), + MGR_EVT_LOOP_RESOLUTION = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_SAS_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_SAS_START + 0x9), + MGR_EVT_CASCADED_SCAN_SMP_FAIL = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_SAS_INFO_LOCAL, PS3_EVT_CLASS_FATAL, + MGR_EVT_SAS_START + 0xa), + MGR_EVT_ENCL_EXCEED = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_SAS_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_SAS_START + 0xb), + MGR_EVT_TRI_MODE_SWITCH = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_SAS_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_SAS_START + 0xc), + MGR_EVT_CHANGE_PHY_BY_USER = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_SAS_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_SAS_START + 0xd), + MGR_EVT_REV_HIBERNATE_CMD = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_SAS_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_SAS_START + 0xe), + MGR_EVT_PHY_CHANGE = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_SAS_INFO_LOCAL, PS3_EVT_CLASS_WARNING, + MGR_EVT_SAS_START + 0xf), + MGR_EVT_INQUIRY_INFO = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_SAS_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_SAS_START + 0x10), + MGR_EVT_SAS_SATA_LINK_SPEED_NOMATCH = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_SAS_INFO_LOCAL, PS3_EVT_CLASS_WARNING, + MGR_EVT_SAS_START + 0x11), + MGR_EVT_SAS_SATA_LN_EXCEPTION = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_SAS_INFO_LOCAL, PS3_EVT_CLASS_WARNING, + MGR_EVT_SAS_START + 0x12), + MGR_EVT_SAS_SATA_DRIVER_INFO = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_SAS_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_SAS_START + 0x13), + MGR_EVT_SAS_END = (MGR_EVT_SAS_START+0x14), +}; + +enum MgrEvtPdCountCode { + MGR_EVT_PD_COUNT_START = MGR_EVT_TYPE_BASE_LOCAL(PS3_EVT_PD_COUNT_LOCAL), + MGR_EVT_DEVM_DISK_IN = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_COUNT_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_COUNT_START + 0x1), + MGR_EVT_DEVM_DISK_OUT = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_COUNT_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_COUNT_START + 0x2), + MGR_EVT_MULITPILE_PD_IN = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_TRUE, + PS3_EVT_PD_COUNT_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_COUNT_START + 0x3), + MGR_EVT_MULITPILE_PD_OUT = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_TRUE, + PS3_EVT_PD_COUNT_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_COUNT_START + 0x4), + MGR_EVT_DEVM_JBOD = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_COUNT_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_COUNT_START + 0x5), + MGR_EVT_DEVM_READY = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_COUNT_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_COUNT_START + 0x6), + MGR_EVT_MULITPILE_JBOD = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_TRUE, + PS3_EVT_PD_COUNT_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_COUNT_START + 0x7), + MGR_EVT_MULITPILE_READY = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_TRUE, + PS3_EVT_PD_COUNT_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_COUNT_START + 0x8), + MGR_EVT_BACKPLANE_ON = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_COUNT_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_COUNT_START + 0x9), + MGR_EVT_BACKPLANE_OFF = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_COUNT_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_COUNT_START + 0xa), + MGR_EVT_MULITPILE_PD_STATE_CHANGE = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_TRUE, + PS3_EVT_PD_COUNT_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_COUNT_START + 0xb), + MGR_EVT_DEVM_DISK_CHANGE = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_COUNT_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_COUNT_START + 0xc), + MGR_EVT_DEFAULT_UNUSED = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_COUNT_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_COUNT_START + 0xd), + MGR_EVT_PD_PRE_READY = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_INTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_COUNT_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_COUNT_START + 0xe), + MGR_EVT_PD_COUNT_END = (MGR_EVT_PD_COUNT_START+0xf), +}; + +enum MgrEvtVdCountCode { + MGR_EVT_VD_COUNT_START = MGR_EVT_TYPE_BASE_LOCAL(PS3_EVT_VD_COUNT_LOCAL), + MGR_EVT_VD_OPTIMAL = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_COUNT_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_VD_COUNT_START + 0x1), + MGR_EVT_VD_PARTIAL_DEGRADE = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_COUNT_LOCAL, PS3_EVT_CLASS_CRITICAL, + MGR_EVT_VD_COUNT_START + 0x2), + MGR_EVT_VD_DEGRADE = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_COUNT_LOCAL, PS3_EVT_CLASS_CRITICAL, + MGR_EVT_VD_COUNT_START + 0x3), + MGR_EVT_VD_CREATED = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_COUNT_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_VD_COUNT_START + 0x4), + MGR_EVT_VD_DELETED = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_COUNT_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_VD_COUNT_START + 0x5), + MGR_EVT_VD_HIDDEN_CHANGE = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_COUNT_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_VD_COUNT_START + 0x6), + MGR_EVT_MULITPILE_VD_IN = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_TRUE, + PS3_EVT_VD_COUNT_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_VD_COUNT_START + 0x7), + MGR_EVT_MULITPILE_VD_OUT = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_TRUE, + PS3_EVT_VD_COUNT_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_VD_COUNT_START + 0x8), + MGR_EVT_VD_UNLOCK = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_COUNT_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_VD_COUNT_START + 0x9), + MGR_EVT_VD_OFFLINE = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_COUNT_LOCAL, PS3_EVT_CLASS_FATAL, + MGR_EVT_VD_COUNT_START + 0xa), + MGR_EVT_VD_COUNT_END = (MGR_EVT_VD_COUNT_START+0xb), +}; + +enum MgrEvtCtrlInfoCode { + MGR_EVT_CTRL_INFO_START = MGR_EVT_TYPE_BASE_LOCAL(PS3_EVT_CTRL_INFO_LOCAL), + MGR_EVT_CTRL_REBOOT = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_CTRL_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_CTRL_INFO_START + 0x1), + MGR_EVT_CTRL_SHUTDOWN = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_CTRL_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_CTRL_INFO_START + 0x2), + MGR_EVT_CTRL_TIME_CHANGE = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_CTRL_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_CTRL_INFO_START + 0x3), + MGR_EVT_CTRL_EVENT_LOG_CLEARED = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_CTRL_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_CTRL_INFO_START + 0x4), + MGR_EVT_CTRL_RBLD_RATE_CHANGED = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_CTRL_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_CTRL_INFO_START + 0x5), + MGR_EVT_CTRL_ENABLEMOVEBACK_CHANGED = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_CTRL_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_CTRL_INFO_START + 0x6), + MGR_EVT_CTRL_ENABLENCQ_CHANGED = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_CTRL_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_CTRL_INFO_START + 0x7), + MGR_EVT_CTRL_AUTO_REBUILD_CHANGED = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_CTRL_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_CTRL_INFO_START + 0x8), + MGR_EVT_CTRL_EGHS_READY_CHANGED = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_CTRL_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_CTRL_INFO_START + 0x9), + MGR_EVT_CTRL_EGHS_SPARE_CHANGED = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_CTRL_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_CTRL_INFO_START + 0xa), + MGR_EVT_CTRL_BGI_RATE_CHANGED = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_CTRL_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_CTRL_INFO_START + 0xb), + MGR_EVT_CTRL_MIGRATE_RATE_CHANGED = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_CTRL_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_CTRL_INFO_START + 0xc), + MGR_EVT_CTRL_CC_RATE_CHANGED = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_CTRL_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_CTRL_INFO_START + 0xd), + MGR_EVT_CTRL_DIRECT_CHANGE = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_CTRL_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_CTRL_INFO_START + 0xe), + MGR_EVT_CTRL_PR_START = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_CTRL_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_CTRL_INFO_START + 0xf), + MGR_EVT_CTRL_PR_PAUSE = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_CTRL_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_CTRL_INFO_START + 0x10), + MGR_EVT_CTRL_PR_REMINDER_PAUSE = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_CTRL_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_CTRL_INFO_START + 0x11), + MGR_EVT_CTRL_PR_RESUME = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_CTRL_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_CTRL_INFO_START + 0x12), + MGR_EVT_CTRL_PR_DONE = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_CTRL_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_CTRL_INFO_START + 0x13), + MGR_EVT_CTRL_PR_RATE_CHANGED = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_CTRL_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_CTRL_INFO_START + 0x14), + MGR_EVT_CTRL_PR_CANT_START = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_CTRL_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_CTRL_INFO_START + 0x15), + MGR_EVT_CTRL_PR_PROP_CHANGED = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_CTRL_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_CTRL_INFO_START + 0x16), + MGR_EVT_CTRL_PARAM_CHANGE = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_CTRL_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_CTRL_INFO_START + 0x17), + MGR_EVT_CTRL_AUTO_CC_PARAM_CHANGE = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_CTRL_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_CTRL_INFO_START + 0x18), + MGR_EVT_CTRL_RECOVERY_FACTORY = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_CTRL_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_CTRL_INFO_START + 0x19), + MGR_EVT_CTRL_PROFILEID_CHANGED = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_CTRL_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_CTRL_INFO_START + 0x1a), + MGR_EVT_CTRL_SANPSHOT_CREATE = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_CTRL_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_CTRL_INFO_START + 0x1b), + MGR_EVT_CTRL_SANPSHOT_DELETE = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_CTRL_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_CTRL_INFO_START + 0x1c), + MGR_EVT_CTRL_ENABLEPDM_CHANGED = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_CTRL_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_CTRL_INFO_START + 0x1d), + MGR_EVT_CTRL_PDMSUPPORTREADYPD = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_CTRL_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_CTRL_INFO_START + 0x1e), + MGR_EVT_CTRL_PDMTIMERINTERVAL = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_CTRL_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_CTRL_INFO_START + 0x1f), + MGR_EVT_CTRL_SECURITY_KEY_CHANGE = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_CTRL_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_CTRL_INFO_START + 0x20), + MGR_EVT_CTRL_SECURITY_KEY_CREATE = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_CTRL_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_CTRL_INFO_START + 0x21), + MGR_EVT_CTRL_SECURITY_KEY_DESTROY = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_CTRL_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_CTRL_INFO_START + 0x22), + MGR_EVT_CTRL_SECURITY_KEY_ESCROW = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_CTRL_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_CTRL_INFO_START + 0x23), + MGR_EVT_CTRL_SECURITY_KEY_FAILED = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_CTRL_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_CTRL_INFO_START + 0x24), + MGR_EVT_CTRL_SECURITY_KEY_INVALID = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_CTRL_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_CTRL_INFO_START + 0x25), + MGR_EVT_AUTOCONFIG = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_CTRL_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_CTRL_INFO_START + 0x26), + MGR_EVT_CTRL_FOREIGN_IMPORTED_ALL = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_CTRL_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_CTRL_INFO_START + 0x27), + MGR_EVT_CTRL_FOREIGN_CLEAR = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_CTRL_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_CTRL_INFO_START + 0x28), + MGR_EVT_CTRL_FOREIGN_IMPORT_FAIL = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_CTRL_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_CTRL_INFO_START + 0x29), + MGR_EVT_CTRL_FOREIGN_IMPORT_PART_FAIL = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_CTRL_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_CTRL_INFO_START + 0x2a), + MGR_EVT_CTRL_FOREIGN_IMPORT_FAIL_PDS = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_CTRL_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_CTRL_INFO_START + 0x2b), + MGR_EVT_CTRL_FOREIGN_PD_DETECTED = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_CTRL_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_CTRL_INFO_START + 0x2c), + MGR_EVT_PD_INSERT_TO_DG = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_CTRL_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_CTRL_INFO_START + 0x2d), + MGR_EVT_PD_IMPORT_TO_DG = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_CTRL_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_CTRL_INFO_START + 0x2e), + MGR_EVT_HARD_RESET = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_CTRL_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_CTRL_INFO_START + 0x2f), + MGR_EVT_DPF_CFG_MODIFY = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_CTRL_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_CTRL_INFO_START + 0x30), + MGR_EVT_SPOR = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_CTRL_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_CTRL_INFO_START + 0x31), + MGR_EVT_ONF_ABNORMAL = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_CTRL_INFO_LOCAL, PS3_EVT_CLASS_FATAL, + MGR_EVT_CTRL_INFO_START + 0x32), + MGR_EVT_CTRL_LOADED = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_CTRL_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_CTRL_INFO_START + 0x33), + MGR_EVT_CACHE_PROTECTION_CMPT_READY = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_CTRL_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_CTRL_INFO_START + 0x34), + MGR_EVT_CACHE_PROTECTION_CMPT_NOT_READY = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_CTRL_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_CTRL_INFO_START + 0x35), + MGR_EVT_CACHE_PROTECTION_CMPT_INSTABLE = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_CTRL_INFO_LOCAL, PS3_EVT_CLASS_WARNING, + MGR_EVT_CTRL_INFO_START + 0x36), + MGR_EVT_MAIN_MEDIUM_NOT_AVAILABLE = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_CTRL_INFO_LOCAL, PS3_EVT_CLASS_CRITICAL, + MGR_EVT_CTRL_INFO_START + 0x37), + MGR_EVT_BACKUP_MEDIUM_NOT_AVAILABLE = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_CTRL_INFO_LOCAL, PS3_EVT_CLASS_WARNING, + MGR_EVT_CTRL_INFO_START + 0x38), + MGR_EVT_MEDIUM_DATA_RECOVER = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_CTRL_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_CTRL_INFO_START + 0x39), + MGR_EVT_INIT_FAIL = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_CTRL_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_CTRL_INFO_START + 0x3a), + MGR_EVT_CTRL_POWER_MODE_CHANGED = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_CTRL_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_CTRL_INFO_START + 0x3b), + MGR_EVT_CTRL_INFO_END = (MGR_EVT_CTRL_INFO_START+0x3c), +}; + +enum MgrEvtPdAttrCode { + MGR_EVT_PD_ATTR_START = MGR_EVT_TYPE_BASE_LOCAL(PS3_EVT_PD_ATTR_LOCAL), + MGR_EVT_PD_INFO_CHANGE = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_START + 0x1), + MGR_EVT_PD_STATE_CHANGE = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_START + 0x2), + MGR_EVT_PD_MARKED_JBOD = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_START + 0x3), + MGR_EVT_PD_MARKED_READY = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_START + 0x4), + MGR_EVT_PD_MARKED_ONLINE = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_START + 0x5), + MGR_EVT_PD_MARKED_OFFLINE = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_START + 0x6), + MGR_EVT_PD_MARKED_FAILED = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_LOCAL, PS3_EVT_CLASS_CRITICAL, + MGR_EVT_PD_ATTR_START + 0x7), + MGR_EVT_PD_MARKED_MISSING = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_LOCAL, PS3_EVT_CLASS_CRITICAL, + MGR_EVT_PD_ATTR_START + 0x8), + MGR_EVT_PD_MARKED_REBUILD = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_START + 0x9), + MGR_EVT_PD_MARKED_REPLACE = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_START + 0xa), + MGR_EVT_PD_MARKED_UNCONFIGUREDBAD = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_START + 0xb), + MGR_EVT_PD_MARKED_FOREIGN = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_START + 0xc), + MGR_EVT_PD_NR_GLOBAL_SPARE_ADDED = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_START + 0xd), + MGR_EVT_PD_NR_GLOBAL_SPARE_DELETED = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_START + 0xe), + MGR_EVT_PD_NR_DEDICATED_SPARE_ADDED = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_START + 0xf), + MGR_EVT_PD_NR_DEDICATED_SPARE_DELETED = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_START + 0x10), + MGR_EVT_PD_R_GLOBAL_SPARE_ADDED = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_START + 0x11), + MGR_EVT_PD_R_GLOBAL_SPARE_DELETED = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_START + 0x12), + MGR_EVT_PD_R_DEDICATED_SPARE_ADDED = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_START + 0x13), + MGR_EVT_PD_R_DEDICATED_SPARE_DELETED = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_START + 0x14), + MGR_EVT_PD_RBLD_ABORT_BY_USER = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_START + 0x15), + MGR_EVT_PD_RBLD_DONE_PD = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_START + 0x16), + MGR_EVT_PD_RBLD_FAILED_BAD_SOURCE = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_LOCAL, PS3_EVT_CLASS_CRITICAL, + MGR_EVT_PD_ATTR_START + 0x17), + MGR_EVT_PD_RBLD_FAILED_BAD_TARGET = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_LOCAL, PS3_EVT_CLASS_CRITICAL, + MGR_EVT_PD_ATTR_START + 0x18), + MGR_EVT_PD_RBLD_PROGRESS = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_LOCAL, PS3_EVT_CLASS_PROCESS, + MGR_EVT_PD_ATTR_START + 0x19), + MGR_EVT_PD_RBLD_SUSPENDED_REMINDER = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_START + 0x1a), + MGR_EVT_PD_RBLD_SUSPENDED = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_START + 0x1b), + MGR_EVT_PD_RBLD_RESUME = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_START + 0x1c), + MGR_EVT_PD_RBLD_START = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_START + 0x1d), + MGR_EVT_PD_RBLD_START_AUTO = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_START + 0x1e), + MGR_EVT_PD_RBLD_MEDIUM_ERROR = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_LOCAL, PS3_EVT_CLASS_FATAL, + MGR_EVT_PD_ATTR_START + 0x1f), + MGR_EVT_PD_EMERGENCY_SPARE = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_START + 0x20), + MGR_EVT_PD_RECOVER_MEDIUM_ERROR = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_LOCAL, PS3_EVT_CLASS_FATAL, + MGR_EVT_PD_ATTR_START + 0x21), + MGR_EVT_PD_MOVEBACK_START = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_START + 0x22), + MGR_EVT_PD_MOVEBACK_ABORT = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_START + 0x23), + MGR_EVT_PD_MOVEBACK_ABORT_FOR_REBUILD = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_LOCAL, PS3_EVT_CLASS_CRITICAL, + MGR_EVT_PD_ATTR_START + 0x24), + MGR_EVT_PD_MOVEBACK_DONE = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_START + 0x25), + MGR_EVT_PD_MOVEBACK_PROGRESS = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_LOCAL, PS3_EVT_CLASS_PROCESS, + MGR_EVT_PD_ATTR_START + 0x26), + MGR_EVT_PD_MOVEBACK_SUSPENDED = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_START + 0x27), + MGR_EVT_PD_MOVEBACK_SUSPENDED_REMINDER = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_START + 0x28), + MGR_EVT_PD_MOVEBACK_RESUME = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_START + 0x29), + MGR_EVT_PD_MOVEBACK_START_AUTO = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_START + 0x2a), + MGR_EVT_PD_MOVEBACK_FAILED_BAD_SOURCE = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_LOCAL, PS3_EVT_CLASS_CRITICAL, + MGR_EVT_PD_ATTR_START + 0x2b), + MGR_EVT_PD_MOVEBACK_FAILED_BAD_TARGET = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_START + 0x2c), + MGR_EVT_PD_MOVEBACK_ABORT_BY_USER = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_START + 0x2d), + MGR_EVT_PD_MOVEBACK_MEDIUM_ERROR = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_LOCAL, PS3_EVT_CLASS_FATAL, + MGR_EVT_PD_ATTR_START + 0x2e), + MGR_EVT_PD_FGI_START = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_START + 0x2f), + MGR_EVT_PD_FGI_COMPLETE = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_START + 0x30), + MGR_EVT_PD_FGI_FAILED = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_LOCAL, PS3_EVT_CLASS_CRITICAL, + MGR_EVT_PD_ATTR_START + 0x31), + MGR_EVT_PD_FGI_PROGRESS = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_LOCAL, PS3_EVT_CLASS_PROCESS, + MGR_EVT_PD_ATTR_START + 0x32), + MGR_EVT_PD_FGI_ABORT = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_START + 0x33), + MGR_EVT_PD_PR_ABORTED = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_LOCAL, PS3_EVT_CLASS_WARNING, + MGR_EVT_PD_ATTR_START + 0x34), + MGR_EVT_PD_PR_CORRECTED = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_START + 0x35), + MGR_EVT_PD_PR_UNCORRECTABLE = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_LOCAL, PS3_EVT_CLASS_FATAL, + MGR_EVT_PD_ATTR_START + 0x36), + MGR_EVT_PD_PR_FOUND_MEDIUM_ERROR = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_LOCAL, PS3_EVT_CLASS_WARNING, + MGR_EVT_PD_ATTR_START + 0x37), + MGR_EVT_PD_PR_PROGRESS = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_LOCAL, PS3_EVT_CLASS_PROCESS, + MGR_EVT_PD_ATTR_START + 0x38), + MGR_EVT_PD_SET_BOOT_DRIVE = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_START + 0x39), + MGR_EVT_PD_RESET_BOOT_DRIVE = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_START + 0x3a), + MGR_EVT_PD_RESET_BOOT_DRIVE_WITH_DISK_OUT = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_START + 0x3b), + MGR_EVT_PD_SET_BOOT_DRIVE_WITH_DISK_IN = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_START + 0x3c), + MGR_EVT_PD_POWERSAVE_TO_ON = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_START + 0x3d), + MGR_EVT_PD_POWERSAVE_TO_TRANSITION = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_START + 0x3e), + MGR_EVT_PD_TRANSITION_TO_ON = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_START + 0x3f), + MGR_EVT_PD_TRANSITION_TO_POWERSAVE = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_START + 0x40), + MGR_EVT_PD_ON_TO_POWERSAVE = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_START + 0x41), + MGR_EVT_PD_START_LOCATE = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_START + 0x42), + MGR_EVT_PD_STOP_LOCATE = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_START + 0x43), + MGR_EVT_PD_PFA_ERROR = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_LOCAL, PS3_EVT_CLASS_WARNING, + MGR_EVT_PD_ATTR_START + 0x44), + MGR_EVT_PD_NOT_PRESENT = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_LOCAL, PS3_EVT_CLASS_WARNING, + MGR_EVT_PD_ATTR_START + 0x45), + MGR_EVT_DEVM_DISK_FAULT = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_LOCAL, PS3_EVT_CLASS_CRITICAL, + MGR_EVT_PD_ATTR_START + 0x46), + MGR_EVT_PHY_FLASHOFFLINE = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_LOCAL, PS3_EVT_CLASS_WARNING, + MGR_EVT_PD_ATTR_START + 0x47), + MGR_EVT_PHY_FLASHONLINE = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_START + 0x48), + MGR_EVT_NFR_DONE = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_START + 0x49), + MGR_EVT_NFR_REPAIRED = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_START + 0x4a), + MGR_EVT_NFR_REPORT = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_START + 0x4b), + MGR_EVT_NFR_STOP = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_START + 0x4c), + MGR_EVT_PD_DOWNLOAD_DONE = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_START + 0x4d), + MGR_EVT_PD_DOWNLOAD_START = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_START + 0x4e), + MGR_EVT_NVME_PHY_BAD = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_START + 0x4f), + MGR_EVT_PD_ATTR_END = (MGR_EVT_PD_ATTR_START+0x50), +}; + +enum MgrEvtVdAttrCode { + MGR_EVT_VD_ATTR_START = MGR_EVT_TYPE_BASE_LOCAL(PS3_EVT_VD_ATTR_LOCAL), + MGR_EVT_MULITPILE_VD_STATE_CHANGE = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_TRUE, + PS3_EVT_VD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_VD_ATTR_START + 0x1), + MGR_EVT_VD_OFFLINE_OLD = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_ATTR_LOCAL, PS3_EVT_CLASS_FATAL, + MGR_EVT_VD_ATTR_START + 0x2), + MGR_EVT_VD_LOCK = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_ATTR_LOCAL, PS3_EVT_CLASS_WARNING, + MGR_EVT_VD_ATTR_START + 0x3), + MGR_EVT_VD_PASSWD_CHANGED = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_VD_ATTR_START + 0x4), + MGR_EVT_VD_SETTINGS_CHANGE = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_VD_ATTR_START + 0x5), + MGR_EVT_VD_PD_CHANGE = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_INTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_VD_ATTR_START + 0x6), + MGR_EVT_VD_STATE_CHANGE = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_VD_ATTR_START + 0x7), + MGR_EVT_PD_RBLD_DONE_LD = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_VD_ATTR_START + 0x8), + MGR_EVT_VD_FGI_START = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_VD_ATTR_START + 0x9), + MGR_EVT_VD_FGI_COMPLETE = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_VD_ATTR_START + 0xa), + MGR_EVT_VD_FGI_FAILED = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_ATTR_LOCAL, PS3_EVT_CLASS_CRITICAL, + MGR_EVT_VD_ATTR_START + 0xb), + MGR_EVT_VD_FGI_PROGRESS = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_ATTR_LOCAL, PS3_EVT_CLASS_PROCESS, + MGR_EVT_VD_ATTR_START + 0xc), + MGR_EVT_VD_FGI_ABORT = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_VD_ATTR_START + 0xd), + MGR_EVT_VD_BGI_START = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_VD_ATTR_START + 0xe), + MGR_EVT_VD_BGI_COMPLETE = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_VD_ATTR_START + 0xf), + MGR_EVT_VD_BGI_FAILED = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_ATTR_LOCAL, PS3_EVT_CLASS_CRITICAL, + MGR_EVT_VD_ATTR_START + 0x10), + MGR_EVT_VD_BGI_PROGRESS = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_ATTR_LOCAL, PS3_EVT_CLASS_PROCESS, + MGR_EVT_VD_ATTR_START + 0x11), + MGR_EVT_VD_BGI_ABORT = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_ATTR_LOCAL, PS3_EVT_CLASS_WARNING, + MGR_EVT_VD_ATTR_START + 0x12), + MGR_EVT_VD_BGI_PAUSE = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_VD_ATTR_START + 0x13), + MGR_EVT_VD_BGI_REMINDER_PAUSE = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_VD_ATTR_START + 0x14), + MGR_EVT_VD_BGI_RESUME = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_VD_ATTR_START + 0x15), + MGR_EVT_VD_BGI_CORRECTABLE_ERROR = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_VD_ATTR_START + 0x16), + MGR_EVT_VD_BGI_UNCORRECTABLE_ERROR = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_ATTR_LOCAL, PS3_EVT_CLASS_FATAL, + MGR_EVT_VD_ATTR_START + 0x17), + MGR_EVT_VD_ALTER_ATTR_BY_MIGRATION = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_VD_ATTR_START + 0x18), + MGR_EVT_VD_BBM_CLEARED = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_VD_ATTR_START + 0x19), + MGR_EVT_VD_BBM_PERCENT_FULL = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_ATTR_LOCAL, PS3_EVT_CLASS_WARNING, + MGR_EVT_VD_ATTR_START + 0x1a), + MGR_EVT_VD_BBM_100_FULL = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_ATTR_LOCAL, PS3_EVT_CLASS_WARNING, + MGR_EVT_VD_ATTR_START + 0x1b), + MGR_EVT_VD_BBM_LOG_FULL = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_ATTR_LOCAL, PS3_EVT_CLASS_FATAL, + MGR_EVT_VD_ATTR_START + 0x1c), + MGR_EVT_VD_BBM_LOG_RCT = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_ATTR_LOCAL, PS3_EVT_CLASS_FATAL, + MGR_EVT_VD_ATTR_START + 0x1d), + MGR_EVT_VD_BBM_LOG_WCT = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_ATTR_LOCAL, PS3_EVT_CLASS_CRITICAL, + MGR_EVT_VD_ATTR_START + 0x1e), + MGR_EVT_VD_BBM_DEL_RCT = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_TRUE, + PS3_EVT_VD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_VD_ATTR_START + 0x1f), + MGR_EVT_VD_CC_ABORTED = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_VD_ATTR_START + 0x20), + MGR_EVT_VD_CC_CORRECTED_MEDIUM_ERROR = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_VD_ATTR_START + 0x21), + MGR_EVT_VD_CC_DOUBLE_MEDIUM_ERRORS = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_ATTR_LOCAL, PS3_EVT_CLASS_FATAL, + MGR_EVT_VD_ATTR_START + 0x22), + MGR_EVT_VD_CC_DONE = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_VD_ATTR_START + 0x23), + MGR_EVT_VD_CC_DONE_INCON = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_VD_ATTR_START + 0x24), + MGR_EVT_VD_CC_FAILED = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_ATTR_LOCAL, PS3_EVT_CLASS_CRITICAL, + MGR_EVT_VD_ATTR_START + 0x25), + MGR_EVT_VD_CC_FAILED_UNCOR = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_ATTR_LOCAL, PS3_EVT_CLASS_FATAL, + MGR_EVT_VD_ATTR_START + 0x26), + MGR_EVT_VD_CC_INCONSISTENT_PARITY = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_ATTR_LOCAL, PS3_EVT_CLASS_WARNING, + MGR_EVT_VD_ATTR_START + 0x27), + MGR_EVT_VD_CC_INCONSISTENT_PARITY_LOGGING_DISABLED = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_ATTR_LOCAL, PS3_EVT_CLASS_WARNING, + MGR_EVT_VD_ATTR_START + 0x28), + MGR_EVT_VD_CC_PROGRESS = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_ATTR_LOCAL, PS3_EVT_CLASS_PROCESS, + MGR_EVT_VD_ATTR_START + 0x29), + MGR_EVT_VD_CC_START = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_VD_ATTR_START + 0x2a), + MGR_EVT_VD_CC_PAUSE = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_VD_ATTR_START + 0x2b), + MGR_EVT_VD_CC_REMINDER_PAUSE = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_VD_ATTR_START + 0x2c), + MGR_EVT_VD_CC_RESUME = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_VD_ATTR_START + 0x2d), + MGR_EVT_VD_RW_RAID1X_WRITE_CARDBUSY = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_VD_ATTR_START + 0x2e), + MGR_EVT_VD_SET_BOOT_DRIVE = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_VD_ATTR_START + 0x2f), + MGR_EVT_VD_RESET_BOOT_DRIVE = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_VD_ATTR_START + 0x30), + MGR_EVT_VD_RESET_BOOT_DRIVE_WITH_DISK_OUT = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_VD_ATTR_START + 0x31), + MGR_EVT_VD_SET_BOOT_DRIVE_WITH_DISK_IN = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_VD_ATTR_START + 0x32), + MGR_EVT_ALL_CONFIGS_MISSING = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_VD_ATTR_START + 0x33), + MGR_EVT_PDS_MISSING = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_VD_ATTR_START + 0x34), + MGR_EVT_NVSRAM_RECOVER_FAIL = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_VD_ATTR_START + 0x35), + MGR_EVT_NVSRAM_UPDATE_FAIL = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_VD_ATTR_START + 0x36), + MGR_EVT_VD_ERASE_ABORT = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_VD_ATTR_START + 0x37), + MGR_EVT_VD_ERASE_COMPLETE = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_VD_ATTR_START + 0x38), + MGR_EVT_VD_ERASE_DISK_FAILED = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_ATTR_LOCAL, PS3_EVT_CLASS_WARNING, + MGR_EVT_VD_ATTR_START + 0x39), + MGR_EVT_VD_ERASE_FAILED = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_ATTR_LOCAL, PS3_EVT_CLASS_CRITICAL, + MGR_EVT_VD_ATTR_START + 0x3a), + MGR_EVT_VD_ERASE_PROGRESS = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_ATTR_LOCAL, PS3_EVT_CLASS_PROCESS, + MGR_EVT_VD_ATTR_START + 0x3b), + MGR_EVT_VD_ERASE_START = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_VD_ATTR_START + 0x3c), + MGR_EVT_VD_EXPAND_FINISH = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_VD_ATTR_START + 0x3d), + MGR_EVT_VD_EXPAND_START = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_VD_ATTR_START + 0x3e), + MGR_EVT_VD_FLUSH_CLEAR_FAILED = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_VD_ATTR_START + 0x3f), + MGR_EVT_VD_FLUSH_RECORD_FAILED = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_VD_ATTR_START + 0x40), + MGR_EVT_VD_FLUSH_SET_INCONSIST = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_VD_ATTR_START + 0x41), + MGR_EVT_VDS_MISSING = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_VD_ATTR_START + 0x42), + MGR_EVT_VD_BBM_UNCORRECTABLE_LOGGED = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_ATTR_LOCAL, PS3_EVT_CLASS_FATAL, + MGR_EVT_VD_ATTR_START + 0x43), + MGR_EVT_VD_FLUSH_CACHE_PINNED = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_ATTR_LOCAL, PS3_EVT_CLASS_FATAL, + MGR_EVT_VD_ATTR_START + 0x44), + MGR_EVT_VD_FLUSH_OCR_CACHE_PINNED = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_ATTR_LOCAL, PS3_EVT_CLASS_FATAL, + MGR_EVT_VD_ATTR_START + 0x45), + MGR_EVT_VD_FLUSH_CACHE_DISCARDED = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_VD_ATTR_START + 0x46), + MGR_EVT_VD_FLUSH_CACHE_DISCARDED_DEL = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_VD_ATTR_START + 0x47), + MGR_EVT_VD_FLUSH_CACHE_DESTAGED = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_ATTR_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_VD_ATTR_START + 0x48), + MGR_EVT_VD_CM_RECOVERY_CRC_FAIL = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_ATTR_LOCAL, PS3_EVT_CLASS_WARNING, + MGR_EVT_VD_ATTR_START + 0x49), + MGR_EVT_VD_BGI_COMPLETE_UNCOR = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_VD_ATTR_LOCAL, PS3_EVT_CLASS_FATAL, + MGR_EVT_VD_ATTR_START + 0x4a), + MGR_EVT_VD_ATTR_END = (MGR_EVT_VD_ATTR_START+0x4b), +}; + +enum MgrEvtDgInfoCode { + MGR_EVT_DG_INFO_START = MGR_EVT_TYPE_BASE_LOCAL(PS3_EVT_DG_INFO_LOCAL), + MGR_EVT_DG_CREATED = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_DG_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_DG_INFO_START + 0x1), + MGR_EVT_DG_DELETED = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_DG_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_DG_INFO_START + 0x2), + MGR_EVT_DG_SETTING_CHANGE = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_DG_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_DG_INFO_START + 0x3), + MGR_EVT_DG_MIGRATION_START = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_DG_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_DG_INFO_START + 0x4), + MGR_EVT_DG_MIGRATION_RECOVER = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_DG_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_DG_INFO_START + 0x5), + MGR_EVT_DG_MIGRATION_SUCCESS = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_DG_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_DG_INFO_START + 0x6), + MGR_EVT_DG_MIGRATION_ALLOC_RESOURCE_FAILED = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_DG_INFO_LOCAL, PS3_EVT_CLASS_WARNING, + MGR_EVT_DG_INFO_START + 0x7), + MGR_EVT_DG_MIGRATION_FAILED = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_DG_INFO_LOCAL, PS3_EVT_CLASS_FATAL, + MGR_EVT_DG_INFO_START + 0x8), + MGR_EVT_DG_MIGRATION_RECOVER_FAILED = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_DG_INFO_LOCAL, PS3_EVT_CLASS_FATAL, + MGR_EVT_DG_INFO_START + 0x9), + MGR_EVT_DG_MIGRATION_PROGRESS = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_DG_INFO_LOCAL, PS3_EVT_CLASS_PROCESS, + MGR_EVT_DG_INFO_START + 0xa), + MGR_EVT_DG_MIGRATION_INTERNAL_WARNING = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_DG_INFO_LOCAL, PS3_EVT_CLASS_WARNING, + MGR_EVT_DG_INFO_START + 0xb), + MGR_EVT_DG_PRSWITCH_CHANGE = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_DG_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_DG_INFO_START + 0xc), + MGR_EVT_DG_PR_ABORTED_WM_FAILED = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_DG_INFO_LOCAL, PS3_EVT_CLASS_WARNING, + MGR_EVT_DG_INFO_START + 0xd), + MGR_EVT_DG_PR_ABORTED_SPINUP_FAILED = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_DG_INFO_LOCAL, PS3_EVT_CLASS_WARNING, + MGR_EVT_DG_INFO_START + 0xe), + MGR_EVT_DG_INFO_END = (MGR_EVT_DG_INFO_START+0xf), +}; + +enum MgrEvtBbuInfoCode { + MGR_EVT_BBU_START = MGR_EVT_TYPE_BASE_LOCAL(PS3_EVT_BBU_INFO_LOCAL), + MGR_EVT_BBU_PRESENT = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_BBU_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_BBU_START + 0x1), + MGR_EVT_BBU_NOT_PRESENT = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_BBU_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_BBU_START + 0x2), + MGR_EVT_BBU_GOOD = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_BBU_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_BBU_START + 0x3), + MGR_EVT_BBU_BAD = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_BBU_INFO_LOCAL, PS3_EVT_CLASS_WARNING, + MGR_EVT_BBU_START + 0x4), + MGR_EVT_BBU_CAP_BELOW_THRESHOLD = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_BBU_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_BBU_START + 0x5), + MGR_EVT_BBU_CAP_ABOVE_THRESHOLD = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_BBU_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_BBU_START + 0x6), + MGR_EVT_BBU_CHARGE_STATUS = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_BBU_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_BBU_START + 0x7), + MGR_EVT_BBU_CHARGE_COMPLETE = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_BBU_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_BBU_START + 0x8), + MGR_EVT_BBU_INSERT = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_BBU_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_BBU_START + 0x9), + MGR_EVT_BBU_ABSENT = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_BBU_INFO_LOCAL, PS3_EVT_CLASS_WARNING, + MGR_EVT_BBU_START + 0xa), + MGR_EVT_BBU_TEMPERATURE_NORMAL = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_BBU_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_BBU_START + 0xb), + MGR_EVT_BBU_TEMPERATURE_HIGH = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_BBU_INFO_LOCAL, PS3_EVT_CLASS_CRITICAL, + MGR_EVT_BBU_START + 0xc), + MGR_EVT_BBU_VOLTAGE_NORMAL = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_BBU_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_BBU_START + 0xd), + MGR_EVT_BBU_VOLTAGE_HIGH = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_BBU_INFO_LOCAL, PS3_EVT_CLASS_WARNING, + MGR_EVT_BBU_START + 0xe), + MGR_EVT_BBU_CURRENT_NORMAL = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_BBU_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_BBU_START + 0xf), + MGR_EVT_BBU_CURRENT_HIGH = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_BBU_INFO_LOCAL, PS3_EVT_CLASS_WARNING, + MGR_EVT_BBU_START + 0x10), + MGR_EVT_BBU_LOAD_NORMAL = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_BBU_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_BBU_START + 0x11), + MGR_EVT_BBU_VOLTAGE_LOW = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_BBU_INFO_LOCAL, PS3_EVT_CLASS_WARNING, + MGR_EVT_BBU_START + 0x12), + MGR_EVT_BBU_BATTERY_CAP_ABOVE_SOH_THRESHOLD = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_BBU_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_BBU_START + 0x13), + MGR_EVT_BBU_BATTERY_CAP_BELOW_SOH_THRESHOLD = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_BBU_INFO_LOCAL, PS3_EVT_CLASS_CRITICAL, + MGR_EVT_BBU_START + 0x14), + MGR_EVT_BBU_LEARN_STAGE = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_BBU_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_BBU_START + 0x15), + MGR_EVT_BBU_LEARN_REQUESTED = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_BBU_INFO_LOCAL, PS3_EVT_CLASS_WARNING, + MGR_EVT_BBU_START + 0x16), + MGR_EVT_BBU_LEARN_POSTPONED = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_BBU_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_BBU_START + 0x17), + MGR_EVT_BBU_LEARN_TIMEOUT = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_BBU_INFO_LOCAL, PS3_EVT_CLASS_CRITICAL, + MGR_EVT_BBU_START + 0x18), + MGR_EVT_BBU_LEARN_MANUAL = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_BBU_INFO_LOCAL, PS3_EVT_CLASS_WARNING, + MGR_EVT_BBU_START + 0x19), + MGR_EVT_BBU_LEARN_RESCHEDULED = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_BBU_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_BBU_START + 0x1a), + MGR_EVT_BBU_PROPERTIES_CHANGED = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_BBU_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_BBU_START + 0x1b), + MGR_EVT_BBU_END = (MGR_EVT_BBU_START+0x1c), +}; + +enum MgrEvtConfigCode { + MGR_EVT_CONFIG_START = MGR_EVT_TYPE_BASE_LOCAL(PS3_EVT_CONFIG_LOCAL), + MGR_EVT_CONFIG_INFO = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_CONFIG_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_CONFIG_START + 0x1), + MGR_EVT_CONFIG_FLASH_READ_FAIL = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_CONFIG_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_CONFIG_START + 0x2), + MGR_EVT_CONFIG_FLASH_WRITE_FAIL = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_CONFIG_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_CONFIG_START + 0x3), + MGR_EVT_CONFIG_NVSRAM_READ_FAIL = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_CONFIG_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_CONFIG_START + 0x4), + MGR_EVT_CONFIG_NVSRAM_WRITE_FAIL = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_CONFIG_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_CONFIG_START + 0x5), + MGR_EVT_CONFIG_VERSION_MISMATCH = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_CONFIG_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_CONFIG_START + 0x6), + MGR_EVT_CONFIG_MEDIA_VALUE_CHECK_FAIL = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_CONFIG_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_CONFIG_START + 0x7), + MGR_EVT_CONFIG_ERASE_NVSRAM = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_CONFIG_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_CONFIG_START + 0x8), + MGR_EVT_PHY_LINKSPEED_CFG_CHANGE = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_CONFIG_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_CONFIG_START + 0x9), + MGR_EVT_CONFIG_END = (MGR_EVT_CONFIG_START+0xa), +}; + +enum MgrEvtIoInfoCode { + MGR_EVT_IO_START = MGR_EVT_TYPE_BASE_LOCAL(PS3_EVT_IO_INFO_LOCAL), + MGR_EVT_IO_TEST = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_IO_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_IO_START + 0x1), + MGR_EVT_IO_SENSE_DATA = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_IO_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_IO_START + 0x2), + MGR_EVT_DEVICE_RESET = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_IO_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_IO_START + 0x3), + MGR_EVT_NVME_DEVICE_RESET = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_IO_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_IO_START + 0x4), + MGR_EVT_DEVICE_ABNORMAL = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_IO_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_IO_START + 0x5), + MGR_EVT_IO_SENSE = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_IO_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_IO_START + 0x6), + MGR_EVT_IO_END = (MGR_EVT_IO_START+0x7), +}; + +enum MgrEvtUkeyInfoCode { + MGR_EVT_UKEY_START = MGR_EVT_TYPE_BASE_LOCAL(PS3_EVT_UKEY_INFO_LOCAL), + MGR_EVT_UKEY_INSERT = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_UKEY_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_UKEY_START + 0x1), + MGR_EVT_UKEY_DEGRADE = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_UKEY_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_UKEY_START + 0x2), + MGR_EVT_RAID_KEY_OVERCURRENT = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_UKEY_INFO_LOCAL, PS3_EVT_CLASS_WARNING, + MGR_EVT_UKEY_START + 0x3), + MGR_EVT_RAID_KEY_OVERCURRENT_RECOVER = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_UKEY_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_UKEY_START + 0x4), + MGR_EVT_UKEY_END = (MGR_EVT_UKEY_START+0x5), +}; + +enum MgrEvtHwRInfoCode { + MGR_EVT_HWR_START = MGR_EVT_TYPE_BASE_LOCAL(PS3_EVT_HWR_INFO_LOCAL), + MGR_EVT_HWR_HAC_RESET_BEGIN = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_HWR_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_HWR_START + 0x1), + MGR_EVT_HWR_HAC_RESET_END = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_HWR_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_HWR_START + 0x2), + MGR_EVT_REPORT_HAC_ECC = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_HWR_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_HWR_START + 0x3), + MGR_EVT_HWR_END = (MGR_EVT_HWR_START+0x4), +}; + +enum MgrEvtAlarmInfoCode { + MGR_EVT_ALARM_START = MGR_EVT_TYPE_BASE_LOCAL(PS3_EVT_ALARM_INFO_LOCAL), + MGR_EVT_ALARM_ENABLED = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_ALARM_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_ALARM_START + 0x1), + MGR_EVT_ALARM_DISABLED = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_ALARM_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_ALARM_START + 0x2), + MGR_EVT_ALARM_END = (MGR_EVT_ALARM_START+0x3), +}; + +enum MgrEvtEccInfoCode { + MGR_EVT_ECC_START = MGR_EVT_TYPE_BASE_LOCAL(PS3_EVT_ECC_INFO_LOCAL), + MGR_EVT_ECC_CNT_EXCEED = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_ECC_INFO_LOCAL, PS3_EVT_CLASS_WARNING, + MGR_EVT_ECC_START + 0x1), + MGR_EVT_ECC_ERR_INTR = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_ECC_INFO_LOCAL, PS3_EVT_CLASS_CRITICAL, + MGR_EVT_ECC_START + 0x2), + MGR_EVT_ECC_CNT_CLEAR = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_ECC_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_ECC_START + 0x3), + MGR_EVT_ECC_CLI_CHANGE = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_ECC_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_ECC_START + 0x4), + MGR_EVT_ECC_FACTORY_CHANGE = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_INTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_ECC_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_ECC_START + 0x5), + MGR_EVT_ECC_END = (MGR_EVT_ECC_START+0x6), +}; + +enum MgrEvtUpgradeInfoCode { + MGR_EVT_UPGRADE_START = MGR_EVT_TYPE_BASE_LOCAL(PS3_EVT_UPGRADE_INFO_LOCAL), + MGR_EVT_IMAGE_DOWNLOAD_ERROR = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_UPGRADE_INFO_LOCAL, PS3_EVT_CLASS_CRITICAL, + MGR_EVT_UPGRADE_START + 0x1), + MGR_EVT_IMAGE_AUTHENTICATION_FAIL = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_UPGRADE_INFO_LOCAL, PS3_EVT_CLASS_CRITICAL, + MGR_EVT_UPGRADE_START + 0x2), + MGR_EVT_IMAGE_VERSION_CHECK_FAIL = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_UPGRADE_INFO_LOCAL, PS3_EVT_CLASS_CRITICAL, + MGR_EVT_UPGRADE_START + 0x3), + MGR_EVT_IMAGE_CHECKSUM_CHECK_FAIL = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_UPGRADE_INFO_LOCAL, PS3_EVT_CLASS_CRITICAL, + MGR_EVT_UPGRADE_START + 0x4), + MGR_EVT_IMAGE_FROM_BACKUP = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_UPGRADE_INFO_LOCAL, PS3_EVT_CLASS_WARNING, + MGR_EVT_UPGRADE_START + 0x5), + MGR_EVT_DEV_OPEN_ERROR = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_UPGRADE_INFO_LOCAL, PS3_EVT_CLASS_CRITICAL, + MGR_EVT_UPGRADE_START + 0x6), + MGR_EVT_DEV_FLASH_ERROR = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_UPGRADE_INFO_LOCAL, PS3_EVT_CLASS_CRITICAL, + MGR_EVT_UPGRADE_START + 0x7), + MGR_EVT_DEV_ERASE_ERROR = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_UPGRADE_INFO_LOCAL, PS3_EVT_CLASS_CRITICAL, + MGR_EVT_UPGRADE_START + 0x8), + MGR_EVT_DEV_CLOSE_ERROR = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_UPGRADE_INFO_LOCAL, PS3_EVT_CLASS_CRITICAL, + MGR_EVT_UPGRADE_START + 0x9), + MGR_EVT_FLASH_GENERAL_ERROR = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_UPGRADE_INFO_LOCAL, PS3_EVT_CLASS_CRITICAL, + MGR_EVT_UPGRADE_START + 0xa), + MGR_EVT_FLASH_TIMEOUT = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_UPGRADE_INFO_LOCAL, PS3_EVT_CLASS_CRITICAL, + MGR_EVT_UPGRADE_START + 0xb), + MGR_EVT_UPGRADE_SUCCESS = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_UPGRADE_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_UPGRADE_START + 0xc), + MGR_EVT_UPGRADE_SYNC_START = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_UPGRADE_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_UPGRADE_START + 0xd), + MGR_EVT_UPGRADE_SYNC_FAILED = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_UPGRADE_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_UPGRADE_START + 0xe), + MGR_EVT_UPGRADE_SYNC_SUCCESS = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_UPGRADE_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_UPGRADE_START + 0xf), + MGR_EVT_UPGRADE_END = (MGR_EVT_UPGRADE_START+0x10), +}; + +enum MgrEvtTempInfoCode { + MGR_EVT_TEMP_START = MGR_EVT_TYPE_BASE_LOCAL(PS3_EVT_TEMP_INFO_LOCAL), + MGR_EVT_TEMP_WITHIN_OPTIMAL_RANGE = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_TEMP_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_TEMP_START + 0x1), + MGR_EVT_TEMP_ABOVE_OPTIMAL_RANGE = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_TEMP_INFO_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_TEMP_START + 0x2), + MGR_EVT_TEMP_WARNING = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_TEMP_INFO_LOCAL, PS3_EVT_CLASS_WARNING, + MGR_EVT_TEMP_START + 0x3), + MGR_EVT_TEMP_CRITICAL = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_TEMP_INFO_LOCAL, PS3_EVT_CLASS_CRITICAL, + MGR_EVT_TEMP_START + 0x4), + MGR_EVT_TEMP_END = (MGR_EVT_TEMP_START+0x5), +}; + +enum MgrEvtPdAttrExtendCode { + MGR_EVT_PD_ATTR_EXTEND_START = MGR_EVT_TYPE_BASE_LOCAL(PS3_EVT_PD_ATTR_EXTEND_LOCAL), + MGR_EVT_PD_ERASE_ABORT = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_EXTEND_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_EXTEND_START + 0x1), + MGR_EVT_PD_ERASE_COMPLETE = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_EXTEND_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_EXTEND_START + 0x2), + MGR_EVT_PD_ERASE_FAILED = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_EXTEND_LOCAL, PS3_EVT_CLASS_CRITICAL, + MGR_EVT_PD_ATTR_EXTEND_START + 0x3), + MGR_EVT_PD_ERASE_PROGRESS = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_EXTEND_LOCAL, PS3_EVT_CLASS_PROCESS, + MGR_EVT_PD_ATTR_EXTEND_START + 0x4), + MGR_EVT_PD_ERASE_START = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_EXTEND_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_EXTEND_START + 0x5), + MGR_EVT_PD_PDM_ABORT_BY_USER = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_EXTEND_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_EXTEND_START + 0x6), + MGR_EVT_PD_PDM_ABORT_FOR_REBUILD = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_EXTEND_LOCAL, PS3_EVT_CLASS_CRITICAL, + MGR_EVT_PD_ATTR_EXTEND_START + 0x7), + MGR_EVT_PD_PDM_ABORT = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_EXTEND_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_EXTEND_START + 0x8), + MGR_EVT_PD_PDM_DONE = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_EXTEND_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_EXTEND_START + 0x9), + MGR_EVT_PD_PDM_FAILED_BAD_SOURCE = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_EXTEND_LOCAL, PS3_EVT_CLASS_CRITICAL, + MGR_EVT_PD_ATTR_EXTEND_START + 0xa), + MGR_EVT_PD_PDM_FAILED_BAD_TARGET = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_EXTEND_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_EXTEND_START + 0xb), + MGR_EVT_PD_PDM_MEDIUM_ERROR = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_EXTEND_LOCAL, PS3_EVT_CLASS_FATAL, + MGR_EVT_PD_ATTR_EXTEND_START + 0xc), + MGR_EVT_PD_PDM_PROGRESS = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_EXTEND_LOCAL, PS3_EVT_CLASS_PROCESS, + MGR_EVT_PD_ATTR_EXTEND_START + 0xd), + MGR_EVT_PD_PDM_REPLACED_SOURCE = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_EXTEND_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_EXTEND_START + 0xe), + MGR_EVT_PD_PDM_RESUME = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_EXTEND_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_EXTEND_START + 0xf), + MGR_EVT_PD_PDM_START_AUTO = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_EXTEND_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_EXTEND_START + 0x10), + MGR_EVT_PD_PDM_START = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_EXTEND_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_EXTEND_START + 0x11), + MGR_EVT_PD_PDM_SUSPENDED_REMINDER = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_EXTEND_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_EXTEND_START + 0x12), + MGR_EVT_PD_PDM_SUSPENDED = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_EXTEND_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_EXTEND_START + 0x13), + MGR_EVT_PD_PFA_ERROR_CLEAR = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_EXTEND_LOCAL, PS3_EVT_CLASS_WARNING, + MGR_EVT_PD_ATTR_EXTEND_START + 0x14), + MGR_EVT_PD_SANITIZE_START = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_EXTEND_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_EXTEND_START + 0x15), + MGR_EVT_PD_FORMAT_START = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_EXTEND_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_EXTEND_START + 0x16), + MGR_EVT_PD_SANITIZE_PROGRESS = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_EXTEND_LOCAL, PS3_EVT_CLASS_PROCESS, + MGR_EVT_PD_ATTR_EXTEND_START + 0x17), + MGR_EVT_PD_SANITIZE_DONE = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_EXTEND_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_EXTEND_START + 0x18), + MGR_EVT_PD_FORMAT_DONE = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_EXTEND_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_EXTEND_START + 0x19), + MGR_EVT_PD_DPF_ERROR = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_EXTEND_LOCAL, PS3_EVT_CLASS_WARNING, + MGR_EVT_PD_ATTR_EXTEND_START + 0x1a), + MGR_EVT_PD_MARKED_SHIELD = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_EXTEND_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_EXTEND_START + 0x1b), + MGR_EVT_PD_BBM_CORRECTED = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_EXTEND_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_EXTEND_START + 0x1c), + MGR_EVT_PD_BBM_UNRECOVERABLE = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_EXTEND_LOCAL, PS3_EVT_CLASS_FATAL, + MGR_EVT_PD_ATTR_EXTEND_START + 0x1d), + MGR_EVT_PD_BBM_PUNCTURING = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_EXTEND_LOCAL, PS3_EVT_CLASS_FATAL, + MGR_EVT_PD_ATTR_EXTEND_START + 0x1e), + MGR_EVT_PD_BBM_REASSIGN_WR_ERROR = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_EXTEND_LOCAL, PS3_EVT_CLASS_FATAL, + MGR_EVT_PD_ATTR_EXTEND_START + 0x1f), + MGR_EVT_PD_UNABLE_ACCESS = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_EXTEND_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_EXTEND_START + 0x20), + MGR_EVT_MULITPILE_PD_UNABLE_ACCESS = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_TRUE, + PS3_EVT_PD_ATTR_EXTEND_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_EXTEND_START + 0x21), + MGR_EVT_PD_INSERT_FAIL = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_EXTEND_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_EXTEND_START + 0x22), + MGR_EVT_PHY_SATA_D2H_FAIL = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_EXTEND_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_EXTEND_START + 0x23), + MGR_EVT_PD_NOT_INSERTED = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_EXTEND_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_EXTEND_START + 0x24), + MGR_EVT_PD_NOT_SUPPORT = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_EXTEND_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_EXTEND_START + 0x25), + MGR_EVT_MULITPILE_PD_NOT_SUPPORT = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_TRUE, + PS3_EVT_PD_ATTR_EXTEND_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_EXTEND_START + 0x26), + MGR_EVT_NVDATA_INVALID = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_EXTEND_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_EXTEND_START + 0x27), + MGR_EVT_PD_RBLD_NON_UNMAP_PD = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_EXTEND_LOCAL, PS3_EVT_CLASS_CRITICAL, + MGR_EVT_PD_ATTR_EXTEND_START + 0x28), + MGR_EVT_PD_MOVEBACK_NON_UNMAP_PD = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_EXTEND_LOCAL, PS3_EVT_CLASS_CRITICAL, + MGR_EVT_PD_ATTR_EXTEND_START + 0x29), + MGR_EVT_PD_PDM_NON_UNMAP_PD = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_EXTEND_LOCAL, PS3_EVT_CLASS_CRITICAL, + MGR_EVT_PD_ATTR_EXTEND_START + 0x2a), + MGR_EVT_PD_RBLD_FAILED = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_EXTEND_LOCAL, PS3_EVT_CLASS_CRITICAL, + MGR_EVT_PD_ATTR_EXTEND_START + 0x2b), + MGR_EVT_PD_SANITIZE_FAILED = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_EXTEND_LOCAL, PS3_EVT_CLASS_CRITICAL, + MGR_EVT_PD_ATTR_EXTEND_START + 0x2c), + MGR_EVT_PD_SPIN_FAIL = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_EXTEND_LOCAL, PS3_EVT_CLASS_WARNING, + MGR_EVT_PD_ATTR_EXTEND_START + 0x2d), + MGR_EVT_PD_RBLD_MEDIA_MIX_NOT_SUPPORT = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_EXTEND_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_EXTEND_START + 0x2e), + MGR_EVT_PD_RBLD_DRIVE_MIX_NOT_SUPPORT = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_EXTEND_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_EXTEND_START + 0x2f), + MGR_EVT_PD_PDM_MEDIA_MIX_NOT_SUPPORT = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_EXTEND_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_EXTEND_START + 0x30), + MGR_EVT_PD_PDM_DRIVE_MIX_NOT_SUPPORT = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_EXTEND_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_EXTEND_START + 0x31), + MGR_EVT_PD_MOVEBACK_MEDIA_MIX_NOT_SUPPORT = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_EXTEND_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_EXTEND_START + 0x32), + MGR_EVT_PD_MOVEBACK_DRIVE_MIX_NOT_SUPPORT = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_EXTEND_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_EXTEND_START + 0x33), + MGR_EVT_NFR_FAILED = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_EXTEND_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_EXTEND_START + 0x34), + MGR_EVT_PD_RBLD_SMALL_SIZE = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_EXTEND_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_EXTEND_START + 0x35), + MGR_EVT_PD_MOVEBACK_SMALL_SIZE = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_EXTEND_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_EXTEND_START + 0x36), + MGR_EVT_NEGO_LOWER_LINK_SPEED = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_EXTEND_LOCAL, PS3_EVT_CLASS_WARNING, + MGR_EVT_PD_ATTR_EXTEND_START + 0x37), + MGR_EVT_PD_NVME_CMD_ERR = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_EXTEND_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_EXTEND_START + 0x38), + MGR_EVT_PD_NVME_CMD_TO = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_EXTEND_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_EXTEND_START + 0x39), + MGR_EVT_PD_NVME_INSERT_FAIL = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_EXTEND_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_EXTEND_START + 0x3a), + MGR_EVT_PCIE_LINK_NEGO_ABNORMAL = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_EXTEND_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_EXTEND_START + 0x3b), + MGR_EVT_PD_ISOLATION = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_EXTEND_LOCAL, PS3_EVT_CLASS_WARNING, + MGR_EVT_PD_ATTR_EXTEND_START + 0x3c), + MGR_EVT_PD_REPAIR_FAILED = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_EXTEND_LOCAL, PS3_EVT_CLASS_WARNING, + MGR_EVT_PD_ATTR_EXTEND_START + 0x3d), + MGR_EVT_PD_PCIE_LINK_UP_WITHOUT_I2C = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_EXTEND_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_EXTEND_START + 0x3e), + MGR_EVT_PD_PCIE_ENUMERATE_FAILED = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_EXTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_EXTEND_LOCAL, PS3_EVT_CLASS_WARNING, + MGR_EVT_PD_ATTR_EXTEND_START + 0x3f), + MGR_EVT_PD_MISS_LIGHTING = PS3_MK_EVT(PS3_EVT_TYPE_RAIDHBA, + PS3_EVT_EXP_INTERNAL, PS3_EVT_IS_BATCH_FALSE, + PS3_EVT_PD_ATTR_EXTEND_LOCAL, PS3_EVT_CLASS_INFO, + MGR_EVT_PD_ATTR_EXTEND_START + 0x40), + MGR_EVT_PD_ATTR_EXTEND_END = (MGR_EVT_PD_ATTR_EXTEND_START+0x41), +}; + +static inline unsigned int ps3EvtCodeExtendToNormal(unsigned int evtcode) +{ + unsigned int event = 0; + + switch (evtcode) { + case PS3_EVT_PD_ATTR_EXTEND_LOCAL: + event = PS3_EVT_PD_ATTR_LOCAL; + break; + default: + event = evtcode; + break; + } + return event; +} + +#endif diff --git a/drivers/scsi/linkdata/ps3stor/include/ps3_htp_nvme_spec.h b/drivers/scsi/linkdata/ps3stor/include/ps3_htp_nvme_spec.h new file mode 100644 index 0000000000000000000000000000000000000000..909606c31a48138b3b97c952556ea85e817387be --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/include/ps3_htp_nvme_spec.h @@ -0,0 +1,189 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __PS3_HTP_NVME_SPEC_H__ +#define __PS3_HTP_NVME_SPEC_H__ + + +enum NvmeGenCmdSC { + NVME_SC_SUCCESS = 0x00, + NVME_SC_INVALID_OPCODE = 0x01, + NVME_SC_INVALID_FIELD = 0x02, + NVME_SC_COMMAND_ID_CONFLICT = 0x03, + NVME_SC_DATA_TRANSFER_ERROR = 0x04, + NVME_SC_ABORTED_POWER_LOSS = 0x05, + NVME_SC_INTERNAL_DEVICE_ERROR = 0x06, + NVME_SC_ABORTED_BY_REQUEST = 0x07, + NVME_SC_ABORTED_SQ_DELETION = 0x08, + NVME_SC_ABORTED_FAILED_FUSED = 0x09, + NVME_SC_ABORTED_MISSING_FUSED = 0x0a, + NVME_SC_INVALID_NAMESPACE_OR_FORMAT = 0x0b, + NVME_SC_COMMAND_SEQUENCE_ERROR = 0x0c, + NVME_SC_INVALID_SGL_SEG_DESCRIPTOR = 0x0d, + NVME_SC_INVALID_NUM_SGL_DESCIRPTORS = 0x0e, + NVME_SC_DATA_SGL_LENGTH_INVALID = 0x0f, + NVME_SC_METADATA_SGL_LENGTH_INVALID = 0x10, + NVME_SC_SGL_DESCRIPTOR_TYPE_INVALID = 0x11, + NVME_SC_INVALID_CONTROLLER_MEM_BUF = 0x12, + NVME_SC_INVALID_PRP_OFFSET = 0x13, + NVME_SC_ATOMIC_WRITE_UNIT_EXCEEDED = 0x14, + NVME_SC_OPERATION_DENIED = 0x15, + NVME_SC_INVALID_SGL_OFFSET = 0x16, + + NVME_SC_HOSTID_INCONSISTENT_FORMAT = 0x18, + NVME_SC_KEEP_ALIVE_EXPIRED = 0x19, + NVME_SC_KEEP_ALIVE_INVALID = 0x1a, + NVME_SC_ABORTED_PREEMPT = 0x1b, + NVME_SC_SANITIZE_FAILED = 0x1c, + NVME_SC_SANITIZE_IN_PROGRESS = 0x1d, + NVME_SC_SGL_DATA_BLOCK_GRANULARITY_INVALID = 0x1e, + NVME_SC_COMMAND_INVALID_IN_CMB = 0x1f, + NVME_SC_NAMESPACE_IS_WRITE_PROTECTED = 0x20, + NVME_SC_COMMAND_INTERRUPTED = 0x21, + NVME_SC_TRANSIENT_TRANSPORT_ERROR = 0x22, + + NVME_SC_LBA_OUT_OF_RANGE = 0x80, + NVME_SC_CAPACITY_EXCEEDED = 0x81, + NVME_SC_NAMESPACE_NOT_READY = 0x82, + NVME_SC_RESERVATION_CONFLICT = 0x83, + NVME_SC_FORMAT_IN_PROGRESS = 0x84, +}; + + +enum NvmeCmdSpecSC { + NVME_CSC_COMPLETION_QUEUE_INVALID = 0x00, + NVME_CSC_INVALID_QUEUE_IDENTIFIER = 0x01, + NVME_CSC_MAXIMUM_QUEUE_SIZE_EXCEEDED = 0x02, + NVME_CSC_ABORT_COMMAND_LIMIT_EXCEEDED = 0x03, + + NVME_CSC_ASYNC_EVENT_REQUEST_LIMIT_EXCEEDED = 0x05, + NVME_CSC_INVALID_FIRMWARE_SLOT = 0x06, + NVME_CSC_INVALID_FIRMWARE_IMAGE = 0x07, + NVME_CSC_INVALID_INTERRUPT_VECTOR = 0x08, + NVME_CSC_INVALID_LOG_PAGE = 0x09, + NVME_CSC_INVALID_FORMAT = 0x0a, + NVME_CSC_FIRMWARE_REQ_CONVENTIONAL_RESET = 0x0b, + NVME_CSC_INVALID_QUEUE_DELETION = 0x0c, + NVME_CSC_FEATURE_ID_NOT_SAVEABLE = 0x0d, + NVME_CSC_FEATURE_NOT_CHANGEABLE = 0x0e, + NVME_CSC_FEATURE_NOT_NAMESPACE_SPECIFIC = 0x0f, + NVME_CSC_FIRMWARE_REQ_NVM_RESET = 0x10, + NVME_CSC_FIRMWARE_REQ_RESET = 0x11, + NVME_CSC_FIRMWARE_REQ_MAX_TIME_VIOLATION = 0x12, + NVME_CSC_FIRMWARE_ACTIVATION_PROHIBITED = 0x13, + NVME_CSC_OVERLAPPING_RANGE = 0x14, + NVME_CSC_NAMESPACE_INSUFFICIENT_CAPACITY = 0x15, + NVME_CSC_NAMESPACE_ID_UNAVAILABLE = 0x16, + + NVME_CSC_NAMESPACE_ALREADY_ATTACHED = 0x18, + NVME_CSC_NAMESPACE_IS_PRIVATE = 0x19, + NVME_CSC_NAMESPACE_NOT_ATTACHED = 0x1a, + NVME_CSC_THINPROVISIONING_NOT_SUPPORTED = 0x1b, + NVME_CSC_CONTROLLER_LIST_INVALID = 0x1c, + NVME_CSC_DEVICE_SELF_TEST_IN_PROGRESS = 0x1d, + NVME_CSC_BOOT_PARTITION_WRITE_PROHIBITED = 0x1e, + NVME_CSC_INVALID_CTRLR_ID = 0x1f, + NVME_CSC_INVALID_SECONDARY_CTRLR_STATE = 0x20, + NVME_CSC_INVALID_NUM_CTRLR_RESOURCES = 0x21, + NVME_CSC_INVALID_RESOURCE_ID = 0x22, + + NVME_CSC_CONFLICTING_ATTRIBUTES = 0x80, + NVME_CSC_INVALID_PROTECTION_INFO = 0x81, + NVME_CSC_ATTEMPTED_WRITE_TO_RO_RANGE = 0x82, +}; + + +enum NvmeMediaErrSC { + NVME_MSC_WRITE_FAULTS = 0x80, + NVME_MSC_UNRECOVERED_READ_ERROR = 0x81, + NVME_MSC_GUARD_CHECK_ERROR = 0x82, + NVME_MSC_APPLICATION_TAG_CHECK_ERROR = 0x83, + NVME_MSC_REFERENCE_TAG_CHECK_ERROR = 0x84, + NVME_MSC_COMPARE_FAILURE = 0x85, + NVME_MSC_ACCESS_DENIED = 0x86, + NVME_MSC_DEALLOCATED_OR_UNWRITTEN_BLOCK = 0x87, +}; + +enum NvmeStatusCodes { + NVME_SUCCESS = 0x0000, + NVME_INVALID_OPCODE = 0x0001, + NVME_INVALID_FIELD = 0x0002, + NVME_CID_CONFLICT = 0x0003, + NVME_DATA_TRAS_ERROR = 0x0004, + NVME_POWER_LOSS_ABORT = 0x0005, + NVME_INTERNAL_DEV_ERROR = 0x0006, + NVME_CMD_ABORT_REQ = 0x0007, + NVME_CMD_ABORT_SQ_DEL = 0x0008, + NVME_CMD_ABORT_FAILED_FUSE = 0x0009, + NVME_CMD_ABORT_MISSING_FUSE = 0x000a, + NVME_INVALID_NSID = 0x000b, + NVME_CMD_SEQ_ERROR = 0x000c, + NVME_INVALID_SGL_SEG_DESCR = 0x000d, + NVME_INVALID_NUM_SGL_DESCRS = 0x000e, + NVME_DATA_SGL_LEN_INVALID = 0x000f, + NVME_MD_SGL_LEN_INVALID = 0x0010, + NVME_SGL_DESCR_TYPE_INVALID = 0x0011, + NVME_INVALID_USE_OF_CMB = 0x0012, + NVME_INVALID_PRP_OFFSET = 0x0013, + NVME_CMD_SET_CMB_REJECTED = 0x002b, + NVME_INVALID_CMD_SET = 0x002c, + NVME_LBA_RANGE = 0x0080, + NVME_CAP_EXCEEDED = 0x0081, + NVME_NS_NOT_READY = 0x0082, + NVME_NS_RESV_CONFLICT = 0x0083, + NVME_FORMAT_IN_PROGRESS = 0x0084, + NVME_INVALID_CQID = 0x0100, + NVME_INVALID_QID = 0x0101, + NVME_MAX_QSIZE_EXCEEDED = 0x0102, + NVME_ACL_EXCEEDED = 0x0103, + NVME_RESERVED = 0x0104, + NVME_AER_LIMIT_EXCEEDED = 0x0105, + NVME_INVALID_FW_SLOT = 0x0106, + NVME_INVALID_FW_IMAGE = 0x0107, + NVME_INVALID_IRQ_VECTOR = 0x0108, + NVME_INVALID_LOG_ID = 0x0109, + NVME_INVALID_FORMAT = 0x010a, + NVME_FW_REQ_RESET = 0x010b, + NVME_INVALID_QUEUE_DEL = 0x010c, + NVME_FID_NOT_SAVEABLE = 0x010d, + NVME_FEAT_NOT_CHANGEABLE = 0x010e, + NVME_FEAT_NOT_NS_SPEC = 0x010f, + NVME_FW_REQ_SUSYSTEM_RESET = 0x0110, + NVME_NS_ALREADY_ATTACHED = 0x0118, + NVME_NS_PRIVATE = 0x0119, + NVME_NS_NOT_ATTACHED = 0x011A, + NVME_NS_CTRL_LIST_INVALID = 0x011C, + NVME_CONFLICTING_ATTRS = 0x0180, + NVME_INVALID_PROT_INFO = 0x0181, + NVME_WRITE_TO_RO = 0x0182, + NVME_CMD_SIZE_LIMIT = 0x0183, + NVME_ZONE_BOUNDARY_ERROR = 0x01b8, + NVME_ZONE_FULL = 0x01b9, + NVME_ZONE_READ_ONLY = 0x01ba, + NVME_ZONE_OFFLINE = 0x01bb, + NVME_ZONE_INVALID_WRITE = 0x01bc, + NVME_ZONE_TOO_MANY_ACTIVE = 0x01bd, + NVME_ZONE_TOO_MANY_OPEN = 0x01be, + NVME_ZONE_INVAL_TRANSITION = 0x01bf, + NVME_WRITE_FAULT = 0x0280, + NVME_UNRECOVERED_READ = 0x0281, + NVME_E2E_GUARD_ERROR = 0x0282, + NVME_E2E_APP_ERROR = 0x0283, + NVME_E2E_REF_ERROR = 0x0284, + NVME_CMP_FAILURE = 0x0285, + NVME_ACCESS_DENIED = 0x0286, + NVME_DULB = 0x0287, + NVME_MORE = 0x2000, + NVME_DNR = 0x4000, + NVME_NO_COMPLETE = 0xffff, +}; + + +enum NvmeStatusCodeType { + NVME_SCT_GENERIC = 0x0, + NVME_SCT_COMMAND_SPECIFIC = 0x1, + NVME_SCT_MEDIA_ERROR = 0x2, + NVME_SCT_PATH = 0x3, + + NVME_SCT_VENDOR_SPECIFIC = 0x7, +}; + +#endif diff --git a/drivers/scsi/linkdata/ps3stor/include/ps3_htp_register_fifo.h b/drivers/scsi/linkdata/ps3stor/include/ps3_htp_register_fifo.h new file mode 100644 index 0000000000000000000000000000000000000000..f5bc5dfc17baf271e4176a770a4a0dc2de092bfb --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/include/ps3_htp_register_fifo.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __PS3_HTP_REGISTER_FIFO_H__ +#define __PS3_HTP_REGISTER_FIFO_H__ + +#include "hwapi/include_v200/s1861_regs/s1861_global_baseaddr.h" +#include "hwapi/include_v200/s1861_regs/s1861_hil_reg0_ps3_request_queue_reg.h" +#include "hwapi/include_v200/s1861_regs/s1861_hil_reg0_ps3_register_f_reg.h" +#include "hwapi/include_v200/s1861_regs/s1861_hil_reg0_ps3_register_s_reg.h" + +#ifdef __cplusplus +extern "C" { +#endif + +union ps3RequestFifo { + unsigned char reserved0[HIL_REG0_PS3_REQUEST_QUEUE_SIZE]; + struct HilReg0Ps3RequestQueue request_fifo; +}; + +union ps3RegShare { + unsigned char reserved0[HIL_REG0_PS3_REGISTER_S_SIZE]; + struct HilReg0Ps3RegisterS share_reg; +}; + +union ps3RegExclusive { + unsigned char reserved0[HIL_REG0_PS3_REGISTER_F_SIZE]; + struct HilReg0Ps3RegisterF Excl_reg; +}; + +struct Ps3Fifo { + union ps3RegExclusive reg_f; + union ps3RequestFifo cmd_fifo; + union ps3RegShare reg_s; +}; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/drivers/scsi/linkdata/ps3stor/include/ps3_htp_trace_id.h b/drivers/scsi/linkdata/ps3stor/include/ps3_htp_trace_id.h new file mode 100644 index 0000000000000000000000000000000000000000..2520a93bc6b35b4fc8d24ec3b97d2ff662e74e5d --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/include/ps3_htp_trace_id.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __PS3_HTP_TRACE_ID_H__ +#define __PS3_HTP_TRACE_ID_H__ + +#define TRACE_ID_CHIP_OUT_COUNT_MASK 0x000FFFFFFFFFFFFFLLU + +#define TRACE_ID_CHIP_OUT_CPUID_SHIFT 52 +#define TRACE_ID_CHIP_OUT_CPUID_MASK 0x7FFLLU + +static inline void traceIdCpuIdSet(unsigned long long *traceId, + unsigned short cpuId) +{ + *traceId &= ~(TRACE_ID_CHIP_OUT_CPUID_MASK + << TRACE_ID_CHIP_OUT_CPUID_SHIFT); + *traceId |= ((unsigned long long)cpuId & TRACE_ID_CHIP_OUT_CPUID_MASK) + << TRACE_ID_CHIP_OUT_CPUID_SHIFT; +} + +#endif diff --git a/drivers/scsi/linkdata/ps3stor/include/ps3_mgr_evt_common.h b/drivers/scsi/linkdata/ps3stor/include/ps3_mgr_evt_common.h new file mode 100644 index 0000000000000000000000000000000000000000..b2e151a956ac865a87bab0fe2e98fd68e252d65a --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/include/ps3_mgr_evt_common.h @@ -0,0 +1,85 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __PS3_MGR_EVT_COMMON_H__ +#define __PS3_MGR_EVT_COMMON_H__ + +#define MGR_EVT_TYPE_OFFSET (80) +#define MGR_EVT_LOG_INFO_MAX_SIZE (116) + +#if !(defined(PS3_PRODUCT_EXPANDER) || defined(PS3_PRODUCT_SWITCH)) +#define MGR_EVT_TYPE_EXTEND_OFFSET (200) +#define MGR_EVT_EXTEND_TYPE_START (17) + + +#define MGR_EVT_TYPE_BASE_LOCAL(sn) (sn < MGR_EVT_EXTEND_TYPE_START ? \ + (sn - 1) * MGR_EVT_TYPE_OFFSET : \ + MGR_EVT_TYPE_OFFSET * (MGR_EVT_EXTEND_TYPE_START - 1) \ + + MGR_EVT_TYPE_EXTEND_OFFSET * (sn - MGR_EVT_EXTEND_TYPE_START)) + + + +#define MGR_EVENT_CODE_2_TYPE(code) \ + (code <= MGR_EVT_TYPE_OFFSET * (MGR_EVT_EXTEND_TYPE_START - 1) ? \ + ((code) / MGR_EVT_TYPE_OFFSET + 1) : \ + (code - MGR_EVT_TYPE_OFFSET * (MGR_EVT_EXTEND_TYPE_START - 1)) / \ + MGR_EVT_TYPE_EXTEND_OFFSET + MGR_EVT_EXTEND_TYPE_START) + +#else +#define MGR_EVT_TYPE_BASE_LOCAL(sn) ((sn - 1) * MGR_EVT_TYPE_OFFSET) +#define MGR_EVENT_CODE_2_TYPE(code) ((code) / MGR_EVT_TYPE_OFFSET + 1) +#endif + +#define PS3_EVT_TYPE_OFFSET (30) +#define PS3_EVT_EXPOSE_OFFSET (28) +#define PS3_EVT_BATCH_OFFSET (27) +#define PS3_EVT_ATTR_OFFSET (16) +#define PS3_EVT_LEVEL_OFFSET (12) +#define PS3_EVT_CODE_OFFSET (0) + +#define PS3_MAKE_EVT_CODE(type, expose, batch, attr, level, code) \ + ((((type)&0b11) << PS3_EVT_TYPE_OFFSET) | \ + (((expose)&0b11) << PS3_EVT_EXPOSE_OFFSET) | \ + (((batch)&0b1) << PS3_EVT_BATCH_OFFSET) | \ + (((attr)&0x3F) << PS3_EVT_ATTR_OFFSET) | \ + (((level)&0xF) << PS3_EVT_LEVEL_OFFSET) | \ + ((code)&0xFFF) << PS3_EVT_CODE_OFFSET) +#define PS3_MK_EVT(type, expose, batch, attr, level, code) \ + (PS3_MAKE_EVT_CODE(type, expose, batch, attr, level, code)) + +#define PS3_EVT_TYPE(evtcode) ((evtcode >> PS3_EVT_TYPE_OFFSET) & 0b11) +#define PS3_EVT_EXPOSE(evtcode) ((evtcode >> PS3_EVT_EXPOSE_OFFSET) & 0b11) +#define PS3_EVT_IS_BATCH(evtcode) ((evtcode >> PS3_EVT_BATCH_OFFSET) & 0b1) +#define PS3_EVT_ATTR_EXTEND(evtcode) ((U8)((evtcode >> PS3_EVT_ATTR_OFFSET) & 0x3F)) +#define PS3_EVT_LEVEL(evtcode) ((evtcode >> PS3_EVT_LEVEL_OFFSET) & 0xF) +#define PS3_EVT_CODE(evtcode) (((unsigned int)evtcode >> PS3_EVT_CODE_OFFSET) & 0xFFF) + +enum Ps3EventType { + PS3_EVT_TYPE_UNKNOWN = 0b0000, + PS3_EVT_TYPE_RAIDHBA = 0b0001, + PS3_EVT_TYPE_EXPANDER = 0b0010, + PS3_EVT_TYPE_PCIESWITCH = 0b0011 +}; + +enum Ps3EventExpose { + PS3_EVT_EXP_UNKNOWN = 0b0000, + PS3_EVT_EXP_EXTERNAL = 0b0001, + PS3_EVT_EXP_INTERNAL = 0b0010, + PS3_EVT_EXP_MAX = 0b0011 +}; + +enum MgrEventLevel { + PS3_EVT_CLASS_UNKNOWN = 0b0000, + PS3_EVT_CLASS_DEBUG = 0b0011, + PS3_EVT_CLASS_PROCESS = 0b0101, + PS3_EVT_CLASS_INFO = 0b0001, + PS3_EVT_CLASS_WARNING = 0b0010, + PS3_EVT_CLASS_CRITICAL = 0b0100, + PS3_EVT_CLASS_FATAL = 0b1000, + PS3_EVT_CLASS_MAX +}; + +enum MgrEventIsBatch { + PS3_EVT_IS_BATCH_FALSE = 0b0, + PS3_EVT_IS_BATCH_TRUE = 0b1, +}; + +#endif diff --git a/drivers/scsi/linkdata/ps3stor/linux/ps3_base.c b/drivers/scsi/linkdata/ps3stor/linux/ps3_base.c new file mode 100644 index 0000000000000000000000000000000000000000..9431726c34e651e935fd10a787a1b1a12d1f58c0 --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/linux/ps3_base.c @@ -0,0 +1,1880 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ps3_htp_def.h" +#include "ps3_htp_event.h" +#include "ps3_htp_ioctl.h" +#include "ps3_cli.h" +#include "ps3_instance_manager.h" +#include "ps3_ioc_manager.h" +#include "ps3_ioc_state.h" +#include "ps3_scsih.h" +#include "ps3_driver_log.h" +#include "ps3_cmd_channel.h" +#include "ps3_mgr_cmd.h" +#include "ps3_event.h" +#include "ps3_mgr_cmd_err.h" +#include "ps3_scsi_cmd_err.h" +#include "ps3_device_manager.h" +#include "ps3_cmd_complete.h" +#include "ps3_device_update.h" +#include "ps3_debug.h" +#include "ps3_ioctl.h" +#include "ps3_driver_log.h" +#include "ps3_inner_data.h" +#include "ps3_cmd_statistics.h" +#include "ps3_trace_id_alloc.h" +#include "ps3_module_para.h" +#include "ps3_scsi_cmd_err.h" +#include "ps3_drv_ver.h" +#include "ps3_pcie_err_handle.h" +#include "ps3_r1x_write_lock.h" +#include "ps3_watchdog.h" +#if defined PS3_HARDWARE_ASIC +#include "ps3_pci.h" +#endif +#include "ps3_cli_debug.h" +#include "ps3_recovery.h" +#include "ps3_kernel_version.h" +const char *const PS3_CHRDEV_NAME = "ps3stor-ioctl"; +#define PS3_PCI_DRIVER_NAME "ps3stor" +#define PS3_SCSI_HOST_NAME "ps3stor_scsi_host" +#define PS3_SCSI_DEFAULT_INIT_ID (-1) +#define PS3_SCSI_DEFAULT_CMD_PER_LUN (256) + +#define PS3_BIOS_PARAM_DEFAULT_HEADER (64) +#define PS3_BIOS_PARAM_DEFAULT_SECTORS (32) +#define PS3_BIOS_PARAM_MAX_HEADER (255) +#define PS3_BIOS_PARAM_MAX_SECTORS (63) +#define PS3_BIOS_PARAM_DEV_CAPCITY_THRESHOLD_1G (0x200000) + +#define IS_DMA64 (sizeof(dma_addr_t) == 8) +#define PS3_HOST_UUIID(_dev) (_dev->bus->number << 8 | _dev->devfn) +static const unsigned char DMA_MASK_BIT_44 = PCIE_DMA_HOST_ADDR_BIT_POS; +static const unsigned char DMA_MASK_BIT_52 = PCIE_DMA_HOST_ADDR_BIT_POS_VALID; +static const unsigned char DMA_MASK_BIT_32 = 32; +static const unsigned int PS3_REGISTER_BAR_INDEX = 0x02; + +static unsigned int ps3_mgmt_chrdev_major_no; + +#ifdef __KERNEL__ +#define PCI_DEVICE_SET(vend, dev, subven, subdev, cls, cls_mask) \ + .vendor = (vend), .device = (dev), .subvendor = (subven), \ + .subdevice = (subdev), .class = (cls), .class_mask = (cls_mask) +#else +#define PCI_DEVICE_SET(vend, dev, subven, subdev, class, class_mask) \ + .vendor = (vend), .device = (dev), .subvendor = PCI_ANY_ID, \ + .subdevice = PCI_ANY_ID +#endif + +static struct pci_device_id ps3_pci_table[] = { + { PCI_DEVICE(PCI_VENDOR_ID_PS3_FPGA, PCI_DEVICE_ID_PS3_RAID_FPGA) }, + { PCI_DEVICE(PCI_VENDOR_ID_PS3_FPGA, PCI_DEVICE_ID_PS3_HBA) }, + { PCI_DEVICE(PCI_VENDOR_ID_PS3_ASIC, PCI_DEVICE_ID_PS3_RAID_FPGA) }, + { PCI_DEVICE(PCI_VENDOR_ID_PS3_ASIC, PCI_DEVICE_ID_PS3_HBA) }, + { PCI_DEVICE(PCI_VENDOR_ID_STARS, PCI_DEVICE_ID_STARS_IOC_2020_18i) }, + { PCI_DEVICE(PCI_VENDOR_ID_STARS, PCI_DEVICE_ID_STARS_ROC_2020_10i) }, + { PCI_DEVICE(PCI_VENDOR_ID_PS3, PCI_DEVICE_ID_STARS_IOC_2020_18i) }, + { PCI_DEVICE(PCI_VENDOR_ID_PS3, PCI_DEVICE_ID_STARS_ROC_2020_10i) }, + { PCI_DEVICE(PCI_VENDOR_ID_PS3, PCI_DEVICE_ID_STARS_IOC_2213_16i) }, + { 0, 0, 0, 0 } +}; + +MODULE_DEVICE_TABLE(pci, ps3_pci_table); + +static int ps3_mgmt_open(struct inode *pinode, struct file *filep) +{ + (void)pinode; + (void)filep; + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + + return PS3_SUCCESS; +} + +static int ps3_bios_param(struct scsi_device *sdev, struct block_device *bdev, + sector_t capacity, int params[]) +{ + int heads = PS3_BIOS_PARAM_DEFAULT_HEADER; + int sectors = PS3_BIOS_PARAM_DEFAULT_SECTORS; + sector_t cylinders = capacity; + unsigned long dummy = heads * sectors; + (void)sdev; + (void)bdev; + + sector_div(cylinders, dummy); + + if ((unsigned long)capacity >= PS3_BIOS_PARAM_DEV_CAPCITY_THRESHOLD_1G) { + heads = PS3_BIOS_PARAM_MAX_HEADER; + sectors = PS3_BIOS_PARAM_MAX_SECTORS; + dummy = heads * sectors; + cylinders = capacity; + sector_div(cylinders, dummy); + } + + params[0] = heads; + params[1] = sectors; + params[2] = cylinders; + + return 0; +} + +#if defined(PS3_TAGSET_SUPPORT) +static MAP_QUEUES_RET_TYPE ps3_map_queues(struct Scsi_Host *shost) +{ + struct ps3_instance *instance = NULL; + int qoff = 0; + int offset = 0; + struct blk_mq_queue_map *map = NULL; + + instance = (struct ps3_instance *)shost->hostdata; + + if (shost->nr_hw_queues == 1) + goto l_out; + + offset = instance->irq_context.high_iops_msix_vectors; + qoff = 0; + + map = &shost->tag_set.map[HCTX_TYPE_DEFAULT]; + map->nr_queues = instance->irq_context.valid_msix_vector_count - offset; + map->queue_offset = 0; +#if defined(PS3_MAP_QUEUES_RET) + (void)qoff; + return MAP_QUEUES_RET_VAL(blk_mq_pci_map_queues(map, instance->pdev, offset)); +#else + blk_mq_pci_map_queues(map, instance->pdev, offset); + qoff += map->nr_queues; + + map = &shost->tag_set.map[HCTX_TYPE_POLL]; + map->nr_queues = 0; + if (map->nr_queues) { + map->queue_offset = qoff; + blk_mq_map_queues(map); + } +#endif +l_out: + return MAP_QUEUES_RET_VAL(0); +} + +#endif + +static ssize_t page_size_show(struct device *cdev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret = 0; + + if (buf == NULL) { + ret = 0; + goto l_out; + } + (void)cdev; + (void)attr; + + ret = snprintf(buf, PAGE_SIZE, "%ld\n", (unsigned long)PAGE_SIZE - 1); + +l_out: + return ret; +} + +static inline ssize_t vd_io_outstanding_show(struct device *cdev, + struct device_attribute *attr, + char *buf) +{ + return ps3_vd_io_outstanding_show(cdev, attr, buf); +} + +static inline ssize_t io_outstanding_show(struct device *cdev, + struct device_attribute *attr, + char *buf) +{ + return ps3_io_outstanding_show(cdev, attr, buf); +} + +static inline ssize_t is_load_show(struct device *cdev, + struct device_attribute *attr, char *buf) +{ + return ps3_is_load_show(cdev, attr, buf); +} + +static inline ssize_t dump_ioc_regs_show(struct device *cdev, + struct device_attribute *attr, + char *buf) +{ + return ps3_dump_ioc_regs_show(cdev, attr, buf); +} + +static inline ssize_t max_scsi_cmds_show(struct device *cdev, + struct device_attribute *attr, + char *buf) +{ + return ps3_max_scsi_cmds_show(cdev, attr, buf); +} + +static inline ssize_t event_subscribe_info_show(struct device *cdev, + struct device_attribute *attr, + char *buf) +{ + return ps3_event_subscribe_info_show(cdev, attr, buf); +} + +static inline ssize_t ioc_state_show(struct device *cdev, + struct device_attribute *attr, char *buf) +{ + return ps3_ioc_state_show(cdev, attr, buf); +} + +static ssize_t log_level_store(struct device *cdev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + return ps3_log_level_store(cdev, attr, buf, count); +} + +static inline ssize_t log_level_show(struct device *cdev, + struct device_attribute *attr, char *buf) +{ + return ps3_log_level_show(cdev, attr, buf); +} + +static inline ssize_t io_trace_store(struct device *cdev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + return ps3_io_trace_switch_store(cdev, attr, buf, count); +} + +static inline ssize_t io_trace_show(struct device *cdev, + struct device_attribute *attr, char *buf) +{ + return ps3_io_trace_switch_show(cdev, attr, buf); +} + +static inline ssize_t dump_type_show(struct device *cdev, + struct device_attribute *attr, char *buf) +{ + return ps3_dump_type_show(cdev, attr, buf); +} + +static inline ssize_t dump_type_store(struct device *cdev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + return ps3_dump_type_store(cdev, attr, buf, count); +} + +static inline ssize_t dump_dir_show(struct device *cdev, + struct device_attribute *attr, char *buf) +{ + return ps3_dump_dir_show(cdev, attr, buf); +} + +static inline ssize_t dump_state_show(struct device *cdev, + struct device_attribute *attr, char *buf) +{ + return ps3_dump_state_show(cdev, attr, buf); +} + +static inline ssize_t dump_state_store(struct device *cdev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + return ps3_dump_state_store(cdev, attr, buf, count); +} + +static inline ssize_t soc_dead_reset_store(struct device *cdev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + return ps3_soc_dead_reset_store(cdev, attr, buf, count); +} +static inline ssize_t halt_support_cli_show(struct device *cdev, + struct device_attribute *attr, + char *buf) +{ + return ps3_halt_support_cli_show(cdev, attr, buf); +} + +static inline ssize_t halt_support_cli_store(struct device *cdev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + return ps3_halt_support_cli_store(cdev, attr, buf, count); +} + +static inline ssize_t qos_switch_show(struct device *cdev, + struct device_attribute *attr, char *buf) +{ + return ps3_qos_switch_show(cdev, attr, buf); +} + +static ssize_t qos_switch_store(struct device *cdev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + return ps3_qos_switch_store(cdev, attr, buf, count); +} +static inline ssize_t product_model_show(struct device *cdev, + struct device_attribute *attr, + char *buf) +{ + return ps3_product_model_show(cdev, attr, buf); +} + +#if defined(PS3_SUPPORT_DEBUG) || \ + (defined(PS3_CFG_RELEASE) && defined(PS3_CFG_OCM_DBGBUG)) || \ + (defined(PS3_CFG_RELEASE) && defined(PS3_CFG_OCM_RELEASE)) +#else + +static inline ssize_t irq_prk_support_show(struct device *cdev, + struct device_attribute *attr, + char *buf) +{ + return ps3_irq_prk_support_show(cdev, attr, buf); +} + +static inline ssize_t irq_prk_support_store(struct device *cdev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + return ps3_irq_prk_support_store(cdev, attr, buf, count); +} +#endif + +static inline ssize_t cli_ver_show(struct device *cdev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret = 0; + (void)cdev; + (void)attr; + if (buf != NULL) + ret = snprintf(buf, PAGE_SIZE, "%d\n", PS3_IOCTL_VERSION); + + return ret; +} + +static DEVICE_ATTR_RO(page_size); +static DEVICE_ATTR_RO(vd_io_outstanding); +static DEVICE_ATTR_RO(io_outstanding); +static DEVICE_ATTR_RO(is_load); +static DEVICE_ATTR_RO(dump_ioc_regs); +static DEVICE_ATTR_RO(max_scsi_cmds); +static DEVICE_ATTR_RO(event_subscribe_info); +static DEVICE_ATTR_RO(ioc_state); +static DEVICE_ATTR_RW(log_level); +static DEVICE_ATTR_RW(io_trace); +static DEVICE_ATTR_RO(dump_dir); +static DEVICE_ATTR_RW(dump_type); +static DEVICE_ATTR_RW(dump_state); +static DEVICE_ATTR_WO(soc_dead_reset); +static DEVICE_ATTR_RW(halt_support_cli); +static DEVICE_ATTR_RW(qos_switch); +static DEVICE_ATTR_RO(product_model); +static DEVICE_ATTR_RO(cli_ver); + +#if defined(PS3_SUPPORT_DEBUG) || \ + (defined(PS3_CFG_RELEASE) && defined(PS3_CFG_OCM_DBGBUG)) || \ + (defined(PS3_CFG_RELEASE) && defined(PS3_CFG_OCM_RELEASE)) +#else +static DEVICE_ATTR_RW(irq_prk_support); +#endif + +#if defined(PS3_HOST_ATTRS) +static struct attribute *ps3_host_attrs[] = { + &dev_attr_page_size.attr, + &dev_attr_vd_io_outstanding.attr, + &dev_attr_io_outstanding.attr, + &dev_attr_is_load.attr, + &dev_attr_dump_ioc_regs.attr, + &dev_attr_max_scsi_cmds.attr, + &dev_attr_event_subscribe_info.attr, + &dev_attr_ioc_state.attr, + &dev_attr_log_level.attr, + &dev_attr_io_trace.attr, + &dev_attr_dump_state.attr, + &dev_attr_dump_type.attr, + &dev_attr_dump_dir.attr, + &dev_attr_soc_dead_reset.attr, + &dev_attr_halt_support_cli.attr, + &dev_attr_qos_switch.attr, + &dev_attr_product_model.attr, + &dev_attr_cli_ver.attr, +#if defined(PS3_SUPPORT_DEBUG) || \ + (defined(PS3_CFG_RELEASE) && defined(PS3_CFG_OCM_DBGBUG)) || \ + (defined(PS3_CFG_RELEASE) && defined(PS3_CFG_OCM_RELEASE)) +#else + &dev_attr_irq_prk_support.attr, +#endif + NULL, +}; + +static const struct attribute_group ps3_host_attr_group = { + .attrs = ps3_host_attrs +}; + +const struct attribute_group *ps3_host_groups[] = { &ps3_host_attr_group, + NULL }; +#else +static struct device_attribute *ps3_host_attrs[] = { + &dev_attr_page_size, + &dev_attr_vd_io_outstanding, + &dev_attr_io_outstanding, + &dev_attr_is_load, + &dev_attr_dump_ioc_regs, + &dev_attr_max_scsi_cmds, + &dev_attr_event_subscribe_info, + &dev_attr_ioc_state, + &dev_attr_log_level, + &dev_attr_io_trace, + &dev_attr_dump_state, + &dev_attr_dump_type, + &dev_attr_dump_dir, + &dev_attr_soc_dead_reset, + &dev_attr_halt_support_cli, + &dev_attr_qos_switch, + &dev_attr_product_model, + &dev_attr_cli_ver, +#if defined(PS3_SUPPORT_DEBUG) || \ + (defined(PS3_CFG_RELEASE) && defined(PS3_CFG_OCM_DBGBUG)) || \ + (defined(PS3_CFG_RELEASE) && defined(PS3_CFG_OCM_RELEASE)) +#else + &dev_attr_irq_prk_support, +#endif + NULL, +}; +#endif +static struct scsi_host_template ps3_scsi_host_template = { + .module = THIS_MODULE, + .name = PS3_SCSI_HOST_NAME, + .proc_name = PS3_SCSI_HOST_PROC_NAME, + .queuecommand = ps3_scsih_queue_command, + .eh_abort_handler = ps3_err_scsi_task_mgr_abort, + .slave_alloc = ps3_scsi_slave_alloc, + .slave_configure = ps3_scsi_slave_configure, + .slave_destroy = ps3_scsi_slave_destroy, + .change_queue_depth = ps3_change_queue_depth, + .eh_device_reset_handler = ps3_device_reset_handler, + .eh_target_reset_handler = ps3_err_reset_target, + .eh_host_reset_handler = ps3_err_reset_host, + .eh_timed_out = ps3_err_reset_timer, + .bios_param = ps3_bios_param, +#if defined(PS3_TAGSET_SUPPORT) + .map_queues = ps3_map_queues, +#endif + +#if defined(PS3_TRACK_QUEUE_DEPTH) + .track_queue_depth = 1, +#endif + +#if defined(PS3_HOST_ATTRS) + .shost_groups = ps3_host_groups, +#else + .shost_attrs = ps3_host_attrs, +#endif +}; + +static inline void ps3_set_product_model(struct ps3_instance *instance, + const unsigned short id) +{ + if (PS3_DEVICE_IS_SWITCH(id)) + instance->product_model = PS3_PSW_PRODUCT_MODEL; + else + instance->product_model = PS3_PRODUCT_MODEL; +} + +static struct ps3_instance *ps3_instance_create(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + struct Scsi_Host *host = NULL; + struct ps3_instance *instance = NULL; + + if (pdev == NULL) { + LOG_ERROR("pci_dev is null!\n"); + return NULL; + } + + host = scsi_host_alloc(&ps3_scsi_host_template, + sizeof(struct ps3_instance)); + + if (!host) { + LOG_ERROR("pci_id[%u] scsi_host_alloc failed!\n", pdev->dev.id); + return NULL; + } + + host->unique_id = PS3_HOST_UUIID(pdev); + instance = (struct ps3_instance *)shost_priv(host); + memset(instance, 0, sizeof(*instance)); + + instance->pdev = pdev; + instance->host = host; + instance->peer_instance = NULL; + ps3_set_product_model(instance, id->device); + instance->dev_id = id->device; + instance->state_machine.can_hostreset = PS3_FALSE; + instance->pci_err_handle_state = PS3_DEVICE_ERR_STATE_CLEAN; + instance->is_pcie_err_detected = PS3_FALSE; + mb(); /* in order to force CPU ordering */ + + if (ps3_instance_add(instance) != PS3_SUCCESS) { + scsi_remove_host(host); + scsi_host_put(host); + instance = NULL; + } + + LOG_INFO("hno:%u scsi_host_alloc [unique_id:%d]!\n", PS3_HOST(instance), + host->unique_id); + return instance; +} + +static inline void ps3_instance_put(struct ps3_instance *instance) +{ + if (ps3_instance_remove(instance) != PS3_SUCCESS) + LOG_ERROR("ps3_instance remove NOK\n"); +} + +int ps3_pci_init(struct pci_dev *pdev, struct ps3_instance *instance) +{ + resource_size_t base_addr = 0; + int ret = PS3_SUCCESS; + + ret = pci_enable_device_mem(pdev); + if (ret != PS3_SUCCESS) { + LOG_ERROR("hno:%u pci id[%u] pci enable failed\n", + PS3_HOST(instance), pdev->dev.id); + goto l_pci_enable_device_mem_failed; + } + + pci_set_master(pdev); + + instance->reg_bar = PS3_REGISTER_BAR_INDEX; + ret = (pci_resource_flags(pdev, instance->reg_bar) & IORESOURCE_MEM); + if (!ret) { + LOG_ERROR("hno:%u Bar %lu isnot IORESOURCE_MEM\n", + PS3_HOST(instance), instance->reg_bar); + goto l_bar_check_failed; + } + ret = pci_request_selected_regions(pdev, 1 << instance->reg_bar, + "PS3 pci regions"); + if (ret != PS3_SUCCESS) { + LOG_ERROR("hno:%u IO memory region busy\n", PS3_HOST(instance)); + goto l_pci_request_selected_regions_failed; + } +#if defined(PS3_SUPPORT_PCIE_REPORT) + pci_enable_pcie_error_reporting(pdev); +#endif + if (instance->ioc_adpter->reg_set) { + instance->reg_set = + (struct Ps3Fifo __iomem *)instance->ioc_adpter->reg_set( + pdev, instance->reg_bar); + } else { + instance->reg_set = (struct Ps3Fifo __iomem *)ioremap( + pci_resource_start(pdev, instance->reg_bar), + PS3_REGISTER_SET_SIZE); + } + if (instance->reg_set == NULL) { + LOG_ERROR("hno:%u ioremap failed\n", PS3_HOST(instance)); + goto l_ioremap_failed; + } else { + pci_set_drvdata(pdev, instance); + } + + ps3_atomic_set(&instance->watchdog_reg_read_fail_count, 0); + LOG_INFO( + "reg_bar_idx = %lu, bar_base_paddr = 0x%llx, reg_set_vaddr = 0x%p\n", + instance->reg_bar, (unsigned long long)base_addr, instance->reg_set); + + return PS3_SUCCESS; +l_ioremap_failed: + pci_release_selected_regions(instance->pdev, 1 << instance->reg_bar); +#if defined(PS3_SUPPORT_PCIE_REPORT) + pci_disable_pcie_error_reporting(pdev); +#endif +l_pci_request_selected_regions_failed: + pci_disable_device(instance->pdev); +l_bar_check_failed: +l_pci_enable_device_mem_failed: + return -PS3_FAILED; +} + +static void ps3_pci_exit(struct ps3_instance *instance) +{ + if (instance->reg_set != NULL) { + iounmap(instance->reg_set); + instance->reg_set = NULL; + } + + if (pci_is_enabled(instance->pdev)) { + pci_release_selected_regions(instance->pdev, + 1 << instance->reg_bar); +#if defined(PS3_SUPPORT_PCIE_REPORT) + pci_disable_pcie_error_reporting(instance->pdev); +#endif + pci_disable_device(instance->pdev); + } +} + +static int ps3_scsi_init(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + struct Scsi_Host *host = instance->host; + + host->can_queue = instance->cmd_attr.cur_can_que; + host->this_id = PS3_SCSI_DEFAULT_INIT_ID; + host->sg_tablesize = instance->cmd_context.max_host_sge_count; + host->max_sectors = instance->ctrl_info.maxSectors; + host->cmd_per_lun = PS3_SCSI_DEFAULT_CMD_PER_LUN; + host->max_channel = instance->ctrl_info.channelInfo.channelNum - 1; + host->max_id = instance->dev_context.max_dev_per_channel; + host->max_lun = PS3_FRAME_LUN_BUFLEN; + host->max_cmd_len = PS3_FRAME_CDB_BUFLEN; +#if defined(PS3_SUPPORT_BIO_MERGE) + host->use_clustering = instance->use_clusting; +#endif + + if (instance->ioc_adpter->sas_transport_get != NULL) { + host->transportt = instance->ioc_adpter->sas_transport_get(); + host->transportt->user_scan = ps3_sas_user_scan; + } + +#if defined(PS3_TAGSET_SUPPORT) + host->host_tagset = 0; + host->nr_hw_queues = 1; + + if ((instance->irq_context.valid_msix_vector_count > + instance->irq_context.high_iops_msix_vectors) && + ps3_tagset_enable_query() && (instance->smp_affinity_enable)) { + host->host_tagset = 1; + host->nr_hw_queues = + instance->irq_context.valid_msix_vector_count - + instance->irq_context.high_iops_msix_vectors; + } +#endif + +#if defined(PS3_CHANGE_QUEUE_DEPTH) + + ret = scsi_init_shared_tag_map(host, host->can_queue); + if (ret) { + LOG_ERROR("hno:%u Failed to shared tag from\n", + PS3_HOST(instance)); + ret = -PS3_FAILED; + goto l_out; + } +#endif + if (scsi_add_host(instance->host, &instance->pdev->dev)) { + LOG_ERROR("hno:%u Failed to add host\n", PS3_HOST(instance)); + ret = -PS3_FAILED; + goto l_out; + } + + instance->is_host_added = PS3_TRUE; +l_out: + + return ret; +} + +static inline int __ps3_config_dma_mask32(struct ps3_instance *instance) +{ + if (dma_set_mask(&instance->pdev->dev, DMA_BIT_MASK(DMA_MASK_BIT_32)) || + dma_set_coherent_mask(&instance->pdev->dev, + DMA_BIT_MASK(DMA_MASK_BIT_32))) { + LOG_ERROR("hno:%u 32 dma mask set failed\n", + PS3_HOST(instance)); + return -PS3_FAILED; + } + instance->dma_mask = DMA_MASK_BIT_32; + LOG_INFO("hno:%u 32 dma mask set\n", PS3_HOST(instance)); + return PS3_SUCCESS; +} + +static inline unsigned char ps3_dma_mask_get(struct ps3_instance *instance) +{ + return ((instance->dma_addr_bit_pos > DMA_MASK_BIT_52) ? + DMA_MASK_BIT_52 : + DMA_MASK_BIT_44); +} + +static inline int __ps3_config_dma_mask(struct ps3_instance *instance) +{ + unsigned char dma_mask = 0; + + dma_mask = ps3_dma_mask_get(instance); + if (dma_set_mask(&instance->pdev->dev, DMA_BIT_MASK(dma_mask)) || + dma_set_coherent_mask(&instance->pdev->dev, + DMA_BIT_MASK(dma_mask))) { + LOG_ERROR("hno:%u 62 dma mask set failed\n", + PS3_HOST(instance)); + return __ps3_config_dma_mask32(instance); + } + instance->dma_mask = dma_mask; + LOG_INFO("hno:%u dma mask set %u\n", PS3_HOST(instance), instance->dma_mask); + return PS3_SUCCESS; +} + +static int ps3_config_dma_mask(struct ps3_instance *instance) +{ + int ret = -PS3_FAILED; + unsigned char is_dma64_support = PS3_FALSE; + + if (!ps3_ioc_mgr_is_dma64_support(instance, &is_dma64_support)) + goto l_out; + if (is_dma64_support) { + if (IS_DMA64) { + ret = __ps3_config_dma_mask(instance); + } else { + LOG_ERROR("hno:%u unsupport 32bit system\n", + PS3_HOST(instance)); + } + } else { + LOG_ERROR("hno:%u soc unsupport 64bit dma\n", + PS3_HOST(instance)); + } +l_out: + return ret; +} + +static inline void ps3_device_busy_threshold_cfg(struct ps3_instance *instance) +{ + int device_busy = 0; + + device_busy = ps3_device_busy_threshold_query(); + if (device_busy <= 0 || device_busy > instance->cmd_attr.cur_can_que) + instance->device_busy_threshold = PS3_DEVICE_IO_BUSY_THRESHOLD; + else + instance->device_busy_threshold = device_busy; +} + +static void ps3_cmd_attr_context_init(struct ps3_instance *instance) +{ + unsigned int cmd_qdepth = ps3_throttle_qdepth_query(); + + instance->cmd_attr.cur_can_que = + instance->cmd_context.max_scsi_cmd_count - + instance->cmd_context.max_r1x_cmd_count; + + if ((cmd_qdepth != 0) && + cmd_qdepth <= (unsigned int)instance->cmd_attr.cur_can_que) { + instance->cmd_attr.throttle_que_depth = cmd_qdepth; + } else { + instance->cmd_attr.throttle_que_depth = + PS3_DEVICE_QDEPTH_DEFAULT_VALUE; + } + + instance->cmd_attr.vd_io_threshold = 0; + instance->cmd_attr.is_support_direct_cmd = PS3_FALSE; + + ps3_device_busy_threshold_cfg(instance); +} + +static int ps3_init_ioc_prepare(struct ps3_instance *instance) +{ + ps3_ioc_mgr_req_queue_lock_init(instance); + instance->watchdog_context.is_stop = PS3_TRUE; + ps3_atomic_set(&instance->watchdog_ref, 0); + ps3_err_fault_context_init(instance); + + if (!ps3_ioc_fw_version_get(instance)) + goto l_get_reg_failed; + + if (!ps3_feature_support_reg_get(instance)) + goto l_get_reg_failed; + + if (ps3_ioc_init_cmd_context_init(instance) != PS3_SUCCESS) + goto l_ioc_init_failed; + + if (ps3_ctrl_info_buf_alloc(instance) != PS3_SUCCESS) + goto l_ctrl_info_init_failed; + + if (ps3_cmd_context_init(instance) != PS3_SUCCESS) + goto l_cmd_context_init_failed; + + ps3_cmd_attr_context_init(instance); + + if (ps3_event_context_init(instance) != PS3_SUCCESS) + goto l_event_context_init_failed; + + if (ps3_webSubscribe_context_init(instance) != PS3_SUCCESS) + goto l_web_context_init_failed; + + if (ps3_cmd_statistics_init(instance) != PS3_SUCCESS) + goto l_cmd_stat_failed; + + if (ps3_debug_mem_alloc(instance) != PS3_SUCCESS) + goto l_debug_mem_failed; + + if (ps3_dump_init(instance) != PS3_SUCCESS) + goto l_dump_init_failed; + + if (ps3_drv_info_buf_alloc(instance) != PS3_SUCCESS) + goto l_drv_info_init_failed; + + if (ps3_host_mem_info_buf_alloc(instance) != PS3_SUCCESS) + goto l_host_mem_init_failed; + return PS3_SUCCESS; + +l_host_mem_init_failed: + ps3_drv_info_buf_free(instance); +l_drv_info_init_failed: + ps3_dump_exit(instance); +l_dump_init_failed: + ps3_debug_mem_free(instance); +l_debug_mem_failed: + ps3_cmd_statistics_exit(instance); +l_cmd_stat_failed: + ps3_webSubscribe_context_exit(instance); +l_web_context_init_failed: + ps3_event_context_exit(instance); +l_event_context_init_failed: + ps3_cmd_context_exit(instance); +l_cmd_context_init_failed: + ps3_ctrl_info_buf_free(instance); +l_ctrl_info_init_failed: + ps3_ioc_init_cmd_context_exit(instance); +l_ioc_init_failed: + ps3_recovery_context_exit(instance); +l_get_reg_failed: + return -PS3_FAILED; +} + +static void ps3_init_ioc_prepare_exit(struct ps3_instance *instance) +{ + (void)ps3_debug_mem_free(instance); + ps3_cmd_statistics_exit(instance); + ps3_event_context_exit(instance); + ps3_webSubscribe_context_exit(instance); + ps3_ctrl_info_buf_free(instance); + ps3_ioc_init_cmd_context_exit(instance); + ps3_cmd_context_exit(instance); + ps3_recovery_context_exit(instance); + ps3_err_fault_context_exit(instance); + ps3_dump_dma_buf_free(instance); + ps3_dump_exit(instance); + ps3_drv_info_buf_free(instance); + ps3_host_mem_info_buf_free(instance); +} + +static int ps3_init_ioc_complete(struct ps3_instance *instance) +{ + if (ps3_mgr_cmd_init(instance) != PS3_SUCCESS) + goto l_mgr_cmd_init_failed; + + if (ps3_device_mgr_init(instance) != PS3_SUCCESS) + goto l_device_mgr_init_failed; + + if (ps3_sas_device_mgr_init(instance) != PS3_SUCCESS) + goto l_sas_device_mgr_init_failed; + + if (ps3_qos_init(instance) != PS3_SUCCESS) + goto l_qos_init_failed; + + return PS3_SUCCESS; +l_qos_init_failed: + ps3_sas_device_mgr_exit(instance); +l_sas_device_mgr_init_failed: + ps3_device_mgr_exit(instance); +l_device_mgr_init_failed: + ps3_mgr_cmd_exit(instance); +l_mgr_cmd_init_failed: + return -PS3_FAILED; +} + +static void ps3_init_ioc_complete_exit(struct ps3_instance *instance) +{ + ps3_sas_device_mgr_exit(instance); + ps3_device_mgr_exit(instance); + ps3_mgr_cmd_exit(instance); + + ps3_qos_exit(instance); +} + +int ps3_pci_init_complete(struct ps3_instance *instance) +{ + if (ps3_config_dma_mask(instance) != PS3_SUCCESS) + goto l_failed; + + if (ps3_irq_context_init(instance) != PS3_SUCCESS) + goto l_failed; + if (instance->ioc_adpter->irq_init) { + if (instance->ioc_adpter->irq_init(instance) != PS3_SUCCESS) + goto l_irqs_init_failed; + } else { + if (ps3_irqs_init(instance) != PS3_SUCCESS) + goto l_irqs_init_failed; + } + + return PS3_SUCCESS; +l_irqs_init_failed: + ps3_irq_context_exit(instance); +l_failed: + return -PS3_FAILED; +} + +static int ps3_wait_reg_access_done(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + unsigned int read_count = 0; + const unsigned int retry_max = 9000; + + ps3_wait_scsi_cmd_done(instance, PS3_TRUE); + ps3_wait_mgr_cmd_done(instance, PS3_TRUE); + while (ps3_atomic_read(&instance->reg_op_count) != 0) { + ps3_msleep(PS3_LOOP_TIME_INTERVAL_20MS); + + if (read_count++ > retry_max) { + LOG_INFO("hno:%u wait reg op over:%d ms, failed\n", + PS3_HOST(instance), + read_count * PS3_LOOP_TIME_INTERVAL_20MS); + ret = -PS3_FAILED; + goto l_out; + } + } + +l_out: + return ret; +} + +void ps3_pci_init_complete_exit(struct ps3_instance *instance) +{ + ps3_irqs_exit(instance); + + instance->is_pci_reset = PS3_TRUE; + mb(); /* in order to force CPU ordering */ + if (ps3_wait_reg_access_done(instance) != PS3_SUCCESS) { + LOG_ERROR("hno:%u wait reg access done NOK\n", + PS3_HOST(instance)); + return; + } + + ps3_pci_exit(instance); + + if (!ps3_pci_err_recovery_get(instance) && + !instance->state_machine.is_suspend) { + pci_set_drvdata(instance->pdev, NULL); + } + + ps3_irq_context_exit(instance); +} + +static unsigned char ps3_bit_pos_set(struct ps3_instance *instance) +{ + unsigned char bit_pos = 0; + unsigned char ret = PS3_TRUE; + + if (!ps3_ioc_atu_support_retry_read(instance, &bit_pos)) { + ret = PS3_FALSE; + goto l_out; + } + if (bit_pos <= PS3_BIT_POS_44) { + instance->dma_addr_bit_pos = PCIE_DMA_HOST_ADDR_BIT_POS; + goto l_out; + } + if (ps3_get_pci_function(instance->pdev) == PS3_FUNC_ID_0) + instance->dma_addr_bit_pos = PCIE_DMA_HOST_ADDR_BIT_POS_F0; + else + instance->dma_addr_bit_pos = PCIE_DMA_HOST_ADDR_BIT_POS_F1; +l_out: + mb(); /* in order to force CPU ordering */ + LOG_WARN("hno:%u set bit pos %u\n", PS3_HOST(instance), + instance->dma_addr_bit_pos); + return ret; +} + +static int ps3_firmware_init(struct pci_dev *pdev, + struct ps3_instance *instance) +{ + int ret; + + if (ps3_recovery_context_init(instance) != PS3_SUCCESS) + goto l_recovery_context_init_failed; + + if (ps3_pci_init(pdev, instance) != PS3_SUCCESS) + goto l_failed; + + if (!ps3_ioc_recovery_count_get( + instance, + &instance->recovery_context->ioc_recovery_count)) { + goto l_failed; + } + + if (instance->ioc_adpter->ioc_security_check && + instance->ioc_adpter->ioc_security_check(instance)) { + LOG_WARN("hno:%u:ioc is in security state\n", + PS3_HOST(instance)); + goto l_failed; + } + if (instance->ioc_adpter->ioc_init_state_to_ready(instance) != + PS3_SUCCESS) { + goto l_failed; + } + atomic_set(&instance->state_machine.state, PS3_INSTANCE_STATE_READY); + if (!ps3_bit_pos_set(instance)) + goto l_failed; + if (ps3_pci_init_complete(instance) != PS3_SUCCESS) + goto l_failed; + instance->pci_err_handle_state = PS3_DEVICE_ERR_STATE_NORMAL; + + pci_save_state(pdev); + ret = ps3_init_ioc_prepare(instance); + if (ret != PS3_SUCCESS) + goto l_failed; + + if (instance->ioc_adpter->ioc_init_proc(instance) != PS3_SUCCESS) + goto l_failed; + atomic_set(&instance->state_machine.state, + PS3_INSTANCE_STATE_PRE_OPERATIONAL); + + if (ps3_ctrl_info_get(instance) != PS3_SUCCESS) + goto l_failed; + + if (ps3_init_ioc_complete(instance) != PS3_SUCCESS) + goto l_failed; + + return PS3_SUCCESS; + +l_failed: + ps3_recovery_context_exit(instance); +l_recovery_context_init_failed: + ps3_pci_init_complete_exit(instance); + ps3_init_ioc_prepare_exit(instance); + return -PS3_FAILED; +} + +static void ps3_firmware_exit(struct ps3_instance *instance) +{ + ps3_pci_init_complete_exit(instance); + ps3_init_ioc_complete_exit(instance); + ps3_init_ioc_prepare_exit(instance); +} + +static void ps3_scsi_remove_host(struct ps3_instance *instance) +{ + if (ps3_sas_is_support_smp(instance)) { + sas_remove_host(instance->host); + scsi_remove_host(instance->host); + } else { + scsi_remove_host(instance->host); + } +} + +static unsigned char ps3_cmd_context_empty(struct ps3_cmd_context *cmd_context) +{ + unsigned char ret = PS3_TRUE; + unsigned int i = 0; + + for (i = 0; i < cmd_context->max_cmd_count; i++) { + if (cmd_context->cmd_buf[i]->cmd_state.state == + PS3_CMD_STATE_PROCESS) { + ret = PS3_FALSE; + break; + } + } + + return ret; +} + +static int ps3_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct ps3_instance *instance = NULL; + unsigned long flags = 0; + int ret; + +#ifdef PS3_HARDWARE_ASIC + unsigned short real_dev_id = 0; + unsigned int check_count = ps3_hba_check_time_query() * 10 + 1; +#endif + LOG_INFO("device[%d] %s\n", pdev->dev.id, __func__); + + if (ps3_available_func_id_query() >= PS3_FUNC_UNLIMITED) { + LOG2_WARN("PCI Device %04x:%02x:%02x:%x probe start.\n", + ps3_get_pci_domain(pdev), ps3_get_pci_bus(pdev), + ps3_get_pci_slot(pdev), ps3_get_pci_function(pdev)); + } else { + if ((unsigned int)ps3_get_pci_function(pdev) != + ps3_available_func_id_query()) { + LOG2_WARN("Function %x not available\n", + ps3_get_pci_function(pdev)); + goto l_func_id_error; + } else { + LOG2_WARN("PCI Device %04x:%02x:%02x:%x probe start.\n", + ps3_get_pci_domain(pdev), + ps3_get_pci_bus(pdev), ps3_get_pci_slot(pdev), + ps3_get_pci_function(pdev)); + } + } + + + instance = ps3_instance_create(pdev, id); + if (instance == NULL) + goto l_ps3_instance_create_failed; + +#ifdef PS3_HARDWARE_ASIC + while (id->device == PCI_DEVICE_ID_PS3_RAID_FPGA && check_count > 0) { + pci_read_config_word(pdev, PCI_DEVICE_ID, &real_dev_id); + LOG_INFO("get real device id is[0x%x]\n", real_dev_id); + if (real_dev_id == PCI_DEVICE_ID_PS3_HBA || + real_dev_id == PCI_DEVICE_ID_STARS_IOC_2020_18i || + real_dev_id == PCI_DEVICE_ID_STARS_ROC_2020_10i) { + ((struct pci_device_id *)id)->device = real_dev_id; + break; + } + check_count--; + ps3_msleep(100); + }; +#endif + ps3_ioc_adp_init(instance, id); + instance->ioc_adpter->ioc_resource_prepare(instance); + ps3_debug_context_init(instance); + + device_disable_async_suspend(&pdev->dev); + + ret = ps3_firmware_init(pdev, instance); + if (ret != PS3_SUCCESS) { + LOG_WARN("ps3_firmware_init fail:%d\n", ret); + goto l_firmware_init_failed; + } + ps3_atomic_set(&instance->state_machine.state, + PS3_INSTANCE_STATE_OPERATIONAL); + + instance->ioc_adpter->irq_enable(instance); + ps3_dump_ctrl_set_int_ready(instance); + + ps3_ioctl_init(instance, PS3_MAX_IOCTL_CMDS); + ps3_scsi_cmd_timeout_adjust(); + + ret = ps3_scsi_init(instance); + if (ret != PS3_SUCCESS) + goto l_scsi_init_failed; + instance->state_machine.is_load = PS3_TRUE; + mb(); /* in order to force CPU ordering */ + ret = ps3_watchdog_start(instance); + if (ret != PS3_SUCCESS) + goto l_watch_dog_start_failed; + + ret = ps3_device_mgr_data_init(instance); + if (ret != PS3_SUCCESS) + goto l_dev_info_get_failed; + ret = ps3_sas_device_data_init(instance); + if (ret != PS3_SUCCESS) + goto l_expander_get_failed; + instance->state_machine.can_hostreset = PS3_TRUE; + + ps3_scsi_scan_host(instance); + + + instance->is_scan_host_finish = PS3_TRUE; + + ret = ps3_event_subscribe(instance); + if (ret == -PS3_FAILED) { + goto l_event_subscribe_failed; + } else if (ret == -PS3_RECOVERED) { + LOG_INFO( + "device[%d] skip event/vd info subscribe due to recovery during probe\n", + pdev->dev.id); + goto l_event_subscribe_recovered; + } + + ret = ps3_dev_mgr_vd_info_subscribe(instance); + if (ret != PS3_SUCCESS && ret != -PS3_RECOVERED) { + LOG_INFO("device[%d] vd info subscribe failed, ret: %d\n", + pdev->dev.id, ret); + goto l_vd_info_subscribe_failed; + } +l_event_subscribe_recovered: + instance->is_probe_finish = PS3_TRUE; + mb(); /* in order to force CPU ordering */ + LOG2_WARN("PCI Device %04x:%02x:%02x:%x probe end.\n", + ps3_get_pci_domain(pdev), ps3_get_pci_bus(pdev), + ps3_get_pci_slot(pdev), ps3_get_pci_function(pdev)); + + ps3_ioc_can_hardreset_set(instance, PS3_IOC_CAN_HARDRESET); + return PS3_SUCCESS; + +l_vd_info_subscribe_failed: +l_event_subscribe_failed: + ps3_dev_mgr_vd_info_unsubscribe(instance); + ps3_event_unsubscribe(instance); + ps3_spin_lock_irqsave(&instance->recovery_context->recovery_lock, + &flags); + ps3_atomic_set(&instance->event_context.abort_eventcmd, 0); + ps3_atomic_set(&instance->dev_context.abort_vdpending_cmd, 0); + ps3_spin_unlock_irqrestore(&instance->recovery_context->recovery_lock, + flags); + ps3_sas_device_data_exit(instance); +l_expander_get_failed: +l_scsi_init_failed: + instance->state_machine.is_load = PS3_FALSE; + mb(); /* in order to force CPU ordering */ + ps3_device_mgr_data_exit(instance); +l_dev_info_get_failed: + ps3_watchdog_stop(instance); +l_watch_dog_start_failed: + ps3_scsi_remove_host(instance); + instance->state_machine.can_hostreset = PS3_FALSE; + mb(); /* in order to force CPU ordering */ + ps3_dump_work_stop(instance); + ps3_recovery_context_exit(instance); + instance->ioc_adpter->irq_disable(instance); + ps3_irqs_sync(instance); + + ps3_irqpolls_enable(instance); + ps3_firmware_exit(instance); +l_firmware_init_failed: + instance->is_probe_failed = PS3_TRUE; + mb(); /* in order to force CPU ordering */ + ps3_instance_put(instance); + scsi_host_put(instance->host); +l_ps3_instance_create_failed: + ps3_dma_dump_mapping(pdev); +l_func_id_error: + dev_warn(&pdev->dev, "PCI Device %04x:%02x:%02x:%x probe failed.\n", + ps3_get_pci_domain(pdev), ps3_get_pci_bus(pdev), + ps3_get_pci_slot(pdev), ps3_get_pci_function(pdev)); + return -ENODEV; +} + +static int ps3_cancel_event_wk_in_unload(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + unsigned int cur_state = 0; + unsigned int ioc_state = 0; + + cur_state = ps3_atomic_read(&instance->state_machine.state); + ioc_state = instance->ioc_adpter->ioc_state_get(instance); + if ((ioc_state != PS3_FW_STATE_RUNNING) || + (cur_state != PS3_INSTANCE_STATE_OPERATIONAL)) { + LOG_WARN( + "hno:%u goto half hard reset, cur_state:%s,IOC state is %s!\n", + PS3_HOST(instance), namePS3InstanceState(cur_state), + ps3_ioc_state_print(ioc_state)); + ret = -PS3_FAILED; + goto l_out; + } + cancel_delayed_work_sync( + &instance->event_context.delay_work->event_work); + +l_out: + return ret; +} + +static void ps3_shutdown(struct pci_dev *pdev) +{ + struct ps3_instance *instance = + (struct ps3_instance *)pci_get_drvdata(pdev); + if (instance == NULL) { + LOG_ERROR("device[%d] %s fail\n", pdev->dev.id, __func__); + return; + } + LOG2_WARN("PCI Device %04x:%02x:%02x:%x shutdown start.\n", + ps3_get_pci_domain(pdev), ps3_get_pci_bus(pdev), + ps3_get_pci_slot(pdev), ps3_get_pci_function(pdev)); + + instance->state_machine.is_load = PS3_FALSE; + mb(); /* in order to force CPU ordering */ + ps3_recovery_cancel_work_sync(instance); + if (instance->pci_err_handle_state == PS3_DEVICE_ERR_STATE_CLEAN) { + while (!ps3_cmd_context_empty(&instance->cmd_context)) + ps3_msleep(10000); + } + if (instance->is_half_hard_reset) + goto l_reset_to_ready; + + ps3_r1x_conflict_queue_clean_all( + instance, PS3_SCSI_RESULT_HOST_STATUS(DID_NO_CONNECT), + PS3_TRUE); + + ps3_qos_waitq_clear_all(instance, PS3_STATUS_HOST_NOT_FOUND); + + if (ps3_cancel_event_wk_in_unload(instance) != PS3_SUCCESS) + goto l_reset_to_ready; + + if (ps3_event_unsubscribe(instance) != PS3_SUCCESS) { + LOG_ERROR("device[%d] event unsubscribe NOK.\n", pdev->dev.id); + goto l_reset_to_ready; + } + + if (ps3_dev_mgr_vd_info_unsubscribe(instance) != PS3_SUCCESS) { + LOG_ERROR("device[%d] vd unsubscribe NOK.\n", pdev->dev.id); + goto l_reset_to_ready; + } + + if (ps3_atomic_read(&instance->webSubscribe_context.is_subscribe) == + 1) { + ps3_wait_mgr_cmd_done(instance, PS3_TRUE); + if (ps3_web_unsubscribe(instance) != PS3_SUCCESS) { + LOG_ERROR("device[%d] web unsubscribe NOK.\n", + pdev->dev.id); + goto l_reset_to_ready; + } + } + + instance->is_host_added = PS3_FALSE; + ps3_watchdog_stop(instance); + + ps3_dump_exit(instance); + + if (instance->is_half_hard_reset) + goto l_release_res; + + if (ps3_soc_unload(instance, PS3_FALSE, PS3_UNLOAD_SUB_TYPE_SHUTDOWN, + PS3_SUSPEND_TYPE_NONE) != PS3_SUCCESS) { + LOG_ERROR("device[%d] unload failed.\n", pdev->dev.id); + goto l_reset_to_ready; + } + + goto l_release_res; +l_reset_to_ready: + if (instance->is_host_added) { + ps3_watchdog_stop(instance); + ps3_dump_exit(instance); + if (instance->is_half_hard_reset) + goto l_release_res; + } + + if (ps3_hard_reset_to_ready_with_doorbell(instance) != PS3_SUCCESS) + LOG_ERROR("device[%d] hard reset NOK.\n", PS3_HOST(instance)); +l_release_res: + while (atomic_read(&instance->cmd_statistics.io_outstanding) != 0) + ps3_msleep(3000); + + instance->state_machine.can_hostreset = PS3_FALSE; + ps3_check_and_wait_host_reset(instance); + if (!instance->is_half_hard_reset) { + instance->ioc_adpter->irq_disable(instance); + ps3_irqs_sync(instance); + } + ps3_recovery_destroy(instance); + + ps3_instance_state_transfer_to_quit(instance); + ps3_recovery_clean(instance); + + ps3_ioctl_clean(instance); + + ps3_firmware_exit(instance); + ps3_instance_put(instance); + LOG2_WARN("PCI Device %04x:%02x:%02x:%x shutdown end.\n", + ps3_get_pci_domain(pdev), ps3_get_pci_bus(pdev), + ps3_get_pci_slot(pdev), ps3_get_pci_function(pdev)); +} + +void ps3_remove(struct pci_dev *pdev) +{ + struct ps3_instance *instance = + (struct ps3_instance *)pci_get_drvdata(pdev); + if (instance == NULL) { + LOG_ERROR("device[%d] %s fail\n", pdev->dev.id, __func__); + return; + } + instance->state_machine.is_load = PS3_FALSE; + LOG2_WARN("PCI Device %04x:%02x:%02x:%x remove start\n", + ps3_get_pci_domain(pdev), ps3_get_pci_bus(pdev), + ps3_get_pci_slot(pdev), ps3_get_pci_function(pdev)); + mb(); /* in order to force CPU ordering */ + ps3_recovery_cancel_work_sync(instance); + if (instance->pci_err_handle_state == PS3_DEVICE_ERR_STATE_CLEAN) { + ps3_dump_exit(instance); + while (ps3_atomic_read(&instance->is_err_scsi_processing) > 0) + ps3_msleep(10); + + while (!ps3_cmd_context_empty(&instance->cmd_context)) + ps3_msleep(10000); + + ps3_all_reply_fifo_complete(instance); + + ps3_cmd_force_stop(instance); + cancel_delayed_work_sync( + &instance->event_context.delay_work->event_work); + goto l_release_res; + } + if (instance->is_half_hard_reset) + goto l_reset_to_ready; + if (ps3_cancel_event_wk_in_unload(instance) != PS3_SUCCESS) + goto l_reset_to_ready; + if (ps3_event_unsubscribe(instance) != PS3_SUCCESS) { + LOG_ERROR("device[%d] event unsubscribe NOK.\n", pdev->dev.id); + goto l_reset_to_ready; + } + if (ps3_dev_mgr_vd_info_unsubscribe(instance) != PS3_SUCCESS) { + LOG_ERROR("device[%d] vd unsubscribe NOK.\n", pdev->dev.id); + goto l_reset_to_ready; + } + if (ps3_atomic_read(&instance->webSubscribe_context.is_subscribe) == + 1) { + ps3_wait_mgr_cmd_done(instance, PS3_TRUE); + if (ps3_web_unsubscribe(instance) != PS3_SUCCESS) { + LOG_ERROR("device[%d] web unsubscribe NOK.\n", + pdev->dev.id); + goto l_reset_to_ready; + } + } + + ps3_r1x_conflict_queue_clean_all( + instance, PS3_SCSI_RESULT_HOST_STATUS(DID_NO_CONNECT), + PS3_TRUE); + + ps3_qos_waitq_clear_all(instance, PS3_STATUS_HOST_NOT_FOUND); + + ps3_scsi_remove_host(instance); + + instance->is_host_added = PS3_FALSE; + + ps3_watchdog_stop(instance); + ps3_dump_exit(instance); + + if (instance->is_half_hard_reset) + goto l_release_res; + if (ps3_soc_unload(instance, PS3_FALSE, PS3_UNLOAD_SUB_TYPE_REMOVE, + PS3_SUSPEND_TYPE_NONE) != PS3_SUCCESS) { + LOG_ERROR("device[%d] unload failed.\n", pdev->dev.id); + goto l_reset_to_ready; + } + + goto l_release_res; +l_reset_to_ready: + if (instance->is_host_added) { + ps3_watchdog_stop(instance); + ps3_dump_exit(instance); + if (instance->is_half_hard_reset) + goto l_release_res; + } + if (ps3_hard_reset_to_ready_with_doorbell(instance) != PS3_SUCCESS) + LOG_ERROR("device[%d] hard reset NOK.\n", PS3_HOST(instance)); + +l_release_res: + while (atomic_read(&instance->cmd_statistics.io_outstanding) != 0) + ps3_msleep(3000); + instance->state_machine.can_hostreset = PS3_FALSE; + ps3_check_and_wait_host_reset(instance); + + ps3_instance_state_transfer_to_quit(instance); + ps3_recovery_destroy(instance); + + if (instance->is_host_added) + ps3_scsi_remove_host(instance); + + while (ps3_atomic_read(&instance->host_reset_processing) > 0) + ps3_msleep(PS3_LOOP_TIME_INTERVAL_100MS); + ps3_recovery_clean(instance); + if (!instance->is_half_hard_reset) { + instance->ioc_adpter->irq_disable(instance); + ps3_irqs_sync(instance); + } + ps3_ioctl_clean(instance); + + ps3_firmware_exit(instance); + + ps3_instance_put(instance); + scsi_host_put(instance->host); + ps3_dma_dump_mapping(pdev); + LOG2_WARN("PCI Device %04x:%02x:%02x:%x remove end\n", + ps3_get_pci_domain(pdev), ps3_get_pci_bus(pdev), + ps3_get_pci_slot(pdev), ps3_get_pci_function(pdev)); +} + +#ifdef CONFIG_PM +static int ps3_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct ps3_instance *instance = + (struct ps3_instance *)pci_get_drvdata(pdev); + struct ps3_cmd *cmd = NULL; + struct ps3_cmd *abort_cmd = NULL; + unsigned char suspend_type = PS3_SUSPEND_TYPE_SLEEP; + + (void)state; + if (instance == NULL) { + LOG2_ERROR("device[%d] %s NOK\n", pdev->dev.id, __func__); + return PS3_SUCCESS; + } + + LOG2_WARN("PCI Device %04x:%02x:%02x:%x %s start.\n", + ps3_get_pci_domain(pdev), ps3_get_pci_bus(pdev), + ps3_get_pci_slot(pdev), ps3_get_pci_function(pdev), __func__); + instance->is_suspend = PS3_TRUE; + mb(); /* in order to force CPU ordering */ + instance->state_machine.is_suspend = PS3_TRUE; + mb(); /* in order to force CPU ordering */ + ps3_recovery_cancel_work_sync(instance); + if (instance->pci_err_handle_state == PS3_DEVICE_ERR_STATE_CLEAN) { + while (!ps3_cmd_context_empty(&instance->cmd_context)) + ps3_msleep(10000); + } + if (instance->is_half_hard_reset) + goto l_reset_to_ready; + + ps3_r1x_conflict_queue_clean_all( + instance, PS3_SCSI_RESULT_HOST_STATUS(DID_NO_CONNECT), + PS3_TRUE); + + ps3_qos_waitq_clear_all(instance, PS3_STATUS_HOST_NOT_FOUND); + + if (ps3_cancel_event_wk_in_unload(instance) != PS3_SUCCESS) + goto l_reset_to_ready; + + if (ps3_event_unsubscribe(instance) != PS3_SUCCESS) { + LOG_ERROR("device[%d] event unsubscribe NOK.\n", pdev->dev.id); + goto l_reset_to_ready; + } + + if (ps3_dev_mgr_vd_info_unsubscribe(instance) != PS3_SUCCESS) { + LOG_ERROR("device[%d] vd unsubscribe NOK.\n", pdev->dev.id); + goto l_reset_to_ready; + } + + if (ps3_atomic_read(&instance->webSubscribe_context.is_subscribe) == + 1) { + ps3_wait_mgr_cmd_done(instance, PS3_TRUE); + if (ps3_web_unsubscribe(instance) != PS3_SUCCESS) { + LOG_ERROR("device[%d] web unsubscribe NOK.\n", + pdev->dev.id); + goto l_reset_to_ready; + } + } + instance->ioc_adpter->irq_disable(instance); + ps3_irqs_sync(instance); + + ps3_irqpolls_enable(instance); + + ps3_dump_work_stop(instance); + ps3_watchdog_stop(instance); + ps3_recovery_cancel_work_sync(instance); + + if (instance->is_half_hard_reset) + goto l_release_res; +#if defined(PS3_SUPPORT_FLUSH_SCHEDULED) + flush_scheduled_work(); +#endif + scsi_block_requests(instance->host); + + if (state.event == PM_EVENT_FREEZE) + suspend_type = PS3_SUSPEND_TYPE_HIBERNATE; + LOG_INFO("event[%d] suspend_type[%d].\n", state.event, suspend_type); + if (ps3_soc_unload(instance, PS3_FALSE, PS3_UNLOAD_SUB_TYPE_SUSPEND, + suspend_type) != PS3_SUCCESS) { + LOG_ERROR("device[%d] unload NOK.\n", pdev->dev.id); + if (ps3_hard_reset_to_ready_with_doorbell(instance) != + PS3_SUCCESS) { + LOG_ERROR("device[%d] hard reset NOK.\n", + PS3_HOST(instance)); + } + } + + goto l_release_res; +l_reset_to_ready: + if (!instance->is_half_hard_reset) { + instance->ioc_adpter->irq_disable(instance); + ps3_irqs_sync(instance); + + ps3_irqpolls_enable(instance); + } + ps3_dump_work_stop(instance); + ps3_watchdog_stop(instance); + ps3_recovery_cancel_work_sync(instance); + if (instance->is_half_hard_reset) + goto l_release_res; + + if (ps3_hard_reset_to_ready_with_doorbell(instance) != PS3_SUCCESS) + LOG_ERROR("device[%d] hard reset NOK.\n", PS3_HOST(instance)); + + if (instance->event_context.event_cmd != NULL) { + cmd = instance->event_context.event_cmd; + instance->event_context.event_cmd = NULL; + ps3_mgr_cmd_free(instance, cmd); + abort_cmd = instance->event_context.event_abort_cmd; + if (abort_cmd != NULL) { + instance->event_context.event_abort_cmd = NULL; + ps3_task_cmd_free(instance, abort_cmd); + } + ps3_atomic_set(&instance->event_context.subwork, 0); + } + + ps3_dev_mgr_vd_info_clear(instance); + + if (ps3_atomic_read(&instance->webSubscribe_context.is_subscribe) == + 1) { + ps3_web_cmd_clear(instance); + } +#if defined(PS3_SUPPORT_FLUSH_SCHEDULED) + flush_scheduled_work(); +#endif + scsi_block_requests(instance->host); +l_release_res: + + pci_set_drvdata(instance->pdev, instance); + pci_save_state(pdev); + instance->is_half_hard_reset = PS3_FALSE; + ps3_base_free_resources(instance); + ps3_instance_put(instance); + + LOG2_WARN("PCI Device %04x:%02x:%02x:%x %s end.\n", + ps3_get_pci_domain(pdev), ps3_get_pci_bus(pdev), + ps3_get_pci_slot(pdev), ps3_get_pci_function(pdev), __func__); + instance->is_suspend = PS3_FALSE; + return PS3_SUCCESS; +} + +static int ps3_resume(struct pci_dev *pdev) +{ + int ret = PS3_SUCCESS; + struct ps3_instance *instance = + (struct ps3_instance *)pci_get_drvdata(pdev); + + if (instance == NULL) { + LOG2_ERROR("device[%d] %s NOK\n", pdev->dev.id, __func__); + return PS3_SUCCESS; + } + + LOG2_WARN("PCI Device %04x:%02x:%02x:%x %s start.\n", + ps3_get_pci_domain(pdev), ps3_get_pci_bus(pdev), + ps3_get_pci_slot(pdev), ps3_get_pci_function(pdev), __func__); + instance->is_resume = PS3_TRUE; + mb(); /* in order to force CPU ordering */ + ps3_recovery_cancel_work_sync(instance); + pci_restore_state(pdev); + + instance->pdev = pdev; + if (ps3_instance_add(instance) != PS3_SUCCESS) + goto l_out; + if (ps3_available_func_id_query() < PS3_FUNC_UNLIMITED && + (unsigned int)ps3_get_pci_function(pdev) != ps3_available_func_id_query()) { + LOG2_WARN("Function %d not available\n", + ps3_get_pci_function(pdev)); + goto l_out; + } + + ret = ps3_base_init_resources(instance); + if (ret != PS3_SUCCESS) { + LOG_ERROR("hno:%u base init resources NOK, ret: %d\n", + PS3_HOST(instance), ret); + goto l_out; + } + instance->pci_err_handle_state = PS3_DEVICE_ERR_STATE_NORMAL; + + if (instance->ioc_adpter->ioc_init_state_to_ready(instance) != + PS3_SUCCESS) { + goto l_out; + } + + ps3_atomic_set(&instance->state_machine.state, + PS3_INSTANCE_STATE_READY); + + ps3_all_reply_fifo_init(instance); + + if (instance->ioc_adpter->ioc_init_proc(instance) != PS3_SUCCESS) + goto l_out; + + ps3_atomic_set(&instance->state_machine.state, + PS3_INSTANCE_STATE_PRE_OPERATIONAL); + + instance->ioc_adpter->irq_enable(instance); + + ps3_atomic_set(&instance->state_machine.state, + PS3_INSTANCE_STATE_OPERATIONAL); + + instance->state_machine.is_load = PS3_TRUE; + scsi_unblock_requests(instance->host); + + ps3_dump_ctrl_set_int_ready(instance); + + ret = ps3_watchdog_start(instance); + if (ret != PS3_SUCCESS) + goto l_out; + + instance->event_req_info.eventTypeMapProcResult = + instance->event_req_info.eventTypeMap; + ret = ps3_event_subscribe(instance); + if (ret == -PS3_FAILED) { + goto l_out; + } else if (ret == -PS3_RECOVERED) { + LOG_INFO( + "device[%d] skip event/vd info subscribe due to recovery during probe\n", + pdev->dev.id); + goto l_event_subscribe_recovered; + } + + ret = ps3_dev_mgr_vd_info_subscribe(instance); + if (ret != PS3_SUCCESS && ret != -PS3_RECOVERED) { + LOG_INFO("device[%d] vd info subscribe NOK, ret: %d\n", + pdev->dev.id, ret); + goto l_out; + } + + if (ps3_atomic_read(&instance->webSubscribe_context.is_subscribe) == + 1) { + ret = ps3_web_subscribe(instance); + if (ret != PS3_SUCCESS && ret != -PS3_IN_UNLOAD) + goto l_out; + } + +l_event_subscribe_recovered: + instance->state_machine.is_suspend = PS3_FALSE; + instance->is_probe_finish = PS3_TRUE; + instance->is_resume = PS3_FALSE; + LOG2_WARN("PCI Device %04x:%02x:%02x:%x %s end.\n", + ps3_get_pci_domain(pdev), ps3_get_pci_bus(pdev), + ps3_get_pci_slot(pdev), ps3_get_pci_function(pdev), __func__); + + ps3_ioc_can_hardreset_set(instance, PS3_IOC_CAN_HARDRESET); + return PS3_SUCCESS; +l_out: + instance->state_machine.is_suspend = PS3_FALSE; + instance->is_resume = PS3_FALSE; + instance->state_machine.is_load = PS3_TRUE; + ps3_instance_state_transfer_to_dead(instance); + scsi_unblock_requests(instance->host); + ps3_dma_dump_mapping(pdev); + LOG2_WARN("PCI Device %04x:%02x:%02x:%x %s NOK.\n", + ps3_get_pci_domain(pdev), ps3_get_pci_bus(pdev), + ps3_get_pci_slot(pdev), ps3_get_pci_function(pdev), __func__); + return -ENODEV; +} +#endif + +static ssize_t version_show(struct device_driver *dd, char *buf) +{ + (void)dd; + return snprintf(buf, strlen(PS3_DRV_VERSION) + 2, "%s\n", + PS3_DRV_VERSION); +} +static DRIVER_ATTR_RO(version); + +static ssize_t release_date_show(struct device_driver *dd, char *buf) +{ + (void)dd; + return snprintf(buf, strlen(PS3_DRV_BUILD_TIME) + 2, "%s\n", + PS3_DRV_BUILD_TIME); +} +static DRIVER_ATTR_RO(release_date); + +static const struct file_operations ps3_mgmt_fops = { .owner = THIS_MODULE, + .unlocked_ioctl = + ps3_ioctl_fops, + .open = ps3_mgmt_open, + .release = NULL, + .llseek = noop_llseek, + .read = NULL, + .write = NULL, + .fasync = ps3_fasync }; + +static struct pci_driver ps3_pci_driver; +static void init_pci_driver(struct pci_driver *drv) +{ + drv->name = PS3_PCI_DRIVER_NAME; + drv->id_table = ps3_pci_table; + drv->probe = ps3_probe; + drv->remove = ps3_remove; +#ifdef CONFIG_PM + drv->suspend = ps3_suspend; + drv->resume = ps3_resume; +#endif + drv->shutdown = ps3_shutdown; + if (ps3_aer_handle_support_query()) + ps3_pci_err_handler_init(drv); + else + LOG_INFO("aer handle not supported\n"); +} + +static int __init ps3stor_init(void) +{ + int ret = PS3_SUCCESS; + + pr_warn("ps3stor driver init start, version[%s], commit_id[%s], build_time[%s], toolchain_id[%s]\n", + PS3_DRV_VERSION, PS3_DRV_COMMIT_ID, PS3_DRV_BUILD_TIME, + PS3_DRV_TOOLCHAIN_ID); + + ret = ps3_debug_init(); + if (ret < 0) { + pr_err("ps3stor log init fail\n"); + goto l_debug_init_failed; + } + ps3_host_info_get(); + ps3cmd_init(); + ps3_mgmt_info_init(); + + LOG_FILE_WARN("ps3stor driver ver[%s], commit_id[%s], build_time[%s], toolchain_id[%s]\n", + PS3_DRV_VERSION, PS3_DRV_COMMIT_ID, PS3_DRV_BUILD_TIME, + PS3_DRV_TOOLCHAIN_ID); + + ret = ps3_sas_attach_transport(); + if (ret != PS3_SUCCESS) { + pr_err("ps3stor transport fail\n"); + ret = -ENODEV; + goto l_sas_transport_failed; + } + + ret = register_chrdev(0, PS3_CHRDEV_NAME, &ps3_mgmt_fops); + if (ret < 0) { + LOG_ERROR("ps3stor: failed to open device node\n"); + goto l_register_chrdev_failed; + } + ps3_mgmt_chrdev_major_no = ret; + + init_pci_driver(&ps3_pci_driver); + ret = pci_register_driver(&ps3_pci_driver); + if (ret) { + LOG_ERROR("ps3stor: PCI hotplug registration failed\n"); + goto l_pci_register_driver_failed; + } + + ret = driver_create_file(&ps3_pci_driver.driver, &driver_attr_version); + if (ret) + goto l_attr_ver_failed; + ret = driver_create_file(&ps3_pci_driver.driver, + &driver_attr_release_date); + if (ret) + goto l_attr_release_date_failed; + + ps3_trace_id_init(); + ps3_version_verbose_fill(); + + return ret; + +l_attr_release_date_failed: + driver_remove_file(&ps3_pci_driver.driver, &driver_attr_version); +l_attr_ver_failed: + pci_unregister_driver(&ps3_pci_driver); +l_pci_register_driver_failed: + unregister_chrdev(ps3_mgmt_chrdev_major_no, PS3_CHRDEV_NAME); +l_register_chrdev_failed: + ps3_sas_release_transport(); +l_sas_transport_failed: + ps3_debug_exit(); + ps3cmd_exit(); + ps3_mgmt_exit(); +l_debug_init_failed: + return ret; +} + +static void __exit ps3stor_exit(void) +{ + driver_remove_file(&ps3_pci_driver.driver, &driver_attr_version); + driver_remove_file(&ps3_pci_driver.driver, &driver_attr_release_date); + + pci_unregister_driver(&ps3_pci_driver); + unregister_chrdev(ps3_mgmt_chrdev_major_no, PS3_CHRDEV_NAME); + + ps3_sas_release_transport(); + ps3_debug_exit(); + ps3cmd_exit(); + ps3_mgmt_exit(); +} + +MODULE_INFO(private_version, PS3_PRIVATE_VERSION); +MODULE_INFO(product_support, PS3_DRV_PRODUCT_SUPPORT); +MODULE_INFO(build_time, PS3_DRV_BUILD_TIME); +MODULE_INFO(toolchain_id, PS3_DRV_TOOLCHAIN_ID); +MODULE_INFO(commit_id, PS3_DRV_COMMIT_ID); +MODULE_VERSION(PS3_DRV_VERSION); +MODULE_AUTHOR(PS3_DRV_AUTHOR); +MODULE_DESCRIPTION(PS3_DRV_DESCRIPTION); +MODULE_LICENSE(PS3_DRV_LICENSE); + +module_init(ps3stor_init); +module_exit(ps3stor_exit); diff --git a/drivers/scsi/linkdata/ps3stor/linux/ps3_cli.c b/drivers/scsi/linkdata/ps3stor/linux/ps3_cli.c new file mode 100644 index 0000000000000000000000000000000000000000..d249417e9cf2ee6067e57bc3b797277c0cb59e42 --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/linux/ps3_cli.c @@ -0,0 +1,334 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ps3_cli.h" +#include "ps3_instance_manager.h" +#include "ps3_kernel_version.h" +#include "ps3_util.h" + +#define PS3_CLI_STATIC_MINOR 26 +#define PS3_CLI_DYNAMIC_MINOR MISC_DYNAMIC_MINOR +#define PS3_CLI_HASH_LEN 256 + +struct ps3_cli_cmd_s { + struct ps3_cli_cmd_s *next; + char cmd[PS3_CLI_CMD_MAXLEN]; + char help[PS3_CLI_HELP_LEN]; + void (*func)(int argc, char *argv[]); +}; + +static int misc_registered; +static atomic_t dev_opened; +static int cmd_ready; +static char ps3_cli_input[PS3_CLI_INPUT_LEN]; +static char ps3_cli_output[PS3_CLI_OUTLINE_LEN]; +static char __user *read_buf; +static int read_buf_len; +static int read_buf_ptr; + +static struct ps3_cli_cmd_s *ps3_cli_cmd_head[PS3_CLI_HASH_LEN]; +static struct mutex ps3_cli_mutex; + +#define __pl() + +static inline int ps3_cli_minor_get(void) +{ + if (strstr(ps3_host_release_get(), "5.10.134-16.2.an8")) + return PS3_CLI_DYNAMIC_MINOR; +#if defined(PS3_STATIC_MINOR) + return PS3_CLI_STATIC_MINOR; +#else + return PS3_CLI_DYNAMIC_MINOR; +#endif +} + +static ssize_t ps3_cli_write(struct file *fp, const char __user *buffer, + size_t nbytes, loff_t *ppos) +{ + __pl(); + (void)fp; + (void)ppos; + if (nbytes > PS3_CLI_INPUT_LEN - 1) + return -EINVAL; + + if (copy_from_user(ps3_cli_input, buffer, nbytes)) + return -EFAULT; + ps3_cli_input[nbytes] = '\0'; + cmd_ready = 1; + __pl(); + return (ssize_t)nbytes; +} + +static u32 str_hash(const char *name) +{ + u32 hash, hash0 = 0x12a3fe2d, hash1 = 0x37abe8f9; + const signed char *scp = (const signed char *)name; + + while (*scp) { + hash = hash1 + (hash0 ^ (((int)*scp++) * 7152373)); + + if (hash & 0x80000000) + hash -= 0x7fffffff; + hash1 = hash0; + hash0 = hash; + } + return hash0 << 1; +} + +static struct ps3_cli_cmd_s *ps3_cli_find_cmd(const char *cmd) +{ + u32 idx = str_hash(cmd) & (PS3_CLI_HASH_LEN - 1); + struct ps3_cli_cmd_s *p; + + for (p = ps3_cli_cmd_head[idx]; p; p = p->next) + if (!strcmp(p->cmd, cmd)) + return p; + return NULL; +} + +int ps3stor_cli_printf(const char *fmt, ...) +{ + va_list args; + int len, n, ret; + + __pl(); + + va_start(args, fmt); + len = vsnprintf(ps3_cli_output, PS3_CLI_OUTLINE_LEN, fmt, args); + va_end(args); + + if (read_buf_ptr >= read_buf_len) + return read_buf_ptr; + + n = read_buf_len - read_buf_ptr; + if (n > len) + n = len; + + ret = copy_to_user(read_buf + read_buf_ptr, ps3_cli_output, n); + if (ret < 0) { + pr_err("copy_to_user err=%d\n", ret); + return -1; + } + + read_buf_ptr += n; + + __pl(); + + return len; +} +EXPORT_SYMBOL(ps3stor_cli_printf); + +int ps3stor_cli_register(void (*func)(int argc, char *argv[]), const char *cmd_str, + const char *help) +{ + u32 idx = str_hash(cmd_str) & (PS3_CLI_HASH_LEN - 1); + struct ps3_cli_cmd_s *cmd; + int ret; + + __pl(); + + ret = mutex_lock_killable(&ps3_cli_mutex); + if (ret != 0) { + pr_err("%s(): mutex_lock_killable return err = %d\n", + __func__, ret); + return ret; + } + cmd = ps3_cli_find_cmd(cmd_str); + if (cmd) { + pr_err("cmd=['%s'] has already been registered\n", cmd_str); + mutex_unlock(&ps3_cli_mutex); + return -EEXIST; + } + + cmd = kmalloc(sizeof(struct ps3_cli_cmd_s), GFP_KERNEL); + if (cmd == NULL) { + mutex_unlock(&ps3_cli_mutex); + return -ENOMEM; + } + + memset(cmd, 0, sizeof(struct ps3_cli_cmd_s)); + + PS3_STRCPY(cmd->cmd, cmd_str, PS3_CLI_CMD_MAXLEN - 1); + PS3_STRCPY(cmd->help, help, PS3_CLI_HELP_LEN - 1); + + cmd->func = func; + + cmd->next = ps3_cli_cmd_head[idx]; + ps3_cli_cmd_head[idx] = cmd; + + mutex_unlock(&ps3_cli_mutex); + __pl(); + return 0; +} +EXPORT_SYMBOL(ps3stor_cli_register); + +static void ps3_parse_cmd(char *cmdline, int *argc, char *argv[]) +{ + char *p = cmdline; + int i = 0, spc = 1; + + while (*p) { + if (spc) { + if (*p != ' ') { + spc = 0; + argv[i] = p; + i++; + } else { + *p = '\0'; + } + } else { + if (*p == ' ') { + spc = 1; + *p = '\0'; + } else { + } + } + p++; + } + *argc = i; +} + +static ssize_t ps3_cli_read(struct file *fp, char __user *buf, size_t nbytes, + loff_t *ppos) +{ + struct ps3_cli_cmd_s *cmd; + int argc; + char *argv[PS3_MAX_ARGV]; + int ret; + + (void)fp; + (void)ppos; + __pl(); + + if (!cmd_ready) { + pr_err("ps3_cli_write() must be called before ps_cli_read()\n"); + return -EINVAL; + } + read_buf = buf; + read_buf_len = (int)nbytes; + read_buf_ptr = 0; + + ps3_parse_cmd(ps3_cli_input, &argc, argv); + + ret = mutex_lock_killable(&ps3_cli_mutex); + if (ret != 0) { + pr_err("ps3stor_cli_register(): mutex_lock_killable return err = %d\n", + ret); + return ret; + } + cmd = ps3_cli_find_cmd((const char *)argv[0]); + if (cmd != NULL) + cmd->func(argc, argv); + mutex_unlock(&ps3_cli_mutex); + + __pl(); + return read_buf_ptr; +} + +static int ps3_cli_open(struct inode *ip, struct file *fp) +{ + (void)ip; + (void)fp; + if (atomic_cmpxchg(&dev_opened, 0, 1) == 1) { + pr_err("/dev/ps3stor_cli has already been opened\n"); + return -EBUSY; + } + cmd_ready = 0; + return 0; +} + +static int ps3_cli_release(struct inode *ip, struct file *fp) +{ + (void)ip; + (void)fp; + ps3_atomic_set(&dev_opened, 0); + return 0; +} + +static const struct file_operations ps3_cli_fops = { .owner = NULL, + .unlocked_ioctl = NULL, + .open = ps3_cli_open, + .release = ps3_cli_release, + .llseek = NULL, + .read = ps3_cli_read, + .write = ps3_cli_write, + .fasync = NULL }; + +static struct miscdevice ps3_cli_device = { + .minor = PS3_CLI_STATIC_MINOR, + .name = "ps3stor_cli", + .fops = &ps3_cli_fops, +}; + +static void ps3_cli_help(int argc, char *argv[]) +{ + int i; + struct ps3_cli_cmd_s *cmd; + (void)argc; + (void)argv; + __pl(); + for (i = 0; i < PS3_CLI_HASH_LEN; i++) { + for (cmd = ps3_cli_cmd_head[i]; cmd != NULL; cmd = cmd->next) { + ps3stor_cli_printf("%20s -- %s\n", cmd->cmd, + (const char *)cmd->help); + } + } + __pl(); +} + +static void ps3_free_cmds(void) +{ + int i; + struct ps3_cli_cmd_s *cmd; + + for (i = 0; i < PS3_CLI_HASH_LEN; i++) { + while (ps3_cli_cmd_head[i]) { + cmd = ps3_cli_cmd_head[i]; + ps3_cli_cmd_head[i] = cmd->next; + kfree(cmd); + } + } +} + +int ps3cmd_init(void) +{ + int err; + + if (misc_registered != 0) + return -EBUSY; + + ps3_atomic_set(&dev_opened, 0); + mutex_init(&ps3_cli_mutex); + + ps3_cli_device.minor = ps3_cli_minor_get(); + err = misc_register(&ps3_cli_device); + if (err) { + pr_warn("Couldn't initialize miscdevice /dev/ps3stor_cli.\n"); + return err; + } + misc_registered = 1; + ps3stor_cli_register(ps3_cli_help, "help", + "show this help information"); + return 0; +} + +void ps3cmd_exit(void) +{ + if (!misc_registered) + return; + misc_deregister(&ps3_cli_device); + ps3_free_cmds(); + misc_registered = 0; + mutex_destroy(&ps3_cli_mutex); +} diff --git a/drivers/scsi/linkdata/ps3stor/linux/ps3_cli.h b/drivers/scsi/linkdata/ps3stor/linux/ps3_cli.h new file mode 100644 index 0000000000000000000000000000000000000000..c0bfd99122f9829d1fb8ddca150f665fa15b14de --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/linux/ps3_cli.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _PS3_CLI_H_ +#define _PS3_CLI_H_ + +#define PS3_CLI_INPUT_LEN 2048 +#define PS3_CLI_OUTPUT_MAX (16 * 1024 * 1024) +#define PS3_CLI_OUTLINE_LEN 4096 +#define PS3_CLI_HELP_LEN 256 +#define PS3_CLI_CMD_MAXLEN 32 +#define PS3_MAX_ARGV 64 + +int ps3stor_cli_register(void (*func)(int argc, char *argv[]), const char *cmd_str, + const char *help); + +int ps3stor_cli_printf(const char *fmt, ...); + +int ps3cmd_init(void); + +void ps3cmd_exit(void); + + +#endif diff --git a/drivers/scsi/linkdata/ps3stor/linux/ps3_cli_debug.c b/drivers/scsi/linkdata/ps3stor/linux/ps3_cli_debug.c new file mode 100644 index 0000000000000000000000000000000000000000..b7b7c076ba876e1692b6f43564a8b8f4f0be7ce6 --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/linux/ps3_cli_debug.c @@ -0,0 +1,4023 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ps3_cli_debug.h" +#include "ps3_cli.h" +#include "ps3_driver_log.h" +#include "ps3_ioc_manager.h" +#include "ps3_ioc_state.h" +#include "ps3_event.h" +#include "ps3_scsi_cmd_err.h" +#include "ps3_cmd_statistics.h" +#include "ps3_scsih.h" +#include "ps3_util.h" +#include "ps3_dump.h" +#include "ps3_recovery.h" +#include "ps3_r1x_write_lock.h" +#include "ps3_irq.h" +#include "ps3_cmd_complete.h" +#include "ps3_qos.h" +static void ps3_cli_host_and_instance_ls(int argc, char *argv[]); +static void ps3_cli_register_rw(int argc, char *argv[]); +static void ps3_cli_register_dump(int argc, char *argv[]); +static void ps3_cli_init_frame_dump(int argc, char *argv[]); +static void ps3_cli_event_subscirbe_info_dump(int argc, char *argv[]); +static void ps3_cli_dev_mgr_cli_base_dump(int argc, char *argv[]); +static void ps3_cli_dev_mgr_cli_detail_dump(int argc, char *argv[]); +static void ps3_cli_reply_fifo_dump(int argc, char *argv[]); +static void ps3_cli_cmd_dump(int argc, char *argv[]); +static void ps3_cli_event_delay_set(int argc, char *argv[]); +static void ps3_cli_crashdump_set(int argc, char *argv[]); +static void ps3_cli_force_to_stop(int argc, char *argv[]); +static void ps3_io_statis_dump_cli_cb(int argc, char *argv[]); +static void ps3_io_statis_clear_cli_cb(int argc, char *argv[]); +static void ps3_debug_mem_rw_cli_cb(int argc, char *argv[]); +static void ps3_debug_mem_para_cli_cb(int argc, char *argv[]); +static void ps3_scsi_device_lookup_cb(int argc, char *argv[]); +static void ps3_hard_reset_cb(int argc, char *argv[]); +static void ps3_soc_halt_cb(int argc, char *argv[]); +static void ps3_cmd_stat_switch_store_cb(int argc, char *argv[]); +static void ps3_stat_total_show_cb(int argc, char *argv[]); +static void ps3_stat_buf_clr_cb(int argc, char *argv[]); +static void ps3_stat_interval_show_cb(int argc, char *argv[]); +static void ps3_stat_interval_store_cb(int argc, char *argv[]); +static void ps3_stat_inc_show_cb(int argc, char *argv[]); +static void ps3_cmd_stat_switch_show_cb(int argc, char *argv[]); +static void ps3_reply_fifo_reset_cb(int argc, char *argv[]); +static void ps3_cli_remove_host_force(int argc, char *argv[]); +static void ps3_hardreset_cnt_clear_cli_cb(int argc, char *argv[]); +static void ps3_hardreset_cnt_show_cli_cb(int argc, char *argv[]); +static void ps3_cli_ramfs_test_set(int argc, char *argv[]); +static void ps3_no_wait_cli_cmd(int argc, char *argv[]); +static void ps3_cli_qos_info(int argc, char *argv[]); +static void ps3_cli_special_log(int argc, char *argv[]); + +static unsigned char ps3_cli_wait_flag = PS3_FALSE; +struct ps3_cli_debug_cmd { + void (*func)(int argc, char *argv[]); + const char *func_name; + const char *help; +}; + +static struct ps3_cli_debug_cmd g_ps3_cli_debug_cmd_table[] = { + { ps3_cli_host_and_instance_ls, "ls", + "ls option[host][instance][all]" }, + { ps3_cli_register_rw, "register_rw", + "register_rw host_no xxx(host num) name/offset xxx(name/offset) read/write xxx(value)" }, + { ps3_cli_register_dump, "register_dump", + "register_dump host_no xxx(host number)" }, + { ps3_cli_init_frame_dump, "init_frame_dump", + "init_frame_dump host_no xxx(host number)" }, + { ps3_cli_event_subscirbe_info_dump, "event_subscribe_dump", + "event_subscribe_dump host_no xxx(host number)" }, + { ps3_cli_event_delay_set, "event_delay_set", + "event_delay_set host_no xxx(host number) delay xxx(seconds)" }, + { ps3_cli_crashdump_set, "crashdump", + "crashdump host_no xxx(host number) wait xxx(seconds)" }, + { ps3_cli_dev_mgr_cli_base_dump, "dmbi", + "device manager base information: dmbi option[vd][pd][all]" }, + { ps3_cli_dev_mgr_cli_detail_dump, "dmdi", + "device manager detail information: dmdi " }, + { ps3_cli_reply_fifo_dump, "reply_fifo_dump", + "reply_fifo_dump host_no xxx(host number) isr_sn xxx(isr SN)\n" + "\t\toption[start_idx xxx default: last_reply_idx][count xxx default: 100]" }, + { ps3_cli_cmd_dump, "cmd_dump", + "cmd_dump host_no xxx(host number) cmd_frame_id xxx(cmd_frame_id)" }, + { ps3_io_statis_dump_cli_cb, "show", + "show io statis (detail|summary)" }, + { ps3_io_statis_clear_cli_cb, "clear", + "clear io statis " }, + { ps3_cli_force_to_stop, "force_to_stop", "force_to_stop" }, + { ps3_debug_mem_para_cli_cb, "debug_mem_para_dump", + "debug_mem_para_dump host_no xxx(host no)" }, + { ps3_debug_mem_rw_cli_cb, "debug_mem", + "debug_mem host_no (host no) entry_index(0--max entry cnt) dir(0/1 r/w) length(Bytes)" }, + { ps3_scsi_device_lookup_cb, "scsi_device_dump", + "scsi_device_dump host_no xxx(host number)" }, + { ps3_hard_reset_cb, "hard_reset", + "hard_reset host_no xxx(host number)" }, + { ps3_soc_halt_cb, "soc_halt", "soc_halt host_no xxx(host number)" }, + { ps3_cmd_stat_switch_store_cb, "cmd_stat_switch_store", + "cmd_stat_switch_store host_no xxx(host number) value xxx mask xxx\n" + "\t\t[bit0:OUTSTAND_SWITCH_OPEN, bit1:INC_SWITCH_OPEN,\n" + "\t\tbit2:LOG_SWITCH_OPEN, bit3:DEV_SWITCH_OPEN]" }, + { ps3_cmd_stat_switch_show_cb, "cmd_stat_switch_show", + "cmd_stat_switch_show host_no xxx" }, + { ps3_stat_total_show_cb, "cmd_stat_dump", + "cmd_stat_dump host_no xxx" }, + { ps3_stat_inc_show_cb, "inc_stat_dump", "inc_stat_dump host_no xxx" }, + { ps3_stat_buf_clr_cb, "cmd_stat_clear", "cmd_stat_clear host_no xxx" }, + { ps3_stat_interval_show_cb, "cmd_stat_interval_show", + "cmd_stat_interval_show host_no xxx" }, + { ps3_stat_interval_store_cb, "cmd_stat_interval_store", + "cmd_stat_interval_store host_no xxx interval xxx(ms)" }, + { ps3_reply_fifo_reset_cb, "ps3_all_reply_fifo_reset", + "ps3_all_reply_fifo_reset host_no xxx" }, + { ps3_cli_remove_host_force, "remove_host", + "remove one host and free resource: (host number)" }, + { ps3_hardreset_cnt_clear_cli_cb, "hardreset_cnt_clear", + "hardreset_cnt_clear host_no xxx(host number)" }, + { ps3_hardreset_cnt_show_cli_cb, "hardreset_cnt_show", + "hardreset_cnt_show host_no xxx(host number)" }, + { ps3_cli_ramfs_test_set, "ramfs_test_set", + "set or clear filesystem type to ramfs: (0: clear, 1: set)" }, + { ps3_no_wait_cli_cmd, "ps3_no_wait_cli_cmd", + "clean_wait_cli_cmd flag xxx" }, + { ps3_cli_qos_info, "qos_dump", + "qos_dump host_no xxx(host number) pd/vd xxx(id) | all" }, + { ps3_cli_special_log, "spe_log", + "spe_log host_no xxx(host number) set xxx(0/1)" }, +}; + +#define DRIVER_REGISTER_DEBUG_SIMULATOR +#define REG_OFFSET(member) (offsetof(struct Ps3Fifo, member)) +struct ps3_reg_dump_desc { + const char *reg_name; + unsigned int reg_offset; + unsigned int reg_cnt; + unsigned char is_readable; +}; + +static struct ps3_reg_dump_desc g_reg_table[] = { + { "ps3RequestQueue", PS3_REG_OFFSET_REQUEST_QUE, 1, PS3_TRUE }, + { "ps3Doorbell", PS3_REG_OFFSET_DOORBELL, 1, PS3_FALSE }, + { "ps3DoorbellIrqClear", PS3_REG_OFFSET_DOORBELL_IRQ_CLEAR, 1, + PS3_TRUE }, + { "ps3DoorbellIrqMask", PS3_REG_OFFSET_DOORBELL_IRQ_MASK, 1, PS3_TRUE }, + { "ps3IrqControl", PS3_REG_OFFSET_IRQ_CONTROL, 1, PS3_TRUE }, + { "ps3SoftresetKey", PS3_REG_OFFSET_SOFTRESET_KEY, 1, PS3_TRUE }, + { "ps3SoftresetState", PS3_REG_OFFSET_SOFTRESET_STATE, 1, PS3_TRUE }, + { "ps3Softreset", PS3_REG_OFFSET_SOFTRESET, 1, PS3_TRUE }, + { "ps3SoftresetIrqClear", PS3_REG_OFFSET_SOFTRESET_IRQ_CLEAR, 1, + PS3_TRUE }, + { "ps3SoftresetIrqMask", PS3_REG_OFFSET_SOFTRESET_IRQ_MASK, 1, + PS3_TRUE }, + { "ps3SoftresetKeyShiftRegLow", + PS3_REG_OFFSET_SOFTRESET_KEY_SHIFT_REG_LOW, 1, PS3_TRUE }, + { "ps3SoftresetKeyShiftRegHigh", + PS3_REG_OFFSET_SOFTRESET_KEY_SHIFT_REG_HIGH, 1, PS3_TRUE }, + { "ps3SoftresetTimeCnt", PS3_REG_OFFSET_SOFTRESET_TIME_CNT, 1, + PS3_TRUE }, + { "ps3SoftresetTimeOutEn", PS3_REG_OFFSET_SOFTRESET_TIME_OUT_CNT, 1, + PS3_TRUE }, + { "ps3HardresetKey", PS3_REG_OFFSET_HARDRESET_KEY, 1, PS3_TRUE }, + { "ps3HardresetState", PS3_REG_OFFSET_HARDRESET_STATE, 1, PS3_TRUE }, + { "ps3Hardreset", PS3_REG_OFFSET_HARDRESET, 1, PS3_TRUE }, + { "ps3HardresetKeyShiftRegLow", + PS3_REG_OFFSET_HARDRESET_KEY_SHIFT_REG_LOW, 1, PS3_TRUE }, + { "ps3HardresetKeyShiftRegHigh", + PS3_REG_OFFSET_HARDRESET_KEY_SHIFT_REG_HIGH, 1, PS3_TRUE }, + { "ps3HardresetTimeCnt", PS3_REG_OFFSET_HARDRESET_TIME_CNT, 1, + PS3_TRUE }, + { "ps3HardresetTimeOutEn", PS3_REG_OFFSET_HARDRESET_TIME_OUT_CNT, 1, + PS3_TRUE }, + { "ps3KeyGapCfg", PS3_REG_OFFSET_KEY_GAP_CFG, 1, PS3_TRUE }, + { "ps3HardresetIrqClear", PS3_REG_OFFSET_HARDRESET_IRQ_CLEAR, 1, + PS3_TRUE }, + { "ps3HardresetIrqMask", PS3_REG_OFFSET_HARDRESET_IRQ_MASK, 1, + PS3_TRUE }, + { "ps3SocFwState", PS3_REG_OFFSET_SOC_FW_STATE, 1, PS3_TRUE }, + { "ps3MaxFwCmd", PS3_REG_OFFSET_MAX_FW_CMD, 1, PS3_TRUE }, + { "ps3MaxChainSize", PS3_REG_OFFSET_MAX_CHAIN_SIZE, 1, PS3_TRUE }, + { "ps3MaxVdInfoSize", PS3_REG_OFFSET_MAX_VD_INFO_SIZE, 1, PS3_TRUE }, + { "ps3MaxNvmePageSize", PS3_REG_OFFSET_MAX_NVME_PAGE_SIZE, 1, + PS3_TRUE }, + { "ps3FeatureSupport", PS3_REG_OFFSET_FEATURE_SUPPORT, 1, PS3_TRUE }, + { "ps3FirmwareVersion", PS3_REG_OFFSET_FIRMWARE_VERSION, 1, PS3_TRUE }, + { "ps3MaxReplyque", PS3_REG_OFFSET_REPLY_QUE, 1, PS3_TRUE }, + { "ps3HardwareVersion", PS3_REG_OFFSET_HARDWARE_VERSION, 1, PS3_TRUE }, + { "ps3MgrQueueDepth", PS3_REG_OFFSET_MGR_QUEUE_DEPTH, 1, PS3_TRUE }, + { "ps3CmdQueueDepth", PS3_REG_OFFSET_CMD_QUEUE_DEPTH, 1, PS3_TRUE }, + { "ps3TfifoDepth", PS3_REG_OFFSET_TFIFO_DEPTH, 1, PS3_TRUE }, + { "ps3MaxSecR1xCmds", PS3_REG_OFFSET_MAX_SEC_R1X_CMDS, 1, PS3_TRUE }, + { "ps3HilAdvice2directCnt0", PS3_REG_OFFSET_HIL_ADVICE2DIRECT_CNT0, 1, + PS3_TRUE }, + { "ps3HilAdvice2directCnt1", PS3_REG_OFFSET_HIL_ADVICE2DIRECT_CNT1, 1, + PS3_TRUE }, + { "ps3HilAdvice2directCnt2", PS3_REG_OFFSET_HIL_ADVICE2DIRECT_CNT2, 1, + PS3_TRUE }, + { "ps3HilAdvice2directCnt3", PS3_REG_OFFSET_HIL_ADVICE2DIRECT_CNT3, 1, + PS3_TRUE }, + { "ps3HilAdvice2directCntAll", PS3_REG_OFFSET_HIL_ADVICE2DIRECT_CNT_ALL, + 1, PS3_TRUE }, + { "ps3IrqStatusRpt", PS3_REG_OFFSET_IRQ_STATUS_RPT, 1, PS3_TRUE }, + { "ps3DumpCtrl", PS3_REG_OFFSET_DUMP_CTRL, 1, PS3_FALSE }, + { "ps3DumpCtrlIrqClear", PS3_REG_OFFSET_DUMP_CTRl_IRQ_CLEAR, 1, + PS3_TRUE }, + { "ps3DumpCtrlIrqMask", PS3_REG_OFFSET_DUMP_CTRl_IRQ_MASK, 1, + PS3_TRUE }, + { "ps3DumpStatus", PS3_REG_OFFSET_DUMP_STATUS, 1, PS3_TRUE }, + { "ps3DumpDataSize", PS3_REG_OFFSET_DUMP_DATA_SIZE, 1, PS3_TRUE }, + { "ps3CmdTrigger", PS3_REG_OFFSET_CMD_TRIGGER, 1, PS3_TRUE }, + { "ps3CmdTriggerIrqClear", PS3_REG_OFFSET_CMD_TRIGGER_IRQ_CLEAR, 1, + PS3_TRUE }, + { "ps3CmdTriggerIrqMask", PS3_REG_OFFSET_CMD_TRIGGER_IRQ_MASK, 1, + PS3_TRUE }, + { "ps3RegCmdState", PS3_REG_OFFSET_REG_CMD_STATE, 1, PS3_TRUE }, + { "ps3SoftresetCounter", PS3_REG_OFFSET_SOFTRESET_COUNTER, 1, + PS3_TRUE }, + { "ps3Debug0", PS3_REG_OFFSET_DEBUG0, 1, PS3_TRUE }, + { "ps3Debug0IrqClear", PS3_REG_OFFSET_DEBUG0_IRQ_CLEAR, 1, PS3_TRUE }, + { "ps3Debug0IrqMask", PS3_REG_OFFSET_DEBUG0_IRQ_MASK, 1, PS3_TRUE }, + { "ps3Debug1", PS3_REG_OFFSET_DEBUG1, 1, PS3_TRUE }, + { "ps3Debug1IrqClear", PS3_REG_OFFSET_DEBUG1_IRQ_CLEAR, 1, PS3_TRUE }, + { "ps3Debug1IrqMask", PS3_REG_OFFSET_DEBUG1_IRQ_MASK, 1, PS3_TRUE }, + { "ps3Debug2", PS3_REG_OFFSET_DEBUG2, 1, PS3_TRUE }, + { "ps3Debug2IrqClear", PS3_REG_OFFSET_DEBUG2_IRQ_CLEAR, 1, PS3_TRUE }, + { "ps3Debug2IrqMask", PS3_REG_OFFSET_DEBUG2_IRQ_MASK, 1, PS3_TRUE }, + { "ps3Debug3", PS3_REG_OFFSET_DEBUG3, 1, PS3_TRUE }, + { "ps3Debug3IrqClear", PS3_REG_OFFSET_DEBUG3_IRQ_CLEAR, 1, PS3_TRUE }, + { "ps3Debug3IrqMask", PS3_REG_OFFSET_DEBUG3_IRQ_MASK, 1, PS3_TRUE }, + { "ps3Debug4", PS3_REG_OFFSET_DEBUG4, 1, PS3_TRUE }, + { "ps3Debug4IrqMask", PS3_REG_OFFSET_DEBUG4_IRQ_CLEAR, 1, PS3_TRUE }, + { "ps3Debug4IrqMask", PS3_REG_OFFSET_DEBUG4_IRQ_MASK, 1, PS3_TRUE }, + { "ps3Debug5", PS3_REG_OFFSET_DEBUG5, 1, PS3_TRUE }, + { "ps3Debug6", PS3_REG_OFFSET_DEBUG6, 1, PS3_TRUE }, + { "ps3Debug7", PS3_REG_OFFSET_DEBUG7, 1, PS3_TRUE }, + { "ps3Debug8", PS3_REG_OFFSET_DEBUG8, 1, PS3_TRUE }, + { "ps3Debug9", PS3_REG_OFFSET_DEBUG9, 1, PS3_TRUE }, + { "ps3Debug10", PS3_REG_OFFSET_DEBUG10, 1, PS3_TRUE }, + { "ps3Debug11", PS3_REG_OFFSET_DEBUG11, 1, PS3_TRUE }, + { "ps3Debug12", PS3_REG_OFFSET_DEBUG12, 1, PS3_TRUE }, + { "ps3SessioncmdAddr", PS3_REG_SESSION_ADDR, 1, PS3_TRUE }, + { "ps3SessioncmdAddrIrqClear", PS3_REG_SESSION_ADDR_IRQ_CLEAR, 1, + PS3_TRUE }, + { "ps3SessioncmdAddrIrqMask", PS3_REG_SESSION_ADDR_IRQ_MASK, 1, + PS3_TRUE }, +}; + +static void ps3_cli_register_dump(int argc, char *argv[]) +{ + struct ps3_instance *instance = NULL; + char *buf = NULL; + unsigned short host_no = 0; + int ret = 0; + + buf = kzalloc(PAGE_SIZE, GFP_KERNEL); + if (buf == NULL) { + ps3stor_cli_printf( + "malloc buf failed for register dump cli!\n"); + goto l_malloc_failed; + } + + if (argc < 3) { + ps3stor_cli_printf("Too few args for register dump cli cmd!\n"); + goto l_out; + } + + if (strcmp(argv[1], "host_no") != 0) { + ps3stor_cli_printf("host_no is needed!\n"); + goto l_out; + } + + ret = kstrtou16(argv[2], 0, &host_no); + if (ret != 0) { + ps3stor_cli_printf( + "Can not parse host_no for register dump cmd!\n"); + goto l_out; + } + + instance = ps3_instance_lookup(host_no); + if (instance == NULL) { + ps3stor_cli_printf( + "Invalid host_no %d for register dump cmd!\n", host_no); + goto l_out; + } + + if (instance->reg_set == NULL) { + ps3stor_cli_printf("ioc reg_set has not been alloc!\n"); + goto l_out; + } + + (void)ps3_ioc_reg_dump(instance, buf); + ps3stor_cli_printf(buf); + ps3stor_cli_printf("\n"); + +l_out: + kfree(buf); +l_malloc_failed: + return; +} + +static void ps3_cli_init_frame_dump(int argc, char *argv[]) +{ + struct ps3_instance *instance = NULL; + struct ps3_cmd_context *cmd_context = NULL; + struct PS3InitReqFrame *init_frame_msg = NULL; + unsigned short host_no = 0; + int ret = 0; + + if (argc < 3) { + ps3stor_cli_printf( + "Too few args for init_frame_dump cli cmd!\n"); + goto l_out; + } + + if (strcmp(argv[1], "host_no") != 0) { + ps3stor_cli_printf("host_no is needed!\n"); + goto l_out; + } + + ret = kstrtou16(argv[2], 0, &host_no); + if (ret != 0) { + ps3stor_cli_printf( + "Can not parse host_no for init_frame_dump cmd!\n"); + goto l_out; + } + + instance = ps3_instance_lookup(host_no); + if (instance == NULL) { + ps3stor_cli_printf( + "Invalid host_no %d for init_frame_dump cmd!\n", + host_no); + goto l_out; + } + + cmd_context = &instance->cmd_context; + init_frame_msg = (struct PS3InitReqFrame *)cmd_context->init_frame_buf; + + if (init_frame_msg == NULL) { + ps3stor_cli_printf("init frame has not been alloc!\n"); + goto l_out; + } + + ps3stor_cli_printf("init frame:\n"); + ps3stor_cli_printf("reqHead.cmdType:\t%u\n", + init_frame_msg->reqHead.cmdType); + ps3stor_cli_printf("length:\t%u\n", init_frame_msg->length); + ps3stor_cli_printf("operater:\t%u\n", init_frame_msg->operater); + ps3stor_cli_printf("pageSize:\t%u\n", init_frame_msg->pageSize); + ps3stor_cli_printf("pciIrqType:\t%u\n", init_frame_msg->pciIrqType); + ps3stor_cli_printf("msixVector:\t%u\n", init_frame_msg->msixVector); + ps3stor_cli_printf("timeStamp:\t%llu\n", init_frame_msg->timeStamp); + ps3stor_cli_printf("reqFrameBufBaseAddr:\t0x%llx\n", + init_frame_msg->reqFrameBufBaseAddr); + ps3stor_cli_printf("replyFifoDescBaseAddr:\t0x%llx\n", + init_frame_msg->replyFifoDescBaseAddr); + ps3stor_cli_printf("respFrameBaseAddr:\t0x%llx\n", + init_frame_msg->respFrameBaseAddr); + ps3stor_cli_printf("eventTypeMap:\t0x%08x\n", + init_frame_msg->eventTypeMap); + ps3stor_cli_printf("reqFrameMaxNum:\t%u\n", + init_frame_msg->reqFrameMaxNum); + ps3stor_cli_printf("respFrameMaxNum:\t%u\n", + init_frame_msg->respFrameMaxNum); + ps3stor_cli_printf("bufSizePerRespFrame:\t%u\n", + init_frame_msg->bufSizePerRespFrame); + ps3stor_cli_printf("hilMode:\t%u\n", init_frame_msg->hilMode); + ps3stor_cli_printf("systemInfoBufAddr:\t0x%llx\n", + init_frame_msg->systemInfoBufAddr); + ps3stor_cli_printf("debugMemAddr:\t0x%llx\n", + init_frame_msg->debugMemArrayAddr); + ps3stor_cli_printf("debugMemSize:\t%u\n", + init_frame_msg->debugMemArrayNum); + ps3stor_cli_printf("filterTableAddr:\t0x%llx\n", + init_frame_msg->filterTableAddr); + ps3stor_cli_printf("filterTableLen:\t%u\n", + init_frame_msg->filterTableLen); + + ps3stor_cli_printf("respStatus:\t0x%08x\n", init_frame_msg->respStatus); + +l_out: + return; +} + +static int ps3_register_offset_lookup(const char *reg_name_string, + unsigned int *reg_offset) +{ + unsigned int idx = 0; + struct ps3_reg_dump_desc *reg_desc = NULL; + + for (idx = 0; idx < ARRAY_SIZE(g_reg_table); idx++) { + reg_desc = &g_reg_table[idx]; + + if (reg_desc->reg_cnt != 1) { + LOG_DEBUG("Reg %s is not a single register!\n", + reg_desc->reg_name); + continue; + } + + if (strcmp(reg_name_string, reg_desc->reg_name) == 0) { + *reg_offset = reg_desc->reg_offset; + return PS3_SUCCESS; + } + } + + return -PS3_FAILED; +} + +static void ps3_register_read_write(struct ps3_instance *instance, int argc, + char *argv[]) +{ + const char *reg_name_offset_string = argv[3]; + const char *reg_loc_string = argv[4]; + const char *state_ops_string = argv[5]; + unsigned int reg_value = 0; + unsigned int reg_offset = 0; + int ret = 0; + unsigned long long value; + + if (strcmp(reg_name_offset_string, "offset") == 0) + ret = kstrtouint(reg_loc_string, 0, ®_offset); + else + ret = ps3_register_offset_lookup(reg_loc_string, ®_offset); + + if (ret != PS3_SUCCESS) { + ps3stor_cli_printf("Reg %s not supported to read and write!\n", + reg_loc_string); + goto l_out; + } + + if (strcmp(state_ops_string, "read") == 0) { + PS3_IOC_REG_READ_OFFSET_WITCH_CHECK(instance, reg_offset, + value); + ps3stor_cli_printf("0x%llx\n", value); + } else if (strcmp(state_ops_string, "write") == 0) { + if (argc < 7) { + ps3stor_cli_printf("No reg value for %s write cmd!\n", + reg_loc_string); + goto l_out; + } + + ret = kstrtouint(argv[6], 0, ®_value); + if (ret != 0) { + ps3stor_cli_printf( + "Invalid reg value for %s write cmd!\n", + reg_loc_string); + goto l_out; + } + + PS3_IOC_REG_WRITE_OFFSET_WITH_CHECK(instance, reg_offset, + (unsigned long long)reg_value); + ps3stor_cli_printf("Write 0x%x to %s!\n", reg_value, + reg_loc_string); + } else { + ps3stor_cli_printf("Invalid ops for %s cmd!\n", reg_loc_string); + } + +l_out: + return; +} + +static void ps3_cli_register_rw(int argc, char *argv[]) +{ + struct ps3_instance *instance = NULL; + unsigned short host_no = 0; + int ret = 0; + + if (argc < 6) { + ps3stor_cli_printf("Too few args for register cli cmd!\n"); + goto l_out; + } + + if (strcmp(argv[1], "host_no") != 0) { + ps3stor_cli_printf("host_no is needed!\n"); + goto l_out; + } + + ret = kstrtou16(argv[2], 0, &host_no); + if (ret != 0) { + ps3stor_cli_printf( + "Can not parse host_no for register cli cmd!\n"); + goto l_out; + } + + instance = ps3_instance_lookup(host_no); + if (instance == NULL) { + ps3stor_cli_printf("Invalid host_no %d for register cli cmd!\n", + host_no); + goto l_out; + } + + ps3_register_read_write(instance, argc, argv); +l_out: + return; +} + +ssize_t ps3_ioc_reg_dump(struct ps3_instance *instance, char *buf) +{ + ssize_t len = 0; + ssize_t total_len = PAGE_SIZE; + unsigned int idx = 0; + unsigned long long value = 0; + struct ps3_reg_dump_desc *reg_desc = NULL; + + for (idx = 0; idx < ARRAY_SIZE(g_reg_table); idx++) { + reg_desc = &g_reg_table[idx]; + +#ifndef DRIVER_REGISTER_DEBUG_SIMULATOR + if (!reg_desc->is_readable) + continue; +#endif + if (reg_desc->reg_cnt > 1) + continue; + + PS3_IOC_REG_READ_OFFSET_WITCH_CHECK( + instance, reg_desc->reg_offset, value); + len += snprintf(buf + len, total_len - len, "%s:\t0x%08llx\n", + reg_desc->reg_name, value); + } + + return len; +} + +static void ps3_dev_mgr_print_vd(struct PS3VDEntry *p_entry, unsigned char is_detail) +{ + unsigned char i = 0; + unsigned char j = 0; + + ps3stor_cli_printf( + "vd channel, id, virtDiskId: [%u:%u:%u], magicNum:%#x\n", + PS3_CHANNEL(&p_entry->diskPos), PS3_TARGET(&p_entry->diskPos), + PS3_VDID(&p_entry->diskPos), p_entry->diskPos.diskMagicNum); + ps3stor_cli_printf("vd dev_type: PS3_DEV_TYPE_VD\n"); + ps3stor_cli_printf("vd isHidden: %s\n", + (p_entry->isHidden) ? "true" : "false"); + ps3stor_cli_printf("vd accessPolicy: %s\n", + ps3_get_vd_access_plolicy_str( + (enum VDAccessPolicy)p_entry->accessPolicy)); + ps3stor_cli_printf("vd diskGrpId: %u\n", p_entry->diskGrpId); + + if (!is_detail) + return; + ps3stor_cli_printf("vd sectorSize: %u\n", p_entry->sectorSize); + ps3stor_cli_printf("vd stripeDataSize: %u\n", + p_entry->stripeDataSize); + ps3stor_cli_printf("vd physDrvCnt: %u\n", p_entry->physDrvCnt); + ps3stor_cli_printf("vd stripSize: %u\n", p_entry->stripSize); + ps3stor_cli_printf("vd isDirectEnable: %s\n", + (p_entry->isDirectEnable) ? "true" : "false"); + ps3stor_cli_printf("vd isNvme: %s\n", + (p_entry->isNvme) ? "true" : "false"); + ps3stor_cli_printf("vd isSsd: %s\n", + (p_entry->isSsd) ? "true" : "false"); + ps3stor_cli_printf("vd isWriteDirectEnable: %s\n", + (p_entry->isWriteDirectEnable) ? "true" : "false"); + ps3stor_cli_printf("vd raidLevel: %u\n", p_entry->raidLevel); + ps3stor_cli_printf("vd spanCount: %u\n", p_entry->spanCount); + ps3stor_cli_printf("vd diskState: %u\n", p_entry->diskState); + ps3stor_cli_printf("vd startLBA: %llu\n", p_entry->startLBA); + ps3stor_cli_printf("vd extentSize: %llu\n", p_entry->extentSize); + ps3stor_cli_printf("vd isTaskMgmtEnable: %u\n", + p_entry->isTaskMgmtEnable); + ps3stor_cli_printf("vd taskAbortTimeout: %u\n", + p_entry->taskAbortTimeout); + ps3stor_cli_printf("vd taskResetTimeout: %u\n", + p_entry->taskResetTimeout); + ps3stor_cli_printf("vd mapBlock: %llu\n", p_entry->mapBlock); + ps3stor_cli_printf("vd mapBlockVer: %u\n", p_entry->mapBlockVer); + ps3stor_cli_printf("vd maxIOSize: %u\n", p_entry->maxIOSize); + ps3stor_cli_printf("vd devQueDepth: %u\n", p_entry->devQueDepth); + ps3stor_cli_printf("vd dmaAddrAlignShift: %u\n", + p_entry->dmaAddrAlignShift); + ps3stor_cli_printf("vd dmaLenAlignShift: %u\n", + p_entry->dmaLenAlignShift); + ps3stor_cli_printf("vd capacity(sector): %llu\n", p_entry->capacity); + ps3stor_cli_printf("vd bdev_bdi_cap: %u\n", p_entry->bdev_bdi_cap); + ps3stor_cli_printf("vd umapBlkDescCnt: %u\n", p_entry->umapBlkDescCnt); + ps3stor_cli_printf("vd umapNumblk: %u\n", p_entry->umapNumblk); + ps3stor_cli_printf("vd virtDiskSeq: %llu\n", p_entry->virtDiskSeq); + ps3stor_cli_printf("vd normalQuota:%u directQuota:%u\n", + p_entry->normalQuota, p_entry->directQuota); + ps3stor_cli_printf("vd dev_busy_scale: %u\n", p_entry->dev_busy_scale); + + for (i = 0; i < p_entry->spanCount; i++) { + ps3stor_cli_printf(" span[%u].spanStripeDataSize: %u\n", i, + p_entry->span[i].spanStripeDataSize); + ps3stor_cli_printf(" span[%u].spanState: %u\n", i, + p_entry->span[i].spanState); + ps3stor_cli_printf(" span[%u].spanPdNum: %u\n", i, + p_entry->span[i].spanPdNum); + + for (j = 0; j < p_entry->span[i].spanPdNum; j++) { + ps3stor_cli_printf( + " span[%u].extent[%u] - member pd:[%u:%u:%u]\n", + i, j, + p_entry->span[i] + .extent[j] + .phyDiskID.ps3Dev.softChan, + p_entry->span[i] + .extent[j] + .phyDiskID.ps3Dev.devID, + p_entry->span[i] + .extent[j] + .phyDiskID.ps3Dev.phyDiskID); + ps3stor_cli_printf( + " span[%u].extent[%u].state: %u\n", i, j, + p_entry->span[i].extent[j].state); + } + } +} + +static void ps3_dev_mgr_show_base_vd(struct ps3_instance *instance) +{ + unsigned short i = 0; + unsigned short j = 0; + unsigned char chan_id = 0; + struct ps3_dev_context *p_dev_ctx = &instance->dev_context; + struct ps3_channel *vd_chan = p_dev_ctx->channel_vd; + unsigned char vd_table_idx = p_dev_ctx->vd_table_idx & 1; + struct ps3_vd_table *p_table = &p_dev_ctx->vd_table[vd_table_idx]; + struct PS3VDEntry *p_entries = + p_dev_ctx->vd_entries_array[vd_table_idx]; + unsigned short vd_idx = PS3_INVALID_DEV_ID; + unsigned short virtDiskIdx = PS3_INVALID_DEV_ID; + + ps3stor_cli_printf( + "\n===========start print VD base information=========\n"); + for (i = 0; i < p_dev_ctx->vd_channel_count; i++) { + chan_id = vd_chan[i].channel; + for (j = 0; j < vd_chan[i].max_dev_num; j++) { + vd_idx = p_table->vd_idxs[chan_id][j]; + virtDiskIdx = get_offset_of_vdid( + PS3_VDID_OFFSET(instance), vd_idx); + if (vd_idx != PS3_INVALID_DEV_ID) { + ps3_dev_mgr_print_vd(&p_entries[virtDiskIdx], + PS3_FALSE); + } + } + } + ps3stor_cli_printf( + "\n============end print VD base information==========\n"); +} + +static void ps3_dev_mgr_print_pd(struct ps3_pd_entry *p_entry, unsigned char is_detail, + unsigned char is_raid) +{ + ps3stor_cli_printf( + "pd channel, id, pdFlatId: [%u:%u:%u], magicNum: %#x\n", + PS3_CHANNEL(&p_entry->disk_pos), PS3_TARGET(&p_entry->disk_pos), + PS3_PDID(&p_entry->disk_pos), p_entry->disk_pos.diskMagicNum); + ps3stor_cli_printf("pd state: %s\n", + getDeviceStateName((enum DeviceState)p_entry->state)); + ps3stor_cli_printf("pd dev_type: %s\n", + namePS3DevType((enum PS3DevType)p_entry->dev_type)); + ps3stor_cli_printf("pd config_flag: %s\n", + getPdStateName((enum MicPdState)p_entry->config_flag, + is_raid)); + ps3stor_cli_printf("pd pd_flags: 0x%02x\n", + p_entry->pd_flags); + ps3stor_cli_printf("pd support_ncq: %d\n", + p_entry->support_ncq); + + if (is_detail) { + ps3stor_cli_printf("pd RWCT: %u\n", + p_entry->RWCT); + ps3stor_cli_printf("pd scsi_interface_type: %u\n", + p_entry->scsi_interface_type); + ps3stor_cli_printf("pd task_abort_timeout: %u\n", + p_entry->task_abort_timeout); + ps3stor_cli_printf("pd task_reset_timeout: %u\n", + p_entry->task_reset_timeout); + ps3stor_cli_printf("pd max_io_size: %u\n", + p_entry->max_io_size); + ps3stor_cli_printf("pd is_direct_disable: %s\n", + (p_entry->is_direct_disable) ? "true" : + "false"); + ps3stor_cli_printf("pd dev_queue_depth: %u\n", + p_entry->dev_queue_depth); + ps3stor_cli_printf("pd sector_size: %u\n", + p_entry->sector_size); + ps3stor_cli_printf("pd encl_id: %u\n", + p_entry->encl_id); + ps3stor_cli_printf("pd phy_id: %u\n", + p_entry->phy_id); + ps3stor_cli_printf("pd dma_addr_alignment: %u\n", + p_entry->dma_addr_alignment); + ps3stor_cli_printf("pd dma_len_alignment: %u\n", + p_entry->dma_len_alignment); + ps3stor_cli_printf("pd protect: %u\n", + p_entry->protect); + ps3stor_cli_printf("qos pd quota:normal[%u] direct[%u]\n", + p_entry->normal_quota, + p_entry->direct_quota); + } +} + +static void ps3_dev_mgr_show_base_pd(struct ps3_instance *instance) +{ + unsigned short i = 0; + unsigned short j = 0; + unsigned char chan_id = 0; + struct ps3_dev_context *p_dev_ctx = &instance->dev_context; + struct ps3_channel *pd_chan = p_dev_ctx->channel_pd; + struct ps3_pd_table *p_table = &p_dev_ctx->pd_table; + unsigned short pd_idx = PS3_INVALID_DEV_ID; + + ps3stor_cli_printf( + "\n===========start print PD base information=========\n"); + for (i = 0; i < p_dev_ctx->pd_channel_count; i++) { + chan_id = pd_chan[i].channel; + for (j = 0; j < pd_chan[i].max_dev_num; j++) { + pd_idx = p_table->pd_idxs[chan_id][j]; + if (pd_idx != PS3_INVALID_DEV_ID) { + ps3_dev_mgr_print_pd( + &p_dev_ctx->pd_entries_array[pd_idx], + PS3_DRV_FALSE, instance->is_raid); + } + } + } + ps3stor_cli_printf( + "\n============end print PD base information==========\n"); +} + +static void ps3_cli_dev_mgr_cli_base_dump(int argc, char *argv[]) +{ + int ret = 0; + unsigned short host_no = 0; + struct ps3_instance *instance = NULL; + + if (argc < 3) { + ps3stor_cli_printf("Too few args for dmbi!\n"); + goto l_out; + } + + ret = kstrtou16(argv[1], 0, &host_no); + if (ret != 0) { + ps3stor_cli_printf( + "Can not parse host_no \"%s\" for dmbi cmd!\n", + argv[1]); + goto l_out; + } + + instance = ps3_instance_lookup(host_no); + if (instance == NULL) { + ps3stor_cli_printf("Invalid host_no %d for dmbi cmd!\n", + host_no); + goto l_out; + } + + if (strcmp(argv[2], "vd") == 0) { + ps3_dev_mgr_show_base_vd(instance); + goto l_out; + } else if (strcmp(argv[2], "pd") == 0) { + ps3_dev_mgr_show_base_pd(instance); + goto l_out; + } else if (strcmp(argv[2], "all") == 0) { + ps3_dev_mgr_show_base_vd(instance); + ps3_dev_mgr_show_base_pd(instance); + goto l_out; + } else { + ps3stor_cli_printf("Invalid subtype \"%s\" for dmbi cmd!\n", + argv[2]); + } +l_out: + return; +} + +static void ps3_dev_mgr_show_detail_info(struct ps3_instance *instance, + unsigned short channel, unsigned short id) +{ + struct ps3_dev_context *p_dev_ctx = &instance->dev_context; + struct ps3_pd_table *p_pd_table = &p_dev_ctx->pd_table; + unsigned char vd_table_idx = p_dev_ctx->vd_table_idx & 1; + struct ps3_vd_table *p_vd_table = &p_dev_ctx->vd_table[vd_table_idx]; + struct PS3VDEntry *p_vd_array = + p_dev_ctx->vd_entries_array[vd_table_idx]; + unsigned short disk_idx = 0; + unsigned short virtDiskIdx = PS3_INVALID_DEV_ID; + + if (PS3_IS_VD_CHANNEL(instance, channel)) { + if (p_vd_table->vd_idxs[channel] == NULL) { + ps3stor_cli_printf( + "==error==invalid channel parameter: %d\n", + channel); + goto l_out; + } + + if (id >= p_dev_ctx->max_dev_in_channel[channel]) { + ps3stor_cli_printf( + "==error==invalid id parameter: %d\n", id); + goto l_out; + } + + disk_idx = p_vd_table->vd_idxs[channel][id]; + virtDiskIdx = + get_offset_of_vdid(PS3_VDID_OFFSET(instance), disk_idx); + if (virtDiskIdx == PS3_INVALID_DEV_ID) { + ps3stor_cli_printf("==warn==vd is not exist: [%u:%u]\n", + channel, id); + goto l_out; + } + ps3stor_cli_printf( + "\n===========start print VD detail information=========\n"); + ps3_dev_mgr_print_vd(&p_vd_array[virtDiskIdx], PS3_DRV_TRUE); + ps3stor_cli_printf( + "\n============end print VD detail information==========\n"); + + } else { + if (p_pd_table->pd_idxs[channel] == NULL) { + ps3stor_cli_printf( + "==error==invalid channel parameter: %d\n", + channel); + goto l_out; + } + + if (id >= p_dev_ctx->max_dev_in_channel[channel]) { + ps3stor_cli_printf( + "==error==invalid id parameter: %d\n", id); + goto l_out; + } + + disk_idx = p_pd_table->pd_idxs[channel][id]; + if (disk_idx == PS3_INVALID_DEV_ID) { + ps3stor_cli_printf("==warn==pd is not exist: [%u:%u]\n", + channel, id); + goto l_out; + } + ps3stor_cli_printf( + "\n===========start print PD detail information=========\n"); + ps3_dev_mgr_print_pd(&p_dev_ctx->pd_entries_array[disk_idx], + PS3_DRV_TRUE, instance->is_raid); + ps3stor_cli_printf( + "\n============end print PD detail information==========\n"); + } +l_out: + return; +} + +static void ps3_cli_dev_mgr_cli_detail_dump(int argc, char *argv[]) +{ + int ret = 0; + unsigned short host_no = 0; + unsigned short channel = 0; + unsigned short id = 0; + struct ps3_instance *instance = NULL; + + if (argc < 4) { + ps3stor_cli_printf("Too few args for dmdi!\n"); + goto l_out; + } + + ret = kstrtou16(argv[1], 0, &host_no); + if (ret != 0) { + ps3stor_cli_printf( + "Can not parse host_no \"%s\" for dmdi cmd!\n", + argv[1]); + goto l_out; + } + + instance = ps3_instance_lookup(host_no); + if (instance == NULL) { + ps3stor_cli_printf("Invalid host_no %d for dmdi cmd!\n", + host_no); + goto l_out; + } + + ret = kstrtou16(argv[2], 0, &channel); + if (ret != 0) { + ps3stor_cli_printf( + "Can not parse chanel \"%s\" for dmdi cmd!\n", argv[2]); + goto l_out; + } + + ret = kstrtou16(argv[3], 0, &id); + if (ret != 0) { + ps3stor_cli_printf( + "Can not parse targetId \"%s\" for dmdi cmd!\n", + argv[3]); + goto l_out; + } + + ps3_dev_mgr_show_detail_info(instance, channel, id); +l_out: + return; +} + +static void ps3_cli_event_subscirbe_info_dump(int argc, char *argv[]) +{ + struct ps3_instance *instance = NULL; + char *buf = NULL; + unsigned short host_no = 0; + int ret = 0; + + buf = kzalloc(PAGE_SIZE, GFP_KERNEL); + if (buf == NULL) { + ps3stor_cli_printf( + "malloc buf failed for event subscribe info cli!\n"); + goto l_malloc_failed; + } + + if (argc < 3) { + ps3stor_cli_printf( + "Too few args for event_subscribe_dump cli cmd!\n"); + goto l_out; + } + + if (strcmp(argv[1], "host_no") != 0) { + ps3stor_cli_printf("host_no is needed!\n"); + goto l_out; + } + + ret = kstrtou16(argv[2], 0, &host_no); + if (ret != 0) { + ps3stor_cli_printf( + "Can not parse host_no for event_subscribe_dump cmd!\n"); + goto l_out; + } + + instance = ps3_instance_lookup(host_no); + if (instance == NULL) { + ps3stor_cli_printf( + "Invalid host_no %d for event_subscribe_dump cmd!\n", + host_no); + goto l_out; + } + + (void)ps3_event_subscribe_info_get(instance, buf, PAGE_SIZE); + ps3stor_cli_printf(buf); + +l_out: + kfree(buf); +l_malloc_failed: + return; +} + +static void ps3_cli_event_delay_set(int argc, char *argv[]) +{ + struct ps3_instance *instance = NULL; + unsigned short host_no = 0; + unsigned int delay_seconds = 0; + int ret = 0; + + if (argc < 3) { + ps3stor_cli_printf( + "Too few args for event_subscribe_dump cli cmd!\n"); + goto l_out; + } + + if (strcmp(argv[1], "host_no") != 0) { + ps3stor_cli_printf("host_no is needed!\n"); + goto l_out; + } + + ret = kstrtou16(argv[2], 0, &host_no); + if (ret != 0) { + ps3stor_cli_printf( + "Can not parse host_no for event_subscribe_dump cmd!\n"); + goto l_out; + } + + instance = ps3_instance_lookup(host_no); + if (instance == NULL) { + ps3stor_cli_printf( + "Invalid host_no %d for event_subscribe_dump cmd!\n", + host_no); + goto l_out; + } + + if (strcmp(argv[3], "delay") == 0) { + ret = kstrtouint(argv[4], 0, &delay_seconds); + if (ret != 0) { + ps3stor_cli_printf( + "Can not parse delay seconds for event delay cmd!\n"); + goto l_out; + } + ret = ps3_event_delay_set(instance, delay_seconds); + if (ret != 0) { + ps3stor_cli_printf( + "Unable to config event delay ret %d\n", ret); + goto l_out; + } else { + ps3stor_cli_printf("Config event delay %d success\n", + delay_seconds); + } + } + +l_out: + return; +} + +static void ps3_cli_crashdump_set(int argc, char *argv[]) +{ + struct ps3_instance *instance = NULL; + unsigned short host_no = 0; + unsigned int delay_seconds = 0; + int ret = 0; + + if (argc < 3) { + ps3stor_cli_printf("Too few args for crashdump cli cmd!\n"); + goto l_out; + } + + if (strcmp(argv[1], "host_no") != 0) { + ps3stor_cli_printf("host_no is needed!\n"); + goto l_out; + } + + ret = kstrtou16(argv[2], 0, &host_no); + if (ret != 0) { + ps3stor_cli_printf( + "Can not parse host_no for event_subscribe_dump cmd!\n"); + goto l_out; + } + + instance = ps3_instance_lookup(host_no); + if (instance == NULL) { + ps3stor_cli_printf( + "Invalid host_no %d for event_subscribe_dump cmd!\n", + host_no); + goto l_out; + } + + if (strcmp(argv[3], "wait") == 0) { + ret = kstrtouint(argv[4], 0, &delay_seconds); + if (ret != 0) { + ps3stor_cli_printf( + "Can not parse delay seconds for event delay cmd!\n"); + goto l_out; + } + + if (delay_seconds < WAIT_DUMP_TIMES_MIN) { + ps3stor_cli_printf("min %d!\n", WAIT_DUMP_TIMES_MIN); + delay_seconds = WAIT_DUMP_TIMES_MIN; + } else if (delay_seconds > WAIT_DUMP_TIMES_MAX) { + ps3stor_cli_printf("max %d!\n", WAIT_DUMP_TIMES_MAX); + delay_seconds = WAIT_DUMP_TIMES_MAX; + } + + instance->dump_context.dump_dma_wait_times = delay_seconds; + ps3stor_cli_printf("set crashdump dma wait times %d success\n", + delay_seconds); + } + +l_out: + return; +} + +int ps3_dump_context_show(const char *prefix, struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + struct ps3_dump_context *ctxt = &instance->dump_context; + + ps3stor_cli_printf("%sdump_type : %d\n", prefix, + ctxt->dump_type); + ps3stor_cli_printf("%sdump_state : %d\n", prefix, + ctxt->dump_state); + ps3stor_cli_printf("%sdump_work_status : %d\n", prefix, + ctxt->dump_work_status); + ps3stor_cli_printf("%sdump_env : %d\n", prefix, + ctxt->dump_env); + ps3stor_cli_printf("%sdump_dma_vaddr : %p\n", prefix, + ctxt->dump_dma_buf); + ps3stor_cli_printf("%sdump_dma_paddr : %llx\n", prefix, + ctxt->dump_dma_addr); + ps3stor_cli_printf("%sdump_dma_buff_size : %#x\n", prefix, + PS3_DUMP_DMA_BUF_SIZE); + ps3stor_cli_printf("%sdump_dma_wait_times : %#x\n", prefix, + ctxt->dump_dma_wait_times); + ps3stor_cli_printf("%sdump_data_size : %llu\n", prefix, + ctxt->dump_data_size); + ps3stor_cli_printf("%sdump_data_size_copyed: %llu\n", prefix, + ctxt->copyed_data_size); + ps3stor_cli_printf("%sdump_dir : %s\n", prefix, + ctxt->dump_dir); + ps3stor_cli_printf("%sdump_file : %s\n", prefix, + ctxt->dump_out_file.filename); + ps3stor_cli_printf("%sdump_file_type : %d\n", prefix, + ctxt->dump_out_file.type); + ps3stor_cli_printf("%sdump_file_size : %llu\n", prefix, + ctxt->dump_out_file.file_size); + ps3stor_cli_printf("%sdump_file_write_cnt : %llu\n", prefix, + ctxt->dump_out_file.file_w_cnt); + ps3stor_cli_printf("%sdump_file_status : %d\n", prefix, + ctxt->dump_out_file.file_status); + ps3stor_cli_printf("%sdump_file_fp : %p\n", prefix, + ctxt->dump_out_file.fp); + if (ctxt->dump_pending_cmd) { + ps3stor_cli_printf( + "%sdump_pending_cmd : [trace_id:0x%llx][CFID:%d]\n", + prefix, ctxt->dump_pending_cmd->trace_id, + ctxt->dump_pending_cmd->cmd_word.cmdFrameID); + } + ps3stor_cli_printf("%sdump_notify_reg_times: %d\n", prefix, + ctxt->dump_pending_send_times); + ps3stor_cli_printf("%sdump_type_times : %d\n", prefix, + ctxt->dump_type_times); + ps3stor_cli_printf("%sdump_state_times : %d\n", prefix, + ctxt->dump_state_times); + + return ret; +} + +static void ps3_host_info_detail_dump(void) +{ + struct ps3_instance *instance = NULL; + struct list_head *pitem = NULL; + struct Scsi_Host *phost = NULL; + + list_for_each(pitem, &ps3_mgmt_info_get()->instance_list_head) { + instance = list_entry(pitem, struct ps3_instance, list_item); + phost = instance->host; + ps3stor_cli_printf("host_no:%d scsi_host detail info\n", + phost->host_no); + ps3stor_cli_printf(" host_failed %d\n", phost->host_failed); + ps3stor_cli_printf(" host_eh_scheduled %d\n", + phost->host_eh_scheduled); + ps3stor_cli_printf(" host_no %d\n", phost->host_no); + ps3stor_cli_printf(" eh_deadline %d\n", phost->eh_deadline); + ps3stor_cli_printf(" last_reset %ul\n", phost->last_reset); + ps3stor_cli_printf(" max_id %d\n", phost->max_id); + ps3stor_cli_printf(" max_lun %d\n", phost->max_lun); + ps3stor_cli_printf(" max_channel %d\n", phost->max_channel); + ps3stor_cli_printf(" unique_id %d\n", phost->unique_id); + ps3stor_cli_printf(" max_cmd_len %d\n", phost->max_cmd_len); + ps3stor_cli_printf(" can_queue %d\n", phost->can_queue); + ps3stor_cli_printf(" cmd_per_lun %d\n", phost->cmd_per_lun); + ps3stor_cli_printf(" sg_tablesize %d\n", + phost->sg_tablesize); + ps3stor_cli_printf(" sg_prot_tablesize %d\n", + phost->sg_prot_tablesize); + ps3stor_cli_printf(" max_sectors %d\n", phost->max_sectors); + ps3stor_cli_printf(" dma_boundary 0x%x\n", + phost->dma_boundary); + + switch (phost->shost_state) { + case SHOST_CREATED: + ps3stor_cli_printf(" shost_state SHOST_CREATED\n"); + break; + case SHOST_RUNNING: + ps3stor_cli_printf(" shost_state SHOST_RUNNING\n"); + break; + case SHOST_CANCEL: + ps3stor_cli_printf(" shost_state SHOST_CANCEL\n"); + break; + case SHOST_DEL: + ps3stor_cli_printf(" shost_state SHOST_DEL\n"); + break; + case SHOST_RECOVERY: + ps3stor_cli_printf(" shost_state SHOST_RECOVERY\n"); + break; + case SHOST_CANCEL_RECOVERY: + ps3stor_cli_printf( + " shost_state SHOST_CANCEL_RECOVERY\n"); + break; + case SHOST_DEL_RECOVERY: + ps3stor_cli_printf( + " shost_state SHOST_DEL_RECOVERY\n"); + break; + default: + break; + } + ps3stor_cli_printf("\n"); + } +} + +static void ps3_cmd_context_detail_dump(const char *format_space, + struct ps3_cmd_context *cmd_context) +{ + unsigned int i = 0; + + ps3stor_cli_printf("%smax_cmd_count:%d\n", format_space, + cmd_context->max_cmd_count); + ps3stor_cli_printf("%smax_scsi_cmd_count:%d\n", format_space, + cmd_context->max_scsi_cmd_count); + ps3stor_cli_printf("%smax_mgr_cmd_count:%d\n", format_space, + cmd_context->max_mgr_cmd_count); + ps3stor_cli_printf("%sreq_frame_buf_phys:0x%llx\n", format_space, + cmd_context->req_frame_buf_phys); + ps3stor_cli_printf("%sresponse_frame_buf_phys:0x%llx\n", format_space, + cmd_context->response_frame_buf_phys); + ps3stor_cli_printf("%sinit_frame_buf_phys:0x%llx\n", format_space, + cmd_context->init_frame_buf_phys); + ps3stor_cli_printf("%sinit_frame_sys_info_phys:0x%llx\n", format_space, + cmd_context->init_frame_sys_info_phys); + ps3stor_cli_printf("%s sgl_mode_support: %u\n", format_space, + cmd_context->sgl_mode_support); + + if (cmd_context->cmd_buf == NULL) + goto l_out; + for (i = cmd_context->max_scsi_cmd_count; + i < cmd_context->max_cmd_count; i++) { + if (cmd_context->cmd_buf[i]->cmd_state.state == + PS3_CMD_STATE_INIT) { + continue; + } + ps3stor_cli_printf( + "%s pending cmd mgr : [trace_id:0x%llx][CFID:%d][type:%d][isrSN:%d][%s]\n", + format_space, cmd_context->cmd_buf[i]->trace_id, + cmd_context->cmd_buf[i]->cmd_word.cmdFrameID, + cmd_context->cmd_buf[i]->cmd_word.type, + cmd_context->cmd_buf[i]->cmd_word.isrSN, + namePS3CmdState( + cmd_context->cmd_buf[i]->cmd_state.state)); + } + for (i = 0; i < cmd_context->max_scsi_cmd_count; i++) { + if (cmd_context->cmd_buf[i]->cmd_state.state == + PS3_CMD_STATE_INIT) { + continue; + } + ps3stor_cli_printf( + "%s pending cmd scsi: [trace_id:0x%llx][CFID:%d][type:%d][isrSN:%d][%s]\n", + format_space, cmd_context->cmd_buf[i]->trace_id, + cmd_context->cmd_buf[i]->cmd_word.cmdFrameID, + cmd_context->cmd_buf[i]->cmd_word.type, + cmd_context->cmd_buf[i]->cmd_word.isrSN, + namePS3CmdState( + cmd_context->cmd_buf[i]->cmd_state.state)); + } +l_out: + ps3stor_cli_printf("\n"); +} + +static void ps3_irq_context_detail_dump(const char *format_space, + struct ps3_irq_context *irq_context) +{ + unsigned int i = 0; + + ps3stor_cli_printf("%sreply_fifo_depth:%d\n", format_space, + irq_context->reply_fifo_depth); + ps3stor_cli_printf("%svalid_msix_vector_count:%d\n", format_space, + irq_context->valid_msix_vector_count); + ps3stor_cli_printf("%shigh_iops_msix_vectors:%d\n", format_space, + irq_context->high_iops_msix_vectors); + ps3stor_cli_printf("%sreply_fifo_desc_buf_phys:0x%llx\n", format_space, + irq_context->reply_fifo_desc_buf_phys); + for (i = 0; i < irq_context->valid_msix_vector_count; i++) { + ps3stor_cli_printf( + "%sreply_fifo_phys_base_addr_buf[%d]:0x%llx\n", + format_space, i, + irq_context->reply_fifo_phys_base_addr_buf[i]); + } + + for (i = 0; (int)i < irq_context->cpu_msix_table_sz - 1; i++) { + if (irq_context->cpu_msix_table == NULL) + break; + ps3stor_cli_printf("%scpu_msix_table[%d]:0x%llx\n", + format_space, i, + irq_context->cpu_msix_table[i]); + } + + ps3stor_cli_printf("%shigh_iops_io_count:%d\n", format_space, + irq_context->high_iops_io_count.counter); + ps3stor_cli_printf("%sis_enable_interrupts:%s\n", format_space, + irq_context->is_enable_interrupts ? "PS3_TRUE" : + "PS3_FALSE"); + ps3stor_cli_printf("%sis_support_balance:%s\n", format_space, + irq_context->is_support_balance ? "PS3_TRUE" : + "PS3_FALSE"); + ps3stor_cli_printf("%sis_balance_current_perf_mode:%s\n", format_space, + irq_context->is_balance_current_perf_mode ? + "PS3_TRUE" : + "PS3_FALSE"); + switch (irq_context->pci_irq_type) { + case PS3_PCI_IRQ_LEGACY: + ps3stor_cli_printf("%spci_irq_type:PS3_PCI_IRQ_LEGACY\n", + format_space); + break; + case PS3_PCI_IRQ_MSI: + ps3stor_cli_printf("%spci_irq_type:PS3_PCI_IRQ_MSI\n", + format_space); + break; + case PS3_PCI_IRQ_MSIX: + ps3stor_cli_printf("%spci_irq_type:PS3_PCI_IRQ_MSIX\n", + format_space); + break; + default: + break; + } + ps3stor_cli_printf("\n"); +} + +static void ps3_dev_context_detail_dump(const char *format_space, + struct ps3_dev_context *dev_context) +{ + ps3stor_cli_printf("%svd_table_idx:%d\n", format_space, + dev_context->vd_table_idx); + ps3stor_cli_printf("%spd_channel_count:%d\n", format_space, + dev_context->pd_channel_count); + ps3stor_cli_printf("%svd_channel_count:%d\n", format_space, + dev_context->vd_channel_count); + ps3stor_cli_printf("%stotal_vd_count:%d\n", format_space, + dev_context->total_vd_count); + ps3stor_cli_printf("%stotal_pd_count:%d\n", format_space, + dev_context->total_pd_count); + ps3stor_cli_printf("%smax_dev_per_channel:%d\n", format_space, + dev_context->max_dev_per_channel); + ps3stor_cli_printf("%spd_list_buf_phys:0x%llx\n", format_space, + dev_context->pd_list_buf_phys); + ps3stor_cli_printf("%svd_list_buf_phys:0x%llx\n", format_space, + dev_context->vd_list_buf_phys); + ps3stor_cli_printf("%spd_info_buf_phys:0x%llx\n", format_space, + dev_context->pd_info_buf_phys); + ps3stor_cli_printf("%svd_info_buf_phys_sync:0x%llx\n", format_space, + dev_context->vd_info_buf_phys_sync); + ps3stor_cli_printf("%svd_info_buf_phys_async:0x%llx\n", format_space, + dev_context->vd_info_buf_phys_async); + ps3stor_cli_printf("\n"); +} + +static void +ps3_event_context_detail_dump(const char *format_space, + struct ps3_event_context *event_context) +{ + if (event_context->delay_work) { + ps3stor_cli_printf("%sevent_delay:%d\n", format_space, + event_context->delay_work->event_delay); + } + ps3stor_cli_printf("\n"); +} + +static void +ps3_fault_context_detail_dump(const char *format_space, + struct ps3_fault_context *fault_context) +{ + ps3stor_cli_printf("%sioc_busy:%d\n", format_space, + fault_context->ioc_busy); + ps3stor_cli_printf("%slast_time:%d\n", format_space, + fault_context->last_time); + ps3stor_cli_printf("\n"); +} + +static void ps3_ioc_ctrl_info_detail_dump(const char *format_space, + struct PS3IocCtrlInfo *ctrl_info) +{ + size_t i = 0; + + ps3stor_cli_printf("%smaxVdCount:%d\n", format_space, + ctrl_info->maxVdCount); + ps3stor_cli_printf("%smaxPdCount:%d\n", format_space, + ctrl_info->maxPdCount); + ps3stor_cli_printf("%smaxSectors:%d\n", format_space, + ctrl_info->maxSectors); + ps3stor_cli_printf("%sPS3IocCtrlProp.enableSnapshot:%s\n", format_space, + ctrl_info->properties.enableSnapshot ? "PS3_TRUE" : + "PS3_FALSE"); + ps3stor_cli_printf("%sPS3IocCtrlProp.enableSoftReset:%s\n", + format_space, + ctrl_info->properties.enableSoftReset ? "PS3_TRUE" : + "PS3_FALSE"); + ps3stor_cli_printf("%sPS3IocCtrlCapable.supportUnevenSpans:%s\n", + format_space, + ctrl_info->capabilities.supportUnevenSpans ? + "PS3_TRUE" : + "PS3_FALSE"); + ps3stor_cli_printf("%sPS3IocCtrlCapable.supportJbodSecure:%s\n", + format_space, + ctrl_info->capabilities.supportJbodSecure ? + "PS3_TRUE" : + "PS3_FALSE"); + ps3stor_cli_printf("%sPS3IocCtrlCapable.supportNvmePassthru:%s\n", + format_space, + ctrl_info->capabilities.supportNvmePassthru ? + "PS3_TRUE" : + "PS3_FALSE"); + ps3stor_cli_printf("%sPS3IocCtrlCapable.supportDirectCmd:%s\n", + format_space, + ctrl_info->capabilities.supportDirectCmd ? + "PS3_TRUE" : + "PS3_FALSE"); + ps3stor_cli_printf("%sPS3IocCtrlCapable.supportSataDirectCmd:%s\n", + format_space, + ctrl_info->capabilities.supportSataDirectCmd ? + "PS3_TRUE" : + "PS3_FALSE"); + ps3stor_cli_printf("%sPS3IocCtrlCapable.supportSataNcq:%s\n", + format_space, + ctrl_info->capabilities.supportSataNcq ? + "PS3_TRUE" : + "PS3_FALSE"); + ps3stor_cli_printf("%sPS3IocCtrlCapable.supportAcceleration:%s\n", + format_space, + ctrl_info->capabilities.supportAcceleration ? + "PS3_TRUE" : + "PS3_FALSE"); + ps3stor_cli_printf("%sscsiTaskAbortTimeout:%d\n", format_space, + ctrl_info->scsiTaskAbortTimeout); + ps3stor_cli_printf("%sscsiTaskResetTimeout:%d\n", format_space, + ctrl_info->scsiTaskResetTimeout); + ps3stor_cli_printf("%siocPerfMode:%d\n", format_space, + ctrl_info->iocPerfMode); + ps3stor_cli_printf("%svdQueueNum:%d\n", format_space, + ctrl_info->vdQueueNum); + ps3stor_cli_printf("%scancelTimeOut:%d\n", format_space, + ctrl_info->cancelTimeOut); + ps3stor_cli_printf("%schannelInfo channelNum:%d\n", format_space, + ctrl_info->channelInfo.channelNum); + for (i = 0; i < ctrl_info->channelInfo.channelNum; i++) { + ps3stor_cli_printf( + "%s channels[%d].channelType:%d\n", format_space, i, + ctrl_info->channelInfo.channels[i].channelType); + ps3stor_cli_printf( + "%s channels[%d].maxDevNum:%d\n", format_space, i, + ctrl_info->channelInfo.channels[i].maxDevNum); + } + + ps3stor_cli_printf("\n"); +} + +static void +ps3_cmd_attr_context_detail_dump(const char *format_space, + struct ps3_cmd_attr_context *cmd_attr) +{ + ps3stor_cli_printf("%sthrottle_que_depth:%d\n", format_space, + cmd_attr->throttle_que_depth); + ps3stor_cli_printf("%scur_can_que:%d\n", format_space, + cmd_attr->cur_can_que); + ps3stor_cli_printf("%svd_io_threshold:%d\n", format_space, + cmd_attr->vd_io_threshold); + ps3stor_cli_printf("%sis_support_direct_cmd:%s\n", format_space, + cmd_attr->is_support_direct_cmd ? "PS3_TRUE" : + "PS3_FALSE"); + ps3stor_cli_printf("%snvme_page_size:%d\n", format_space, + cmd_attr->nvme_page_size); + + ps3stor_cli_printf("\n"); +} + +static void ps3_instance_info_detail_dump(void) +{ + struct ps3_instance *instance = NULL; + struct list_head *pitem = NULL; + struct Scsi_Host *phost = NULL; + int instance_state = 0; + + list_for_each(pitem, &ps3_mgmt_info_get()->instance_list_head) { + instance = list_entry(pitem, struct ps3_instance, list_item); + phost = instance->host; + instance_state = atomic_read(&instance->state_machine.state); + ps3stor_cli_printf("host_no:%d instance detail info\n", + phost->host_no); + ps3stor_cli_printf(" %s\n", + namePS3InstanceState(instance_state)); + ps3stor_cli_printf(" reg_bar:0x%x\n", instance->reg_bar); + ps3stor_cli_printf(" is_load:%s\n", + instance->state_machine.is_load ? + "PS3_TRUE" : + "PS3_FALSE"); + ps3stor_cli_printf(" is_support_sync_cache:%s\n", + instance->is_support_sync_cache ? + "PS3_TRUE" : + "PS3_FALSE"); + + ps3stor_cli_printf(" ps3_cmd_context detail info dump\n"); + ps3_cmd_context_detail_dump(" ", &instance->cmd_context); + + ps3stor_cli_printf(" ps3_irq_context detail info dump\n"); + ps3_irq_context_detail_dump(" ", &instance->irq_context); + + ps3stor_cli_printf(" ps3_dev_context detail info dump\n"); + ps3_dev_context_detail_dump(" ", &instance->dev_context); + + ps3stor_cli_printf(" ps3_event_context detail info dump\n"); + ps3_event_context_detail_dump(" ", + &instance->event_context); + + ps3stor_cli_printf(" ps3_fault_context detail info dump\n"); + ps3_fault_context_detail_dump(" ", + &instance->fault_context); + + ps3stor_cli_printf(" PS3IocCtrlInfo detail info dump\n"); + ps3_ioc_ctrl_info_detail_dump(" ", &instance->ctrl_info); + + ps3stor_cli_printf( + " ps3_cmd_attr_context detail info dump\n"); + ps3_cmd_attr_context_detail_dump(" ", + &instance->cmd_attr); + + ps3stor_cli_printf(" ps3_dump_context detail info dump\n"); + ps3_dump_context_show(" ", instance); + } +} + +static void ps3_cli_host_and_instance_ls(int argc, char *argv[]) +{ + if (argc < 2) { + ps3stor_cli_printf("ls host/instance/all\n"); + return; + } + + ps3_mutex_lock(&ps3_mgmt_info_get()->ps3_mgmt_lock); + if (strcmp(argv[1], "host") == 0) { + ps3_host_info_detail_dump(); + goto l_out; + } + + if (strcmp(argv[1], "instance") == 0) { + ps3_instance_info_detail_dump(); + goto l_out; + } + + if (strcmp(argv[1], "all") == 0) { + ps3_host_info_detail_dump(); + ps3_instance_info_detail_dump(); + goto l_out; + } + +l_out: + ps3_mutex_unlock(&ps3_mgmt_info_get()->ps3_mgmt_lock); +} + +static ssize_t ps3_reply_fifo_dump(struct ps3_instance *instance, unsigned short isr_sn, + char *buf, ssize_t total_len, unsigned short index, + unsigned short count) +{ + ssize_t len = 0; + struct PS3ReplyWord *fifo = NULL; + unsigned int i = 0; + struct ps3_irq *irq = NULL; + unsigned short start_idx = 0; + unsigned short once_print_cnt = 0; + unsigned short cnt = 0; + + if (isr_sn >= instance->irq_context.valid_msix_vector_count) { + ps3stor_cli_printf( + "invalid isr_sn %d (max:%d)\n", isr_sn, + instance->irq_context.valid_msix_vector_count - 1); + return PS3_FAILED; + } + + irq = instance->irq_context.irqs + isr_sn; + if (irq == NULL) + goto l_out; + len += snprintf(buf + len, total_len - len, "irq_name: %s\n", + irq->name); + len += snprintf(buf + len, total_len - len, "irqNo: %d\n", + irq->irqNo); + len += snprintf(buf + len, total_len - len, "isrSN: %d\n", + irq->isrSN); + len += snprintf(buf + len, total_len - len, "last_reply_idx: %d\n", + irq->last_reply_idx); + len += snprintf(buf + len, total_len - len, "irq_poll_th: %d\n", + irq->irq_poll_sched_threshold); + len += snprintf(buf + len, total_len - len, "is_sched_irq_poll: %d\n", + irq->is_sched_irq_poll); + len += snprintf(buf + len, total_len - len, "is_enable_irq: %d\n", + irq->is_enable_irq); + + if (index == 0) + start_idx = irq->last_reply_idx; + else + start_idx = index; + + if (count == 0) + once_print_cnt = 100; + else + once_print_cnt = count; + + while (cnt < once_print_cnt) { + if (i == instance->irq_context.reply_fifo_depth) + i = 0; + else + i = start_idx; + for (; i < instance->irq_context.reply_fifo_depth; ++i, ++cnt) { + if (total_len - len < 100) { + ps3stor_cli_printf(buf); + memset(buf, 0, total_len); + len = 0; + } + fifo = irq->reply_fifo_virt_base_addr + i; + len += snprintf( + buf + len, total_len - len, + "reply_word:index[%u], replyFlags[0x%x], CFID[0x%x], mode[%d], retType[%d] diskType[%d]\n", + i, fifo->retStatus, fifo->cmdFrameID, + fifo->mode, fifo->retType, fifo->diskType); + if (cnt >= once_print_cnt) + break; + } + } + ps3stor_cli_printf(buf); + + i = 0; + LOG_DEBUG("reply fifo[%d]", irq->isrSN); + while (i < instance->irq_context.reply_fifo_depth) { + fifo = irq->reply_fifo_virt_base_addr + i; + LOG_DEBUG( + "reply_word:index[%u], replyFlags[0x%x], CFID[0x%x] mode[%d] retType[%d] diskType[%d]\n", + i, fifo->retStatus, fifo->cmdFrameID, fifo->mode, + fifo->retType, fifo->diskType); + ++i; + } + +l_out: + return len; +} + +static void ps3_cli_reply_fifo_dump(int argc, char *argv[]) +{ + struct ps3_instance *instance = NULL; + char *buf = NULL; + unsigned short host_no = 0; + unsigned short isr_sn = 0; + int ret = 0; + unsigned short start_idx = 0; + unsigned short count = 0; + + buf = kzalloc(PAGE_SIZE, GFP_KERNEL); + if (buf == NULL) { + ps3stor_cli_printf("malloc buf failed!\n"); + goto l_malloc_failed; + } + + if (argc < 5) { + ps3stor_cli_printf("Too few args, must input 5 args!\n"); + goto l_out; + } + + if (strcmp(argv[1], "host_no") != 0) { + ps3stor_cli_printf("host_no is needed!\n"); + goto l_out; + } + + ret = kstrtou16(argv[2], 0, &host_no); + if (ret != 0) { + ps3stor_cli_printf("Can not parse host_no!\n"); + goto l_out; + } + + if (strcmp(argv[3], "isr_sn") != 0) { + ps3stor_cli_printf("isr_sn is needed!\n"); + goto l_out; + } + + ret = kstrtou16(argv[4], 0, &isr_sn); + if (ret != 0) { + ps3stor_cli_printf("Can not parse isr_sn!\n"); + goto l_out; + } + + if (isr_sn >= PS3_MAX_REPLY_QUE_COUNT) { + ps3stor_cli_printf("Invalid isr_sn %d, max isr_sn %d!\n", + isr_sn, PS3_MAX_REPLY_QUE_COUNT); + goto l_out; + } + + instance = ps3_instance_lookup(host_no); + if (instance == NULL) { + ps3stor_cli_printf("Invalid host_no %d !\n", host_no); + goto l_out; + } + + if (argc == 7) { + if (strcmp(argv[5], "start_idx") == 0) { + ret = kstrtou16(argv[6], 0, &start_idx); + if (ret != 0) { + ps3stor_cli_printf( + "Can not parse start_idx!\n"); + goto l_out; + } + } else if (strcmp(argv[5], "count") == 0) { + ret = kstrtou16(argv[6], 0, &count); + if (ret != 0) { + ps3stor_cli_printf("Can not parse count!\n"); + goto l_out; + } + } + } else if (argc == 9) { + if (strcmp(argv[5], "start_idx") == 0) { + ret = kstrtou16(argv[6], 0, &start_idx); + if (ret != 0) { + ps3stor_cli_printf( + "Can not parse start_idx!\n"); + goto l_out; + } + } + if (strcmp(argv[7], "count") == 0) { + ret = kstrtou16(argv[8], 0, &count); + if (ret != 0) { + ps3stor_cli_printf("Can not parse count!\n"); + goto l_out; + } + } + } + + (void)ps3_reply_fifo_dump(instance, isr_sn, buf, PAGE_SIZE, start_idx, + count); + +l_out: + kfree(buf); +l_malloc_failed: + return; +} + +static ssize_t ps3_req_head_dump(struct PS3ReqFrameHead *head, char *buf, + ssize_t total_len, size_t len) +{ + len += snprintf(buf + len, total_len - len, "cmdType: %d\n", + head->cmdType); + len += snprintf(buf + len, total_len - len, "cmdSubType: %d\n", + head->cmdSubType); + len += snprintf(buf + len, total_len - len, "cmdFrameID: %d\n", + head->cmdFrameID); + len += snprintf(buf + len, total_len - len, "control: 0x%x\n", + head->control); + len += snprintf(buf + len, total_len - len, + "noReplyWord: 0x%x\n", head->noReplyWord); + len += snprintf(buf + len, total_len - len, "dataFormat: 0x%x\n", + head->dataFormat); + len += snprintf(buf + len, total_len - len, + "reqFrameFormat: 0x%x\n", head->reqFrameFormat); + len += snprintf(buf + len, total_len - len, + "mapBlockVer: 0x%x\n", head->mapBlockVer); + len += snprintf(buf + len, total_len - len, "isWrite: 0x%x\n", + head->isWrite); + len += snprintf(buf + len, total_len - len, "devID: 0x%x\n", + head->devID.diskID); + len += snprintf(buf + len, total_len - len, "traceID: %lld\n", + head->traceID); + return len; +} + +static ssize_t ps3_mgr_req_frame_dump(struct ps3_cmd *cmd, char *buf, + ssize_t total_len, size_t len) +{ + struct PS3MgrReqFrame *mgr_req = (struct PS3MgrReqFrame *)cmd->req_frame; + + if (mgr_req->reqHead.cmdType == PS3_CMD_MANAGEMENT || + mgr_req->reqHead.cmdType == PS3_CMD_IOCTL) { + len = ps3_req_head_dump(&mgr_req->reqHead, buf, total_len, len); + len += snprintf(buf + len, total_len - len, + "sgeCount: %d\n", mgr_req->sgeCount); + len += snprintf(buf + len, total_len - len, + "sgeOffset: %d\n", mgr_req->sgeOffset); + len += snprintf(buf + len, total_len - len, + "syncFlag: %d\n", mgr_req->syncFlag); + len += snprintf(buf + len, total_len - len, + "timeout: %d\n", mgr_req->timeout); + len += snprintf(buf + len, total_len - len, + "abortFlag: %d\n", mgr_req->abortFlag); + len += snprintf(buf + len, total_len - len, + "pendingFlag: %d\n", mgr_req->pendingFlag); + } else if (mgr_req->reqHead.cmdType == PS3_CMD_SCSI_TASK_MANAGEMENT) { + struct PS3MgrTaskReqFrame *task_req = + (struct PS3MgrTaskReqFrame *)cmd->req_frame; + len = ps3_req_head_dump(&task_req->reqHead, buf, total_len, + len); + len += snprintf(buf + len, total_len - len, + "taskID: %d\n", task_req->taskID); + len += snprintf(buf + len, total_len - len, + "abortedCmdType: %d\n", + task_req->abortedCmdType); + } else { + struct PS3FrontEndReqFrame *fe_req = + (struct PS3FrontEndReqFrame *)cmd->req_frame; + len = ps3_req_head_dump(&fe_req->reqHead, buf, total_len, len); + len += snprintf(buf + len, total_len - len, + "sgeCount: %d\n", fe_req->sgeCount); + } + return len; +} + +static ssize_t ps3_req_frame_dump(struct ps3_cmd *cmd, char *buf, + ssize_t total_len, size_t len) +{ + if (PS3_CMD_TYPE_IS_RW(cmd->cmd_word.type)) { + if (cmd->req_frame->mgrReq.reqHead.reqFrameFormat == + PS3_REQFRAME_FORMAT_FRONTEND) { + struct PS3FrontEndReqFrame *fe_req = + (struct PS3FrontEndReqFrame *)cmd->req_frame; + len = ps3_req_head_dump(&fe_req->reqHead, buf, + total_len, len); + len += snprintf(buf + len, total_len - len, + "dataXferLen: %d\n", + fe_req->dataXferLen); + len += snprintf(buf + len, total_len - len, + "sgeCount: %d\n", + fe_req->sgeCount); + } else { + struct PS3HwReqFrame *hw_req = + (struct PS3HwReqFrame *)cmd->req_frame; + len += snprintf(buf + len, total_len - len, + "traceID: %llu\n", + hw_req->reqHead.traceID); + len += snprintf(buf + len, total_len - len, + "vlba: 0x%llx\n", + hw_req->softwareZone.virtDiskLba); + len += snprintf(buf + len, total_len - len, + "numBlocks: %u\n", + hw_req->softwareZone.numBlocks); + len += snprintf(buf + len, total_len - len, + "opcode: %u\n", + hw_req->softwareZone.opcode); + len += snprintf(buf + len, total_len - len, + "sglOffset: %u\n", + hw_req->softwareZone.sglOffset); + len += snprintf(buf + len, total_len - len, + "sglFormat: %u\n", + hw_req->softwareZone.sglFormat); + len += snprintf(buf + len, total_len - len, + "isResendCmd: %u\n", + hw_req->softwareZone.isResendCmd); + len += snprintf(buf + len, total_len - len, + "subOpcode: %u\n", + hw_req->softwareZone.subOpcode); + len += snprintf(buf + len, total_len - len, + "sgeCount: %u\n", + hw_req->softwareZone.sgeCount); + } + } else if (cmd->cmd_word.type == PS3_CMDWORD_TYPE_MGR) { + len = ps3_mgr_req_frame_dump(cmd, buf, total_len, len); + } else { + len += snprintf(buf + len, total_len - len, + "req frame is null\n"); + } + + return len; +} + +static ssize_t ps3_cmd_dump(struct ps3_instance *instance, unsigned short CFID, char *buf, + ssize_t total_len) +{ + ssize_t len = 0; + struct ps3_cmd *cmd = NULL; + + cmd = (struct ps3_cmd *)instance->cmd_context.cmd_buf[CFID]; + if (cmd == NULL) + goto l_out; + len += snprintf(buf + len, total_len - len, "----dump ps3_cmd----\n"); + len += snprintf(buf + len, total_len - len, "cmdFrameID: %d\n", + CFID); + len += snprintf(buf + len, total_len - len, "trace_id: %lld\n", + cmd->trace_id); + len += snprintf(buf + len, total_len - len, "no_reply_word: %d\n", + cmd->no_reply_word); + len += snprintf(buf + len, total_len - len, "is_retry_cmd: %d\n", + cmd->io_attr.is_retry_cmd); + len += snprintf(buf + len, total_len - len, "direct_flag: %d\n", + cmd->io_attr.direct_flag); + len += snprintf(buf + len, total_len - len, "dev_type: %s\n", + namePS3DevType((enum PS3DevType)cmd->io_attr.dev_type)); + len += snprintf(buf + len, total_len - len, "rw_flag: %d\n", + cmd->io_attr.rw_flag); + + len += snprintf(buf + len, total_len - len, "------cmd_word------\n"); + len += snprintf(buf + len, total_len - len, "type: %d\n", + cmd->cmd_word.type); + len += snprintf(buf + len, total_len - len, "direct: %d\n", + cmd->cmd_word.direct); + len += snprintf(buf + len, total_len - len, "isrSN: %d\n", + cmd->cmd_word.isrSN); + len += snprintf(buf + len, total_len - len, "phyDiskID: %d\n", + cmd->cmd_word.phyDiskID); + len += snprintf(buf + len, total_len - len, "queID: %d\n", + cmd->cmd_word.qMask); + len += snprintf(buf + len, total_len - len, "cmdFrameID: %d\n", + cmd->cmd_word.cmdFrameID); + len += snprintf(buf + len, total_len - len, "virtDiskID: %d\n", + cmd->cmd_word.virtDiskID); + + len += snprintf(buf + len, total_len - len, "------req_frame------\n"); + if (cmd->cmd_word.cmdFrameID == 0) { + len += snprintf(buf + len, total_len - len, + "--cmd is null--\n"); + goto l_out; + } + + len = ps3_req_frame_dump(cmd, buf, total_len, len); + +l_out: + return len; +} + +static void ps3_cli_cmd_dump(int argc, char *argv[]) +{ + struct ps3_instance *instance = NULL; + char *buf = NULL; + unsigned short host_no = 0; + unsigned short cmdFrameID = 0; + int ret = 0; + + buf = kzalloc(PAGE_SIZE, GFP_KERNEL); + if (buf == NULL) { + ps3stor_cli_printf("malloc buf failed!\n"); + goto l_malloc_failed; + } + + if (argc < 5) { + ps3stor_cli_printf("Too few args, must input 5 args!\n"); + goto l_out; + } + + if (strcmp(argv[1], "host_no") != 0) { + ps3stor_cli_printf("host_no is needed!\n"); + goto l_out; + } + + ret = kstrtou16(argv[2], 0, &host_no); + if (ret != 0) { + ps3stor_cli_printf("Can not parse host_no!\n"); + goto l_out; + } + + if (strcmp(argv[3], "cmd_frame_id") != 0) { + ps3stor_cli_printf("cmd_frame_id is needed!\n"); + goto l_out; + } + + ret = kstrtou16(argv[4], 0, &cmdFrameID); + if (ret != 0) { + ps3stor_cli_printf("Can not parse host_no!\n"); + goto l_out; + } + + instance = ps3_instance_lookup(host_no); + if (instance == NULL) { + ps3stor_cli_printf("Invalid host_no %d !\n", host_no); + goto l_out; + } + + if (cmdFrameID >= instance->cmd_context.max_cmd_count) { + ps3stor_cli_printf("Invalid cmd_frame_id %d, max id %d!\n", + cmdFrameID, + instance->cmd_context.max_cmd_count); + goto l_out; + } + + (void)ps3_cmd_dump(instance, cmdFrameID, buf, PAGE_SIZE); + ps3stor_cli_printf(buf); + +l_out: + kfree(buf); +l_malloc_failed: + return; +} + +static ssize_t ps3_io_statis_to_str(struct ps3_dev_io_statis *disk_io_statis, + char *buf, ssize_t total_len) +{ + ssize_t len = 0; + const unsigned int temp_array_len = 256; + char temp[256] = { 0 }; + ssize_t temp_len = 0; + + (void)disk_io_statis; + (void)buf; + + memset(temp, 0, temp_array_len); + temp_len = snprintf(temp, temp_array_len, "%-20s:%llu\n", "readSendCnt", + (unsigned long long)atomic64_read(&disk_io_statis->read_send_cnt)); + if ((len + temp_len) > total_len) + goto l_out; + len += snprintf(buf + len, total_len - len, "%-20s:%llu\n", + "readSendCnt", + (unsigned long long)atomic64_read(&disk_io_statis->read_send_cnt)); + + memset(temp, 0, temp_array_len); + temp_len = + snprintf(temp, temp_array_len, "%-20s:%llu\n", "readSendOkCnt", + (unsigned long long)atomic64_read(&disk_io_statis->read_send_ok_cnt)); + if ((len + temp_len) > total_len) + goto l_out; + len += snprintf(buf + len, total_len - len, "%-20s:%llu\n", + "readSendOkCnt", + (unsigned long long)atomic64_read(&disk_io_statis->read_send_ok_cnt)); + + memset(temp, 0, temp_array_len); + temp_len = snprintf( + temp, temp_array_len, "%-20s:%llu\n", "readOutStandCnt", + (unsigned long long)atomic64_read(&disk_io_statis->read_send_wait_cnt)); + if ((len + temp_len) > total_len) + goto l_out; + + len += snprintf( + buf + len, total_len - len, "%-20s:%llu\n", "readOutStandCnt", + (unsigned long long)atomic64_read(&disk_io_statis->read_send_wait_cnt)); + + memset(temp, 0, temp_array_len); + temp_len = snprintf(temp, temp_array_len, "%-20s:%llu\n", "readRecvCnt", + (unsigned long long)atomic64_read(&disk_io_statis->read_recv_cnt)); + if ((len + temp_len) > total_len) + goto l_out; + len += snprintf(buf + len, total_len - len, "%-20s:%llu\n", + "readRecvCnt", + (unsigned long long)atomic64_read(&disk_io_statis->read_recv_cnt)); + + memset(temp, 0, temp_array_len); + temp_len = + snprintf(temp, temp_array_len, "%-20s:%llu\n", "readRecvOkCnt", + (unsigned long long)atomic64_read(&disk_io_statis->read_recv_ok_cnt)); + if ((len + temp_len) > total_len) + goto l_out; + len += snprintf(buf + len, total_len - len, "%-20s:%llu\n", + "readRecvOkCnt", + (unsigned long long)atomic64_read(&disk_io_statis->read_recv_ok_cnt)); + + memset(temp, 0, temp_array_len); + temp_len = snprintf(temp, temp_array_len, "%-20s:%llu\n", "readOkBytes", + (unsigned long long)atomic64_read(&disk_io_statis->read_ok_bytes)); + if ((len + temp_len) > total_len) + goto l_out; + len += snprintf(buf + len, total_len - len, "%-20s:%llu\n", + "readOkBytes", + (unsigned long long)atomic64_read(&disk_io_statis->read_ok_bytes)); + + memset(temp, 0, temp_array_len); + temp_len = + snprintf(temp, temp_array_len, "%-20s:%llu\n", "writeSendCnt", + (unsigned long long)atomic64_read(&disk_io_statis->write_send_cnt)); + if ((len + temp_len) > total_len) + goto l_out; + len += snprintf(buf + len, total_len - len, "%-20s:%llu\n", + "writeSendCnt", + (unsigned long long)atomic64_read(&disk_io_statis->write_send_cnt)); + + memset(temp, 0, temp_array_len); + temp_len = snprintf( + temp, temp_array_len, "%-20s:%llu\n", "writeSendOkCnt", + (unsigned long long)atomic64_read(&disk_io_statis->write_send_ok_cnt)); + if ((len + temp_len) > total_len) + goto l_out; + len += snprintf(buf + len, total_len - len, "%-20s:%llu\n", + "writeSendOkCnt", + (unsigned long long)atomic64_read(&disk_io_statis->write_send_ok_cnt)); + + memset(temp, 0, temp_array_len); + temp_len = snprintf( + temp, temp_array_len, "%-20s:%llu\n", "writeOutStandCnt", + (unsigned long long)atomic64_read(&disk_io_statis->write_send_wait_cnt)); + if ((len + temp_len) > total_len) + goto l_out; + len += snprintf( + buf + len, total_len - len, "%-20s:%llu\n", "writeOutStandCnt", + (unsigned long long)atomic64_read(&disk_io_statis->write_send_wait_cnt)); + + + memset(temp, 0, temp_array_len); + temp_len = + snprintf(temp, temp_array_len, "%-20s:%llu\n", "writeRecvCnt", + (unsigned long long)atomic64_read(&disk_io_statis->write_recv_cnt)); + if ((len + temp_len) > total_len) + goto l_out; + len += snprintf(buf + len, total_len - len, "%-20s:%llu\n", + "writeRecvCnt", + (unsigned long long)atomic64_read(&disk_io_statis->write_recv_cnt)); + + memset(temp, 0, temp_array_len); + temp_len = snprintf( + temp, temp_array_len, "%-20s:%llu\n", "writeRecvOkCnt", + (unsigned long long)atomic64_read(&disk_io_statis->write_recv_ok_cnt)); + if ((len + temp_len) > total_len) + goto l_out; + len += snprintf(buf + len, total_len - len, "%-20s:%llu\n", + "writeRecvOkCnt", + (unsigned long long)atomic64_read(&disk_io_statis->write_recv_ok_cnt)); + + memset(temp, 0, temp_array_len); + temp_len = + snprintf(temp, temp_array_len, "%-20s:%llu\n\n", "writeOkBytes", + (unsigned long long)atomic64_read(&disk_io_statis->write_ok_bytes)); + if ((len + temp_len) > total_len) + goto l_out; + len += snprintf(buf + len, total_len - len, "%-20s:%llu\n\n", + "writeOkBytes", + (unsigned long long)atomic64_read(&disk_io_statis->write_ok_bytes)); + +l_out: + return len; +} + +static ssize_t ps3_io_statis_detail_dump(struct ps3_instance *instance, + char *buf, ssize_t total_len) +{ + ssize_t len = 0; + struct scsi_device *sdev = NULL; + struct ps3_scsi_priv_data *scsi_priv = NULL; + struct ps3_dev_io_statis disk_io_statis; + unsigned int i = 0; + const unsigned int temp_array_len = 256; + char temp[256] = { 0 }; + ssize_t temp_len = 0; + unsigned char dev_type = 0; + + if (!instance || !buf || total_len <= 0) { + ps3stor_cli_printf("invalid parameters"); + return 0; + } + + memset(temp, 0, temp_array_len); + temp_len = snprintf(temp, temp_array_len, "%s\n", + "disk io statistics detail:"); + if ((len + temp_len) > total_len) + goto l_out; + len += snprintf(buf + len, total_len - len, "%s\n", + "disk io statistics detail:"); + + list_for_each_entry(sdev, &instance->host->__devices, siblings) { + if (scsi_device_get(sdev)) + continue; + + ps3_mutex_lock(&instance->dev_context.dev_priv_lock); + scsi_priv = (struct ps3_scsi_priv_data *)sdev->hostdata; + if (scsi_priv == NULL) { + ps3_mutex_unlock(&instance->dev_context.dev_priv_lock); + scsi_device_put(sdev); + continue; + } + + memset(&disk_io_statis, 0, sizeof(struct ps3_dev_io_statis)); + memcpy(&disk_io_statis, &scsi_priv->statis, + sizeof(struct ps3_dev_io_statis)); + dev_type = scsi_priv->dev_type; + ps3_mutex_unlock(&instance->dev_context.dev_priv_lock); + + memset(temp, 0, temp_array_len); + temp_len = snprintf(temp, temp_array_len, "%-20s:%d\n", "index", + i); + if ((len + temp_len) > total_len) { + scsi_device_put(sdev); + goto l_out; + } + len += snprintf(buf + len, total_len - len, "%-20s:%d\n", + "index", i); + + memset(temp, 0, temp_array_len); + temp_len = snprintf(temp, temp_array_len, "%-20s:[%d,%d]\n", + "diskId", sdev->channel, sdev->id); + if ((len + temp_len) > total_len) { + scsi_device_put(sdev); + goto l_out; + } + len += snprintf(buf + len, total_len - len, "%-20s:[%d,%d]\n", + "diskId", sdev->channel, sdev->id); + + memset(temp, 0, temp_array_len); + temp_len = + snprintf(temp, temp_array_len, "%-20s:%s\n", "diskType", + (dev_type == PS3_DEV_TYPE_VD ? "VD" : "PD")); + if ((len + temp_len) > total_len) { + scsi_device_put(sdev); + goto l_out; + } + len += snprintf(buf + len, total_len - len, "%-20s:%s\n", + "diskType", + (dev_type == PS3_DEV_TYPE_VD ? "VD" : "PD")); + + if (len >= total_len) { + scsi_device_put(sdev); + break; + } + len += ps3_io_statis_to_str(&disk_io_statis, buf + len, + total_len - len); + + scsi_device_put(sdev); + i++; + } + +l_out: + return len; +} + +static void ps3_io_statis_clear_by_target(struct ps3_instance *instance, + unsigned int channel, unsigned int target) +{ + struct scsi_device *sdev = NULL; + + if (!instance) { + ps3stor_cli_printf("invalid parameters"); + return; + } + + sdev = scsi_device_lookup(instance->host, channel, target, 0); + if (sdev) { + if (sdev->channel == channel && sdev->id == target) + ps3_io_statis_clear(sdev); + + scsi_device_put(sdev); + } +} + +static ssize_t ps3_io_statis_summary_dump(struct ps3_instance *instance, + char *buf, ssize_t total_len) +{ + ssize_t len = 0; + struct scsi_device *sdev = NULL; + struct ps3_scsi_priv_data *scsi_priv = NULL; + struct ps3_dev_io_statis disk_io_statis; + struct ps3_dev_io_statis disk_io_statis_total; + unsigned int pd_cnt = 0, vd_cnt = 0; + const unsigned int temp_array_len = 256; + char temp[256] = { 0 }; + ssize_t temp_len = 0; + unsigned char dev_type = 0; + + if (!instance || !buf || total_len <= 0) { + ps3stor_cli_printf("invalid parameters"); + return 0; + } + + ps3_dev_io_statis_init(&disk_io_statis_total); + list_for_each_entry(sdev, &instance->host->__devices, siblings) { + if (scsi_device_get(sdev)) + continue; + + ps3_mutex_lock(&instance->dev_context.dev_priv_lock); + scsi_priv = (struct ps3_scsi_priv_data *)sdev->hostdata; + if (scsi_priv == NULL) { + ps3_mutex_unlock(&instance->dev_context.dev_priv_lock); + scsi_device_put(sdev); + continue; + } + memset(&disk_io_statis, 0, sizeof(struct ps3_dev_io_statis)); + memcpy(&disk_io_statis, &scsi_priv->statis, + sizeof(struct ps3_dev_io_statis)); + + dev_type = scsi_priv->dev_type; + ps3_mutex_unlock(&instance->dev_context.dev_priv_lock); + + atomic64_add(atomic64_read(&disk_io_statis.read_send_cnt), + &disk_io_statis_total.read_send_cnt); + atomic64_add(atomic64_read(&disk_io_statis.read_send_ok_cnt), + &disk_io_statis_total.read_send_ok_cnt); + atomic64_add(atomic64_read(&disk_io_statis.read_send_err_cnt), + &disk_io_statis_total.read_send_err_cnt); + atomic64_add(atomic64_read(&disk_io_statis.read_send_wait_cnt), + &disk_io_statis_total.read_send_wait_cnt); + atomic64_add(atomic64_read(&disk_io_statis.read_recv_cnt), + &disk_io_statis_total.read_recv_cnt); + atomic64_add(atomic64_read(&disk_io_statis.read_recv_err_cnt), + &disk_io_statis_total.read_recv_err_cnt); + atomic64_add(atomic64_read(&disk_io_statis.read_recv_ok_cnt), + &disk_io_statis_total.read_recv_ok_cnt); + atomic64_add(atomic64_read(&disk_io_statis.read_ok_bytes), + &disk_io_statis_total.read_ok_bytes); + + atomic64_add(atomic64_read(&disk_io_statis.write_send_cnt), + &disk_io_statis_total.write_send_cnt); + atomic64_add(atomic64_read(&disk_io_statis.write_send_ok_cnt), + &disk_io_statis_total.write_send_ok_cnt); + atomic64_add(atomic64_read(&disk_io_statis.write_send_err_cnt), + &disk_io_statis_total.write_send_err_cnt); + atomic64_add(atomic64_read(&disk_io_statis.write_send_wait_cnt), + &disk_io_statis_total.write_send_wait_cnt); + atomic64_add(atomic64_read(&disk_io_statis.write_recv_cnt), + &disk_io_statis_total.write_recv_cnt); + atomic64_add(atomic64_read(&disk_io_statis.write_recv_err_cnt), + &disk_io_statis_total.write_recv_err_cnt); + atomic64_add(atomic64_read(&disk_io_statis.write_recv_ok_cnt), + &disk_io_statis_total.write_recv_ok_cnt); + atomic64_add(atomic64_read(&disk_io_statis.write_ok_bytes), + &disk_io_statis_total.write_ok_bytes); + + if (dev_type == PS3_DEV_TYPE_VD) + vd_cnt++; + else + pd_cnt++; + + scsi_device_put(sdev); + } + + memcpy(&disk_io_statis, &disk_io_statis_total, + sizeof(struct ps3_dev_io_statis)); + + memset(temp, 0, temp_array_len); + temp_len = snprintf(temp, temp_array_len, "%s\n", + "disk io statistics summary:"); + if ((len + temp_len) > total_len) + goto l_out; + len += snprintf(buf + len, total_len - len, "%s\n", + "disk io statistics summary:"); + + memset(temp, 0, temp_array_len); + temp_len = snprintf(temp, temp_array_len, "%-20s:%d\n", "VD count", + vd_cnt); + if ((len + temp_len) > total_len) + goto l_out; + len += snprintf(buf + len, total_len - len, "%-20s:%d\n", "VD count", + vd_cnt); + + memset(temp, 0, temp_array_len); + temp_len = snprintf(temp, temp_array_len, "%-20s:%d\n", "PD count", + pd_cnt); + if ((len + temp_len) > total_len) + goto l_out; + len += snprintf(buf + len, total_len - len, "%-20s:%d\n", "PD count", + pd_cnt); + + if (len >= total_len) + goto l_out; + len += ps3_io_statis_to_str(&disk_io_statis, buf, total_len - len); + +l_out: + return len; +} + +void ps3_io_statis_dump_cli_cb_test(unsigned char detail) +{ + struct ps3_instance *instance = NULL; + struct list_head *pitem = NULL; + char *buf = NULL; + + buf = kzalloc(PAGE_SIZE, GFP_KERNEL); + if (buf == NULL) { + ps3stor_cli_printf("malloc buf failed!\n"); + goto l_malloc_failed; + } + + if (detail) { + list_for_each(pitem, + &ps3_mgmt_info_get()->instance_list_head) { + instance = list_entry(pitem, struct ps3_instance, + list_item); + if (instance) { + (void)ps3_io_statis_detail_dump(instance, buf, + PAGE_SIZE); + break; + } + } + } else { + list_for_each(pitem, + &ps3_mgmt_info_get()->instance_list_head) { + instance = list_entry(pitem, struct ps3_instance, + list_item); + if (instance) { + (void)ps3_io_statis_summary_dump(instance, buf, + PAGE_SIZE); + break; + } + } + } + + ps3stor_cli_printf(buf); + LOG_DEBUG("buf = %s\n", buf); + + kfree(buf); + buf = NULL; + +l_malloc_failed: + return; +} + +static void ps3_io_statis_dump_cli_cb(int argc, char *argv[]) +{ + struct ps3_instance *instance = NULL; + struct list_head *pitem = NULL; + char *buf = NULL; + + buf = kzalloc(PAGE_SIZE, GFP_KERNEL); + if (buf == NULL) { + ps3stor_cli_printf("malloc buf failed!\n"); + goto l_malloc_failed; + } + + if (argc < 4) { + ps3stor_cli_printf("Too few args, must input 4 args!\n"); + goto l_out; + } + + if (strcmp(argv[0], "show") != 0) { + ps3stor_cli_printf("invalid arg 0 %s!\n", argv[0]); + goto l_out; + } + + if (strcmp(argv[1], "io") != 0) { + ps3stor_cli_printf("invalid arg 1 %s!\n", argv[1]); + goto l_out; + } + + if (strcmp(argv[2], "statis") != 0) { + ps3stor_cli_printf("invalid arg 2 %s!\n", argv[2]); + goto l_out; + } + + if (strcmp(argv[3], "detail") == 0) { + list_for_each(pitem, + &ps3_mgmt_info_get()->instance_list_head) { + instance = list_entry(pitem, struct ps3_instance, + list_item); + if (instance) { + (void)ps3_io_statis_detail_dump(instance, buf, + PAGE_SIZE); + break; + } + } + } else { + list_for_each(pitem, + &ps3_mgmt_info_get()->instance_list_head) { + instance = list_entry(pitem, struct ps3_instance, + list_item); + if (instance) { + (void)ps3_io_statis_summary_dump(instance, buf, + PAGE_SIZE); + break; + } + } + } + + ps3stor_cli_printf(buf); + LOG_INFO("buf: %s\n", buf); + +l_out: + kfree(buf); + buf = NULL; + +l_malloc_failed: + return; +} + +static void ps3_io_statis_clear_cli_cb(int argc, char *argv[]) +{ + struct ps3_instance *instance = NULL; + struct list_head *pitem = NULL; + unsigned int channel = 0, target = 0; + int ret = 0; + + if (argc < 5) { + ps3stor_cli_printf("Too few args, must input 5 args!\n"); + goto l_out; + } + + if (strcmp(argv[0], "clear") != 0) { + ps3stor_cli_printf("invalid arg 0 %s!\n", argv[0]); + goto l_out; + } + + if (strcmp(argv[1], "io") != 0) { + ps3stor_cli_printf("invalid arg 1 %s!\n", argv[1]); + goto l_out; + } + + if (strcmp(argv[2], "statis") != 0) { + ps3stor_cli_printf("invalid arg 2 %s!\n", argv[2]); + goto l_out; + } + + ret = kstrtouint(argv[3], 0, &channel); + if (ret != 0) { + ps3stor_cli_printf("Invalid channel %s!\n", argv[3]); + goto l_out; + } + ret = kstrtouint(argv[4], 0, &target); + if (ret != 0) { + ps3stor_cli_printf("Invalid target %s!\n", argv[4]); + goto l_out; + } + + list_for_each(pitem, &ps3_mgmt_info_get()->instance_list_head) { + instance = list_entry(pitem, struct ps3_instance, list_item); + if (instance) { + ps3_io_statis_clear_by_target(instance, channel, + target); + ps3stor_cli_printf( + "clear host %d channel %d target %d io statistics\n", + instance->host->host_no, channel, target); + break; + } + } + +l_out: + return; +} +static ssize_t ps3_hardreset_cnt_show(struct ps3_instance *instance, char *buf, + ssize_t total_len) +{ + ssize_t len = 0; + + if (!instance || !buf || total_len <= 0) { + ps3stor_cli_printf("invalid parameters"); + goto l_out; + } + + len += snprintf(buf + len, total_len - len, "%s:%u\n", "hard reset cnt", + instance->recovery_context->hardreset_count); + +l_out: + return len; +} + +static ssize_t ps3_hardreset_cnt_clear(struct ps3_instance *instance, char *buf, + ssize_t total_len) +{ + ssize_t len = 0; + + if (!instance || !buf || total_len <= 0) { + ps3stor_cli_printf("invalid parameters"); + goto l_out; + } + + instance->recovery_context->hardreset_count = 0; + len += snprintf(buf + len, total_len - len, "%s:%u\n", "hard reset cnt", + instance->recovery_context->hardreset_count); +l_out: + return len; +} + +static void ps3_hardreset_cnt_show_cli_cb(int argc, char *argv[]) +{ + struct ps3_instance *instance = NULL; + char *buf = NULL; + unsigned short host_no = 0; + int ret = 0; + + buf = kzalloc(PAGE_SIZE, GFP_KERNEL); + if (buf == NULL) { + ps3stor_cli_printf("malloc buf failed!\n"); + goto l_malloc_failed; + } + + if (argc < 3) { + ps3stor_cli_printf("Too few args for register dump cli cmd!\n"); + goto l_out; + } + + if (strcmp(argv[1], "host_no") != 0) { + ps3stor_cli_printf("host_no is needed!\n"); + goto l_out; + } + + ret = kstrtou16(argv[2], 0, &host_no); + if (ret != 0) { + ps3stor_cli_printf( + "Can not parse host_no for register dump cmd!\n"); + goto l_out; + } + + instance = ps3_instance_lookup(host_no); + if (instance == NULL) { + ps3stor_cli_printf( + "Invalid host_no %d for register dump cmd!\n", host_no); + goto l_out; + } + + (void)ps3_hardreset_cnt_show(instance, buf, PAGE_SIZE); + ps3stor_cli_printf(buf); + LOG_INFO("buf: %s\n", buf); + +l_out: + kfree(buf); + buf = NULL; + +l_malloc_failed: + return; +} + +static void ps3_hardreset_cnt_clear_cli_cb(int argc, char *argv[]) +{ + struct ps3_instance *instance = NULL; + char *buf = NULL; + unsigned short host_no = 0; + int ret = 0; + + buf = kzalloc(PAGE_SIZE, GFP_KERNEL); + if (buf == NULL) { + ps3stor_cli_printf("malloc buf failed!\n"); + goto l_malloc_failed; + } + + if (argc < 3) { + ps3stor_cli_printf("Too few args for register dump cli cmd!\n"); + goto l_out; + } + + if (strcmp(argv[1], "host_no") != 0) { + ps3stor_cli_printf("host_no is needed!\n"); + goto l_out; + } + + ret = kstrtou16(argv[2], 0, &host_no); + if (ret != 0) { + ps3stor_cli_printf( + "Can not parse host_no for register dump cmd!\n"); + goto l_out; + } + + instance = ps3_instance_lookup(host_no); + if (instance == NULL) { + ps3stor_cli_printf( + "Invalid host_no %d for register dump cmd!\n", host_no); + goto l_out; + } + (void)ps3_hardreset_cnt_clear(instance, buf, PAGE_SIZE); + ps3stor_cli_printf(buf); + LOG_INFO("buf: %s\n", buf); + +l_out: + kfree(buf); + buf = NULL; + +l_malloc_failed: + return; +} + +static void ps3_cli_stop_all_instance(void) +{ + struct ps3_instance *instance = NULL; + struct list_head *pitem = NULL; + struct ps3_cmd *cmd = NULL; + struct scsi_cmnd *s_cmd = NULL; + struct ps3_scsi_priv_data *data = NULL; + unsigned int index = 0; + + list_for_each(pitem, &ps3_mgmt_info_get()->instance_list_head) { + instance = list_entry(pitem, struct ps3_instance, list_item); + if (instance == NULL) + continue; + + ps3_instance_state_transfer_to_dead(instance); + instance->ioc_adpter->irq_disable(instance); + ps3_irqs_sync(instance); + + ps3_r1x_conflict_queue_clean_all( + instance, PS3_SCSI_RESULT_HOST_STATUS(DID_NO_CONNECT), + PS3_TRUE); + for (index = 0; + index < instance->cmd_context.max_scsi_cmd_count; + index++) { + cmd = instance->cmd_context.cmd_buf[index]; + if (cmd->scmd != NULL) { + PS3_IO_OUTSTAND_DEC(instance, cmd->scmd); + PS3_VD_OUTSTAND_DEC(instance, cmd->scmd); + PS3_IO_BACK_ERR_INC(instance, cmd->scmd); + PS3_DEV_BUSY_DEC(cmd->scmd); + cmd->scmd->result = + ((int)PS3_SCSI_RESULT_HOST_STATUS( + DID_NO_CONNECT)); + s_cmd = cmd->scmd; + ps3_scsi_dma_unmap(cmd); + data = (struct ps3_scsi_priv_data *) + cmd->scmd->device->hostdata; + if (likely(data != NULL)) { + ps3_r1x_write_unlock(&data->lock_mgr, + cmd); + } + ps3_scsi_cmd_free(cmd); + SCMD_IO_DONE(s_cmd); + } + } + + for (index = instance->cmd_context.max_scsi_cmd_count; + index < instance->cmd_context.max_cmd_count; index++) { + cmd = instance->cmd_context.cmd_buf[index]; + if (cmd->req_frame->mgrReq.reqHead.noReplyWord == + PS3_CMD_WORD_NO_REPLY_WORD) { + cmd->resp_frame->normalRespFrame.respStatus = + PS3_STATUS_DEVICE_NOT_FOUND; + } else if (cmd->cmd_receive_cb != NULL) { + cmd->resp_frame->normalRespFrame.respStatus = + PS3_STATUS_DEVICE_NOT_FOUND; + cmd->cmd_receive_cb(cmd, + PS3_REPLY_WORD_FLAG_FAIL); + } + } + + cancel_delayed_work_sync( + &instance->event_context.delay_work->event_work); + ps3_watchdog_stop(instance); + + instance->ioc_adpter->irq_enable(instance); + ps3_irqpolls_enable(instance); + + ps3stor_cli_printf("host_no:%d instance stopped!\n", + instance->host->host_no); + } +} + +static void ps3_cli_force_to_stop(int argc, char *argv[]) +{ + (void)argc; + (void)argv; + ps3_mutex_lock(&ps3_mgmt_info_get()->ps3_mgmt_lock); + + ps3stor_cli_printf("force_to_stop begain.... !\n"); + ps3_cli_stop_all_instance(); + ps3stor_cli_printf("force_to_stop success !\n"); + + ps3_mutex_unlock(&ps3_mgmt_info_get()->ps3_mgmt_lock); +} + +static void ps3_debug_mem_dump(struct ps3_instance *ins, unsigned short entry, unsigned int len) +{ + unsigned int i = 0; + const unsigned int once_dump_len = 1024; + char buf[256] = { 0 }; + + for (; i < ins->debug_context.debug_mem_vaddr[entry].debugMemSize; + ++i) { + if (ins->debug_context.debug_mem_vaddr[entry].debugMemAddr == + 0 || + len == 0) { + break; + } + memset(buf, '\0', sizeof(buf)); + if (len <= once_dump_len) { + (void)snprintf(buf, sizeof(buf), + "base addr[0x%llx] len[%u]\n", + ins->debug_context.debug_mem_vaddr[entry] + .debugMemAddr + + i * once_dump_len, + len); + DATA_DUMP(((unsigned char *)(ins->debug_context + .debug_mem_vaddr[entry] + .debugMemAddr + + i * once_dump_len)), + len, buf); + break; + } + len -= once_dump_len; + (void)snprintf(buf, sizeof(buf), + "base addr[0x%llx] len[%u]\n", + ins->debug_context.debug_mem_vaddr[entry] + .debugMemAddr + i * once_dump_len, once_dump_len); + DATA_DUMP(((unsigned char *)(ins->debug_context.debug_mem_vaddr[entry] + .debugMemAddr + i * once_dump_len)), once_dump_len, buf); + } +} + +static void ps3_debug_mem_write(struct ps3_instance *ins, unsigned short entry, unsigned int len) +{ + const unsigned char l_value = 0x55; + const unsigned char h_value = 0xaa; + unsigned int i = 0; + struct Ps3DebugMemEntry *debug_mem_entry = NULL; + + debug_mem_entry = &ins->debug_context.debug_mem_vaddr[entry]; + for (i = 0; i < len; i += 2) { + *((unsigned char *)(uintptr_t)debug_mem_entry->debugMemAddr + i) = l_value; + *((unsigned char *)(uintptr_t)debug_mem_entry->debugMemAddr + i + 1) = h_value; + } +} + +static void ps3_debug_mem_rw_cli_cb(int argc, char *argv[]) +{ + struct ps3_instance *instance = NULL; + unsigned short host_no = 0; + unsigned short entry = 0; + unsigned short dir = 0; + unsigned int len = 0; + int ret = 0; + + if (argc < 9) { + ps3stor_cli_printf("Too few args, must input 9 args!\n"); + goto l_out; + } + + if (strcmp(argv[1], "host_no") != 0) { + ps3stor_cli_printf("host_no is needed!\n"); + goto l_out; + } + + ret = kstrtou16(argv[2], 0, &host_no); + if (ret != 0) { + ps3stor_cli_printf("Can not parse host_no!\n"); + goto l_out; + } + instance = ps3_instance_lookup(host_no); + if (instance == NULL) { + ps3stor_cli_printf("Invalid host_no %u !\n", host_no); + goto l_out; + } + + if (strcmp(argv[3], "entry_index") != 0) { + ps3stor_cli_printf("entry_index is needed!\n"); + goto l_out; + } + ret = kstrtou16(argv[4], 0, &entry); + if (ret != 0) { + ps3stor_cli_printf("Can not parse entry!\n"); + goto l_out; + } + + if (instance->debug_context.debug_mem_array_num == 0) { + ps3stor_cli_printf("host_no %d not support debug mem!\n", + host_no); + goto l_out; + } + + if (entry >= instance->debug_context.debug_mem_array_num) { + ps3stor_cli_printf("Invalid entry %u !\n", entry); + goto l_out; + } + + if (strcmp(argv[5], "dir") != 0) { + ps3stor_cli_printf("dir is needed!\n"); + goto l_out; + } + ret = kstrtou16(argv[6], 0, &dir); + if (ret != 0) { + ps3stor_cli_printf("Can not parse entry!\n"); + goto l_out; + } + if (!(dir == 0 || dir == 1)) { + ps3stor_cli_printf("Invalid dir %u, need is 0 or 1 !\n", dir); + goto l_out; + } + + if (strcmp(argv[7], "length") != 0) { + ps3stor_cli_printf("length is needed!\n"); + goto l_out; + } + ret = kstrtouint(argv[8], 0, &len); + if (ret != 0) { + ps3stor_cli_printf("Can not parse entry!\n"); + goto l_out; + } + if (len > (instance->debug_context.debug_mem_vaddr[entry].debugMemSize * + 1024)) { + ps3stor_cli_printf("len over mem [%u], max[%u]!\n", len, + instance->debug_context + .debug_mem_vaddr[entry] + .debugMemSize * + 1024); + goto l_out; + } + + if (dir == 1) + ps3_debug_mem_write(instance, entry, len); + else + ps3_debug_mem_dump(instance, entry, len); + +l_out: + return; +} + +static void ps3_debug_mem_para_dump(struct ps3_instance *instance) +{ + struct ps3_debug_context *cxt = &instance->debug_context; + unsigned int i = 0; + + ps3stor_cli_printf("debug mem array num %u\n", + cxt->debug_mem_array_num); + if (cxt->debug_mem_array_num == 0) + goto l_out; + + for (; i < cxt->debug_mem_array_num; ++i) { + ps3stor_cli_printf( + "entry[%u] vaddr[0x%llx] dma[0x%llx] max_size[%u]KB\n", + i, cxt->debug_mem_vaddr[i].debugMemAddr, + cxt->debug_mem_buf[i].debugMemAddr, + cxt->debug_mem_buf[i].debugMemSize); + } +l_out: + return; +} + +static void ps3_debug_mem_para_cli_cb(int argc, char *argv[]) +{ + struct ps3_instance *instance = NULL; + unsigned short host_no = 0; + int ret = 0; + + if (argc < 3) { + ps3stor_cli_printf("Too few args, must input 3 args!\n"); + goto l_out; + } + + if (strcmp(argv[1], "host_no") != 0) { + ps3stor_cli_printf("host_no is needed!\n"); + goto l_out; + } + + ret = kstrtou16(argv[2], 0, &host_no); + if (ret != 0) { + ps3stor_cli_printf("Can not parse host_no!\n"); + goto l_out; + } + instance = ps3_instance_lookup(host_no); + if (instance == NULL) { + ps3stor_cli_printf("Invalid host_no %u !\n", host_no); + goto l_out; + } + + if (instance->debug_context.debug_mem_array_num == 0) { + ps3stor_cli_printf("host_no %d not support debug mem!\n", + host_no); + goto l_out; + } + + ps3_debug_mem_para_dump(instance); + +l_out: + return; +} + +static void ps3_scsi_device_loop_dump(struct ps3_instance *instance) +{ + struct scsi_device *sdev = NULL; + unsigned long flags = 0; + struct Scsi_Host *shost = instance->host; + + if (instance->host == NULL) + goto l_out; + + spin_lock_irqsave(shost->host_lock, flags); + list_for_each_entry(sdev, &shost->__devices, siblings) { + if (sdev == NULL) + continue; + ps3stor_cli_printf( + "channel[%u], id[%u], lun[%llu], state[%d]!\n", + sdev->channel, sdev->id, sdev->lun, sdev->sdev_state); + } + spin_unlock_irqrestore(shost->host_lock, flags); + +l_out: + return; +} + +static void ps3_scsi_device_lookup_cb(int argc, char *argv[]) +{ + struct ps3_instance *instance = NULL; + unsigned short host_no = 0; + int ret = 0; + + if (argc < 3) { + ps3stor_cli_printf("Too few args, must input 3 args!\n"); + goto l_out; + } + + if (strcmp(argv[1], "host_no") != 0) { + ps3stor_cli_printf("host_no is needed!\n"); + goto l_out; + } + + ret = kstrtou16(argv[2], 0, &host_no); + if (ret != 0) { + ps3stor_cli_printf("Can not parse host_no!\n"); + goto l_out; + } + instance = ps3_instance_lookup(host_no); + if (instance == NULL) { + ps3stor_cli_printf("Invalid host_no %u !\n", host_no); + goto l_out; + } + + ps3_scsi_device_loop_dump(instance); + +l_out: + return; +} + +static void ps3_hard_reset_cb(int argc, char *argv[]) +{ + struct ps3_instance *instance = NULL; + unsigned short host_no = 0; + int ret = 0; + + if (argc < 3) { + ps3stor_cli_printf("Too few args, must input 3 args!\n"); + goto l_out; + } + + if (strcmp(argv[1], "host_no") != 0) { + ps3stor_cli_printf("host_no is needed!\n"); + goto l_out; + } + + ret = kstrtou16(argv[2], 0, &host_no); + if (ret != 0) { + ps3stor_cli_printf("Can not parse host_no!\n"); + goto l_out; + } + instance = ps3_instance_lookup(host_no); + if (instance == NULL) { + ps3stor_cli_printf("Invalid host_no %u !\n", host_no); + goto l_out; + } + ps3stor_cli_printf("hno:%u entry hard reset !\n", host_no); + + LOG_INFO("hno:%u entry hard reset!\n", PS3_HOST(instance)); + + LOG_WARN("hno:%u cli call recovery request!\n", PS3_HOST(instance)); + if ((!PS3_IOC_HARD_RECOVERY_SUPPORT(instance)) || + (!ps3_hard_reset_enable_query())) { + LOG_WARN( + "hno:%u soc feature unsupport Hard reset! need to be offline!\n", + PS3_HOST(instance)); + goto l_out; + } + if (ps3_need_block_hard_reset_request(instance)) { + LOG_WARN("hno:%u can not start hard reset\n", + PS3_HOST(instance)); + goto l_out; + } + + ps3_hard_recovery_request_with_retry(instance); + LOG_WARN("hno:%u cli call recovery request!\n", PS3_HOST(instance)); + +l_out: + return; +} + +static void ps3_soc_halt_cb(int argc, char *argv[]) +{ + struct ps3_instance *instance = NULL; + unsigned short host_no = 0; + int ret = 0; + + if (argc < 3) { + ps3stor_cli_printf("Too few args, must input 3 args!\n"); + goto l_out; + } + + if (strcmp(argv[1], "host_no") != 0) { + ps3stor_cli_printf("host_no is needed!\n"); + goto l_out; + } + + ret = kstrtou16(argv[2], 0, &host_no); + if (ret != 0) { + ps3stor_cli_printf("Can not parse host_no!\n"); + goto l_out; + } + instance = ps3_instance_lookup(host_no); + if (instance == NULL) { + ps3stor_cli_printf("Invalid host_no %u !\n", host_no); + goto l_out; + } + + if (PS3_IOC_STATE_HALT_SUPPORT(instance)) { + LOG_WARN("hno:%u cli call trans to halt!\n", + PS3_HOST(instance)); + ps3_instance_state_transfer_to_dead(instance); + instance->ioc_adpter->ioc_force_to_halt(instance); + LOG_WARN("hno:%u cli call trans to halt end!\n", + PS3_HOST(instance)); + } else { + ps3stor_cli_printf("host does not support HALT!\n", host_no); + } + +l_out: + return; +} + +static void ps3_cmd_stat_switch_store + (unsigned short host_no, unsigned short switch_flag, unsigned short mask) +{ + struct ps3_instance *instance = ps3_instance_lookup(host_no); + struct ps3_cmd_stat_wrokq_context *ctx = NULL; + unsigned char is_inc_switch_open = PS3_FALSE; + + if (instance == NULL) { + ps3stor_cli_printf("invalid host_no %u\n", host_no); + goto l_out; + } + + if ((instance->cmd_statistics.cmd_stat_switch & mask) == + (switch_flag & mask)) { + ps3stor_cli_printf("switch_flag no change 0x%x\n", + instance->cmd_statistics.cmd_stat_switch); + goto l_out; + } + + is_inc_switch_open = ps3_stat_inc_switch_is_open(instance); + + instance->cmd_statistics.cmd_stat_switch &= (~mask); + instance->cmd_statistics.cmd_stat_switch |= (switch_flag & mask); + + ctx = &instance->cmd_statistics.stat_workq; + if ((ctx->stat_queue != NULL) && (!ctx->is_stop) && + ps3_stat_inc_switch_is_open(instance) && !is_inc_switch_open) { + ps3stor_cli_printf("schedule delay work\n"); + queue_delayed_work( + ctx->stat_queue, &ctx->stat_work, + msecs_to_jiffies( + instance->cmd_statistics.stat_interval)); + } + + ps3stor_cli_printf("stat switch value is 0x%x\n", + instance->cmd_statistics.cmd_stat_switch); +l_out: + return; +} + +static void ps3_cmd_stat_switch_store_cb(int argc, char *argv[]) +{ + unsigned short host_no = 0; + unsigned short switch_flag = 0; + unsigned short mask = 0; + int ret = PS3_SUCCESS; + + if (argc < 7) { + ps3stor_cli_printf("Too few args, must input 7 args!\n"); + goto l_out; + } + + if (strcmp(argv[1], "host_no") != 0) { + ps3stor_cli_printf("host_no is needed!\n"); + goto l_out; + } + + ret = kstrtou16(argv[2], 0, &host_no); + if (ret != 0) { + ps3stor_cli_printf("Can not parse host_no!\n"); + goto l_out; + } + + if (strcmp(argv[3], "value") != 0) { + ps3stor_cli_printf("value is needed!\n"); + goto l_out; + } + + ret = kstrtou16(argv[4], 0, &switch_flag); + if (ret != 0) { + ps3stor_cli_printf("Can not parse value!\n"); + goto l_out; + } + + if (strcmp(argv[5], "mask") != 0) { + ps3stor_cli_printf("mask is needed!\n"); + goto l_out; + } + + ret = kstrtou16(argv[6], 0, &mask); + if (ret != 0) { + ps3stor_cli_printf("Can not parse mask!\n"); + goto l_out; + } + + ps3_cmd_stat_switch_store(host_no, switch_flag, mask); + +l_out: + return; +} + +static inline void ps3_cmd_stat_switch_show(unsigned short host_no) +{ + struct ps3_instance *instance = ps3_instance_lookup(host_no); + + if (instance == NULL) { + ps3stor_cli_printf("invalid host_no %u\n", host_no); + goto l_out; + } + + ps3stor_cli_printf("[bit0:OUTSTAND_SWITCH_OPEN, bit1:INC_SWITCH_OPEN\n" + "bit2:LOG_SWITCH_OPEN, bit3:DEV_SWITCH_OPEN]\n"); + ps3stor_cli_printf("stat switch value is 0x%x\n", + instance->cmd_statistics.cmd_stat_switch); + +l_out: + return; +} + +static void ps3_cmd_stat_switch_show_cb(int argc, char *argv[]) +{ + unsigned short host_no = 0; + int ret = PS3_SUCCESS; + + if (argc < 3) { + ps3stor_cli_printf("Too few args, must input 3 args!\n"); + goto l_out; + } + + if (strcmp(argv[1], "host_no") != 0) { + ps3stor_cli_printf("host_no is needed!\n"); + goto l_out; + } + + ret = kstrtou16(argv[2], 0, &host_no); + if (ret != 0) { + ps3stor_cli_printf("Can not parse host_no!\n"); + goto l_out; + } + + ps3_cmd_stat_switch_show(host_no); +l_out: + return; +} + +static void ps3_stat_total_dump(struct ps3_instance *instance, char *buf, + int total_len) +{ + int len = 0; + const unsigned int temp_array_len = total_len; + char *tmp_buf = NULL; + int temp_len = 0; + struct ps3_cmd_statistics_context *ctx = &instance->cmd_statistics; + unsigned short i = 0; + + len += snprintf(buf + len, total_len - len, + "----host[%u] total cmd statistics show begin----\n", + PS3_HOST(instance)); + len += snprintf( + buf + len, total_len - len, "----cmd_outstandin:%u----\n", + ps3_atomic_read(&instance->cmd_statistics.cmd_outstanding)); + len += snprintf( + buf + len, total_len - len, + "%-25s %-15s %-15s %-15s %-15s %-20s %-20s %-20s\n", + " ", "start", "back_good", "back_err", "not_back", "avg(us)", + "max(us)", "min(us)"); + + tmp_buf = kzalloc(temp_array_len, GFP_KERNEL); + if (tmp_buf == NULL) + goto l_out; + + for (; i < PS3_QOS_PD_PRO; ++i) { + temp_len = snprintf( + tmp_buf, temp_array_len, + "%-25s %-15llu %-15llu %-15llu %-15llu %-20llu %-20llu %-20llu\n", + ps3_cmd_stat_item_tostring((enum ps3_cmd_stat_item)i), + ctx->total_stat.stat[i].start, + ctx->total_stat.stat[i].back_good, + ctx->total_stat.stat[i].back_err, + ctx->total_stat.stat[i].not_back, + ctx->total_stat.stat[i].lagency.avg, + ctx->total_stat.stat[i].lagency.max_lagency, + ctx->total_stat.stat[i].lagency.min_lagency); + if ((len + temp_len) > total_len) { + ps3stor_cli_printf("print over!\n"); + goto l_out; + } + + memset(tmp_buf, 0, temp_array_len); + + len += snprintf( + buf + len, total_len - len, + "%-25s %-15llu %-15llu %-15llu %-15llu %-20llu %-20llu %-20llu\n", + ps3_cmd_stat_item_tostring((enum ps3_cmd_stat_item)i), + ctx->total_stat.stat[i].start, + ctx->total_stat.stat[i].back_good, + ctx->total_stat.stat[i].back_err, + ctx->total_stat.stat[i].not_back, + ctx->total_stat.stat[i].lagency.avg, + ctx->total_stat.stat[i].lagency.max_lagency, + ctx->total_stat.stat[i].lagency.min_lagency); + } + + temp_len = snprintf(tmp_buf, temp_array_len, + "----host[%u] cmd statistics show end----\n", + PS3_HOST(instance)); + if ((len + temp_len) > total_len) { + ps3stor_cli_printf("print over!\n"); + goto l_out; + } + + len += snprintf(buf + len, total_len - len, + "----host[%u] cmd statistics show end----\n", + PS3_HOST(instance)); +l_out: + if (tmp_buf != NULL) { + kfree((void *)tmp_buf); + tmp_buf = NULL; + } +} + +static void ps3_stat_total_show_cb(int argc, char *argv[]) +{ + struct ps3_instance *instance = NULL; + char *buf = NULL; + unsigned short host_no = 0; + int ret = PS3_SUCCESS; + + if (argc < 3) { + ps3stor_cli_printf("Too few args, must input 3 args!\n"); + goto l_out; + } + + if (strcmp(argv[1], "host_no") != 0) { + ps3stor_cli_printf("host_no is needed!\n"); + goto l_out; + } + + ret = kstrtou16(argv[2], 0, &host_no); + if (ret != 0) { + ps3stor_cli_printf("Can not parse host_no!\n"); + goto l_out; + } + + instance = ps3_instance_lookup(host_no); + if (instance == NULL) { + ps3stor_cli_printf("Invalid host_no %d !\n", host_no); + goto l_out; + } + + buf = kzalloc(PAGE_SIZE * 2, GFP_KERNEL); + if (buf == NULL) { + ps3stor_cli_printf("malloc buf failed!\n"); + goto l_out; + } + + ps3_stat_total_dump(instance, buf, PAGE_SIZE * 2); + ps3stor_cli_printf(buf); + +l_out: + if (buf != NULL) { + kfree(buf); + buf = NULL; + } +} + +static void ps3_stat_inc_dump(struct ps3_instance *instance, char *buf, + int total_len) +{ + int len = 0; + const unsigned int temp_array_len = PAGE_SIZE; + char *tmp_buf = NULL; + int temp_len = 0; + struct ps3_cmd_statistics_context *ctx = &instance->cmd_statistics; + unsigned short i = 0; + + len += snprintf(buf + len, total_len - len, + "----host[%u] inc cmd statistics show begin----\n", + PS3_HOST(instance)); + len += snprintf( + buf + len, total_len - len, + "%-25s %-15s %-15s %-15s %-15s %-20s %-20s %-20s\n", + " ", "start", "back_good", "back_err", "not_back", "avg(us)", + "max(us)", "min(us)"); + + tmp_buf = kzalloc(temp_array_len, GFP_KERNEL); + if (tmp_buf == NULL) + goto l_out; + + for (; i < PS3_QOS_PD_PRO; ++i) { + temp_len = snprintf( + tmp_buf, temp_array_len, + "%-25s %-15llu %-15llu %-15llu %-15llu %-20llu %-20llu %-20llu\n", + ps3_cmd_stat_item_tostring((enum ps3_cmd_stat_item)i), + ctx->inc_stat.stat[i].start, + ctx->inc_stat.stat[i].back_good, + ctx->inc_stat.stat[i].back_err, + ctx->inc_stat.stat[i].not_back, + ctx->inc_stat.stat[i].lagency.avg, + ctx->inc_stat.stat[i].lagency.max_lagency, + ctx->inc_stat.stat[i].lagency.min_lagency); + if ((len + temp_len) > total_len) { + ps3stor_cli_printf("print over!\n"); + goto l_out; + } + + memset(tmp_buf, 0, temp_array_len); + + len += snprintf( + buf + len, total_len - len, + "%-25s %-15llu %-15llu %-15llu %-15llu %-20llu %-20llu %-20llu\n", + ps3_cmd_stat_item_tostring((enum ps3_cmd_stat_item)i), + ctx->inc_stat.stat[i].start, + ctx->inc_stat.stat[i].back_good, + ctx->inc_stat.stat[i].back_err, + ctx->inc_stat.stat[i].not_back, + ctx->inc_stat.stat[i].lagency.avg, + ctx->inc_stat.stat[i].lagency.max_lagency, + ctx->inc_stat.stat[i].lagency.min_lagency); + } + + temp_len = snprintf(tmp_buf, temp_array_len, + "----host[%u] cmd statistics show end----\n", + PS3_HOST(instance)); + if ((len + temp_len) > total_len) { + ps3stor_cli_printf("print over!\n"); + goto l_out; + } + + len += snprintf(buf + len, total_len - len, + "----host[%u] cmd statistics show end----\n", + PS3_HOST(instance)); +l_out: + if (tmp_buf != NULL) { + kfree((void *)tmp_buf); + tmp_buf = NULL; + } +} + +static void ps3_stat_inc_show_cb(int argc, char *argv[]) +{ + struct ps3_instance *instance = NULL; + char *buf = NULL; + unsigned short host_no = 0; + int ret = PS3_SUCCESS; + + if (argc < 3) { + ps3stor_cli_printf("Too few args, must input 3 args!\n"); + goto l_out; + } + + if (strcmp(argv[1], "host_no") != 0) { + ps3stor_cli_printf("host_no is needed!\n"); + goto l_out; + } + + ret = kstrtou16(argv[2], 0, &host_no); + if (ret != 0) { + ps3stor_cli_printf("Can not parse host_no!\n"); + goto l_out; + } + + instance = ps3_instance_lookup(host_no); + if (instance == NULL) { + ps3stor_cli_printf("Invalid host_no %d !\n", host_no); + goto l_out; + } + + buf = kzalloc(PAGE_SIZE, GFP_KERNEL); + if (buf == NULL) { + ps3stor_cli_printf("malloc buf failed!\n"); + goto l_out; + } + + ps3_stat_inc_dump(instance, buf, PAGE_SIZE); + ps3stor_cli_printf(buf); + +l_out: + if (buf != NULL) { + kfree(buf); + buf = NULL; + } +} + +static void ps3_stat_buf_clr_cb(int argc, char *argv[]) +{ + struct ps3_instance *instance = NULL; + unsigned short host_no = 0; + int ret = PS3_SUCCESS; + + if (argc < 3) { + ps3stor_cli_printf("Too few args, must input 3 args!\n"); + goto l_out; + } + + if (strcmp(argv[1], "host_no") != 0) { + ps3stor_cli_printf("host_no is needed!\n"); + goto l_out; + } + + ret = kstrtou16(argv[2], 0, &host_no); + if (ret != 0) { + ps3stor_cli_printf("Can not parse host_no!\n"); + goto l_out; + } + + instance = ps3_instance_lookup(host_no); + if (instance == NULL) { + ps3stor_cli_printf("Invalid host_no %d !\n", host_no); + goto l_out; + } + + ps3_stat_all_clear(instance); + ps3stor_cli_printf("cmd stat clear complete\n"); + +l_out: + return; +} + +static void ps3_stat_interval_show_cb(int argc, char *argv[]) +{ + struct ps3_instance *instance = NULL; + unsigned short host_no = 0; + int ret = PS3_SUCCESS; + + if (argc < 3) { + ps3stor_cli_printf("Too few args, must input 3 args!\n"); + goto l_out; + } + + if (strcmp(argv[1], "host_no") != 0) { + ps3stor_cli_printf("host_no is needed!\n"); + goto l_out; + } + + ret = kstrtou16(argv[2], 0, &host_no); + if (ret != 0) { + ps3stor_cli_printf("Can not parse host_no!\n"); + goto l_out; + } + + instance = ps3_instance_lookup(host_no); + if (instance == NULL) { + ps3stor_cli_printf("Invalid host_no %d !\n", host_no); + goto l_out; + } + + ps3stor_cli_printf("cmd stat interval is %u ms\n", + instance->cmd_statistics.stat_interval); + +l_out: + return; +} + +static void ps3_stat_interval_store_cb(int argc, char *argv[]) +{ + struct ps3_instance *instance = NULL; + unsigned short host_no = 0; + unsigned int interval = 0; + int ret = PS3_SUCCESS; + + if (argc < 5) { + ps3stor_cli_printf("Too few args, must input 3 args!\n"); + goto l_out; + } + + if (strcmp(argv[1], "host_no") != 0) { + ps3stor_cli_printf("host_no is needed!\n"); + goto l_out; + } + + ret = kstrtou16(argv[2], 0, &host_no); + if (ret != 0) { + ps3stor_cli_printf("Can not parse host_no!\n"); + goto l_out; + } + + instance = ps3_instance_lookup(host_no); + if (instance == NULL) { + ps3stor_cli_printf("Invalid host_no %d !\n", host_no); + goto l_out; + } + + if (strcmp(argv[3], "interval") != 0) { + ps3stor_cli_printf("value err!\n"); + goto l_out; + } + + ret = kstrtouint(argv[4], 0, &interval); + if (ret != 0) { + ps3stor_cli_printf("Can not parse value!\n"); + goto l_out; + } + + if (interval < 1000) { + ps3stor_cli_printf("interval need > 1000ms!\n"); + goto l_out; + } + + instance->cmd_statistics.stat_interval = interval; + + ps3stor_cli_printf("cmd stat interval is %u ms\n", + instance->cmd_statistics.stat_interval); + +l_out: + return; +} + +static void ps3_reply_fifo_reset_cb(int argc, char *argv[]) +{ + struct ps3_instance *instance = NULL; + unsigned short host_no = 0; + int ret = PS3_SUCCESS; + + if (argc < 3) { + ps3stor_cli_printf("Too few args, must input 3 args!\n"); + goto l_out; + } + + if (strcmp(argv[1], "host_no") != 0) { + ps3stor_cli_printf("host_no is needed!\n"); + goto l_out; + } + + ret = kstrtou16(argv[2], 0, &host_no); + if (ret != 0) { + ps3stor_cli_printf("Can not parse host_no!\n"); + goto l_out; + } + + instance = ps3_instance_lookup(host_no); + if (instance == NULL) { + ps3stor_cli_printf("Invalid host_no %d !\n", host_no); + goto l_out; + } + + ps3_all_reply_fifo_complete(instance); + ps3_all_reply_fifo_init(instance); + ps3stor_cli_printf("all reply fifo complete reply_index to 0\n"); + +l_out: + return; +} + +static void ps3_cli_remove_host_force(int argc, char *argv[]) +{ + struct ps3_instance *instance = NULL; + unsigned short host_no = 0; + int ret = PS3_SUCCESS; + + if (argc < 3) { + ps3stor_cli_printf("Too few args, must input 3 args!\n"); + goto l_out; + } + + if (strcmp(argv[1], "host_no") != 0) { + ps3stor_cli_printf("host_no is needed!\n"); + goto l_out; + } + + ret = kstrtou16(argv[2], 0, &host_no); + if (ret != 0) { + ps3stor_cli_printf("Can not parse host_no!\n"); + goto l_out; + } + + instance = ps3_instance_lookup(host_no); + if (instance == NULL) { + ps3stor_cli_printf("Invalid host_no %d !\n", host_no); + goto l_out; + } + + ps3_remove(instance->pdev); + + ps3stor_cli_printf("remove instance %d completed\n", host_no); +l_out: + return; +} + +static void ps3_cli_ramfs_test_set(int argc, char *argv[]) +{ + unsigned short ramfs_enable = 0; + int ret = PS3_SUCCESS; + + if (argc < 2) { + ps3stor_cli_printf("Too few args, must input 2 args!\n"); + goto l_out; + } + + ret = kstrtou16(argv[1], 0, &ramfs_enable); + if (ret != 0) { + ps3stor_cli_printf("Can not parse ramfs_enable!\n"); + goto l_out; + } + + ps3_ramfs_test_store((int)ramfs_enable); + + ps3stor_cli_printf("set ramfs test enable to %d\n", ramfs_enable); +l_out: + return; +} + +unsigned char ps3_get_wait_cli_flag(void) +{ + return ps3_cli_wait_flag; +} + +static void ps3_no_wait_cli_cmd(int argc, char *argv[]) +{ + unsigned short wait_cli_flag = PS3_FALSE; + int ret = PS3_SUCCESS; + + if (argc < 3) { + ps3stor_cli_printf("Too few args, must input 3 args!\n"); + goto l_out; + } + + if (strcmp(argv[1], "flag") != 0) { + ps3stor_cli_printf("host_no is needed!\n"); + goto l_out; + } + + ret = kstrtou16(argv[2], 0, &wait_cli_flag); + if (ret != 0) { + ps3stor_cli_printf("Can not parse wait_cli_flag!\n", argv[3]); + goto l_out; + } + + if (wait_cli_flag) + ps3_cli_wait_flag = PS3_TRUE; + else + ps3_cli_wait_flag = PS3_FALSE; + ps3stor_cli_printf("ps3 no wait cli flag %d\n", ps3_cli_wait_flag); + +l_out: + return; +} + +static void ps3_qos_show_pd(struct ps3_instance *instance, unsigned short disk_id) +{ + struct ps3_qos_pd_mgr *qos_pd_mgr = NULL; + struct qos_wait_queue *waitq = NULL; + unsigned short i = 0; + + if (disk_id > 0 && disk_id <= instance->qos_context.max_pd_count) { + ps3stor_cli_printf("qos pd[%u] info\n", disk_id); + qos_pd_mgr = &instance->qos_context.pd_ctx.qos_pd_mgrs[disk_id]; + ps3stor_cli_printf( + "-----valid[%u] vid[%u] used_quota[%u] quota[%u] total_waited_cmd[%u]\n", + ps3_atomic_read(&qos_pd_mgr->valid), qos_pd_mgr->vd_id, + ps3_atomic_read(&qos_pd_mgr->pd_used_quota), + qos_pd_mgr->pd_quota, qos_pd_mgr->total_wait_cmd_cnt); + if (qos_pd_mgr->total_wait_cmd_cnt > 0) { + for (i = 1; i < qos_pd_mgr->waitq_cnt; i++) { + waitq = &qos_pd_mgr->waitqs[i]; + if (waitq->count > 0) { + ps3stor_cli_printf( + " vid[%u] waitq_cnt[%u]\n", + waitq->id, waitq->count); + } + } + } + + if (qos_pd_mgr->dev_type == PS3_DEV_TYPE_NVME_SSD) { + waitq = &qos_pd_mgr->waitqs[0]; + ps3stor_cli_printf( + " direct_used_quota[%u] direct_quota[%u] waitq_cnt[%u]\n", + ps3_atomic_read(&qos_pd_mgr->direct_used_quota), + qos_pd_mgr->direct_quota, waitq->count); + } + + } else { + ps3stor_cli_printf("disk_id[%u] is error\n", disk_id); + } +} + +static void ps3_qos_show_vd(struct ps3_instance *instance, unsigned short disk_id) +{ + unsigned char i = 0; + struct ps3_qos_vd_mgr *qos_vd_mgr = NULL; + struct ps3_qos_cq_context *qos_cq_ctx = &instance->qos_context.cq_ctx; + struct ps3_qos_softq_mgr *qos_softq_mgr = NULL; + + if (disk_id > 0 && disk_id <= instance->qos_context.max_vd_count) { + ps3stor_cli_printf("qos vd[%u] info\n", disk_id); + if (instance->qos_context.vd_ctx.qos_vd_mgrs) { + qos_vd_mgr = &instance->qos_context.vd_ctx + .qos_vd_mgrs[disk_id]; + ps3stor_cli_printf( + "-----valid[%u] quota[%u] quota_waitq[%u] exclusive[%u]\n", + qos_vd_mgr->valid, + ps3_atomic_read(&qos_vd_mgr->vd_quota), + qos_vd_mgr->vd_quota_wait_q.count, + ps3_atomic_read( + &qos_vd_mgr->exclusive_cmd_cnt)); + } + + if (qos_cq_ctx->cmdqs) { + for (i = 0; i < qos_cq_ctx->cmdq_cnt; i++) { + qos_softq_mgr = &qos_cq_ctx->cmdqs[i]; + ps3stor_cli_printf( + "-----cmdq[%u] waitq_cnt[%u]\n", i, + qos_softq_mgr->waitqs[disk_id].count); + } + } + } else { + ps3stor_cli_printf("disk_id[%u] is error\n", disk_id); + } +} + +static void ps3_qos_stat_dump(struct ps3_instance *instance, char *buf, + int total_len) +{ + int len = 0; + struct ps3_qos_tg_context *qos_tg_ctx = &instance->qos_context.tg_ctx; + struct ps3_qos_pd_context *qos_pd_ctx = &instance->qos_context.pd_ctx; + struct ps3_qos_vd_context *qos_vd_ctx = &instance->qos_context.vd_ctx; + struct ps3_qos_cq_context *qos_cq_ctx = &instance->qos_context.cq_ctx; + struct ps3_qos_softq_mgr *qos_softq_mgr = NULL; + struct ps3_qos_pd_mgr *qos_pd_mgr = NULL; + struct ps3_qos_vd_mgr *qos_vd_mgr = NULL; + struct qos_wait_queue *cmd_waitq = NULL; + unsigned short i = 0; + + len += snprintf( + buf + len, total_len - len, + "----host[%u] qos cmd statistics show begin switch[%u] total_qos_cmd[%llu]\n", + PS3_HOST(instance), instance->qos_context.qos_switch, + ps3_atomic64_read(&instance->cmd_statistics.cmd_qos_total)); + + if (qos_tg_ctx->vd_cmd_waitqs) { + len += snprintf(buf + len, total_len - len, + "tag: share[%u] mgr[%u]\n", + ps3_atomic_read(&qos_tg_ctx->share_free_cnt), + ps3_atomic_read(&qos_tg_ctx->mgr_free_cnt)); + + if (qos_tg_ctx->mgr_cmd_wait_q.count > 0) { + len += snprintf(buf + len, total_len - len, + "mgr cmd waitq count[%u]\n", + qos_tg_ctx->mgr_cmd_wait_q.count); + if (len >= total_len) + goto l_out; + } + + for (i = 1; i <= instance->qos_context.max_vd_count; i++) { + cmd_waitq = &qos_tg_ctx->vd_cmd_waitqs[i]; + if (cmd_waitq->count) { + len += snprintf(buf + len, total_len - len, + "vd[%u] cmd waitq_count[%u]\n", + i, cmd_waitq->count); + if (len >= total_len) + goto l_out; + } + } + } + + if (qos_cq_ctx->cmdqs) { + qos_softq_mgr = &qos_cq_ctx->mgrq; + len += snprintf(buf + len, total_len - len, + "mgrq: free[%u] waitq_count[%u]\n", + ps3_atomic_read(&qos_softq_mgr->free_cnt), + qos_softq_mgr->total_wait_cmd_cnt); + if (len >= total_len) + goto l_out; + + for (i = 0; i < qos_cq_ctx->cmdq_cnt; i++) { + qos_softq_mgr = &qos_cq_ctx->cmdqs[i]; + len += snprintf( + buf + len, total_len - len, + "cmdq[%u]: free[%u] waitq_count[%u]\n", i, + ps3_atomic_read(&qos_softq_mgr->free_cnt), + qos_softq_mgr->total_wait_cmd_cnt); + if (len >= total_len) + goto l_out; + } + } + + if (qos_pd_ctx->qos_pd_mgrs) { + for (i = 1; i <= instance->qos_context.max_pd_count; i++) { + qos_pd_mgr = &qos_pd_ctx->qos_pd_mgrs[i]; + if (qos_pd_mgr->total_wait_cmd_cnt) { + len += snprintf( + buf + len, total_len - len, + "pid[%u] vid[%u] valid[%u] used_quota[%u] waitq_count[%u]\n", + qos_pd_mgr->disk_id, qos_pd_mgr->vd_id, + ps3_atomic_read(&qos_pd_mgr->valid), + ps3_atomic_read( + &qos_pd_mgr->pd_used_quota), + qos_pd_mgr->total_wait_cmd_cnt); + if (len >= total_len) + goto l_out; + } + + if (qos_pd_mgr->total_waited_direct_cmd) { + len += snprintf( + buf + len, total_len - len, + "pid[%u] valid[%u] direct_used_quota[%u] waitq_count[%u]\n", + qos_pd_mgr->disk_id, qos_pd_mgr->vd_id, + ps3_atomic_read( + &qos_pd_mgr->direct_used_quota), + qos_pd_mgr->total_waited_direct_cmd); + if (len >= total_len) + goto l_out; + } + } + } + + if (qos_vd_ctx->qos_vd_mgrs) { + for (i = 1; i <= instance->qos_context.max_vd_count; i++) { + qos_vd_mgr = &qos_vd_ctx->qos_vd_mgrs[i]; + if (qos_vd_mgr->vd_quota_wait_q.count) { + len += snprintf( + buf + len, total_len - len, + "vid[%u] quota[%u] waitq_count[%u]\n", + i, + ps3_atomic_read(&qos_vd_mgr->vd_quota), + qos_vd_mgr->vd_quota_wait_q.count); + if (len >= total_len) + goto l_out; + } + } + } + + len += snprintf(buf + len, total_len - len, + "----host[%u] qos cmd statistics show end----\n", + PS3_HOST(instance)); +l_out: + return; +} + +static void ps3_qos_show_delay(struct ps3_instance *instance) +{ + struct ps3_cmd_statistics_context *ctx = &instance->cmd_statistics; + unsigned short i = 0; + + ps3stor_cli_printf("----host[%u] qos delay show begin----\n", + PS3_HOST(instance)); + ps3stor_cli_printf("%-25s %-20s %-20s %-20s\n", " ", "avg(us)", + "max(us)", "min(us)"); + for (i = PS3_QOS_PD_PRO; i < PS3_CMD_STAT_COUNT; i++) { + ps3stor_cli_printf( + "%-25s %-20llu %-20llu %-20llu\n", + ps3_cmd_stat_item_tostring((enum ps3_cmd_stat_item)i), + ctx->total_stat.stat[i].lagency.avg, + ctx->total_stat.stat[i].lagency.max_lagency, + ctx->total_stat.stat[i].lagency.min_lagency); + } + + ps3stor_cli_printf("----host[%u] qos delay show end----\n", + PS3_HOST(instance)); +} + +static void ps3_cli_qos_info(int argc, char *argv[]) +{ + struct ps3_instance *instance = NULL; + unsigned short host_no = 0; + unsigned short disk_id = 0; + char *buf = NULL; + int ret = 0; + + if (argc < 4) { + ps3stor_cli_printf("Too few args, must input 4 args!\n"); + goto l_out; + } + + if (strcmp(argv[1], "host_no") != 0) { + ps3stor_cli_printf("host_no is needed!\n"); + goto l_out; + } + + ret = kstrtou16(argv[2], 0, &host_no); + if (ret != 0) { + ps3stor_cli_printf("Can not parse host_no!\n"); + goto l_out; + } + + instance = ps3_instance_lookup(host_no); + if (instance == NULL) { + ps3stor_cli_printf("Invalid host_no %u !\n", host_no); + goto l_out; + } + + if (!instance->qos_context.inited) { + ps3stor_cli_printf("qos not support host_no %u !\n", host_no); + goto l_out; + } + + if (strcmp(argv[3], "pd") == 0) { + if (argc < 5) { + ps3stor_cli_printf("pd_id is needed!\n"); + goto l_out; + } + + ret = kstrtou16(argv[4], 0, &disk_id); + if (ret != 0) { + ps3stor_cli_printf("Can not parse disk id!\n"); + goto l_out; + } + ps3_qos_show_pd(instance, disk_id); + goto l_out; + } else if (strcmp(argv[3], "vd") == 0) { + if (argc < 5) { + ps3stor_cli_printf("vd_id is needed!\n"); + goto l_out; + } + + ret = kstrtou16(argv[4], 0, &disk_id); + if (ret != 0) { + ps3stor_cli_printf("Can not parse disk id!\n"); + goto l_out; + } + ps3_qos_show_vd(instance, disk_id); + goto l_out; + } else if (strcmp(argv[3], "all") == 0) { + buf = kzalloc(PAGE_SIZE * 2, GFP_KERNEL); + if (buf == NULL) { + ps3stor_cli_printf("malloc buf failed!\n"); + goto l_out; + } + + ps3_qos_stat_dump(instance, buf, PAGE_SIZE * 2); + ps3stor_cli_printf(buf); + } else if (strcmp(argv[3], "delay") == 0) { + ps3_qos_show_delay(instance); + } +l_out: + if (buf != NULL) { + kfree(buf); + buf = NULL; + } +} + +static void ps3_cli_special_log(int argc, char *argv[]) +{ + struct ps3_instance *instance = NULL; + unsigned short host_no = 0; + unsigned short print_switch = 0; + int ret = 0; + + if (argc < 5) { + ps3stor_cli_printf("Too few args, must input 5 args!\n"); + goto l_out; + } + + if (strcmp(argv[1], "host_no") != 0) { + ps3stor_cli_printf("host_no is needed!\n"); + goto l_out; + } + + ret = kstrtou16(argv[2], 0, &host_no); + if (ret != 0) { + ps3stor_cli_printf("Can not parse host_no!\n"); + goto l_out; + } + + instance = ps3_instance_lookup(host_no); + if (instance == NULL) { + ps3stor_cli_printf("Invalid host_no %u !\n", host_no); + goto l_out; + } + + if (strcmp(argv[3], "set") == 0) { + ret = kstrtou16(argv[4], 0, &print_switch); + if (ret != 0) { + ps3stor_cli_printf("Can not parse log_switch!\n"); + goto l_out; + } + + instance->is_print_special_log = (print_switch > 0 ? 1 : 0); + ps3stor_cli_printf("set print_special_log=%u\n", + instance->is_print_special_log); + } +l_out: + return; +} + + +void ps3_cli_debug_init(void) +{ + int ret = 0; + struct ps3_cli_debug_cmd *cmd = NULL; + unsigned int table_index = 0; + unsigned int table_size = ARRAY_SIZE(g_ps3_cli_debug_cmd_table); + + for (table_index = 0; table_index < table_size; table_index++) { + cmd = &g_ps3_cli_debug_cmd_table[table_index]; + ret = ps3stor_cli_register(cmd->func, cmd->func_name, + cmd->help); + if (ret) + pr_err("cli register failed:%s:%d\n", cmd->func_name, ret); + } +} diff --git a/drivers/scsi/linkdata/ps3stor/linux/ps3_cli_debug.h b/drivers/scsi/linkdata/ps3stor/linux/ps3_cli_debug.h new file mode 100644 index 0000000000000000000000000000000000000000..a90bf8255a9a0a116c657a97a7986ded83db14ca --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/linux/ps3_cli_debug.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _PS3_CLI_DEBUG_H_ +#define _PS3_CLI_DEBUG_H_ +#include "ps3_instance_manager.h" + +ssize_t ps3_ioc_reg_dump(struct ps3_instance *instance, char *buf); + +void ps3_cli_debug_init(void); + +void ps3_io_statis_dump_cli_cb_test(unsigned char detail); + +unsigned char ps3_get_wait_cli_flag(void); +#endif diff --git a/drivers/scsi/linkdata/ps3stor/linux/ps3_driver_log.c b/drivers/scsi/linkdata/ps3stor/linux/ps3_driver_log.c new file mode 100644 index 0000000000000000000000000000000000000000..44d5b40463632efb1bf29673303183c392312d0c --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/linux/ps3_driver_log.c @@ -0,0 +1,960 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __KERNEL__ +#include +#endif + +#include "ps3_driver_log.h" +#include "ps3_module_para.h" +#include "ps3_kernel_version.h" +#include "ps3_util.h" + +#if defined DRIVER_DEBUG && defined __KERNEL__ + +#define FILE_NAME_SIZE 128 +#define PS3_KLOG_OUT_WAIT (5 * HZ) +#define DRIVER_SWITCH_FILE +#define LOG_PATH_LEN 100 +#define DRV_LOG_FILE_SIZE_MIN_MB 10 +#define DRV_LOG_FILE_SIZE_MAX_MB 200 + +struct ps3_debug g_ps3_debug; +char g_log_path_str[LOG_PATH_LEN] = { 0 }; +char g_log_path_bin[LOG_PATH_LEN] = { 0 }; + +static inline int time_for_log(char *buff, int buf_len) +{ +#if defined(PS3_DUMP_TIME_32) + struct timeval tv; + struct tm td; + + do_gettimeofday(&tv); + time_to_tm(tv.tv_sec, -sys_tz.tz_minuteswest * 60, &td); + + return snprintf(buff, buf_len, "[%04ld-%02d-%02d;%02d:%02d:%02d.%ld]", + td.tm_year + 1900, td.tm_mon + 1, td.tm_mday, + td.tm_hour, td.tm_min, td.tm_sec, tv.tv_usec); +#else + struct timespec64 tv; + struct tm td; + + ktime_get_real_ts64(&tv); + time64_to_tm(tv.tv_sec, -sys_tz.tz_minuteswest * 60, &td); + return snprintf(buff, buf_len, "[%04ld-%02d-%02d;%02d:%02d:%02d.%ld]", + td.tm_year + 1900, td.tm_mon + 1, td.tm_mday, + td.tm_hour, td.tm_min, td.tm_sec, tv.tv_nsec * 1000); +#endif +} + +static inline int time_for_file_name(char *buff, int buf_len) +{ +#if defined(PS3_DUMP_TIME_32) + struct timeval tv; + struct tm td; + + do_gettimeofday(&tv); + time_to_tm(tv.tv_sec, -sys_tz.tz_minuteswest * 60, &td); +#else + struct timespec64 tv; + struct tm td; + + ktime_get_real_ts64(&tv); + time64_to_tm(tv.tv_sec, -sys_tz.tz_minuteswest * 60, &td); +#endif + return snprintf(buff, buf_len, "%04ld-%02d-%02d_%02d:%02d:%02d", + td.tm_year + 1900, td.tm_mon + 1, td.tm_mday, + td.tm_hour, td.tm_min, td.tm_sec); +} + +static inline char *ps3_stack_top(void) +{ +#if defined(PS3_THREAD_INFO) + unsigned long *ptr = (unsigned long *)(current->thread_info + 1); +#else + unsigned long *ptr = (unsigned long *)(task_thread_info(current) + 1); +#endif + return (char *)(ptr + 1); +} + +static inline struct ps3_thread_local *ps3_thread_local_get(struct ps3_thread_key *key) +{ + return (struct ps3_thread_local *)(ps3_stack_top() + key->offset); +} + +void ps3_thread_key_create(int size, struct ps3_thread_key *key) +{ + key->offset = g_ps3_debug.key_offset; + g_ps3_debug.key_offset += sizeof(struct ps3_thread_local) + size; +} + +void *ps3_thread_get_specific(struct ps3_thread_key *key) +{ + struct ps3_thread_local *local = ps3_thread_local_get(key); + + if (local->magic != DEBUG_TRACE_MAGIC) + return NULL; + return (void *)local->data; +} + +void ps3_thread_clear_specific(struct ps3_thread_key *key) +{ + struct ps3_thread_local *local = ps3_thread_local_get(key); + + local->magic = 0; +} + +int ps3_filter_file_add(char *name) +{ + struct debug_file *file = NULL; + + file = kmalloc(sizeof(struct debug_file), GFP_ATOMIC); + if (!file) { + ps3_print(PRINT_ERR, "kmalloc size %lu failed\n", PAGE_SIZE); + return -ENOMEM; + } + PS3_STRCPY(file->name, name, sizeof(file->name)); + INIT_LIST_HEAD(&file->list); + + list_add_rcu(&file->list, &g_ps3_debug.filter_file); + return 0; +} + +void ps3_filter_file_del(char *filename) +{ + struct debug_file *file = NULL; + + list_for_each_entry_rcu(file, &g_ps3_debug.filter_file, list) { + if (!strcmp(file->name, filename)) { + list_del_rcu(&file->list); + synchronize_rcu(); + kfree(file); + return; + } + } +} + +#ifndef PS3_CFG_RELEASE +static inline int ps3_filter_file_print(const char *filename) +{ + struct debug_file *file; + + rcu_read_lock(); + list_for_each_entry_rcu(file, &g_ps3_debug.filter_file, list) { + if (!strcmp(file->name, filename)) { + rcu_read_unlock(); + return 1; + } + } + rcu_read_unlock(); + return 0; +} + +#endif +void ps3_filter_file_clear(void) +{ + struct debug_file *file = NULL; + + do { + file = list_first_or_null_rcu(&g_ps3_debug.filter_file, + struct debug_file, list); + if (file) { + list_del_rcu(&file->list); + synchronize_rcu(); + kfree(file); + } + } while (file); +} + +int ps3_filter_func_add(char *name) +{ + struct debug_func *func = NULL; + + func = kmalloc(sizeof(struct debug_func), GFP_ATOMIC); + if (!func) { + ps3_print(PRINT_ERR, "kmalloc size %lu failed\n", PAGE_SIZE); + return -ENOMEM; + } + PS3_STRCPY(func->name, name, sizeof(func->name)); + INIT_LIST_HEAD(&func->list); + + list_add_rcu(&func->list, &g_ps3_debug.filter_func); + return 0; +} + +void ps3_filter_func_del(char *name) +{ + struct debug_func *func = NULL; + + list_for_each_entry_rcu(func, &g_ps3_debug.filter_func, list) { + if (!strcmp(func->name, name)) { + list_del_rcu(&func->list); + synchronize_rcu(); + kfree(func); + return; + } + } +} + +void ps3_filter_func_clear(void) +{ + struct debug_func *func = NULL; + + do { + func = list_first_or_null_rcu(&g_ps3_debug.filter_func, + struct debug_func, list); + if (func) { + list_del_rcu(&func->list); + synchronize_rcu(); + kfree(func); + } + } while (func); +} + +void ps3_level_set(int level) +{ + g_ps3_debug.level = level; +} + +int ps3_level_get(void) +{ + return (int)g_ps3_debug.level; +} + +#if defined(PS3_SUPPORT_DEBUG) || \ + (defined(PS3_CFG_RELEASE) && defined(PS3_CFG_OCM_DBGBUG)) || \ + (defined(PS3_CFG_RELEASE) && defined(PS3_CFG_OCM_RELEASE)) + +static void ps3_file_close(struct file **file) +{ + filp_close(*file, NULL); + *file = NULL; +} + +static int ps3_file_open(struct ps3_log *log, struct file **pp_file) +{ + struct file *file; + int flags_new = O_CREAT | O_RDWR | O_APPEND | O_LARGEFILE; + int flags_rewrite = O_CREAT | O_RDWR | O_LARGEFILE | O_TRUNC; + int err = 0; + int len = 0; + char filename[FILE_NAME_SIZE]; + static unsigned long j; +#ifdef DRIVER_SWITCH_FILE + memset(filename, 0, FILE_NAME_SIZE); + len += snprintf(filename, FILE_NAME_SIZE, "%s", log->file_path); + if (log->file_num == 0) { + time_for_file_name(filename + len, FILE_NAME_SIZE - len); + } else { + snprintf(filename + len, FILE_NAME_SIZE - len, "%04d", + log->index++); + log->index = log->index % log->file_num; + } + + if (log->file_num == 1 && log->file != NULL) { + ps3_file_close(&log->file); + log->file_pos = 0; + } +#else + memset(filename, 0, FILE_NAME_SIZE); + PS3_STRCPY(filename, path, FILE_NAME_SIZE); +#endif + if (log->file_num == 0) { + file = filp_open(filename, flags_new, 0666); + } else { + file = filp_open(filename, flags_rewrite, 0666); + if (IS_ERR(file)) { + err = (int)PTR_ERR(file); + if (err == -ENOENT) + file = filp_open(filename, flags_new, 0666); + } + } + if (IS_ERR(file)) { + err = (int)PTR_ERR(file); + if (printk_timed_ratelimit(&j, PS3_LOG_LIMIT_INTERVAL_MSEC)) { + ps3_print(PRINT_INFO, "open file:%s failed[errno:%d]\n", + filename, err); + } + goto l_out; + } + + if (!ps3_fs_requires_dev(file)) { +#if defined(PS3_SUPPORT_DEBUG) || \ + (defined(PS3_CFG_RELEASE) && defined(PS3_CFG_OCM_DBGBUG)) || \ + (defined(PS3_CFG_RELEASE) && defined(PS3_CFG_OCM_RELEASE)) + if (printk_timed_ratelimit(&j, PS3_LOG_LIMIT_INTERVAL_MSEC)) { + ps3_print( + PRINT_INFO, + "unexpected filesystem, superblock flags: 0x%x\n", + file->f_inode->i_sb->s_type->fs_flags); + } +#endif + ps3_file_close(&file); + err = -EINVAL; + goto l_out; + } + + mapping_set_gfp_mask(file->f_path.dentry->d_inode->i_mapping, GFP_NOFS); + + ps3_print(PRINT_INFO, "redirect file %s\n", filename); + + *pp_file = file; + +l_out: + return err; +} + +static void ps3_file_sync(struct file *file) +{ + struct address_space *mapping; + void *journal; + int ret = 0; + int err; + + (void)ret; + (void)err; + + if (!file || !file->f_op || !file->f_op->fsync) + goto l_end; + + journal = current->journal_info; + current->journal_info = NULL; + + mapping = file->f_mapping; + + ret = filemap_fdatawrite(mapping); + +#if defined(PS3_FILEMAP_WAIT) + mutex_lock(&mapping->host->i_mutex); + err = file->f_op->fsync(file, file->f_path.dentry, 1); + if (!ret) + ret = err; + mutex_unlock(&mapping->host->i_mutex); + err = filemap_fdatawait(mapping); + if (!ret) + ret = err; + +#else + err = file->f_op->fsync(file, 0, file->f_mapping->host->i_size, 1); +#endif + + current->journal_info = journal; + +l_end: + return; +} + +static int ps3_file_write(struct file *file, char *buf, int len) +{ + int ret = 0; + + void *journal; +#if defined(PS3_SUPPORT_FS) + mm_segment_t old_fs; +#endif + +#if defined(PS3_KERNEL_WRITE_GET_DS) + old_fs = get_fs(); + set_fs(get_ds()); +#elif defined(PS3_KERNEL_WRITE) + old_fs = get_fs(); + set_fs(KERNEL_DS); +#elif defined(PS3_VFS_WRITE) +#else + old_fs = force_uaccess_begin(); +#endif + + journal = current->journal_info; + current->journal_info = NULL; + + if (!file) { + return 0; + } + + do { +#if defined(PS3_KERNEL_WRITE_FILE_OP) + ret = file->f_op->write(file, buf, len, &file->f_pos); +#elif defined(PS3_KERNEL_WRITE_FILE_VFS) + ret = vfs_write(file, buf, len, &file->f_pos); +#else + ret = kernel_write(file, buf, len, &file->f_pos); +#endif + } while (ret == -EINTR); + + if (ret >= 0) { +#if defined(PS3_FSNOTIFY_FILE) + fsnotify_modify(file); +#elif defined(PS3_FSNOTIFY_FILE_PATH) + if (file->f_path.dentry) + fsnotify_modify(file->f_path.dentry); +#endif + } + + current->journal_info = journal; +#if defined(PS3_SET_FS) + set_fs(old_fs); +#elif defined(PS3_FORCE_UACCESS) +#else + force_uaccess_end(old_fs); +#endif + + return ret; +} + +static void ps3_klog_in(struct ps3_log *log, char *buf, const int len) +{ + int begin = 0; + int end = 0; + int free_size; + unsigned long flags; + static unsigned long j; + + spin_lock_irqsave(&log->lock, flags); + + if (log->head > log->tail) { + if (printk_timed_ratelimit(&j, PS3_LOG_LIMIT_INTERVAL_MSEC)) { + ps3_print(PRINT_INFO, + "FAILURE: log head exceeds log tail\n"); + PS3_BUG_NO_SYNC(); + } + } + + free_size = log->buf_size - (log->tail - log->head); + + if (free_size <= len) { + log->is_drop = 1; + spin_unlock_irqrestore(&log->lock, flags); + return; + } + + begin = log->tail % log->buf_size; + end = (log->tail + len) % log->buf_size; + + if (begin < end) { + memcpy(log->buf + begin, buf, len); + } else { + memcpy(log->buf + begin, buf, log->buf_size - begin); + memcpy(log->buf, buf + log->buf_size - begin, end); + } + + log->tail = log->tail + len; + + spin_unlock_irqrestore(&log->lock, flags); +} + +static void ps3_klog_out(struct ps3_log *log) +{ + int len = 0; + int rc = 0; + long long tail; + int begin; + int end; + int schedule_count_th = 0; + const int max_loop = 4096; + static unsigned long j; + +#ifdef DRIVER_SWITCH_FILE + struct file *file = NULL; +#endif + + if (log->file == NULL) { + rc = ps3_file_open(log, &log->file); + if (log->file != NULL) + log->file_pos = 0; + else + return; + } + + do { + tail = log->tail; + begin = log->head % log->buf_size; + end = tail % log->buf_size; + len = 0; + rc = 0; + + schedule_count_th++; + if (schedule_count_th >= max_loop) { + schedule_count_th = 0; + schedule_timeout_interruptible(PS3_KLOG_OUT_WAIT); + } + + if (log->is_drop) { + rc = ps3_file_write(log->file, DEBUG_DROP_LOG_STRING, + strlen(DEBUG_DROP_LOG_STRING)); + if (rc < 0) + break; + log->is_drop = 0; + } + + if (begin < end) { + rc = ps3_file_write(log->file, log->buf + begin, + end - begin); + if (rc > 0) + len += rc; + } else if (begin > end) { + rc = ps3_file_write(log->file, log->buf + begin, + log->buf_size - begin); + if (rc > 0) { + len += rc; + rc = ps3_file_write(log->file, log->buf, end); + if (rc > 0) + len += rc; + } + } + log->head += len; + log->file_pos += len; + + LOG_BUG_ON(log->head > log->tail, + "FAILURE: log head exceeds log tail\n"); + } while (log->head != log->tail && rc > 0); + + if (rc < 0) { + if (printk_timed_ratelimit(&j, PS3_LOG_LIMIT_INTERVAL_MSEC)) { + ps3_print(PRINT_INFO, "write file %s error %d\n", + log->file_path, rc); + } + return; + } + +#ifdef DRIVER_SWITCH_FILE + if (log->file_pos >= log->file_size) { + rc = ps3_file_open(log, &file); + if (rc >= 0 && log->file != NULL && log->file_num != 1) { + ps3_file_close(&log->file); + log->file = file; + log->file_pos = 0; + } + } +#endif +} + +static int ps3_klog_flush(void *arg) +{ + int i; + + while (!kthread_should_stop()) { + schedule_timeout_interruptible(PS3_KLOG_OUT_WAIT); + + for (i = 0; i < ARRAY_SIZE(g_ps3_debug.log); i++) + ps3_klog_out(&g_ps3_debug.log[i]); + } + return 0; +} + +static int ps3_klog_init(struct ps3_log *log, long long buf_size, char *file_path, + long long file_size, unsigned int file_num) +{ + int rc = 0; + + memset(log, 0, sizeof(*log)); + spin_lock_init(&log->lock); + + log->buf = vmalloc(buf_size + PAGE_SIZE); + if (!log->buf) { + rc = -ENOMEM; + goto l_end; + } + + log->file = NULL; + log->head = 0; + log->tail = 0; + log->buf_size = buf_size; + + log->file_path = file_path; + log->file_pos = 0; + log->file_size = file_size; + log->file_num = file_num; + log->index = 0; +l_end: + return rc; +} + +static void ps3_klog_exit(struct ps3_log *log) +{ + if (log->buf) + vfree(log->buf); + if (log->file) + ps3_file_close(&log->file); +} + +static inline char *ps3_file_name_locale(char *file) +{ + char *p_slash = strrchr(file, '/'); + + return (p_slash == NULL) ? file : (p_slash + 1); +} + +void ps3_log_string(enum debug_level level, const char *file, int line, + const char *fmt, ...) +{ + struct ps3_ctxt *ctxt = NULL; + char *buf = NULL; + int len = 0; + unsigned long flags = 0; + + va_list args; + + if (level > g_ps3_debug.level) { +#ifndef PS3_CFG_RELEASE + if (!ps3_filter_file_print(file)) + return; +#else + return; +#endif + } + + if (!in_interrupt()) + local_irq_save(flags); + + ctxt = per_cpu_ptr(g_ps3_debug.ctxt, get_cpu()); + put_cpu(); + + buf = ctxt->buff; + + len = snprintf(buf, PAGE_SIZE, "%s", ps3_debug_level_name(level)); + len += time_for_log(buf + len, PAGE_SIZE - len); + len += snprintf(buf + len, PAGE_SIZE - len, + "[%d][%d]%s:%4d:", raw_smp_processor_id(), current->pid, + ps3_file_name_locale((char *)file), line); + + va_start(args, fmt); + len += vsnprintf(buf + len, PAGE_SIZE - len, fmt, args); + va_end(args); + + if (!in_interrupt()) + local_irq_restore(flags); + + if (ps3_log_tty_query()) { + if (buf[0] == 'I' || buf[0] == 'W') { + pr_warn_ratelimited("%s", + buf + LOG_INFO_PREFIX_LEN); + } else if (buf[0] == 'E') { + pr_warn_ratelimited("%s", + buf + LOG_ERROR_PREFIX_LEN); + } + } + ps3_klog_in(&g_ps3_debug.log[DEBUG_TYPE_STRING], buf, len); + + wake_up_process(g_ps3_debug.task); +} + +void ps3_log_binary(const char *file, int line, char *ptr, int size, char *str) +{ +#define LINE_TOTAL 16 + struct ps3_ctxt *ctxt = NULL; + char *buf = NULL; + int len = 0; + int i; + unsigned long flags = 0; + + if (!in_interrupt()) + local_irq_save(flags); + + ctxt = per_cpu_ptr(g_ps3_debug.ctxt, get_cpu()); + put_cpu(); + + buf = ctxt->buff; + + len += time_for_log(buf + len, PAGE_SIZE - len); + len += snprintf(buf + len, PAGE_SIZE - len, "[%d]%s:%d, size:%d, ", + current->pid, ps3_file_name_locale((char *)file), line, + size); + + len += snprintf(buf + len, PAGE_SIZE - len, "%s", str); + + for (i = 0; i < size; i++) { + if (i % LINE_TOTAL == 0) + len += snprintf(buf + len, PAGE_SIZE - len, "%08x ", i); + + len += snprintf(buf + len, PAGE_SIZE - len, "%02hhx", ptr[i]); + if ((i % LINE_TOTAL) == (LINE_TOTAL - 1) || i == (size - 1)) + len += snprintf(buf + len, PAGE_SIZE - len, "\n"); + else if ((i % LINE_TOTAL) == (LINE_TOTAL / 2 - 1)) + len += snprintf(buf + len, PAGE_SIZE - len, " "); + else + len += snprintf(buf + len, PAGE_SIZE - len, " "); + } + + if (!in_interrupt()) + local_irq_restore(flags); + + ps3_klog_in(&g_ps3_debug.log[DEBUG_TYPE_BINARY], buf, len); + + wake_up_process(g_ps3_debug.task); +} + +void ps3_log_sync(void) +{ + ps3_file_sync(g_ps3_debug.log[DEBUG_TYPE_STRING].file); + ps3_file_sync(g_ps3_debug.log[DEBUG_TYPE_BINARY].file); +} + +int ps3_debug_init(void) +{ + struct task_struct *task = NULL; + struct ps3_ctxt *ctxt = NULL; + int rc = 0; + int i; + int nid; + unsigned int file_num = 0; + unsigned int log_path_len = 0; + unsigned int input_log_space = ps3_log_space_size_query(); + unsigned int input_log_file_size = ps3_log_file_size_query(); + unsigned int log_file_size = 0; + char *log_path_p = NULL; + struct ps3_log *log_bin = &g_ps3_debug.log[DEBUG_TYPE_BINARY]; + struct ps3_log *log_str = &g_ps3_debug.log[DEBUG_TYPE_STRING]; + + INIT_LIST_HEAD(&g_ps3_debug.filter_file); + INIT_LIST_HEAD(&g_ps3_debug.filter_func); + + g_ps3_debug.level = ps3_log_level_query(); + g_ps3_debug.ctxt = alloc_percpu(struct ps3_ctxt); + if (!g_ps3_debug.ctxt) { + rc = -ENOMEM; + ps3_print(PRINT_ERR, "alloc percpu failed\n"); + goto l_end; + } + + for_each_possible_cpu(i) { + ctxt = per_cpu_ptr(g_ps3_debug.ctxt, i); + memset(ctxt, 0, sizeof(*ctxt)); + } + + for_each_possible_cpu(i) { + ctxt = per_cpu_ptr(g_ps3_debug.ctxt, i); + nid = cpu_to_node(i); + + ctxt->page = alloc_pages_node(nid, GFP_ATOMIC, 0); + if (!ctxt->page) { + rc = -ENOMEM; + ps3_print(PRINT_ERR, "kmalloc size %lu failed\n", + PAGE_SIZE); + goto l_free_cpu_buff; + } + ctxt->buff = PS3_KMAP(ctxt->page); + } + + log_path_p = ps3_log_path_query(); + log_path_len = strlen(log_path_p); + if (log_path_p != NULL && log_path_p[0] == '/') { + if (log_path_p[log_path_len - 1] == '/') { + snprintf(g_log_path_str, LOG_PATH_LEN, "%s%s.", + log_path_p, LOG_FILE_PREFIX); + snprintf(g_log_path_bin, LOG_PATH_LEN, "%s%s.", + log_path_p, BINARY_FILE_PREFIX); + } else { + snprintf(g_log_path_str, LOG_PATH_LEN, "%s/%s.", + log_path_p, LOG_FILE_PREFIX); + snprintf(g_log_path_bin, LOG_PATH_LEN, "%s/%s.", + log_path_p, BINARY_FILE_PREFIX); + } + } else { + snprintf(g_log_path_str, LOG_PATH_LEN, "%s.", LOG_FILE_PATH); + snprintf(g_log_path_bin, LOG_PATH_LEN, "%s.", BINARY_FILE_PATH); + } + if (input_log_file_size < DRV_LOG_FILE_SIZE_MIN_MB || + input_log_file_size > DRV_LOG_FILE_SIZE_MAX_MB) { + ps3_log_file_size_modify(LOG_FILE_SIZE >> MEGABYTE); + input_log_file_size = LOG_FILE_SIZE >> MEGABYTE; + } + if (input_log_space && input_log_space < input_log_file_size) { + ps3_log_file_size_modify(input_log_space); + input_log_file_size = input_log_space; + } + log_file_size = input_log_file_size << MEGABYTE; + + if (input_log_space) { + file_num = input_log_space / input_log_file_size; + if (file_num == 0) { + ps3_print(PRINT_ERR, "filenum shouldnot be 0\n"); + PS3_BUG(); + } + } else { + file_num = 0; + } + + rc = ps3_klog_init(log_str, LOG_BUF_SIZE, g_log_path_str, log_file_size, + file_num); + if (rc < 0) + goto l_free_cpu_buff; + + rc = ps3_klog_init(log_bin, BIN_BUF_SIZE, g_log_path_bin, + BINARY_FILE_SIZE, 0); + if (rc < 0) + goto l_free_string; + + task = kthread_create(ps3_klog_flush, NULL, "ps3_klog_flush"); + if (IS_ERR(task)) { + rc = (int)PTR_ERR(task); + ps3_print(PRINT_ERR, "Create kernel thread, err: %d\n", rc); + goto l_free_binary; + } + wake_up_process(task); + g_ps3_debug.task = task; + rc = 0; + ps3_print(PRINT_INFO, + "PS3 debug init logpath[%s] strlogsize[%dM] filenum[%d]\n", + g_log_path_str, (log_file_size >> MEGABYTE), + log_str->file_num); +l_end: + return rc; + +l_free_binary: + ps3_klog_exit(&g_ps3_debug.log[DEBUG_TYPE_BINARY]); + +l_free_string: + ps3_klog_exit(&g_ps3_debug.log[DEBUG_TYPE_STRING]); + +l_free_cpu_buff: + for_each_possible_cpu(i) { + ctxt = per_cpu_ptr(g_ps3_debug.ctxt, i); + if (ctxt && ctxt->page) { + PS3_KUNMAP(ctxt->page, ctxt->buff); + __free_page(ctxt->page); + } + } + free_percpu(g_ps3_debug.ctxt); + goto l_end; +} + +void ps3_debug_exit(void) +{ + int i = 0; + struct ps3_ctxt *ctxt; + + if (g_ps3_debug.task == NULL) + return; + + kthread_stop(g_ps3_debug.task); + + for (i = 0; i < ARRAY_SIZE(g_ps3_debug.log); i++) + ps3_klog_exit(&g_ps3_debug.log[i]); + + if (g_ps3_debug.ctxt) { + for_each_possible_cpu(i) { + ctxt = per_cpu_ptr(g_ps3_debug.ctxt, i); + if (ctxt && ctxt->page) { + PS3_KUNMAP(ctxt->page, ctxt->buff); + __free_page(ctxt->page); + } + } + + free_percpu(g_ps3_debug.ctxt); + g_ps3_debug.ctxt = NULL; + } +} +#else + +void ps3_log_sync(void) +{ +} + +int ps3_debug_init(void) +{ + g_ps3_debug.level = LEVEL_WARN; + return 0; +}; + +void ps3_debug_exit(void) +{ +} + +#endif + +#else + +int g_ps3_log_level = LEVEL_INFO; + +void ps3_thread_key_create(int size, struct ps3_thread_key *key) +{ + (void)size; + (void)key; +} + +void *ps3_thread_get_specific(struct ps3_thread_key *key) +{ + return key; +} + +void ps3_thread_clear_specific(struct ps3_thread_key *key) +{ + (void)key; +} + +int ps3_filter_file_add(char *name) +{ + (void)name; + return 0; +} +void ps3_filter_file_del(char *name) +{ + (void)name; +} +void ps3_filter_file_clear(void) +{ +} + +int ps3_filter_func_add(char *name) +{ + (void)name; + return 0; +} +void ps3_filter_func_del(char *name) +{ + (void)name; +} +void ps3_filter_func_clear(void) +{ +} +void ps3_level_set(int level) +{ + g_ps3_log_level = level; +} + +int ps3_level_get(void) +{ + return g_ps3_log_level; +} + +void ps3_log_sync(void) +{ +} + +int ps3_debug_init(void) +{ + g_ps3_log_level = LEVEL_WARN; + return 0; +}; + +void ps3_debug_exit(void) +{ +} + +#endif + +static int g_ramfs_test_enable; +int ps3_ramfs_test_query(void) +{ + return g_ramfs_test_enable; +} + +void ps3_ramfs_test_store(int val) +{ + g_ramfs_test_enable = val; +} diff --git a/drivers/scsi/linkdata/ps3stor/linux/ps3_driver_log.h b/drivers/scsi/linkdata/ps3stor/linux/ps3_driver_log.h new file mode 100644 index 0000000000000000000000000000000000000000..2e49776945bb0e5f3a8b76f962051de265b84ff0 --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/linux/ps3_driver_log.h @@ -0,0 +1,655 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _PS3_DRIVER_LOG_H_ +#define _PS3_DRIVER_LOG_H_ + +#include +#include +#include +#include + +#include "ps3_htp_def.h" +#include "ps3_platform_utils.h" +#ifdef __cplusplus +extern "C" { +#endif + +#define PS3_HOST(ins) ((ins)->host->host_no) +#define DRIVER_DEBUG + +#define LOG_INFO_PREFIX_LEN 32 +#define LOG_ERROR_PREFIX_LEN 33 +#define MEGABYTE 20 + +enum debug_level { + LEVEL_ERROR, + LEVEL_WARN, + LEVEL_INFO, + LEVEL_DEBUG, +}; + +static inline const char *ps3_debug_level_name(enum debug_level lv) +{ + static const char * const level[] = { + [LEVEL_ERROR] = "ERROR", + [LEVEL_WARN] = "WARN", + [LEVEL_INFO] = "INFO", + [LEVEL_DEBUG] = "DBG", + }; + + return level[lv]; +} + +int ps3_level_get(void); +#define PS3_LOG_LIMIT_INTERVAL_MSEC (24 * 60 * 60 * 1000) +#ifdef __KERNEL__ + +#define PRINT_DEBUG LEVEL_DEBUG +#define PRINT_INFO LEVEL_INFO +#define PRINT_WARN LEVEL_WARN +#define PRINT_ERR LEVEL_ERROR + +#define ps3_print(level, fmt, ...) \ + do { \ + if (level == LEVEL_DEBUG) { \ + pr_debug("[PS3STOR]%s():%d;" fmt, \ + __func__, __LINE__, ##__VA_ARGS__); \ + } else if (level == LEVEL_INFO) { \ + pr_info("[PS3STOR]%s():%d;" fmt, \ + __func__, __LINE__, ##__VA_ARGS__); \ + } else if (level == LEVEL_WARN) { \ + pr_warn("[PS3STOR]%s():%d;" fmt, \ + __func__, __LINE__, ##__VA_ARGS__); \ + } else if (level == LEVEL_ERROR) { \ + pr_err("[PS3STOR]%s():%d;" fmt, \ + __func__, __LINE__, ##__VA_ARGS__); \ + } \ + } while (0) + +#define ps3_printk(level, fmt, ...) \ + do { \ + if (level <= ps3_level_get()) { \ + if (level == LEVEL_DEBUG) { \ + pr_debug("[PS3STOR][%u]%d;" fmt, \ + current->pid, __LINE__, ##__VA_ARGS__); \ + } else if (level == LEVEL_INFO) { \ + pr_info("[PS3STOR][%u]%d;" fmt, \ + current->pid, __LINE__, ##__VA_ARGS__); \ + } else if (level == LEVEL_WARN) { \ + pr_warn("[PS3STOR][%u]%d;" fmt, \ + current->pid, __LINE__, ##__VA_ARGS__); \ + } else if (level == LEVEL_ERROR) { \ + pr_err("[PS3STOR][%u]%d;" fmt, \ + current->pid, __LINE__, ##__VA_ARGS__); \ + } \ + } \ + } while (0) + +#define ps3_printk_ratelimited(level, fmt, ...) \ + do { \ + if (level <= ps3_level_get()) { \ + if (level == LEVEL_INFO) { \ + pr_info_ratelimited( \ + "[PS3STOR][%u]%d;" fmt, \ + current->pid, __LINE__, \ + ##__VA_ARGS__); \ + } else if (level == LEVEL_WARN) { \ + pr_warn_ratelimited( \ + "[PS3STOR][%u]%d;" fmt, \ + current->pid, __LINE__, \ + ##__VA_ARGS__); \ + } else if (level == LEVEL_ERROR) { \ + pr_err_ratelimited( \ + "[PS3STOR][%u]%d;" fmt, \ + current->pid, __LINE__, \ + ##__VA_ARGS__); \ + } \ + } \ + } while (0) + +#else + +#define PRINT_DEBUG LEVEL_DEBUG +#define PRINT_INFO LEVEL_INFO +#define PRINT_WARN LEVEL_WARN +#define PRINT_ERR LEVEL_ERROR + +#include +#include +#include +#define __percpu + +static inline unsigned long long get_now_ms(void) +{ + struct timeval tv; + unsigned long long timestamp = 0; + + gettimeofday(&tv, NULL); + timestamp = tv.tv_sec * 1000 + tv.tv_usec / 1000; + return timestamp; +} + +#define filename_printf(x) (strrchr((x), '/') ? strrchr((x), '/') + 1 : (x)) + +#define ps3_print(lock_chk, level, fmt, ...) \ + do { \ + if (level != LEVEL_DEBUG && lock_chk) { \ + EXPECT_EQ(ps3_get_irq_spin_lock_count(), 0); \ + } \ + if (level <= ps3_level_get()) { \ + if (level == LEVEL_DEBUG) { \ + printf("DEBUG:%llu:%s:%s():%d:[%lu];" fmt, \ + get_now_ms(), \ + filename_printf(__FILE__), \ + __func__, __LINE__, pthread_self(), \ + ##__VA_ARGS__); \ + } else if (level == LEVEL_INFO) { \ + printf("INFO:%llu:%s:%s():%d:[%lu];" fmt, \ + get_now_ms(), \ + filename_printf(__FILE__), \ + __func__, __LINE__, pthread_self(), \ + ##__VA_ARGS__); \ + } else if (level == LEVEL_WARN) { \ + printf("WARN:%llu:%s:%s():%d:[%lu];" fmt, \ + get_now_ms(), \ + filename_printf(__FILE__), \ + __func__, __LINE__, pthread_self(), \ + ##__VA_ARGS__); \ + } else if (level == LEVEL_ERROR) { \ + printf("ERROR:%llu:%s:%s():%d:[%lu];" fmt, \ + get_now_ms(), \ + filename_printf(__FILE__), \ + __func__, __LINE__, pthread_self(), \ + ##__VA_ARGS__); \ + } \ + } \ + } while (0) + +#endif + +#define LOG_BUG_ON(cond, fmt, ...) \ + do { \ + if ((cond)) { \ + LOG_ERROR(fmt, ##__VA_ARGS__); \ + LOG_SYNC(); \ + BUG(); \ + } \ + } while (0) + +#define DEBUG_TRACE_MAGIC 0x456789 +#define LOG_BUF_SIZE (1024LL << 11) +#define BIN_BUF_SIZE (1024LL << 10) + +#define LOG_FILE_SIZE (200LL << 20) +#define LOG_FILE_PATH "/var/log/ps3sas_drv.log" +#define LOG_FILE_PREFIX "ps3sas_drv.log" +#define BINARY_FILE_PREFIX "ps3sas_drv.bin" + +#define BINARY_FILE_SIZE (200LL << 20) +#define BINARY_FILE_PATH "/var/log/ps3sas_drv.bin" + +#define DEBUG_DROP_LOG_STRING "\nwarnning:drop some logs\n\n" + +enum { + DEBUG_TYPE_STRING, + DEBUG_TYPE_BINARY, + DEBUG_TYPE_NR, +}; + +struct debug_func { + struct list_head list; + char name[64]; +}; + +struct debug_file { + struct list_head list; + char name[64]; +}; + +struct ps3_log { + struct { + char *buf; + int buf_size; + long long head; + long long tail; + spinlock_t lock; + unsigned char is_drop; + }; + struct { + char *file_path; + struct file *file; + long long file_pos; + long long file_size; + unsigned int file_num; + unsigned int index; + }; +}; + +struct ps3_thread_local { + int magic; + char data[]; +}; + +struct ps3_ctxt { + struct page *page; + void *buff; +}; + +struct ps3_thread_key { + int offset; +}; + +struct ps3_debug { + enum debug_level level; + unsigned short key_offset; + struct ps3_ctxt __percpu *ctxt; + struct list_head filter_func; + struct list_head filter_file; + struct task_struct *task; + struct ps3_log log[DEBUG_TYPE_NR]; +}; + +void ps3_thread_key_create(int size, struct ps3_thread_key *key); +void *ps3_thread_get_specific(struct ps3_thread_key *key); +void ps3_thread_clear_specific(struct ps3_thread_key *key); + +int ps3_filter_file_add(char *name); +void ps3_filter_file_del(char *name); +void ps3_filter_file_clear(void); + +int ps3_filter_func_add(char *name); +void ps3_filter_func_del(char *name); +void ps3_filter_func_clear(void); +void ps3_level_set(int level); +int ps3_level_get(void); + +int ps3_debug_init(void); +void ps3_debug_exit(void); + +void ps3_log_string(enum debug_level level, const char *file, int line, + const char *fmt, ...); + +void ps3_log_binary(const char *file, int line, char *ptr, int size, char *str); + +void ps3_log_sync(void); + +int ps3_ramfs_test_query(void); +void ps3_ramfs_test_store(int val); + +#if defined DRIVER_DEBUG && defined __KERNEL__ + +#if defined(PS3_SUPPORT_DEBUG) || \ + (defined(PS3_CFG_RELEASE) && defined(PS3_CFG_OCM_DBGBUG)) || \ + (defined(PS3_CFG_RELEASE) && defined(PS3_CFG_OCM_RELEASE)) + +#define WRITE_LOG(level, fmt, ...) \ + ps3_log_string(level, __FILE__, __LINE__, fmt, ##__VA_ARGS__) + +#define LOG_DEBUG(fmt, ...) WRITE_LOG(LEVEL_DEBUG, fmt, ##__VA_ARGS__) +#define LOG_INFO(fmt, ...) WRITE_LOG(LEVEL_INFO, fmt, ##__VA_ARGS__) +#define LOG_WARN(fmt, ...) WRITE_LOG(LEVEL_WARN, fmt, ##__VA_ARGS__) +#define LOG_ERROR(fmt, ...) WRITE_LOG(LEVEL_ERROR, fmt, ##__VA_ARGS__) + +#define LOG2_DEBUG(fmt, ...) \ + do { \ + WRITE_LOG(LEVEL_DEBUG, fmt, ##__VA_ARGS__); \ + ps3_print(PRINT_DEBUG, fmt, ##__VA_ARGS__); \ + } while (0) +#define LOG2_INFO(fmt, ...) \ + do { \ + WRITE_LOG(LEVEL_INFO, fmt, ##__VA_ARGS__); \ + ps3_print(PRINT_INFO, fmt, ##__VA_ARGS__); \ + } while (0) +#define LOG2_WARN(fmt, ...) \ + do { \ + WRITE_LOG(LEVEL_WARN, fmt, ##__VA_ARGS__); \ + ps3_print(PRINT_WARN, fmt, ##__VA_ARGS__); \ + } while (0) +#define LOG2_ERROR(fmt, ...) \ + do { \ + WRITE_LOG(LEVEL_ERROR, fmt, ##__VA_ARGS__); \ + ps3_print(PRINT_ERR, fmt, ##__VA_ARGS__); \ + } while (0) + +#define LOG_LEVEL(log_lvl, fmt, ...) WRITE_LOG(log_lvl, fmt, ##__VA_ARGS__) + +#define LOG_INFO_LIM(fmt, ...) WRITE_LOG(LEVEL_INFO, fmt, ##__VA_ARGS__) +#define LOG_WARN_LIM(fmt, ...) WRITE_LOG(LEVEL_WARN, fmt, ##__VA_ARGS__) +#define LOG_ERROR_LIM(fmt, ...) WRITE_LOG(LEVEL_ERROR, fmt, ##__VA_ARGS__) + +#define LOG_INFO_TIME_LIM(caller_jiffies, time, fmt, ...) \ + do { \ + (void)caller_jiffies; \ + (void)time; \ + WRITE_LOG(LEVEL_INFO, fmt, ##__VA_ARGS__); \ + } while (0) +#define LOG_WARN_TIME_LIM(caller_jiffies, time, fmt, ...) \ + do { \ + (void)caller_jiffies; \ + (void)time; \ + WRITE_LOG(LEVEL_WARN, fmt, ##__VA_ARGS__); \ + } while (0) +#define LOG_ERROR_TIME_LIM(caller_jiffies, time, fmt, ...) \ + do { \ + (void)caller_jiffies; \ + (void)time; \ + WRITE_LOG(LEVEL_ERROR, fmt, ##__VA_ARGS__); \ + } while (0) + +#define LOG2_INFO_LIM(fmt, ...) WRITE_LOG(LEVEL_INFO, fmt, ##__VA_ARGS__) +#define LOG2_WARN_LIM(fmt, ...) WRITE_LOG(LEVEL_WARN, fmt, ##__VA_ARGS__) +#define LOG2_ERROR_LIM(fmt, ...) WRITE_LOG(LEVEL_ERROR, fmt, ##__VA_ARGS__) + +#define LOG_FILE_INFO(fmt, ...) WRITE_LOG(LEVEL_INFO, fmt, ##__VA_ARGS__) +#define LOG_FILE_WARN(fmt, ...) WRITE_LOG(LEVEL_WARN, fmt, ##__VA_ARGS__) +#define LOG_FILE_ERROR(fmt, ...) WRITE_LOG(LEVEL_ERROR, fmt, ##__VA_ARGS__) + +#define LOG_SPC_ERROR(fmt, ...) WRITE_LOG(LEVEL_ERROR, fmt, ##__VA_ARGS__) + +#define LOG_INFO_IN_IRQ(ins, fmt, ...) WRITE_LOG(LEVEL_INFO, fmt, ##__VA_ARGS__) +#define LOG_WARN_IN_IRQ(ins, fmt, ...) WRITE_LOG(LEVEL_WARN, fmt, ##__VA_ARGS__) +#define LOG_ERROR_IN_IRQ(ins, fmt, ...) \ + WRITE_LOG(LEVEL_ERROR, fmt, ##__VA_ARGS__) + +#define LOG_INFO_LIM_WITH_CHECK(ins, need_prk_err, fmt, ...) \ + WRITE_LOG(LEVEL_INFO, fmt, ##__VA_ARGS__) +#define LOG_WARN_LIM_WITH_CHECK(ins, need_prk_err, fmt, ...) \ + WRITE_LOG(LEVEL_WARN, fmt, ##__VA_ARGS__) +#define LOG_ERROR_LIM_WITH_CHECK(ins, need_prk_err, fmt, ...) \ + WRITE_LOG(LEVEL_ERROR, fmt, ##__VA_ARGS__) + +#define LOG_SYNC() ps3_log_sync() +#define DATA_DUMP(ptr, size, str) \ + ps3_log_binary(__FILE__, __LINE__, (char *)ptr, size, str) +#else + +#define LOG_DEBUG(fmt, ...) ps3_printk(LEVEL_DEBUG, fmt, ##__VA_ARGS__) +#define LOG_INFO(fmt, ...) ps3_printk(LEVEL_INFO, fmt, ##__VA_ARGS__) +#define LOG_WARN(fmt, ...) ps3_printk(LEVEL_WARN, fmt, ##__VA_ARGS__) +#define LOG_ERROR(fmt, ...) ps3_printk(LEVEL_ERROR, fmt, ##__VA_ARGS__) + +#define LOG2_DEBUG(fmt, ...) ps3_print(PRINT_DEBUG, fmt, ##__VA_ARGS__) +#define LOG2_INFO(fmt, ...) ps3_print(PRINT_INFO, fmt, ##__VA_ARGS__) +#define LOG2_WARN(fmt, ...) ps3_print(PRINT_WARN, fmt, ##__VA_ARGS__) +#define LOG2_ERROR(fmt, ...) ps3_print(PRINT_ERR, fmt, ##__VA_ARGS__) + +#define LOG_INFO_LIM(fmt, ...) \ + ps3_printk_ratelimited(LEVEL_INFO, fmt, ##__VA_ARGS__) +#define LOG_WARN_LIM(fmt, ...) \ + ps3_printk_ratelimited(LEVEL_WARN, fmt, ##__VA_ARGS__) +#define LOG_ERROR_LIM(fmt, ...) \ + ps3_printk_ratelimited(LEVEL_ERROR, fmt, ##__VA_ARGS__) + +#define LOG_INFO_TIME_LIM(caller_jiffies, time, fmt, ...) \ + do { \ + if (printk_timed_ratelimit(caller_jiffies, time)) { \ + ps3_printk(LEVEL_INFO, fmt, ##__VA_ARGS__); \ + } \ + } while (0) +#define LOG_WARN_TIME_LIM(caller_jiffies, time, fmt, ...) \ + do { \ + if (printk_timed_ratelimit(caller_jiffies, time)) { \ + ps3_printk(LEVEL_WARN, fmt, ##__VA_ARGS__); \ + } \ + } while (0) +#define LOG_ERROR_TIME_LIM(caller_jiffies, time, fmt, ...) \ + do { \ + if (printk_timed_ratelimit(caller_jiffies, time)) { \ + ps3_printk(LEVEL_ERROR, fmt, ##__VA_ARGS__); \ + } \ + } while (0) + +#define LOG2_INFO_LIM(fmt, ...) LOG_INFO_LIM(fmt, ##__VA_ARGS__) +#define LOG2_WARN_LIM(fmt, ...) LOG_WARN_LIM(fmt, ##__VA_ARGS__) +#define LOG2_ERROR_LIM(fmt, ...) LOG_ERROR_LIM(fmt, ##__VA_ARGS__) +#define LOG_SPC_ERROR(fmt, ...) LOG_INFO(fmt, ##__VA_ARGS__) + +#define LOG_FILE_INFO(fmt, ...) +#define LOG_FILE_WARN(fmt, ...) +#define LOG_FILE_ERROR(fmt, ...) + +#define LOG_LEVEL(log_lvl, fmt, ...) ps3_printk(log_lvl, fmt, ##__VA_ARGS__) + +#define LOG_INFO_IN_IRQ(ins, fmt, ...) \ + do { \ + if ((ins)->is_irq_prk_support) { \ + ps3_printk(LEVEL_INFO, fmt, ##__VA_ARGS__); \ + } \ + } while (0) + +#define LOG_WARN_IN_IRQ(ins, fmt, ...) \ + do { \ + if ((ins)->is_irq_prk_support) { \ + ps3_printk(LEVEL_WARN, fmt, ##__VA_ARGS__); \ + } \ + } while (0) + +#define LOG_ERROR_IN_IRQ(ins, fmt, ...) \ + do { \ + if ((ins)->is_irq_prk_support) { \ + ps3_printk(LEVEL_ERROR, fmt, ##__VA_ARGS__); \ + } \ + } while (0) +#define LOG_INFO_LIM_WITH_CHECK(ins, need_prk_err, fmt, ...) \ + do { \ + if (need_prk_err) { \ + LOG_INFO_LIM(fmt, ##__VA_ARGS__); \ + } else { \ + LOG_INFO_IN_IRQ((ins), fmt, ##__VA_ARGS__); \ + } \ + } while (0) + +#define LOG_WARN_LIM_WITH_CHECK(ins, need_prk_err, fmt, ...) \ + do { \ + if (need_prk_err) { \ + LOG_WARN_LIM(fmt, ##__VA_ARGS__); \ + } else { \ + LOG_WARN_IN_IRQ((ins), fmt, ##__VA_ARGS__); \ + } \ + } while (0) + +#define LOG_ERROR_LIM_WITH_CHECK(ins, need_prk_err, fmt, ...) \ + do { \ + if (need_prk_err) { \ + LOG_ERROR_LIM(fmt, ##__VA_ARGS__); \ + } else { \ + LOG_ERROR_IN_IRQ((ins), fmt, ##__VA_ARGS__); \ + } \ + } while (0) + +#define LOG_SYNC() +#define DATA_DUMP(ptr, size, str) +#endif + +static inline unsigned char ps3_fs_requires_dev(struct file *fp) +{ + if (ps3_ramfs_test_query()) + return PS3_FALSE; + return (fp->f_inode->i_sb->s_type->fs_flags & FS_REQUIRES_DEV); +} + +#else + +#define LOG_DEBUG(fmt, ...) ps3_print(PS3_TRUE, PRINT_DEBUG, fmt, ##__VA_ARGS__) +#define LOG_INFO(fmt, ...) ps3_print(PS3_TRUE, PRINT_INFO, fmt, ##__VA_ARGS__) +#define LOG_WARN(fmt, ...) ps3_print(PS3_TRUE, PRINT_WARN, fmt, ##__VA_ARGS__) +#define LOG_ERROR(fmt, ...) ps3_print(PS3_TRUE, PRINT_ERR, fmt, ##__VA_ARGS__) + +#define LOG2_DEBUG(fmt, ...) \ + ps3_print(PS3_TRUE, PRINT_DEBUG, fmt, ##__VA_ARGS__) +#define LOG2_INFO(fmt, ...) ps3_print(PS3_TRUE, PRINT_INFO, fmt, ##__VA_ARGS__) +#define LOG2_WARN(fmt, ...) ps3_print(PS3_TRUE, PRINT_WARN, fmt, ##__VA_ARGS__) +#define LOG2_ERROR(fmt, ...) ps3_print(PS3_TRUE, PRINT_ERR, fmt, ##__VA_ARGS__) + +#define LOG_LEVEL(log_lvl, fmt, ...) \ + ps3_print(PS3_TRUE, log_lvl, fmt, ##__VA_ARGS__) + +#define LOG_INFO_LIM(fmt, ...) \ + ps3_print(PS3_TRUE, LEVEL_INFO, fmt, ##__VA_ARGS__) +#define LOG_WARN_LIM(fmt, ...) \ + ps3_print(PS3_TRUE, LEVEL_WARN, fmt, ##__VA_ARGS__) +#define LOG_ERROR_LIM(fmt, ...) \ + ps3_print(PS3_TRUE, LEVEL_ERROR, fmt, ##__VA_ARGS__) + +#define LOG_INFO_TIME_LIM(caller_jiffies, time, fmt, ...) \ + do { \ + (void)caller_jiffies; \ + (void)time; \ + ps3_print(PS3_TRUE, LEVEL_INFO, fmt, ##__VA_ARGS__); \ + } while (0) +#define LOG_WARN_TIME_LIM(caller_jiffies, time, fmt, ...) \ + do { \ + (void)caller_jiffies; \ + (void)time; \ + ps3_print(PS3_TRUE, LEVEL_WARN, fmt, ##__VA_ARGS__); \ + } while (0) +#define LOG_ERROR_TIME_LIM(caller_jiffies, time, fmt, ...) \ + do { \ + (void)caller_jiffies; \ + (void)time; \ + ps3_print(PS3_TRUE, LEVEL_ERROR, fmt, ##__VA_ARGS__); \ + } while (0) + +#define LOG2_INFO_LIM(fmt, ...) LOG_INFO_LIM(fmt, ##__VA_ARGS__) +#define LOG2_WARN_LIM(fmt, ...) LOG_WARN_LIM(fmt, ##__VA_ARGS__) +#define LOG2_ERROR_LIM(fmt, ...) LOG_ERROR_LIM(fmt, ##__VA_ARGS__) + +#define LOG_FILE_INFO(fmt, ...) +#define LOG_FILE_WARN(fmt, ...) +#define LOG_FILE_ERROR(fmt, ...) + +#define LOG_SPC_ERROR(fmt, ...) LOG_INFO(fmt, ##__VA_ARGS__) + +#define LOG_INFO_IN_IRQ(ins, fmt, ...) \ + do { \ + (void)ins; \ + ps3_print(PS3_FALSE, LEVEL_INFO, fmt, ##__VA_ARGS__); \ + } while (0) +#define LOG_WARN_IN_IRQ(ins, fmt, ...) \ + do { \ + (void)ins; \ + ps3_print(PS3_FALSE, LEVEL_WARN, fmt, ##__VA_ARGS__); \ + } while (0) +#define LOG_ERROR_IN_IRQ(ins, fmt, ...) \ + do { \ + (void)ins; \ + ps3_print(PS3_FALSE, LEVEL_ERROR, fmt, ##__VA_ARGS__); \ + } while (0) +#define LOG_INFO_LIM_WITH_CHECK(ins, need_prk_err, fmt, ...) \ + do { \ + (void)ins; \ + ps3_print((need_prk_err), LEVEL_INFO, fmt, ##__VA_ARGS__); \ + } while (0) + +#define LOG_WARN_LIM_WITH_CHECK(ins, need_prk_err, fmt, ...) \ + do { \ + (void)ins; \ + ps3_print((need_prk_err), LEVEL_WARN, fmt, ##__VA_ARGS__); \ + } while (0) + +#define LOG_ERROR_LIM_WITH_CHECK(ins, need_prk_err, fmt, ...) \ + do { \ + (void)ins; \ + ps3_print((need_prk_err), LEVEL_ERROR, fmt, ##__VA_ARGS__); \ + } while (0) + +#define LOG_SYNC() +#define DATA_DUMP(ptr, size, str) + +static inline unsigned char ps3_fs_requires_dev(struct file *fp) +{ + (void)fp; + return PS3_TRUE; +} +#endif + +#ifdef PS3_CFG_RELEASE + +#define PS3_BUG() +#define PS3_BUG_NO_SYNC() + +#if defined(PS3_CFG_OCM_DBGBUG) || defined(PS3_CFG_OCM_RELEASE) + +#define PS3_BUG_ON(cond) \ + do { \ + if ((cond)) { \ + pr_err("BUG_ON's condition(%s) has been triggered\n", \ + #cond); \ + LOG_ERROR( \ + "BUG_ON's condition(%s) has been triggered\n", \ + #cond); \ + } \ + } while (0) + +#define PS3_BUG_ON_NO_SYNC(cond) \ + do { \ + if ((cond)) { \ + pr_err("BUG_ON's condition(%s) has been triggered\n", \ + #cond); \ + LOG_ERROR( \ + "BUG_ON's condition(%s) has been triggered\n", \ + #cond); \ + } \ + } while (0) + +#else +#define PS3_BUG_ON(cond) \ + do { \ + if ((cond)) { \ + pr_err("BUG_ON's condition(%s) has been triggered\n", \ + #cond); \ + } \ + } while (0) + +#define PS3_BUG_ON_NO_SYNC(cond) \ + do { \ + if ((cond)) { \ + pr_err("BUG_ON's condition(%s) has been triggered\n", \ + #cond); \ + } \ + } while (0) + +#endif + +#else +#define PS3_BUG_ON(cond) \ + do { \ + if ((cond)) { \ + pr_err("BUG_ON's condition(%s) has been triggered\n", \ + #cond); \ + LOG_ERROR( \ + "BUG_ON's condition(%s) has been triggered\n", \ + #cond); \ + LOG_SYNC(); \ + } \ + BUG_ON(cond); \ + } while (0) + +#define PS3_BUG(void) \ + do { \ + LOG_SYNC(); \ + BUG(void); \ + } while (0) + +#define PS3_BUG_ON_NO_SYNC(cond) \ + do { \ + if ((cond)) { \ + pr_err( \ + "BUG_ON's condition(%s) has been triggered\n", \ + #cond); \ + LOG_ERROR( \ + "BUG_ON's condition(%s) has been triggered\n", \ + #cond); \ + } \ + BUG_ON(cond); \ + } while (0) + +#define PS3_BUG_NO_SYNC(void) BUG(void) + +#endif + +#define PS3_WARN_ON(cond) WARN_ON(cond) + +#ifdef __cplusplus +} +#endif +#endif diff --git a/drivers/scsi/linkdata/ps3stor/linux/ps3_dump.c b/drivers/scsi/linkdata/ps3stor/linux/ps3_dump.c new file mode 100644 index 0000000000000000000000000000000000000000..1228bcdadac40d48e143500a07a351dbc172d896 --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/linux/ps3_dump.c @@ -0,0 +1,1008 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ps3_dump.h" +#include "ps3_mgr_cmd.h" +#include "ps3_cmd_channel.h" +#include "ps3_mgr_channel.h" +#include "ps3_cmd_statistics.h" +#include "ps3_ioc_manager.h" +#include "ps3_util.h" +#include "ps3_cli.h" +#include "ps3_module_para.h" +#include "ps3_kernel_version.h" + +static inline void ps3_dump_status_set(struct ps3_instance *instance, + unsigned long long value); + +int ps3_dump_local_time(struct rtc_time *tm) +{ +#if defined(PS3_DUMP_TIME_32) + struct timeval time; + unsigned long long local_time; + + do_gettimeofday(&time); + local_time = (unsigned long long)(time.tv_sec - (sys_tz.tz_minuteswest * 60)); + rtc_time_to_tm(local_time, tm); +#else + struct timespec64 time; + unsigned long long local_time; + + ktime_get_real_ts64(&time); + local_time = (unsigned long long)(time.tv_sec - (sys_tz.tz_minuteswest * 60)); + rtc_time64_to_tm(local_time, tm); +#endif + tm->tm_mon += 1; + tm->tm_year += 1900; + return 0; +} + +int ps3_dump_filename_build(struct ps3_instance *instance, char *filename, + unsigned int len, unsigned char *prefix) +{ + struct ps3_dump_context *ctxt = &instance->dump_context; + struct rtc_time tm; + char *p_str = filename; + + if (filename == NULL || prefix == NULL) + return -1; + + ps3_dump_local_time(&tm); + p_str += + snprintf(p_str, len - (p_str - filename), "%s", ctxt->dump_dir); + p_str += + snprintf(p_str, len - (p_str - filename), (const char *)prefix); + p_str += snprintf(p_str, len - (p_str - filename), "host%d-", + instance->host->host_no); + p_str += snprintf(p_str, len - (p_str - filename), + "%04d%02d%02d-%02d%02d%02d-", tm.tm_year, tm.tm_mon, + tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); + p_str += snprintf(p_str, len - (p_str - filename), "%llu", + instance->ioc_fw_version); + + return 0; +} + +int ps3_dump_file_open(struct ps3_dump_context *ctxt, unsigned int dump_type) +{ + struct ps3_dump_file_info *file_info = &ctxt->dump_out_file; + unsigned char filename[PS3_DUMP_FILE_NAME_LEN] = { 0 }; + unsigned char *p_prefix = NULL; + int ret = PS3_SUCCESS; + struct file *fp = NULL; +#if defined(PS3_SUPPORT_FS) + mm_segment_t old_fs; +#endif + + if (file_info->fp || file_info->file_status == PS3_DUMP_FILE_OPEN) { + LOG_INFO("dump file open: file already open\n"); + ret = -PS3_FAILED; + goto l_out; + } + + memset(file_info, 0, sizeof(struct ps3_dump_file_info)); + + switch (dump_type) { + case PS3_DUMP_TYPE_FW_LOG: + p_prefix = (unsigned char *)PS3_DUMP_FILE_FWLOG_PREFIX; + break; + case PS3_DUMP_TYPE_BAR_DATA: + p_prefix = (unsigned char *)PS3_DUMP_FILE_BAR_PREFIX; + break; + case PS3_DUMP_TYPE_CRASH: + p_prefix = (unsigned char *)PS3_DUMP_FILE_CORE_PREFIX; + break; + default: + LOG_INFO("dump file create: unknown dump type %d\n", dump_type); + ret = -PS3_FAILED; + goto l_out; + } + + if (ps3_dump_filename_build(ctxt->instance, (char *)filename, + PS3_DUMP_FILE_NAME_LEN, p_prefix) != 0) { + LOG_INFO("dump file create: filename build NOK\n"); + ret = -PS3_FAILED; + goto l_out; + } + + fp = (struct file *)filp_open( + (char *)filename, O_CREAT | O_RDWR | O_TRUNC | O_LARGEFILE, 0); + if (IS_ERR(fp)) { + LOG_INFO( + "dump file create: filp_open error filename %s errno %d\n", + filename, (int)PTR_ERR(fp)); + ret = -PS3_FAILED; + goto l_out; + } + if (!ps3_fs_requires_dev(fp)) { +#if defined(PS3_KERNEL_WRITE_GET_DS) + old_fs = get_fs(); + set_fs(get_ds()); +#elif defined(PS3_KERNEL_WRITE) + old_fs = get_fs(); + set_fs(KERNEL_DS); +#elif defined(PS3_VFS_WRITE) +#else + old_fs = force_uaccess_begin(); +#endif + filp_close(fp, NULL); +#if defined(PS3_SET_FS) + set_fs(old_fs); +#elif defined(PS3_FORCE_UACCESS) +#else + force_uaccess_end(old_fs); +#endif + + ret = -PS3_FAILED; + goto l_out; + } + + memcpy(file_info->filename, filename, PS3_DUMP_FILE_NAME_LEN); + file_info->type = dump_type; + file_info->file_status = PS3_DUMP_FILE_OPEN; + file_info->fp = fp; + +l_out: + return ret; +} + +int ps3_dump_file_write(struct ps3_dump_file_info *file_info, unsigned char *buf, unsigned int len) +{ + struct file *fp = NULL; +#if defined(PS3_SUPPORT_FS) + mm_segment_t old_fs; +#endif + int ret = 0; + + if (file_info && file_info->fp) { + fp = file_info->fp; +#if defined(PS3_KERNEL_WRITE_GET_DS) + old_fs = get_fs(); + set_fs(get_ds()); +#elif defined(PS3_KERNEL_WRITE) + old_fs = get_fs(); + set_fs(KERNEL_DS); +#elif defined(PS3_VFS_WRITE) +#else + old_fs = force_uaccess_begin(); +#endif + +#if defined(PS3_KERNEL_WRITE_FILE) + ret = kernel_write(fp, (char *)buf, len, &fp->f_pos); +#else + ret = vfs_write(fp, (char *)buf, len, &fp->f_pos); +#endif +#if defined(PS3_SET_FS) + set_fs(old_fs); +#elif defined(PS3_FORCE_UACCESS) +#else + force_uaccess_end(old_fs); +#endif + + if (ret > 0) + file_info->file_size += len; + file_info->file_w_cnt++; + } + + return ret; +} + +int ps3_dump_file_close(struct ps3_dump_file_info *file_info) +{ +#if defined(PS3_SUPPORT_FS) + mm_segment_t old_fs; +#endif + if (file_info && file_info->fp) { + PS3_BUG_ON(IS_ERR(file_info->fp)); +#if defined(PS3_KERNEL_WRITE_GET_DS) + old_fs = get_fs(); + set_fs(get_ds()); +#elif defined(PS3_KERNEL_WRITE) + old_fs = get_fs(); + set_fs(KERNEL_DS); +#elif defined(PS3_VFS_WRITE) +#else + old_fs = force_uaccess_begin(); +#endif + filp_close(file_info->fp, NULL); +#if defined(PS3_SET_FS) + set_fs(old_fs); +#elif defined(PS3_FORCE_UACCESS) +#else + force_uaccess_end(old_fs); +#endif + + file_info->fp = NULL; + file_info->file_status = PS3_DUMP_FILE_CLOSE; + } + return 0; +} + +struct ps3_dump_context *dev_to_dump_context(struct device *cdev) +{ + struct Scsi_Host *shost = class_to_shost(cdev); + struct ps3_instance *instance = (struct ps3_instance *)shost->hostdata; + + return &instance->dump_context; +} + +static inline unsigned char ps3_dump_ctrl_get(struct ps3_instance *instance, + unsigned long long *dump_ctrl) +{ + unsigned char ret = PS3_TRUE; + + PS3_IOC_REG_READ_WITH_CHECK(instance, reg_f.Excl_reg, ps3DumpCtrl, + *dump_ctrl); + if (*dump_ctrl == U64_MAX) { + LOG_INFO("hno:%u read reg ps3DumpCtrl NOK!\n", + PS3_HOST(instance)); + ret = PS3_FALSE; + goto l_out; + } + *dump_ctrl &= 0xff; + +l_out: + return ret; +} + +static inline void ps3_dump_ctrl_set(struct ps3_instance *instance, unsigned long long value) +{ + PS3_IOC_REG_WRITE(instance, reg_f.Excl_reg, ps3DumpCtrl, value); +} + +static inline void ps3_dump_abort(struct ps3_instance *instance) +{ + struct ps3_dump_context *ctxt = &instance->dump_context; + unsigned long long dump_ctrl; + unsigned char ret = PS3_TRUE; + + if (ctxt->dump_state == PS3_DUMP_STATE_ABORTED || + ctxt->dump_state == PS3_DUMP_STATE_INVALID) { + goto l_ret; + } + + ret = ps3_dump_ctrl_get(instance, &dump_ctrl); + if (ret) { + if (dump_ctrl) { + LOG_WARN("dump ctrl is not cleared 0x%llx\n", + dump_ctrl); + } + ps3_dump_ctrl_set(instance, PS3_DUMP_CTRL_DUMP_ABORT); + } else { + LOG_INFO("hno:%u read ps3DumpCtrl NOK!\n", PS3_HOST(instance)); + } + ctxt->dump_state = PS3_DUMP_STATE_ABORTED; +l_ret: + return; +} + +static inline void ps3_dump_end(struct ps3_instance *instance) +{ + unsigned long long dump_ctrl; + unsigned char ret = PS3_TRUE; + + ret = ps3_dump_ctrl_get(instance, &dump_ctrl); + if (ret) { + if (dump_ctrl) { + LOG_WARN("dump ctrl is not cleared 0x%llx\n", + dump_ctrl); + } + ps3_dump_ctrl_set(instance, PS3_DUMP_CTRL_DUMP_END); + } else { + LOG_INFO("hno:%u read ps3DumpCtrl NOK!\n", PS3_HOST(instance)); + } +} + +static inline int ps3_dump_trigger(struct ps3_instance *instance, int dump_type) +{ + unsigned long long dump_ctrl; + unsigned long long ctrl_val = 0; + int ret = PS3_SUCCESS; + unsigned char reg_ret = PS3_TRUE; + + switch (dump_type) { + case PS3_DUMP_TYPE_CRASH: + ctrl_val = PS3_DUMP_CTRL_DUMP_CORE_FILE; + break; + case PS3_DUMP_TYPE_FW_LOG: + ctrl_val = PS3_DUMP_CTRL_DUMP_FW_LOG; + break; + case PS3_DUMP_TYPE_BAR_DATA: + ctrl_val = PS3_DUMP_CTRL_DUMP_BAR_DATA; + break; + default: + ret = -PS3_FAILED; + goto l_ret; + } + + reg_ret = ps3_dump_ctrl_get(instance, &dump_ctrl); + if (reg_ret) { + if (dump_ctrl) { + LOG_WARN("dump ctrl is not cleared 0x%llx\n", + dump_ctrl); + ret = -PS3_FAILED; + } else { + ps3_dump_ctrl_set(instance, ctrl_val); + } + } else { + LOG_INFO("hno:%u read ps3DumpCtrl NOK!\n", PS3_HOST(instance)); + ret = -PS3_FAILED; + } +l_ret: + return ret; +} + +static inline unsigned char ps3_dump_status_get(struct ps3_instance *instance, + unsigned long long *dump_status) +{ + unsigned char ret = PS3_TRUE; + + PS3_IOC_REG_READ_WITH_CHECK(instance, reg_f.Excl_reg, ps3DumpStatus, + *dump_status); + if (*dump_status == U64_MAX) { + LOG_INFO("hno:%u read reg ps3DumpStatus NOK!\n", + PS3_HOST(instance)); + *dump_status = 0; + ret = PS3_FALSE; + goto l_out; + } + + *dump_status &= 0xff; + +l_out: + return ret; +} + +static inline void ps3_dump_status_set(struct ps3_instance *instance, unsigned long long value) +{ + PS3_IOC_REG_WRITE(instance, reg_f.Excl_reg, ps3DumpStatus, value); +} + +static inline unsigned char ps3_dump_data_size_get_clear(struct ps3_instance *instance, + unsigned long long *data_size) +{ + unsigned char ret = PS3_TRUE; + + PS3_IOC_REG_READ_WITH_CHECK(instance, reg_f.Excl_reg, ps3DumpDataSize, + *data_size); + if (*data_size == U64_MAX) { + LOG_INFO("hno:%u read reg ps3DumpDataSize NOK!\n", + PS3_HOST(instance)); + *data_size = 0; + ret = PS3_FALSE; + } + + PS3_IOC_REG_WRITE_WITH_CHECK(instance, reg_f.Excl_reg, ps3DumpDataSize, + 0); + return ret; +} + +static unsigned char ps3_dump_status_check(unsigned long long status, unsigned char dump_type) +{ + unsigned char ret = PS3_TRUE; + + switch (dump_type) { + case PS3_DUMP_TYPE_FW_LOG: + ret = PS3_REG_TEST(status, PS3_DUMP_STATUS_REG_FW_DUMP_MASK); + break; + case PS3_DUMP_TYPE_BAR_DATA: + ret = PS3_REG_TEST(status, PS3_DUMP_STATUS_REG_BAR_DUMP_MASK); + break; + case PS3_DUMP_TYPE_CRASH: + ret = PS3_REG_TEST(status, PS3_DUMP_STATUS_REG_CRASH_DUMP_MASK); + break; + default: + LOG_INFO("invalid type %d\n", dump_type); + ret = PS3_FALSE; + goto l_out; + } + + if (ret == PS3_TRUE) + ret = PS3_REG_TEST(status, PS3_DUMP_STATUS_REG_DMA_FINISH_MASK); + +l_out: + return ret; +} + +static int ps3_dump_dma_data_copy(struct ps3_dump_context *ctxt, unsigned long long status) +{ + unsigned long long dma_size = 0; + unsigned long long dump_ctrl; + int ret = PS3_SUCCESS; + unsigned char reg_ret = PS3_TRUE; + + if (!ps3_dump_data_size_get_clear(ctxt->instance, &dma_size)) { + ret = -PS3_FAILED; + goto l_out; + } + + if (dma_size == 0 || dma_size > PS3_DUMP_DMA_BUF_SIZE) { + LOG_ERROR("error: invalid data size %llu\n", dma_size); + ret = -PS3_FAILED; + goto l_out; + } + ctxt->dump_data_size += dma_size; + + ret = ps3_dump_file_write(&ctxt->dump_out_file, ctxt->dump_dma_buf, + dma_size); + if (ret < 0 || ret != (int)dma_size) { + LOG_WARN("error: write data failure ret %d, dma_size %llu\n", + ret, dma_size); + ret = -PS3_FAILED; + goto l_out; + } + ctxt->copyed_data_size += ret; + + PS3_REG_CLR(status, PS3_DUMP_STATUS_REG_DMA_FINISH_MASK); + ps3_dump_status_set(ctxt->instance, status); + + reg_ret = ps3_dump_ctrl_get(ctxt->instance, &dump_ctrl); + if (reg_ret) { + if (dump_ctrl) { + LOG_WARN("dump ctrl is not cleared 0x%llx\n", + dump_ctrl); + } + ps3_dump_ctrl_set(ctxt->instance, PS3_DUMP_CTRL_COPY_FINISH); + ret = PS3_SUCCESS; + } else { + LOG_INFO("hno:%u read ps3DumpCtrl NOK!\n", + PS3_HOST(ctxt->instance)); + ret = -PS3_FAILED; + } + +l_out: + return ret; +} + +static inline void ps3_dump_work_done(struct ps3_dump_context *ctxt, + int cur_state) +{ + ctxt->dump_work_status = PS3_DUMP_WORK_STOP; + ps3_dump_file_close(&ctxt->dump_out_file); + cur_state = ctxt->dump_state; + ctxt->dump_state = PS3_DUMP_STATE_INVALID; + if (cur_state == PS3_DUMP_STATE_COPY_DONE) { + LOG_INFO("end dump in COPY_DONE STATE\n"); + ps3_dump_end(ctxt->instance); + } +} + +static void ps3_dump_work(struct work_struct *work) +{ + unsigned int work_wait_times = 0; + struct ps3_dump_context *ctxt = + ps3_container_of(work, struct ps3_dump_context, dump_work.work); + unsigned long long status = 0, delay_ms = 0; + int cur_state = 0; + unsigned char ret = PS3_TRUE; + + ctxt->dump_work_status = PS3_DUMP_WORK_RUNNING; + ret = ps3_dump_status_get(ctxt->instance, &status); + if (!ret) { + delay_ms = PS3_REG_READ_INTERVAL_MS; + LOG_INFO("ps3_dump_status_get error, delay %llums try again\n", + delay_ms); + queue_delayed_work(ctxt->dump_work_queue, &ctxt->dump_work, + msecs_to_jiffies(delay_ms)); + goto l_out; + } + + + while (delay_ms == 0) { + if (ctxt->is_hard_recovered) { + LOG_WARN("dump in hard recovery, ready to abort\n"); + ctxt->dump_state = PS3_DUMP_STATE_PRE_ABORT; + } + + switch (ctxt->dump_state) { + case PS3_DUMP_STATE_START: + if ((status & PS3_DUMP_STATUS_REG_INVALID_BITS_MASK) == + 0 || + (status & PS3_DUMP_STATUS_REG_ABORT_MASK) != 0) { + LOG_INFO( + "abort dump in START STATE, status: 0x%llx\n", + status); + ctxt->dump_state = PS3_DUMP_STATE_PRE_ABORT; + continue; + } + + if ((status & PS3_DUMP_STATUS_REG_MASK) == 0 || + ps3_dump_status_check(status, ctxt->dump_type) == + PS3_FALSE) { + delay_ms = WAIT_DUMP_COLLECT; + work_wait_times++; + break; + } + + if (ctxt->dump_out_file.file_status != + PS3_DUMP_FILE_OPEN) { + if (ps3_dump_file_open(ctxt, + (unsigned int)ctxt->dump_type) != + PS3_SUCCESS) { + delay_ms = WAIT_DUMP_COLLECT; + work_wait_times++; + break; + } + } + work_wait_times = 0; + ctxt->dump_state = PS3_DUMP_STATE_COPYING; + break; + case PS3_DUMP_STATE_COPYING: + if (status & PS3_DUMP_STATUS_REG_ABORT_MASK) { + LOG_INFO("abort dump in COPYING STATE\n"); + ctxt->dump_state = PS3_DUMP_STATE_PRE_ABORT; + continue; + } + + if ((status & PS3_DUMP_STATUS_REG_MASK) == 0) { + ctxt->dump_state = PS3_DUMP_STATE_COPY_DONE; + ctxt->dump_work_status = PS3_DUMP_WORK_DONE; + break; + } + if (ps3_dump_status_check(status, ctxt->dump_type) == + PS3_FALSE) { + delay_ms = WAIT_DUMP_COLLECT; + work_wait_times++; + break; + } + if (ps3_dump_dma_data_copy(ctxt, status) != + PS3_SUCCESS) { + LOG_INFO("abort dump in COPYING STATE\n"); + ctxt->dump_state = PS3_DUMP_STATE_PRE_ABORT; + continue; + } + + delay_ms = WAIT_DUMP_DMA_DONE; + work_wait_times = 0; + break; + case PS3_DUMP_STATE_PRE_ABORT: + ps3_dump_abort(ctxt->instance); +#if defined(PS3_FALLTHROUGH) + ps3_dump_work_done(ctxt, cur_state); + work_wait_times = 0; + goto l_out; +#endif + case PS3_DUMP_STATE_ABORTED: + case PS3_DUMP_STATE_COPY_DONE: + ps3_dump_work_done(ctxt, cur_state); + work_wait_times = 0; + goto l_out; + default: + LOG_INFO("warn: dump work state %d\n", + ctxt->dump_state); + ctxt->dump_state = PS3_DUMP_STATE_PRE_ABORT; + continue; + } + if (delay_ms) { + if (work_wait_times >= + max_t(unsigned int, WAIT_DUMP_TIMES_MIN, ctxt->dump_dma_wait_times)) { + LOG_INFO( + "error: wait too many times %d for dump %s, abort it\n", + work_wait_times, + ps3_dump_type_to_name(ctxt->dump_type)); + ctxt->dump_state = PS3_DUMP_STATE_PRE_ABORT; + work_wait_times = 0; + delay_ms = 0; + continue; + } + + queue_delayed_work(ctxt->dump_work_queue, + &ctxt->dump_work, + msecs_to_jiffies(delay_ms)); + break; + } + } + +l_out: + return; +} + +int ps3_dump_dma_buf_alloc(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + struct ps3_dump_context *ctxt = &instance->dump_context; + + ctxt->dump_dma_buf = (unsigned char *)ps3_dma_alloc_coherent( + instance, PS3_DUMP_DMA_BUF_SIZE, (unsigned long long *)&ctxt->dump_dma_addr); + if (ctxt->dump_dma_buf == NULL) { + LOG_ERROR("host_no[%d], dump dma alloc NOK!\n", + PS3_HOST(instance)); + ret = -PS3_FAILED; + } + + return ret; +} + +void ps3_dump_dma_buf_free(struct ps3_instance *instance) +{ + struct ps3_dump_context *ctxt = &instance->dump_context; + + if (ctxt->dump_dma_buf != NULL) { + ps3_dma_free_coherent(instance, PS3_DUMP_DMA_BUF_SIZE, + ctxt->dump_dma_buf, ctxt->dump_dma_addr); + ctxt->dump_dma_buf = NULL; + } +} + +static void ps3_dump_reset(struct ps3_dump_context *ctxt) +{ + ctxt->dump_data_size = 0; + ctxt->copyed_data_size = 0; + ctxt->dump_type = PS3_DUMP_TYPE_UNKNOWN; + ctxt->dump_state = PS3_DUMP_STATE_INVALID; + memset(&ctxt->dump_out_file, 0, sizeof(ctxt->dump_out_file)); +} + +int ps3_dump_type_set(struct ps3_dump_context *ctxt, int type, unsigned int env) +{ + int ret = PS3_SUCCESS; + int cur_state = PS3_INSTANCE_STATE_INIT; + + LOG_INFO("dump type set: type %d\n", type); + + if ((type < PS3_DUMP_TYPE_CRASH) || (type > PS3_DUMP_TYPE_BAR_DATA)) { + ret = -PS3_FAILED; + goto l_ret; + } + + if (ctxt->dump_work_queue == NULL) { + LOG_WARN("dump type set: no work to do, type %d\n", type); + ret = -PS3_FAILED; + goto l_ret; + } + + ps3_mutex_lock(&ctxt->dump_lock); + if (ctxt->dump_state != PS3_DUMP_STATE_INVALID) { + LOG_FILE_ERROR( + "dump type set: work is busy, current state: %d, type %d\n", + ctxt->dump_state, type); + ret = -PS3_FAILED; + goto l_unlock; + } + + ps3_dump_reset(ctxt); + + + ctxt->is_hard_recovered = PS3_FALSE; + + + cur_state = ps3_atomic_read(&ctxt->instance->state_machine.state); + if (!ps3_state_is_normal(cur_state)) { + LOG_WARN("h_no[%u], instance state is unnormal[%s]\n", + PS3_HOST(ctxt->instance), + namePS3InstanceState(cur_state)); + ret = -PS3_FAILED; + goto l_unlock; + } + + ctxt->dump_state = PS3_DUMP_STATE_START; + ctxt->dump_type = type; + ctxt->dump_env = env; + ctxt->dump_type_times++; + + + ret = ps3_dump_trigger(ctxt->instance, type); + if (ret != PS3_SUCCESS) { + ps3_dump_reset(ctxt); + goto l_unlock; + } + + queue_delayed_work(ctxt->dump_work_queue, &ctxt->dump_work, 0); +l_unlock: + ps3_mutex_unlock(&ctxt->dump_lock); +l_ret: + return ret; +} + +int ps3_dump_state_set(struct ps3_dump_context *ctxt, int state) +{ + int ret = PS3_SUCCESS; + int cur_state; + + LOG_INFO("dump state set: state %d\n", state); + + if (state != PS3_DUMP_STATE_INVALID) { + ret = -PS3_FAILED; + goto l_ret; + } + + ps3_mutex_lock(&ctxt->dump_lock); + cur_state = ctxt->dump_state; + if (cur_state == PS3_DUMP_STATE_INVALID) + goto l_unlock; + + cancel_delayed_work_sync(&ctxt->dump_work); + ctxt->dump_work_status = PS3_DUMP_WORK_CANCEL; + ctxt->dump_state_times++; + + if (cur_state == PS3_DUMP_STATE_PRE_ABORT || + cur_state == PS3_DUMP_STATE_START || + cur_state == PS3_DUMP_STATE_COPYING) { + ps3_dump_abort(ctxt->instance); + } + + ps3_dump_file_close(&ctxt->dump_out_file); + + ps3_dump_reset(ctxt); + if (cur_state == PS3_DUMP_STATE_COPY_DONE) + ps3_dump_end(ctxt->instance); +l_unlock: + ps3_mutex_unlock(&ctxt->dump_lock); +l_ret: + return ret; +} + +void ps3_dump_detect(struct ps3_instance *instance) +{ + struct ps3_dump_context *p_dump_ctx = &instance->dump_context; + int ret = PS3_SUCCESS; + int dump_type = 0; + unsigned long long status = 0; + union HilReg0Ps3RegisterFPs3DumpStatus dump_status = { 0 }; + unsigned char is_trigger_log; + + if (!ps3_dump_status_get(instance, &status)) + goto l_out; + dump_status.val = status; + + dump_type = dump_status.reg.hasAutoDump; + if (dump_type == 0) + goto l_out; + + ps3_ioc_dump_support_get(instance); + + if (!PS3_IOC_DUMP_SUPPORT(instance)) + goto l_out; + + LOG_DEBUG("hno:%u detect dump log file type[%d], status[%llx]\n", + PS3_HOST(instance), dump_type, status); + + is_trigger_log = ps3_dump_is_trigger_log(instance); + if (!is_trigger_log) { + LOG_DEBUG("cannot dump type set!\n"); + goto l_out; + } + + ret = ps3_dump_type_set(p_dump_ctx, dump_type, PS3_DUMP_ENV_NOTIFY); + LOG_DEBUG("hno:%u type[%d] autodump set trigger ret %d\n", + PS3_HOST(instance), dump_type, ret); + if (ret == PS3_SUCCESS) + goto l_out; + +l_out: + return; +} + +static void ps3_dump_irq_handler_work(struct work_struct *work) +{ + struct ps3_dump_context *ctxt = ps3_container_of( + work, struct ps3_dump_context, dump_irq_handler_work); + + ctxt->dump_irq_handler_work_status = PS3_DUMP_IRQ_HANDLER_WORK_RUNNING; + ps3_dump_detect(ctxt->instance); + ctxt->dump_irq_handler_work_status = PS3_DUMP_IRQ_HANDLER_WORK_DONE; +} + +irqreturn_t ps3_dump_irq_handler(int virq, void *dev_id) +{ + struct ps3_instance *pInstance = (struct ps3_instance *)dev_id; + struct ps3_dump_context *p_dump_ctx = &pInstance->dump_context; + unsigned long flags = 0; + + spin_lock_irqsave(&p_dump_ctx->dump_irq_handler_lock, flags); + if (p_dump_ctx->dump_enabled) { + + LOG_DEBUG( + "hno:%u dump irq received, virq: %d, dev_id: 0x%llx\n", + PS3_HOST(pInstance), virq, (unsigned long long)(uintptr_t)dev_id); + + if (!work_busy(&p_dump_ctx->dump_irq_handler_work)) { + queue_work(p_dump_ctx->dump_irq_handler_work_queue, + &p_dump_ctx->dump_irq_handler_work); + } + } + + spin_unlock_irqrestore(&p_dump_ctx->dump_irq_handler_lock, flags); + + return IRQ_HANDLED; +} + +static void ps3_dump_dir_init(char *dump_dir) +{ + char *log_path_p; + unsigned int log_path_len = 0; + + log_path_p = ps3_log_path_query(); + if (log_path_p != NULL) + log_path_len = strlen(log_path_p); + + if (log_path_p != NULL && log_path_p[0] == '/') { + if (log_path_p[log_path_len - 1] == '/') { + snprintf(dump_dir, PS3_DUMP_FILE_DIR_LEN, "%s", + log_path_p); + } else { + snprintf(dump_dir, PS3_DUMP_FILE_DIR_LEN, "%s/", + log_path_p); + } + } else { + LOG_INFO("provided log dump dir not valid, using default\n"); + snprintf(dump_dir, PS3_DUMP_FILE_DIR_LEN, "%s/", + PS3_DUMP_FILE_DIR); + } +} + +int ps3_dump_init(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + struct ps3_dump_context *ctxt = &instance->dump_context; + + if (reset_devices) { + LOG_INFO( + "resetting device in progress, Do not initialize dump\n"); + goto l_ret; + } + + if (ctxt->dump_dma_buf != NULL) { + LOG_INFO("hno:%u init already\n", PS3_HOST(instance)); + goto l_ret; + } + memset((void *)ctxt, 0, sizeof(struct ps3_dump_context)); + + if (!ps3_ioc_dump_support_get(instance)) + goto l_ret; + + ps3_dump_dir_init((char *)ctxt->dump_dir); + + ctxt->instance = instance; + ctxt->dump_type = PS3_DUMP_TYPE_UNKNOWN; + ctxt->dump_state = PS3_DUMP_STATE_INVALID; + ctxt->dump_dma_wait_times = WAIT_DUMP_TIMES_DEFAULT; + if (ps3_dump_dma_buf_alloc(instance) != PS3_SUCCESS) { + ret = -PS3_FAILED; + goto l_failed; + } + + INIT_DELAYED_WORK(&ctxt->dump_work, ps3_dump_work); + ctxt->dump_work_queue = + create_singlethread_workqueue((char *)"ps3_dump_work_queue"); + if (ctxt->dump_work_queue == NULL) { + LOG_ERROR("dump work queue create NOK\n"); + ret = -PS3_FAILED; + goto l_failed; + } + + INIT_WORK(&ctxt->dump_irq_handler_work, ps3_dump_irq_handler_work); + ctxt->dump_irq_handler_work_queue = create_singlethread_workqueue( + (char *)"ps3_dump_irq_handler_work_queue"); + if (ctxt->dump_irq_handler_work_queue == NULL) { + LOG_ERROR("dump irq handler work queue create NOK\n"); + ret = -PS3_FAILED; + goto l_failed; + } + ps3_mutex_init(&ctxt->dump_lock); + + spin_lock_init(&ctxt->dump_irq_handler_lock); + ctxt->dump_enabled = 1; + goto l_ret; +l_failed: + ps3_dump_exit(instance); +l_ret: + return ret; +} + +void ps3_dump_exit(struct ps3_instance *instance) +{ + struct ps3_dump_context *ctxt = &instance->dump_context; + unsigned long flags = 0; + + if (ctxt->dump_dma_buf == NULL) + return; + + spin_lock_irqsave(&ctxt->dump_irq_handler_lock, flags); + ctxt->dump_enabled = 0; + spin_unlock_irqrestore(&ctxt->dump_irq_handler_lock, flags); + + if (ctxt->dump_irq_handler_work_queue != NULL) { + if (!cancel_work_sync(&ctxt->dump_irq_handler_work)) { + flush_workqueue(ctxt->dump_irq_handler_work_queue); + } else { + ctxt->dump_irq_handler_work_status = + PS3_DUMP_IRQ_HANDLER_WORK_CANCEL; + } + destroy_workqueue(ctxt->dump_irq_handler_work_queue); + ctxt->dump_irq_handler_work_queue = NULL; + } + + if (ctxt->dump_work_queue != NULL) { + if (!cancel_delayed_work_sync(&ctxt->dump_work)) + flush_workqueue(ctxt->dump_work_queue); + else + ctxt->dump_work_status = PS3_DUMP_WORK_CANCEL; + destroy_workqueue(ctxt->dump_work_queue); + ctxt->dump_work_queue = NULL; + } + + ctxt->dump_state = PS3_DUMP_STATE_INVALID; + ps3_dump_file_close(&ctxt->dump_out_file); + + ps3_mutex_destroy(&ctxt->dump_lock); + LOG_INFO("hno:%u dump destroy work and stop service\n", + PS3_HOST(instance)); +} + +void ps3_dump_work_stop(struct ps3_instance *instance) +{ + struct ps3_dump_context *ctxt = &instance->dump_context; + unsigned long flags = 0; + + spin_lock_irqsave(&ctxt->dump_irq_handler_lock, flags); + if (ctxt->dump_enabled == 0) { + spin_unlock_irqrestore(&ctxt->dump_irq_handler_lock, flags); + return; + } + spin_unlock_irqrestore(&ctxt->dump_irq_handler_lock, flags); + + if (ctxt->dump_irq_handler_work_queue != NULL) { + if (!cancel_work_sync(&ctxt->dump_irq_handler_work)) { + flush_workqueue(ctxt->dump_irq_handler_work_queue); + } else { + ctxt->dump_irq_handler_work_status = + PS3_DUMP_IRQ_HANDLER_WORK_CANCEL; + } + } + + if (ctxt->dump_work_queue != NULL) { + ps3_dump_state_set(ctxt, PS3_DUMP_STATE_INVALID); + if (!cancel_delayed_work_sync(&ctxt->dump_work)) + flush_workqueue(ctxt->dump_work_queue); + else + ctxt->dump_work_status = PS3_DUMP_WORK_CANCEL; + } +} +void ps3_dump_ctrl_set_int_ready(struct ps3_instance *instance) +{ + if (reset_devices) { + LOG_INFO( + "resetting device in progress, unable to confiure ps3DumpCtrl\n"); + return; + } + PS3_IOC_REG_WRITE(instance, reg_f.Excl_reg, ps3DumpCtrl, + PS3_DUMP_CTRL_DUMP_INT_READY); +} + +unsigned char ps3_dump_is_trigger_log(struct ps3_instance *instance) +{ + unsigned char is_support_halt = PS3_IOC_STATE_HALT_SUPPORT(instance); + unsigned long long dump_ctrl; + unsigned char is_trigger_log = PS3_FALSE; + int cur_state = ps3_atomic_read(&instance->state_machine.state); + unsigned char is_halt = + (is_support_halt && (cur_state == PS3_INSTANCE_STATE_DEAD)); + unsigned char ret = PS3_TRUE; + + if (likely(is_halt || (cur_state == PS3_INSTANCE_STATE_OPERATIONAL && + !ps3_pci_err_recovery_get(instance)))) { + is_trigger_log = PS3_TRUE; + } + + if (instance->is_support_dump_ctrl) { + ret = ps3_dump_ctrl_get(instance, &dump_ctrl); + if (ret) { + if (dump_ctrl != 0) + is_trigger_log = PS3_FALSE; + } else { + LOG_INFO("hno:%u read ps3DumpCtrl NOK!\n", + PS3_HOST(instance)); + is_trigger_log = PS3_FALSE; + } + } + + return is_trigger_log; +} diff --git a/drivers/scsi/linkdata/ps3stor/linux/ps3_pcie_err_handle.c b/drivers/scsi/linkdata/ps3stor/linux/ps3_pcie_err_handle.c new file mode 100644 index 0000000000000000000000000000000000000000..aed81db9dbff09a22b4dbdfb04bfb603fc4efbbf --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/linux/ps3_pcie_err_handle.c @@ -0,0 +1,244 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include + +#include "ps3_pcie_err_handle.h" +#include "ps3_driver_log.h" +#include "ps3_recovery.h" +#include "ps3_ioc_state.h" +#include "ps3_module_para.h" +#include "ps3_kernel_version.h" + +static pci_ers_result_t ps3_pci_err_detected(struct pci_dev *pdev, + pci_channel_state_t state); +static pci_ers_result_t ps3_pci_mmio_enabled(struct pci_dev *pdev); +static pci_ers_result_t ps3_pci_slot_reset(struct pci_dev *pdev); +static void ps3_pci_resume(struct pci_dev *pdev); + +extern int ps3_pci_init(struct pci_dev *pdev, struct ps3_instance *instance); +extern int ps3_pci_init_complete(struct ps3_instance *instance); +extern void ps3_pci_init_complete_exit(struct ps3_instance *instance); + +static struct pci_error_handlers ps3_err_handlers = { + .error_detected = ps3_pci_err_detected, + .mmio_enabled = ps3_pci_mmio_enabled, + .slot_reset = ps3_pci_slot_reset, + .resume = ps3_pci_resume +}; + +void ps3_pci_err_handler_init(struct pci_driver *drv) +{ + drv->err_handler = &ps3_err_handlers; +} + +int ps3_base_init_resources(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + struct pci_dev *pdev = instance->pdev; + + ret = ps3_pci_init(pdev, instance); + if (ret) { + LOG_ERROR("hno:%u pci init failed, ret: %d\n", + PS3_HOST(instance), ret); + goto l_out; + } + + instance->is_pci_reset = PS3_FALSE; + + ret = ps3_pci_init_complete(instance); + if (ret) { + LOG_ERROR("hno:%u pci init complete failed, ret: %d\n", + PS3_HOST(instance), ret); + goto l_out; + } + + pci_save_state(pdev); + +l_out: + + return ret; +} + +void ps3_base_free_resources(struct ps3_instance *instance) +{ + instance->ioc_adpter->irq_disable(instance); + ps3_irqs_sync(instance); + + ps3_irqpolls_enable(instance); + ps3_dump_work_stop(instance); + ps3_pci_init_complete_exit(instance); +} + +static pci_ers_result_t ps3_pci_err_detected(struct pci_dev *pdev, + pci_channel_state_t state) +{ + pci_ers_result_t ret = PCI_ERS_RESULT_NEED_RESET; + struct ps3_instance *instance = + (struct ps3_instance *)pci_get_drvdata(pdev); + if (instance == NULL) { + LOG_INFO("get instance failed\n"); + dev_info(&pdev->dev, "[PS3]%s():%d; get instance failed\n", + __func__, __LINE__); + ret = PCI_ERS_RESULT_DISCONNECT; + goto l_out; + } + + LOG_INFO("[%04x:%02x:%02x:%x]:PCIe err detected state:%u\n", + ps3_get_pci_domain(pdev), ps3_get_pci_bus(pdev), + ps3_get_pci_slot(pdev), ps3_get_pci_function(pdev), state); + dev_info(&pdev->dev, "[PS3]%s():%d;PCIe err detected state:%u\n", + __func__, __LINE__, state); + instance->is_pcie_err_detected = PS3_TRUE; + switch (state) { + case pci_channel_io_normal: + ret = PCI_ERS_RESULT_CAN_RECOVER; + break; + case pci_channel_io_frozen: + ps3_pci_err_recovery_set(instance, PS3_TRUE); + scsi_block_requests(instance->host); + ps3_watchdog_stop(instance); + if (ps3_recovery_cancel_work_sync(instance) != PS3_SUCCESS) { + LOG_ERROR("hno:%u work sync failed, state: %u\n", + PS3_HOST(instance), state); + } + instance->pci_err_handle_state = PS3_DEVICE_ERR_STATE_CLEAN; + ps3_base_free_resources(instance); + ret = PCI_ERS_RESULT_NEED_RESET; + break; + case pci_channel_io_perm_failure: + ps3_pci_err_recovery_set(instance, PS3_TRUE); + ps3_watchdog_stop(instance); + if (ps3_recovery_cancel_work_sync(instance) != PS3_SUCCESS) { + LOG_ERROR("hno:%u work sync failed, state: %u\n", + PS3_HOST(instance), state); + } + ps3_cmd_force_stop(instance); + ps3_pci_err_recovery_set(instance, PS3_FALSE); + ps3_instance_state_transfer_to_dead(instance); + ret = PCI_ERS_RESULT_DISCONNECT; + break; + default: + ret = PCI_ERS_RESULT_RECOVERED; + break; + } + + instance->is_pcie_err_detected = PS3_FALSE; +l_out: + LOG_INFO("[%04x:%02x:%02x:%x]:PCIe err detect state:%u ret:%u\n", + ps3_get_pci_domain(pdev), ps3_get_pci_bus(pdev), + ps3_get_pci_slot(pdev), ps3_get_pci_function(pdev), state, + ret); + dev_info(&pdev->dev, "[PS3]%s():%d;PCIe err detect state:%u ret:%u\n", + __func__, __LINE__, state, ret); + return ret; +} + +static pci_ers_result_t ps3_pci_mmio_enabled(struct pci_dev *pdev) +{ + pci_ers_result_t ret = PCI_ERS_RESULT_RECOVERED; + + LOG_INFO("[%04x:%02x:%02x:%x]: PCIe err mmio enabled\n", + ps3_get_pci_domain(pdev), ps3_get_pci_bus(pdev), + ps3_get_pci_slot(pdev), ps3_get_pci_function(pdev)); + dev_info(&pdev->dev, "[PS3]%s():%d; PCIe err mmio enabled\n", + __func__, __LINE__); + + return ret; +} + +static pci_ers_result_t ps3_pci_slot_reset(struct pci_dev *pdev) +{ + int ret; + struct ps3_instance *instance = + (struct ps3_instance *)pci_get_drvdata(pdev); + + LOG_INFO("hno:%u PCIe err slot reset begin.\n", PS3_HOST(instance)); + dev_info(&pdev->dev, "[PS3]%s():%d;hno:%u PCIe err slot reset begin.\n", + __func__, __LINE__, PS3_HOST(instance)); + + instance->pdev = pdev; + pci_restore_state(pdev); + + ps3_irq_context_exit(instance); + + ret = ps3_base_init_resources(instance); + if (ret) { + LOG_ERROR("hno:%u base init resources failed, ret: %d\n", + PS3_HOST(instance), ret); + dev_info(&pdev->dev, + "[PS3]hno:%u init resources failed, ret: %d\n", + PS3_HOST(instance), ret); + goto l_out; + } + + LOG_INFO("hno:%u PCIe err slot reset succeed.\n", PS3_HOST(instance)); + dev_info(&pdev->dev, + "[PS3]%s():%d;hno:%u PCIe err slot reset succeed.\n", + __func__, __LINE__, PS3_HOST(instance)); + ps3_pci_err_recovery_set(instance, PS3_FALSE); + instance->pci_err_handle_state = PS3_DEVICE_ERR_STATE_INIT; + return PCI_ERS_RESULT_RECOVERED; +l_out: + if (instance) + ps3_instance_state_transfer_to_dead(instance); + ps3_pci_err_recovery_set(instance, PS3_FALSE); + instance->pci_err_handle_state = PS3_DEVICE_ERR_STATE_INIT; + + return PCI_ERS_RESULT_DISCONNECT; +} + +static void ps3_pci_resume(struct pci_dev *pdev) +{ + int ret = PS3_SUCCESS; + unsigned int fw_cur_state = PS3_FW_STATE_UNDEFINED; + struct ps3_instance *instance = + (struct ps3_instance *)pci_get_drvdata(pdev); + + LOG_INFO("hno:%u PCIe err resume\n", PS3_HOST(instance)); + dev_info(&pdev->dev, "[PS3]%s():%d;hno:%u PCIe err resume\n", + __func__, __LINE__, PS3_HOST(instance)); + + fw_cur_state = instance->ioc_adpter->ioc_state_get(instance); + if (fw_cur_state == PS3_FW_STATE_RUNNING) { + LOG_INFO("hno:%u not need repeat recovery\n", + PS3_HOST(instance)); + dev_info(&pdev->dev, "[PS3]hno:%u not need repeat recovery\n", + PS3_HOST(instance)); + goto l_norecovery; + } + ret = ps3_hard_recovery_request_with_retry(instance); + if (ret != PS3_SUCCESS) { + LOG_ERROR("hno:%u hard reset NOK, ret: %d\n", + PS3_HOST(instance), ret); + dev_info(&pdev->dev, "[PS3]hno:%u hard reset NOK, ret: %d\n", + PS3_HOST(instance), ret); + goto l_failed; + } + ret = ps3_instance_wait_for_operational(instance, PS3_TRUE); + if (ret != PS3_SUCCESS) { + LOG_ERROR("hno:%u wait for opt NOK.\n", PS3_HOST(instance)); + dev_info(&pdev->dev, "[PS3]hno:%u wait for opt NOK.\n", + PS3_HOST(instance)); + goto l_failed; + } + +l_norecovery: +#if defined(PS3_AER_CLEAR_STATUS) + pci_aer_clear_nonfatal_status(pdev); +#elif defined(PS3_AER_CLEAR_STATUS_LOW_KERNER) + pci_cleanup_aer_uncorrect_error_status(pdev); +#endif + ps3_watchdog_start(instance); + scsi_unblock_requests(instance->host); + instance->pci_err_handle_state = PS3_DEVICE_ERR_STATE_NORMAL; + return; +l_failed: +#if defined(PS3_AER_CLEAR_STATUS) + pci_aer_clear_nonfatal_status(pdev); +#elif defined(PS3_AER_CLEAR_STATUS_LOW_KERNER) + pci_cleanup_aer_uncorrect_error_status(pdev); +#endif + if (instance) + ps3_instance_state_transfer_to_dead(instance); + instance->pci_err_handle_state = PS3_DEVICE_ERR_STATE_NORMAL; +} diff --git a/drivers/scsi/linkdata/ps3stor/linux/ps3_pcie_err_handle.h b/drivers/scsi/linkdata/ps3stor/linux/ps3_pcie_err_handle.h new file mode 100644 index 0000000000000000000000000000000000000000..cb27fec77376c49c27c239194d3b8ea65fd89703 --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/linux/ps3_pcie_err_handle.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _PS3_PCIE_ERR_HANDLE_H_ +#define _PS3_PCIE_ERR_HANDLE_H_ + +#include +#include + +#include "ps3_instance_manager.h" +#ifdef __cplusplus +extern "C" { +#endif + +void ps3_pci_err_handler_init(struct pci_driver *drv); + +int ps3_base_init_resources(struct ps3_instance *instance); + +void ps3_base_free_resources(struct ps3_instance *instance); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/drivers/scsi/linkdata/ps3stor/ps3_cmd_channel.c b/drivers/scsi/linkdata/ps3stor/ps3_cmd_channel.c new file mode 100644 index 0000000000000000000000000000000000000000..6c84419d3b565791b1de8a19e1b6d1631da298cb --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/ps3_cmd_channel.c @@ -0,0 +1,1688 @@ +// SPDX-License-Identifier: GPL-2.0 +#ifndef _WINDOWS +#include +#include +#include +#include +#include +#include +#include +#include "ps3_trace_id_alloc.h" +#endif + +#include "ps3_cmd_channel.h" +#include "ps3_instance_manager.h" +#include "ps3_inner_data.h" +#include "ps3_irq.h" +#include "ps3_cmd_complete.h" +#include "ps3_ioc_manager.h" +#include "ps3_scsih_cmd_parse.h" +#include "ps3_htp.h" +#include "ps3_util.h" +#include "ps3_ioctl.h" +#include "ps3_cmd_statistics.h" +#include "ps3_r1x_write_lock.h" +#include "ps3_module_para.h" +#include "ps3_scsih.h" +#include "ps3_ioc_state.h" +#include "ps3_mgr_cmd.h" + +static int ps3_req_frame_alloc(struct ps3_instance *instance); +static void ps3_req_frame_free(struct ps3_instance *instance); +static int ps3_cmd_resp_frame_alloc(struct ps3_instance *instance); +static void ps3_cmd_resp_frame_free(struct ps3_instance *instance); +static int ps3_cmd_buf_alloc(struct ps3_instance *instance); +static void ps3_cmd_buf_free(struct ps3_instance *instance); +static int ps3_cmd_ext_buf_alloc(struct ps3_instance *instance); +static void ps3_cmd_ext_buf_free(struct ps3_instance *instance); +static int ps3_cmd_r1xlock_buff_alloc(struct ps3_instance *instance); +static void ps3_cmd_r1xlock_buff_free(struct ps3_instance *instance); +static int ps3_cmd_init(struct ps3_instance *instance); +static void ps3_cmd_content_init(struct ps3_cmd *cmd); +static inline unsigned char is_mgr_cmd(struct ps3_instance *instance, + unsigned int index); +static inline unsigned char is_task_cmd(struct ps3_instance *instance, + unsigned int index); +static void cmd_pool_free(struct list_head *pool_list, spinlock_t *pool_lock, + struct ps3_cmd *cmd); +static struct ps3_cmd *cmd_pool_alloc(struct list_head *pool_list, + spinlock_t *pool_lock); +#define PS3_RESP_FRAME_LENGTH (PS3_SENSE_BUFFER_SIZE + 32) + +static inline unsigned char is_mgr_cmd(struct ps3_instance *instance, + unsigned int index) +{ + index -= instance->cmd_context.max_scsi_cmd_count; + return index < instance->max_mgr_cmd_count ? PS3_DRV_TRUE : + PS3_DRV_FALSE; +} + +static inline unsigned char is_task_cmd(struct ps3_instance *instance, + unsigned int index) +{ + index -= (instance->cmd_context.max_scsi_cmd_count + + instance->max_mgr_cmd_count); + return index < instance->max_task_cmd_count ? PS3_DRV_TRUE : + PS3_DRV_FALSE; +} + +static inline unsigned char is_r1x_peer_cmd(struct ps3_instance *instance, + unsigned int index) +{ + struct ps3_cmd_context *cmd_ctx = &instance->cmd_context; + + return ((cmd_ctx->max_scsi_cmd_count - cmd_ctx->max_r1x_cmd_count) <= + index && + index < cmd_ctx->max_scsi_cmd_count) ? + PS3_DRV_TRUE : + PS3_DRV_FALSE; +} + +struct ps3_cmd *ps3_r1x_peer_cmd_alloc(struct ps3_instance *instance, + unsigned int index) +{ + struct ps3_cmd *cmd = NULL; + struct ps3_cmd_context *context = &instance->cmd_context; + unsigned int offset = + context->max_scsi_cmd_count - context->max_r1x_cmd_count; + + if (instance->r1x_mode == PS3_R1X_MODE_PERF) { + cmd = context->cmd_buf[index + offset]; + cmd->cmd_state.state = PS3_CMD_STATE_PROCESS; + ps3_trace_id_alloc(&cmd->trace_id); + init_completion(&cmd->sync_done); + } else { + cmd = cmd_pool_alloc(&context->r1x_scsi_cmd_pool, + &context->r1x_scsi_pool_lock); + if (cmd != NULL && cmd->is_aborting == 1) { + cmd->cmd_state.state = PS3_CMD_STATE_INIT; + cmd->trace_id = 0; + cmd_pool_free(&context->r1x_scsi_cmd_pool, + &context->r1x_scsi_pool_lock, cmd); + cmd = NULL; + } + } + return cmd; +} + +struct ps3_cmd *ps3_mgr_cmd_alloc(struct ps3_instance *instance) +{ + struct ps3_cmd_context *context = &instance->cmd_context; + + return cmd_pool_alloc(&context->mgr_cmd_pool, &context->mgr_pool_lock); +} + +int ps3_mgr_cmd_free(struct ps3_instance *instance, struct ps3_cmd *cmd) +{ + int ret = PS3_SUCCESS; + unsigned long flags = 0; + + ps3_spin_lock_irqsave(&cmd->cmd_state.lock, &flags); + ret = ps3_mgr_cmd_free_nolock(instance, cmd); + ps3_spin_unlock_irqrestore(&cmd->cmd_state.lock, flags); + + return ret; +} + +struct ps3_cmd *ps3_task_cmd_alloc(struct ps3_instance *instance) +{ + struct ps3_cmd *cmd = NULL; + struct ps3_cmd_context *context = NULL; + + context = &instance->cmd_context; + cmd = cmd_pool_alloc(&context->task_cmd_pool, &context->task_pool_lock); + if (cmd == NULL) + cmd = ps3_mgr_cmd_alloc(instance); + return cmd; +} + +int ps3_task_cmd_free(struct ps3_instance *instance, struct ps3_cmd *cmd) +{ + unsigned long flags = 0; + int ret = PS3_SUCCESS; + (void)instance; + + ps3_spin_lock_irqsave(&cmd->cmd_state.lock, &flags); + ret = ps3_mgr_cmd_free_nolock(instance, cmd); + ps3_spin_unlock_irqrestore(&cmd->cmd_state.lock, flags); + + return ret; +} + +static int ps3_req_frame_alloc(struct ps3_instance *instance) +{ + unsigned int size = 0; + struct ps3_cmd_context *context = &instance->cmd_context; + + size = PS3_DEFAULT_REQ_FRAME_SIZE * context->max_cmd_count; + + context->req_frame_dma_pool = (struct dma_pool *)ps3_dma_pool_create( + "PS3 req frame pool", &instance->pdev->dev, size, + DMA_ALIGN_BYTES_256, 0); + if (!context->req_frame_dma_pool) { + LOG_ERROR("Failed to setup frame pool\n"); + goto l_create_dma_pool_failed; + } + + context->req_frame_buf = (unsigned char *)ps3_dma_pool_alloc( + instance, context->req_frame_dma_pool, GFP_KERNEL, + &context->req_frame_buf_phys); + if (!context->req_frame_buf) { + LOG_ERROR("Failed to alloc frame dma memory\n"); + goto l_free_mem; + } + + return PS3_SUCCESS; + +l_free_mem: + if (context->req_frame_dma_pool) { + ps3_dma_pool_destroy(context->req_frame_dma_pool); + context->req_frame_dma_pool = NULL; + } +l_create_dma_pool_failed: + return -PS3_FAILED; +} + +static void ps3_req_frame_free(struct ps3_instance *instance) +{ + struct ps3_cmd_context *context = &instance->cmd_context; +#ifndef _WINDOWS + if (context->req_frame_buf) { + ps3_dma_pool_free(context->req_frame_dma_pool, + context->req_frame_buf, + context->req_frame_buf_phys); + context->req_frame_buf = NULL; + } + if (context->req_frame_dma_pool) { + ps3_dma_pool_destroy(context->req_frame_dma_pool); + context->req_frame_dma_pool = NULL; + } +#else + if (context->req_frame_buf != NULL) { + ps3_dma_free_coherent(instance, context->req_frame_buf_size, + context->req_frame_buf, + context->req_frame_buf_phys); + context->req_frame_buf = NULL; + context->req_frame_buf_size = 0; + } +#endif +} + +static int ps3_cmd_resp_frame_alloc(struct ps3_instance *instance) +{ + unsigned int sense_size = 0; + struct ps3_cmd_context *context = &instance->cmd_context; + + sense_size = PS3_RESP_FRAME_LENGTH * context->max_cmd_count; + + context->response_frame_dma_pool = + (struct dma_pool *)ps3_dma_pool_create("PS3 respSense pool", + &instance->pdev->dev, + sense_size, + DMA_ALIGN_BYTES_4K, 0); + + if (!context->response_frame_dma_pool) { + LOG_ERROR("Failed to setup sense pool\n"); + goto l_failed_alloc; + } + context->response_frame_buf = (unsigned char *)ps3_dma_pool_alloc( + instance, context->response_frame_dma_pool, GFP_KERNEL, + &context->response_frame_buf_phys); + if (!context->response_frame_buf) { + LOG_ERROR("Failed to alloc sense dma memory\n"); + goto l_free_mem; + } + ps3_get_so_addr_ranger(instance, context->response_frame_buf_phys, + sense_size); + return PS3_SUCCESS; + +l_free_mem: + if (context->response_frame_dma_pool) { + ps3_dma_pool_destroy(context->response_frame_dma_pool); + context->response_frame_dma_pool = NULL; + } + +l_failed_alloc: + return -PS3_FAILED; +} + +static void ps3_cmd_resp_frame_free(struct ps3_instance *instance) +{ + struct ps3_cmd_context *context = &instance->cmd_context; +#ifndef _WINDOWS + if (context->response_frame_buf) { + ps3_dma_pool_free(context->response_frame_dma_pool, + context->response_frame_buf, + context->response_frame_buf_phys); + context->response_frame_buf = NULL; + } + if (context->response_frame_dma_pool) { + ps3_dma_pool_destroy(context->response_frame_dma_pool); + context->response_frame_dma_pool = NULL; + } +#else + if (context->response_frame_buf != NULL) { + ps3_dma_free_coherent(instance, + context->response_frame_buf_size, + context->response_frame_buf, + context->response_frame_buf_phys); + context->response_frame_buf = NULL; + context->response_frame_buf_size = 0; + } +#endif +} + +static void ps3_cmd_mgr_trans_free(struct ps3_instance *instance) +{ + unsigned int i = 0; + struct ps3_cmd *cmd = NULL; + struct ps3_cmd_context *context = &instance->cmd_context; + + if (context->cmd_buf == NULL) + return; + + for (i = 0; i < instance->max_mgr_cmd_count; i++) { + if (context->cmd_buf) { + cmd = context->cmd_buf[instance->cmd_context + .max_scsi_cmd_count + + i]; + if (cmd && cmd->transient) { + ps3_kfree(instance, cmd->transient); + cmd->transient = NULL; + } + } + } +} + +static int ps3_cmd_mgr_trans_alloc(struct ps3_instance *instance) +{ + unsigned int i = 0; + struct ps3_cmd *cmd; + struct ps3_cmd_context *context = &instance->cmd_context; + + for (i = 0; i < instance->max_mgr_cmd_count; i++) { + cmd = context->cmd_buf[instance->cmd_context.max_scsi_cmd_count + + i]; + + cmd->transient = (struct ps3_ioctl_transient *)ps3_kzalloc( + instance, sizeof(struct ps3_ioctl_transient)); + if (cmd->transient == NULL) { + LOG_ERROR("Failed to alloc sge dma memory\n"); + goto l_free; + } + } + return PS3_SUCCESS; +l_free: + ps3_cmd_mgr_trans_free(instance); + return -PS3_FAILED; +} + +static int ps3_cmd_ext_buf_alloc(struct ps3_instance *instance) +{ + unsigned int i = 0; + unsigned int sge_frame_size = 0; + struct ps3_cmd *cmd; + struct ps3_cmd_context *context = &instance->cmd_context; + + sge_frame_size = sizeof(struct PS3Sge) * context->ext_sge_frame_count; + context->ext_buf_size = + PS3_MAX(sge_frame_size, PS3_CMD_EXT_BUF_DEFAULT_SIZE); +#ifndef _WINDOWS + context->ext_buf_dma_pool = (struct dma_pool *)ps3_dma_pool_create( + "PS3 ext buf pool", &instance->pdev->dev, context->ext_buf_size, + PS3_CMD_EXT_BUF_DEFAULT_SIZE, 0); + if (!context->ext_buf_dma_pool) { + LOG_ERROR("Failed to setup sense pool\n"); + goto l_failed_alloc; + } + + for (i = 0; i < context->max_scsi_cmd_count; i++) { + cmd = context->cmd_buf[i]; + cmd->ext_buf = + ps3_dma_pool_zalloc(instance, context->ext_buf_dma_pool, + GFP_KERNEL, + (dma_addr_t *)&cmd->ext_buf_phys); + if (!cmd->ext_buf) { + LOG_ERROR("Failed to alloc scsi ext buf memory\n"); + goto l_free_sge; + } + } + + context->mgr_ext_buf_size = PS3_CMD_EXT_BUF_SIZE_MGR; + context->mgr_ext_buf_dma_pool = (struct dma_pool *)ps3_dma_pool_create( + "PS3 mgr ext buf pool", &instance->pdev->dev, + context->ext_buf_size, PS3_CMD_EXT_BUF_SIZE_MGR, 0); + if (!context->mgr_ext_buf_dma_pool) { + LOG_ERROR("Failed to setup sense pool\n"); + goto l_failed_alloc; + } + + for (i = context->max_scsi_cmd_count; i < context->max_cmd_count; i++) { + cmd = context->cmd_buf[i]; + cmd->ext_buf = ps3_dma_pool_zalloc( + instance, context->mgr_ext_buf_dma_pool, GFP_KERNEL, + (dma_addr_t *)&cmd->ext_buf_phys); + if (!cmd->ext_buf) { + LOG_ERROR("Failed to alloc mgr ext buf memory\n"); + goto l_free_sge; + } + } + + return PS3_SUCCESS; +l_free_sge: + ps3_cmd_ext_buf_free(instance); +l_failed_alloc: + return -PS3_FAILED; +#else + for (i = 0; i < context->max_scsi_cmd_count; i++) { + cmd = context->cmd_buf[i]; + cmd->ext_buf = ps3_dma_alloc_coherent( + instance, context->ext_buf_size, + (unsigned long long *)&cmd->ext_buf_phys); + if (cmd->ext_buf == NULL) { + LOG_ERROR("Failed to alloc scsi ext buf memory\n"); + goto l_failed; + } + } + + context->mgr_ext_buf_size = PS3_CMD_EXT_BUF_SIZE_MGR; + for (i = context->max_scsi_cmd_count; i < context->max_cmd_count; i++) { + cmd = context->cmd_buf[i]; + cmd->ext_buf = ps3_dma_alloc_coherent( + instance, context->mgr_ext_buf_size, + (unsigned long long *)&cmd->ext_buf_phys); + if (cmd->ext_buf == NULL) { + LOG_ERROR("Failed to alloc mgr ext buf memory\n"); + goto l_failed; + } + } + + return PS3_SUCCESS; + +l_failed: + ps3_cmd_ext_buf_free(instance); + return -PS3_FAILED; + +#endif +} + +static void ps3_cmd_ext_buf_free(struct ps3_instance *instance) +{ + unsigned int i = 0; + struct ps3_cmd *cmd = NULL; + struct ps3_cmd_context *context = &instance->cmd_context; + + if (context->cmd_buf == NULL) + return; +#ifndef _WINDOWS + for (i = 0; i < context->max_scsi_cmd_count; i++) { + cmd = context->cmd_buf[i]; + if ((cmd != NULL) && (cmd->ext_buf != NULL)) { + ps3_dma_pool_free(context->ext_buf_dma_pool, + cmd->ext_buf, cmd->ext_buf_phys); + cmd->ext_buf = NULL; + } + } + + if (context->ext_buf_dma_pool) { + ps3_dma_pool_destroy(context->ext_buf_dma_pool); + context->ext_buf_dma_pool = NULL; + } + + for (i = context->max_scsi_cmd_count; i < context->max_cmd_count; i++) { + cmd = context->cmd_buf[i]; + if ((cmd != NULL) && (cmd->ext_buf != NULL)) { + ps3_dma_pool_free(context->mgr_ext_buf_dma_pool, + cmd->ext_buf, cmd->ext_buf_phys); + cmd->ext_buf = NULL; + } + } + + if (context->mgr_ext_buf_dma_pool) { + ps3_dma_pool_destroy(context->mgr_ext_buf_dma_pool); + context->mgr_ext_buf_dma_pool = NULL; + } +#else + for (i = 0; i < context->max_scsi_cmd_count; i++) { + cmd = context->cmd_buf[i]; + if ((cmd != NULL) && (cmd->ext_buf != NULL)) { + ps3_dma_free_coherent(instance, context->ext_buf_size, + cmd->ext_buf, cmd->ext_buf_phys); + cmd->ext_buf = NULL; + } + } + + for (i = context->max_scsi_cmd_count; i < context->max_cmd_count; i++) { + cmd = context->cmd_buf[i]; + if ((cmd != NULL) && (cmd->ext_buf != NULL)) { + ps3_dma_free_coherent(instance, + context->mgr_ext_buf_size, + cmd->ext_buf, cmd->ext_buf_phys); + cmd->ext_buf = NULL; + } + } +#endif +} + +static int ps3_cmd_r1xlock_buff_alloc(struct ps3_instance *instance) +{ + unsigned int i = 0; + struct ps3_cmd_context *context = &instance->cmd_context; + struct ps3_cmd *cmd = NULL; + unsigned int node_buff_size = ps3_r1x_get_node_Buff_size(); + + for (i = 0; i < context->max_scsi_cmd_count; i++) { + cmd = context->cmd_buf[i]; + cmd->szblock_cnt = 0; + cmd->node_buff = ps3_kzalloc(instance, node_buff_size); + if (!cmd->node_buff) { + LOG_ERROR( + "Failed to alloc r1x write lock range node buf memory\n"); + goto l_free_node; + } + } + + return PS3_SUCCESS; + +l_free_node: + ps3_cmd_r1xlock_buff_free(instance); + return -PS3_FAILED; +} + +static void ps3_cmd_r1xlock_buff_free(struct ps3_instance *instance) +{ + unsigned int i = 0; + struct ps3_cmd_context *context = &instance->cmd_context; + struct ps3_cmd *cmd = NULL; + + if (context->cmd_buf == NULL) + return; + + for (i = 0; i < context->max_cmd_count; i++) { + cmd = context->cmd_buf[i]; + if (cmd != NULL) { + cmd->szblock_cnt = 0; + if (cmd->node_buff != NULL) { + ps3_kfree(instance, cmd->node_buff); + cmd->node_buff = NULL; + } + } + } +} + +static int ps3_cmd_buf_alloc(struct ps3_instance *instance) +{ + unsigned int i = 0; + struct ps3_cmd_context *context = &instance->cmd_context; + + context->cmd_buf = (struct ps3_cmd **)ps3_kcalloc( + instance, context->max_cmd_count, sizeof(struct ps3_cmd *)); + if (context->cmd_buf == NULL) { + LOG_ERROR("Failed to kcalloc memory for cmd_buf\n"); + goto l_failed; + } + memset(context->cmd_buf, 0, + sizeof(struct ps3_cmd *) * context->max_cmd_count); + for (i = 0; i < context->max_cmd_count; i++) { + context->cmd_buf[i] = (struct ps3_cmd *)ps3_kzalloc( + instance, sizeof(struct ps3_cmd)); + if (context->cmd_buf[i] == NULL) { + LOG_ERROR("Failed to malloc memory for ps3_cmd\n"); + goto l_failed; + } + } + + INIT_LIST_HEAD(&context->mgr_cmd_pool); + INIT_LIST_HEAD(&context->task_cmd_pool); + INIT_LIST_HEAD(&context->r1x_scsi_cmd_pool); + ps3_spin_lock_init(&context->mgr_pool_lock); + ps3_spin_lock_init(&context->task_pool_lock); + ps3_spin_lock_init(&context->r1x_scsi_pool_lock); + return PS3_SUCCESS; + +l_failed: + ps3_cmd_buf_free(instance); + return -PS3_FAILED; +} + +static void ps3_cmd_buf_free(struct ps3_instance *instance) +{ + unsigned int i = 0; + struct ps3_cmd_context *context = &instance->cmd_context; + + if (context->cmd_buf == NULL) + goto l_out; + + while (i < context->max_cmd_count && context->cmd_buf[i]) { + ps3_kfree(instance, context->cmd_buf[i]); + context->cmd_buf[i] = NULL; + i++; + } + + ps3_kfree(instance, context->cmd_buf); + context->cmd_buf = NULL; + INIT_LIST_HEAD(&context->mgr_cmd_pool); + INIT_LIST_HEAD(&context->task_cmd_pool); + INIT_LIST_HEAD(&context->r1x_scsi_cmd_pool); +#ifdef _WINDOWS + INIT_LIST_HEAD(&context->scsi_cmd_pool); +#endif +l_out: + return; +} + +static int ps3_cmd_init(struct ps3_instance *instance) +{ + unsigned short i = 0; + int ret = PS3_SUCCESS; + unsigned int offset = 0; + + struct ps3_cmd *cmd = NULL; + struct ps3_cmd_context *context = &instance->cmd_context; + + for (i = 0; i < context->max_cmd_count; i++) { + cmd = context->cmd_buf[i]; + if (!cmd) { + LOG_ERROR("Failed %s\n", __func__); + ret = -PS3_FAILED; + goto l_out; + } + + cmd->instance = instance; + offset = i * PS3_RESP_FRAME_LENGTH; + cmd->resp_frame = + (union PS3RespFrame *)(context->response_frame_buf + + offset); + cmd->resp_frame_phys = + context->response_frame_buf_phys + offset; + cmd->index = i; + cmd->is_aborting = 0; +#ifndef _WINDOWS + cmd->scmd = NULL; +#endif + offset = i * PS3_DEFAULT_REQ_FRAME_SIZE; + cmd->req_frame = + (union PS3ReqFrame *)(context->req_frame_buf + offset); + cmd->req_frame_phys = context->req_frame_buf_phys + offset; + ps3_spin_lock_init(&cmd->cmd_state.lock); + ps3_cmd_content_init(cmd); + if (is_r1x_peer_cmd(instance, i)) { + if (instance->r1x_mode == PS3_R1X_MODE_NORMAL) { + list_add_tail(&cmd->cmd_list, + &context->r1x_scsi_cmd_pool); + } + } else if (is_task_cmd(instance, i)) { + list_add_tail(&cmd->cmd_list, &context->task_cmd_pool); + init_completion(&cmd->sync_done); + } else if (is_mgr_cmd(instance, i)) { + list_add_tail(&cmd->cmd_list, &context->mgr_cmd_pool); + init_completion(&cmd->sync_done); + } +#ifdef _WINDOWS + else + list_add_tail(&cmd->cmd_list, &context->scsi_cmd_pool); +#endif + } + +l_out: + return ret; +} + +static inline void ps3_host_max_sge_count(struct ps3_cmd_context *context) +{ + context->max_host_sge_count = PS3_FRAME_REQ_SGE_NUM_HW; + if (context->sgl_mode_support) { + if (context->ext_sge_frame_count > PS3_FRAME_REQ_EXT_SGE_MIN) { + context->max_host_sge_count += + (unsigned short)context->ext_sge_frame_count - + PS3_FRAME_REQ_EXT_SGE_MIN; + } + } else if (context->ext_sge_frame_count > 1) { + context->max_host_sge_count = PS3_MAX( + (unsigned short)(context->ext_sge_frame_count - 1), + context->max_host_sge_count); + } +} + +static inline void ps3_r1x_mode_set(struct ps3_instance *instance) +{ + struct ps3_cmd_context *context = &instance->cmd_context; + + if (context->max_r1x_cmd_count >= context->max_scsi_cmd_count / 2) + instance->r1x_mode = PS3_R1X_MODE_PERF; + LOG_INFO("host_no:%u r1x_mode:%u\n", PS3_HOST(instance), + instance->r1x_mode); +} + +int ps3_cmd_context_init(struct ps3_instance *instance) +{ + int ret = -PS3_FAILED; + struct ps3_cmd_context *context = &instance->cmd_context; + int cpu = 0; + long long *scsi_cmd_deliver = NULL; + + if (!ps3_ioc_mgr_max_fw_cmd_get(instance, &context->max_cmd_count)) + goto l_failed; +#ifdef PS3_HARDWARE_SIM + context->max_r1x_cmd_count = 16; +#else + if (!ps3_get_max_r1x_cmds_with_check(instance, + &context->max_r1x_cmd_count)) { + goto l_failed; + } +#endif + LOG_DEBUG("host_no:%u max_r1x_cmd_count:%u\n", PS3_HOST(instance), + context->max_r1x_cmd_count); + context->max_mgr_cmd_count = instance->max_mgr_cmd_total_count; + context->max_scsi_cmd_count = + context->max_cmd_count - instance->max_mgr_cmd_total_count; + + if (context->max_r1x_cmd_count > (context->max_scsi_cmd_count / 2)) + context->max_r1x_cmd_count = (context->max_scsi_cmd_count / 2); + ps3_r1x_mode_set(instance); + + LOG_DEBUG("host_no:%u max_r1x_cmd_final count:%u\n", PS3_HOST(instance), + context->max_r1x_cmd_count); + + if (!ps3_ioc_mgr_max_chain_size_get(instance, + &context->ext_sge_frame_count)) { + goto l_failed; + } + context->ext_sge_frame_count /= sizeof(struct PS3Sge); + + if (!ps3_ioc_mgr_max_nvme_page_size_get( + instance, &instance->cmd_attr.nvme_page_size)) { + goto l_failed; + } + context->max_prp_count = + PS3_FRAME_REQ_PRP_NUM_FE + (instance->cmd_attr.nvme_page_size / + sizeof(unsigned long long)); + + ps3_host_max_sge_count(context); + + if (context->max_cmd_count <= instance->max_mgr_cmd_total_count) { + LOG_ERROR("max_cmd_count %d too few\n", context->max_cmd_count); + goto l_failed; + } + + ret = ps3_cmd_buf_alloc(instance); + if (ret != PS3_SUCCESS) + goto l_failed; + + ret = ps3_req_frame_alloc(instance); + if (ret != PS3_SUCCESS) + goto l_failed; + + ret = ps3_cmd_resp_frame_alloc(instance); + if (ret != PS3_SUCCESS) + goto l_failed; + + ret = ps3_cmd_ext_buf_alloc(instance); + if (ret != PS3_SUCCESS) + goto l_failed; + + ret = ps3_cmd_r1xlock_buff_alloc(instance); + if (ret != PS3_SUCCESS) + goto l_failed; + + ret = ps3_cmd_mgr_trans_alloc(instance); + if (ret != PS3_SUCCESS) + goto l_failed; + + ret = ps3_cmd_init(instance); + if (ret != PS3_SUCCESS) + goto l_failed; + + instance->scsi_cmd_deliver = alloc_percpu(long long); + if (!instance->scsi_cmd_deliver) { + LOG_ERROR("alloc per_cpu scsi_cmd_deliver failed. hno:%u\n", + PS3_HOST(instance)); + ret = -PS3_FAILED; + goto l_failed; + } else { + for_each_possible_cpu(cpu) { + scsi_cmd_deliver = + per_cpu_ptr(instance->scsi_cmd_deliver, cpu); + *scsi_cmd_deliver = 0; + } + } + + return ret; + +l_failed: + ps3_cmd_context_exit(instance); + return ret; +} + +void ps3_cmd_context_exit(struct ps3_instance *instance) +{ + ps3_cmd_mgr_trans_free(instance); + ps3_cmd_ext_buf_free(instance); + ps3_cmd_r1xlock_buff_free(instance); + ps3_cmd_resp_frame_free(instance); + ps3_req_frame_free(instance); + ps3_cmd_buf_free(instance); + + if (instance->scsi_cmd_deliver) { + free_percpu(instance->scsi_cmd_deliver); + instance->scsi_cmd_deliver = NULL; + } +} + +static void ps3_cmd_content_init(struct ps3_cmd *cmd) +{ + cmd->cmd_word_value = 0; +#ifndef _WINDOWS + cmd->scmd = NULL; +#else + cmd->srb = NULL; + memset(&cmd->scmd_imp, 0, sizeof(cmd->scmd_imp)); + cmd->scmd = &cmd->scmd_imp; +#endif + cmd->trace_id = 0; + cmd->no_reply_word = 0; + cmd->os_sge_map_count = 0; + INIT_LIST_HEAD(&cmd->cmd_list); + cmd->cmd_receive_cb = NULL; + cmd->cmd_send_cb = NULL; + + cmd->cmd_state.state = PS3_CMD_STATE_INIT; + cmd->cmd_state.reset_flag = 0; + cmd->qos_processing = PS3_FALSE; + cmd->time_out = 0; + cmd->is_interrupt = PS3_DRV_FALSE; + cmd->retry_cnt = 0; + cmd->is_force_polling = 0; + cmd->is_inserted_c_q = 0; + cmd->is_got_r1x = 0; + cmd->is_r1x_aborting = 0; + cmd->r1x_peer_cmd = NULL; + cmd->r1x_reply_flag = 0; + cmd->is_r1x_scsi_complete = PS3_FALSE; + cmd->flighting = PS3_FALSE; + cmd->r1x_read_pd = 0; + + memset(&cmd->io_attr, 0, sizeof(struct ps3_scsi_io_attr)); + memset((void *)&cmd->sync_done, 0, sizeof(cmd->sync_done)); + if (cmd->transient == NULL || cmd->transient->sge_num == 0) + memset((void *)cmd->req_frame, 0, sizeof(union PS3ReqFrame)); + + memset((void *)cmd->resp_frame, 0xff, sizeof(union PS3RespFrame)); + + memset(cmd->ext_buf, 0, cmd->instance->cmd_context.ext_buf_size); + + INIT_LIST_HEAD(&cmd->qos_list); + memset(&cmd->target_pd, 0, + sizeof(struct ps3_qos_member_pd_info) * PS3_QOS_MAX_PD_IN_VD); + cmd->target_pd_count = 0; + cmd->first_over_quota_pd_idx = 0; + cmd->qos_waitq_flag = 0; + memset(&cmd->cmdq_info, 0, + sizeof(struct ps3_qos_cmdq_info) * PS3_QOS_MAX_CMDQ_ONE_CMD); + cmd->cmdq_count = 0; +} + +static void ps3_scsi_cmd_content_init(struct ps3_cmd *cmd) +{ + cmd->cmd_word_value = 0; +#ifndef _WINDOWS + cmd->scmd = NULL; +#else + cmd->srb = NULL; + memset(&cmd->scmd_imp, 0, sizeof(cmd->scmd_imp)); + cmd->scmd = &cmd->scmd_imp; +#endif + cmd->trace_id = 0; + cmd->no_reply_word = 0; + cmd->os_sge_map_count = 0; + INIT_LIST_HEAD(&cmd->cmd_list); + cmd->cmd_receive_cb = NULL; + cmd->cmd_send_cb = NULL; + + cmd->cmd_state.state = PS3_CMD_STATE_INIT; + cmd->cmd_state.reset_flag = 0; + cmd->qos_processing = PS3_FALSE; + cmd->time_out = 0; + cmd->is_interrupt = PS3_DRV_FALSE; + cmd->retry_cnt = 0; + cmd->is_force_polling = 0; + cmd->is_inserted_c_q = 0; + cmd->is_got_r1x = 0; + cmd->is_r1x_aborting = 0; + cmd->r1x_peer_cmd = NULL; + cmd->r1x_reply_flag = 0; + cmd->is_r1x_scsi_complete = PS3_FALSE; + cmd->flighting = PS3_FALSE; + cmd->r1x_read_pd = 0; + + if (cmd->req_frame->hwReq.sgl[PS3_FRAME_REQ_SGE_NUM_HW - 1].length != + 0) { + memset(cmd->ext_buf, 0, + cmd->instance->cmd_context.ext_buf_size); + } + + if (!ps3_scsih_is_rw_type(cmd->io_attr.rw_flag) && + (cmd->transient == NULL || cmd->transient->sge_num == 0)) { + memset((void *)cmd->req_frame, 0, sizeof(union PS3ReqFrame)); + } + + if (cmd->resp_frame->normalRespFrame.respStatus != 0xFF) + cmd->resp_frame->normalRespFrame.respStatus = 0xFF; + + if (cmd->resp_frame->sasRespFrame.status != 0xFF) + cmd->resp_frame->sasRespFrame.status = 0xFF; + + memset((void *)&cmd->sync_done, 0, sizeof(cmd->sync_done)); + memset(&cmd->io_attr, 0, sizeof(struct ps3_scsi_io_attr)); + INIT_LIST_HEAD(&cmd->qos_list); + memset(&cmd->target_pd, 0, + sizeof(struct ps3_qos_member_pd_info) * PS3_QOS_MAX_PD_IN_VD); + cmd->target_pd_count = 0; + cmd->first_over_quota_pd_idx = 0; + cmd->qos_waitq_flag = 0; + memset(&cmd->cmdq_info, 0, + sizeof(struct ps3_qos_cmdq_info) * PS3_QOS_MAX_CMDQ_ONE_CMD); + cmd->cmdq_count = 0; +} + +#ifndef _WINDOWS +struct ps3_cmd *ps3_scsi_cmd_alloc(struct ps3_instance *instance, + unsigned int tag) +{ + struct ps3_cmd *cmd = NULL; + struct ps3_cmd_context *context = &instance->cmd_context; + + if (tag < (unsigned int)instance->cmd_attr.cur_can_que) { + cmd = context->cmd_buf[tag]; + cmd->cmd_state.state = PS3_CMD_STATE_PROCESS; + ps3_trace_id_alloc(&cmd->trace_id); + cmd->flighting = PS3_TRUE; + } + return cmd; +} + +int ps3_scsi_cmd_free(struct ps3_cmd *cmd) +{ + int ret = -PS3_FAILED; + + if (cmd->index < (unsigned int)cmd->instance->cmd_attr.cur_can_que) { + ps3_scsi_cmd_content_init(cmd); + ret = PS3_SUCCESS; + } + return ret; +} +#else +struct ps3_cmd *ps3_scsi_cmd_alloc(struct ps3_instance *instance) +{ + struct ps3_cmd *cmd = NULL; + struct ps3_cmd_context *context = &instance->cmd_context; + unsigned long flags = 0; + + ps3_spin_lock_irqsave(&context->scsi_pool_lock, &flags); + if (!list_empty(&context->scsi_cmd_pool)) { + cmd = list_entry(list_remove_head(&context->scsi_cmd_pool), + struct ps3_cmd, cmd_list); + } + ps3_spin_unlock_irqrestore(&context->scsi_pool_lock, flags); + + if (cmd != NULL) { + cmd->trace_id = ps3_atomic64_inc(&context->trace_id); + cmd->cmd_state.state = PS3_CMD_STATE_PROCESS; + } + return cmd; +} + +int ps3_scsi_cmd_free(struct ps3_cmd *cmd) +{ + int ret = -PS3_FAILED; + unsigned int max_count = 0; + unsigned long flags = 0; + struct ps3_cmd_context *context = &cmd->instance->cmd_context; + + if (unlikely(cmd == NULL)) + goto l_out; + + max_count = context->max_scsi_cmd_count; + if (cmd->index < max_count) { + ps3_cmd_content_init(cmd); + ret = PS3_SUCCESS; + } + + ps3_spin_lock_irqsave(&context->scsi_pool_lock, &flags); + list_add_tail(&cmd->cmd_list, &context->scsi_cmd_pool); + ps3_spin_unlock_irqrestore(&context->scsi_pool_lock, flags); +l_out: + return ret; +} +#endif + +static void cmd_pool_free(struct list_head *pool_list, spinlock_t *pool_lock, + struct ps3_cmd *cmd) +{ + unsigned long flags = 0; + + ps3_spin_lock_irqsave(pool_lock, &flags); + list_add_tail(&cmd->cmd_list, pool_list); + ps3_spin_unlock_irqrestore(pool_lock, flags); +} + +static struct ps3_cmd *cmd_pool_alloc(struct list_head *pool_list, + spinlock_t *pool_lock) +{ + struct ps3_cmd *cmd = NULL; + unsigned long flags = 0; +#ifdef _WINDOWS + struct ps3_cmd_context *context = NULL; +#endif + ps3_spin_lock_irqsave(pool_lock, &flags); + if (!list_empty(pool_list)) { +#ifdef _WINDOWS + cmd = list_first_entry(pool_list, struct ps3_cmd, cmd_list); +#else + cmd = list_entry(pool_list->next, struct ps3_cmd, cmd_list); +#endif + list_del_init(&cmd->cmd_list); + } + ps3_spin_unlock_irqrestore(pool_lock, flags); + + if (cmd != NULL) { +#ifndef _WINDOWS + ps3_trace_id_alloc(&cmd->trace_id); +#else + context = &cmd->instance->cmd_context; + cmd->trace_id = ps3_atomic64_inc(&context->trace_id); +#endif + cmd->cmd_state.state = PS3_CMD_STATE_PROCESS; + init_completion(&cmd->sync_done); + } + + return cmd; +} + +unsigned char ps3_r1x_peer_cmd_free_nolock(struct ps3_cmd *cmd) +{ + unsigned char ret = PS3_TRUE; + struct ps3_cmd_context *context = NULL; + struct ps3_instance *instance = cmd->instance; + + context = &instance->cmd_context; + if (!is_r1x_peer_cmd(instance, cmd->index)) { + ret = PS3_FALSE; + goto l_out; + } + + if (cmd->cmd_state.state == PS3_CMD_STATE_INIT) + goto l_out; + + ps3_scsi_cmd_content_init(cmd); + if (instance->r1x_mode == PS3_R1X_MODE_NORMAL) { + cmd_pool_free(&context->r1x_scsi_cmd_pool, + &context->r1x_scsi_pool_lock, cmd); + } +l_out: + return ret; +} + +int ps3_mgr_cmd_free_nolock(struct ps3_instance *instance, struct ps3_cmd *cmd) +{ + int ret = PS3_SUCCESS; + struct ps3_cmd_context *context = NULL; + + context = &instance->cmd_context; + if (cmd->index < context->max_scsi_cmd_count) { + ret = -PS3_FAILED; + goto l_out; + } + + if (cmd->cmd_state.state == PS3_CMD_STATE_INIT) + goto l_out; + + ps3_cmd_content_init(cmd); + if (is_task_cmd(instance, cmd->index)) { + cmd_pool_free(&context->task_cmd_pool, &context->task_pool_lock, + cmd); + } else if (is_mgr_cmd(instance, cmd->index)) { + cmd_pool_free(&context->mgr_cmd_pool, &context->mgr_pool_lock, + cmd); + } else { + LOG_INFO_IN_IRQ(instance, "host_no:%u CFID:%u not mgr cmd!\n", + PS3_HOST(instance), cmd->index); + PS3_BUG(); + ret = -PS3_FAILED; + } +l_out: + return ret; +} + +int ps3_async_cmd_send(struct ps3_instance *instance, struct ps3_cmd *cmd) +{ + int ret = PS3_SUCCESS; + int cur_state = PS3_INSTANCE_STATE_INIT; + + ps3_atomic_inc(&instance->cmd_statistics.cmd_delivering); + mb(); /* in order to force CPU ordering */ + ret = ps3_cmd_send_pre_check(instance); + if (ret != PS3_SUCCESS) + goto l_out; + + cur_state = ps3_atomic_read(&instance->state_machine.state); + if (cur_state != PS3_INSTANCE_STATE_OPERATIONAL) { + if (instance->is_probe_finish && !instance->is_resume) { + LOG_FILE_ERROR( + "host_no:%u cannot send async cmd due to %s, return fail\n", + PS3_HOST(instance), + namePS3InstanceState(cur_state)); + ret = -PS3_FAILED; + } else { + LOG_FILE_WARN( + "host_no:%u cannot send async cmd due to %s, return recovered\n", + PS3_HOST(instance), + namePS3InstanceState(cur_state)); + ret = -PS3_RECOVERED; + } + goto l_out; + } + + instance->ioc_adpter->cmd_send(instance, &cmd->cmd_word); +l_out: + ps3_atomic_dec(&instance->cmd_statistics.cmd_delivering); + return ret; +} +#ifndef _WINDOWS + +static void ps3_r1x_peer_cmd_build(struct ps3_cmd *cmd, + struct ps3_cmd *peer_cmd) +{ + memcpy(peer_cmd->req_frame, cmd->req_frame, sizeof(union PS3ReqFrame)); + memcpy((void *)&peer_cmd->io_attr, (void *)&cmd->io_attr, + sizeof(struct ps3_scsi_io_attr)); + memcpy(peer_cmd->ext_buf, cmd->ext_buf, + cmd->instance->cmd_context.ext_buf_size); + + peer_cmd->scmd = cmd->scmd; + peer_cmd->is_got_r1x = cmd->is_got_r1x; + peer_cmd->szblock_cnt = cmd->szblock_cnt; + peer_cmd->os_sge_map_count = cmd->os_sge_map_count; + peer_cmd->cmd_receive_cb = cmd->cmd_receive_cb; + peer_cmd->io_attr.pd_entry = cmd->io_attr.peer_pd_entry; + peer_cmd->io_attr.disk_id = + PS3_PDID(&peer_cmd->io_attr.pd_entry->disk_pos); + peer_cmd->io_attr.plba = cmd->io_attr.plba_back; + + peer_cmd->cmd_word_value = cmd->cmd_word_value; + peer_cmd->cmd_word.phyDiskID = + PS3_PDID(&peer_cmd->io_attr.pd_entry->disk_pos); + peer_cmd->cmd_word.cmdFrameID = peer_cmd->index; + + peer_cmd->req_frame->hwReq.reqHead.cmdFrameID = peer_cmd->index; + peer_cmd->req_frame->hwReq.reqHead.devID.diskID = + peer_cmd->io_attr.pd_entry->disk_pos.diskDev.diskID; + peer_cmd->req_frame->hwReq.reqHead.traceID = peer_cmd->trace_id; + + ps3_vd_direct_req_frame_build(peer_cmd); + + cmd->r1x_peer_cmd = peer_cmd; + peer_cmd->r1x_peer_cmd = cmd; + cmd->is_r1x_scsi_complete = PS3_FALSE; + peer_cmd->is_r1x_scsi_complete = PS3_FALSE; + + LOG_DEBUG( + "hno:%u r1x direct write cmd:%d, peer cmd:%d build: tid:0x%llx pid:%u plba:0x%llx\n", + PS3_HOST(cmd->instance), cmd->index, peer_cmd->index, + peer_cmd->trace_id, peer_cmd->cmd_word.phyDiskID, + peer_cmd->io_attr.plba); +} + +static struct ps3_cmd *ps3_r1x_scsi_peer_prepare(struct ps3_instance *instance, + struct ps3_cmd *cmd) +{ + struct ps3_cmd *peer_cmd = NULL; + + if (cmd->io_attr.direct_flag != PS3_CMDWORD_DIRECT_ADVICE || + cmd->io_attr.peer_pd_entry == NULL) { + goto _lout; + } + + peer_cmd = ps3_r1x_peer_cmd_alloc(instance, cmd->index); + if (peer_cmd != NULL) { + ps3_r1x_peer_cmd_build(cmd, peer_cmd); + } else { + LOG_DEBUG( + "host_no:%u cmd:%d can not alloc r1x peer cmd any more\n", + PS3_HOST(instance), cmd->index); + instance->ioc_adpter->io_cmd_rebuild(cmd); + } +_lout: + return peer_cmd; +} + +void ps3_wait_scsi_cmd_done(struct ps3_instance *instance, + unsigned char time_out) +{ + int cpu = 0; + long long result = 0; + unsigned short try_cnt = 0; + + if (instance->scsi_cmd_deliver) { + do { + result = 0; + for_each_possible_cpu(cpu) { + result += *per_cpu_ptr( + instance->scsi_cmd_deliver, cpu); + } + + if (result > 0) { + ps3_msleep(PS3_LOOP_TIME_INTERVAL_100MS); + if (time_out) { + try_cnt++; + if (try_cnt > + PS3_WAIT_SCSI_CMD_DONE_COUNT) { + LOG_WARN( + "hno:%u wait scsi cmd done NOK\n", + PS3_HOST(instance)); + break; + } + } + } + } while (result); + } + + LOG_INFO("wait scsi cmd done end. hno:%u try_cnt[%u]\n", + PS3_HOST(instance), try_cnt); +} + +void ps3_scsi_cmd_deliver_get(struct ps3_instance *instance) +{ + long long *cmd_deliver = NULL; + + cmd_deliver = get_cpu_ptr(instance->scsi_cmd_deliver); + (*cmd_deliver)++; + put_cpu_ptr(cmd_deliver); +} + +void ps3_scsi_cmd_deliver_put(struct ps3_instance *instance) +{ + long long *cmd_deliver = NULL; + + cmd_deliver = get_cpu_ptr(instance->scsi_cmd_deliver); + (*cmd_deliver)--; + put_cpu_ptr(cmd_deliver); +} + +void ps3_wait_mgr_cmd_done(struct ps3_instance *instance, + unsigned char time_out) +{ + unsigned short try_cnt = 0; + + while (ps3_atomic_read(&instance->cmd_statistics.cmd_delivering) != 0) { + ps3_msleep(PS3_LOOP_TIME_INTERVAL_100MS); + if (time_out) { + try_cnt++; + if (try_cnt > PS3_WAIT_SCSI_CMD_DONE_COUNT) { + LOG_WARN("hno:%u wait mgr cmd done NOK\n", + PS3_HOST(instance)); + break; + } + } + } + + LOG_INFO("wait mgr cmd done end. hno:%u try_cnt[%u]\n", + PS3_HOST(instance), try_cnt); +} + +int ps3_scsi_cmd_send(struct ps3_instance *instance, struct ps3_cmd *cmd, + unsigned char need_prk_err) +{ + int ret = PS3_SUCCESS; + struct ps3_cmd *peer_cmd = NULL; + + ret = ps3_cmd_send_pre_check(instance); + if (ret != PS3_SUCCESS) + goto l_out; + + if (unlikely(instance->task_manager_host_busy)) { + LOG_INFO_LIM_WITH_CHECK( + instance, need_prk_err, + "host_no:%u cannot send block cmd due to task_manager_host_busy\n", + PS3_HOST(instance)); + + ret = -PS3_RETRY; + goto l_out; + } + + if (!instance->state_machine.is_load) { + LOG_WARN_LIM_WITH_CHECK( + instance, need_prk_err, + "host_no:%u instance state not is_load\n", + PS3_HOST(instance)); + ret = -PS3_FAILED; + goto l_out; + } + + + if (!ps3_is_instance_state_normal(instance, need_prk_err)) { + ret = -PS3_RECOVERED; + goto l_out; + } + + peer_cmd = ps3_r1x_scsi_peer_prepare(instance, cmd); + + PS3_DEV_IO_START_INC(instance, cmd); + PS3_DEV_IO_START_OK_INC(instance, cmd); + PS3_DEV_IO_OUTSTAND_INC(instance, cmd); + PS3_IO_DRV2IOC_START_INC(instance, cmd); + cmd->flighting = PS3_FALSE; + wmb(); /* in order to force CPU ordering */ + + ps3_ioc_scsi_cmd_send(instance, &cmd->cmd_word); + if (peer_cmd != NULL) { + PS3_IO_DRV2IOC_START_INC(instance, peer_cmd); + ps3_ioc_scsi_cmd_send(instance, &peer_cmd->cmd_word); + } +l_out: + + return ret; +} +#endif + +struct ps3_cmd *ps3_cmd_find(struct ps3_instance *instance, + unsigned short cmd_frame_id) +{ + struct ps3_cmd *cmd = NULL; + struct ps3_cmd_context *context = &instance->cmd_context; + + if (cmd_frame_id >= context->max_cmd_count) { + LOG_ERROR_IN_IRQ(instance, "host_no:%u CFID:%d invalid\n", + PS3_HOST(instance), cmd_frame_id); + goto l_failed; + } + + cmd = context->cmd_buf[cmd_frame_id]; + if (cmd->index != cmd_frame_id) { + LOG_ERROR_IN_IRQ( + instance, + "host_no:%u CFID:%d incorrect, expect CFID:%d\n", + PS3_HOST(instance), cmd_frame_id, cmd->index); + cmd = NULL; + goto l_failed; + } + +l_failed: + return cmd; +} + +int ps3_cmd_dispatch(struct ps3_instance *instance, unsigned short cmd_frame_id, + struct PS3ReplyWord *reply_word) +{ + int ret = -PS3_FAILED; + struct ps3_cmd *cmd = NULL; + unsigned short reply_flags = 0xff; + + if (reply_word->retType == PS3_HARD_RET && + reply_word->retStatus == PS3_REPLY_WORD_FLAG_REPEAT_REPLY) { + LOG_ERROR_IN_IRQ( + instance, + "hno:%u repeated response CFID:%u reply_word:0x%llx\n", + PS3_HOST(instance), cmd_frame_id, + *(unsigned long long *)reply_word); + goto l_out; + } + cmd = ps3_cmd_find(instance, cmd_frame_id); + if (cmd == NULL) + goto l_out; + memcpy(&(cmd->reply_word), reply_word, sizeof(struct PS3ReplyWord)); + reply_flags = reply_word->retStatus; + if (cmd->cmd_receive_cb) { + ret = cmd->cmd_receive_cb(cmd, reply_flags); + } else { + LOG_ERROR_IN_IRQ( + instance, + "warn ps3 cmd index %d has no cmd_receive_cb\n", + cmd->index); + } +l_out: + return ret; +} + +unsigned char +ps3_is_instance_state_allow_cmd_execute(struct ps3_instance *instance) +{ + unsigned char ret = PS3_TRUE; + int cur_state = PS3_INSTANCE_STATE_INIT; + + cur_state = ps3_atomic_read(&instance->state_machine.state); + if (cur_state == PS3_INSTANCE_STATE_DEAD && + (PS3_IOC_STATE_HALT_SUPPORT(instance) == PS3_TRUE) && + PS3_HALT_CLI_SUPPORT(instance)) { + goto l_out; + } + + if (cur_state != PS3_INSTANCE_STATE_OPERATIONAL && + cur_state != PS3_INSTANCE_STATE_PRE_OPERATIONAL && + cur_state != PS3_INSTANCE_STATE_SOFT_RECOVERY) { + LOG_FILE_INFO( + "host_no:%u cannot handle cmd, driver state: %s\n", + PS3_HOST(instance), namePS3InstanceState(cur_state)); + ret = PS3_FALSE; + } + +l_out: + return ret; +} + +int ps3_cmd_send_pre_check(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + + if (instance->is_probe_finish && !instance->state_machine.is_load) { + LOG_FILE_INFO("host_no:%u instance state is unloading\n", + PS3_HOST(instance)); + ret = -PS3_FAILED; + goto l_out; + } + if (ps3_pci_err_recovery_get(instance)) { + LOG_FILE_WARN( + "host_no:%u cannot send block cmd due to pci err recovery\n", + PS3_HOST(instance)); + ret = -PS3_FAILED; + goto l_out; + } +l_out: + return ret; +} + +int ps3_mgr_cmd_send_pre_check(struct ps3_instance *instance, + unsigned char no_check) +{ + int ret = PS3_SUCCESS; + + if (!no_check && !instance->state_machine.is_load) { + LOG_WARN_LIM("hno[%u] instance state not is_load\n", + PS3_HOST(instance)); + ret = -PS3_IN_UNLOAD; + goto l_out; + } + if (!ps3_is_instance_state_allow_cmd_execute(instance)) { + ret = -PS3_RECOVERED; + goto l_out; + } + if (ps3_pci_err_recovery_get(instance)) { + LOG_WARN_LIM("hno[%u] host in pci err recovery\n", + PS3_HOST(instance)); + ret = -PS3_IN_PCIE_ERR; + goto l_out; + } +l_out: + return ret; +} + +int ps3_mgr_cmd_send_check(struct ps3_instance *instance, struct ps3_cmd *cmd) +{ + int ret = PS3_SUCCESS; + int cur_state = ps3_atomic_read(&instance->state_machine.state); + + if (!instance->state_machine.is_load) { + if (PS3_MGR_CMD_TYPE(cmd) != PS3_CMD_MANAGEMENT) { + ret = -PS3_IN_UNLOAD; + goto l_failed; + } + } + if (!ps3_is_instance_state_allow_cmd_execute(instance)) { + ret = -PS3_RECOVERED; + if (PS3_MGR_CMD_TYPE(cmd) == PS3_CMD_IOCTL) { + cur_state = + ps3_atomic_read(&instance->state_machine.state); + if (cur_state == PS3_INSTANCE_STATE_QUIT || + cur_state == PS3_INSTANCE_STATE_DEAD) { + goto l_failed; + } + cmd->resp_frame->normalRespFrame.respStatus = + PS3_DRV_MGR_BUSY; + ret = -PS3_RESP_ERR; + } + goto l_failed; + } + if (ps3_pci_err_recovery_get(instance)) { + ret = -PS3_IN_PCIE_ERR; + goto l_failed; + } + goto l_out; + +l_failed: + LOG_WARN_LIM( + "hno:%u, tid:0x%llx CFID:%u type:%d state:%d send check ret:%d\n", + PS3_HOST(instance), cmd->trace_id, cmd->index, + PS3_MGR_CMD_TYPE(cmd), cur_state, ret); +l_out: + return ret; +} + +void ps3_dma_addr_bit_pos_update(struct ps3_instance *instance, + unsigned char bit_pos) +{ + unsigned int i = 0; + struct ps3_irq_context *irq_context = &instance->irq_context; + struct ps3_cmd_context *cmd_context = &instance->cmd_context; + struct ps3_debug_context *debug_context = &instance->debug_context; + struct ps3_dump_context *dump_context = &instance->dump_context; + struct ps3_dev_context *dev_context = &instance->dev_context; + struct ps3_sas_dev_context *ps3_sas_ctx = &instance->sas_dev_context; + struct ps3_cmd *cmd = NULL; + + irq_context->reply_fifo_desc_buf_phys = + PCIE_DMA_HOST_ADDR_BIT_POS_CLEAR_NEW( + bit_pos, irq_context->reply_fifo_desc_buf_phys); + irq_context->reply_fifo_desc_buf_phys = + PCIE_DMA_HOST_ADDR_BIT_POS_SET_NEW( + instance->dma_addr_bit_pos, + irq_context->reply_fifo_desc_buf_phys); + for (; i < irq_context->valid_msix_vector_count; i++) { + irq_context->reply_fifo_desc_buf[i].ReplyFifoBaseAddr = + PCIE_DMA_HOST_ADDR_BIT_POS_CLEAR_NEW( + bit_pos, irq_context->reply_fifo_desc_buf[i] + .ReplyFifoBaseAddr); + irq_context->reply_fifo_desc_buf[i] + .ReplyFifoBaseAddr = PCIE_DMA_HOST_ADDR_BIT_POS_SET_NEW( + instance->dma_addr_bit_pos, + irq_context->reply_fifo_desc_buf[i].ReplyFifoBaseAddr); + irq_context->reply_fifo_phys_base_addr_buf[i] = + PCIE_DMA_HOST_ADDR_BIT_POS_CLEAR_NEW( + bit_pos, + irq_context->reply_fifo_phys_base_addr_buf[i]); + irq_context->reply_fifo_phys_base_addr_buf[i] = + PCIE_DMA_HOST_ADDR_BIT_POS_SET_NEW( + instance->dma_addr_bit_pos, + irq_context->reply_fifo_phys_base_addr_buf[i]); + } + cmd_context->init_frame_buf_phys = PCIE_DMA_HOST_ADDR_BIT_POS_CLEAR_NEW( + bit_pos, cmd_context->init_frame_buf_phys); + cmd_context->init_frame_buf_phys = PCIE_DMA_HOST_ADDR_BIT_POS_SET_NEW( + instance->dma_addr_bit_pos, cmd_context->init_frame_buf_phys); + cmd_context->init_filter_table_phy_addr = + PCIE_DMA_HOST_ADDR_BIT_POS_CLEAR_NEW( + bit_pos, cmd_context->init_filter_table_phy_addr); + cmd_context->init_filter_table_phy_addr = + PCIE_DMA_HOST_ADDR_BIT_POS_SET_NEW( + instance->dma_addr_bit_pos, + cmd_context->init_filter_table_phy_addr); + if (cmd_context->init_frame_sys_info_buf != NULL) { + cmd_context->init_frame_sys_info_phys = + PCIE_DMA_HOST_ADDR_BIT_POS_CLEAR_NEW( + bit_pos, cmd_context->init_frame_sys_info_phys); + cmd_context->init_frame_sys_info_phys = + PCIE_DMA_HOST_ADDR_BIT_POS_SET_NEW( + instance->dma_addr_bit_pos, + cmd_context->init_frame_sys_info_phys); + } + instance->ctrl_info_buf_h = PCIE_DMA_HOST_ADDR_BIT_POS_CLEAR_NEW( + bit_pos, instance->ctrl_info_buf_h); + instance->ctrl_info_buf_h = PCIE_DMA_HOST_ADDR_BIT_POS_SET_NEW( + instance->dma_addr_bit_pos, instance->ctrl_info_buf_h); + cmd_context->req_frame_buf_phys = PCIE_DMA_HOST_ADDR_BIT_POS_CLEAR_NEW( + bit_pos, cmd_context->req_frame_buf_phys); + cmd_context->req_frame_buf_phys = PCIE_DMA_HOST_ADDR_BIT_POS_SET_NEW( + instance->dma_addr_bit_pos, cmd_context->req_frame_buf_phys); + cmd_context->response_frame_buf_phys = + PCIE_DMA_HOST_ADDR_BIT_POS_CLEAR_NEW( + bit_pos, cmd_context->response_frame_buf_phys); + cmd_context->response_frame_buf_phys = + PCIE_DMA_HOST_ADDR_BIT_POS_SET_NEW( + instance->dma_addr_bit_pos, + cmd_context->response_frame_buf_phys); + for (i = 0; i < cmd_context->max_cmd_count; i++) { + cmd = cmd_context->cmd_buf[i]; + if (cmd->ext_buf != NULL) { + cmd->ext_buf_phys = + PCIE_DMA_HOST_ADDR_BIT_POS_CLEAR_NEW( + bit_pos, cmd->ext_buf_phys); + cmd->ext_buf_phys = PCIE_DMA_HOST_ADDR_BIT_POS_SET_NEW( + instance->dma_addr_bit_pos, cmd->ext_buf_phys); + } + } + if (debug_context->debug_mem_buf != NULL) { + debug_context->debug_mem_buf_phy = + PCIE_DMA_HOST_ADDR_BIT_POS_CLEAR_NEW( + bit_pos, debug_context->debug_mem_buf_phy); + debug_context->debug_mem_buf_phy = + PCIE_DMA_HOST_ADDR_BIT_POS_SET_NEW( + instance->dma_addr_bit_pos, + debug_context->debug_mem_buf_phy); + for (i = 0; i < debug_context->debug_mem_array_num; i++) { + debug_context->debug_mem_buf[i].debugMemAddr = + PCIE_DMA_HOST_ADDR_BIT_POS_CLEAR_NEW( + bit_pos, debug_context->debug_mem_buf[i] + .debugMemAddr); + debug_context->debug_mem_buf[i].debugMemAddr = + PCIE_DMA_HOST_ADDR_BIT_POS_SET_NEW( + instance->dma_addr_bit_pos, + debug_context->debug_mem_buf[i] + .debugMemAddr); + } + } + if (dump_context->dump_dma_buf != NULL) { + dump_context->dump_dma_addr = + PCIE_DMA_HOST_ADDR_BIT_POS_CLEAR_NEW( + bit_pos, dump_context->dump_dma_addr); + dump_context->dump_dma_addr = + PCIE_DMA_HOST_ADDR_BIT_POS_SET_NEW( + instance->dma_addr_bit_pos, + dump_context->dump_dma_addr); + } + instance->drv_info_buf_phys = PCIE_DMA_HOST_ADDR_BIT_POS_CLEAR_NEW( + bit_pos, instance->drv_info_buf_phys); + instance->drv_info_buf_phys = PCIE_DMA_HOST_ADDR_BIT_POS_SET_NEW( + instance->dma_addr_bit_pos, instance->drv_info_buf_phys); + instance->host_mem_info_buf_phys = PCIE_DMA_HOST_ADDR_BIT_POS_CLEAR_NEW( + bit_pos, instance->host_mem_info_buf_phys); + instance->host_mem_info_buf_phys = PCIE_DMA_HOST_ADDR_BIT_POS_SET_NEW( + instance->dma_addr_bit_pos, instance->host_mem_info_buf_phys); + if (dev_context->pd_list_buf != NULL) { + dev_context->pd_list_buf_phys = + PCIE_DMA_HOST_ADDR_BIT_POS_CLEAR_NEW( + bit_pos, dev_context->pd_list_buf_phys); + dev_context->pd_list_buf_phys = + PCIE_DMA_HOST_ADDR_BIT_POS_SET_NEW( + instance->dma_addr_bit_pos, + dev_context->pd_list_buf_phys); + } + if (dev_context->pd_info_buf != NULL) { + dev_context->pd_info_buf_phys = + PCIE_DMA_HOST_ADDR_BIT_POS_CLEAR_NEW( + bit_pos, dev_context->pd_info_buf_phys); + dev_context->pd_info_buf_phys = + PCIE_DMA_HOST_ADDR_BIT_POS_SET_NEW( + instance->dma_addr_bit_pos, + dev_context->pd_info_buf_phys); + } + if (dev_context->vd_list_buf != NULL) { + dev_context->vd_list_buf_phys = + PCIE_DMA_HOST_ADDR_BIT_POS_CLEAR_NEW( + bit_pos, dev_context->vd_list_buf_phys); + dev_context->vd_list_buf_phys = + PCIE_DMA_HOST_ADDR_BIT_POS_SET_NEW( + instance->dma_addr_bit_pos, + dev_context->vd_list_buf_phys); + } + if (dev_context->vd_info_buf_sync != NULL) { + dev_context->vd_info_buf_phys_sync = + PCIE_DMA_HOST_ADDR_BIT_POS_CLEAR_NEW( + bit_pos, dev_context->vd_info_buf_phys_sync); + dev_context->vd_info_buf_phys_sync = + PCIE_DMA_HOST_ADDR_BIT_POS_SET_NEW( + instance->dma_addr_bit_pos, + dev_context->vd_info_buf_phys_sync); + } + if (dev_context->vd_info_buf_async != NULL) { + dev_context->vd_info_buf_phys_async = + PCIE_DMA_HOST_ADDR_BIT_POS_CLEAR_NEW( + bit_pos, dev_context->vd_info_buf_phys_async); + dev_context->vd_info_buf_phys_async = + PCIE_DMA_HOST_ADDR_BIT_POS_SET_NEW( + instance->dma_addr_bit_pos, + dev_context->vd_info_buf_phys_async); + } + if (ps3_sas_ctx->ps3_sas_buff != NULL) { + ps3_sas_ctx->ps3_sas_buff_dma_addr = + PCIE_DMA_HOST_ADDR_BIT_POS_CLEAR_NEW( + bit_pos, ps3_sas_ctx->ps3_sas_buff_dma_addr); + ps3_sas_ctx->ps3_sas_buff_dma_addr = + PCIE_DMA_HOST_ADDR_BIT_POS_SET_NEW( + instance->dma_addr_bit_pos, + ps3_sas_ctx->ps3_sas_buff_dma_addr); + } + if (ps3_sas_ctx->ps3_sas_phy_buff != NULL) { + ps3_sas_ctx->ps3_sas_phy_buff_dma_addr = + PCIE_DMA_HOST_ADDR_BIT_POS_CLEAR_NEW( + bit_pos, + ps3_sas_ctx->ps3_sas_phy_buff_dma_addr); + ps3_sas_ctx->ps3_sas_phy_buff_dma_addr = + PCIE_DMA_HOST_ADDR_BIT_POS_SET_NEW( + instance->dma_addr_bit_pos, + ps3_sas_ctx->ps3_sas_phy_buff_dma_addr); + } + instance->so_start_addr = PCIE_DMA_HOST_ADDR_BIT_POS_CLEAR_NEW( + bit_pos, instance->so_start_addr); + instance->so_start_addr = PCIE_DMA_HOST_ADDR_BIT_POS_SET_NEW( + instance->dma_addr_bit_pos, instance->so_start_addr); + instance->so_end_addr = PCIE_DMA_HOST_ADDR_BIT_POS_CLEAR_NEW( + bit_pos, instance->so_end_addr); + instance->so_end_addr = PCIE_DMA_HOST_ADDR_BIT_POS_SET_NEW( + instance->dma_addr_bit_pos, instance->so_end_addr); +} + +unsigned char ps3_bit_pos_update(struct ps3_instance *instance) +{ + unsigned char old_bit_pos = instance->dma_addr_bit_pos; + unsigned char bit_pos = 0; + unsigned char ret = PS3_FALSE; + + if (!ps3_ioc_atu_support_retry_read(instance, &bit_pos)) + goto l_out; + switch (bit_pos) { + case PS3_BIT_POS_DEFAULT: + case PS3_BIT_POS_44: + instance->dma_addr_bit_pos = PCIE_DMA_HOST_ADDR_BIT_POS; + break; + case PS3_BIT_POS_53: + instance->dma_addr_bit_pos = PCIE_DMA_HOST_ADDR_BIT_POS_F1; + break; + case PS3_BIT_POS_54: + instance->dma_addr_bit_pos = PCIE_DMA_HOST_ADDR_BIT_POS_F0; + break; + default: + LOG_WARN("hno:%u bit pos value is unexpect %u\n", + PS3_HOST(instance), bit_pos); + goto l_out; + } + mb(); /* in order to force CPU ordering */ + if (instance->dma_addr_bit_pos == old_bit_pos) { + ret = PS3_TRUE; + goto l_out; + } + ps3_dma_addr_bit_pos_update(instance, old_bit_pos); + LOG_WARN("hno:%u bit pos %u change to %u\n", PS3_HOST(instance), + old_bit_pos, instance->dma_addr_bit_pos); + ret = PS3_TRUE; +l_out: + return ret; +} diff --git a/drivers/scsi/linkdata/ps3stor/ps3_cmd_channel.h b/drivers/scsi/linkdata/ps3stor/ps3_cmd_channel.h new file mode 100644 index 0000000000000000000000000000000000000000..4181cd2705e6764f9a9fb4d1302fc4c0b5f25f2b --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/ps3_cmd_channel.h @@ -0,0 +1,353 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _PS3_CMD_CHANNEL_H_ +#define _PS3_CMD_CHANNEL_H_ + +#include "ps3_htp.h" +#include "ps3_htp_reqframe.h" + +#ifdef _WINDOWS +#include "ps3_def.h" +#include "ps3_cmd_adp.h" + +#endif +#include "ps3_platform_utils.h" +#include "ps3_inner_data.h" +#include "ps3_driver_log.h" +#include "ps3_htp_ioctl.h" + +#define PS3_DEFAULT_REQ_FRAME_SIZE (256) + +#define PS3_WAIT_SCSI_CMD_DONE_COUNT (1200) + +enum { + DMA_ALIGN_BYTES_4K = 4096, + DMA_ALIGN_BYTES_2K = 2048, + DMA_ALIGN_BYTES_1K = 1024, + DMA_ALIGN_BYTES_512 = 512, + DMA_ALIGN_BYTES_256 = 256, + DMA_ALIGN_BYTES_128 = 128, + DMA_ALIGN_BYTES_64 = 64, + DMA_ALIGN_BYTES_16 = 16, + DMA_ALIGN_BYTES_4 = 4, +}; + +enum { + PS3_CMD_STATE_INIT = 0, + PS3_CMD_STATE_PROCESS = 1, + PS3_CMD_STATE_COMPLETE = 2, + PS3_CMD_STATE_DEAD = 3, +}; +enum { + PS3_CMD_FLAG_SOFTRESET = 1, + PS3_CMD_FLAG_HARDRESET = 2, +}; + +enum { + PS3_R1X_MODE_NORMAL = 0, + PS3_R1X_MODE_PERF = 1, +}; + +#define PS3_CMD_EXT_BUF_DEFAULT_SIZE (4096) +#define PS3_CMD_EXT_BUF_SIZE_MGR (4096) + +#define PS3_MIN_SCSI_CMD_COUNT (4096) + +static inline const char *namePS3CmdState(unsigned int s) +{ + static const char * const myNames[] = { + [PS3_CMD_STATE_INIT] = "PS3_CMD_STATE_INIT", + [PS3_CMD_STATE_PROCESS] = "PS3_CMD_STATE_PROCESS", + [PS3_CMD_STATE_COMPLETE] = "PS3_CMD_STATE_COMPLETE", + [PS3_CMD_STATE_DEAD] = "PS3_CMD_STATE_DEAD" + }; + + if (s > PS3_CMD_STATE_DEAD) + return "PS3_CMD_STATE_INVALID"; + + return myNames[s]; +} + +struct ps3_cmd_state_t { + unsigned char state; + unsigned char reset_flag; + unsigned char reserived[6]; + spinlock_t lock; +}; +union ps3_scsi_cdb_option { + struct { + unsigned char non_ncq : 1; + unsigned char reserved0 : 1; + unsigned char reserved1 : 1; + unsigned char fua : 1; + unsigned char dpo : 1; + unsigned char protect : 3; + }; + unsigned char option; +}; + +struct ps3_scsi_io_attr { + unsigned char is_retry_cmd; + unsigned char direct_flag; + unsigned char seq_flag; + unsigned char dev_type; + union { + struct { + unsigned char rw_flag : 7; + unsigned char is_confilct_check : 1; + }; + unsigned char rw_type; + }; + unsigned int num_blocks; + unsigned int lba_lo; + unsigned int lba_hi; + const struct ps3_pd_entry *pd_entry; + const struct ps3_pd_entry *peer_pd_entry; + const struct PS3VDEntry *vd_entry; + + unsigned long long plba; + unsigned long long plba_back; + unsigned char span_idx; + unsigned char span_pd_idx; + unsigned short disk_id; + unsigned char is_use_frontend_prp; + unsigned char span_pd_idx_p; + unsigned char span_pd_idx_q; + unsigned char is_force_normal : 1; + unsigned char reserved : 7; + union ps3_scsi_cdb_option cdb_opts; + unsigned char cdb[PS3_FRAME_CDB_BUFLEN]; + unsigned int sgl_buf_len; + unsigned int reserved1; +}; + +struct ps3_ioctl_transient { + unsigned short sge_num; + unsigned char reserved[6]; + void *transient_buff[PS3_MAX_IOCTL_SGE_NUM]; +}; + +#define PS3_QOS_MAX_PD_IN_VD (17) +struct ps3_qos_member_pd_info { + unsigned short flat_disk_id; + unsigned short strip_count; + unsigned char get_quota; +}; + +#define PS3_QOS_MAX_CMDQ_ONE_CMD 2 +struct ps3_qos_cmdq_info { + unsigned char que_id; + unsigned char get_rc; +}; + +struct ps3_cmd { + union PS3ReqFrame *req_frame; + unsigned long long req_frame_phys; + void *ext_buf; + unsigned long long ext_buf_phys; + union PS3RespFrame *resp_frame; + unsigned long long resp_frame_phys; + struct ps3_instance *instance; + union { + struct PS3CmdWord cmd_word; + struct PS3InitCmdWord init_cmd_word; + unsigned long long cmd_word_value; + }; + + struct scsi_cmnd *scmd; + +#ifndef _WINDOWS + struct list_head cmd_list; +#else + SCSI_REQUEST_BLOCK * srb; + struct list_head cmd_list; +#endif + + unsigned long long trace_id; + unsigned short index; + unsigned char no_reply_word; + unsigned char is_force_polling; + unsigned char is_got_r1x; + unsigned char is_inserted_c_q; + unsigned char is_r1x_aborting; + unsigned char is_r1x_scsi_complete; + unsigned short r1x_read_pd; + struct ps3_cmd *r1x_peer_cmd; + unsigned char is_aborting; + unsigned char r1x_reply_flag; + unsigned char qos_processing; + unsigned int os_sge_map_count; + struct ps3_cmd_state_t cmd_state; + unsigned short time_out; + unsigned char is_interrupt; + unsigned char szblock_cnt; + unsigned int retry_cnt; + void *node_buff; +#ifdef _WINDOWS + KEVENT sync_done; +#else + struct completion sync_done; +#endif + int (*cmd_send_cb)(struct ps3_instance *, struct ps3_cmd *, + unsigned short); + int (*cmd_receive_cb)(struct ps3_cmd *cmd, unsigned short reply_flags); + struct ps3_ioctl_transient *transient; + struct ps3_scsi_io_attr io_attr; +#ifdef _WINDOWS + struct scsi_cmnd scmd_imp; +#endif + struct PS3ReplyWord reply_word; + + struct list_head qos_list; + struct ps3_qos_member_pd_info target_pd[PS3_QOS_MAX_PD_IN_VD]; + struct ps3_qos_cmdq_info cmdq_info[PS3_QOS_MAX_CMDQ_ONE_CMD]; + unsigned short target_pd_count; + unsigned short first_over_quota_pd_idx; + unsigned char qos_waitq_flag; + unsigned char cmdq_count; + unsigned char flighting; +}; + +struct ps3_cmd_context { + unsigned int max_cmd_count; + unsigned int max_scsi_cmd_count; + unsigned int max_mgr_cmd_count; + unsigned int max_prp_count; + struct ps3_cmd **cmd_buf; + + dma_addr_t init_frame_buf_phys; + unsigned char *init_frame_buf; + dma_addr_t init_filter_table_phy_addr; + unsigned char *init_filter_table_buff; +#ifndef _WINDOWS + struct dma_pool *req_frame_dma_pool; +#endif + unsigned int req_frame_buf_size; + dma_addr_t req_frame_buf_phys; + unsigned char *req_frame_buf; +#ifndef _WINDOWS + struct dma_pool *response_frame_dma_pool; +#endif + unsigned int response_frame_buf_size; + dma_addr_t response_frame_buf_phys; + unsigned char *response_frame_buf; +#ifndef _WINDOWS + struct dma_pool *ext_buf_dma_pool; +#endif + unsigned int ext_buf_size; + unsigned int ext_sge_frame_count; +#ifndef _WINDOWS + struct dma_pool *mgr_ext_buf_dma_pool; +#endif + unsigned int mgr_ext_buf_size; + + dma_addr_t init_frame_sys_info_phys; + unsigned char *init_frame_sys_info_buf; + unsigned char sgl_mode_support; + unsigned char reserved0[1]; + unsigned short max_host_sge_count; + +#ifndef _WINDOWS + struct list_head mgr_cmd_pool; + struct list_head task_cmd_pool; + struct list_head r1x_scsi_cmd_pool; + spinlock_t mgr_pool_lock; + spinlock_t task_pool_lock; + spinlock_t r1x_scsi_pool_lock; +#else + struct list_head mgr_cmd_pool; + struct list_head task_cmd_pool; + struct list_head scsi_cmd_pool; + spinlock_t mgr_pool_lock; + spinlock_t task_pool_lock; + spinlock_t scsi_pool_lock; + + atomic64_t trace_id; +#endif + unsigned short max_r1x_cmd_count; + unsigned char reserved1[6]; +}; + +int ps3_cmd_context_init(struct ps3_instance *instance); + +void ps3_cmd_context_exit(struct ps3_instance *instance); + +#ifndef _WINDOWS +struct ps3_cmd *ps3_scsi_cmd_alloc(struct ps3_instance *instance, + unsigned int tag); +#else +struct ps3_cmd *ps3_scsi_cmd_alloc(struct ps3_instance *instance); +#endif +int ps3_scsi_cmd_free(struct ps3_cmd *cmd); + +struct ps3_cmd *ps3_mgr_cmd_alloc(struct ps3_instance *instance); + +int ps3_mgr_cmd_free_nolock(struct ps3_instance *instance, struct ps3_cmd *cmd); +int ps3_mgr_cmd_free(struct ps3_instance *instance, struct ps3_cmd *cmd); + +struct ps3_cmd *ps3_task_cmd_alloc(struct ps3_instance *instance); + +int ps3_task_cmd_free(struct ps3_instance *instance, struct ps3_cmd *cmd); + +int ps3_reply_cmd_dispatcher(struct ps3_instance *instance, + unsigned short cmd_frame_id); + +int ps3_async_cmd_send(struct ps3_instance *instance, struct ps3_cmd *cmd); + +#ifndef _WINDOWS +int ps3_scsi_cmd_send(struct ps3_instance *instance, struct ps3_cmd *cmd, + unsigned char need_prk_err); +#endif + +struct ps3_cmd *ps3_cmd_find(struct ps3_instance *instance, + unsigned short cmd_frame_id); + +int ps3_cmd_dispatch(struct ps3_instance *instance, unsigned short cmd_frame_id, + struct PS3ReplyWord *reply_word); + +struct ps3_cmd *ps3_r1x_peer_cmd_alloc(struct ps3_instance *instance, + unsigned int index); + +unsigned char ps3_r1x_peer_cmd_free_nolock(struct ps3_cmd *cmd); + +static inline unsigned short ps3_cmd_frame_id(struct ps3_cmd *cmd) +{ + return cmd->index; +} + +static inline unsigned long long ps3_cmd_trace_id(struct ps3_cmd *cmd) +{ + return cmd->trace_id; +} + +static inline void ps3_cmd_trace_id_replace(struct ps3_cmd *cmd, + unsigned long long trace_id) +{ + cmd->trace_id = trace_id; +} + +unsigned char +ps3_is_instance_state_allow_cmd_execute(struct ps3_instance *instance); + +int ps3_cmd_send_pre_check(struct ps3_instance *instance); + +void ps3_wait_scsi_cmd_done(struct ps3_instance *instance, + unsigned char time_out); + +void ps3_scsi_cmd_deliver_get(struct ps3_instance *instance); + +void ps3_scsi_cmd_deliver_put(struct ps3_instance *instance); + +void ps3_dma_addr_bit_pos_update(struct ps3_instance *instance, + unsigned char bit_pos); + +unsigned char ps3_bit_pos_update(struct ps3_instance *instance); + +void ps3_wait_mgr_cmd_done(struct ps3_instance *instance, + unsigned char time_out); + +int ps3_mgr_cmd_send_pre_check(struct ps3_instance *instance, + unsigned char no_check); + +int ps3_mgr_cmd_send_check(struct ps3_instance *instance, struct ps3_cmd *cmd); + +#endif diff --git a/drivers/scsi/linkdata/ps3stor/ps3_cmd_complete.c b/drivers/scsi/linkdata/ps3stor/ps3_cmd_complete.c new file mode 100644 index 0000000000000000000000000000000000000000..5700b0dca13b3e1fd5b7aa52977733473db5bbd0 --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/ps3_cmd_complete.c @@ -0,0 +1,363 @@ +// SPDX-License-Identifier: GPL-2.0 +#ifdef _WINDOWS + +#else +#include +#include +#include +#include +#include +#include +#include + +#endif +#include "ps3_cmd_complete.h" +#include "ps3_cmd_channel.h" +#include "ps3_driver_log.h" +#include "ps3_err_def.h" +#include "ps3_ioc_state.h" +#include "ps3_ioc_manager.h" +#include "ps3_scsi_cmd_err.h" + +static inline unsigned char ps3_reply_word_is_valid(unsigned long long val); +static inline void ps3_reply_word_next(struct ps3_irq *irq, + struct PS3ReplyWord **reply_word); +static int ps3_reply_fifo_traverse(struct ps3_irq *irq, + struct ps3_instance *instance, + struct PS3ReplyWord *reply_word, + int *completed_count); +static inline struct PS3ReplyWord *ps3_reply_word_query(struct ps3_irq *irq, + unsigned short idx); + +int ps3_cmd_complete(struct ps3_irq *irq) +{ + int ret = PS3_SUCCESS; + int completed_count = 0; + struct PS3ReplyWord *reply_word = NULL; + struct ps3_instance *instance = irq->instance; + int cur_state = PS3_INSTANCE_STATE_INIT; +#ifndef _WINDOWS + LOG_DEBUG( + "hno:%u start isr_os:%d, name:%s sn:%d, en:%d, poll:%d, last_reply_idx:%d, is_busy:%d\n", + PS3_HOST(irq->instance), irq->irqNo, irq->name, irq->isrSN, + irq->is_enable_irq, irq->is_sched_irq_poll, + irq->last_reply_idx, atomic_read(&irq->is_busy)); +#else + LOG_DEBUG("hno:%u start isr_os:%d, name:%s isr_sn:%d, last_reply_idx:%d, is_busy:%d\n", + PS3_HOST(irq->instance), irq->irqNo, irq->name, irq->isrSN, + irq->last_reply_idx, ps3_atomic_read(&irq->is_busy)); +#endif + cur_state = ps3_atomic_read(&instance->state_machine.state); + if (unlikely(cur_state == PS3_INSTANCE_STATE_QUIT)) { + LOG_WARN_IN_IRQ(instance, "hno:%u instance state is quit\n", + PS3_HOST(instance)); + ret = -PS3_FAILED; + goto l_out; + } + + if (!ps3_irq_busy_add(irq)) { + LOG_INFO_IN_IRQ(instance, "hno:%u irq is_busy:%d\n", + PS3_HOST(instance), + ps3_atomic_read(&irq->is_busy)); + goto l_out; + } + + reply_word = ps3_reply_word_query(irq, irq->last_reply_idx); + if (!ps3_reply_word_is_valid(*(unsigned long long *)reply_word)) { + ps3_irq_busy_dec(irq); + LOG_DEBUG("hno:%u reply_w:0x%llx last_reply_idx:%d\n", + PS3_HOST(instance), *(unsigned long long *)reply_word, + irq->last_reply_idx); + goto l_out; + } + + ret = ps3_reply_fifo_traverse(irq, instance, reply_word, + &completed_count); + if (ret == -PS3_IN_IRQ_POLLING) { + ps3_irq_busy_dec(irq); + LOG_DEBUG("hno:%u reply in irq polling:%d\n", + PS3_HOST(instance), ret); + goto l_out; + } + + if (completed_count != 0) { + wmb(); /* in order to force CPU ordering */ +#ifndef _WINDOWS + ps3_can_queue_depth_update(instance); +#endif + } + + ps3_irq_busy_dec(irq); +#ifndef _WINDOWS + LOG_DEBUG( + "hno:%u end isr_os:%d, name:%s sn:%d, en:%d, poll:%d, lastidx:%d, count:%d, is_busy:%d\n", + PS3_HOST(instance), irq->irqNo, irq->name, irq->isrSN, + irq->is_enable_irq, irq->is_sched_irq_poll, irq->last_reply_idx, + completed_count, atomic_read(&irq->is_busy)); +#else + LOG_DEBUG( + "hno:%u end isr_os:%d, name:%s sn:%d, irq_enable:%d, complete_count:%d, is_busy:%d\n", + PS3_HOST(instance), irq->irqNo, irq->name, irq->isrSN, + irq->last_reply_idx, completed_count, + ps3_atomic_read(&irq->is_busy)); +#endif + +l_out: + return completed_count; +} + +static inline struct PS3ReplyWord *ps3_reply_word_query(struct ps3_irq *irq, + unsigned short idx) +{ + return irq->reply_fifo_virt_base_addr + idx; +} + +static inline unsigned char ps3_reply_word_is_valid(unsigned long long val) +{ + unsigned long long type = val & U64_MAX; + + return (type != U64_MAX); +} + +static inline void ps3_reply_word_next(struct ps3_irq *irq, + struct PS3ReplyWord **reply_word) +{ + if (!irq->last_reply_idx) + *reply_word = ps3_reply_word_query(irq, irq->last_reply_idx); + else + ++(*reply_word); +} +#ifndef _WINDOWS +void ps3_trigger_irq_poll(struct ps3_irq *irq) +{ + LOG_DEBUG("host_no:%u trigger irq_poll isrSN:%d\n", + PS3_HOST(irq->instance), irq->isrSN); + + if (irq->is_sched_irq_poll) { + LOG_DEBUG("host_no:%u irq_poll_is_processing is PS3_TRUE\n", + PS3_HOST(irq->instance)); + goto l_out; + } + + irq->is_sched_irq_poll = PS3_TRUE; + irq->is_enable_irq = PS3_TRUE; + ps3_irq_poll_sched(&irq->irqpoll); + +l_out: + return; +} + +void ps3_can_queue_depth_update(struct ps3_instance *instance) +{ + unsigned long flag = 0; + unsigned long time_threahold = 5; + int old_host_can_que = 0; + + if (instance->fault_context.ioc_busy && + time_after(jiffies, instance->fault_context.last_time + + time_threahold * HZ) && + (atomic_read(&instance->cmd_statistics.io_outstanding) < + (instance->cmd_attr.throttle_que_depth + 1))) { + instance->fault_context.ioc_busy = PS3_FALSE; + + spin_lock_irqsave(instance->host->host_lock, flag); + old_host_can_que = instance->host->can_queue; + instance->host->can_queue = instance->cmd_attr.cur_can_que; + spin_unlock_irqrestore(instance->host->host_lock, flag); + LOG_INFO( + "hno:%u old_can_queue:%d, cur_can_que:%d, ioc_busy:%d.\n", + PS3_HOST(instance), old_host_can_que, + instance->cmd_attr.cur_can_que, + instance->fault_context.ioc_busy); + } +} +#endif +static int ps3_reply_fifo_traverse(struct ps3_irq *irq, + struct ps3_instance *instance, + struct PS3ReplyWord *reply_word, + int *completed_count) +{ + int ret = PS3_SUCCESS; + unsigned int reply_threshold_count = 0; + unsigned long long *r_word = NULL; + static unsigned short reply_word_size = sizeof(struct PS3ReplyWord); + + while (ps3_reply_word_is_valid(*(unsigned long long *)reply_word)) { + r_word = (unsigned long long *)reply_word; + LOG_DEBUG( + "hno:%u CFID:%d reply_word:0x%llx reply_f:%d reply_mode:%d rettype:%d\n", + PS3_HOST(instance), le16_to_cpu(reply_word->cmdFrameID), + *r_word, reply_word->retStatus, reply_word->mode, + reply_word->retType); + ret = ps3_cmd_dispatch(instance, + le16_to_cpu(reply_word->cmdFrameID), + reply_word); + if (unlikely(ret != PS3_SUCCESS)) { + LOG_ERROR_IN_IRQ( + instance, + "host_no:%u CFID:%d dispatch cmd fail\n", + PS3_HOST(instance), + le16_to_cpu(reply_word->cmdFrameID)); + } + + memset(reply_word, 0xff, reply_word_size); + + ++(irq->last_reply_idx); + if (irq->last_reply_idx >= + instance->irq_context.reply_fifo_depth) { + LOG_DEBUG("last_reply_idx = %d, depth=%d\n", + irq->last_reply_idx, + instance->irq_context.reply_fifo_depth); + irq->last_reply_idx = 0; + } + + if (((++(*completed_count)) & (PS3_QOS_NOTIFY_CMD_COUNT - 1)) == + 0) { + ps3_qos_waitq_notify(instance); + } + + ++reply_threshold_count; + + ps3_reply_word_next(irq, &reply_word); + if (!ps3_reply_word_is_valid(*(unsigned long long *)reply_word)) + break; +#ifndef _WINDOWS +#ifdef CONFIG_IRQ_POLL + if (reply_threshold_count >= irq->irq_poll_sched_threshold) { + reply_threshold_count = 0; + wmb(); /* in order to force CPU ordering */ + ps3_trigger_irq_poll(irq); + ret = -PS3_IN_IRQ_POLLING; + break; + } +#endif +#endif + } + + if (((*completed_count) & (PS3_QOS_NOTIFY_CMD_COUNT - 1)) > 0) + ps3_qos_waitq_notify(instance); + + return ret; +} + +int ps3_resp_status_convert(unsigned int resp_status) +{ + int ret = PS3_SUCCESS; + + switch (resp_status) { + case U8_MAX: + ret = -PS3_TIMEOUT; + break; + case SCSI_STATUS_GOOD: + ret = PS3_SUCCESS; + break; + default: + ret = -PS3_RESP_ERR; + break; + } + + return ret; +} + +int ps3_cmd_reply_polling(struct ps3_instance *instance, struct ps3_cmd *cmd, + unsigned long timeout, unsigned char ignore) +{ + const unsigned int seconds_to_msecs_unit = 1000; + const unsigned int step_size = 20; + const unsigned int read_fw_satus_period = 5000; + unsigned int local_resp_status = U8_MAX; + unsigned int msecs = U32_MAX; + unsigned int i = 0; + unsigned long flags = 0; + unsigned int time_out; + int cur_state = PS3_INSTANCE_STATE_INIT; + + time_out = max_t(unsigned long, cmd->time_out, timeout); + if (time_out != 0) + msecs = time_out * seconds_to_msecs_unit; + + for (i = 0; + (i < msecs) && (cmd->resp_frame->normalRespFrame.respStatus == + PS3_SCSI_STATUS_MASK); + i += step_size) { + rmb(); /* in order to force CPU ordering */ + ps3_msleep(step_size); + if (i % read_fw_satus_period) + continue; + + if (ignore) { + cur_state = + ps3_atomic_read(&instance->state_machine.state); + if (cur_state == PS3_INSTANCE_STATE_DEAD) + break; + } else { + if (!ps3_is_instance_state_allow_cmd_execute( + instance)) { + break; + } + } + } + + local_resp_status = + le32_to_cpu(cmd->resp_frame->normalRespFrame.respStatus); + LOG_DEBUG("host_no:%u CFID:%d, respStatus:0x%x\n", PS3_HOST(instance), + cmd->index, local_resp_status); + + if (local_resp_status != U8_MAX) { + ps3_spin_lock_irqsave(&cmd->cmd_state.lock, &flags); + cmd->cmd_state.state = PS3_CMD_STATE_COMPLETE; + ps3_spin_unlock_irqrestore(&cmd->cmd_state.lock, flags); + } + + return ps3_resp_status_convert(local_resp_status); +} +int ps3_cmd_reply_polling_when_recovery(struct ps3_instance *instance, + struct ps3_cmd *cmd, + unsigned long timeout) +{ + const unsigned int seconds_to_msecs_unit = 1000; + const unsigned int step_size = 20; + const unsigned int read_fw_satus_period = 5000; + unsigned int local_resp_status = U8_MAX; + unsigned int msecs = U32_MAX; + unsigned int i = 0; + unsigned long flags = 0; + unsigned int time_out; + + time_out = max_t(unsigned long, cmd->time_out, timeout); + if (time_out != 0) + msecs = time_out * seconds_to_msecs_unit; + + for (i = 0; + (i < msecs) && (cmd->resp_frame->normalRespFrame.respStatus == + PS3_SCSI_STATUS_MASK); + i += step_size) { + rmb(); /* in order to force CPU ordering */ + ps3_msleep(step_size); + if (i % read_fw_satus_period) + continue; + } + + local_resp_status = + le32_to_cpu(cmd->resp_frame->normalRespFrame.respStatus); + LOG_DEBUG("host_no:%u CFID:%d, respStatus:0x%x\n", PS3_HOST(instance), + cmd->index, local_resp_status); + + if (local_resp_status != U8_MAX) { + ps3_spin_lock_irqsave(&cmd->cmd_state.lock, &flags); + cmd->cmd_state.state = PS3_CMD_STATE_COMPLETE; + ps3_spin_unlock_irqrestore(&cmd->cmd_state.lock, flags); + } + + return ps3_resp_status_convert(local_resp_status); +} + +void ps3_all_reply_fifo_complete(struct ps3_instance *instance) +{ + unsigned int i = 0; + + if (instance->irq_context.irqs == NULL) + return; + for (; i < instance->irq_context.valid_msix_vector_count; ++i) + (void)ps3_cmd_complete(instance->irq_context.irqs + i); +} diff --git a/drivers/scsi/linkdata/ps3stor/ps3_cmd_complete.h b/drivers/scsi/linkdata/ps3stor/ps3_cmd_complete.h new file mode 100644 index 0000000000000000000000000000000000000000..bc5f7c701d70e31f19e37f080af034038aa649b7 --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/ps3_cmd_complete.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _PS3_CMD_COMPLETE_H_ +#define _PS3_CMD_COMPLETE_H_ + +#include "ps3_htp_def.h" +#include "ps3_irq.h" +#include "ps3_instance_manager.h" +#include "ps3_inner_data.h" + +int ps3_cmd_complete(struct ps3_irq *irq); + +int ps3_cmd_reply_polling(struct ps3_instance *instance, struct ps3_cmd *cmd, + unsigned long timeout, unsigned char ignore); + +int ps3_cmd_reply_polling_when_recovery(struct ps3_instance *instance, + struct ps3_cmd *cmd, + unsigned long timeout); + +void ps3_all_reply_fifo_complete(struct ps3_instance *instance); + +void ps3_can_queue_depth_update(struct ps3_instance *instance); + +#endif diff --git a/drivers/scsi/linkdata/ps3stor/ps3_cmd_stat_def.h b/drivers/scsi/linkdata/ps3stor/ps3_cmd_stat_def.h new file mode 100644 index 0000000000000000000000000000000000000000..cfd0e7b6e6957824e9e0891267fee6905f5bb361 --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/ps3_cmd_stat_def.h @@ -0,0 +1,189 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _PS3_CMD_STAT_DEF_H_ +#define _PS3_CMD_STAT_DEF_H_ + +#ifndef _WINDOWS +#include +#include +#else +#include "ps3_worker.h" +#endif +#include "ps3_htp_def.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct ps3_instance; + +enum { + PS3_STAT_LOG_COUNT = 0, + PS3_STAT_LOG_MAX_COUNT = 10, + PS3_STAT_START = 0, + PS3_STAT_BACK = 1, + PS3_STAT_WROKQ_NAME_MAX_LEN = 16, + PS3_STAT_WORKQ_INTERVAL_DEFAULT = 5000, +}; + +enum ps3_cmd_stat_item { + PS3_SCSI_DRV_READ, + PS3_SCSI_DRV_WRITE, + PS3_SCSI_DRV_NORW, + PS3_SCSI_DRV_ALL, + PS3_DRV_IOC_READ, + PS3_DRV_IOC_WRITE, + PS3_DRV_IOC_NORW, + PS3_DRV_IOC_ALL, + PS3_DRV_IOC_VD_READ, + PS3_DRV_IOC_VD_WRITE, + PS3_DRV_IOC_VD_NORW, + PS3_DRV_IOC_PD_READ, + PS3_DRV_IOC_PD_WRITE, + PS3_DRV_IOC_PD_NORW, + PS3_DRV_IOC_VD_D_READ, + PS3_DRV_IOC_VD_D_WRITE, + PS3_DRV_IOC_PD_D_READ, + PS3_DRV_IOC_PD_D_WRITE, + PS3_DRV_IOC_MGR, + PS3_SCSI_ABORT, + PS3_SCSI_DEVICE_RESET, + PS3_SCSI_RETRY_CMD, + PS3_QOS_PD_PRO, + PS3_QOS_VD_PRO, + PS3_QOS_TAG_PRO, + PS3_QOS_MGR_PRO, + PS3_QOS_CMD_PRO, + PS3_QOS_PD_QUEUE, + PS3_QOS_VD_QUEUE, + PS3_QOS_TAG_QUEUE, + PS3_QOS_MGR_QUEUE, + PS3_QOS_CMD_QUEUE, + PS3_CMD_STAT_COUNT, +}; + +static inline const char * +ps3_cmd_stat_item_tostring(enum ps3_cmd_stat_item type) +{ + static const char * const itme_string[] = { + [PS3_SCSI_DRV_READ] = "scsi_drv_read", + [PS3_SCSI_DRV_WRITE] = "scsi_drv_write", + [PS3_SCSI_DRV_NORW] = "scsi_drv_norw", + [PS3_SCSI_DRV_ALL] = "scsi_drv_all", + [PS3_DRV_IOC_READ] = "scsi_drv_ioc_read", + [PS3_DRV_IOC_WRITE] = "scsi_drv_ioc_write", + [PS3_DRV_IOC_NORW] = "scsi_drv_ioc_norw", + [PS3_DRV_IOC_ALL] = "scsi_drv_ioc_all", + [PS3_DRV_IOC_VD_READ] = "scsi_drv_ioc_vd_read", + [PS3_DRV_IOC_VD_WRITE] = "scsi_drv_ioc_vd_write", + [PS3_DRV_IOC_VD_NORW] = "scsi_drv_ioc_vd_norw", + [PS3_DRV_IOC_PD_READ] = "scsi_drv_ioc_pd_read", + [PS3_DRV_IOC_PD_WRITE] = "scsi_drv_ioc_pd_write", + [PS3_DRV_IOC_PD_NORW] = "scsi_drv_ioc_pd_norw", + [PS3_DRV_IOC_VD_D_READ] = "scsi_drv_ioc_vd_d_read", + [PS3_DRV_IOC_VD_D_WRITE] = "scsi_drv_ioc_vd_d_write", + [PS3_DRV_IOC_PD_D_READ] = "scsi_drv_ioc_pd_d_read", + [PS3_DRV_IOC_PD_D_WRITE] = "scsi_drv_ioc_pd_d_write", + [PS3_DRV_IOC_MGR] = "mgr_drv_ioc_cmd", + [PS3_SCSI_ABORT] = "task_abort", + [PS3_SCSI_DEVICE_RESET] = "task_reset", + [PS3_SCSI_RETRY_CMD] = "scsi_retry_cmd", + [PS3_QOS_PD_PRO] = "qos_pd_pro", + [PS3_QOS_VD_PRO] = "qos_vd_pro", + [PS3_QOS_TAG_PRO] = "qos_tag_pro", + [PS3_QOS_MGR_PRO] = "qos_mgr_pro", + [PS3_QOS_CMD_PRO] = "qos_cmd_pro", + [PS3_QOS_PD_QUEUE] = "qos_pd_queue", + [PS3_QOS_VD_QUEUE] = "qos_vd_queue", + [PS3_QOS_TAG_QUEUE] = "qos_tag_queue", + [PS3_QOS_MGR_QUEUE] = "qos_mgr_queue", + [PS3_QOS_CMD_QUEUE] = "qos_cmd_queue", + [PS3_CMD_STAT_COUNT] = "type_unknown", + }; + + return (type >= PS3_CMD_STAT_COUNT) ? "type_unknown" : + itme_string[type]; +} + +struct ps3_lagency_info { + unsigned long long start_time; + unsigned long long back_time; + unsigned long long avg; + unsigned long long max_lagency; + unsigned long long min_lagency; + unsigned long long all; +}; + +struct ps3_cmd_stat_entry { + unsigned long long start; + unsigned long long back_good; + unsigned long long back_err; + unsigned long long not_back; + struct ps3_lagency_info lagency; +}; + +struct ps3_total_cmd_stat { + struct ps3_cmd_stat_entry stat[PS3_CMD_STAT_COUNT]; +}; + +enum ps3_cmd_stat_back_flag { + PS3_STAT_BACK_OK, + PS3_STAT_BACK_FAIL, + PS3_STAT_BACK_NO = 0xFF, +}; + +struct ps3_single_cmd_stat { + struct ps3_cmd_stat_entry stat[PS3_CMD_STAT_COUNT]; +}; + +enum ps3_cmd_stat_switch_flag { + PS3_STAT_ALL_SWITCH_CLOSE = 0x00, + PS3_STAT_OUTSTAND_SWITCH_OPEN = 0x01, + PS3_STAT_INC_SWITCH_OPEN = 0x02, + PS3_STAT_LOG_SWITCH_OPEN = 0x04, + PS3_STAT_DEV_SWITCH_OPEN = 0x08, + PS3_STAT_QOS_SWITCH_OPEN = 0x10, + PS3_STAT_ALL_SWITCH_OPEN = + (PS3_STAT_OUTSTAND_SWITCH_OPEN | PS3_STAT_INC_SWITCH_OPEN | + PS3_STAT_LOG_SWITCH_OPEN | PS3_STAT_DEV_SWITCH_OPEN | + PS3_STAT_QOS_SWITCH_OPEN), +}; + +struct ps3_cmd_stat_wrokq_context { +#ifndef _WINDOWS + struct delayed_work stat_work; + struct workqueue_struct *stat_queue; + struct ps3_instance *instance; +#else + struct ps3_delay_worker statis_work; +#endif + unsigned char is_stop; +}; + +struct ps3_cmd_statistics_context { + atomic_t cmd_outstanding; + atomic_t io_outstanding; + atomic_t vd_io_outstanding; + atomic_t cmd_delivering; + atomic_t scsi_cmd_delivering; + atomic64_t cmd_word_send_count; + struct ps3_total_cmd_stat total_stat; + struct ps3_total_cmd_stat inc_stat; + struct ps3_single_cmd_stat **cmd_stat_backup_buf; + struct ps3_single_cmd_stat **last_stat_buf; + struct ps3_single_cmd_stat **cmd_stat_buf; + unsigned int stat_entry_max_count; + unsigned int stat_interval; + struct ps3_cmd_stat_wrokq_context stat_workq; + unsigned char cmd_stat_switch; + unsigned char log_record_count; + atomic_t cli_cnt; + atomic_t cmd_qos_processing; + atomic64_t cmd_qos_total; + unsigned char reserved2[2]; +}; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/drivers/scsi/linkdata/ps3stor/ps3_cmd_statistics.c b/drivers/scsi/linkdata/ps3stor/ps3_cmd_statistics.c new file mode 100644 index 0000000000000000000000000000000000000000..b0e6d6b8a67689f5bd0237a2de5abd143d064d6b --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/ps3_cmd_statistics.c @@ -0,0 +1,1630 @@ +// SPDX-License-Identifier: GPL-2.0 +#ifndef _WINDOWS +#include +#endif + +#include "ps3_cmd_statistics.h" +#include "ps3_cmd_channel.h" +#include "ps3_driver_log.h" +#include "ps3_util.h" + +static int ps3_cmd_stat_buf_alloc(struct ps3_instance *instance); +static void ps3_cmd_stat_buf_free(struct ps3_instance *instance); +static inline void ps3_stat_data_collect(struct ps3_instance *instance); +static void ps3_cmd_stat_content_init(struct ps3_instance *instance); +static inline void ps3_stat_buf_init(struct ps3_instance *instance); +static void ps3_dev_io_recv_bytes_stat_inc(struct ps3_instance *ins, + const struct ps3_cmd *cmd); +static int ps3_cmd_stat_backup_buf_alloc(struct ps3_instance *instance); +static int ps3_last_stat_buf_alloc(struct ps3_instance *instance); +static void ps3_cmd_stat_backup_buf_free(struct ps3_instance *instance); +static void ps3_last_stat_buf_free(struct ps3_instance *instance); +static inline void ps3_cmd_stat_backup_buf_init(struct ps3_instance *instance); +static inline void ps3_last_stat_init(struct ps3_instance *instance); + +int ps3_cmd_statistics_init(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + + ps3_atomic_set(&instance->cmd_statistics.cmd_outstanding, + PS3_CMD_STAT_INIT_VALUE); + ps3_atomic_set(&instance->cmd_statistics.io_outstanding, + PS3_CMD_STAT_INIT_VALUE); + ps3_atomic_set(&instance->cmd_statistics.vd_io_outstanding, + PS3_CMD_STAT_INIT_VALUE); + ps3_atomic_set(&instance->cmd_statistics.cmd_delivering, + PS3_CMD_STAT_INIT_VALUE); + ps3_atomic_set(&instance->cmd_statistics.scsi_cmd_delivering, + PS3_CMD_STAT_INIT_VALUE); + ps3_atomic64_set(&instance->cmd_statistics.cmd_word_send_count, + PS3_CMD_STAT_INIT_VALUE); + ps3_atomic64_set(&instance->cmd_statistics.cmd_qos_total, + PS3_CMD_STAT_INIT_VALUE); + ps3_atomic_set(&instance->cmd_statistics.cmd_qos_processing, + PS3_CMD_STAT_INIT_VALUE); + + ret = ps3_cmd_stat_buf_alloc(instance); + if (ret != PS3_SUCCESS) + goto l_free_stat_buf; + + ret = ps3_cmd_stat_backup_buf_alloc(instance); + if (ret != PS3_SUCCESS) + goto l_free_stat_buf; + + ret = ps3_last_stat_buf_alloc(instance); + if (ret != PS3_SUCCESS) + goto l_free_stat_buf; + + ps3_cmd_stat_content_init(instance); + + ret = ps3_stat_workq_start(instance); + if (ret != PS3_SUCCESS) + goto l_stop_workq; + + goto l_out; + +l_stop_workq: + ps3_stat_workq_stop(instance); + +l_free_stat_buf: + ps3_cmd_stat_buf_free(instance); + ps3_cmd_stat_backup_buf_free(instance); + ps3_last_stat_buf_free(instance); +l_out: + return ret; +} + +void ps3_cmd_statistics_exit(struct ps3_instance *instance) +{ + ps3_stat_workq_stop(instance); + ps3_cmd_stat_buf_free(instance); + ps3_cmd_stat_backup_buf_free(instance); + ps3_last_stat_buf_free(instance); +} + +void ps3_cmd_stat_content_clear(struct ps3_instance *instance) +{ + struct ps3_cmd_statistics_context *ctx = &instance->cmd_statistics; + + memset(&ctx->total_stat, 0, sizeof(struct ps3_total_cmd_stat)); + memset(&ctx->inc_stat, 0, sizeof(struct ps3_total_cmd_stat)); + ps3_stat_buf_init(instance); + ps3_cmd_stat_backup_buf_init(instance); + ps3_last_stat_init(instance); +} + +static void ps3_cmd_stat_content_init(struct ps3_instance *instance) +{ + struct ps3_cmd_statistics_context *ctx = &instance->cmd_statistics; + + ps3_cmd_stat_content_clear(instance); + ctx->stat_interval = PS3_STAT_WORKQ_INTERVAL_DEFAULT; + ctx->log_record_count = PS3_STAT_LOG_COUNT; +#ifdef PS3_CFG_RELEASE + ctx->cmd_stat_switch = PS3_STAT_OUTSTAND_SWITCH_OPEN; +#else + ctx->cmd_stat_switch = PS3_STAT_OUTSTAND_SWITCH_OPEN | + PS3_STAT_INC_SWITCH_OPEN | + PS3_STAT_QOS_SWITCH_OPEN; +#endif +} + +static inline unsigned char ps3_cmd_is_vd(const struct ps3_cmd *cmd) +{ + return cmd->io_attr.dev_type == PS3_DEV_TYPE_VD; +} + +void ps3_dev_io_statis_init(struct ps3_dev_io_statis *statis) +{ + ps3_atomic64_set(&statis->read_send_cnt, PS3_CMD_STAT_INIT_VALUE); + ps3_atomic64_set(&statis->read_send_ok_cnt, PS3_CMD_STAT_INIT_VALUE); + ps3_atomic64_set(&statis->read_send_wait_cnt, PS3_CMD_STAT_INIT_VALUE); + ps3_atomic64_set(&statis->read_send_err_cnt, PS3_CMD_STAT_INIT_VALUE); + ps3_atomic64_set(&statis->read_recv_cnt, PS3_CMD_STAT_INIT_VALUE); + ps3_atomic64_set(&statis->read_recv_ok_cnt, PS3_CMD_STAT_INIT_VALUE); + ps3_atomic64_set(&statis->read_recv_err_cnt, PS3_CMD_STAT_INIT_VALUE); + ps3_atomic64_set(&statis->read_ok_bytes, PS3_CMD_STAT_INIT_VALUE); + ps3_atomic64_set(&statis->write_send_cnt, PS3_CMD_STAT_INIT_VALUE); + ps3_atomic64_set(&statis->write_send_ok_cnt, PS3_CMD_STAT_INIT_VALUE); + ps3_atomic64_set(&statis->write_send_wait_cnt, PS3_CMD_STAT_INIT_VALUE); + ps3_atomic64_set(&statis->write_send_err_cnt, PS3_CMD_STAT_INIT_VALUE); + ps3_atomic64_set(&statis->write_recv_cnt, PS3_CMD_STAT_INIT_VALUE); + ps3_atomic64_set(&statis->write_recv_ok_cnt, PS3_CMD_STAT_INIT_VALUE); + ps3_atomic64_set(&statis->write_recv_err_cnt, PS3_CMD_STAT_INIT_VALUE); + ps3_atomic64_set(&statis->write_ok_bytes, PS3_CMD_STAT_INIT_VALUE); + ps3_atomic64_set(&statis->qos_processing_cnt, PS3_CMD_STAT_INIT_VALUE); +} + +void ps3_io_statis_inc(struct scsi_device *sdev, enum ps3_dev_io_stat_type type) +{ + struct ps3_scsi_priv_data *data = NULL; + + data = PS3_SDEV_PRI_DATA(sdev); + + switch (type) { + case PS3_DEV_IO_STAT_TYPE_R_SEND: + ps3_atomic64_inc(&data->statis.read_send_cnt); + break; + case PS3_DEV_IO_STAT_TYPE_R_SEND_OK: + ps3_atomic64_inc(&data->statis.read_send_ok_cnt); + break; + case PS3_DEV_IO_STAT_TYPE_R_SEND_WAIT: + ps3_atomic64_inc(&data->statis.read_send_wait_cnt); + break; + case PS3_DEV_IO_STAT_TYPE_R_SEND_ERR: + ps3_atomic64_inc(&data->statis.read_send_err_cnt); + break; + case PS3_DEV_IO_STAT_TYPE_R_RECV: + ps3_atomic64_inc(&data->statis.read_recv_cnt); + break; + case PS3_DEV_IO_STAT_TYPE_R_RECV_OK: + ps3_atomic64_inc(&data->statis.read_recv_ok_cnt); + break; + case PS3_DEV_IO_STAT_TYPE_R_RECV_ERR: + ps3_atomic64_inc(&data->statis.read_recv_err_cnt); + break; + case PS3_DEV_IO_STAT_TYPE_W_SEND: + ps3_atomic64_inc(&data->statis.write_send_cnt); + break; + case PS3_DEV_IO_STAT_TYPE_W_SEND_OK: + ps3_atomic64_inc(&data->statis.write_send_ok_cnt); + break; + case PS3_DEV_IO_STAT_TYPE_W_SEND_WAIT: + ps3_atomic64_inc(&data->statis.write_send_wait_cnt); + break; + case PS3_DEV_IO_STAT_TYPE_W_SEND_ERR: + ps3_atomic64_inc(&data->statis.write_send_err_cnt); + break; + case PS3_DEV_IO_STAT_TYPE_W_RECV: + ps3_atomic64_inc(&data->statis.write_recv_cnt); + break; + case PS3_DEV_IO_STAT_TYPE_W_RECV_OK: + ps3_atomic64_inc(&data->statis.write_recv_ok_cnt); + break; + case PS3_DEV_IO_STAT_TYPE_W_RECV_ERR: + ps3_atomic64_inc(&data->statis.write_recv_err_cnt); + break; + default: + LOG_INFO("unknown io statis type %d", type); + break; + } +} + +static inline void ps3_io_statis_add(struct scsi_device *sdev, + enum ps3_dev_io_stat_type type, + unsigned long long offset) +{ + struct ps3_scsi_priv_data *data = NULL; + + data = PS3_SDEV_PRI_DATA(sdev); + + switch (type) { + case PS3_DEV_IO_STAT_TYPE_R_OK_BYTES: + ps3_atomic64_add(offset, &data->statis.read_ok_bytes); + break; + case PS3_DEV_IO_STAT_TYPE_W_OK_BYTES: + ps3_atomic64_add(offset, &data->statis.write_ok_bytes); + break; + default: + LOG_INFO("unknown io statis type %d", type); + break; + } +} + +void ps3_io_statis_dec(struct scsi_device *sdev, enum ps3_dev_io_stat_type type) +{ + struct ps3_scsi_priv_data *data = NULL; + + data = PS3_SDEV_PRI_DATA(sdev); + + switch (type) { + case PS3_DEV_IO_STAT_TYPE_R_SEND_WAIT: + ps3_atomic64_dec(&data->statis.read_send_wait_cnt); + break; + case PS3_DEV_IO_STAT_TYPE_W_SEND_WAIT: + ps3_atomic64_dec(&data->statis.write_send_wait_cnt); + break; + default: + LOG_INFO("io statis type %d not support to dec", type); + break; + } +} + +void ps3_io_statis_clear(struct scsi_device *sdev) +{ + struct ps3_scsi_priv_data *data = NULL; + struct ps3_instance *instance = + (struct ps3_instance *)sdev->host->hostdata; + + ps3_mutex_lock(&instance->dev_context.dev_priv_lock); + data = PS3_SDEV_PRI_DATA(sdev); + if (data != NULL) + ps3_dev_io_statis_init(&data->statis); + ps3_mutex_unlock(&instance->dev_context.dev_priv_lock); +} + +void ps3_dev_io_start_inc(struct ps3_instance *instance, + const struct ps3_cmd *cmd) +{ + if (instance == NULL || cmd == NULL) + goto l_out; + + if (ps3_scsih_is_read_cmd(cmd->io_attr.rw_flag)) { + ps3_io_statis_inc(cmd->scmd->device, + PS3_DEV_IO_STAT_TYPE_R_SEND); + } else if (ps3_scsih_is_write_cmd(cmd->io_attr.rw_flag)) { + ps3_io_statis_inc(cmd->scmd->device, + PS3_DEV_IO_STAT_TYPE_W_SEND); + } + +l_out: + return; +} + +void ps3_dev_io_start_err_inc(struct ps3_instance *instance, + const struct ps3_cmd *cmd) +{ + if (instance == NULL || cmd == NULL) + goto l_out; + + if (ps3_scsih_is_read_cmd(cmd->io_attr.rw_flag)) { + ps3_io_statis_inc(cmd->scmd->device, + PS3_DEV_IO_STAT_TYPE_R_SEND_ERR); + } else if (ps3_scsih_is_write_cmd(cmd->io_attr.rw_flag)) { + ps3_io_statis_inc(cmd->scmd->device, + PS3_DEV_IO_STAT_TYPE_W_SEND_ERR); + } + +l_out: + return; +} + +void ps3_dev_io_start_ok_inc(struct ps3_instance *instance, + const struct ps3_cmd *cmd) +{ + if (instance == NULL || cmd == NULL) + goto l_out; + + if (ps3_scsih_is_read_cmd(cmd->io_attr.rw_flag)) { + ps3_io_statis_inc(cmd->scmd->device, + PS3_DEV_IO_STAT_TYPE_R_SEND_OK); + } else if (ps3_scsih_is_write_cmd(cmd->io_attr.rw_flag)) { + ps3_io_statis_inc(cmd->scmd->device, + PS3_DEV_IO_STAT_TYPE_W_SEND_OK); + } + +l_out: + return; +} + +void ps3_dev_io_outstand_dec(const struct ps3_cmd *cmd) +{ + if (ps3_scsih_is_read_cmd(cmd->io_attr.rw_flag)) { + ps3_io_statis_dec(cmd->scmd->device, + PS3_DEV_IO_STAT_TYPE_R_SEND_WAIT); + } else if (ps3_scsih_is_write_cmd(cmd->io_attr.rw_flag)) { + ps3_io_statis_dec(cmd->scmd->device, + PS3_DEV_IO_STAT_TYPE_W_SEND_WAIT); + } +} + +void ps3_dev_io_qos_inc(struct ps3_scsi_priv_data *priv_data) +{ + ps3_atomic64_inc(&priv_data->statis.qos_processing_cnt); +} + +void ps3_dev_io_qos_dec(struct ps3_scsi_priv_data *priv_data) +{ + ps3_atomic64_dec(&priv_data->statis.qos_processing_cnt); +} + +void ps3_io_outstand_dec(struct ps3_instance *instance, + const struct scsi_cmnd *s_cmd) +{ + if (instance == NULL || s_cmd == NULL) + goto l_out; + + ps3_atomic_dec(&instance->cmd_statistics.io_outstanding); + + if ((instance->cmd_attr.vd_io_threshold != 0) && + ps3_is_vd_rw_cmd(s_cmd)) { + ps3_atomic_dec(&instance->cmd_statistics.vd_io_outstanding); + } + +l_out: + return; +} + +void ps3_vd_outstand_dec(struct ps3_instance *instance, + const struct scsi_cmnd *s_cmd) +{ + struct ps3_scsi_priv_data *data = PS3_SDEV_PRI_DATA(s_cmd->device); + struct PS3VDEntry *vd_entry = NULL; + + if (data->dev_type == PS3_DEV_TYPE_VD) { + vd_entry = ps3_dev_mgr_lookup_vd_info( + instance, s_cmd->device->channel, s_cmd->device->id); + if (unlikely(vd_entry != NULL)) { + if (ps3_is_read_cmd(s_cmd)) { + atomic_dec(&data->rd_io_outstand); + } else if (ps3_is_write_cmd(s_cmd)) { + if (!vd_entry->isNvme && !vd_entry->isSsd) + atomic_dec(&data->wr_io_outstand); + } + } + } +} + +void ps3_dev_io_outstand_inc(const struct ps3_cmd *cmd) +{ + if (ps3_scsih_is_read_cmd(cmd->io_attr.rw_flag)) { + ps3_io_statis_inc(cmd->scmd->device, + PS3_DEV_IO_STAT_TYPE_R_SEND_WAIT); + } else if (ps3_scsih_is_write_cmd(cmd->io_attr.rw_flag)) { + ps3_io_statis_inc(cmd->scmd->device, + PS3_DEV_IO_STAT_TYPE_W_SEND_WAIT); + } +} + +void ps3_vd_outstand_inc(struct ps3_instance *instance, + const struct scsi_cmnd *s_cmd) +{ + struct ps3_scsi_priv_data *data = PS3_SDEV_PRI_DATA(s_cmd->device); + struct PS3VDEntry *vd_entry = NULL; + + if (data->dev_type == PS3_DEV_TYPE_VD) { + vd_entry = ps3_dev_mgr_lookup_vd_info( + instance, s_cmd->device->channel, s_cmd->device->id); + if (unlikely(vd_entry != NULL)) { + if (ps3_is_read_cmd(s_cmd)) { + atomic_inc(&data->rd_io_outstand); + } else if (ps3_is_write_cmd(s_cmd)) { + if (!vd_entry->isNvme && !vd_entry->isSsd) + atomic_inc(&data->wr_io_outstand); + } + } + } +} + +void ps3_io_outstand_inc(struct ps3_instance *instance, + const struct ps3_cmd *cmd) +{ + if (ps3_stat_outstand_switch_is_open(instance)) { + ps3_atomic_inc(&instance->cmd_statistics.io_outstanding); + + if (ps3_is_vd_rw_cmd(cmd->scmd) && + (instance->cmd_attr.vd_io_threshold != 0)) { + ps3_atomic_inc( + &instance->cmd_statistics.vd_io_outstanding); + } + } +} + +void ps3_io_recv_ok_stat_inc(struct ps3_instance *ins, + const struct ps3_cmd *cmd) +{ + if (ins == NULL || cmd == NULL) + goto l_out; + + if (ps3_scsih_is_read_cmd(cmd->io_attr.rw_flag)) { + ps3_io_statis_inc(cmd->scmd->device, + PS3_DEV_IO_STAT_TYPE_R_RECV_OK); + } else if (ps3_scsih_is_write_cmd(cmd->io_attr.rw_flag)) { + ps3_io_statis_inc(cmd->scmd->device, + PS3_DEV_IO_STAT_TYPE_W_RECV_OK); + } + +l_out: + return; +} + +void ps3_dev_io_back_inc(struct ps3_instance *ins, const struct ps3_cmd *cmd, + unsigned char status) +{ + if (ins == NULL || cmd == NULL) + goto l_out; + + if (ps3_scsih_is_read_cmd(cmd->io_attr.rw_flag)) { + ps3_io_statis_inc(cmd->scmd->device, + PS3_DEV_IO_STAT_TYPE_R_RECV); + } else if (ps3_scsih_is_write_cmd(cmd->io_attr.rw_flag)) { + ps3_io_statis_inc(cmd->scmd->device, + PS3_DEV_IO_STAT_TYPE_W_RECV); + } + + if (status == PS3_REPLY_WORD_FLAG_SUCCESS) { + ps3_io_recv_ok_stat_inc(ins, cmd); + ps3_dev_io_recv_bytes_stat_inc(ins, cmd); + } + +l_out: + return; +} + +static void ps3_dev_io_recv_bytes_stat_inc(struct ps3_instance *ins, + const struct ps3_cmd *cmd) +{ + if (ins == NULL || cmd == NULL || cmd->scmd == NULL) + goto l_out; + + if (ps3_scsih_is_read_cmd(cmd->io_attr.rw_flag)) { + ps3_io_statis_add(cmd->scmd->device, + PS3_DEV_IO_STAT_TYPE_R_OK_BYTES, + scsi_bufflen(cmd->scmd)); + } else if (ps3_scsih_is_write_cmd(cmd->io_attr.rw_flag)) { + ps3_io_statis_add(cmd->scmd->device, + PS3_DEV_IO_STAT_TYPE_W_OK_BYTES, + scsi_bufflen(cmd->scmd)); + } + +l_out: + return; +} + +void ps3_qos_cmd_inc(struct ps3_instance *instance) +{ + ps3_atomic64_inc(&instance->cmd_statistics.cmd_qos_total); +} +static void ps3_cmd_stat_buf_free(struct ps3_instance *instance) +{ + unsigned int i = 0; + struct ps3_cmd_statistics_context *ctx = &instance->cmd_statistics; + + if (ctx->cmd_stat_buf == NULL) + goto l_out; + + for (; i < ctx->stat_entry_max_count; ++i) { + if (ctx->cmd_stat_buf[i] == NULL) + continue; + ps3_kfree(instance, ctx->cmd_stat_buf[i]); + ctx->cmd_stat_buf[i] = NULL; + } + + ps3_kfree(instance, ctx->cmd_stat_buf); + ctx->cmd_stat_buf = NULL; +l_out: + return; +} + +static int ps3_cmd_stat_buf_alloc(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + unsigned int i = 0; + struct ps3_cmd_statistics_context *ctx = &instance->cmd_statistics; + + ctx->stat_entry_max_count = instance->cmd_context.max_cmd_count; + ctx->cmd_stat_buf = (struct ps3_single_cmd_stat **)ps3_kzalloc( + instance, ctx->stat_entry_max_count * + sizeof(struct ps3_single_cmd_stat *)); + if (ctx->cmd_stat_buf == NULL) { + LOG_ERROR("failed to kcalloc for cmd_stat_buf\n"); + ret = -PS3_FAILED; + goto l_out; + } + + for (; i < ctx->stat_entry_max_count; ++i) { + ctx->cmd_stat_buf[i] = + (struct ps3_single_cmd_stat *)ps3_kzalloc( + instance, sizeof(struct ps3_single_cmd_stat)); + if (ctx->cmd_stat_buf[i] == NULL) { + LOG_ERROR( + "Failed to alloc mem for ps3_single_cmd_stat\n"); + ret = -PS3_FAILED; + goto l_free_mem; + } + } + + goto l_out; + +l_free_mem: + ps3_cmd_stat_buf_free(instance); +l_out: + return ret; +} + +static void ps3_last_stat_buf_free(struct ps3_instance *instance) +{ + unsigned int i = 0; + struct ps3_cmd_statistics_context *ctx = &instance->cmd_statistics; + + if (ctx->last_stat_buf == NULL) + goto l_out; + + for (; i < ctx->stat_entry_max_count; ++i) { + if (ctx->last_stat_buf[i] == NULL) + continue; + ps3_kfree(instance, ctx->last_stat_buf[i]); + ctx->last_stat_buf[i] = NULL; + } + + ps3_kfree(instance, ctx->last_stat_buf); + ctx->last_stat_buf = NULL; +l_out: + return; +} + +static int ps3_last_stat_buf_alloc(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + unsigned int i = 0; + struct ps3_cmd_statistics_context *ctx = &instance->cmd_statistics; + + ctx->stat_entry_max_count = instance->cmd_context.max_cmd_count; + ctx->last_stat_buf = (struct ps3_single_cmd_stat **)ps3_kzalloc( + instance, ctx->stat_entry_max_count * + sizeof(struct ps3_single_cmd_stat *)); + if (ctx->last_stat_buf == NULL) { + LOG_ERROR("failed to kzalloc for last_stat_buf\n"); + ret = -PS3_FAILED; + goto l_out; + } + + for (; i < ctx->stat_entry_max_count; ++i) { + ctx->last_stat_buf[i] = + (struct ps3_single_cmd_stat *)ps3_kzalloc( + instance, sizeof(struct ps3_single_cmd_stat)); + if (ctx->last_stat_buf[i] == NULL) { + LOG_ERROR( + "Failed to alloc mem for ps3_single_cmd_stat\n"); + ret = -PS3_FAILED; + goto l_free_mem; + } + } + + goto l_out; + +l_free_mem: + ps3_last_stat_buf_free(instance); +l_out: + return ret; +} + +static inline struct ps3_single_cmd_stat * +ps3_last_stat_entry_find(unsigned int index, struct ps3_instance *instance) +{ + return (index < instance->cmd_statistics.stat_entry_max_count) ? + instance->cmd_statistics.last_stat_buf[index] : + NULL; +} + +static inline void ps3_last_stat_init(struct ps3_instance *instance) +{ + unsigned int i = 0; + struct ps3_cmd_statistics_context *ctx = &instance->cmd_statistics; + + for (; i < ctx->stat_entry_max_count; ++i) { + memset(ctx->last_stat_buf[i], 0, + sizeof(struct ps3_single_cmd_stat)); + mb(); /* in order to force CPU ordering */ + } +} +static void ps3_cmd_stat_backup_buf_free(struct ps3_instance *instance) +{ + unsigned int i = 0; + struct ps3_cmd_statistics_context *ctx = &instance->cmd_statistics; + + if (ctx->cmd_stat_backup_buf == NULL) + goto l_out; + + for (; i < ctx->stat_entry_max_count; ++i) { + if (ctx->cmd_stat_backup_buf[i] == NULL) + continue; + ps3_kfree(instance, ctx->cmd_stat_backup_buf[i]); + ctx->cmd_stat_backup_buf[i] = NULL; + } + + ps3_kfree(instance, ctx->cmd_stat_backup_buf); + ctx->cmd_stat_backup_buf = NULL; +l_out: + return; +} + +static int ps3_cmd_stat_backup_buf_alloc(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + unsigned int i = 0; + struct ps3_cmd_statistics_context *ctx = &instance->cmd_statistics; + + ctx->stat_entry_max_count = instance->cmd_context.max_cmd_count; + ctx->cmd_stat_backup_buf = (struct ps3_single_cmd_stat **)ps3_kzalloc( + instance, ctx->stat_entry_max_count * + sizeof(struct ps3_single_cmd_stat *)); + if (ctx->cmd_stat_backup_buf == NULL) { + LOG_ERROR("failed to kzalloc for cmd_stat_backup_buf\n"); + ret = -PS3_FAILED; + goto l_out; + } + + for (; i < ctx->stat_entry_max_count; ++i) { + ctx->cmd_stat_backup_buf[i] = + (struct ps3_single_cmd_stat *)ps3_kzalloc( + instance, sizeof(struct ps3_single_cmd_stat)); + if (ctx->cmd_stat_backup_buf[i] == NULL) { + LOG_ERROR( + "Failed to alloc mem for ps3_single_cmd_stat\n"); + ret = -PS3_FAILED; + goto l_free_mem; + } + } + + goto l_out; + +l_free_mem: + ps3_cmd_stat_backup_buf_free(instance); +l_out: + return ret; +} + +static inline struct ps3_single_cmd_stat * +ps3_backup_stat_entry_find(unsigned int index, struct ps3_instance *instance) +{ + return (index < instance->cmd_statistics.stat_entry_max_count) ? + instance->cmd_statistics.cmd_stat_backup_buf[index] : + NULL; +} + +static inline void ps3_cmd_stat_backup_buf_init(struct ps3_instance *instance) +{ + unsigned int i = 0; + struct ps3_cmd_statistics_context *ctx = &instance->cmd_statistics; + + for (; i < ctx->stat_entry_max_count; ++i) { + memset(ctx->cmd_stat_backup_buf[i], 0, + sizeof(struct ps3_single_cmd_stat)); + mb(); /* in order to force CPU ordering */ + } +} +static inline struct ps3_single_cmd_stat * +ps3_stat_entry_find(unsigned int index, struct ps3_instance *instance) +{ + return (index < instance->cmd_statistics.stat_entry_max_count) ? + instance->cmd_statistics.cmd_stat_buf[index] : + NULL; +} + +static inline void ps3_stat_buf_init(struct ps3_instance *instance) +{ + unsigned int i = 0; + struct ps3_cmd_statistics_context *ctx = &instance->cmd_statistics; + + for (; i < ctx->stat_entry_max_count; ++i) { + memset(ctx->cmd_stat_buf[i], 0, + sizeof(struct ps3_single_cmd_stat)); + mb(); /* in order to force CPU ordering */ + } +} + +void ps3_stat_all_clear(struct ps3_instance *instance) +{ + ps3_stat_buf_init(instance); + ps3_cmd_stat_backup_buf_init(instance); + ps3_last_stat_init(instance); + memset(&instance->cmd_statistics.total_stat, 0, + sizeof(struct ps3_total_cmd_stat)); + memset(&instance->cmd_statistics.inc_stat, 0, + sizeof(struct ps3_total_cmd_stat)); +} + +static inline void ps3_stat_start_inc(unsigned short type, + struct ps3_single_cmd_stat *cmd_stat) +{ + if (type >= PS3_CMD_STAT_COUNT) { + LOG_ERROR("type:%u overflow\n", type); + goto l_out; + } + + cmd_stat->stat[type].start++; + + cmd_stat->stat[type].lagency.start_time = ps3_tick_count_get(); + +l_out: + return; +} + +static inline void ps3_stat_lagency_update(struct ps3_lagency_info *lagency, + unsigned long long start_time, + unsigned long long back_time) +{ + unsigned long long intervals = back_time - start_time; + + lagency->all += intervals; + if (intervals > lagency->max_lagency) + lagency->max_lagency = intervals; + + if (intervals < lagency->min_lagency || lagency->min_lagency == 0) + lagency->min_lagency = intervals; +} + +static inline void ps3_stat_lagency_merge(struct ps3_lagency_info *target, + const struct ps3_lagency_info *source) +{ + target->all += source->all; + if (source->max_lagency > target->max_lagency) + target->max_lagency = source->max_lagency; + + if ((source->min_lagency < target->min_lagency && + source->min_lagency != 0) || + target->min_lagency == 0) { + target->min_lagency = source->min_lagency; + } +} + +static inline void +ps3_stat_lagency_avg_calc(struct ps3_total_cmd_stat *total_stat) +{ + unsigned long long back_count = 0; + unsigned short i = 0; + + for (; i < PS3_CMD_STAT_COUNT; ++i) { + back_count = total_stat->stat[i].back_good + + total_stat->stat[i].back_err; + if (back_count != 0) { + total_stat->stat[i].lagency.avg = PS3_DIV64_32( + total_stat->stat[i].lagency.all, back_count); + } + mb(); /* in order to force CPU ordering */ + } +} + +static inline void ps3_stat_lagency_inc_cal(struct ps3_lagency_info *inc, + const struct ps3_lagency_info *cur, + const struct ps3_lagency_info *last) +{ + inc->all += cur->all - last->all; + inc->max_lagency = (cur->max_lagency > inc->max_lagency) ? + cur->max_lagency : + inc->max_lagency; + inc->min_lagency = ((cur->min_lagency < inc->min_lagency && + cur->min_lagency != 0) || + inc->min_lagency == 0) ? + cur->min_lagency : + inc->min_lagency; +} + +static inline void ps3_stat_back_inc(unsigned short type, + struct ps3_single_cmd_stat *single_stat, + unsigned char status) +{ + if (type >= PS3_CMD_STAT_COUNT) { + LOG_ERROR("type:%u err\n", type); + goto l_out; + } + + single_stat->stat[type].lagency.back_time = ps3_tick_count_get(); + + switch (status) { + case PS3_STAT_BACK_OK: + single_stat->stat[type].back_good++; + break; + case PS3_STAT_BACK_FAIL: + single_stat->stat[type].back_err++; + break; + case PS3_STAT_BACK_NO: + single_stat->stat[type].not_back++; + break; + default: + single_stat->stat[type].back_err++; + break; + } + + ps3_stat_lagency_update(&single_stat->stat[type].lagency, + single_stat->stat[type].lagency.start_time, + single_stat->stat[type].lagency.back_time); + if (type == PS3_SCSI_ABORT) { + LOG_WARN("start:%llu back:%llu all:%llu\n", + single_stat->stat[type].lagency.start_time, + single_stat->stat[type].lagency.back_time, + single_stat->stat[type].lagency.all); + } + +l_out: + return; +} + +#ifndef _WINDOWS +static inline void ps3_scmd_start_inc(struct ps3_single_cmd_stat *stat_entry, + const struct scsi_cmnd *s_cmd) +{ + unsigned char type = + PS3_SCSI_CMD_TYPE(ps3_scsih_cdb_rw_type_get(s_cmd->cmnd)); + + if (ps3_scsih_is_rw_type(type)) { + if (ps3_scsih_is_read_cmd(type)) + ps3_stat_start_inc(PS3_SCSI_DRV_READ, stat_entry); + else + ps3_stat_start_inc(PS3_SCSI_DRV_WRITE, stat_entry); + } else { + ps3_stat_start_inc(PS3_SCSI_DRV_NORW, stat_entry); + } + + ps3_stat_start_inc(PS3_SCSI_DRV_ALL, stat_entry); + + if (s_cmd->retries != 0) + ps3_stat_start_inc(PS3_SCSI_RETRY_CMD, stat_entry); +} + +static inline void ps3_scmd_back_inc(struct ps3_single_cmd_stat *stat_entry, + const struct scsi_cmnd *s_cmd, + unsigned char status) +{ + unsigned char type = + PS3_SCSI_CMD_TYPE(ps3_scsih_cdb_rw_type_get(s_cmd->cmnd)); + + if (ps3_scsih_is_rw_type(type)) { + if (ps3_scsih_is_read_cmd(type)) { + ps3_stat_back_inc(PS3_SCSI_DRV_READ, stat_entry, + status); + } else { + ps3_stat_back_inc(PS3_SCSI_DRV_WRITE, stat_entry, + status); + } + } else { + ps3_stat_back_inc(PS3_SCSI_DRV_NORW, stat_entry, status); + } + + ps3_stat_back_inc(PS3_SCSI_DRV_ALL, stat_entry, status); + + if (s_cmd->retries != 0) + ps3_stat_back_inc(PS3_SCSI_RETRY_CMD, stat_entry, status); +} + +void ps3_scmd_inc(struct ps3_instance *instance, struct scsi_cmnd *s_cmd, + unsigned char type, unsigned char status) +{ + struct ps3_single_cmd_stat *stat_entry = NULL; + + if (s_cmd == NULL) { + LOG_ERROR("scsi cmd is null\n"); + goto l_out; + } + + if (SCMD_GET_REQUEST(s_cmd) == NULL) { + LOG_ERROR("scsi_cmnd->request is null\n"); + goto l_out; + } + + stat_entry = + ps3_stat_entry_find(SCMD_GET_REQUEST(s_cmd)->tag, instance); + if (stat_entry == NULL) { + LOG_ERROR("host_no:%u CFID:%u stat entry find fail\n", + PS3_HOST(instance), SCMD_GET_REQUEST(s_cmd)->tag); + goto l_out; + } + + if (type == PS3_STAT_START) + ps3_scmd_start_inc(stat_entry, s_cmd); + else if (type == PS3_STAT_BACK) + ps3_scmd_back_inc(stat_entry, s_cmd, status); + else + LOG_ERROR("type:%u is err\n", type); + +l_out: + return; +} +#else +static inline void ps3_scmd_start_inc(struct ps3_single_cmd_stat *stat_entry, + const struct scsi_cmnd *s_cmd) +{ + unsigned char type = + PS3_SCSI_CMD_TYPE(ps3_scsih_cdb_rw_type_get(s_cmd->cmnd)); + + if (ps3_scsih_is_rw_type(type)) { + if (ps3_scsih_is_read_cmd(type)) + ps3_stat_start_inc(PS3_SCSI_DRV_READ, stat_entry); + else + ps3_stat_start_inc(PS3_SCSI_DRV_WRITE, stat_entry); + } else + ps3_stat_start_inc(PS3_SCSI_DRV_NORW, stat_entry); + + ps3_stat_start_inc(PS3_SCSI_DRV_ALL, stat_entry); +} + +static inline void ps3_scmd_back_inc(struct ps3_single_cmd_stat *stat_entry, + const struct scsi_cmnd *s_cmd, + unsigned char status) +{ + unsigned char type = PS3_SCSI_CMD_TYPE( + ps3_scsih_cdb_rw_type_get(scsi_cmnd_cdb(s_cmd))); + + if (ps3_scsih_is_rw_type(type)) { + if (ps3_scsih_is_read_cmd(type)) { + ps3_stat_back_inc(PS3_SCSI_DRV_READ, stat_entry, + status); + } else { + ps3_stat_back_inc(PS3_SCSI_DRV_WRITE, stat_entry, + status); + } + } else { + ps3_stat_back_inc(PS3_SCSI_DRV_NORW, stat_entry, status); + } + + ps3_stat_back_inc(PS3_SCSI_DRV_ALL, stat_entry, status); +} + +void ps3_scmd_inc(struct ps3_instance *instance, const struct scsi_cmnd *s_cmd, + unsigned int tag, unsigned char type, unsigned char status) +{ + struct ps3_single_cmd_stat *stat_entry = NULL; + + if (s_cmd == NULL) { + LOG_ERROR("scsi cmd is null\n"); + goto l_out; + } + + stat_entry = ps3_stat_entry_find(tag, instance); + if (stat_entry == NULL) { + LOG_ERROR("host_no:%u CFID:%u stat entry find fail\n", + PS3_HOST(instance), tag); + goto l_out; + } + + if (type == PS3_STAT_START) + ps3_scmd_start_inc(stat_entry, s_cmd); + else if (type == PS3_STAT_BACK) + ps3_scmd_back_inc(stat_entry, s_cmd, status); + else + LOG_ERROR("type:%u is err\n", type); + +l_out: + return; +} +#endif +static inline unsigned char ps3_is_direct_cmd(unsigned char type) +{ + return (type == PS3_CMDWORD_DIRECT_OK || + type == PS3_CMDWORD_DIRECT_ADVICE); +} + +static inline void +ps3_direct_cmd_start_inc(const struct ps3_cmd *cmd, + struct ps3_single_cmd_stat *stat_entry, + unsigned char vd_flag) +{ + unsigned short type = PS3_DRV_IOC_READ; + + if (ps3_is_direct_cmd(cmd->io_attr.direct_flag)) { + if (ps3_scsih_is_read_cmd(cmd->io_attr.rw_flag)) { + type = vd_flag ? PS3_DRV_IOC_VD_D_READ : + PS3_DRV_IOC_PD_D_READ; + } else { + type = vd_flag ? PS3_DRV_IOC_VD_D_WRITE : + PS3_DRV_IOC_PD_D_WRITE; + } + ps3_stat_start_inc(type, stat_entry); + } +} + +static inline void +ps3_scmd_drv2ioc_start_vd_inc(const struct ps3_cmd *cmd, + struct ps3_single_cmd_stat *stat_entry) +{ + if (ps3_scsih_is_rw_type(cmd->io_attr.rw_flag)) { + if (ps3_scsih_is_read_cmd(cmd->io_attr.rw_flag)) { + ps3_stat_start_inc(PS3_DRV_IOC_VD_READ, stat_entry); + ps3_stat_start_inc(PS3_DRV_IOC_READ, stat_entry); + } else { + ps3_stat_start_inc(PS3_DRV_IOC_VD_WRITE, stat_entry); + ps3_stat_start_inc(PS3_DRV_IOC_WRITE, stat_entry); + } + } else { + ps3_stat_start_inc(PS3_DRV_IOC_NORW, stat_entry); + ps3_stat_start_inc(PS3_DRV_IOC_VD_NORW, stat_entry); + } + + ps3_direct_cmd_start_inc(cmd, stat_entry, PS3_TRUE); +} + +static inline void +ps3_scmd_drv2ioc_start_pd_inc(const struct ps3_cmd *cmd, + struct ps3_single_cmd_stat *stat_entry) +{ + if (ps3_scsih_is_rw_type(cmd->io_attr.rw_flag)) { + if (ps3_scsih_is_read_cmd(cmd->io_attr.rw_flag)) { + ps3_stat_start_inc(PS3_DRV_IOC_PD_READ, stat_entry); + ps3_stat_start_inc(PS3_DRV_IOC_READ, stat_entry); + } else { + ps3_stat_start_inc(PS3_DRV_IOC_PD_WRITE, stat_entry); + ps3_stat_start_inc(PS3_DRV_IOC_WRITE, stat_entry); + } + } else { + ps3_stat_start_inc(PS3_DRV_IOC_PD_NORW, stat_entry); + ps3_stat_start_inc(PS3_DRV_IOC_NORW, stat_entry); + } + + ps3_direct_cmd_start_inc(cmd, stat_entry, PS3_FALSE); +} + +void ps3_scmd_drv2ioc_start_inc(struct ps3_instance *instance, + const struct ps3_cmd *cmd) +{ + struct ps3_single_cmd_stat *stat_entry = NULL; + + if (cmd == NULL) + goto l_out; + + stat_entry = ps3_stat_entry_find(cmd->index, instance); + if (stat_entry == NULL) { + LOG_ERROR("host_no:%u index:%u stat entry find fail\n", + PS3_HOST(instance), cmd->index); + goto l_out; + } + + if (cmd->io_attr.dev_type == PS3_DEV_TYPE_VD) + ps3_scmd_drv2ioc_start_vd_inc(cmd, stat_entry); + else + ps3_scmd_drv2ioc_start_pd_inc(cmd, stat_entry); + + ps3_stat_start_inc(PS3_DRV_IOC_ALL, stat_entry); + ps3_atomic_inc(&instance->cmd_statistics.cmd_outstanding); + +l_out: + return; +} + +static inline void +ps3_direct_cmd_back_inc(const struct ps3_cmd *cmd, + struct ps3_single_cmd_stat *stat_entry, + unsigned char vd_flag, unsigned char status) +{ + unsigned short type = PS3_DRV_IOC_READ; + + if (ps3_is_direct_cmd(cmd->io_attr.direct_flag)) { + if (ps3_scsih_is_read_cmd(cmd->io_attr.rw_flag)) { + type = vd_flag ? PS3_DRV_IOC_VD_D_READ : + PS3_DRV_IOC_PD_D_READ; + } else { + type = vd_flag ? PS3_DRV_IOC_VD_D_WRITE : + PS3_DRV_IOC_PD_D_WRITE; + } + ps3_stat_back_inc(type, stat_entry, status); + } +} + +static inline void +ps3_scmd_drv2ioc_back_vd_inc(const struct ps3_cmd *cmd, + struct ps3_single_cmd_stat *stat_entry, + unsigned char status) +{ + if (ps3_scsih_is_rw_type(cmd->io_attr.rw_flag)) { + if (ps3_scsih_is_read_cmd(cmd->io_attr.rw_flag)) { + ps3_stat_back_inc(PS3_DRV_IOC_VD_READ, stat_entry, + status); + ps3_stat_back_inc(PS3_DRV_IOC_READ, stat_entry, status); + } else { + ps3_stat_back_inc(PS3_DRV_IOC_VD_WRITE, stat_entry, + status); + ps3_stat_back_inc(PS3_DRV_IOC_WRITE, stat_entry, + status); + } + } else { + ps3_stat_back_inc(PS3_DRV_IOC_NORW, stat_entry, status); + ps3_stat_back_inc(PS3_DRV_IOC_VD_NORW, stat_entry, status); + } + + ps3_direct_cmd_back_inc(cmd, stat_entry, PS3_TRUE, status); +} + +static inline void +ps3_scmd_drv2ioc_back_pd_inc(const struct ps3_cmd *cmd, + struct ps3_single_cmd_stat *stat_entry, + unsigned char status) +{ + if (ps3_scsih_is_rw_type(cmd->io_attr.rw_flag)) { + if (ps3_scsih_is_read_cmd(cmd->io_attr.rw_flag)) { + ps3_stat_back_inc(PS3_DRV_IOC_PD_READ, stat_entry, + status); + ps3_stat_back_inc(PS3_DRV_IOC_READ, stat_entry, status); + } else { + ps3_stat_back_inc(PS3_DRV_IOC_PD_WRITE, stat_entry, + status); + ps3_stat_back_inc(PS3_DRV_IOC_WRITE, stat_entry, + status); + } + } else { + ps3_stat_back_inc(PS3_DRV_IOC_PD_NORW, stat_entry, status); + ps3_stat_back_inc(PS3_DRV_IOC_NORW, stat_entry, status); + } + + ps3_direct_cmd_back_inc(cmd, stat_entry, PS3_FALSE, status); +} + +void ps3_scmd_drv2ioc_back_inc(struct ps3_instance *instance, + const struct ps3_cmd *cmd, unsigned char status) +{ + struct ps3_single_cmd_stat *stat_entry = NULL; + + if (cmd == NULL) { + LOG_ERROR("host_no:%u cmd is null\n", PS3_HOST(instance)); + goto l_out; + } + + stat_entry = ps3_stat_entry_find(cmd->index, instance); + if (stat_entry == NULL) { + LOG_ERROR("host_no:%u index:%u stat entry find fail\n", + PS3_HOST(instance), cmd->index); + goto l_out; + } + + if (cmd->io_attr.dev_type == PS3_DEV_TYPE_VD) + ps3_scmd_drv2ioc_back_vd_inc(cmd, stat_entry, status); + else + ps3_scmd_drv2ioc_back_pd_inc(cmd, stat_entry, status); + + ps3_stat_back_inc(PS3_DRV_IOC_ALL, stat_entry, status); + ps3_atomic_dec(&instance->cmd_statistics.cmd_outstanding); + +l_out: + return; +} + +static inline void +ps3_task_cmd_start_inc(struct ps3_single_cmd_stat *stat_entry, + const struct ps3_cmd *cmd) +{ + switch (cmd->req_frame->mgrReq.reqHead.cmdSubType) { + case PS3_TASK_CMD_SCSI_TASK_ABORT: + ps3_stat_start_inc(PS3_SCSI_ABORT, stat_entry); + break; + + case PS3_TASK_CMD_SCSI_TASK_RESET: + ps3_stat_start_inc(PS3_SCSI_DEVICE_RESET, stat_entry); + break; + + default: + break; + } +} + +void ps3_mgr_start_inc(struct ps3_instance *instance, const struct ps3_cmd *cmd) +{ + struct ps3_single_cmd_stat *stat_entry = + ps3_stat_entry_find(cmd->index, instance); + if (stat_entry == NULL) { + LOG_ERROR("host_no:%u CFID:%u stat entry find fail\n", + PS3_HOST(instance), cmd->index); + goto l_out; + } + + switch (cmd->req_frame->mgrReq.reqHead.cmdType) { + case PS3_CMD_MANAGEMENT: + case PS3_CMD_SAS_MANAGEMENT: + case PS3_CMD_IOCTL: + goto l_mgr_inc; + case PS3_CMD_SCSI_TASK_MANAGEMENT: + ps3_task_cmd_start_inc(stat_entry, cmd); + goto l_mgr_inc; + default: + goto l_out; + } + +l_mgr_inc: + ps3_stat_start_inc(PS3_DRV_IOC_MGR, stat_entry); +l_out: + return; +} + +static inline void ps3_task_cmd_back_inc(struct ps3_single_cmd_stat *stat_entry, + const struct ps3_cmd *cmd, + unsigned char status) +{ + switch (cmd->req_frame->mgrReq.reqHead.cmdSubType) { + case PS3_TASK_CMD_SCSI_TASK_ABORT: + ps3_stat_back_inc(PS3_SCSI_ABORT, stat_entry, status); + break; + + case PS3_TASK_CMD_SCSI_TASK_RESET: + ps3_stat_back_inc(PS3_SCSI_DEVICE_RESET, stat_entry, status); + break; + + default: + break; + } +} + +void ps3_mgr_back_inc(struct ps3_instance *instance, const struct ps3_cmd *cmd, + unsigned char status) +{ + struct ps3_single_cmd_stat *stat_entry = + ps3_stat_entry_find(cmd->index, instance); + if (stat_entry == NULL) { + LOG_ERROR("host_no:%u index:%u stat entry find fail\n", + PS3_HOST(instance), cmd->index); + goto l_out; + } + + switch (cmd->req_frame->mgrReq.reqHead.cmdType) { + case PS3_CMD_MANAGEMENT: + case PS3_CMD_SAS_MANAGEMENT: + case PS3_CMD_IOCTL: + goto l_mgr_inc; + + case PS3_CMD_SCSI_TASK_MANAGEMENT: + ps3_task_cmd_back_inc(stat_entry, cmd, status); + goto l_mgr_inc; + + default: + goto l_out; + } + +l_mgr_inc: + ps3_stat_back_inc(PS3_DRV_IOC_MGR, stat_entry, status); +l_out: + return; +} + +#ifndef _WINDOWS + +static inline void ps3_stat_workq_service(struct work_struct *work) +{ + struct ps3_cmd_stat_wrokq_context *ctx = ps3_container_of( + work, struct ps3_cmd_stat_wrokq_context, stat_work.work); + struct ps3_instance *instance = NULL; + + if (ctx == NULL) { + LOG_ERROR("ps3_cmd_stat_wrokq_context is null\n"); + goto l_out; + } + + instance = ctx->instance; + if (!ps3_stat_inc_switch_is_open(instance)) { + LOG_INFO("cmd_stat_switch is close\n"); + goto l_out; + } + + if (ctx->is_stop) { + LOG_INFO("cmd_stat_switch workq is stop\n"); + goto l_out; + } + + ps3_stat_data_collect(instance); + + if (ctx->stat_queue != NULL) { + queue_delayed_work( + ctx->stat_queue, &ctx->stat_work, + msecs_to_jiffies( + instance->cmd_statistics.stat_interval)); + } + +l_out: + return; +} + +int ps3_stat_workq_start(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + struct ps3_cmd_stat_wrokq_context *ctx = + &instance->cmd_statistics.stat_workq; + char qname[PS3_STAT_WROKQ_NAME_MAX_LEN] = { 0 }; + struct delayed_work *work = &ctx->stat_work; + + if (ctx->stat_queue != NULL) { + LOG_INFO("host_no:%u stat work for is already started!\n", + PS3_HOST(instance)); + goto l_out; + } + + memset(qname, 0, sizeof(qname)); + memset(ctx, 0, sizeof(struct ps3_cmd_stat_wrokq_context)); + INIT_DELAYED_WORK(work, ps3_stat_workq_service); + snprintf(qname, sizeof(qname), "ps3_stat_%u", PS3_HOST(instance)); + + ctx->stat_queue = create_singlethread_workqueue(qname); + if (ctx->stat_queue == NULL) { + LOG_ERROR("host_no:%u cmd stat work queue create failed\n", + PS3_HOST(instance)); + ret = -PS3_FAILED; + goto l_out; + } + + ctx->instance = instance; + + if (!ps3_stat_inc_switch_is_open(instance)) { + LOG_INFO("cmd_stat_switch is close\n"); + goto l_out; + } + + ctx->is_stop = PS3_FALSE; + queue_delayed_work( + ctx->stat_queue, work, + msecs_to_jiffies(instance->cmd_statistics.stat_interval)); + +l_out: + + return ret; +} + +void ps3_stat_workq_stop(struct ps3_instance *instance) +{ + struct ps3_cmd_stat_wrokq_context *ctx = NULL; + struct workqueue_struct *workq = NULL; + + ctx = &instance->cmd_statistics.stat_workq; + + ctx->is_stop = PS3_TRUE; + + if (ctx->stat_queue == NULL) + goto l_out; + + workq = ctx->stat_queue; + if (!cancel_delayed_work_sync(&ctx->stat_work)) + flush_workqueue(workq); + ctx->stat_queue = NULL; + + destroy_workqueue(workq); + +l_out: + memset(ctx, 0, sizeof(struct ps3_cmd_stat_wrokq_context)); +} +#else + +static inline void ps3_stat_workq_service(void *ins) +{ + struct ps3_instance *instance = (struct ps3_instance *)ins; + struct ps3_cmd_stat_wrokq_context *ctx = + &instance->cmd_statistics.stat_workq; + + if (ctx == NULL) { + LOG_ERROR("ps3_cmd_stat_wrokq_context is null\n"); + goto l_out; + } + + if (!ps3_stat_inc_switch_is_open(instance)) { + LOG_INFO("cmd_stat_switch is close\n"); + goto l_out; + } + + if (ctx->is_stop) { + LOG_INFO("cmd_stat_switch workq is stop\n"); + goto l_out; + } + + ps3_stat_data_collect(instance); + + ps3_delay_worker_start(&ctx->statis_work, + instance->cmd_statistics.stat_interval); + +l_out: + return; +} + +int ps3_stat_workq_start(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + struct ps3_cmd_stat_wrokq_context *ctx = + &instance->cmd_statistics.stat_workq; + struct ps3_delay_worker *statis_work = &ctx->statis_work; + + memset(ctx, 0, sizeof(struct ps3_cmd_stat_wrokq_context)); + + if (ps3_delay_worker_init(instance, statis_work, "ps3_statis", + ps3_stat_workq_service) != PS3_SUCCESS) { + LOG_ERROR("host_no:%u timer init failed\n", PS3_HOST(instance)); + ret = -PS3_FAILED; + goto l_out; + } + + if (!ps3_stat_inc_switch_is_open(instance)) { + LOG_INFO("cmd_stat_switch is close\n"); + goto l_out; + } + + ctx->is_stop = PS3_FALSE; + if (ps3_delay_worker_start(statis_work, + instance->cmd_statistics.stat_interval) != + PS3_SUCCESS) { + LOG_ERROR("host_no:%u timer request failed\n", + PS3_HOST(instance)); + ret = -PS3_FAILED; + ctx->is_stop = PS3_TRUE; + goto l_out; + } +l_out: + return ret; +} + +void ps3_stat_workq_stop(struct ps3_instance *instance) +{ + struct ps3_cmd_stat_wrokq_context *ctx = NULL; + + ctx = &instance->cmd_statistics.stat_workq; + + ctx->is_stop = PS3_TRUE; + ps3_delay_worker_exit(&ctx->statis_work); + + memset(ctx, 0, sizeof(struct ps3_cmd_stat_wrokq_context)); +} +#endif +static void ps3_backup_stat_entry_build(struct ps3_instance *instance) +{ + unsigned int i = 0; + struct ps3_cmd_statistics_context *ctx = &instance->cmd_statistics; + + for (; i < ctx->stat_entry_max_count; ++i) { + memcpy(ctx->cmd_stat_backup_buf[i], ctx->cmd_stat_buf[i], + sizeof(struct ps3_single_cmd_stat)); + mb(); /* in order to force CPU ordering */ + } +} + +static inline void ps3_last_stat_entry_build(struct ps3_instance *instance) +{ + unsigned int i = 0; + struct ps3_cmd_statistics_context *ctx = &instance->cmd_statistics; + + for (; i < ctx->stat_entry_max_count; ++i) { + memcpy(ctx->last_stat_buf[i], ctx->cmd_stat_backup_buf[i], + sizeof(struct ps3_single_cmd_stat)); + mb(); /* in order to force CPU ordering */ + } +} +static void ps3_stat_inc_data_collect(struct ps3_instance *instance) +{ + unsigned int i = 0; + unsigned short j = 0; + struct ps3_cmd_statistics_context *ctx = &instance->cmd_statistics; + struct ps3_single_cmd_stat *backup_stat = NULL; + struct ps3_single_cmd_stat *last_stat = NULL; + unsigned long long inc_back_good = 0; + unsigned long long inc_back_err = 0; + + memset(&ctx->inc_stat, 0, sizeof(ctx->inc_stat)); + for (; i < ctx->stat_entry_max_count; ++i) { + backup_stat = ps3_backup_stat_entry_find(i, instance); + if (backup_stat == NULL) { + LOG_WARN("host_no:%u index:%u get backup_stat err\n", + PS3_HOST(instance), i); + continue; + } + last_stat = ps3_last_stat_entry_find(i, instance); + if (last_stat == NULL) { + LOG_WARN("host_no:%u index:%u get last_stat err\n", + PS3_HOST(instance), i); + continue; + } + + mb(); /* in order to force CPU ordering */ + + for (j = 0; j < PS3_CMD_STAT_COUNT; ++j) { + ctx->inc_stat.stat[j].start += + backup_stat->stat[j].start - + last_stat->stat[j].start; + inc_back_good = backup_stat->stat[j].back_good - + last_stat->stat[j].back_good; + inc_back_err = backup_stat->stat[j].back_err - + last_stat->stat[j].back_err; + if ((inc_back_good + inc_back_err) == 0) + continue; + mb(); /* in order to force CPU ordering */ + ctx->inc_stat.stat[j].back_good += inc_back_good; + ctx->inc_stat.stat[j].back_err += inc_back_err; + ctx->inc_stat.stat[j].not_back += + backup_stat->stat[j].not_back - + last_stat->stat[j].not_back; + ps3_stat_lagency_inc_cal(&ctx->inc_stat.stat[j].lagency, + &backup_stat->stat[j].lagency, + &last_stat->stat[j].lagency); + } + } + ps3_stat_lagency_avg_calc(&ctx->inc_stat); +} + +static inline void ps3_stat_total_data_collect(struct ps3_instance *instance) +{ + struct ps3_cmd_statistics_context *ctx = &instance->cmd_statistics; + unsigned long long back_count = 0; + unsigned short i = 0; + + for (; i < PS3_CMD_STAT_COUNT; ++i) { + ctx->total_stat.stat[i].start += ctx->inc_stat.stat[i].start; + ctx->total_stat.stat[i].back_good += + ctx->inc_stat.stat[i].back_good; + ctx->total_stat.stat[i].back_err += + ctx->inc_stat.stat[i].back_err; + back_count = ctx->total_stat.stat[i].back_good + + ctx->total_stat.stat[i].back_err; + ctx->total_stat.stat[i].not_back = + ctx->total_stat.stat[i].start - back_count; + ps3_stat_lagency_merge(&ctx->total_stat.stat[i].lagency, + &ctx->inc_stat.stat[i].lagency); + mb(); /* in order to force CPU ordering */ + if (back_count != 0) { + ctx->total_stat.stat[i].lagency.avg = PS3_DIV64_32( + ctx->total_stat.stat[i].lagency.all, + back_count); + } + } +} + +static inline void ps3_stat_data_log(const struct ps3_cmd_stat_entry *stat, + const struct ps3_cmd_stat_entry *inc_stat, + unsigned short stat_num) +{ + unsigned short i = 0; + + for (; i < stat_num; ++i) { + if (inc_stat[i].start == 0) + continue; + LOG_INFO( + "[%s:] st:%llu bg:%llu ber:%llu nb:%llu avg[%lluus] max[%lluus] min[%lluus]\n", + ps3_cmd_stat_item_tostring((enum ps3_cmd_stat_item)i), + stat[i].start, stat[i].back_good, stat[i].back_err, + stat[i].not_back, stat[i].lagency.avg, + stat[i].lagency.max_lagency, + stat[i].lagency.min_lagency); + } +} + +static inline void ps3_stat_total_data_log(struct ps3_instance *instance) +{ + LOG_INFO("host_no:%u total cmd stat show begin\n", PS3_HOST(instance)); + ps3_stat_data_log(instance->cmd_statistics.total_stat.stat, + instance->cmd_statistics.inc_stat.stat, + PS3_CMD_STAT_COUNT); + LOG_INFO("host_no:%u total cmd stat show end\n", PS3_HOST(instance)); +} + +static inline void ps3_stat_inc_data_log(struct ps3_instance *instance) +{ + LOG_INFO("host_no:%u inc cmd stat show begin\n", PS3_HOST(instance)); + ps3_stat_data_log(instance->cmd_statistics.inc_stat.stat, + instance->cmd_statistics.inc_stat.stat, + PS3_CMD_STAT_COUNT); + LOG_INFO("host_no:%u inc cmd stat show end\n", PS3_HOST(instance)); +} + +static inline unsigned char ps3_stat_is_write_log(struct ps3_instance *instance) +{ + return instance->cmd_statistics.log_record_count == PS3_STAT_LOG_COUNT; +} + +static inline void ps3_stat_log_count_inc(struct ps3_instance *instance) +{ + instance->cmd_statistics.log_record_count++; + if (instance->cmd_statistics.log_record_count == + PS3_STAT_LOG_MAX_COUNT) { + instance->cmd_statistics.log_record_count = 0; + } +} + +static inline void ps3_stat_write_log(struct ps3_instance *instance) +{ + if (ps3_stat_log_switch_is_open(instance) && + ps3_stat_inc_switch_is_open(instance)) { + if (ps3_stat_is_write_log(instance)) { + ps3_stat_inc_data_log(instance); + + ps3_stat_total_data_log(instance); + } + ps3_stat_log_count_inc(instance); + } +} + +static inline void ps3_stat_data_collect(struct ps3_instance *instance) +{ + ps3_backup_stat_entry_build(instance); + + ps3_stat_inc_data_collect(instance); + + ps3_stat_total_data_collect(instance); + + ps3_last_stat_entry_build(instance); + + ps3_stat_write_log(instance); +} + +void ps3_qos_stat_inc(struct ps3_instance *instance, struct ps3_cmd *cmd, + unsigned short stat, unsigned short type) +{ + struct ps3_single_cmd_stat *stat_entry = + ps3_stat_entry_find(cmd->index, instance); + if (stat_entry == NULL) { + LOG_ERROR("host_no:%u index:%u stat entry find fail\n", + PS3_HOST(instance), cmd->index); + return; + } + + if (type == PS3_STAT_START) + ps3_stat_start_inc(stat, stat_entry); + else if (type == PS3_STAT_BACK) + ps3_stat_back_inc(stat, stat_entry, PS3_STAT_BACK_OK); +} diff --git a/drivers/scsi/linkdata/ps3stor/ps3_cmd_statistics.h b/drivers/scsi/linkdata/ps3stor/ps3_cmd_statistics.h new file mode 100644 index 0000000000000000000000000000000000000000..201cd3db46377cbfe2d95227665bc0ade2bc769b --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/ps3_cmd_statistics.h @@ -0,0 +1,412 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _PS3_CMD_STATICTIS_H_ +#define _PS3_CMD_STATICTIS_H_ +#ifndef _WINDOWS +#include +#include +#include +#endif + +#include "ps3_htp_def.h" +#include "ps3_scsih_cmd_parse.h" +#include "ps3_instance_manager.h" +#include "ps3_cmd_channel.h" +#include "ps3_platform_utils.h" +#include "ps3_scsih.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define PS3_CMD_STAT_INIT_VALUE (0) + +#define PS3_CMD_WORD_INDEX_SHIFT (0x000000FF) + +void ps3_dev_io_statis_init(struct ps3_dev_io_statis *statis); + +void ps3_io_statis_inc(struct scsi_device *sdev, + enum ps3_dev_io_stat_type type); + +void ps3_io_statis_dec(struct scsi_device *sdev, + enum ps3_dev_io_stat_type type); + +void ps3_io_statis_clear(struct scsi_device *sdev); + +int ps3_cmd_statistics_init(struct ps3_instance *instance); + + +void ps3_cmd_statistics_exit(struct ps3_instance *instance); + +void ps3_dev_io_start_inc(struct ps3_instance *instance, + const struct ps3_cmd *cmd); + +void ps3_dev_io_start_err_inc(struct ps3_instance *instance, + const struct ps3_cmd *cmd); + +void ps3_dev_io_start_ok_inc(struct ps3_instance *instance, + const struct ps3_cmd *cmd); + +void ps3_dev_io_back_inc(struct ps3_instance *instance, + const struct ps3_cmd *cmd, unsigned char status); + +static inline void ps3_dev_scsi_err_stat_inc(struct ps3_cmd *cmd) +{ + if (ps3_scsih_is_read_cmd(cmd->io_attr.rw_flag)) { + ps3_io_statis_inc(cmd->scmd->device, + PS3_DEV_IO_STAT_TYPE_R_RECV_ERR); + } else if (ps3_scsih_is_write_cmd(cmd->io_attr.rw_flag)) { + ps3_io_statis_inc(cmd->scmd->device, + PS3_DEV_IO_STAT_TYPE_W_RECV_ERR); + } +} + +static inline unsigned char ps3_is_vd_rw_cmd(const struct scsi_cmnd *s_cmd) +{ + return (PS3_SDEV_PRI_DATA(s_cmd->device)->dev_type == + PS3_DEV_TYPE_VD) && + ps3_scsih_cdb_is_rw_cmd(s_cmd->cmnd); +} + +static inline unsigned char ps3_is_read_cmd(const struct scsi_cmnd *s_cmd) +{ + return ps3_scsih_cdb_is_read_cmd(s_cmd->cmnd); +} +static inline unsigned char ps3_is_write_cmd(const struct scsi_cmnd *s_cmd) +{ + return ps3_scsih_cdb_is_write_cmd(s_cmd->cmnd); +} + +void ps3_io_outstand_inc(struct ps3_instance *instance, + const struct ps3_cmd *cmd); + +void ps3_io_outstand_dec(struct ps3_instance *instance, + const struct scsi_cmnd *s_cmd); + +void ps3_vd_outstand_dec(struct ps3_instance *instance, + const struct scsi_cmnd *s_cmd); + +void ps3_vd_outstand_inc(struct ps3_instance *instance, + const struct scsi_cmnd *s_cmd); + +void ps3_dev_io_outstand_dec(const struct ps3_cmd *cmd); + +void ps3_dev_io_qos_inc(struct ps3_scsi_priv_data *priv_data); + +void ps3_dev_io_qos_dec(struct ps3_scsi_priv_data *priv_data); + +void ps3_dev_io_outstand_inc(const struct ps3_cmd *cmd); + +void ps3_qos_cmd_inc(struct ps3_instance *instance); + +void ps3_qos_stat_inc(struct ps3_instance *instance, struct ps3_cmd *cmd, + unsigned short stat, unsigned short type); + +static inline unsigned char +ps3_cmd_word_index_get(struct ps3_instance *instance) +{ + unsigned long long cmd_count = + (unsigned long long)ps3_atomic64_inc_return( + &instance->cmd_statistics.cmd_word_send_count); + return (unsigned char)(cmd_count & PS3_CMD_WORD_INDEX_SHIFT); +} + +static inline void ps3_cmd_word_stat_inc(struct ps3_instance *instance, + struct PS3CmdWord *cmd_word) +{ + (void)cmd_word; + (void)ps3_cmd_word_index_get(instance); +} + +#ifndef _WINDOWS +void ps3_scmd_inc(struct ps3_instance *instance, struct scsi_cmnd *s_cmd, + unsigned char type, unsigned char status); +#else +void ps3_scmd_inc(struct ps3_instance *instance, const struct scsi_cmnd *s_cmd, + unsigned int tag, unsigned char type, unsigned char status); +#endif +void ps3_scmd_drv2ioc_start_inc(struct ps3_instance *instance, + const struct ps3_cmd *cmd); + +void ps3_scmd_drv2ioc_back_inc(struct ps3_instance *instance, + const struct ps3_cmd *cmd, unsigned char status); + +static inline unsigned char +ps3_resp_stauts_to_stat_status(unsigned char reply_flags) +{ + return (reply_flags == PS3_REPLY_WORD_FLAG_SUCCESS) ? + (unsigned char)PS3_STAT_BACK_OK : + (unsigned char)PS3_STAT_BACK_FAIL; +} + +void ps3_mgr_start_inc(struct ps3_instance *instance, + const struct ps3_cmd *cmd); + +void ps3_mgr_back_inc(struct ps3_instance *instance, const struct ps3_cmd *cmd, + unsigned char status); + +void ps3_stat_all_clear(struct ps3_instance *instance); + +static inline unsigned char +ps3_stat_inc_switch_is_open(const struct ps3_instance *instance) +{ + return (instance->cmd_statistics.cmd_stat_switch & + PS3_STAT_INC_SWITCH_OPEN); +} + +static inline unsigned char +ps3_stat_log_switch_is_open(const struct ps3_instance *instance) +{ + return (instance->cmd_statistics.cmd_stat_switch & + PS3_STAT_LOG_SWITCH_OPEN); +} + +static inline unsigned char +ps3_stat_outstand_switch_is_open(const struct ps3_instance *instance) +{ + return (instance->cmd_statistics.cmd_stat_switch & + PS3_STAT_OUTSTAND_SWITCH_OPEN); +} + +static inline unsigned char +ps3_stat_dev_switch_is_open(const struct ps3_instance *instance) +{ + return (instance->cmd_statistics.cmd_stat_switch & + PS3_STAT_DEV_SWITCH_OPEN); +} + +#ifndef _WINDOWS +static inline void ps3_sdev_busy_inc(struct scsi_cmnd *scmd) +{ +#if defined DRIVER_SUPPORT_PRIV_BUSY + struct ps3_scsi_priv_data *device_priv_data = + (struct ps3_scsi_priv_data *)scmd->device->hostdata; + if (device_priv_data == NULL) + return; + + atomic_inc(&device_priv_data->sdev_priv_busy); +#else +#ifdef PS3_UT + atomic_inc(&scmd->device->device_busy); +#else + (void)scmd; +#endif +#endif +} + +static inline void ps3_sdev_busy_dec(struct scsi_cmnd *scmd) +{ +#if defined DRIVER_SUPPORT_PRIV_BUSY + struct ps3_scsi_priv_data *device_priv_data = + (struct ps3_scsi_priv_data *)scmd->device->hostdata; + if (device_priv_data == NULL) + return; + + atomic_dec(&device_priv_data->sdev_priv_busy); +#else +#ifdef PS3_UT + atomic_dec(&scmd->device->device_busy); +#else + (void)scmd; +#endif +#endif +} + +#define PS3_DEV_BUSY_DEC(cmd) ps3_sdev_busy_dec((cmd)) + +#define PS3_DEV_BUSY_INC(cmd) ps3_sdev_busy_inc((cmd)) +#else +#define PS3_DEV_BUSY_DEC(cmd) +#define PS3_DEV_BUSY_INC(cmd) +#endif + +static inline unsigned char +ps3_stat_qos_switch_is_open(const struct ps3_instance *instance) +{ + return (instance->cmd_statistics.cmd_stat_switch & + PS3_STAT_QOS_SWITCH_OPEN); +} + +#define PS3_CMD_WORD_STAT_INC(instance, cmd_word) \ + do { \ + if (ps3_stat_inc_switch_is_open((instance))) { \ + ps3_cmd_word_stat_inc((instance), (cmd_word)); \ + } \ + } while (0) + +#define PS3_DEV_IO_START_OK_INC(instance, cmd) \ + do { \ + if (ps3_stat_dev_switch_is_open((instance))) { \ + ps3_dev_io_start_ok_inc((instance), (cmd)); \ + } \ + } while (0) + +#define PS3_DEV_IO_BACK_INC(instance, cmd, status) \ + do { \ + if (ps3_stat_dev_switch_is_open((instance))) { \ + ps3_dev_io_back_inc((instance), (cmd), (status)); \ + } \ + } while (0) + +#define PS3_DEV_IO_START_ERR_INC(instance, cmd) \ + do { \ + if (ps3_stat_dev_switch_is_open((instance))) { \ + ps3_dev_io_start_err_inc((instance), (cmd)); \ + } \ + } while (0) + +#define PS3_DEV_IO_START_INC(instance, cmd) \ + do { \ + if (ps3_stat_dev_switch_is_open((instance))) { \ + ps3_dev_io_start_inc((instance), (cmd)); \ + } \ + } while (0) +#ifndef _WINDOWS +#define PS3_IO_BACK_INC(instance, s_cmd, status) \ + do { \ + if (ps3_stat_inc_switch_is_open((instance))) { \ + ps3_scmd_inc( \ + (instance), (s_cmd), PS3_STAT_BACK, \ + ps3_resp_stauts_to_stat_status((status))); \ + } \ + } while (0) +#else +#define PS3_IO_BACK_INC(instance, s_cmd, tag, status) \ + do { \ + if (ps3_stat_inc_switch_is_open((instance))) { \ + ps3_scmd_inc( \ + (instance), (s_cmd), (tag), PS3_STAT_BACK, \ + ps3_resp_stauts_to_stat_status((status))); \ + } \ + } while (0) +#endif +#define PS3_IO_OUTSTANDING_INC(instance, cmd) ps3_io_outstand_inc(instance, cmd) + +#define PS3_DEV_IO_ERR_STAT_INC(instance, cmd) \ + do { \ + if (ps3_stat_dev_switch_is_open((instance))) { \ + ps3_dev_scsi_err_stat_inc((cmd)); \ + } \ + } while (0) +#ifndef _WINDOWS +#define PS3_IO_START_INC(instance, s_cmd) \ + do { \ + if (ps3_stat_inc_switch_is_open((instance))) { \ + ps3_scmd_inc((instance), (s_cmd), PS3_STAT_START, \ + PS3_STAT_BACK_OK); \ + } \ + } while (0) + +#define PS3_IO_BACK_ERR_INC(instance, s_cmd) \ + do { \ + if (ps3_stat_inc_switch_is_open((instance))) { \ + ps3_scmd_inc((instance), (s_cmd), PS3_STAT_BACK, \ + PS3_STAT_BACK_FAIL); \ + } \ + } while (0) +#else +#define PS3_IO_START_INC(instance, s_cmd, tag) \ + do { \ + if (ps3_stat_inc_switch_is_open((instance))) { \ + ps3_scmd_inc((instance), (s_cmd), (tag), \ + PS3_STAT_START, PS3_STAT_BACK_OK); \ + } \ + } while (0) + +#define PS3_IO_BACK_ERR_INC(instance, s_cmd, tag) \ + do { \ + if (ps3_stat_inc_switch_is_open((instance))) { \ + ps3_scmd_inc((instance), (s_cmd), (tag), \ + PS3_STAT_BACK, PS3_STAT_BACK_FAIL); \ + } \ + } while (0) +#endif +#define PS3_IO_DRV2IOC_START_INC(instance, cmd) \ + do { \ + if (ps3_stat_inc_switch_is_open((instance))) { \ + ps3_scmd_drv2ioc_start_inc((instance), (cmd)); \ + } \ + } while (0) + +#define PS3_IOC_DRV2IOC_BACK_INC(instance, cmd, status) \ + do { \ + if (ps3_stat_inc_switch_is_open((instance))) { \ + ps3_scmd_drv2ioc_back_inc( \ + (instance), (cmd), \ + ps3_resp_stauts_to_stat_status((status))); \ + } \ + } while (0) + +#define PS3_MGR_CMD_STAT_INC(instance, cmd) \ + do { \ + if (ps3_stat_inc_switch_is_open((instance))) { \ + ps3_mgr_start_inc((instance), (cmd)); \ + } \ + } while (0) + +#define PS3_MGR_CMD_BACK_INC(instance, cmd, status) \ + do { \ + if (ps3_stat_inc_switch_is_open((instance))) { \ + ps3_mgr_back_inc( \ + (instance), (cmd), \ + ps3_resp_stauts_to_stat_status((status))); \ + } \ + } while (0) + +#define PS3_IO_OUTSTAND_DEC(instance, s_cmd) \ + do { \ + if (ps3_stat_outstand_switch_is_open((instance))) { \ + ps3_io_outstand_dec((instance), (s_cmd)); \ + } \ + } while (0) + +#define PS3_VD_OUTSTAND_DEC(instance, s_cmd) \ + ps3_vd_outstand_dec(instance, s_cmd) \ + +#define PS3_VD_OUTSTAND_INC(instance, s_cmd) ps3_vd_outstand_inc(instance, s_cmd) + +#define PS3_DEV_IO_OUTSTAND_DEC(instance, cmd) \ + do { \ + if (ps3_stat_dev_switch_is_open((instance))) { \ + ps3_dev_io_outstand_dec((cmd)); \ + } \ + } while (0) + +#define PS3_DEV_IO_OUTSTAND_INC(instance, cmd) \ + do { \ + if (ps3_stat_dev_switch_is_open((instance))) { \ + ps3_dev_io_outstand_inc((cmd)); \ + } \ + } while (0) + +#define PS3_QOS_CMD_INC(instance) \ + do { \ + if (ps3_stat_qos_switch_is_open((instance))) { \ + ps3_qos_cmd_inc((instance)); \ + } \ + } while (0) + +#define PS3_QOS_STAT_START(instance, cmd, type) \ + do { \ + if (ps3_stat_inc_switch_is_open((instance))) { \ + ps3_qos_stat_inc((instance), (cmd), (type), \ + PS3_STAT_START); \ + } \ + } while (0) + +#define PS3_QOS_STAT_END(instance, cmd, type) \ + do { \ + if (ps3_stat_inc_switch_is_open((instance))) { \ + ps3_qos_stat_inc((instance), (cmd), (type), \ + PS3_STAT_BACK); \ + } \ + } while (0) + +int ps3_stat_workq_start(struct ps3_instance *instance); + +void ps3_stat_workq_stop(struct ps3_instance *instance); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/drivers/scsi/linkdata/ps3stor/ps3_debug.c b/drivers/scsi/linkdata/ps3stor/ps3_debug.c new file mode 100644 index 0000000000000000000000000000000000000000..db823f1b05482abbcd1572b606842da1acbbd5e6 --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/ps3_debug.c @@ -0,0 +1,1172 @@ +// SPDX-License-Identifier: GPL-2.0 +#ifndef _WINDOWS + +#include +#include +#include +#include +#ifdef __KERNEL__ +#include +#else +#include +#endif +#include "ps3_htp_def.h" +#include "ps3_instance_manager.h" +#include "ps3_ioc_manager.h" +#include "ps3_ioc_state.h" + +#include "ps3_debug.h" +#include "ps3_irq.h" +#include "ps3_driver_log.h" +#include "ps3_cli_debug.h" +#include "ps3_module_para.h" +#include "ps3_util.h" + +#endif + +#include "ps3_htp_def.h" +#include "ps3_instance_manager.h" +#include "ps3_event.h" +#include "ps3_dump.h" +#include "ps3_kernel_version.h" + +#define REG_OFFSET_BY_ADDR(instance, reg) \ + ((unsigned char *)(reg) - ((unsigned char *)(instance)->reg_set)) + +struct ps3_reg_name { + unsigned long long read_dump_interval_ms; + unsigned long long write_dump_interval_ms; + unsigned int reg_cnt; + char name[32]; +}; + +static struct ps3_reg_name g_ps3_reg_name_table[] = { + { 0, 0, 8, "reserved0" }, + { 0, 0, 1, "ps3Doorbell" }, + { 0, 0, 1, "ps3DoorbellIrqClear" }, + { 0, 0, 1, "ps3DoorbellIrqMask" }, + { 0, 0, 1, "ps3IrqControl" }, + { 0, 0, 20, "reserved1" }, + + { 0, 0, 1, "ps3SoftresetKey" }, + { 1000, 0, 1, "ps3SoftresetState" }, + { 0, 0, 1, "ps3Softreset" }, + { 0, 0, 1, "ps3SoftresetIrqClear" }, + { 0, 0, 1, "ps3SoftresetIrqMask" }, + { 0, 0, 1, "ps3SoftresetKeyShiftRegLow" }, + { 0, 0, 1, "ps3SoftresetKeyShiftRegHigh" }, + { 0, 0, 1, "ps3SoftresetTimeCnt" }, + { 0, 0, 1, "ps3SoftresetTimeOutEn" }, + { 0, 0, 23, "reserved2" }, + + { 0, 0, 1, "ps3HardresetKey" }, + { 1000, 0, 1, "ps3HardresetState" }, + { 0, 0, 1, "ps3Hardreset" }, + { 0, 0, 1, "ps3HardresetKeyShiftRegLow" }, + { 0, 0, 1, "ps3HardresetKeyShiftRegHigh" }, + { 0, 0, 1, "ps3HardresetTimeCnt" }, + { 0, 0, 1, "ps3HardresetTimeOutEn" }, + { 0, 0, 1, "ps3KeyGapCfg" }, + { 0, 0, 1, "ps3HardresetIrqClear" }, + { 0, 0, 1, "ps3HardresetIrqMask" }, + { 0, 0, 22, "reserved3" }, + + { 10000, 0, 1, "ps3SocFwState" }, + { 0, 0, 1, "ps3MaxFwCmd" }, + { 0, 0, 1, "ps3MaxChainSize" }, + { 0, 0, 1, "ps3MaxVdInfoSize" }, + { 0, 0, 1, "ps3MaxNvmePageSize" }, + { 0, 0, 1, "ps3FeatureSupport" }, + { 0, 0, 1, "ps3FirmwareVersion" }, + { 0, 0, 1, "ps3MaxReplyque" }, + { 0, 0, 1, "ps3HardwareVersion" }, + { 0, 0, 1, "ps3MgrQueueDepth" }, + { 0, 0, 1, "ps3CmdQueueDepth" }, + { 0, 0, 1, "ps3TfifoDepth" }, + { 0, 0, 1, "ps3MaxSecR1xCmds" }, + { 0, 0, 19, "reserved4" }, + + { 0, 0, 1, "ps3HilAdvice2directCnt0" }, + { 0, 0, 1, "ps3HilAdvice2directCnt1" }, + { 0, 0, 1, "ps3HilAdvice2directCnt2" }, + { 0, 0, 1, "ps3HilAdvice2directCnt3" }, + { 0, 0, 1, "ps3HilAdvice2directCntAll" }, + { 0, 0, 3, "reserved5" }, + { 0, 0, 1, "ps3IrqStatusRpt" }, + { 0, 0, 23, "reserved6" }, + + { 0, 0, 1, "ps3DumpCtrl" }, + { 0, 0, 1, "ps3DumpCtrlIrqClear" }, + { 0, 0, 1, "ps3DumpCtrlIrqMask" }, + { 0, 0, 1, "ps3DumpStatus" }, + { 0, 0, 1, "ps3DumpDataSize" }, + { 0, 0, 27, "reserved7" }, + + { 0, 0, 1, "ps3CmdTrigger" }, + { 0, 0, 1, "ps3CmdTriggerIrqClear" }, + { 0, 0, 1, "ps3CmdTriggerIrqMask" }, + { 0, 0, 1, "ps3SoftresetCounter" }, + { 0, 0, 1, "ps3RegCmdState" }, + { 0, 0, 1, "ps3Debug0" }, + { 0, 0, 1, "ps3Debug0IrqClear" }, + { 0, 0, 1, "ps3Debug0IrqMask" }, + { 0, 0, 1, "ps3Debug1" }, + { 0, 0, 1, "ps3Debug1IrqClear" }, + { 0, 0, 1, "ps3Debug1IrqMask" }, + { 0, 0, 1, "ps3Debug2" }, + { 0, 0, 1, "ps3Debug2IrqClear" }, + { 0, 0, 1, "ps3Debug2IrqMask" }, + { 0, 0, 1, "ps3Debug3" }, + { 0, 0, 1, "ps3Debug3IrqClear" }, + { 0, 0, 1, "ps3Debug3IrqMask" }, + { 0, 0, 1, "ps3Debug4" }, + { 0, 0, 1, "ps3Debug4IrqClear" }, + { 0, 0, 1, "ps3Debug4IrqMask" }, + { 0, 0, 1, "ps3Debug5" }, + { 0, 0, 1, "ps3Debug6" }, + { 0, 0, 1, "ps3Debug7" }, + { 0, 0, 1, "ps3Debug8" }, + { 0, 0, 1, "ps3Debug9" }, + { 0, 0, 1, "ps3Debug10" }, + { 0, 0, 1, "ps3Debug11" }, + { 0, 0, 1, "ps3Debug12" }, + { 0, 0, 4, "reserved8" }, + + { 0, 0, 1, "ps3SessioncmdAddr" }, + { 0, 0, 1, "ps3SessioncmdAddrIrqClear" }, + { 0, 0, 1, "ps3SessioncmdAddrIrqMask" }, + { 0, 0, 7965, "reserved9" }, + { 0, 0, 1, "ps3RequestQueue" }, + { 0, 0, 1, "fifoErrCnt" }, + { 0, 0, 1, "fifoStatus" }, + { 0, 0, 1, "fifoLevelConfig" }, + { 0, 0, 1, "fifoRst" }, + { 0, 0, 1, "fifoIOCnt" }, + { 0, 0, 1, "fifoFlowCnt" }, + { 0, 0, 1, "fifoIntStatus" }, + { 0, 0, 1, "fifoIntSet" }, + { 0, 0, 1, "fifoIntClr" }, + { 0, 0, 1, "fifoIntMask" }, + { 0, 0, 1, "fifoCntClr" }, + { 0, 0, 1, "fifoOrderError" }, + { 0, 0, 4, "fifoDinShift" }, + { 0, 0, 4, "fifoDoutShift" }, + { 0, 0, 1, "fifoStatusMaxLevel" }, + { 0, 0, 1, "fifoInit" }, + { 0, 0, 1, "fifoinitEn" }, + { 0, 0, 1, "fifoinitMax" }, + { 0, 0, 1, "fifoStatusEccCnt" }, + { 0, 0, 1, "fifoStatusEccAddr" }, + { 0, 0, 1, "fifoDecoderOverflow" }, + { 0, 0, 1, "fifoEccBadProject" }, + { 0, 0, 1, "fifoOverFlowWord" }, + { 0, 0, 34, "reserved10" }, + + { 0, 0, 1, "ps3FucntionLock" }, + { 0, 0, 1, "ps3FunctionLockOwner" }, + { 0, 0, 30, "reserved11" }, +}; +static inline unsigned long long ps3_util_now_timestamp_ms_get(void) +{ + unsigned long long timenow = 0; +#ifndef _WINDOWS + +#if defined(PS3_DUMP_TIME_32) + struct timespec now; + + now = current_kernel_time(); +#else + struct timespec64 now; + + ktime_get_coarse_real_ts64(&now); +#endif + timenow = now.tv_sec * 1000 + now.tv_nsec / 1000000; +#else + LARGE_INTEGER now; + + StorPortQuerySystemTime(&now); + + timenow = (unsigned long long)(now.QuadPart / 10000); +#endif + return timenow; +} + +static void ps3_reg_dump_attr_init(struct ps3_instance *instance) +{ + unsigned int local_index = 0; + unsigned int count = 0; + unsigned int table_index = 0; + unsigned int table_cnt = 0; + unsigned int table_size = (sizeof(g_ps3_reg_name_table) / + sizeof(g_ps3_reg_name_table[0])); + struct ps3_reg_dump_attr *reg_dump = instance->debug_context.reg_dump; + + for (table_index = 0; table_index < table_size; table_index++) { + table_cnt = g_ps3_reg_name_table[table_index].reg_cnt; + + for (local_index = 0; local_index < table_cnt; local_index++) { + if (count >= (PS3_REGISTER_SET_SIZE / + sizeof(unsigned long long))) + goto l_out; + + reg_dump[count].read_dump_timestamp = 0; + reg_dump[count].write_dump_timestamp = 0; + reg_dump[count].read_dump_interval_ms = + g_ps3_reg_name_table[table_index] + .read_dump_interval_ms; + reg_dump[count].write_dump_interval_ms = + g_ps3_reg_name_table[table_index] + .write_dump_interval_ms; + memcpy(reg_dump[count].name, + g_ps3_reg_name_table[table_index].name, 32); + reg_dump[count].lastest_value = 0; + + count++; + } + } +l_out: + return; +} + +void ps3_reg_dump(struct ps3_instance *instance, void __iomem *reg, + unsigned long long value, unsigned char is_read) +{ + unsigned char is_dump = PS3_FALSE; + const char *dump_type = NULL; + unsigned long long timestamp_ms = ps3_util_now_timestamp_ms_get(); + unsigned long long last_dump_timestamp = 0; + unsigned long long dump_interval = 0; + unsigned int reg_index = + (unsigned int)REG_OFFSET_BY_ADDR(instance, reg) / + sizeof(unsigned long long); + struct ps3_reg_dump_attr *reg_dump = instance->debug_context.reg_dump; + + if ((reg == NULL) || + ((unsigned char *)reg < (unsigned char *)instance->reg_set) || + (((unsigned char *)reg - (unsigned char *)instance->reg_set) > + PS3_REGISTER_SET_SIZE)) { + return; + } + + if (is_read) { + dump_type = "reg read"; + last_dump_timestamp = reg_dump[reg_index].read_dump_timestamp; + dump_interval = reg_dump[reg_index].read_dump_interval_ms; + } else { + dump_type = "reg write"; + last_dump_timestamp = reg_dump[reg_index].write_dump_timestamp; + dump_interval = reg_dump[reg_index].write_dump_interval_ms; + } + + if (timestamp_ms - last_dump_timestamp >= dump_interval) + is_dump = PS3_TRUE; + + if (reg_dump[reg_index].lastest_value != value) { + is_dump = PS3_TRUE; + reg_dump[reg_index].lastest_value = value; + } + + if (!is_dump) + return; + + LOG_DEBUG("hno:%u %s:0x%04x:%s:0x%08llx\n", PS3_HOST(instance), + dump_type, + (unsigned int)(reg_index * sizeof(unsigned long long)), + reg_dump[reg_index].name, value); + if (is_read) + reg_dump[reg_index].read_dump_timestamp = timestamp_ms; + else + reg_dump[reg_index].write_dump_timestamp = timestamp_ms; +} + +#ifndef _WINDOWS + +static inline unsigned char ps3_is_alloc_debug_mem(unsigned int debug_mem_size) +{ + return (debug_mem_size != 0); +} + +static int ps3_debug_mem_array_alloc(struct ps3_instance *instance, + unsigned int debug_mem_size) +{ + int ret = PS3_SUCCESS; + unsigned int array_num = 0; + unsigned int alloc_size = 0; + struct ps3_debug_context *ctx = &instance->debug_context; + struct Ps3DebugMemEntry *entry = + (struct Ps3DebugMemEntry *)ctx->debug_mem_buf; + unsigned char i = 0; + + for (; i < PS3_DEBUG_MEM_ARRAY_MAX_NUM; ++i) { + if (debug_mem_size == 0) + break; + + array_num++; + if (debug_mem_size <= PS3_MAX_DMA_MEM_SIZE) { + alloc_size = debug_mem_size * 1024; + ctx->debug_mem_vaddr[i].debugMemAddr = + (unsigned long long)(uintptr_t) + ps3_dma_alloc_coherent( + instance, alloc_size, + (unsigned long long *)&entry[i] + .debugMemAddr); + if (ctx->debug_mem_vaddr[i].debugMemAddr == 0) { + LOG_ERROR( + "hno:%u alloc debug_mem[%u] end failed\n", + PS3_HOST(instance), i); + ret = -PS3_ENOMEM; + goto l_out; + } + ctx->debug_mem_vaddr[i].debugMemSize = debug_mem_size; + entry[i].debugMemSize = debug_mem_size; + debug_mem_size = 0; + LOG_INFO( + "hno:%u index[%u] vaddr[0x%llx], dma[0x%llx], size[%u]KB\n", + PS3_HOST(instance), i, + ctx->debug_mem_vaddr[i].debugMemAddr, + entry[i].debugMemAddr, entry[i].debugMemSize); + } else { + debug_mem_size -= PS3_MAX_DMA_MEM_SIZE; + alloc_size = PS3_MAX_DMA_MEM_SIZE * 1024; + ctx->debug_mem_vaddr[i].debugMemAddr = + (unsigned long long)(uintptr_t) + ps3_dma_alloc_coherent( + instance, alloc_size, + (unsigned long long *)&entry[i] + .debugMemAddr); + if (ctx->debug_mem_vaddr[i].debugMemAddr == 0) { + LOG_ERROR( + "hno:%u alloc debug_mem index[%u] failed\n", + PS3_HOST(instance), i); + ret = -PS3_ENOMEM; + goto l_out; + } + ctx->debug_mem_vaddr[i].debugMemSize = + PS3_MAX_DMA_MEM_SIZE; + entry[i].debugMemSize = PS3_MAX_DMA_MEM_SIZE; + LOG_INFO( + "hno:%u index[%u] m vaddr[0x%llx], dma[0x%llx], size[%u]KB\n", + PS3_HOST(instance), i, + ctx->debug_mem_vaddr[i].debugMemAddr, + entry[i].debugMemAddr, entry[i].debugMemSize); + } + + } + ctx->debug_mem_array_num = array_num; + LOG_INFO("hno:%u debug mem array num [%u]\n", PS3_HOST(instance), + array_num); + +l_out: + return ret; +} + +int ps3_debug_mem_alloc(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + unsigned int debug_mem_size = ps3_debug_mem_size_query(); + unsigned int buf_size = + PS3_DEBUG_MEM_ARRAY_MAX_NUM * sizeof(struct Ps3DebugMemEntry); + + if (!ps3_is_alloc_debug_mem(debug_mem_size)) + goto l_out; + + if (debug_mem_size > PS3_MAX_DEBUG_MEM_SIZE_PARA) { + LOG_WARN( + "hno:%u debug mem size is greater than the maximum value\n", + PS3_HOST(instance)); + ret = -PS3_ENOMEM; + goto l_out; + } + + instance->debug_context.debug_mem_buf = + (struct Ps3DebugMemEntry *)ps3_dma_alloc_coherent( + instance, buf_size, + (unsigned long long *)&instance->debug_context + .debug_mem_buf_phy); + if (instance->debug_context.debug_mem_buf == NULL) { + LOG_ERROR("hno:%u alloc debug_mem_buf failed\n", + PS3_HOST(instance)); + ret = -PS3_ENOMEM; + goto l_out; + } + memset(instance->debug_context.debug_mem_buf, 0, buf_size); + + ret = ps3_debug_mem_array_alloc(instance, debug_mem_size); + if (ret != PS3_SUCCESS) { + LOG_ERROR("hno:%u alloc debug_mem_array failed\n", + PS3_HOST(instance)); + ret = -PS3_ENOMEM; + goto l_free; + } + + LOG_INFO( + "hno:%u alloc debug_mem buf vaddr[0x%p], dma[0x%llx], size[%u]KB\n", + PS3_HOST(instance), instance->debug_context.debug_mem_buf, + (unsigned long long)instance->debug_context.debug_mem_buf_phy, + buf_size); + + goto l_out; + +l_free: + ps3_debug_mem_free(instance); +l_out: + return ret; +} + +int ps3_debug_mem_free(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + struct ps3_debug_context *ctx = &instance->debug_context; + struct Ps3DebugMemEntry *entry = ctx->debug_mem_buf; + unsigned int size = 0; + unsigned char i = 0; + + if (entry == NULL) { + ret = PS3_SUCCESS; + goto l_out; + } + + for (i = 0; i < PS3_DEBUG_MEM_ARRAY_MAX_NUM; ++i) { + if (ctx->debug_mem_vaddr[i].debugMemAddr == 0) + continue; + size = ctx->debug_mem_vaddr[i].debugMemSize * 1024; + LOG_INFO("dma free size[%u] addr[0x%llx]\n", size, + ctx->debug_mem_vaddr[i].debugMemAddr); + (void)ps3_dma_free_coherent( + instance, size, + (void *)(uintptr_t)ctx->debug_mem_vaddr[i].debugMemAddr, + entry[i].debugMemAddr); + } + memset(ctx->debug_mem_vaddr, 0, sizeof(ctx->debug_mem_vaddr)); + + size = PS3_DEBUG_MEM_ARRAY_MAX_NUM * sizeof(struct Ps3DebugMemEntry); + (void)ps3_dma_free_coherent(instance, size, ctx->debug_mem_buf, + ctx->debug_mem_buf_phy); + ctx->debug_mem_buf = NULL; + ctx->debug_mem_buf_phy = 0; + LOG_INFO("free debug mem end\n"); + +l_out: + return ret; +} + +void ps3_debug_context_init(struct ps3_instance *instance) +{ + memset(&instance->debug_context, 0, sizeof(struct ps3_debug_context)); + instance->debug_context.io_trace_switch = PS3_FALSE; + + ps3_reg_dump_attr_init(instance); +} + +static inline struct ps3_instance * +ps3_debug_instance_query(struct device *cdev, struct device_attribute *attr, + const char *buf) +{ + struct Scsi_Host *shost = NULL; + struct ps3_instance *instance = NULL; + + if (cdev == NULL || buf == NULL) { + instance = NULL; + goto l_out; + } + (void)attr; + + shost = class_to_shost(cdev); + if (shost == NULL) { + instance = NULL; + goto l_out; + } + + instance = (struct ps3_instance *)shost->hostdata; + +l_out: + return instance; +} + +ssize_t ps3_vd_io_outstanding_show(struct device *cdev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret = 0; + struct ps3_instance *instance = + ps3_debug_instance_query(cdev, attr, buf); + + if (instance == NULL) { + ret = 0; + goto l_out; + } + + ret = snprintf( + buf, PAGE_SIZE, "%d\n", + atomic_read(&instance->cmd_statistics.vd_io_outstanding)); + +l_out: + return ret; +} + +ssize_t ps3_io_outstanding_show(struct device *cdev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret = 0; + struct ps3_instance *instance = + ps3_debug_instance_query(cdev, attr, buf); + + if (instance == NULL) { + ret = 0; + goto l_out; + } + + ret = snprintf(buf, PAGE_SIZE, "%d\n", + atomic_read(&instance->cmd_statistics.io_outstanding)); + +l_out: + return ret; +} + +ssize_t ps3_is_load_show(struct device *cdev, struct device_attribute *attr, + char *buf) +{ + ssize_t ret = 0; + struct ps3_instance *instance = + ps3_debug_instance_query(cdev, attr, buf); + + if (instance == NULL) { + ret = 0; + goto l_out; + } + + ret = snprintf(buf, PAGE_SIZE, "%d\n", instance->state_machine.is_load); + +l_out: + return ret; +} +ssize_t ps3_dump_ioc_regs_show(struct device *cdev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret = 0; + struct ps3_instance *instance = + ps3_debug_instance_query(cdev, attr, buf); + + if (instance == NULL) { + ret = 0; + goto l_out; + } + + ret = ps3_ioc_reg_dump(instance, buf); + +l_out: + return ret; +} + +ssize_t ps3_max_scsi_cmds_show(struct device *cdev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret = 0; + struct ps3_instance *instance = + ps3_debug_instance_query(cdev, attr, buf); + + if (instance == NULL) { + ret = 0; + goto l_out; + } + + ret = snprintf(buf, PAGE_SIZE, "%d\n", + instance->cmd_context.max_scsi_cmd_count); + +l_out: + return ret; +} +#endif +static ssize_t ps3_event_map_get(unsigned int event_type_map, char *buf, + ssize_t left_len) +{ + ssize_t len = 0; + unsigned int mask_bit = 0X00000001; + unsigned int idx = 0; + unsigned int event_type = event_type_map & mask_bit; + unsigned int line_cnt = 2; + + while (mask_bit != 0) { + if (event_type != 0) { + if (idx && (!(idx % line_cnt))) { + len += snprintf(buf + len, left_len - len, + "\n"); + } + + len += snprintf( + buf + len, left_len - len, "\t%s", + ps3_event_print((enum MgrEvtType)event_type)); + } + + mask_bit = mask_bit << 1; + event_type = event_type_map & mask_bit; + idx++; + } + + len += snprintf(buf + len, left_len - len, "\n"); + return len; +} + +ssize_t ps3_event_subscribe_info_get(struct ps3_instance *instance, char *buf, + ssize_t total_len) +{ + ssize_t len = 0; + struct ps3_cmd *cmd = NULL; + struct PS3MgrReqFrame *mgr_req_frame = NULL; + struct PS3MgrEvent *event_req_info = NULL; + unsigned long flags = 0; + + ps3_spin_lock_irqsave(&instance->recovery_context->recovery_lock, + &flags); + cmd = instance->event_context.event_cmd; + if (cmd != NULL) { + mgr_req_frame = (struct PS3MgrReqFrame *)cmd->req_frame; + event_req_info = + (struct PS3MgrEvent *)&mgr_req_frame->value.event; + + len += snprintf(buf + len, total_len - len, + "Event subscribe cmd index:\t%d\n", cmd->index); + + len += snprintf(buf + len, total_len - len, + "Event subscribe level:\t%d\n", + event_req_info->eventLevel); + + len += snprintf(buf + len, total_len - len, + "Event subscribe type:\n"); + len += ps3_event_map_get(event_req_info->eventTypeMap, + buf + len, total_len - len); + + len += snprintf(buf + len, total_len - len, + "Event proc failed type:\n"); + len += ps3_event_map_get(event_req_info->eventTypeMapProcResult, + buf + len, total_len - len); + goto l_out; + } +l_out: + ps3_spin_unlock_irqrestore(&instance->recovery_context->recovery_lock, + flags); + return len; +} +#ifndef _WINDOWS +ssize_t ps3_event_subscribe_info_show(struct device *cdev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret = 0; + struct ps3_instance *instance = + ps3_debug_instance_query(cdev, attr, buf); + + if (instance == NULL) { + ret = 0; + goto l_out; + } + + ret = ps3_event_subscribe_info_get(instance, buf, PAGE_SIZE); +l_out: + return ret; +} + +static ssize_t ps3_ioc_state_dump(struct ps3_instance *instance, char *buf) +{ + ssize_t len = 0; + ssize_t total_len = PAGE_SIZE; + + len += snprintf(buf + len, total_len - len, "%s\n", + ps3_ioc_state_print( + instance->ioc_adpter->ioc_state_get(instance))); + + return len; +} + +ssize_t ps3_ioc_state_show(struct device *cdev, struct device_attribute *attr, + char *buf) +{ + ssize_t ret = 0; + struct ps3_instance *instance = + ps3_debug_instance_query(cdev, attr, buf); + + if (instance == NULL) { + ret = 0; + goto l_out; + } + + ret = ps3_ioc_state_dump(instance, buf); +l_out: + return ret; +} + +ssize_t ps3_log_level_store(struct device *cdev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int level = 0; + ssize_t ret = count; + (void)attr; + + if (cdev == NULL || buf == NULL) + goto l_out; + + if (kstrtoint(buf, 10, &level)) { + LOG_ERROR("invalid log level, could not set log level\n"); + ret = -EINVAL; + goto l_out; + } + +#if defined(PS3_SUPPORT_DEBUG) || \ + (defined(PS3_CFG_RELEASE) && defined(PS3_CFG_OCM_DBGBUG)) || \ + (defined(PS3_CFG_RELEASE) && defined(PS3_CFG_OCM_RELEASE)) +#else + if (level > LEVEL_INFO) { + LOG_INFO("log level limited INFO\n"); + level = LEVEL_INFO; + } +#endif + + LOG_WARN("set log level to %d\n", level); + + ps3_level_set(level); +l_out: + return ret; +} + +ssize_t ps3_log_level_show(struct device *cdev, struct device_attribute *attr, + char *buf) +{ + ssize_t ret = 0; + int level = ps3_level_get(); + (void)attr; + + if (cdev == NULL || buf == NULL) + goto l_out; + ret = snprintf(buf, PAGE_SIZE, "%d\n", level); + + LOG_DEBUG("get log level to %d\n", level); +l_out: + return ret; +} + +ssize_t ps3_io_trace_switch_store(struct device *cdev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned char trace_switch = 0; + ssize_t ret = count; + struct ps3_instance *instance = + ps3_debug_instance_query(cdev, attr, buf); + + if (instance == NULL) { + ret = count; + goto l_out; + } + + if (kstrtou8(buf, 10, &trace_switch)) { + LOG_ERROR("invalid io trace switch, could not set\n"); + ret = count; + goto l_out; + } + + instance->debug_context.io_trace_switch = trace_switch; + + LOG_WARN("set io trace switch is %d\n", + instance->debug_context.io_trace_switch); + +l_out: + return ret; +} + +ssize_t ps3_io_trace_switch_show(struct device *cdev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret = 0; + struct ps3_instance *instance = + ps3_debug_instance_query(cdev, attr, buf); + + if (instance == NULL) { + ret = 0; + goto l_out; + } + + ret = snprintf(buf, PAGE_SIZE, "%d\n", + instance->debug_context.io_trace_switch); + + LOG_DEBUG("get io trace switch is %d\n", + instance->debug_context.io_trace_switch); +l_out: + return ret; +} + +ssize_t ps3_dump_state_show(struct device *cdev, struct device_attribute *attr, + char *buf) +{ + ssize_t ret = 0; + struct ps3_dump_context *ctxt = NULL; + struct ps3_instance *instance = + ps3_debug_instance_query(cdev, attr, buf); + + if (instance == NULL) { + ret = 0; + goto l_out; + } + + ctxt = &instance->dump_context; + ret = snprintf(buf, PAGE_SIZE, "%d\n", ctxt->dump_state); +l_out: + return ret; +} + +ssize_t ps3_dump_state_store(struct device *cdev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int val = 0; + int ret = 0; + struct ps3_dump_context *ctxt = NULL; + (void)attr; + + ctxt = dev_to_dump_context(cdev); + + if (kstrtoint(buf, 0, &val) != 0) { + ret = -EINVAL; + goto l_ret; + } + + if ((val != PS3_DUMP_STATE_INVALID) && + (val != PS3_DUMP_STATE_ABORTED)) { + LOG_ERROR("hno:%u dump state should be %d or %d, %d is an invalid value\n", + PS3_HOST(ctxt->instance), PS3_DUMP_STATE_INVALID, + PS3_DUMP_STATE_ABORTED, val); + ret = -EINVAL; + goto l_ret; + } + + if (ps3_dump_state_set(ctxt, val) != PS3_SUCCESS) { + ret = -EPERM; + goto l_ret; + } + + return count; +l_ret: + return ret; +} + +ssize_t ps3_product_model_show(struct device *cdev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret = 0; + struct ps3_instance *instance = + ps3_debug_instance_query(cdev, attr, buf); + (void)attr; + + if (instance == NULL) + goto l_out; + + ret = snprintf(buf, PAGE_SIZE, "%s\n", instance->product_model); +l_out: + return ret; +} + +ssize_t ps3_dump_dir_show(struct device *cdev, struct device_attribute *attr, + char *buf) +{ + struct ps3_dump_context *ctxt; + struct ps3_instance *instance = + ps3_debug_instance_query(cdev, attr, buf); + ssize_t ret = 0; + + if (instance == NULL) + goto l_out; + + ctxt = &instance->dump_context; + + ret = snprintf(buf, PAGE_SIZE, "%s\n", ctxt->dump_dir); + +l_out: + return ret; +} + +int ps3_dump_dir_length(const char *buf, size_t count) +{ + int i = 0; + char c; + + while ((size_t)i++ < count) { + c = buf[i]; + + if (isdigit(c)) + continue; + + if (isalpha(c)) + continue; + + switch (c) { + case '-': + case '_': + case '/': + case '~': + continue; + break; + default: + goto l_out; + } + } + +l_out: + return i; +} + +ssize_t ps3_dump_type_show(struct device *cdev, struct device_attribute *attr, + char *buf) +{ + struct ps3_dump_context *ctxt; + struct ps3_instance *instance = + ps3_debug_instance_query(cdev, attr, buf); + ssize_t ret = 0; + + if (instance == NULL) + goto l_out; + + ctxt = &instance->dump_context; + + ret = snprintf(buf, PAGE_SIZE, "%d\n", ctxt->dump_type); + +l_out: + return ret; +} + +ssize_t ps3_dump_type_store(struct device *cdev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int val = 0; + int ret = 0; + struct ps3_dump_context *ctxt = NULL; + unsigned char is_trigger_log = PS3_FALSE; + (void)attr; + + ctxt = dev_to_dump_context(cdev); + + if (kstrtoint(buf, 0, &val) != 0) { + ret = -EINVAL; + goto l_ret; + } + + if ((val < PS3_DUMP_TYPE_CRASH) || (val > PS3_DUMP_TYPE_BAR_DATA)) { + LOG_ERROR("host_no[%d],dump state should be %d - %d, %d is an invalid value\n", + PS3_HOST(ctxt->instance), PS3_DUMP_TYPE_CRASH, + PS3_DUMP_TYPE_BAR_DATA, val); + ret = -EINVAL; + goto l_ret; + } + + ps3_ioc_dump_support_get(ctxt->instance); + is_trigger_log = ps3_dump_is_trigger_log(ctxt->instance); + if (!is_trigger_log) { + LOG_INFO("cannot dump type set!\n"); + ret = -EBUSY; + goto l_ret; + } + + if (ps3_dump_type_set(ctxt, val, PS3_DUMP_ENV_CLI) == -PS3_FAILED) { + ret = -EBUSY; + goto l_ret; + } + return count; +l_ret: + return ret; +} + +void ps3_dma_dump_mapping(struct pci_dev *pdev) +{ +#if defined(PS3_DMA_MAPPING) +#if defined(PS3_SUPPORT_DEBUG) || \ + (defined(PS3_CFG_RELEASE) && defined(PS3_CFG_OCM_DBGBUG)) || \ + (defined(PS3_CFG_RELEASE) && defined(PS3_CFG_OCM_RELEASE)) + void (*dma_dump_mappings)(struct device *dev) = NULL; + + dma_dump_mappings = (void (*)(struct device *)) + kallsyms_lookup_name("debug_dma_dump_mappings"); + if (dma_dump_mappings && pdev) { + pr_info("ps3 dma dump mapping begin\n"); + dma_dump_mappings(&pdev->dev); + pr_info("ps3 dma dump mapping end\n"); + } +#endif +#endif + (void)pdev; +} + +ssize_t ps3_soc_dead_reset_store(struct device *cdev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + int val = 0; + int ret = 0; + struct ps3_instance *instance = + ps3_debug_instance_query(cdev, attr, buf); + + (void)attr; + (void)count; + if (kstrtoint(buf, 0, &val) != 0) { + ret = -EINVAL; + goto l_ret; + } + if (ps3_atomic_read(&instance->state_machine.state) != + PS3_INSTANCE_STATE_DEAD) { + ret = -EPERM; + goto l_ret; + } + if (ps3_need_block_hard_reset_request(instance)) { + LOG_WARN("hno:%u can not start hard reset\n", + PS3_HOST(instance)); + } else { + ps3_hard_recovery_request_with_retry(instance); + } + + return count; +l_ret: + return ret; +} +ssize_t ps3_halt_support_cli_show(struct device *cdev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret = 0; + struct ps3_instance *instance = + ps3_debug_instance_query(cdev, attr, buf); + + if (instance == NULL) { + ret = 0; + goto l_out; + } + + ret = snprintf(buf, PAGE_SIZE, "%d\n", instance->is_halt_support_cli); + + LOG_DEBUG("get halt_support_cli is %d\n", + instance->is_halt_support_cli); +l_out: + return ret; +} + +ssize_t ps3_halt_support_cli_store(struct device *cdev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned char halt_support_cli = 0; + unsigned char is_halt_support_cli; + ssize_t ret = count; + struct ps3_instance *instance = + ps3_debug_instance_query(cdev, attr, buf); + + if (instance == NULL) + goto l_out; + + if (kstrtou8(buf, 10, &halt_support_cli)) { + ret = -EINVAL; + LOG_ERROR("invalid io halt_support_cli, could not set\n"); + goto l_out; + } + + is_halt_support_cli = (halt_support_cli == 0) ? (0) : (1); + instance->is_halt_support_cli = is_halt_support_cli; + + LOG_WARN("set halt_support_cli is %d\n", instance->is_halt_support_cli); + +l_out: + return ret; +} + +#if defined(PS3_SUPPORT_DEBUG) || \ + (defined(PS3_CFG_RELEASE) && defined(PS3_CFG_OCM_DBGBUG)) || \ + (defined(PS3_CFG_RELEASE) && defined(PS3_CFG_OCM_RELEASE)) +#else +ssize_t ps3_irq_prk_support_store(struct device *cdev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int is_prk_in_irq = 0; + ssize_t ret = count; + struct ps3_instance *instance = + ps3_debug_instance_query(cdev, attr, buf); + + if (cdev == NULL || buf == NULL) + goto l_out; + + if (kstrtoint(buf, 10, &is_prk_in_irq)) { + LOG_ERROR( + "invalid is_prk_in_irq, could not set is_prk_in_irq\n"); + ret = -EINVAL; + goto l_out; + } + + LOG_WARN("set is_prk_in_irq %d\n", is_prk_in_irq); + + instance->is_irq_prk_support = is_prk_in_irq; +l_out: + return ret; +} + +ssize_t ps3_irq_prk_support_show(struct device *cdev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret = 0; + struct ps3_instance *instance = + ps3_debug_instance_query(cdev, attr, buf); + + if (cdev == NULL || buf == NULL) + goto l_out; + ret = snprintf(buf, PAGE_SIZE, "%d\n", instance->is_irq_prk_support); + + LOG_DEBUG("get log level to %d\n", instance->is_irq_prk_support); +l_out: + return ret; +} +#endif + +ssize_t ps3_qos_switch_show(struct device *cdev, struct device_attribute *attr, + char *buf) +{ + ssize_t ret = 0; + struct ps3_instance *instance = + ps3_debug_instance_query(cdev, attr, buf); + + if (instance == NULL) { + ret = 0; + goto l_out; + } + + ret = snprintf(buf, PAGE_SIZE, "%d\n", + instance->qos_context.qos_switch); + +l_out: + return ret; +} + +ssize_t ps3_qos_switch_store(struct device *cdev, struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned char qos_switch = 0; + ssize_t ret = count; + struct ps3_instance *instance = + ps3_debug_instance_query(cdev, attr, buf); + + if (instance == NULL) { + ret = count; + goto l_out; + } + + if (kstrtou8(buf, 10, &qos_switch)) { + LOG_ERROR("invalid io trace switch, could not set\n"); + ret = count; + goto l_out; + } + + if (instance->qos_context.qos_switch > 0 && qos_switch == 0) { + instance->qos_context.qos_switch = 0; + ps3_qos_close(instance); + } + if (instance->qos_context.qos_switch == 0 && qos_switch > 0) { + ps3_qos_open(instance); + instance->qos_context.qos_switch = qos_switch; + } + + LOG_WARN("set qos switch is %d\n", instance->qos_context.qos_switch); + +l_out: + return ret; +} + +#endif diff --git a/drivers/scsi/linkdata/ps3stor/ps3_debug.h b/drivers/scsi/linkdata/ps3stor/ps3_debug.h new file mode 100644 index 0000000000000000000000000000000000000000..d92a26a62cb7ace75258d0f5b1ff96e1654f6d48 --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/ps3_debug.h @@ -0,0 +1,152 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _PS3_DEBUG_H_ +#define _PS3_DEBUG_H_ + +#ifndef _WINDOWS +#include +#include +#include + +struct ps3_reg_dump_attr { + unsigned long long read_dump_timestamp; + unsigned long long write_dump_timestamp; + unsigned long long read_dump_interval_ms; + unsigned long long write_dump_interval_ms; + unsigned long long lastest_value; + char name[32]; +}; + +struct ps3_debug_context { + unsigned char io_trace_switch; + unsigned char reserved[7]; + struct ps3_reg_dump_attr + reg_dump[PS3_REGISTER_SET_SIZE / sizeof(unsigned long long)]; + struct Ps3DebugMemEntry debug_mem_vaddr[PS3_DEBUG_MEM_ARRAY_MAX_NUM]; + struct Ps3DebugMemEntry *debug_mem_buf; + dma_addr_t debug_mem_buf_phy; + unsigned int debug_mem_array_num; + unsigned char reserved1[4]; +}; + +void ps3_debug_context_init(struct ps3_instance *instance); + +void ps3_reg_dump(struct ps3_instance *instance, void __iomem *reg, + unsigned long long value, unsigned char is_read); + +ssize_t ps3_vd_io_outstanding_show(struct device *cdev, + struct device_attribute *attr, char *buf); + +ssize_t ps3_io_outstanding_show(struct device *cdev, + struct device_attribute *attr, char *buf); + +ssize_t ps3_is_load_show(struct device *cdev, struct device_attribute *attr, + char *buf); + +ssize_t ps3_dump_ioc_regs_show(struct device *cdev, + struct device_attribute *attr, char *buf); + +ssize_t ps3_max_scsi_cmds_show(struct device *cdev, + struct device_attribute *attr, char *buf); + +ssize_t ps3_event_subscribe_info_show(struct device *cdev, + struct device_attribute *attr, char *buf); + +ssize_t ps3_ioc_state_show(struct device *cdev, struct device_attribute *attr, + char *buf); + +ssize_t ps3_log_level_store(struct device *cdev, struct device_attribute *attr, + const char *buf, size_t count); +ssize_t ps3_log_level_show(struct device *cdev, struct device_attribute *attr, + char *buf); + +ssize_t ps3_io_trace_switch_store(struct device *cdev, + struct device_attribute *attr, + const char *buf, size_t count); + +ssize_t ps3_io_trace_switch_show(struct device *cdev, + struct device_attribute *attr, char *buf); +ssize_t ps3_halt_support_cli_show(struct device *cdev, + struct device_attribute *attr, char *buf); +ssize_t ps3_halt_support_cli_store(struct device *cdev, + struct device_attribute *attr, + const char *buf, size_t count); + +#if defined(PS3_SUPPORT_DEBUG) || \ + (defined(PS3_CFG_RELEASE) && defined(PS3_CFG_OCM_DBGBUG)) || \ + (defined(PS3_CFG_RELEASE) && defined(PS3_CFG_OCM_RELEASE)) +#else +ssize_t ps3_irq_prk_support_store(struct device *cdev, + struct device_attribute *attr, + const char *buf, size_t count); + +ssize_t ps3_irq_prk_support_show(struct device *cdev, + struct device_attribute *attr, char *buf); + +#endif + +ssize_t ps3_product_model_show(struct device *cdev, + struct device_attribute *attr, char *buf); + +ssize_t ps3_qos_switch_show(struct device *cdev, struct device_attribute *attr, + char *buf); + +ssize_t ps3_qos_switch_store(struct device *cdev, struct device_attribute *attr, + const char *buf, size_t count); + +#endif +ssize_t ps3_event_subscribe_info_get(struct ps3_instance *instance, char *buf, + ssize_t total_len); + +#ifndef _WINDOWS + +int ps3_debug_mem_alloc(struct ps3_instance *ins); +int ps3_debug_mem_free(struct ps3_instance *ins); +ssize_t ps3_dump_state_show(struct device *cdev, struct device_attribute *attr, + char *buf); +ssize_t ps3_dump_state_store(struct device *cdev, struct device_attribute *attr, + const char *buf, size_t count); +ssize_t ps3_dump_type_show(struct device *cdev, struct device_attribute *attr, + char *buf); + +ssize_t ps3_dump_type_store(struct device *cdev, struct device_attribute *attr, + const char *buf, size_t count); + +ssize_t ps3_dump_dir_show(struct device *cdev, struct device_attribute *attr, + char *buf); + +void ps3_dma_dump_mapping(struct pci_dev *pdev); + +ssize_t ps3_soc_dead_reset_store(struct device *cdev, + struct device_attribute *attr, const char *buf, + size_t count); +#else + +struct ps3_reg_dump_attr { + unsigned long long read_dump_timestamp; + unsigned long long write_dump_timestamp; + unsigned long long read_dump_interval_ms; + unsigned long long write_dump_interval_ms; + unsigned long long lastest_value; + char name[32]; +}; + +struct ps3_debug_context { + unsigned char io_trace_switch; + unsigned char reserved[7]; + struct ps3_reg_dump_attr + reg_dump[PS3_REGISTER_SET_SIZE / sizeof(unsigned long long)]; + struct Ps3DebugMemEntry debug_mem_vaddr[PS3_DEBUG_MEM_ARRAY_MAX_NUM]; + struct Ps3DebugMemEntry *debug_mem_buf; + dma_addr_t debug_mem_buf_phy; + unsigned int debug_mem_array_num; + unsigned char reserved1[4]; +}; + +void ps3_debug_context_init(struct ps3_instance *instance); + +void ps3_reg_dump(struct ps3_instance *instance, void __iomem *reg, + unsigned long long value, unsigned char is_read); + +#endif + +#endif diff --git a/drivers/scsi/linkdata/ps3stor/ps3_device_manager.c b/drivers/scsi/linkdata/ps3stor/ps3_device_manager.c new file mode 100644 index 0000000000000000000000000000000000000000..d3c5fe80676253033a8be2b499cc5334f7654cfe --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/ps3_device_manager.c @@ -0,0 +1,2270 @@ +// SPDX-License-Identifier: GPL-2.0 +#ifndef _WINDOWS +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#endif + +#include "ps3_device_manager.h" +#include "ps3_cmd_statistics.h" +#include "ps3_mgr_cmd.h" +#include "ps3_ioc_manager.h" +#include "ps3_util.h" +#include "ps3_device_update.h" +#include "ps3_module_para.h" +#include "ps3_r1x_write_lock.h" +#include "ps3_qos.h" + +static void ps3_nvme_attr_set(const struct ps3_instance *instance, + struct scsi_device *sdev); + +static int ps3_dev_channel_init(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + unsigned char i = 0; + unsigned short vd_start = 0; + unsigned short pd_start = 0; + struct PS3ChannelInfo *channel_info = &instance->ctrl_info.channelInfo; + struct ps3_channel *pd_chan = instance->dev_context.channel_pd; + struct ps3_channel *vd_chan = instance->dev_context.channel_vd; + + memset(pd_chan, 0, sizeof(struct ps3_channel) * PS3_MAX_CHANNEL_NUM); + memset(vd_chan, 0, sizeof(struct ps3_channel) * PS3_MAX_CHANNEL_NUM); + + instance->dev_context.pd_channel_count = 0; + instance->dev_context.vd_channel_count = 0; + instance->dev_context.max_dev_per_channel = 0; + ps3_atomic_set(&instance->dev_context.abort_vdpending_cmd, 0); + + for (i = 0; i < channel_info->channelNum; i++) { + LOG_INFO("hno:%u channel[%u] is type %s,max dev num is:%u\n", + PS3_HOST(instance), i, + namePS3ChannelType( + (enum PS3ChannelType)channel_info->channels[i] + .channelType), + channel_info->channels[i].maxDevNum); + + if (channel_info->channels[i].maxDevNum > + instance->dev_context.max_dev_per_channel) { + instance->dev_context.max_dev_per_channel = + channel_info->channels[i].maxDevNum; + } + + if (channel_info->channels[i].channelType == PS3_CHAN_TYPE_VD) { + vd_chan->channel = i; + vd_chan->max_dev_num = + channel_info->channels[i].maxDevNum; + vd_chan->channel_start_num = vd_start; + vd_start += vd_chan->max_dev_num; + instance->dev_context.max_dev_in_channel[i] = + vd_chan->max_dev_num; + + vd_chan++; + instance->dev_context.vd_channel_count++; + } else if (channel_info->channels[i].channelType == + PS3_CHAN_TYPE_PD) { + pd_chan->channel = i; + pd_chan->max_dev_num = + channel_info->channels[i].maxDevNum; + pd_chan->channel_start_num = pd_start; + pd_start += pd_chan->max_dev_num; + instance->dev_context.max_dev_in_channel[i] = + pd_chan->max_dev_num; + pd_chan++; + instance->dev_context.pd_channel_count++; + } + } + + if (instance->dev_context.max_dev_per_channel == 0) { + LOG_WARN("hno:%u total dev in channel == 0\n", + PS3_HOST(instance)); + ret = -PS3_FAILED; + } + + return ret; +} + +static void ps3_dev_buff_release(struct ps3_instance *instance) +{ + struct ps3_dev_context *p_dev_ctx = &instance->dev_context; + unsigned char i = 0; + + LOG_INFO("hno:%u release all device mgr buffer\n", PS3_HOST(instance)); + + if (p_dev_ctx->pd_pool.devs_buffer != NULL) { + ps3_kfree(instance, p_dev_ctx->pd_pool.devs_buffer); + p_dev_ctx->pd_pool.devs_buffer = NULL; + } + + if (p_dev_ctx->vd_pri_data_table.vd_pri_data_idxs_array != NULL) { + ps3_vfree(instance, + p_dev_ctx->vd_pri_data_table.vd_pri_data_idxs_array); + p_dev_ctx->vd_pri_data_table.vd_pri_data_idxs_array = NULL; + } + + if (p_dev_ctx->vd_pool.devs_buffer != NULL) { + ps3_kfree(instance, p_dev_ctx->vd_pool.devs_buffer); + p_dev_ctx->vd_pool.devs_buffer = NULL; + } + + if (p_dev_ctx->pd_table.pd_idxs_array != NULL) { + ps3_kfree(instance, p_dev_ctx->pd_table.pd_idxs_array); + p_dev_ctx->pd_table.pd_idxs_array = NULL; + } + + if (p_dev_ctx->pd_entries_array != NULL) { + ps3_kfree(instance, p_dev_ctx->pd_entries_array); + p_dev_ctx->pd_entries_array = NULL; + } + + for (i = 0; i < PS3_VD_TABLE_NUM; i++) { + if (p_dev_ctx->vd_table[i].vd_idxs_array != NULL) { + ps3_kfree(instance, + p_dev_ctx->vd_table[i].vd_idxs_array); + p_dev_ctx->vd_table[i].vd_idxs_array = NULL; + } + + if (p_dev_ctx->vd_entries_array[i] != NULL) { + ps3_kfree(instance, p_dev_ctx->vd_entries_array[i]); + p_dev_ctx->vd_entries_array[i] = NULL; + } + } +} + +static int ps3_vd_buff_alloc(struct ps3_instance *instance) +{ + unsigned char i = 0; + unsigned char j = 0; + struct ps3_channel *p_chan = NULL; + struct ps3_dev_context *p_dev_ctx = &instance->dev_context; + struct ps3_dev_pool *vd_pool = &p_dev_ctx->vd_pool; + struct ps3_vd_table *p_vd_table = p_dev_ctx->vd_table; + struct ps3_pri_data_table *p_vd_pri_data_table = + &p_dev_ctx->vd_pri_data_table; + + ps3_atomic_set(&p_dev_ctx->subwork, 0); + p_dev_ctx->total_vd_count = 0; + if (p_dev_ctx->vd_channel_count > 0) { + p_chan = + &p_dev_ctx->channel_vd[p_dev_ctx->vd_channel_count - 1]; + p_dev_ctx->total_vd_count = + p_chan->channel_start_num + p_chan->max_dev_num; + } + + for (i = 0; i < PS3_VD_TABLE_NUM; i++) { + p_vd_table[i].vd_idxs_array = (unsigned short *)ps3_kzalloc( + instance, + sizeof(unsigned short) * p_dev_ctx->total_vd_count); + if (p_vd_table[i].vd_idxs_array == NULL) { + LOG_ERROR( + "hno:%u, Failed to allocate VD table %d buffer\n", + PS3_HOST(instance), i); + goto free_dev_buff; + } + + p_dev_ctx->vd_entries_array[i] = + (struct PS3VDEntry *)ps3_kzalloc( + instance, + sizeof(struct PS3VDEntry) * + (PS3_MAX_VD_COUNT(instance) + 1)); + if (p_dev_ctx->vd_entries_array[i] == NULL) { + LOG_ERROR( + "hno:%u, Failed to allocate VD entry buffer\n", + PS3_HOST(instance)); + goto free_dev_buff; + } + } + + vd_pool->devs_buffer = (union PS3Device *)ps3_kzalloc( + instance, sizeof(union PS3Device) * p_dev_ctx->total_vd_count); + if (vd_pool->devs_buffer == NULL) { + LOG_ERROR("hno:%u Failed to allocate VD pool buffer\n", + PS3_HOST(instance)); + goto free_dev_buff; + } + + memset(vd_pool->devs_buffer, 0, + sizeof(union PS3Device) * p_dev_ctx->total_vd_count); + + p_vd_pri_data_table->vd_pri_data_idxs_array = + (struct ps3_scsi_priv_data **)ps3_vzalloc( + instance, sizeof(struct ps3_scsi_priv_data *) * + p_dev_ctx->total_vd_count); + if (p_vd_pri_data_table->vd_pri_data_idxs_array == NULL) { + LOG_ERROR("hno:%u, Failed to allocate VD R1X table %d buffer\n", + PS3_HOST(instance), i); + goto free_dev_buff; + } + + memset(p_vd_pri_data_table->vd_pri_data_idxs_array, 0, + sizeof(struct ps3_scsi_priv_data *) * p_dev_ctx->total_vd_count); + + p_chan = p_dev_ctx->channel_vd; + for (i = 0; i < p_dev_ctx->vd_channel_count; i++) { + vd_pool->devs[p_chan->channel] = + &vd_pool->devs_buffer[p_chan->channel_start_num]; + + p_vd_pri_data_table->vd_pri_data_idxs[p_chan->channel] = + &p_vd_pri_data_table->vd_pri_data_idxs_array + [p_chan->channel_start_num]; + + for (j = 0; j < PS3_VD_TABLE_NUM; j++) { + p_vd_table[j].vd_idxs[p_chan->channel] = + &p_vd_table[j].vd_idxs_array + [p_chan->channel_start_num]; + } + + p_chan++; + } + + return PS3_SUCCESS; +free_dev_buff: + ps3_dev_buff_release(instance); + + return -PS3_ENOMEM; +} + +static int ps3_pd_buff_alloc(struct ps3_instance *instance) +{ + unsigned char i = 0; + struct ps3_channel *p_chan = NULL; + struct ps3_dev_context *p_dev_ctx = &instance->dev_context; + struct ps3_dev_pool *pd_pool = &p_dev_ctx->pd_pool; + struct ps3_pd_table *pd_table = &p_dev_ctx->pd_table; + + p_dev_ctx->total_pd_count = 0; + if (p_dev_ctx->pd_channel_count > 0) { + p_chan = + &p_dev_ctx->channel_pd[p_dev_ctx->pd_channel_count - 1]; + p_dev_ctx->total_pd_count = + p_chan->channel_start_num + p_chan->max_dev_num; + } + + p_dev_ctx->pd_entries_array = (struct ps3_pd_entry *)ps3_kzalloc( + instance, + sizeof(struct ps3_pd_entry) * (PS3_MAX_PD_COUNT(instance) + 1)); + if (p_dev_ctx->pd_entries_array == NULL) { + LOG_ERROR("hno:%u, Failed to allocate PD entry buffer\n", + PS3_HOST(instance)); + goto free_dev_pool; + } + + pd_table->pd_idxs_array = (unsigned short *)ps3_kzalloc( + instance, sizeof(unsigned short) * p_dev_ctx->total_pd_count); + if (pd_table->pd_idxs_array == NULL) { + LOG_ERROR("hno:%u, Failed to allocate PD table buffer\n", + PS3_HOST(instance)); + goto free_dev_pool; + } + + pd_pool->devs_buffer = (union PS3Device *)ps3_kzalloc( + instance, sizeof(union PS3Device) * p_dev_ctx->total_pd_count); + if (pd_pool->devs_buffer == NULL) { + LOG_ERROR("hno:%u, Failed to allocate PD pool buffer\n", + PS3_HOST(instance)); + goto free_dev_pool; + } + + p_chan = p_dev_ctx->channel_pd; + for (i = 0; i < p_dev_ctx->pd_channel_count; i++) { + pd_pool->devs[p_chan->channel] = + &pd_pool->devs_buffer[p_chan->channel_start_num]; + pd_table->pd_idxs[p_chan->channel] = + &pd_table->pd_idxs_array[p_chan->channel_start_num]; + p_chan++; + } + + return PS3_SUCCESS; +free_dev_pool: + ps3_dev_buff_release(instance); + + return -PS3_ENOMEM; +} + +static int ps3_dev_buff_alloc(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + + LOG_INFO("hno:%u buff alloc pd count %d, vd count %d\n", + PS3_HOST(instance), PS3_MAX_PD_COUNT(instance), + PS3_MAX_VD_COUNT(instance)); + + if (PS3_MAX_VD_COUNT(instance) > 0) { + ret = ps3_vd_buff_alloc(instance); + if (ret != PS3_SUCCESS) + goto l_out; + } + + if (PS3_MAX_PD_COUNT(instance) > 0) { + ret = ps3_pd_buff_alloc(instance); + if (ret != PS3_SUCCESS) + goto l_out; + } + +l_out: + return ret; +} + +static inline unsigned short +ps3_pd_dma_alignment_calc(unsigned char align_shift) +{ + return align_shift ? 1 << align_shift : 0; +} + +static inline unsigned char ps3_pd_type_localed(const struct PS3PDInfo *pd_info) +{ + unsigned char dev_type = PS3_DEV_TYPE_UNKNOWN; + + dev_type = ps3_get_converted_dev_type(pd_info->driverType, + pd_info->mediumType); + + if (unlikely(dev_type == PS3_DEV_TYPE_UNKNOWN)) { + LOG_ERROR( + "pd[%u:%u:%u], magic_num[%#x], driver_type[%s], medium_type[%s], dev_type is unknown\n", + PS3_CHANNEL(&pd_info->diskPos), + PS3_TARGET(&pd_info->diskPos), + PS3_PDID(&pd_info->diskPos), + pd_info->diskPos.diskMagicNum, + getDriverTypeName((enum DriverType)pd_info->driverType), + getMediumTypeName( + (enum MediumType)pd_info->mediumType)); + PS3_BUG(); + } else { + LOG_INFO( + "pd[%u:%u:%u], magic_num[%#x], driver_type[%s], medium_type[%s], dev_type[%s]\n", + PS3_CHANNEL(&pd_info->diskPos), + PS3_TARGET(&pd_info->diskPos), + PS3_PDID(&pd_info->diskPos), + pd_info->diskPos.diskMagicNum, + getDriverTypeName((enum DriverType)pd_info->driverType), + getMediumTypeName((enum MediumType)pd_info->mediumType), + namePS3DevType((enum PS3DevType)dev_type)); + } + return dev_type; +} + +void ps3_vd_info_show(const struct ps3_instance *instance, + const struct PS3VDEntry *vd_entry) +{ + unsigned char i = 0; + unsigned char j = 0; + unsigned char channel = (unsigned char)PS3_CHANNEL(&vd_entry->diskPos); + unsigned short target_id = PS3_TARGET(&vd_entry->diskPos); + unsigned short vd_id = PS3_VDID(&vd_entry->diskPos); + + LOG_INFO_IN_IRQ( + instance, + "hno:%u disk detail info - vd[%u:%u:%u], magicNum[%#x]\n" + "\tdev_type: PS3_DEV_TYPE_VD, isHidden[%s]\n" + "\taccessPolicy[%s], diskGrpId[%u], sectorSize[%u]\n" + "\tstripeDataSize[%u], physDrvCnt[%u], stripSize[%u]\n" + "\tisDirectEnable[%s], raidLevel[%u], spanCount[%u]\n" + "\tdiskState[%u], startLBA[%llu], extentSize[%llu]\n" + "\tisTaskMgmtEnable[%u], taskAbortTimeout[%u]\n" + "\ttaskResetTimeout[%u], mapBlock[%llu], mapBlockVer[%u]\n" + "\tmaxIOSize[%u], devQueDepth[%u], capacity[%llu], isNvme[%u], isSsd[%u]\n" + "\tvirtDiskSeq[%d] bdev_bdi_cap[%d], umapBlkDescCnt[%d], umapNumblk[%d]\n" + "\tnormalQuota[%u], directQuota[%u] dev_busy_scale[%u]\n", + PS3_HOST(instance), channel, target_id, vd_id, + vd_entry->diskPos.diskMagicNum, + (vd_entry->isHidden) ? "true" : "false", + ps3_get_vd_access_plolicy_str( + (enum VDAccessPolicy)vd_entry->accessPolicy), + vd_entry->diskGrpId, vd_entry->sectorSize, + vd_entry->stripeDataSize, vd_entry->physDrvCnt, + vd_entry->stripSize, + (vd_entry->isDirectEnable) ? "true" : "false", + vd_entry->raidLevel, vd_entry->spanCount, vd_entry->diskState, + vd_entry->startLBA, vd_entry->extentSize, + vd_entry->isTaskMgmtEnable, vd_entry->taskAbortTimeout, + vd_entry->taskResetTimeout, vd_entry->mapBlock, + vd_entry->mapBlockVer, vd_entry->maxIOSize, + vd_entry->devQueDepth, vd_entry->capacity, vd_entry->isNvme, + vd_entry->isSsd, vd_entry->virtDiskSeq, vd_entry->bdev_bdi_cap, + vd_entry->umapBlkDescCnt, vd_entry->umapNumblk, + vd_entry->normalQuota, vd_entry->directQuota, + vd_entry->dev_busy_scale); + + for (i = 0; i < vd_entry->spanCount; i++) { + LOG_INFO_IN_IRQ( + instance, + "hno:%u vd[%u:%u:%u] span[%u] stripeDataSize[%u], state[%u], pdNum[%u]\n", + PS3_HOST(instance), channel, target_id, vd_id, i, + vd_entry->span[i].spanStripeDataSize, + vd_entry->span[i].spanState, + vd_entry->span[i].spanPdNum); + + for (j = 0; j < vd_entry->span[i].spanPdNum; j++) { + LOG_INFO_IN_IRQ( + instance, + "hno:%u vd[%u:%u:%u] span[%u] ext[%u] pd:[%u:%u:%u], state[%u]\n", + PS3_HOST(instance), channel, target_id, vd_id, + i, j, + vd_entry->span[i] + .extent[j] + .phyDiskID.ps3Dev.softChan, + vd_entry->span[i] + .extent[j] + .phyDiskID.ps3Dev.devID, + vd_entry->span[i] + .extent[j] + .phyDiskID.ps3Dev.phyDiskID, + vd_entry->span[i].extent[j].state); + } + } +} + +static void ps3_pd_info_show(const struct ps3_instance *instance, + const struct ps3_pd_entry *pd_entry) +{ + LOG_INFO("hno:%u disk detail info - pd[%u:%u:%u], magicNum[%#x]\n" + "\tstate[%s], dev_type[%s], config_flag[%s], pd_flags[0x%02x]\n" + "\tRWCT[%u], scsi_interface_type[%u]\n" + "\ttask_abort_timeout[%u], task_reset_timeout[%u]\n" + "\tmax_io_size[%u], dev_queue_depth[%u]\n" + "\tsector_size[%u], encl_id[%u], pd phy_id[%u]\n" + "\tdma_addr_alignment[%u], dma_len_alignment[%u]\n" + "\tnormal_quota[%u] direct_quota[%u] is_direct_disable[%u]\n", + PS3_HOST(instance), PS3_CHANNEL(&pd_entry->disk_pos), + PS3_TARGET(&pd_entry->disk_pos), PS3_PDID(&pd_entry->disk_pos), + pd_entry->disk_pos.diskMagicNum, + getDeviceStateName((enum DeviceState)pd_entry->state), + namePS3DevType((enum PS3DevType)pd_entry->dev_type), + getPdStateName((enum MicPdState)pd_entry->config_flag, + instance->is_raid), + pd_entry->pd_flags, pd_entry->RWCT, + pd_entry->scsi_interface_type, pd_entry->task_abort_timeout, + pd_entry->task_reset_timeout, pd_entry->max_io_size, + pd_entry->dev_queue_depth, pd_entry->sector_size, + pd_entry->encl_id, pd_entry->phy_id, + pd_entry->dma_addr_alignment, pd_entry->dma_len_alignment, + pd_entry->normal_quota, pd_entry->direct_quota, + pd_entry->is_direct_disable); +} + +static inline void ps3_pd_info_localed(const struct ps3_instance *instance, + struct ps3_pd_entry *local_entry, + const struct PS3PDInfo *pd_info) +{ + local_entry->disk_pos = pd_info->diskPos; + local_entry->dev_type = ps3_pd_type_localed(pd_info); + local_entry->scsi_interface_type = pd_info->scsiInterfaceType; + local_entry->state = pd_info->diskState; + local_entry->task_abort_timeout = pd_info->taskAbortTimeout; + local_entry->task_reset_timeout = pd_info->taskResetTimeout; + local_entry->config_flag = pd_info->configFlag; + local_entry->pd_flags = pd_info->pdFlags; + local_entry->max_io_size = pd_info->maxIOSize; + local_entry->dev_queue_depth = pd_info->devQueDepth; + local_entry->sector_size = pd_info->sectorSize; + local_entry->is_direct_disable = pd_info->isDirectDisable; + local_entry->encl_id = pd_info->enclId; + local_entry->phy_id = pd_info->phyId; + local_entry->dma_addr_alignment = + ps3_pd_dma_alignment_calc(pd_info->dmaAddrAlignShift); + local_entry->dma_len_alignment = + ps3_pd_dma_alignment_calc(pd_info->dmaLenAlignShift); + local_entry->normal_quota = pd_info->normalQuota; + local_entry->direct_quota = pd_info->directQuota; + + if (unlikely(local_entry->sector_size == 0)) { + LOG_WARN("pd[%u:%u] sector_size is 0\n", + PS3_CHANNEL(&local_entry->disk_pos), + PS3_TARGET(&local_entry->disk_pos)); + } + + ps3_pd_info_show(instance, local_entry); +} + +int ps3_dev_mgr_pd_info_get(struct ps3_instance *instance, + unsigned short channel, unsigned short target_id, + unsigned short pd_id) +{ + int ret = PS3_SUCCESS; + struct PS3PDInfo *pd_info = NULL; + struct ps3_dev_context *dev_ctx = &instance->dev_context; + + LOG_DEBUG("hno:%u, get PD info [%u:%u:%u] start\n", PS3_HOST(instance), + channel, target_id, pd_id); + + ret = ps3_pd_info_get(instance, channel, target_id, pd_id); + if (ret != PS3_SUCCESS) { + LOG_ERROR("hno:%u, get single PD info [%u:%u:%u] NOK\n", + PS3_HOST(instance), channel, target_id, pd_id); + goto l_out; + } + + pd_info = dev_ctx->pd_info_buf; + if (PS3_PDID_INVALID(&pd_info->diskPos)) { + LOG_WARN("hno:%u, cannot found PD info [%u:%u:%u]\n", + PS3_HOST(instance), channel, target_id, pd_id); + ret = -PS3_FAILED; + goto l_out; + } + + if (pd_info->dmaAddrAlignShift > PS3_DMA_ALIGN_SHIFT_MAX) { + LOG_WARN( + "hno:%u PD info [%u:%u:%u] invalid dmaAddrAlignShift\n", + PS3_HOST(instance), channel, target_id, + PS3_PDID(&pd_info->diskPos)); + ret = -PS3_FAILED; + goto l_out; + } + if (pd_info->dmaLenAlignShift > PS3_DMA_ALIGN_SHIFT_MAX) { + LOG_WARN("hno:%u PD info [%u:%u:%u] invalid dmalenAlignShift\n", + PS3_HOST(instance), channel, target_id, + PS3_PDID(&pd_info->diskPos)); + ret = -PS3_FAILED; + goto l_out; + } + if (unlikely(PS3_CHANNEL(&pd_info->diskPos) != channel || + PS3_TARGET(&pd_info->diskPos) != target_id)) { + LOG_ERROR( + "hno:%u PD info get [%u:%u:%u]!=[%u:%u:%u] magic[%#x] unmatched\n", + PS3_HOST(instance), channel, target_id, pd_id, + PS3_CHANNEL(&pd_info->diskPos), + PS3_TARGET(&pd_info->diskPos), + PS3_PDID(&pd_info->diskPos), + pd_info->diskPos.diskMagicNum); + PS3_BUG(); + ret = -PS3_FAILED; + goto l_out; + } + + if (unlikely(PS3_PDID(&pd_info->diskPos) > + PS3_MAX_PD_COUNT(instance))) { + LOG_ERROR("hno:%u init pd info NOK, pd_id[%d] > max[%d]\n", + PS3_HOST(instance), PS3_PDID(&pd_info->diskPos), + PS3_MAX_PD_COUNT(instance)); + + PS3_BUG(); + ret = -PS3_FAILED; + goto l_out; + } + + ps3_pd_info_localed( + instance, + &dev_ctx->pd_entries_array[PS3_PDID(&pd_info->diskPos)], + pd_info); + dev_ctx->pd_table.pd_idxs[channel][target_id] = + PS3_PDID(&pd_info->diskPos); +l_out: + return ret; +} + +static void ps3_pd_info_get_all(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + unsigned short i = 0; + unsigned short j = 0; + unsigned char chan_id = 0; + + struct ps3_channel *pd_chan = instance->dev_context.channel_pd; + struct ps3_dev_context *p_dev_ctx = &instance->dev_context; + struct ps3_pd_table *p_table = &p_dev_ctx->pd_table; + struct ps3_dev_pool *p_pd_pool = &p_dev_ctx->pd_pool; + + memset(p_table->pd_idxs_array, PS3_INVALID_VALUE, + p_dev_ctx->total_pd_count * sizeof(unsigned short)); + + for (i = 0; i < p_dev_ctx->pd_channel_count; i++) { + chan_id = pd_chan[i].channel; + for (j = 0; j < pd_chan[i].max_dev_num; j++) { + if (PS3_PDID_INVALID( + &p_pd_pool->devs[chan_id][j].pd.diskPos)) { + continue; + } + + ret = ps3_dev_mgr_pd_info_get( + instance, chan_id, j, + PS3_PDID(&p_pd_pool->devs[chan_id][j] + .pd.diskPos)); + if (ret != PS3_SUCCESS) { + LOG_ERROR("hno:%u, get PD[%u:%u] info NOK\n", + PS3_HOST(instance), chan_id, j); + } + } + } +} + +int ps3_dev_mgr_vd_info_subscribe(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + + LOG_FILE_INFO("hno:%u %s\n", + PS3_HOST(instance), __func__); + + if (PS3_MAX_VD_COUNT(instance) <= 0) + goto l_out; + + ret = ps3_vd_info_async_get(instance); + if (ret != PS3_SUCCESS) { + LOG_FILE_ERROR("hno:%u async get VD info failed\n", + PS3_HOST(instance)); + } +l_out: + return ret; +} + +int ps3_dev_mgr_vd_info_unsubscribe(struct ps3_instance *instance) +{ + unsigned long flags = 0; + int ret = PS3_SUCCESS; + struct ps3_cmd *cmd = NULL; + struct ps3_cmd *abort_cmd = NULL; + + if (!ps3_check_ioc_state_is_normal_in_unload(instance)) { + ret = -PS3_FAILED; + goto l_out; + } + + cmd = instance->dev_context.vd_pending_cmd; + if (cmd == NULL) { + LOG_WARN("hno:%u vd pending cmd has been cancel\n", + PS3_HOST(instance)); + ret = PS3_SUCCESS; + goto l_out; + } + + ps3_spin_lock_irqsave(&cmd->cmd_state.lock, &flags); + if (cmd->cmd_state.state == PS3_CMD_STATE_INIT) { + ps3_spin_unlock_irqrestore(&cmd->cmd_state.lock, flags); + LOG_WARN("hno:%u who steal free this cmd,CFID:%d\n", + PS3_HOST(instance), cmd->index); + instance->dev_context.vd_pending_cmd = NULL; + goto l_out; + } else { + ps3_spin_unlock_irqrestore(&cmd->cmd_state.lock, flags); + } + + if (ps3_atomic_add_unless(&instance->dev_context.is_vdpending_abort, 1, + 1) == 0) { + ret = PS3_SUCCESS; + goto l_out; + } + ret = ps3_mgr_cmd_cancel_send(instance, cmd->index, + PS3_CANCEL_VDPENDING_CMD); + if (ret == -PS3_ENOMEM) { + LOG_INFO("hno:%u alloc failed\n", PS3_HOST(instance)); + ret = PS3_FAILED; + ps3_atomic_set(&instance->dev_context.is_vdpending_abort, 0); + goto l_out; + } else if (ret != PS3_SUCCESS) { + LOG_INFO("hno:%u reqFrameId=%d cancel_cmd_frame_id[%u] free!\n", + PS3_HOST(instance), + ps3_cmd_frame_id( + instance->dev_context.vdpending_abort_cmd), + cmd->index); + abort_cmd = instance->dev_context.vdpending_abort_cmd; + instance->dev_context.vdpending_abort_cmd = NULL; + if (abort_cmd != NULL) + ps3_task_cmd_free(instance, abort_cmd); + ret = PS3_FAILED; + ps3_atomic_set(&instance->dev_context.is_vdpending_abort, 0); + goto l_out; + } + + ret = ps3_mgr_cmd_cancel_wait(instance, PS3_CANCEL_VDPENDING_CMD); + if (ret != PS3_SUCCESS) { + LOG_ERROR("hno:%u unsubscribe vd pending, cancel cmd NOK\n", + PS3_HOST(instance)); + ps3_atomic_set(&instance->dev_context.is_vdpending_abort, 0); + goto l_out; + } + + LOG_INFO("hno:%u vd pending cmd free, CFID:%d\n", PS3_HOST(instance), + cmd->index); + instance->dev_context.vd_pending_cmd = NULL; + ps3_mgr_cmd_free(instance, cmd); + ps3_atomic_set(&instance->dev_context.is_vdpending_abort, 0); + +l_out: + return ret; +} +int ps3_dev_mgr_vd_info_resubscribe(struct ps3_instance *instance) +{ + unsigned long flags = 0; + int ret = PS3_SUCCESS; + struct ps3_cmd *cmd = NULL; + struct ps3_cmd *abort_cmd = NULL; + unsigned long flags1 = 0; + unsigned char is_need_resend = PS3_FALSE; + + if (!ps3_check_ioc_state_is_normal_in_unload(instance)) { + ret = -PS3_FAILED; + goto l_out; + } + + cmd = instance->dev_context.vd_pending_cmd; + if (cmd == NULL) { + LOG_WARN("hno:%u vd pending cmd has been cancel\n", + PS3_HOST(instance)); + is_need_resend = PS3_TRUE; + ps3_spin_lock_irqsave( + &instance->recovery_context->recovery_lock, &flags1); + goto l_resend; + } + + ps3_spin_lock_irqsave(&cmd->cmd_state.lock, &flags); + if (cmd->cmd_state.state == PS3_CMD_STATE_INIT) { + ps3_spin_unlock_irqrestore(&cmd->cmd_state.lock, flags); + LOG_WARN("hno:%u free vdpending cmd,CFID:%d\n", + PS3_HOST(instance), cmd->index); + instance->dev_context.vd_pending_cmd = NULL; + goto l_out; + } else { + ps3_spin_unlock_irqrestore(&cmd->cmd_state.lock, flags); + } + + if (ps3_atomic_add_unless(&instance->dev_context.is_vdpending_abort, 1, + 1) == 0) { + ret = PS3_SUCCESS; + goto l_out; + } + + ps3_spin_lock_irqsave(&instance->recovery_context->recovery_lock, + &flags1); + ret = ps3_mgr_cmd_cancel_send(instance, cmd->index, + PS3_CANCEL_VDPENDING_CMD); + if (ret == -PS3_ENOMEM) { + ps3_spin_unlock_irqrestore( + &instance->recovery_context->recovery_lock, flags1); + LOG_INFO("hno:%u alloc failed\n", PS3_HOST(instance)); + ret = -PS3_FAILED; + ps3_atomic_set(&instance->dev_context.is_vdpending_abort, 0); + goto l_out; + } else if (ret != PS3_SUCCESS) { + LOG_FILE_INFO( + "hno:%u reqFrameId=%d cancel_cmd_frame_id[%u] free!\n", + PS3_HOST(instance), + ps3_cmd_frame_id( + instance->dev_context.vdpending_abort_cmd), + cmd->index); + abort_cmd = instance->dev_context.vdpending_abort_cmd; + instance->dev_context.vdpending_abort_cmd = NULL; + if (abort_cmd != NULL) + ps3_task_cmd_free(instance, abort_cmd); + ret = -PS3_FAILED; + ps3_spin_unlock_irqrestore( + &instance->recovery_context->recovery_lock, flags1); + ps3_atomic_set(&instance->dev_context.is_vdpending_abort, 0); + goto l_out; + } + ps3_atomic_set(&instance->dev_context.abort_vdpending_cmd, 1); + ps3_spin_unlock_irqrestore(&instance->recovery_context->recovery_lock, + flags1); + + ret = ps3_mgr_cmd_cancel_wait(instance, PS3_CANCEL_VDPENDING_CMD); + if (ret != PS3_SUCCESS) { + LOG_ERROR("hno:%u unsubscribe vd pending, cancel cmd NOK\n", + PS3_HOST(instance)); + ps3_atomic_set(&instance->dev_context.is_vdpending_abort, 0); + goto l_out; + } + ps3_spin_lock_irqsave(&instance->recovery_context->recovery_lock, + &flags1); + if (ps3_atomic_read(&instance->dev_context.subwork) == 0) { + if (ps3_atomic_read(&instance->dev_context.abort_vdpending_cmd) != 0) { + ps3_atomic_set(&instance->dev_context.abort_vdpending_cmd, 0); + LOG_FILE_INFO("hno:%u vd pending cmd free, CFID:%d\n", + PS3_HOST(instance), cmd->index); + instance->dev_context.vd_pending_cmd = NULL; + ps3_mgr_cmd_free(instance, cmd); + is_need_resend = PS3_TRUE; + } + } + ps3_atomic_set(&instance->dev_context.is_vdpending_abort, 0); +l_resend: + if (is_need_resend) { + ret = ps3_dev_mgr_vd_info_subscribe(instance); + if (ret != PS3_SUCCESS) { + ps3_spin_unlock_irqrestore( + &instance->recovery_context->recovery_lock, + flags1); + goto l_out; + } + } + + ps3_spin_unlock_irqrestore(&instance->recovery_context->recovery_lock, + flags1); + +l_out: + return ret; +} + +void ps3_dev_mgr_vd_info_clear(struct ps3_instance *instance) +{ + unsigned long flags = 0; + unsigned long flags1 = 0; + struct ps3_cmd *cmd = NULL; + + ps3_atomic_set(&instance->dev_context.abort_vdpending_cmd, 0); + + cmd = instance->dev_context.vd_pending_cmd; + + if (cmd == NULL) { + LOG_WARN("hno:%u vd pending cmd has been cancel\n", + PS3_HOST(instance)); + goto l_out; + } + + ps3_spin_lock_irqsave(&cmd->cmd_state.lock, &flags); + if (cmd->cmd_state.state == PS3_CMD_STATE_INIT) { + ps3_spin_unlock_irqrestore(&cmd->cmd_state.lock, flags); + LOG_INFO("hno:%u free this cmd,CFID:%d\n", PS3_HOST(instance), + cmd->index); + ps3_spin_lock_irqsave( + &instance->recovery_context->recovery_lock, &flags1); + instance->dev_context.vd_pending_cmd = NULL; + ps3_spin_unlock_irqrestore( + &instance->recovery_context->recovery_lock, flags1); + goto l_out; + } else { + ps3_spin_unlock_irqrestore(&cmd->cmd_state.lock, flags); + } + + LOG_INFO("hno:%u vd pending cmd free, CFID:%d\n", PS3_HOST(instance), + cmd->index); + instance->dev_context.vd_pending_cmd = NULL; + ps3_atomic_set(&instance->dev_context.subwork, 0); + ps3_mgr_cmd_free(instance, cmd); + +l_out: + return; +} + +#ifndef _WINDOWS +void ps3_change_sdev_max_sector(struct ps3_instance *instance, + struct PS3VDEntry *vd_entry) +{ + struct scsi_device *sdev = NULL; + struct PS3Dev *p_vd = &vd_entry->diskPos.diskDev.ps3Dev; + + sdev = ps3_scsi_device_lookup(instance, p_vd->softChan, p_vd->devID, 0); + if (sdev != NULL) { + if (vd_entry->sectorSize == PS3_SECTORSIZE_512B) { + blk_queue_max_hw_sectors(sdev->request_queue, + vd_entry->maxIOSize); + } else { + blk_queue_max_hw_sectors( + sdev->request_queue, + vd_entry->maxIOSize + << (ilog2(vd_entry->sectorSize) - + PS3_512B_SHIFT)); + } + + LOG_INFO_IN_IRQ( + instance, + "hno:%u vd[%u:%u] max sector num change to:%d\n", + PS3_HOST(instance), p_vd->softChan, p_vd->devID, + vd_entry->maxIOSize); + + ps3_scsi_device_put(instance, sdev); + } +} +#endif +int ps3_vd_info_get_all(struct ps3_instance *instance) +{ + int ret = -PS3_FAILED; + unsigned short i = 0; + struct ps3_dev_context *p_dev_ctx = &instance->dev_context; + unsigned char vd_table_idx = (p_dev_ctx->vd_table_idx + 1) & 1; + struct ps3_vd_table *p_vd_table = &p_dev_ctx->vd_table[vd_table_idx]; + struct PS3VDEntry *p_vd_array = + p_dev_ctx->vd_entries_array[vd_table_idx]; + struct PS3VDEntry *p_vd_entry = p_dev_ctx->vd_info_buf_sync->vds; + struct PS3Dev *p_dev = NULL; + unsigned short virtDiskIdx = PS3_INVALID_DEV_ID; + + if (PS3_MAX_VD_COUNT(instance) == 0) { + LOG_INFO("hno:%u vd max count is %d\n", PS3_HOST(instance), + PS3_MAX_VD_COUNT(instance)); + ret = PS3_SUCCESS; + goto l_out; + } + + if (p_dev_ctx->vd_list_buf->count == 0) { + LOG_INFO("hno:%u vd list count is 0\n", PS3_HOST(instance)); + ret = PS3_SUCCESS; + goto l_out; + } + + ret = ps3_vd_info_sync_get(instance, 0, PS3_MAX_VD_COUNT(instance)); + if (ret != PS3_SUCCESS) { + LOG_ERROR("hno:%u, sync get VD info NOK\n", PS3_HOST(instance)); + goto l_out; + } + + memset(p_vd_table->vd_idxs_array, 0, + p_dev_ctx->total_vd_count * sizeof(unsigned short)); + + LOG_INFO("hno:%u get vd info count is %d\n", PS3_HOST(instance), + p_dev_ctx->vd_info_buf_sync->count); + + for (i = 0; i < p_dev_ctx->vd_info_buf_sync->count; i++) { + if (PS3_VDID_INVALID(&p_vd_entry[i].diskPos)) { + LOG_WARN( + "hno:%u, init %d of %d vd info NOK, vdid is 0\n", + PS3_HOST(instance), i, + p_dev_ctx->vd_info_buf_sync->count); + continue; + } + + p_dev = PS3_DEV(&p_vd_entry[i].diskPos); + + virtDiskIdx = get_offset_of_vdid(PS3_VDID_OFFSET(instance), + p_dev->virtDiskID); + if (!ps3_dev_id_valid_check(instance, + (unsigned char)p_dev->softChan, + p_dev->devID, PS3_DISK_TYPE_VD)) { + PS3_BUG(); + continue; + } + + if (unlikely(virtDiskIdx > PS3_MAX_VD_COUNT(instance))) { + LOG_ERROR( + "hno:%u init %d of %d vd info NOK, vir_id[%d] > max[%d]\n", + PS3_HOST(instance), i, + p_dev_ctx->vd_info_buf_sync->count - 1, + virtDiskIdx, PS3_MAX_VD_COUNT(instance)); + + PS3_BUG(); + continue; + } + ps3_vd_busy_scale_get(&p_vd_entry[i]); + memcpy(&p_vd_array[virtDiskIdx], &p_vd_entry[i], + sizeof(struct PS3VDEntry)); + p_vd_table->vd_idxs[p_dev->softChan][p_dev->devID] = + p_dev->virtDiskID; + + ps3_vd_info_show(instance, &p_vd_array[virtDiskIdx]); +#ifndef _WINDOWS + if (p_vd_entry[i].maxIOSize != 0) + ps3_change_sdev_max_sector(instance, &p_vd_entry[i]); +#endif + } + mb(); /* in order to force CPU ordering */ + p_dev_ctx->vd_table_idx = vd_table_idx; + mb(); /* in order to force CPU ordering */ +l_out: + return ret; +} + +int ps3_device_mgr_data_init(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + + LOG_DEBUG("hno:%u enter\n", PS3_HOST(instance)); + + if (PS3_MAX_PD_COUNT(instance) != 0) { + ret = ps3_dev_mgr_pd_list_get(instance); + if (ret != PS3_SUCCESS) + goto l_out; + + ps3_pd_info_get_all(instance); + } + + if (PS3_MAX_VD_COUNT(instance) != 0) { + ret = ps3_dev_mgr_vd_list_get(instance); + if (ret != PS3_SUCCESS) + goto l_out; + instance->dev_context.vd_table_idx = 0; + ps3_vd_info_get_all(instance); + } +l_out: + LOG_DEBUG("hno:%u out\n", PS3_HOST(instance)); + + return ret; +} + +int ps3_device_mgr_data_exit(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + + LOG_DEBUG("hno:%u\n", PS3_HOST(instance)); + + return ret; +} + +int ps3_device_mgr_init(struct ps3_instance *instance) +{ + int ret = -PS3_FAILED; + + if (PS3_MAX_VD_COUNT(instance) == 0 && + PS3_MAX_PD_COUNT(instance) == 0) { + LOG_ERROR("hno:%u max VD and PD count == 0\n", + PS3_HOST(instance)); + ret = -PS3_FAILED; + goto l_out; + } + + ret = ps3_dev_channel_init(instance); + if (ret != PS3_SUCCESS) + goto l_out; + + ret = ps3_dev_buff_alloc(instance); + if (ret != PS3_SUCCESS) + goto l_out; + ps3_mutex_init(&instance->dev_context.dev_priv_lock); + ps3_mutex_init(&instance->dev_context.dev_scan_lock); +#ifdef _WINDOWS + ps3_windows_channel_map_init(instance); + ret = ps3_windows_private_init(instance); + if (ret != PS3_SUCCESS) + goto l_out; +#endif + +l_out: + return ret; +} + +void ps3_device_mgr_exit(struct ps3_instance *instance) +{ +#ifdef _WINDOWS + ps3_windows_private_exit(instance); +#endif + ps3_dev_buff_release(instance); + ps3_mutex_destroy(&instance->dev_context.dev_priv_lock); + ps3_mutex_destroy(&instance->dev_context.dev_scan_lock); +} + +int ps3_dev_mgr_pd_list_get(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + unsigned int i = 0; + struct ps3_dev_context *p_dev_ctx = &instance->dev_context; + struct PS3DevList *p_pd_list = p_dev_ctx->pd_list_buf; + struct PS3Dev *p_dev = NULL; + + ret = ps3_pd_list_get(instance); + if (ret != PS3_SUCCESS) { + LOG_ERROR("hno:%u, dev mgr get pd list NOK\n", + PS3_HOST(instance)); + goto l_out; + } + + memset((unsigned char *)p_dev_ctx->pd_pool.devs_buffer, + PS3_INVALID_VALUE, + p_dev_ctx->total_pd_count * sizeof(union PS3Device)); + + LOG_INFO("hno:%u get pd list count is %d\n", PS3_HOST(instance), + p_pd_list->count); + + for (i = 0; i < p_pd_list->count; i++) { + p_dev = PS3_DEV(&p_pd_list->devs[i].pd.diskPos); + + if (PS3_PDID_INVALID(&p_pd_list->devs[i].pd.diskPos)) { + LOG_WARN("hno:%u, get pd list %d dev pdid is 0\n", + PS3_HOST(instance), i); + continue; + } + + if (!ps3_dev_id_valid_check(instance, + (unsigned char)p_dev->softChan, + p_dev->devID, PS3_DISK_TYPE_PD)) { + PS3_BUG(); + continue; + } + + LOG_INFO( + "hno:%u, pd list %d dev[%u:%u:%u], magic[%#x], state[%s]\n", + PS3_HOST(instance), i, p_dev->softChan, p_dev->devID, + p_dev->phyDiskID, + p_pd_list->devs[i].pd.diskPos.diskMagicNum, + getDeviceStateName((enum DeviceState)p_pd_list->devs[i] + .pd.diskState)); + + p_dev_ctx->pd_pool.devs[p_dev->softChan][p_dev->devID].pd = + p_pd_list->devs[i].pd; + } +l_out: + return ret; +} + +int ps3_dev_mgr_vd_list_get(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + unsigned int i = 0; + struct ps3_dev_context *p_dev_ctx = &instance->dev_context; + struct PS3DevList *p_vd_list = p_dev_ctx->vd_list_buf; + struct PS3Dev *p_dev = NULL; + + ret = ps3_vd_list_get(instance); + if (ret != PS3_SUCCESS) { + LOG_ERROR("hno:%u, dev mgr get vd list NOK\n", + PS3_HOST(instance)); + goto l_out; + } + + memset((unsigned char *)p_dev_ctx->vd_pool.devs_buffer, + PS3_INVALID_VALUE, + p_dev_ctx->total_vd_count * sizeof(union PS3Device)); + + LOG_INFO("hno:%u get vd list count is %d\n", PS3_HOST(instance), + p_vd_list->count); + + for (i = 0; i < p_vd_list->count; i++) { + p_dev = PS3_DEV(&p_vd_list->devs[i].vd.diskPos); + + if (PS3_VDID_INVALID(&p_vd_list->devs[i].vd.diskPos)) { + LOG_WARN("hno:%u, get vd list %d vdid is 0\n", + PS3_HOST(instance), i); + continue; + } + + if (!ps3_dev_id_valid_check(instance, + (unsigned char)p_dev->softChan, + p_dev->devID, PS3_DISK_TYPE_VD)) { + PS3_BUG(); + continue; + } + + LOG_INFO("hno:%u, vd list %d dev[%u:%u:%u], magic[%#x]\n", + PS3_HOST(instance), i, p_dev->softChan, p_dev->devID, + p_dev->virtDiskID, + p_vd_list->devs[i].vd.diskPos.diskMagicNum); + + p_dev_ctx->vd_pool.devs[p_dev->softChan][p_dev->devID].vd = + p_vd_list->devs[i].vd; + } +l_out: + return ret; +} + +unsigned char ps3_dev_id_valid_check(struct ps3_instance *instance, + unsigned char channel, + unsigned short target_id, + unsigned char dev_type) +{ + unsigned char ret = PS3_DRV_FALSE; + struct ps3_dev_context *p_dev_ctx = &instance->dev_context; + + if (dev_type == PS3_DISK_TYPE_VD) { + if (!PS3_IS_VD_CHANNEL(instance, channel)) { + LOG_ERROR_IN_IRQ( + instance, + "hno:%u check channel[%u] is not vd channel\n", + PS3_HOST(instance), channel); + goto l_out; + } + + } else if (dev_type == PS3_DISK_TYPE_PD) { + if (!PS3_IS_PD_CHANNEL(instance, channel)) { + LOG_ERROR_IN_IRQ( + instance, + "hno:%u check channel[%u] is not pd channel\n", + PS3_HOST(instance), channel); + goto l_out; + } + } else { + LOG_ERROR_IN_IRQ(instance, "hno:%u dev id[%u:%u] channel err\n", + PS3_HOST(instance), channel, target_id); + goto l_out; + } + if (unlikely(target_id >= p_dev_ctx->max_dev_in_channel[channel])) { + LOG_ERROR_IN_IRQ( + instance, + "hno:%u check disk[%u:%u] target >= max[%u]\n", + PS3_HOST(instance), channel, target_id, + p_dev_ctx->max_dev_in_channel[channel]); + goto l_out; + } + + ret = PS3_DRV_TRUE; +l_out: + return ret; +} + +unsigned char ps3_get_vd_raid_level(struct ps3_instance *instance, + unsigned char channel, + unsigned short target_id) +{ + struct ps3_dev_context *p_dev_ctx = &instance->dev_context; + unsigned char vd_table_idx = p_dev_ctx->vd_table_idx & 1; + struct ps3_vd_table *p_table = &p_dev_ctx->vd_table[vd_table_idx]; + struct PS3VDEntry *p_vd_array = + p_dev_ctx->vd_entries_array[vd_table_idx]; + struct PS3VDEntry *p_entry = NULL; + unsigned char ret = RAID_UNKNOWN; + unsigned short virtDiskIdx = PS3_INVALID_DEV_ID; + + + if (!ps3_dev_id_valid_check(instance, channel, target_id, + PS3_DISK_TYPE_VD)) { + goto l_out; + } + + virtDiskIdx = get_offset_of_vdid(PS3_VDID_OFFSET(instance), + p_table->vd_idxs[channel][target_id]); + + p_entry = &p_vd_array[virtDiskIdx]; + if (p_entry == NULL) + goto l_out; + + LOG_DEBUG("hno:%u, vd[%u:%u] raid level is:%d\n", PS3_HOST(instance), + channel, target_id, p_entry->raidLevel); + + ret = p_entry->raidLevel; +l_out: + return ret; +} + +struct ps3_scsi_priv_data * +ps3_dev_mgr_lookup_vd_pri_data(struct ps3_instance *instance, + unsigned char channel, unsigned short target_id) +{ + struct ps3_dev_context *p_dev_ctx = &instance->dev_context; + struct ps3_scsi_priv_data *ret = NULL; + + if (!ps3_dev_id_valid_check(instance, channel, target_id, + PS3_DISK_TYPE_VD)) { + goto l_out; + } + + ret = p_dev_ctx->vd_pri_data_table.vd_pri_data_idxs[channel][target_id]; +l_out: + return ret; +} + +static inline unsigned char ps3_dev_is_valid(struct PS3DiskDevPos *diskPos, + unsigned char channel, + unsigned short target_id) +{ + if (PS3_DEV_INVALID(*diskPos) || PS3_CHANNEL(diskPos) != channel || + PS3_TARGET(diskPos) != target_id) { + return PS3_FALSE; + } + return PS3_TRUE; +} + +struct PS3VDEntry *ps3_dev_mgr_lookup_vd_info(struct ps3_instance *instance, + unsigned char channel, + unsigned short target_id) +{ + struct ps3_dev_context *p_dev_ctx = &instance->dev_context; + unsigned char vd_table_idx = p_dev_ctx->vd_table_idx & 1; + struct ps3_vd_table *p_table = &p_dev_ctx->vd_table[vd_table_idx]; + struct PS3VDEntry *p_vd_array = + p_dev_ctx->vd_entries_array[vd_table_idx]; + struct PS3VDEntry *p_entry = NULL; + unsigned short virtDiskIdx = PS3_INVALID_DEV_ID; + + LOG_DEBUG("hno:%u cur_vd_idx[%d]\n", PS3_HOST(instance), + p_dev_ctx->vd_table_idx); + + + if (!ps3_dev_id_valid_check(instance, channel, target_id, + PS3_DISK_TYPE_VD)) { + goto l_out; + } + + virtDiskIdx = get_offset_of_vdid(PS3_VDID_OFFSET(instance), + p_table->vd_idxs[channel][target_id]); + + p_entry = &p_vd_array[virtDiskIdx]; + + if (ps3_dev_is_valid(&p_entry->diskPos, channel, target_id) != + PS3_TRUE) { + p_entry = NULL; + goto l_out; + } +l_out: + return p_entry; +} + +struct ps3_pd_entry *ps3_dev_mgr_lookup_pd_info(struct ps3_instance *instance, + unsigned char channel, + unsigned short target_id) +{ + struct ps3_pd_table *p_table = &instance->dev_context.pd_table; + struct ps3_dev_context *p_dev_ctx = &instance->dev_context; + struct ps3_pd_entry *p_entry = NULL; + unsigned short disk_idx = 0; + + if (!ps3_dev_id_valid_check(instance, channel, target_id, + PS3_DISK_TYPE_PD)) { + goto l_out; + } + + disk_idx = p_table->pd_idxs[channel][target_id]; + p_entry = &p_dev_ctx->pd_entries_array[disk_idx]; + + if (ps3_dev_is_valid(&p_entry->disk_pos, channel, target_id) != + PS3_TRUE) { + p_entry = NULL; + goto l_out; + } +l_out: + return p_entry; +} + +struct PS3VDEntry * +ps3_dev_mgr_lookup_vd_info_by_id(struct ps3_instance *instance, + unsigned short disk_id) +{ + struct ps3_dev_context *p_dev_ctx = &instance->dev_context; + unsigned char vd_table_idx = p_dev_ctx->vd_table_idx & 1; + struct PS3VDEntry *p_vd_array = + p_dev_ctx->vd_entries_array[vd_table_idx]; + struct PS3VDEntry *p_entry = NULL; + unsigned short virtDiskIdx = PS3_INVALID_DEV_ID; + + LOG_DEBUG("hno:%u cur_vd_idx[%d]\n", PS3_HOST(instance), + p_dev_ctx->vd_table_idx); + + virtDiskIdx = get_offset_of_vdid(PS3_VDID_OFFSET(instance), disk_id); + + if (unlikely(virtDiskIdx > PS3_MAX_VD_COUNT(instance))) { + LOG_ERROR_IN_IRQ( + instance, + "hno:%u , dev mgr lookup vd info disk id > max count:%d>%d\n", + PS3_HOST(instance), disk_id, + PS3_MAX_VD_COUNT(instance)); + + PS3_BUG(); + goto l_out; + } + + p_entry = &p_vd_array[virtDiskIdx]; + + if (PS3_DEV_INVALID(p_entry->diskPos)) { + LOG_INFO_IN_IRQ( + instance, + "hno:%u idx[%d], virDisk[%d] dev id is invalid\n", + PS3_HOST(instance), p_dev_ctx->vd_table_idx, disk_id); + p_entry = NULL; + goto l_out; + } +l_out: + return p_entry; +} + +struct ps3_pd_entry * +ps3_dev_mgr_lookup_pd_info_by_id(struct ps3_instance *instance, + unsigned short disk_id) +{ + struct ps3_dev_context *p_dev_ctx = &instance->dev_context; + struct ps3_pd_entry *p_entry = NULL; + + if (unlikely(disk_id > PS3_MAX_PD_COUNT(instance))) { + LOG_ERROR_IN_IRQ( + instance, + "hno:%u, dev mgr lookup pd info disk id > max count:%d>%d\n", + PS3_HOST(instance), disk_id, + PS3_MAX_PD_COUNT(instance)); + PS3_BUG(); + goto l_out; + } + + p_entry = &p_dev_ctx->pd_entries_array[disk_id]; + + if (PS3_DEV_INVALID(p_entry->disk_pos) || + p_entry->config_flag == MIC_PD_STATE_UNKNOWN) { + LOG_INFO_IN_IRQ( + instance, + "hno:%u pdid[%d] dev[%x] id is invalid, config_flag[%d]\n", + PS3_HOST(instance), disk_id, + PS3_DISKID(&p_entry->disk_pos), p_entry->config_flag); + p_entry = NULL; + goto l_out; + } +l_out: + return p_entry; +} + +union PS3Device *ps3_dev_mgr_lookup_vd_list(struct ps3_instance *instance, + unsigned char channel, + unsigned short target_id) +{ + struct ps3_dev_pool *p_vd_pool = &instance->dev_context.vd_pool; + struct ps3_dev_context *p_dev_ctx = &instance->dev_context; + union PS3Device *p_vd = NULL; + + if (!ps3_dev_id_valid_check(instance, channel, target_id, + PS3_DISK_TYPE_VD)) { + goto l_out; + } + + p_vd = &p_vd_pool->devs[channel][target_id]; + + if (PS3_DEV_INVALID(p_vd->vd.diskPos)) { + LOG_INFO("hno:%u idx[%d], dev[%u:%u] dev id is invalid\n", + PS3_HOST(instance), p_dev_ctx->vd_table_idx, channel, + target_id); + p_vd = NULL; + goto l_out; + } +l_out: + return p_vd; +} + +union PS3Device *ps3_dev_mgr_lookup_pd_list(struct ps3_instance *instance, + unsigned char channel, + unsigned short target_id) +{ + struct ps3_dev_pool *p_pd_pool = &instance->dev_context.pd_pool; + struct ps3_dev_context *p_dev_ctx = &instance->dev_context; + union PS3Device *p_pd = NULL; + + if (!ps3_dev_id_valid_check(instance, channel, target_id, + PS3_DISK_TYPE_PD)) { + goto l_out; + } + + p_pd = &p_pd_pool->devs[channel][target_id]; + + if (PS3_DEV_INVALID(p_pd->pd.diskPos)) { + p_pd = NULL; + goto l_out; + } + LOG_INFO("hno:%u idx[%d], dev[%u:%u] dev id is valid\n", + PS3_HOST(instance), p_dev_ctx->vd_table_idx, channel, + target_id); +l_out: + return p_pd; +} + +int ps3_adjust_queue_depth(struct ps3_instance *instance, + unsigned char dev_type, unsigned int queue_depth) +{ + int dev_queue_depth = PS3_QUEUE_DEPTH_DEFAULT; + + switch (dev_type) { + case PS3_DEV_TYPE_SAS_HDD: + case PS3_DEV_TYPE_SAS_SSD: + dev_queue_depth = PS3_QUEUE_DEPTH_SAS; + break; + case PS3_DEV_TYPE_SATA_HDD: + case PS3_DEV_TYPE_SATA_SSD: + dev_queue_depth = PS3_QUEUE_DEPTH_SATA; + break; + case PS3_DEV_TYPE_NVME_SSD: + dev_queue_depth = PS3_QUEUE_DEPTH_NVME; + break; + default: + dev_queue_depth = PS3_QUEUE_DEPTH_DEFAULT; + break; + } + + if (queue_depth != 0 && + (int)queue_depth <= instance->cmd_attr.cur_can_que) { + dev_queue_depth = queue_depth; + } + + return dev_queue_depth; +} + +static inline int ps3_adjust_device_queue_depth(struct scsi_device *sdev, + struct ps3_instance *instance, + int q_depth) +{ + int queue_depth = q_depth; + struct ps3_pd_entry *p_pd_entry = NULL; + + if (PS3_IS_PD_CHANNEL(instance, sdev->channel)) { + p_pd_entry = ps3_dev_mgr_lookup_pd_info(instance, sdev->channel, + sdev->id); + if (p_pd_entry == NULL) { + LOG_WARN_IN_IRQ( + instance, + "hno:%u cannot found PD[%u:%u] device info\n", + PS3_HOST(instance), sdev->channel, sdev->id); + goto l_out; + } + if ((p_pd_entry->dev_type == PS3_DEV_TYPE_SATA_HDD) || + (p_pd_entry->dev_type == PS3_DEV_TYPE_SATA_SSD)) { + if (q_depth > PS3_QUEUE_DEPTH_SATA) + queue_depth = PS3_QUEUE_DEPTH_SATA; + } + } + +l_out: +#if defined(PS3_CHANGE_QUEUE_DEPTH) + scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), queue_depth); +#else + scsi_change_queue_depth(sdev, queue_depth); +#endif + return queue_depth; +} + +#ifndef _WINDOWS +#if defined(PS3_CHANGE_QUEUE_DEPTH) +int ps3_change_queue_depth(struct scsi_device *sdev, int queue_depth, + int reason) +{ + int ret = -EOPNOTSUPP; + struct ps3_instance *instance = NULL; + + instance = (struct ps3_instance *)sdev->host->hostdata; + if (instance == NULL) { + LOG_ERROR_IN_IRQ(instance, "hno:%u have no host\n", + sdev->host->host_no); + goto l_out; + } + + if (queue_depth > sdev->host->can_queue) + queue_depth = sdev->host->can_queue; + if (reason == SCSI_QDEPTH_DEFAULT || reason == SCSI_QDEPTH_RAMP_UP) { + ret = ps3_adjust_device_queue_depth(sdev, instance, + queue_depth); + } else if (reason == SCSI_QDEPTH_QFULL) { + scsi_track_queue_full(sdev, queue_depth); + ret = sdev->queue_depth; + } + LOG_INFO_IN_IRQ( + instance, + "hno:%u change dev[%u:%u] queue depth to [%d] reason [%d]\n", + PS3_HOST(instance), sdev->channel, sdev->id, ret, reason); +l_out: + return ret; +} + +#else +int ps3_change_queue_depth(struct scsi_device *sdev, int queue_depth) +{ + int ret = sdev->queue_depth; + struct ps3_instance *instance = NULL; + + instance = (struct ps3_instance *)sdev->host->hostdata; + if (instance == NULL) { + LOG_ERROR("hno:%u have no host\n", sdev->host->host_no); + goto l_out; + } + + if (queue_depth > sdev->host->can_queue) + queue_depth = sdev->host->can_queue; + + ret = ps3_adjust_device_queue_depth(sdev, instance, queue_depth); + + LOG_INFO("hno:%u change dev[%u:%u] queue depth to [%d]\n", + PS3_HOST(instance), sdev->channel, sdev->id, ret); + +l_out: + return ret; +} + +#endif +static inline void ps3_init_vd_stream(struct ps3_vd_stream_detect *vdsd) +{ + unsigned int index = 0; + unsigned char tyepIndex = 0; + + for (tyepIndex = PS3_SCSI_CMD_TYPE_READ; + tyepIndex < PS3_SCSI_CMD_TYPE_WRITE; tyepIndex++) { + vdsd[tyepIndex - PS3_SCSI_CMD_TYPE_READ].mru_bit_map = + MR_STREM_BITMAP; + ps3_spin_lock_init(&vdsd[tyepIndex - PS3_SCSI_CMD_TYPE_READ] + .ps3_sequence_stream_lock); + + for (index = 0; index < PS3_IO_MAX_STREAMS_TRACKED; index++) { + vdsd[tyepIndex - PS3_SCSI_CMD_TYPE_READ] + .stream_track[index] + .next_seq_lba = 0; + vdsd[tyepIndex - PS3_SCSI_CMD_TYPE_READ] + .stream_track[index] + .rw_type = tyepIndex; + } + } +} +int ps3_scsi_private_init_pd(struct scsi_device *sdev) +{ + int ret = PS3_SUCCESS; + struct ps3_scsi_priv_data *p_priv_data = NULL; + struct ps3_pd_entry *p_pd_entry = NULL; + struct ps3_qos_pd_mgr *p_qos_pd_mgr = NULL; + struct ps3_instance *instance = + (struct ps3_instance *)sdev->host->hostdata; + + p_pd_entry = + ps3_dev_mgr_lookup_pd_info(instance, sdev->channel, sdev->id); + if (p_pd_entry == NULL) { + LOG_WARN("hno:%u cannot found PD[%u:%u] device info\n", + PS3_HOST(instance), sdev->channel, sdev->id); + ret = -ENXIO; + goto l_out; + } + if (!ps3_pd_scsi_visible_check( + instance, &p_pd_entry->disk_pos, p_pd_entry->dev_type, + p_pd_entry->config_flag, p_pd_entry->state)) { + ret = -ENXIO; + LOG_WARN("hno:%u pd was blocked: chan[%d] id[%d]\n", + PS3_HOST(instance), sdev->channel, sdev->id); + goto l_out; + } + + LOG_DEBUG("hno:%u found PD[%u:%u:%u] magic[%#x] device info\n", + PS3_HOST(instance), sdev->channel, sdev->id, + PS3_PDID(&p_pd_entry->disk_pos), + p_pd_entry->disk_pos.diskMagicNum); + + p_priv_data = (struct ps3_scsi_priv_data *)ps3_kzalloc( + instance, sizeof(struct ps3_scsi_priv_data)); + if (p_priv_data == NULL) { + LOG_ERROR("hno:%u pd[%u:%u:%u] Failed to allocate scsi device private data\n", + PS3_HOST(instance), sdev->channel, sdev->id, + PS3_PDID(&p_pd_entry->disk_pos)); + ret = -ENOMEM; + goto l_out; + } + + p_priv_data->disk_pos = p_pd_entry->disk_pos; + p_priv_data->dev_type = p_pd_entry->dev_type; + p_priv_data->is_taskmgmt_enable = PS3_DRV_TRUE; + p_priv_data->task_abort_timeout = p_pd_entry->task_abort_timeout; + p_priv_data->task_reset_timeout = p_pd_entry->task_reset_timeout; + p_priv_data->task_manager_busy = 0; + p_priv_data->encl_id = p_pd_entry->encl_id; + p_priv_data->phy_id = p_pd_entry->phy_id; + ps3_atomic_set(&p_priv_data->rd_io_outstand, PS3_CMD_STAT_INIT_VALUE); + ps3_atomic_set(&p_priv_data->wr_io_outstand, PS3_CMD_STAT_INIT_VALUE); + ps3_atomic_set(&p_priv_data->r1x_read_cmd_swap_total_cnt, + PS3_CMD_STAT_INIT_VALUE); + ps3_atomic_set(&p_priv_data->r1x_read_cmd_swap_res_cnt, + PS3_CMD_STAT_INIT_VALUE); + p_qos_pd_mgr = ps3_qos_pd_mgr_init(instance, p_pd_entry); + if (p_qos_pd_mgr != NULL && + p_qos_pd_mgr->dev_type != p_pd_entry->dev_type) { + ps3_qos_pd_rsc_init(p_qos_pd_mgr, p_pd_entry); + } + p_priv_data->dev_deling = PS3_FALSE; + p_priv_data->swap_flag = PS3_FALSE; + sdev->hostdata = p_priv_data; +l_out: + return ret; +} + +void ps3_vd_busy_scale_get(struct PS3VDEntry *vd_entry) +{ + unsigned short scale = 0; + unsigned int strip_size_shift = 0; + + strip_size_shift = ps3_blocksize_to_shift(vd_entry->stripSize); + scale = vd_entry->span[0].spanStripeDataSize >> strip_size_shift; + if ((vd_entry->raidLevel == RAID1E || vd_entry->raidLevel == RAID10) && + (vd_entry->span[0].spanPdNum & 1)) { + scale = scale >> 1; + } + + vd_entry->dev_busy_scale = scale; +} + +int ps3_scsi_private_init_vd(struct scsi_device *sdev) +{ + int ret = PS3_SUCCESS; + struct ps3_scsi_priv_data *p_priv_data = NULL; + struct PS3VDEntry *p_vd_entry = NULL; + struct ps3_instance *instance = + (struct ps3_instance *)sdev->host->hostdata; + struct ps3_pri_data_table *p_vd_pri_data_table = + &instance->dev_context.vd_pri_data_table; + + p_vd_entry = + ps3_dev_mgr_lookup_vd_info(instance, sdev->channel, sdev->id); + if (p_vd_entry == NULL) { + LOG_WARN("hno:%u cannot found VD[%u:%u] device info\n", + PS3_HOST(instance), sdev->channel, sdev->id); + ret = -ENXIO; + goto l_out; + } + LOG_DEBUG("hno:%u found VD[%u:%u:%u] magic[%#x] device info\n", + PS3_HOST(instance), sdev->channel, sdev->id, + PS3_VDID(&p_vd_entry->diskPos), + p_vd_entry->diskPos.diskMagicNum); + + p_priv_data = (struct ps3_scsi_priv_data *)ps3_kzalloc( + instance, sizeof(struct ps3_scsi_priv_data)); + if (p_priv_data == NULL) { + LOG_ERROR("hno:%u vd[%u:%u:%u] Failed to allocate scsi device private data\n", + PS3_HOST(instance), sdev->channel, sdev->id, + PS3_VDID(&p_vd_entry->diskPos)); + ret = -ENOMEM; + goto l_out; + } + + p_priv_data->disk_pos = p_vd_entry->diskPos; + p_priv_data->dev_type = PS3_DEV_TYPE_VD; + p_priv_data->is_taskmgmt_enable = p_vd_entry->isTaskMgmtEnable; + p_priv_data->task_abort_timeout = p_vd_entry->taskAbortTimeout; + p_priv_data->task_reset_timeout = p_vd_entry->taskResetTimeout; + p_priv_data->task_manager_busy = 0; + ps3_atomic_set(&p_priv_data->rd_io_outstand, PS3_CMD_STAT_INIT_VALUE); + ps3_atomic_set(&p_priv_data->wr_io_outstand, PS3_CMD_STAT_INIT_VALUE); + ps3_atomic_set(&p_priv_data->r1x_read_cmd_swap_total_cnt, + PS3_CMD_STAT_INIT_VALUE); + ps3_atomic_set(&p_priv_data->r1x_read_cmd_swap_res_cnt, + PS3_CMD_STAT_INIT_VALUE); + p_priv_data->dev_deling = PS3_FALSE; + p_priv_data->swap_flag = PS3_FALSE; + ps3_vd_busy_scale_get(p_vd_entry); + ps3_init_vd_stream(p_priv_data->vd_sd); + p_priv_data->r1x_rb_info = + (struct ps3_r1x_read_balance_info *)ps3_kzalloc( + instance, sizeof(struct ps3_r1x_read_balance_info)); + if (p_priv_data->r1x_rb_info == NULL) { + LOG_ERROR( + "hno:%u vd[%u:%u:%u] Failed to allocate r1x_lb_info\n", + PS3_HOST(instance), sdev->channel, sdev->id, + PS3_VDID(&p_vd_entry->diskPos)); + ret = -ENOMEM; + goto l_err; + } + ps3_mutex_lock(&instance->dev_context.dev_priv_lock); + sdev->hostdata = p_priv_data; + + ret = ps3_r1x_lock_prepare_for_vd(instance, sdev, + p_vd_entry->raidLevel); + if (unlikely(ret != PS3_SUCCESS)) { + ps3_kfree(instance, p_priv_data->r1x_rb_info); + p_priv_data->r1x_rb_info = NULL; + ps3_mutex_unlock(&instance->dev_context.dev_priv_lock); + LOG_ERROR("hno:%u vd[%u:%u:%u] Failed to allocate raid1x write lock mgr\n", + PS3_HOST(instance), sdev->channel, sdev->id, + PS3_VDID(&p_vd_entry->diskPos)); + ret = -ENOMEM; + goto l_err; + } + ps3_mutex_unlock(&instance->dev_context.dev_priv_lock); + + p_vd_pri_data_table->vd_pri_data_idxs[PS3_SDEV_CHANNEL(sdev)] + [PS3_SDEV_TARGET(sdev)] = + p_priv_data; + + ps3_qos_vd_init(instance, p_vd_entry); + + goto l_out; +l_err: + ps3_mutex_lock(&instance->dev_context.dev_priv_lock); + if (p_priv_data != NULL) { + ps3_kfree(instance, p_priv_data); + sdev->hostdata = NULL; + } + ps3_mutex_unlock(&instance->dev_context.dev_priv_lock); + +l_out: + return ret; +} + +int ps3_scsi_slave_alloc(struct scsi_device *sdev) +{ + int ret = PS3_SUCCESS; + struct ps3_scsi_priv_data *p_priv_data = NULL; + struct ps3_instance *instance = NULL; + struct PS3VDEntry *p_vd_entry = NULL; + struct ps3_pd_entry *p_pd_entry = NULL; + unsigned int dma_addr_alignment = 0; + unsigned int dma_len_alignment = 0; + unsigned char dev_type = PS3_DISK_TYPE_UNKNOWN; + struct PS3DiskDevPos *p_diskPos = NULL; + + LOG_DEBUG("enter, [%u:%u:%llu]\n", sdev->channel, sdev->id, + (unsigned long long)sdev->lun); + if (sdev->lun != 0) { + ret = -ENXIO; + goto l_out; + } + + instance = (struct ps3_instance *)sdev->host->hostdata; + if (instance == NULL) { + LOG_ERROR("hno:%u have no host\n", sdev->host->host_no); + ret = -ENXIO; + goto l_out; + } + + sdev->hostdata = NULL; + + ret = -ENXIO; + if (PS3_IS_VD_CHANNEL(instance, sdev->channel)) { + dev_type = PS3_DISK_TYPE_VD; + p_vd_entry = ps3_dev_mgr_lookup_vd_info(instance, sdev->channel, + sdev->id); + if (p_vd_entry == NULL) { + LOG_ERROR( + "hno:%u, cannot found VD[%u:%u] device info\n", + PS3_HOST(instance), sdev->channel, sdev->id); + + goto l_out; + } + + p_diskPos = &p_vd_entry->diskPos; + + } else if (PS3_IS_PD_CHANNEL(instance, sdev->channel)) { + dev_type = PS3_DISK_TYPE_PD; + p_pd_entry = ps3_dev_mgr_lookup_pd_info(instance, sdev->channel, + sdev->id); + if (p_pd_entry == NULL) { + LOG_ERROR( + "hno:%u, cannot found PD[%u:%u] device info\n", + PS3_HOST(instance), sdev->channel, sdev->id); + + goto l_out; + } + + p_diskPos = &p_pd_entry->disk_pos; + } else { + LOG_ERROR("hno:%u dev channel[%u] type NOK\n", + PS3_HOST(instance), sdev->channel); + goto l_out; + } + + if (dev_type == PS3_DISK_TYPE_VD) + ret = ps3_scsi_private_init_vd(sdev); + else + ret = ps3_scsi_private_init_pd(sdev); + + if (ret != PS3_SUCCESS) + goto l_dev_done; + + p_priv_data = (struct ps3_scsi_priv_data *)sdev->hostdata; + + ret = ps3_scsi_add_device_ack(instance, p_diskPos, dev_type); + if (unlikely(ret != PS3_SUCCESS)) { + ret = -ENXIO; + goto l_dev_ack_failed; + } + + LOG_INFO("[%u:%u:%llu], dev_type[%s]\n", sdev->channel, sdev->id, + (unsigned long long)sdev->lun, + namePS3DevType((enum PS3DevType)p_priv_data->dev_type)); + + if (p_priv_data->task_abort_timeout == 0) + p_priv_data->task_abort_timeout = PS3_DEFAULT_TASK_MGR_TIMEOUT; + + if (p_priv_data->task_reset_timeout == 0) + p_priv_data->task_reset_timeout = PS3_DEFAULT_TASK_MGR_TIMEOUT; + +#if defined(PS3_CHANGE_QUEUE_DEPTH) + + sdev->tagged_supported = 1; + scsi_activate_tcq(sdev, sdev->queue_depth); +#endif + if (p_priv_data->dev_type == PS3_DEV_TYPE_VD) { + dma_addr_alignment = ps3_pd_dma_alignment_calc( + p_vd_entry->dmaAddrAlignShift); + dma_len_alignment = + ps3_pd_dma_alignment_calc(p_vd_entry->dmaLenAlignShift); + } else { + dma_addr_alignment = p_pd_entry->dma_addr_alignment; + dma_len_alignment = p_pd_entry->dma_len_alignment; + } + + blk_queue_dma_alignment(sdev->request_queue, PS3_SCSI_ALINNMENT_MASK); + if (dma_addr_alignment) { + blk_queue_dma_alignment(sdev->request_queue, + dma_addr_alignment - 1); + } + + if (dma_len_alignment) { +#if defined(PS3_BLK_DMA_PAD) + blk_queue_dma_pad(sdev->request_queue, dma_len_alignment - 1); +#else + blk_queue_update_dma_pad(sdev->request_queue, + dma_len_alignment - 1); +#endif + } + + LOG_INFO("slave_alloc,dma_addr_alignment[%d] dma_len_alignment[%d]\n", + dma_addr_alignment, dma_len_alignment); + + goto l_out; +l_dev_ack_failed: + ps3_scsi_slave_destroy(sdev); +l_dev_done: + ps3_scsi_remove_device_done(instance, p_diskPos, dev_type); +l_out: + LOG_DEBUG("exit, hno:%u\n", sdev->host->host_no); + return ret; +} + +void ps3_scsi_slave_destroy(struct scsi_device *sdev) +{ + int ret = PS3_SUCCESS; + struct ps3_scsi_priv_data *p_priv_data = NULL; + struct ps3_instance *instance = + (struct ps3_instance *)sdev->host->hostdata; + struct ps3_pri_data_table *p_vd_pri_data_table = + &instance->dev_context.vd_pri_data_table; + if (instance == NULL) { + LOG_ERROR("hno:%u have no host\n", sdev->host->host_no); + goto l_out; + } + + LOG_DEBUG("hno:%u enter, max_chan[%u], max_id[%u], max_lun[%llu]\n", + PS3_HOST(instance), sdev->host->max_channel, + sdev->host->max_id, (unsigned long long)sdev->host->max_lun); + + p_priv_data = (struct ps3_scsi_priv_data *)sdev->hostdata; + if (p_priv_data != NULL) { + if (PS3_IS_VD_CHANNEL(instance, sdev->channel)) { + LOG_INFO("hno[%u], vd[%u:%u] r1x conflict destroy\n", + PS3_HOST(instance), PS3_SDEV_CHANNEL(sdev), + PS3_SDEV_TARGET(sdev)); + ps3_mutex_lock(&instance->dev_context.dev_priv_lock); + ps3_r1x_lock_destroy_for_vd(instance, + &p_priv_data->lock_mgr); + ps3_qos_vd_reset(instance, + PS3_VDID(&p_priv_data->disk_pos)); + p_vd_pri_data_table->vd_pri_data_idxs[PS3_SDEV_CHANNEL( + sdev)][PS3_SDEV_TARGET(sdev)] = NULL; + ps3_mutex_unlock(&instance->dev_context.dev_priv_lock); + if (p_priv_data->r1x_rb_info != NULL) { + ps3_kfree(instance, p_priv_data->r1x_rb_info); + p_priv_data->r1x_rb_info = NULL; + } + } else if (PS3_IS_PD_CHANNEL(instance, sdev->channel)) { + ps3_qos_pd_mgr_reset(instance, + PS3_PDID(&p_priv_data->disk_pos)); + } + + ret = ps3_scsi_remove_device_done( + instance, &p_priv_data->disk_pos, + ps3_disk_type((enum PS3DevType)p_priv_data->dev_type)); + if (ret != PS3_SUCCESS) { + LOG_INFO( + "hno:%u dev[%u:%u:%u] magic[%#x] dev del done error %d\n", + PS3_HOST(instance), + PS3_CHANNEL(&p_priv_data->disk_pos), + PS3_TARGET(&p_priv_data->disk_pos), + PS3_PDID(&p_priv_data->disk_pos), + p_priv_data->disk_pos.diskMagicNum, ret); + } + ps3_mutex_lock(&instance->dev_context.dev_priv_lock); + if (sdev->hostdata != NULL) + ps3_kfree(instance, sdev->hostdata); + sdev->hostdata = NULL; + ps3_mutex_unlock(&instance->dev_context.dev_priv_lock); + } + +l_out: + LOG_DEBUG("exit, hno:%u\n", sdev->host->host_no); +} + +static unsigned char ps3_is_nvme_device(struct ps3_instance *instance, + unsigned char dev_type, + unsigned char channel, + unsigned short target_id) +{ + unsigned char ret = PS3_FALSE; + struct PS3VDEntry *vd_entry = NULL; + + if (dev_type == PS3_DEV_TYPE_NVME_SSD) { + ret = PS3_TRUE; + goto l_out; + } + + if (dev_type == PS3_DEV_TYPE_VD) { + vd_entry = ps3_dev_mgr_lookup_vd_info(instance, channel, + target_id); + if (vd_entry == NULL) { + LOG_ERROR("hno:%u cannot found VD[%u:%u] device\n", + PS3_HOST(instance), channel, target_id); + ret = PS3_FALSE; + goto l_out; + } + if (vd_entry->isNvme == 1) { + ret = PS3_TRUE; + goto l_out; + } + + goto l_out; + } + +l_out: + return ret; +} + +static void ps3_nvme_attr_set(const struct ps3_instance *instance, + struct scsi_device *sdev) +{ + unsigned int page_size = instance->cmd_attr.nvme_page_size; + unsigned int align_mask = + (page_size == 0) ? page_size : (page_size - 1); + + LOG_INFO("nvme page size is %u\n", page_size); +#if defined(PS3_BLK_EH_NOT_HANDLED) + queue_flag_set_unlocked(QUEUE_FLAG_NOMERGES, sdev->request_queue); +#else + blk_queue_flag_set(QUEUE_FLAG_NOMERGES, sdev->request_queue); +#endif + blk_queue_virt_boundary(sdev->request_queue, align_mask); +} + +static inline void ps3_nvme_pd_attr_set(struct scsi_device *sdev, + const struct ps3_pd_entry *p_pd_entry) +{ + struct ps3_instance *instance = + (struct ps3_instance *)sdev->host->hostdata; + unsigned int sector_count = 0; + + if (p_pd_entry->max_io_size != 0 && p_pd_entry->sector_size != 0) { + sector_count = p_pd_entry->max_io_size >> PS3_512B_SHIFT; + blk_queue_max_hw_sectors(sdev->request_queue, sector_count); + } + + LOG_INFO( + "nvme attr max_io_size[%u], sector_size[%u], sector_count[%u]\n", + p_pd_entry->max_io_size, p_pd_entry->sector_size, sector_count); + + ps3_nvme_attr_set(instance, sdev); +} + +static inline void ps3_set_queue_depth(struct scsi_device *sdev, + unsigned char dev_type, + unsigned int queue_depth) +{ + int dev_queue_depth = queue_depth; + struct ps3_instance *instance = + (struct ps3_instance *)sdev->host->hostdata; + + dev_queue_depth = + ps3_adjust_queue_depth(instance, dev_type, queue_depth); + +#if defined(PS3_CHANGE_QUEUE_DEPTH) + scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), dev_queue_depth); +#else + scsi_change_queue_depth(sdev, dev_queue_depth); +#endif +} +void ps3_sdev_bdi_stable_writes_set(struct ps3_instance *instance, + struct scsi_device *sdev) +{ +#if defined(PS3_BLK_QUEUE_FLAG_CLEAR) + (void)instance; + blk_queue_flag_set(QUEUE_FLAG_STABLE_WRITES, sdev->request_queue); +#else +#if defined(PS3_BACK_DEV_INFO) + sdev->request_queue->backing_dev_info.capabilities |= + BDI_CAP_STABLE_WRITES; + LOG_INFO("hno:%u, dev type[%u:%u] capabilities[0x%x]\n", + PS3_HOST(instance), sdev->channel, sdev->id, + sdev->request_queue->backing_dev_info.capabilities); +#else + sdev->request_queue->backing_dev_info->capabilities |= + BDI_CAP_STABLE_WRITES; + LOG_INFO("hno:%u, dev type[%u:%u] capabilities[0x%x]\n", + PS3_HOST(instance), sdev->channel, sdev->id, + sdev->request_queue->backing_dev_info->capabilities); +#endif +#endif +} +int ps3_sdev_bdi_stable_writes_get(struct scsi_device *sdev) +{ +#if defined(PS3_BLK_QUEUE_FLAG_CLEAR) + return blk_queue_stable_writes(sdev->request_queue); +#else +#if defined(PS3_BACK_DEV_INFO) + return ((sdev->request_queue->backing_dev_info.capabilities & + BDI_CAP_STABLE_WRITES) == BDI_CAP_STABLE_WRITES); +#else + return ((sdev->request_queue->backing_dev_info->capabilities & + BDI_CAP_STABLE_WRITES) == BDI_CAP_STABLE_WRITES); +#endif +#endif +} + +void ps3_sdev_bdi_stable_writes_clear(struct ps3_instance *instance, + struct scsi_device *sdev) +{ +#if defined(PS3_BLK_QUEUE_FLAG_CLEAR) + (void)instance; + blk_queue_flag_clear(QUEUE_FLAG_STABLE_WRITES, sdev->request_queue); +#else +#if defined(PS3_BACK_DEV_INFO) + sdev->request_queue->backing_dev_info.capabilities &= + ~BDI_CAP_STABLE_WRITES; + ; + LOG_INFO("hno:%u, dev type[%u:%u] capabilities[0x%x]\n", + PS3_HOST(instance), sdev->channel, sdev->id, + sdev->request_queue->backing_dev_info.capabilities); +#else + sdev->request_queue->backing_dev_info->capabilities &= + ~BDI_CAP_STABLE_WRITES; + ; + LOG_INFO("hno:%u, dev type[%u:%u] capabilities[0x%x]\n", + PS3_HOST(instance), sdev->channel, sdev->id, + sdev->request_queue->backing_dev_info->capabilities); +#endif +#endif +} + +int ps3_scsi_slave_configure(struct scsi_device *sdev) +{ + int ret = PS3_SUCCESS; + struct ps3_scsi_priv_data *p_priv_data = NULL; + struct ps3_pd_entry *p_pd_entry = NULL; + struct PS3VDEntry *p_vd_entry = NULL; + unsigned int queue_depth = 0; + unsigned int dma_addr_alignment = 0; + unsigned int dma_len_alignment = 0; + unsigned char io_tmo = PS3_SCSI_CMD_TIMEOUT_DEFAULT; + + struct ps3_instance *instance = + (struct ps3_instance *)sdev->host->hostdata; + if (unlikely(instance == NULL)) { + LOG_ERROR("hno:%u slave configure have no host instance\n", + sdev->host->host_no); + PS3_BUG(); + ret = -ENXIO; + goto l_out; + } + + p_priv_data = (struct ps3_scsi_priv_data *)sdev->hostdata; + if (unlikely(p_priv_data == NULL)) { + LOG_ERROR( + "hno:%u, slave configure have no private data, [%u:%u]\n", + PS3_HOST(instance), sdev->channel, sdev->id); + PS3_BUG(); + ret = -ENXIO; + goto l_out; + } + if (unlikely(p_priv_data->dev_type == PS3_DEV_TYPE_UNKNOWN)) { + LOG_ERROR("hno:%u, dev type[%u:%u] is PS3_DEV_TYPE_UNKNOWN\n", + PS3_HOST(instance), sdev->channel, sdev->id); + ret = -ENXIO; + goto l_out; + } + + if (p_priv_data->dev_type == PS3_DEV_TYPE_VD) { + p_vd_entry = ps3_dev_mgr_lookup_vd_info(instance, sdev->channel, + sdev->id); + if (p_vd_entry == NULL) { + LOG_ERROR( + "hno:%u, cannot found VD[%u:%u] device info\n", + PS3_HOST(instance), sdev->channel, sdev->id); + ret = -ENXIO; + goto l_out; + } + queue_depth = p_vd_entry->devQueDepth; + dma_addr_alignment = ps3_pd_dma_alignment_calc( + p_vd_entry->dmaAddrAlignShift); + dma_len_alignment = + ps3_pd_dma_alignment_calc(p_vd_entry->dmaLenAlignShift); + if (p_vd_entry->bdev_bdi_cap & PS3_STABLE_WRITES_MASK) + ps3_sdev_bdi_stable_writes_set(instance, sdev); + } else { + p_pd_entry = ps3_dev_mgr_lookup_pd_info(instance, sdev->channel, + sdev->id); + if (p_pd_entry == NULL) { + LOG_ERROR( + "hno:%u, cannot found PD[%u:%u] device info\n", + PS3_HOST(instance), sdev->channel, sdev->id); + ret = -ENXIO; + goto l_out; + } + queue_depth = p_pd_entry->dev_queue_depth; + dma_addr_alignment = p_pd_entry->dma_addr_alignment; + dma_len_alignment = p_pd_entry->dma_len_alignment; + } + + blk_queue_dma_alignment(sdev->request_queue, PS3_SCSI_ALINNMENT_MASK); + if (dma_addr_alignment) { + blk_queue_dma_alignment(sdev->request_queue, + dma_addr_alignment - 1); + } + + if (dma_len_alignment) { +#if defined(PS3_BLK_DMA_PAD) + blk_queue_dma_pad(sdev->request_queue, dma_len_alignment - 1); +#else + blk_queue_update_dma_pad(sdev->request_queue, + dma_len_alignment - 1); +#endif + } + if (instance->ctrl_info.ioTimeOut != 0) + io_tmo = instance->ctrl_info.ioTimeOut; + if (ps3_scsi_cmd_timeout_query() != 0) + io_tmo = ps3_scsi_cmd_timeout_query(); + + LOG_INFO("slave_configure, dma_addr_alignment[%d]\n" + "\tdma_len_alignment[%d], io_timeout[%u], queue_depth[%u]\n", + dma_addr_alignment, dma_len_alignment, io_tmo, queue_depth); + + blk_queue_rq_timeout(sdev->request_queue, io_tmo * HZ); + + ps3_set_queue_depth(sdev, p_priv_data->dev_type, queue_depth); + + if (p_priv_data->dev_type == PS3_DEV_TYPE_VD) { + if (p_vd_entry->maxIOSize != 0) { + if (p_vd_entry->sectorSize == PS3_SECTORSIZE_512B) { + blk_queue_max_hw_sectors(sdev->request_queue, + p_vd_entry->maxIOSize); + } else { + blk_queue_max_hw_sectors( + sdev->request_queue, + p_vd_entry->maxIOSize + << (ilog2(p_vd_entry + ->sectorSize) - + PS3_512B_SHIFT)); + } + } else { + LOG_DEBUG( + "hno:%u vd[%u:%u] update max sector num is:0\n", + PS3_HOST(instance), sdev->channel, sdev->id); + } + if (ps3_is_nvme_device(instance, p_priv_data->dev_type, + sdev->channel, sdev->id)) { + ps3_nvme_attr_set(instance, sdev); + } else { +#if defined(PS3_BLK_DMA_PAD) + blk_queue_dma_pad(sdev->request_queue, + dma_len_alignment - 1); +#else + blk_queue_update_dma_pad(sdev->request_queue, + dma_len_alignment - 1); +#endif + } + } else if (p_pd_entry != NULL) { + if (p_priv_data->dev_type == PS3_DEV_TYPE_NVME_SSD) { + ps3_nvme_pd_attr_set(sdev, p_pd_entry); + } else if (PS3_IS_HAC_LIMIT_TYPE(p_priv_data->dev_type)) { + if (p_pd_entry->max_io_size != 0 && + p_pd_entry->sector_size != 0) { + blk_queue_max_hw_sectors( + sdev->request_queue, + p_pd_entry->max_io_size >> + PS3_512B_SHIFT); + } + } + } + + if (ps3_sas_is_support_smp(instance) && p_pd_entry != NULL) { + if (ps3_check_pd_is_vd_member(p_pd_entry->config_flag)) { + LOG_DEBUG("hno:%u, PD[%u:%u] is belong to vd device\n", + PS3_HOST(instance), sdev->channel, sdev->id); + sdev->no_uld_attach = 1; + } + + if (p_priv_data->dev_type == PS3_DEV_TYPE_SAS_HDD || + p_priv_data->dev_type == PS3_DEV_TYPE_SAS_SSD || + p_priv_data->dev_type == PS3_DEV_TYPE_SES) { + LOG_DEBUG( + "hno:%u pd[%u:%u] dev_type[%s] ready read port mode page\n", + PS3_HOST(instance), sdev->channel, sdev->id, + namePS3DevType( + (enum PS3DevType)p_pd_entry->dev_type)); + sas_read_port_mode_page(sdev); + } + } + +l_out: + return ret; +} +#else +void ps3_nvme_attr_set(const struct ps3_instance *instance, + struct scsi_device *sdev) +{ + (void)instance; + (void)sdev; +} +#endif diff --git a/drivers/scsi/linkdata/ps3stor/ps3_device_manager.h b/drivers/scsi/linkdata/ps3stor/ps3_device_manager.h new file mode 100644 index 0000000000000000000000000000000000000000..73c902e548e2d62c496dcb710b5c9283efe76113 --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/ps3_device_manager.h @@ -0,0 +1,563 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _PS3_DEVICE_MANAGER_H_ +#define _PS3_DEVICE_MANAGER_H_ + +#include "ps3_platform_utils.h" +#ifdef _WINDOWS +#include "ps3_dev_adp.h" +#else +#include +#include +#include +#include + +#endif + +#include "ps3_htp_meta.h" +#include "ps3_htp_dev_info.h" +#include "ps3_htp.h" +#include "ps3_err_def.h" +#include "ps3_kernel_version.h" + +#define PS3_QUEUE_DEPTH_DEFAULT (256) +#define PS3_QUEUE_DEPTH_SATA (32) +#define PS3_QUEUE_DEPTH_SAS (64) +#define PS3_QUEUE_DEPTH_NVME (32) + +#define PS3_INVALID_VALUE (0) +#define PS3_INVALID_DEV_ID (0) +#define PS3_INVALID_LIST_ID (0) +#define PS3_DMA_ALIGN_SHIFT_MAX (15) + +#define PS3_VD_TABLE_NUM (2) +#define PS3_SCSI_4B_ALINNMENT_MASK (4) +#define PS3_SCSI_ALINNMENT_MASK (0x3) +#define PS3_SCSI_32B_ALINNMENT_MASK (0x1f) +#define PS3_SCSI_512B_ALINNMENT_MASK (0x000001FF) +#define PS3_SCSI_4K_ALINNMENT_MASK (0xFFF) +#define PS3_512B_SHIFT (9) +#define PS3_SECTORSIZE_512B (512) + +#define PS3_MAX_VD_COUNT(instance) ((instance)->ctrl_info.maxVdCount) +#define PS3_MAX_PD_COUNT(instance) ((instance)->ctrl_info.maxPdCount) + +#define PS3_CHANNEL(pos) ((unsigned char)((pos)->diskDev.ps3Dev.softChan)) +#define PS3_TARGET(pos) ((unsigned short)((pos)->diskDev.ps3Dev.devID)) +#define PS3_DISKID(pos) ((pos)->diskDev.diskID) +#define PS3_PDID(pos) ((pos)->diskDev.ps3Dev.phyDiskID) +#define PS3_VDID(pos) ((pos)->diskDev.ps3Dev.virtDiskID) +#define PS3_DEV(pos) ((struct PS3Dev *)(&(pos)->diskDev.ps3Dev)) +#define PS3_VDID_OFFSET(instance) ((instance)->ctrl_info.offsetOfVDID) + +#define PS3_DEV_INVALID(pos) ((pos).diskDev.diskID == PS3_INVALID_DEV_ID) +#define PS3_PDID_INVALID(pos) (PS3_PDID(pos) == PS3_INVALID_DEV_ID) +#define PS3_VDID_INVALID(pos) (PS3_VDID(pos) == PS3_INVALID_DEV_ID) +#define PS3_IS_VD_CHANNEL(ins, chan) \ + ((ins->dev_context.vd_table[0].vd_idxs[chan] != NULL) ? PS3_DRV_TRUE : \ + PS3_DRV_FALSE) + +#define PS3_IS_PD_CHANNEL(ins, chan) \ + ((ins->dev_context.pd_table.pd_idxs[chan] != NULL) ? PS3_DRV_TRUE : \ + PS3_DRV_FALSE) + +#define PS3_MAX_PD_NUM_ONE_VD (PS3_MAX_PD_COUNT_IN_SPAN * PS3_MAX_SPAN_IN_VD) + +struct ps3_instance; + +#define MR_STREM_BITMAP (0xfedcba9876543210) +#define PS3_IO_MAX_STREAMS_TRACKED (16) +#define BITS_PER_INDEX_STREAM (4) +#define BITS_PER_INDEX_STREAM_SHIFT (2) +#define STREAM_MASK ((1ULL << BITS_PER_INDEX_STREAM) - 1) +#define ZERO_LAST_STREAM (0x0FFFFFFFFFFFFFFF) + +#define MAX_QUE_DEPTH (16) +#define TEST_IO_BLOCK_SIZE (8) +#define IO_STREAM_DETECT_RANGE (MAX_QUE_DEPTH * TEST_IO_BLOCK_SIZE) +#define PS3_IS_HAC_LIMIT_TYPE(type) \ + ((type) == PS3_DEV_TYPE_SAS_HDD || (type) == PS3_DEV_TYPE_SATA_HDD || \ + (type) == PS3_DEV_TYPE_SATA_SSD || (type) == PS3_DEV_TYPE_SAS_SSD || \ + (type) == PS3_DEV_TYPE_SES) + +struct ps3_stream_detect { + unsigned long long next_seq_lba; + unsigned char rw_type; + unsigned char reserved[7]; +}; + + +struct ps3_vd_stream_detect { + spinlock_t ps3_sequence_stream_lock; + unsigned long long mru_bit_map; + struct ps3_stream_detect stream_track[PS3_IO_MAX_STREAMS_TRACKED]; +}; +enum PS3DevType { + PS3_DEV_TYPE_UNKNOWN = 0, + PS3_DEV_TYPE_VD = 1, + PS3_DEV_TYPE_SAS_HDD = 2, + PS3_DEV_TYPE_SATA_HDD = 3, + PS3_DEV_TYPE_SATA_SSD = 4, + PS3_DEV_TYPE_SAS_SSD = 5, + PS3_DEV_TYPE_NVME_SSD = 6, + PS3_DEV_TYPE_SES = 7, + PS3_DEV_TYPE_VEP = 8, + PS3_DEV_TYPE_COUNT, +}; + +enum PS3DevTypeV1 { + PS3_DEV_TYPE_SAS_SATA = 0, + PS3_DEV_TYPE_NVME = 1, +}; + +static inline const char *namePS3DevType(enum PS3DevType e) +{ + static const char * const myNames[] = { + [PS3_DEV_TYPE_UNKNOWN] = "DEV_T_UNKNOWN", + [PS3_DEV_TYPE_VD] = "DEV_T_VD", + [PS3_DEV_TYPE_SAS_HDD] = "DEV_T_SAS_HDD", + [PS3_DEV_TYPE_SATA_HDD] = "DEV_T_SATA_HDD", + [PS3_DEV_TYPE_SATA_SSD] = "DEV_T_SATA_SSD", + [PS3_DEV_TYPE_SAS_SSD] = "DEV_T_SAS_SSD", + [PS3_DEV_TYPE_NVME_SSD] = "DEV_T_NVME_SSD", + [PS3_DEV_TYPE_SES] = "DEV_T_SES", + [PS3_DEV_TYPE_VEP] = "DEV_T_VEP" + }; + + return myNames[e]; +} + +enum ps3_dev_io_stat_type { + PS3_DEV_IO_STAT_TYPE_R_SEND = 1, + PS3_DEV_IO_STAT_TYPE_R_SEND_OK, + PS3_DEV_IO_STAT_TYPE_R_SEND_WAIT, + PS3_DEV_IO_STAT_TYPE_R_SEND_ERR, + PS3_DEV_IO_STAT_TYPE_R_RECV, + PS3_DEV_IO_STAT_TYPE_R_RECV_OK, + PS3_DEV_IO_STAT_TYPE_R_RECV_ERR, + PS3_DEV_IO_STAT_TYPE_R_OK_BYTES, + + PS3_DEV_IO_STAT_TYPE_W_SEND, + PS3_DEV_IO_STAT_TYPE_W_SEND_OK, + PS3_DEV_IO_STAT_TYPE_W_SEND_WAIT, + PS3_DEV_IO_STAT_TYPE_W_SEND_ERR, + PS3_DEV_IO_STAT_TYPE_W_RECV, + PS3_DEV_IO_STAT_TYPE_W_RECV_OK, + PS3_DEV_IO_STAT_TYPE_W_RECV_ERR, + PS3_DEV_IO_STAT_TYPE_W_OK_BYTES, + PS3_DEV_IO_STAT_TYPE_MAX, +}; + +struct ps3_dev_io_statis { + atomic64_t read_send_cnt; + atomic64_t read_send_ok_cnt; + atomic64_t read_send_wait_cnt; + atomic64_t read_send_err_cnt; + atomic64_t read_recv_cnt; + atomic64_t read_recv_ok_cnt; + atomic64_t read_recv_err_cnt; + atomic64_t read_ok_bytes; + + atomic64_t write_send_cnt; + atomic64_t write_send_ok_cnt; + atomic64_t write_send_err_cnt; + atomic64_t write_send_wait_cnt; + atomic64_t write_recv_cnt; + atomic64_t write_recv_ok_cnt; + atomic64_t write_recv_err_cnt; + atomic64_t write_ok_bytes; + + atomic64_t qos_processing_cnt; +}; + +static inline unsigned char ps3_disk_type(enum PS3DevType e) +{ + unsigned char disk_type = PS3_DISK_TYPE_UNKNOWN; + + switch (e) { + case PS3_DEV_TYPE_VD: + disk_type = PS3_DISK_TYPE_VD; + break; + case PS3_DEV_TYPE_SAS_HDD: + case PS3_DEV_TYPE_SATA_HDD: + case PS3_DEV_TYPE_SATA_SSD: + case PS3_DEV_TYPE_SAS_SSD: + case PS3_DEV_TYPE_NVME_SSD: + case PS3_DEV_TYPE_SES: + case PS3_DEV_TYPE_VEP: + disk_type = PS3_DISK_TYPE_PD; + break; + default: + disk_type = PS3_DISK_TYPE_UNKNOWN; + break; + } + return disk_type; +} + +static inline unsigned char ps3_is_fake_pd(unsigned char dev_type) +{ + return (dev_type == PS3_DEV_TYPE_SES || dev_type == PS3_DEV_TYPE_VEP); +} + +struct ps3_r1x_read_balance_info { + atomic_t scsi_outstanding_cmds[PS3_MAX_PD_NUM_ONE_VD + 1]; + unsigned long long last_accessed_block[PS3_MAX_PD_NUM_ONE_VD + 1]; +}; + +struct ps3_r1x_lock_mgr { + int (*try_lock)(struct ps3_r1x_lock_mgr *mgr, void *cmd); + int (*resend_try_lock)(struct ps3_r1x_lock_mgr *mgr, void *cmd); + void (*unlock)(struct ps3_r1x_lock_mgr *mgr, void *cmd); + spinlock_t mgr_lock; + void *hash_mgr; + struct list_head conflict_cmd_list; + struct task_struct *conflict_send_th; + void *hash_mgr_conflict; + int force_ret_code; + unsigned int cmd_count_in_q; + unsigned char dev_deling; + unsigned char thread_stop; +#ifdef _WINDOWS + KEVENT thread_sync; +#else + struct completion thread_sync; +#endif +}; + +struct ps3_scsi_priv_data { + struct PS3DiskDevPos disk_pos; + struct ps3_dev_io_statis statis; + struct ps3_vd_stream_detect vd_sd[2]; + struct ps3_r1x_lock_mgr lock_mgr; + struct ps3_r1x_read_balance_info *r1x_rb_info; + unsigned char is_taskmgmt_enable; + unsigned char task_manager_busy; + unsigned char dev_type; + unsigned char task_abort_timeout; + unsigned char task_reset_timeout; + unsigned char encl_id; + unsigned char phy_id; + unsigned char reserved; + atomic_t rd_io_outstand; + atomic_t wr_io_outstand; + atomic_t r1x_read_cmd_swap_total_cnt; + atomic_t r1x_read_cmd_swap_res_cnt; +#if defined DRIVER_SUPPORT_PRIV_BUSY + atomic_t sdev_priv_busy; +#else + unsigned char reserved1[4]; +#endif + unsigned long ata_cmd_busy; + unsigned char dev_deling; + unsigned char swap_flag; + unsigned short qmask_count; + unsigned char reserved2[6]; +}; + +#ifdef _WINDOWS + +struct scsi_device_real { + struct scsi_device sdev; + struct ps3_scsi_priv_data hostdata; +}; + +#define PS3_SDEV_PRI_DATA(sdev) \ + ((struct ps3_scsi_priv_data *)&( \ + ((struct scsi_device_real *)(sdev))->hostdata)) +#define PS3_SDEV_POS(sdev) (&(PS3_SDEV_PRI_DATA(sdev)->disk_pos)) +#define PS3_SDEV_CHANNEL(sdev) (PS3_CHANNEL(PS3_SDEV_POS(sdev))) +#define PS3_SDEV_TARGET(sdev) (PS3_TARGET(PS3_SDEV_POS(sdev))) +#define PS3_SDEV_PDID(sdev) (PS3_PDID(PS3_SDEV_POS(sdev))) +#define PS3_SDEV_MAGIC(sdev) (PS3_SDEV_POS(sdev)->diskMagicNum) +#else +#define PS3_SDEV_CHANNEL(sdev) ((sdev)->channel) +#define PS3_SDEV_TARGET(sdev) ((sdev)->id) + +#define PS3_SDEV_PRI_DATA(sdev) \ + ((struct ps3_scsi_priv_data *)((sdev)->hostdata)) +#endif + +struct ps3_channel { + unsigned char channel; + unsigned char reserved0; + unsigned short max_dev_num; + unsigned short channel_start_num; + unsigned char reserved1[2]; +}; + +struct ps3_dev_pool { + union PS3Device *devs_buffer; + union PS3Device *devs[PS3_MAX_CHANNEL_NUM]; +}; + +struct ps3_pd_entry { + struct PS3DiskDevPos disk_pos; + unsigned char state; + unsigned char config_flag; + unsigned char RWCT; + unsigned char scsi_interface_type; + unsigned char task_abort_timeout; + unsigned char task_reset_timeout; + unsigned char dev_type; + union { + struct { + unsigned char support_ncq : 1; + unsigned char protect : 1; + unsigned char is_direct_disable : 1; + unsigned char reserved : 5; + }; + unsigned char pd_flags; + }; + unsigned int max_io_size; + unsigned int dev_queue_depth; + unsigned char encl_id; + unsigned char phy_id; + unsigned short sector_size; + unsigned short dma_addr_alignment; + unsigned short dma_len_alignment; + struct sas_rphy *sas_rphy; + unsigned short normal_quota; + unsigned short direct_quota; +}; + +struct ps3_pd_table { + unsigned short *pd_idxs_array; + unsigned short *pd_idxs[PS3_MAX_CHANNEL_NUM]; +}; + +struct ps3_vd_table { + unsigned short *vd_idxs_array; + unsigned short *vd_idxs[PS3_MAX_CHANNEL_NUM]; +}; + +struct ps3_pri_data_table { + struct ps3_scsi_priv_data **vd_pri_data_idxs_array; + struct ps3_scsi_priv_data **vd_pri_data_idxs[PS3_MAX_CHANNEL_NUM]; +}; + +struct ps3_dev_context { + struct ps3_vd_table vd_table[PS3_VD_TABLE_NUM]; + struct ps3_pd_table pd_table; + struct PS3VDEntry *vd_entries_array[PS3_VD_TABLE_NUM]; + struct ps3_pd_entry *pd_entries_array; + struct ps3_channel channel_pd[PS3_MAX_CHANNEL_NUM]; + struct ps3_channel channel_vd[PS3_MAX_CHANNEL_NUM]; + struct ps3_pri_data_table vd_pri_data_table; + + unsigned short max_dev_in_channel[PS3_MAX_CHANNEL_NUM]; + atomic_t subwork; + unsigned char reserved[3]; + struct ps3_dev_pool vd_pool; + struct ps3_dev_pool pd_pool; + + dma_addr_t pd_list_buf_phys; + struct PS3DevList *pd_list_buf; + dma_addr_t vd_list_buf_phys; + struct PS3DevList *vd_list_buf; + dma_addr_t pd_info_buf_phys; + struct PS3PDInfo *pd_info_buf; + dma_addr_t vd_info_buf_phys_sync; + struct PS3VDInfo *vd_info_buf_sync; + dma_addr_t vd_info_buf_phys_async; + struct PS3VDInfo *vd_info_buf_async; + + struct ps3_cmd *vd_pending_cmd; + struct ps3_cmd *vdpending_abort_cmd; + atomic_t abort_vdpending_cmd; + atomic_t is_vdpending_abort; + + unsigned short total_vd_count; + unsigned short total_pd_count; + unsigned short max_dev_per_channel; + unsigned char vd_table_idx; + unsigned char pd_channel_count; + unsigned char vd_channel_count; + struct mutex dev_priv_lock; +#ifdef _WINDOWS + unsigned char channel_map_rang_num; + unsigned char total_os_channel; + struct ps3_windows_private_table windows_table; + + unsigned char reserved2[3]; + KEVENT disk_sync; + spinlock_t dev_lock; +#else + unsigned char reserved2[7]; +#endif + struct mutex dev_scan_lock; +}; + +int ps3_dev_mgr_cli_register(void); + +int ps3_device_mgr_init(struct ps3_instance *instance); + +void ps3_device_mgr_exit(struct ps3_instance *instance); + +int ps3_device_mgr_data_init(struct ps3_instance *instance); + +int ps3_device_mgr_data_exit(struct ps3_instance *instance); + +int ps3_dev_mgr_vd_info_subscribe(struct ps3_instance *instance); + +int ps3_dev_mgr_vd_info_unsubscribe(struct ps3_instance *instance); +int ps3_dev_mgr_vd_info_resubscribe(struct ps3_instance *instance); + +void ps3_dev_mgr_vd_info_clear(struct ps3_instance *instance); + +int ps3_dev_mgr_pd_info_get(struct ps3_instance *instance, + unsigned short channel, unsigned short target_id, + unsigned short pd_id); + +struct ps3_pd_entry * +ps3_dev_mgr_pd_info_find_by_id(struct ps3_instance *instance, + unsigned short disk_id); + +int ps3_dev_mgr_pd_list_get(struct ps3_instance *instance); + +int ps3_dev_mgr_vd_list_get(struct ps3_instance *instance); + +int ps3_vd_info_get_all(struct ps3_instance *instance); + +static inline unsigned short get_offset_of_vdid(unsigned short offsetOfVDID, + unsigned short virtDiskID) +{ + unsigned short virtDiskIdx = PS3_INVALID_DEV_ID; + + if (virtDiskID >= offsetOfVDID) + virtDiskIdx = virtDiskID - offsetOfVDID; + + return virtDiskIdx; +} + +unsigned char ps3_dev_id_valid_check(struct ps3_instance *instance, + unsigned char channel, + unsigned short target_id, + unsigned char dev_type); + +unsigned char ps3_get_vd_raid_level(struct ps3_instance *instance, + unsigned char channel, + unsigned short target_id); + +struct ps3_scsi_priv_data * +ps3_dev_mgr_lookup_vd_pri_data(struct ps3_instance *instance, + unsigned char channel, unsigned short target_id); + +struct PS3VDEntry *ps3_dev_mgr_lookup_vd_info(struct ps3_instance *instance, + unsigned char channel, + unsigned short target_id); + +struct ps3_pd_entry *ps3_dev_mgr_lookup_pd_info(struct ps3_instance *instance, + unsigned char channel, + unsigned short target_id); + +struct PS3VDEntry * +ps3_dev_mgr_lookup_vd_info_by_id(struct ps3_instance *instance, + unsigned short disk_id); + +struct ps3_pd_entry * +ps3_dev_mgr_lookup_pd_info_by_id(struct ps3_instance *instance, + unsigned short disk_id); + +union PS3Device *ps3_dev_mgr_lookup_vd_list(struct ps3_instance *instance, + unsigned char channel, + unsigned short target_id); + +union PS3Device *ps3_dev_mgr_lookup_pd_list(struct ps3_instance *instance, + unsigned char channel, + unsigned short target_id); + +#ifndef _WINDOWS +#if defined(PS3_CHANGE_QUEUE_DEPTH) +int ps3_change_queue_depth(struct scsi_device *sdev, int queue_depth, + int reason); +#else +int ps3_change_queue_depth(struct scsi_device *sdev, int queue_depth); +#endif + +int ps3_scsi_slave_alloc(struct scsi_device *sdev); + +void ps3_scsi_slave_destroy(struct scsi_device *sdev); + +int ps3_scsi_slave_configure(struct scsi_device *sdev); +#endif +static inline unsigned char ps3_check_pd_is_vd_member(unsigned char config_flag) +{ + return (config_flag != MIC_PD_STATE_JBOD && + config_flag != MIC_PD_STATE_READY && + config_flag != MIC_PD_STATE_UNKNOWN); +} + +static inline unsigned char ps3_get_converted_dev_type(unsigned char driverType, + unsigned char mediumType) +{ + unsigned char dev_type = PS3_DEV_TYPE_UNKNOWN; + + switch (driverType) { + case DRIVER_TYPE_SAS: + if (mediumType == DEVICE_TYPE_HDD) + dev_type = PS3_DEV_TYPE_SAS_HDD; + else if (mediumType == DEVICE_TYPE_SSD) + dev_type = PS3_DEV_TYPE_SAS_SSD; + break; + case DRIVER_TYPE_SATA: + if (mediumType == DEVICE_TYPE_HDD) + dev_type = PS3_DEV_TYPE_SATA_HDD; + else if (mediumType == DEVICE_TYPE_SSD) + dev_type = PS3_DEV_TYPE_SATA_SSD; + break; + case DRIVER_TYPE_SES: + if (mediumType == DEVICE_TYPE_ENCLOSURE) + dev_type = PS3_DEV_TYPE_SES; + break; + case DRIVER_TYPE_VEP: + if (mediumType == DEVICE_TYPE_ENCLOSURE) + dev_type = PS3_DEV_TYPE_VEP; + break; + case DRIVER_TYPE_NVME: + if (mediumType == DEVICE_TYPE_SSD) + dev_type = PS3_DEV_TYPE_NVME_SSD; + break; + default: + break; + } + return dev_type; +}; + +void ps3_change_sdev_max_sector(struct ps3_instance *instance, + struct PS3VDEntry *vd_entry); + +void ps3_vd_info_show(const struct ps3_instance *instance, + const struct PS3VDEntry *vd_entry); +int ps3_adjust_queue_depth(struct ps3_instance *instance, + unsigned char dev_type, unsigned int queue_depth); + +static inline const char * +ps3_get_vd_access_plolicy_str(enum VDAccessPolicy policy) +{ + static const char *const vdAccessPolicyName[] = { + [VD_ACCESS_POLICY_READ_WRITE] = "RW", + [VD_ACCESS_POLICY_READ_ONLY] = "RO", + [VD_ACCESS_POLICY_BLOCK] = "BLOCK", + [VD_ACCESS_POLICY_REMOVE_ACCESS] = "REMOVE_ACCESS", + }; + + return (policy < ARRAY_SIZE(vdAccessPolicyName)) ? + vdAccessPolicyName[policy] : + "Unknown"; +} + +static inline unsigned char ps3_is_hdd_pd(unsigned char dev_type) +{ + return (dev_type == PS3_DEV_TYPE_SAS_HDD || + dev_type == PS3_DEV_TYPE_SATA_HDD); +} + +void ps3_vd_busy_scale_get(struct PS3VDEntry *vd_entry); +void ps3_sdev_bdi_stable_writes_set(struct ps3_instance *instance, + struct scsi_device *sdev); + +void ps3_sdev_bdi_stable_writes_clear(struct ps3_instance *instance, + struct scsi_device *sdev); + +int ps3_sdev_bdi_stable_writes_get(struct scsi_device *sdev); + +#endif diff --git a/drivers/scsi/linkdata/ps3stor/ps3_device_manager_sas.c b/drivers/scsi/linkdata/ps3stor/ps3_device_manager_sas.c new file mode 100644 index 0000000000000000000000000000000000000000..35aa89860707798d7d56a5298651b22ad1895885 --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/ps3_device_manager_sas.c @@ -0,0 +1,2161 @@ +// SPDX-License-Identifier: GPL-2.0 +#ifndef _WINDOWS + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "ps3_device_manager_sas.h" +#include "ps3_sas_transport.h" +#include "ps3_util.h" +#include "ps3_mgr_cmd.h" + +static int ps3_sas_expander_event_update(struct ps3_instance *instance, + unsigned char encl_id); + +unsigned char ps3_sas_is_support_smp(struct ps3_instance *instance) +{ + return instance->sas_dev_context.is_support_smp; +} + +static inline unsigned char +ps3_is_sata_end_device(struct ps3_pd_entry *pd_entry) +{ + return (pd_entry->dev_type == PS3_DEV_TYPE_SATA_HDD || + pd_entry->dev_type == PS3_DEV_TYPE_SATA_SSD); +} + +int ps3_sas_rphy_slot_get(struct ps3_instance *instance, + unsigned long long sas_addr, unsigned int *slot_id) +{ + int ret = -PS3_FAILED; + struct ps3_sas_node *ps3_sas_node = + &instance->sas_dev_context.ps3_hba_sas; + struct ps3_sas_port *ps3_sas_port = NULL; + unsigned long flags = 0; + + spin_lock_irqsave(&instance->sas_dev_context.ps3_sas_node_lock, flags); + + list_for_each_entry(ps3_sas_port, &ps3_sas_node->sas_port_list, list) { + if (ps3_sas_port->remote_identify.sas_address == sas_addr) { + *slot_id = ps3_sas_node + ->phys[ps3_sas_port->remote_identify + .phy_identifier] + .slot_id; + ret = PS3_SUCCESS; + goto l_out; + } + } + + list_for_each_entry(ps3_sas_node, + &instance->sas_dev_context.ps3_sas_node_list, + list) { + list_for_each_entry(ps3_sas_port, &ps3_sas_node->sas_port_list, + list) { + if (ps3_sas_port->remote_identify.sas_address == + sas_addr) { + *slot_id = + ps3_sas_node + ->phys[ps3_sas_port + ->remote_identify + .phy_identifier] + .slot_id; + ret = PS3_SUCCESS; + goto l_out; + } + } + } +l_out: + spin_unlock_irqrestore(&instance->sas_dev_context.ps3_sas_node_lock, + flags); + + return ret; +} + +unsigned long long +ps3_sas_rphy_parent_sas_addr_get(struct ps3_instance *instance, + unsigned long long sas_addr) +{ + struct ps3_sas_node *ps3_sas_node = NULL; + struct ps3_sas_port *ps3_sas_port = NULL; + struct ps3_sas_phy *ps3_sas_phy = NULL; + + unsigned long long encl_id = PS3_SAS_INVALID_SAS_ADDR; + unsigned long flags = 0; + + spin_lock_irqsave(&instance->sas_dev_context.ps3_sas_node_lock, flags); + + list_for_each_entry( + ps3_sas_port, + &instance->sas_dev_context.ps3_hba_sas.sas_port_list, list) { + if (ps3_sas_port->remote_identify.sas_address != sas_addr) + continue; + + list_for_each_entry(ps3_sas_phy, &ps3_sas_port->phy_list, + port_siblings) { + if (ps3_sas_phy->remote_identify.sas_address == + sas_addr) { + encl_id = ps3_sas_phy->identify.sas_address; + goto l_out; + } + } + } + + list_for_each_entry(ps3_sas_node, + &instance->sas_dev_context.ps3_sas_node_list, + list) { + list_for_each_entry(ps3_sas_port, &ps3_sas_node->sas_port_list, + list) { + if (ps3_sas_port->remote_identify.sas_address == + sas_addr) { + encl_id = ps3_sas_node->sas_address; + goto l_out; + } + } + } +l_out: + spin_unlock_irqrestore(&instance->sas_dev_context.ps3_sas_node_lock, + flags); + + return encl_id; +} + +unsigned char ps3_sas_encl_id_get(struct ps3_instance *instance, + unsigned long long sas_addr) +{ + unsigned char encl_id = PS3_SAS_INVALID_ID; + struct ps3_sas_node *ps3_sas_node = NULL; + unsigned long flags = 0; + unsigned char i = 0; + + for (i = 0; i < PS3_SAS_HBA_MAX_SAS_NUM; i++) { + if (instance->sas_dev_context.ps3_hba_sas_addr[i] == sas_addr) + return instance->sas_dev_context.ps3_hba_sas.encl_id; + } + + spin_lock_irqsave(&instance->sas_dev_context.ps3_sas_node_lock, flags); + list_for_each_entry(ps3_sas_node, + &instance->sas_dev_context.ps3_sas_node_list, + list) { + if (ps3_sas_node->sas_address == sas_addr) { + encl_id = ps3_sas_node->encl_id; + goto l_out; + } + } +l_out: + spin_unlock_irqrestore(&instance->sas_dev_context.ps3_sas_node_lock, + flags); + + return encl_id; +} + +static void ps3_sas_node_phy_init(struct ps3_sas_phy *ps3_phy, + struct ps3_sas_node *sas_node, + struct PS3PhyInfo *phy_info) +{ + ps3_phy->phy_id = phy_info->phyId; + ps3_phy->encl_id = sas_node->encl_id; + ps3_phy->slot_id = phy_info->slotId; + + ps3_phy->identify.device_type = sas_node->dev_type; + ps3_phy->identify.sas_address = le64_to_cpu(phy_info->sasAddr); + ps3_phy->identify.initiator_port_protocols = + phy_info->initiatorPortProtocol; + ps3_phy->identify.target_port_protocols = phy_info->targetPortProtocols; + ps3_phy->identify.phy_identifier = phy_info->phyId; + + if (phy_info->attachedSasAddr != PS3_SAS_INVALID_SAS_ADDR) { + ps3_phy->remote_identify.device_type = phy_info->attachDevType; + ps3_phy->remote_identify.sas_address = + le64_to_cpu(phy_info->attachedSasAddr); + ps3_phy->remote_identify.initiator_port_protocols = + phy_info->attachInitiatorPortProtocol; + ps3_phy->remote_identify.target_port_protocols = + phy_info->attachTargetPortProtocols; + ps3_phy->remote_identify.phy_identifier = phy_info->phyId; + } +} + +static int ps3_sas_node_phy_add(struct ps3_instance *instance, + struct ps3_sas_phy *ps3_phy, + struct ps3_sas_node *sas_node, + struct PS3PhyInfo *phy_info) +{ + int ret = PS3_SUCCESS; + struct sas_phy *sas_phy = NULL; + + INIT_LIST_HEAD(&ps3_phy->port_siblings); + sas_phy = sas_phy_alloc(sas_node->dev, phy_info->phyId); + if (sas_phy == NULL) { + LOG_ERROR("hno:%u alloc node[%d] phys[%d] buffer failed !\n", + PS3_HOST(instance), sas_node->encl_id, + phy_info->phyId); + ret = -PS3_ENOMEM; + goto l_out; + } + + ps3_sas_node_phy_init(ps3_phy, sas_node, phy_info); + sas_phy->identify = ps3_phy->identify; + + sas_phy->negotiated_linkrate = phy_info->negLinkRate; + sas_phy->minimum_linkrate = phy_info->minLinkRate; + sas_phy->maximum_linkrate = phy_info->maxLinkRate; + sas_phy->minimum_linkrate_hw = phy_info->minLinkRateHw; + sas_phy->maximum_linkrate_hw = phy_info->maxLinkRateHw; + + LOG_INFO( + "hno:%u phy %d in encl[%d], dev_type[%d]\n" + "\tsas_addr[%016llx], n_linkrate[%d], slot_id[%d], i_prol[%d], t_prol[%d]\n" + "\tremote_dev_type[%d], remote_sas_addr[%016llx], i_prol[%d], t_prol[%d]\n" + "\tmin_linkr[%u:%u], max_linikr[%u:%u] !\n", + PS3_HOST(instance), sas_phy->identify.phy_identifier, + sas_node->encl_id, sas_phy->identify.device_type, + sas_phy->identify.sas_address, sas_phy->negotiated_linkrate, + phy_info->slotId, sas_phy->identify.initiator_port_protocols, + sas_phy->identify.target_port_protocols, + ps3_phy->remote_identify.device_type, + ps3_phy->remote_identify.sas_address, + ps3_phy->remote_identify.initiator_port_protocols, + ps3_phy->remote_identify.target_port_protocols, + sas_phy->minimum_linkrate, sas_phy->minimum_linkrate_hw, + sas_phy->maximum_linkrate, sas_phy->maximum_linkrate_hw); + + ret = sas_phy_add(sas_phy); + if (ret != 0) { + LOG_ERROR("hno:%u add node[%d]-phys[%d] failed ret[%d] !\n", + PS3_HOST(instance), sas_node->encl_id, + phy_info->phyId, ret); + sas_phy_free(sas_phy); + goto l_out; + } + + ps3_phy->phy = sas_phy; +l_out: + return ret; +} + +void ps3_sas_node_phy_update(struct ps3_instance *instance, + struct ps3_sas_phy *ps3_phy, + struct PS3PhyInfo *phy_info) +{ + (void)instance; + ps3_phy->identify.initiator_port_protocols = + phy_info->initiatorPortProtocol; + ps3_phy->identify.target_port_protocols = phy_info->targetPortProtocols; + ps3_phy->remote_identify.device_type = phy_info->attachDevType; + ps3_phy->remote_identify.sas_address = phy_info->attachedSasAddr; + ps3_phy->remote_identify.initiator_port_protocols = + phy_info->attachInitiatorPortProtocol; + ps3_phy->remote_identify.target_port_protocols = + phy_info->attachTargetPortProtocols; + ps3_phy->remote_identify.phy_identifier = phy_info->phyId; + ps3_phy->phy->identify = ps3_phy->identify; + + ps3_phy->phy->negotiated_linkrate = phy_info->negLinkRate; + ps3_phy->phy->enabled = phy_info->enable; + ps3_phy->phy->minimum_linkrate = phy_info->minLinkRate; + ps3_phy->phy->maximum_linkrate = phy_info->maxLinkRate; + ps3_phy->phy->minimum_linkrate_hw = phy_info->minLinkRateHw; + ps3_phy->phy->maximum_linkrate_hw = phy_info->maxLinkRateHw; + + LOG_INFO_IN_IRQ( + instance, + "update phy %d, dev_type[%d], enable[%d]\n" + "\tsas_addr[%016llx], n_linkrate[%d], slot_id[%d], i_prol[%d], t_prol[%d]\n" + "\tremote_dev_type[%d], remote_sas_addr[%016llx], i_prol[%d], t_prol[%d]\n" + "\tmin_linkr[%u:%u], max_linikr[%u:%u] !\n", + ps3_phy->phy->identify.phy_identifier, + ps3_phy->phy->identify.device_type, ps3_phy->phy->enabled, + ps3_phy->phy->identify.sas_address, + ps3_phy->phy->negotiated_linkrate, phy_info->slotId, + ps3_phy->phy->identify.initiator_port_protocols, + ps3_phy->phy->identify.target_port_protocols, + ps3_phy->remote_identify.device_type, + ps3_phy->remote_identify.sas_address, + ps3_phy->remote_identify.initiator_port_protocols, + ps3_phy->remote_identify.target_port_protocols, + ps3_phy->phy->minimum_linkrate, + ps3_phy->phy->minimum_linkrate_hw, + ps3_phy->phy->maximum_linkrate, + ps3_phy->phy->maximum_linkrate_hw); +} + +static void ps3_sas_port_phy_update(struct ps3_instance *instance, + struct ps3_sas_node *sas_node) +{ + struct ps3_sas_port *ps3_sas_port = NULL; + struct ps3_sas_port *ps3_sas_port_next = NULL; + unsigned char i = 0; + unsigned long flags = 0; + + for (i = 0; i < sas_node->phy_count; i++) { + if (sas_node->phys[i].remote_identify.sas_address == 0 || + sas_node->phys[i].remote_identify.device_type == + SAS_END_DEVICE) { + continue; + } + + if (sas_node->phys[i].attach_port != NULL) + continue; + + list_for_each_entry_safe(ps3_sas_port, ps3_sas_port_next, + &sas_node->sas_port_list, list) { + if (sas_node->phys[i].remote_identify.sas_address == + ps3_sas_port->remote_identify.sas_address) { + spin_lock_irqsave(&instance->sas_dev_context + .ps3_sas_node_lock, + flags); + list_add_tail(&sas_node->phys[i].port_siblings, + &ps3_sas_port->phy_list); + sas_node->phys[i].attach_port = ps3_sas_port; + spin_unlock_irqrestore( + &instance->sas_dev_context + .ps3_sas_node_lock, + flags); + ps3_sas_port->phy_count++; + sas_port_add_phy(ps3_sas_port->port, + sas_node->phys[i].phy); + } + } + } +} + +static struct ps3_sas_port *ps3_sas_port_find(struct ps3_instance *instance, + struct ps3_sas_node *exp_node, + unsigned long long sas_addr) +{ + struct ps3_sas_port *ps3_sas_port = NULL; + unsigned long flags = 0; + + spin_lock_irqsave(&instance->sas_dev_context.ps3_sas_node_lock, flags); + list_for_each_entry(ps3_sas_port, &exp_node->sas_port_list, list) { + if (ps3_sas_port->remote_identify.sas_address == sas_addr) { + spin_unlock_irqrestore( + &instance->sas_dev_context.ps3_sas_node_lock, + flags); + return ps3_sas_port; + } + } + spin_unlock_irqrestore(&instance->sas_dev_context.ps3_sas_node_lock, + flags); + + return NULL; +} + +static void ps3_sas_port_phy_add(struct ps3_instance *instance, + struct ps3_sas_port *ps3_sas_port, + struct ps3_sas_phy *ps3_phy) +{ + struct ps3_sas_phy *tmp_phy = NULL; + unsigned long flags = 0; + + spin_lock_irqsave(&instance->sas_dev_context.ps3_sas_node_lock, flags); + list_for_each_entry(tmp_phy, &ps3_sas_port->phy_list, port_siblings) { + if (tmp_phy == ps3_phy) { + spin_unlock_irqrestore( + &instance->sas_dev_context.ps3_sas_node_lock, + flags); + return; + } + } + list_add_tail(&ps3_phy->port_siblings, &ps3_sas_port->phy_list); + ps3_phy->attach_port = ps3_sas_port; + spin_unlock_irqrestore(&instance->sas_dev_context.ps3_sas_node_lock, + flags); + + ps3_sas_port->phy_count++; + + sas_port_add_phy(ps3_sas_port->port, ps3_phy->phy); +} + +static struct sas_port * +ps3_sas_sas_port_create(struct ps3_instance *instance, + struct ps3_sas_node *parent_node, + struct list_head *phy_list) +{ + int ret = PS3_SUCCESS; + struct sas_port *sas_port = NULL; + struct ps3_sas_phy *ps3_phy = NULL; + + sas_port = sas_port_alloc_num(parent_node->dev); + if (sas_port == NULL) { + LOG_ERROR("hno:%u alloc sas_port on node[%d] failed !\n", + PS3_HOST(instance), parent_node->encl_id); + goto l_out; + } + + ret = sas_port_add(sas_port); + if (ret != 0) { + LOG_ERROR("hno:%u add sas_port on node[%d] failed !\n", + PS3_HOST(instance), parent_node->encl_id); + goto l_failed; + } + + list_for_each_entry(ps3_phy, phy_list, port_siblings) { + LOG_DEBUG( + "hno:%u add phy[%d] in sas_port[%016llx] on node[%d] !\n", + PS3_HOST(instance), ps3_phy->phy_id, + ps3_phy->remote_identify.sas_address, + parent_node->encl_id); + sas_port_add_phy(sas_port, ps3_phy->phy); + } + + return sas_port; +l_failed: + sas_port_delete(sas_port); +l_out: + return NULL; +} + +static int ps3_sas_end_device_add_past(struct ps3_instance *instance, + struct ps3_sas_port *ps3_sas_port, + struct ps3_pd_entry *pd_entry, + struct sas_rphy *sas_rphy) +{ + int ret = PS3_SUCCESS; + + if (ps3_sas_port->remote_identify.device_type != SAS_END_DEVICE) { + ret = PS3_SUCCESS; + goto l_out; + } + + sas_rphy->identify = ps3_sas_port->remote_identify; + ret = ps3_scsi_add_device_ack(instance, &pd_entry->disk_pos, + PS3_DISK_TYPE_PD); + if (unlikely(ret != PS3_SUCCESS)) { + LOG_ERROR("hno:%u rphy sas_addr[%016llx] end-device[%u:%u:%u]\n" + "\tmagic[%#x] add scsi device ack NOK, ret %d\n", + PS3_HOST(instance), + ps3_sas_port->remote_identify.sas_address, + PS3_CHANNEL(&pd_entry->disk_pos), + PS3_TARGET(&pd_entry->disk_pos), + PS3_PDID(&pd_entry->disk_pos), + pd_entry->disk_pos.diskMagicNum, ret); + ret = -PS3_ACTIVE_ERR; + } else { + LOG_WARN( + "hno:%u rphy sas_addr[%016llx] end-device[%u:%u:%u] add begin\n", + PS3_HOST(instance), + ps3_sas_port->remote_identify.sas_address, + PS3_CHANNEL(&pd_entry->disk_pos), + PS3_TARGET(&pd_entry->disk_pos), + PS3_PDID(&pd_entry->disk_pos)); + scsi_scan_target(&sas_rphy->dev, + PS3_CHANNEL(&pd_entry->disk_pos), + PS3_TARGET(&pd_entry->disk_pos), 0, + SCSI_SCAN_INITIAL); + LOG_WARN( + "hno:%u rphy sas_addr[%016llx] end-device[%u:%u:%u] add end\n", + PS3_HOST(instance), + ps3_sas_port->remote_identify.sas_address, + PS3_CHANNEL(&pd_entry->disk_pos), + PS3_TARGET(&pd_entry->disk_pos), + PS3_PDID(&pd_entry->disk_pos)); + } +l_out: + return ret; +} + +static int ps3_sas_port_rphy_create(struct ps3_instance *instance, + struct ps3_sas_port *ps3_sas_port) +{ + int ret = -PS3_FAILED; + struct sas_rphy *sas_rphy = NULL; + struct ps3_pd_entry *pd_entry = NULL; + + LOG_DEBUG( + "hno:%u enter port rphy create, type[%d], pdflatid[%d], sas_addr[%016llx]\n", + PS3_HOST(instance), ps3_sas_port->remote_identify.device_type, + ps3_sas_port->pd_flat_id, + ps3_sas_port->remote_identify.sas_address); + + if (ps3_sas_port->remote_identify.device_type == SAS_END_DEVICE) { + sas_rphy = sas_end_device_alloc(ps3_sas_port->port); + } else { + sas_rphy = sas_expander_alloc( + ps3_sas_port->port, + (enum sas_device_type) + ps3_sas_port->remote_identify.device_type); + } + if (unlikely(sas_rphy == NULL)) { + LOG_ERROR( + "hno:%u alloc SAS rphy for sas_addr[%016llx] failed !\n", + PS3_HOST(instance), + ps3_sas_port->remote_identify.sas_address); + goto l_out; + } + + sas_rphy->identify = ps3_sas_port->remote_identify; + + if (ps3_sas_port->remote_identify.device_type == SAS_END_DEVICE) { + sas_rphy->identify.target_port_protocols = SAS_PROTOCOL_NONE; + pd_entry = ps3_dev_mgr_lookup_pd_info_by_id( + instance, ps3_sas_port->pd_flat_id); + if (unlikely(pd_entry == NULL)) { + LOG_ERROR( + "hno:%u cannot find pd entry by pd_flat_id[%d] !\n", + PS3_HOST(instance), ps3_sas_port->pd_flat_id); + goto l_out; + } + } + + ret = sas_rphy_add(sas_rphy); + if (unlikely(ret != 0)) { + LOG_ERROR( + "hno:%u add SAS rphy for sas_addr[%016llx] failed !\n", + PS3_HOST(instance), + ps3_sas_port->remote_identify.sas_address); + goto l_out; + } + + ret = ps3_sas_end_device_add_past(instance, ps3_sas_port, pd_entry, + sas_rphy); + +l_out: + ps3_sas_port->rphy = sas_rphy; + LOG_DEBUG( + "hno:%u quit port rphy create, type[%d], pdflatid[%d], sas_addr[%016llx]\n", + PS3_HOST(instance), ps3_sas_port->remote_identify.device_type, + ps3_sas_port->pd_flat_id, + ps3_sas_port->remote_identify.sas_address); + return ret; +} + +struct ps3_sas_node * +ps3_sas_find_node_by_sas_addr(struct ps3_instance *instance, + unsigned long long sas_addr) +{ + struct ps3_sas_node *ps3_sas_node = NULL; + struct ps3_sas_node *ret_node = NULL; + unsigned long flags = 0; + unsigned char i = 0; + + for (i = 0; i < PS3_SAS_HBA_MAX_SAS_NUM; i++) { + if (instance->sas_dev_context.ps3_hba_sas_addr[i] == sas_addr) { + ret_node = &instance->sas_dev_context.ps3_hba_sas; + goto l_out; + } + } + + spin_lock_irqsave(&instance->sas_dev_context.ps3_sas_node_lock, flags); + list_for_each_entry(ps3_sas_node, + &instance->sas_dev_context.ps3_sas_node_list, + list) { + if (ps3_sas_node->sas_address == sas_addr) { + ret_node = ps3_sas_node; + break; + } + } + spin_unlock_irqrestore(&instance->sas_dev_context.ps3_sas_node_lock, + flags); +l_out: + return ret_node; +} + +static void ps3_sanity_check_clean(struct ps3_instance *instance, + struct ps3_sas_node *sas_node, + unsigned long long sas_address, + struct ps3_sas_port *sas_port) +{ + struct ps3_sas_port *ps3_sas_port = NULL; + struct ps3_sas_port *ps3_sas_port_next = NULL; + struct ps3_sas_phy *ps3_sas_phy = NULL; + struct ps3_sas_phy *ps3_sas_phy_next = NULL; + struct ps3_sas_node *exp_node = NULL; + unsigned long flags = 0; + struct ps3_pd_entry *pd_entry = NULL; + + list_for_each_entry_safe(ps3_sas_port, ps3_sas_port_next, + &sas_node->sas_port_list, list) { + if (ps3_sas_port == sas_port) + continue; + list_for_each_entry_safe(ps3_sas_phy, ps3_sas_phy_next, + &ps3_sas_port->phy_list, + port_siblings) { + if (ps3_sas_phy->remote_identify.sas_address != + sas_address) { + continue; + } + + LOG_WARN( + "hno:%u phy[%d] sas_addr[%016llx] == new device SAS addr\n", + PS3_HOST(instance), ps3_sas_phy->phy_id, + ps3_sas_port->remote_identify.sas_address); + + spin_lock_irqsave( + &instance->sas_dev_context.ps3_sas_node_lock, + flags); + list_del(&ps3_sas_phy->port_siblings); + ps3_sas_phy->attach_port = NULL; + spin_unlock_irqrestore( + &instance->sas_dev_context.ps3_sas_node_lock, + flags); + ps3_sas_port->phy_count--; + sas_port_delete_phy(ps3_sas_port->port, + ps3_sas_phy->phy); + } + + if (ps3_sas_port->phy_count != 0) + continue; + + if (ps3_sas_port->remote_identify.device_type == + SAS_EDGE_EXPANDER_DEVICE || + ps3_sas_port->remote_identify.device_type == + SAS_FANOUT_EXPANDER_DEVICE) { + exp_node = ps3_sas_find_node_by_sas_addr( + instance, + ps3_sas_port->remote_identify.sas_address); + if (exp_node == NULL) { + LOG_ERROR( + "hno:%u cannot find node sas_addr[%016llx] !\n", + PS3_HOST(instance), + ps3_sas_port->remote_identify + .sas_address); + PS3_BUG(); + continue; + } + + ps3_sas_expander_node_del(instance, exp_node); + + } else if (ps3_sas_port->remote_identify.device_type == + SAS_END_DEVICE) { + spin_lock_irqsave( + &instance->sas_dev_context.ps3_sas_node_lock, + flags); + list_del(&ps3_sas_port->list); + spin_unlock_irqrestore( + &instance->sas_dev_context.ps3_sas_node_lock, + flags); + + LOG_INFO( + "hno:%u sas_port pdid[%u] delete start, by r SAS addr[0x%llx], change\n", + PS3_HOST(instance), ps3_sas_port->pd_flat_id, + sas_address); + + sas_port_delete(ps3_sas_port->port); + + pd_entry = ps3_dev_mgr_lookup_pd_info_by_id( + instance, ps3_sas_port->pd_flat_id); + if (pd_entry != NULL) + pd_entry->sas_rphy = NULL; + + LOG_INFO( + "hno:%u sas_port pdid[%u] delete end, by r SAS addr[0x%llx], change\n", + PS3_HOST(instance), ps3_sas_port->pd_flat_id, + sas_address); + kfree(ps3_sas_port); + } + } +} + +static int ps3_sas_port_create(struct ps3_instance *instance, + struct ps3_sas_node *sas_node, + struct ps3_sas_port *ps3_sas_port) +{ + int ret = -PS3_FAILED; + unsigned long flags = 0; + + ps3_sas_port->port = ps3_sas_sas_port_create(instance, sas_node, + &ps3_sas_port->phy_list); + if (unlikely(ps3_sas_port->port == NULL)) { + LOG_ERROR("hno:%u cannot add port on parent[%d] !\n", + PS3_HOST(instance), sas_node->encl_id); + + goto l_out; + } + + ret = ps3_sas_port_rphy_create(instance, ps3_sas_port); + if (unlikely(ret != PS3_SUCCESS)) { + LOG_ERROR("hno:%u create rphy[%016llx] on parent[%d] NOK!\n", + PS3_HOST(instance), + ps3_sas_port->remote_identify.sas_address, + sas_node->encl_id); + goto l_failed; + } + + spin_lock_irqsave(&instance->sas_dev_context.ps3_sas_node_lock, flags); + list_add_tail(&ps3_sas_port->list, &sas_node->sas_port_list); + spin_unlock_irqrestore(&instance->sas_dev_context.ps3_sas_node_lock, + flags); + + goto l_out; +l_failed: + if (ps3_sas_port->port != NULL) { + sas_port_delete(ps3_sas_port->port); + ps3_sas_port->port = NULL; + } +l_out: + return ret; +} + +static void ps3_sas_port_del(struct ps3_instance *instance, + struct ps3_sas_port *ps3_sas_port) +{ + struct ps3_sas_phy *ps3_sas_phy = NULL; + struct ps3_sas_phy *ps3_sas_phy_next = NULL; + unsigned long flags = 0; + struct ps3_pd_entry *pd_entry = NULL; + + (void)instance; + + spin_lock_irqsave(&instance->sas_dev_context.ps3_sas_node_lock, flags); + list_del(&ps3_sas_port->list); + spin_unlock_irqrestore(&instance->sas_dev_context.ps3_sas_node_lock, + flags); + + list_for_each_entry_safe(ps3_sas_phy, ps3_sas_phy_next, + &ps3_sas_port->phy_list, port_siblings) { + sas_port_delete_phy(ps3_sas_port->port, ps3_sas_phy->phy); + + list_del(&ps3_sas_phy->port_siblings); + ps3_sas_phy->attach_port = NULL; + ps3_sas_port->phy_count--; + } + + LOG_INFO("hno:%u sas_port pdid[%u] phy_count[%u] delete start\n", + PS3_HOST(instance), ps3_sas_port->pd_flat_id, + ps3_sas_port->phy_count); + sas_port_delete(ps3_sas_port->port); + + pd_entry = ps3_dev_mgr_lookup_pd_info_by_id(instance, + ps3_sas_port->pd_flat_id); + if (pd_entry != NULL) + pd_entry->sas_rphy = NULL; + + LOG_WARN("hno:%u sas_port pdid[%u] phy_count[%u] delete end\n", + PS3_HOST(instance), ps3_sas_port->pd_flat_id, + ps3_sas_port->phy_count); + + kfree(ps3_sas_port); +} + +static int ps3_sas_end_device_try_add(struct ps3_instance *instance, + struct ps3_sas_node *sas_node, + struct ps3_sas_phy *ps3_phy, + struct ps3_pd_entry *pd_entry) +{ + int ret = PS3_SUCCESS; + struct ps3_sas_port *ps3_sas_port = NULL; + + LOG_DEBUG("hno:%u ready add end device[%016llx] on node[%d]\n", + PS3_HOST(instance), ps3_phy->remote_identify.sas_address, + sas_node->encl_id); + + ps3_sas_port = ps3_sas_port_find(instance, sas_node, + ps3_phy->remote_identify.sas_address); + + ps3_sanity_check_clean(instance, sas_node, + ps3_phy->remote_identify.sas_address, + ps3_sas_port); + + if (ps3_sas_port != NULL) { + LOG_DEBUG("hno:%u find exist port[%016llx] on node[%d]\n", + PS3_HOST(instance), + ps3_phy->remote_identify.sas_address, + sas_node->encl_id); + if (ps3_is_sata_end_device(pd_entry) && + ps3_sas_port->pd_flat_id != PS3_PDID(&pd_entry->disk_pos)) { + LOG_INFO( + "hno:%u sata end device [%u:%u] not exist but in port\n", + PS3_HOST(instance), + PS3_CHANNEL(&pd_entry->disk_pos), + PS3_TARGET(&pd_entry->disk_pos)); + ps3_sas_port_del(instance, ps3_sas_port); + ps3_sas_port = NULL; + } else { + ps3_sas_port_phy_add(instance, ps3_sas_port, ps3_phy); + goto l_out; + } + } + + ps3_sas_port = (struct ps3_sas_port *)ps3_kzalloc( + instance, sizeof(struct ps3_sas_port)); + if (ps3_sas_port == NULL) { + LOG_ERROR("hno:%u alloc PS3 port on node[%d] failed !\n", + PS3_HOST(instance), sas_node->encl_id); + ret = -PS3_FAILED; + goto l_out; + } + + INIT_LIST_HEAD(&ps3_sas_port->phy_list); + ps3_sas_port->remote_identify = ps3_phy->remote_identify; + list_add_tail(&ps3_phy->port_siblings, &ps3_sas_port->phy_list); + ps3_phy->attach_port = ps3_sas_port; + ps3_sas_port->phy_count++; + ps3_sas_port->pd_flat_id = pd_entry->disk_pos.diskDev.ps3Dev.phyDiskID; + ret = ps3_sas_port_create(instance, sas_node, ps3_sas_port); + if (ret != PS3_SUCCESS) { + LOG_ERROR( + "hno:%u create end device rphy[%016llx] on parent[%d] NOK!\n", + PS3_HOST(instance), + ps3_phy->remote_identify.sas_address, + sas_node->encl_id); + goto l_failed; + } + pd_entry->sas_rphy = ps3_sas_port->rphy; + + goto l_out; +l_failed: + list_del(&ps3_phy->port_siblings); + ps3_phy->attach_port = NULL; + kfree(ps3_sas_port); + +l_out: + LOG_DEBUG("hno:%u add end device[%016llx] on node[%d] end\n", + PS3_HOST(instance), ps3_phy->remote_identify.sas_address, + sas_node->encl_id); + + return ret; +} + +static int ps3_sas_node_all_phys_add(struct ps3_instance *instance, + struct ps3_sas_node *sas_node) +{ + int ret = PS3_SUCCESS; + struct PS3SasMgr sas_req_param; + struct PS3PhyInfo *phy_info = + instance->sas_dev_context.ps3_sas_phy_buff; + unsigned char i = 0; + + memset(&sas_req_param, 0, sizeof(sas_req_param)); + sas_req_param.enclID = sas_node->encl_id; + sas_req_param.startPhyID = 0; + sas_req_param.phyCount = sas_node->phy_count; + sas_req_param.sasAddr = sas_node->sas_address; + + memset(phy_info, 0, PS3_SAS_REQ_BUFF_LEN); + ret = ps3_sas_phy_get(instance, &sas_req_param); + if (ret != PS3_SUCCESS) { + LOG_ERROR("hno:%u get encl[%d] all phys info NOK\n", + PS3_HOST(instance), sas_req_param.enclID); + goto l_out; + } + + for (i = 0; i < sas_node->phy_count; i++) { + ret = ps3_sas_node_phy_add(instance, &sas_node->phys[i], + sas_node, &phy_info[i]); + if (ret != PS3_SUCCESS) { + LOG_ERROR("hno:%u add node[%d]-phys[%d] NOK !\n", + PS3_HOST(instance), sas_node->encl_id, i); + goto l_out; + } + } +l_out: + return ret; +} + +static int ps3_sas_hba_node_init(struct ps3_instance *instance, + struct PS3ExpanderInfo *exp_info, + unsigned long long *hba_sas_addr) +{ + int ret = PS3_SUCCESS; + unsigned char i = 0; + struct ps3_sas_node *hba_node = &instance->sas_dev_context.ps3_hba_sas; + + LOG_DEBUG("hno:%u enter !\n", PS3_HOST(instance)); + + for (i = 0; i < PS3_SAS_HBA_MAX_SAS_NUM; i++) { + instance->sas_dev_context.ps3_hba_sas_addr[i] = + le64_to_cpu(hba_sas_addr[i]); + LOG_INFO("hno:%u hba SAS addr[%d] is [%016llx] !\n", + PS3_HOST(instance), i, + instance->sas_dev_context.ps3_hba_sas_addr[i]); + } + + hba_node->encl_id = exp_info->enclID; + hba_node->phy_count = exp_info->phyCount; + hba_node->dev_type = exp_info->devType; + + LOG_INFO("hno:%u hba encl_id[%d], phy_count[%d], dev_type[%d] !\n", + PS3_HOST(instance), hba_node->encl_id, hba_node->phy_count, + hba_node->dev_type); + + if (hba_node->phys == NULL) { + hba_node->phys = (struct ps3_sas_phy *)ps3_kcalloc( + instance, hba_node->phy_count, + sizeof(struct ps3_sas_phy)); + } + if (hba_node->phys == NULL) { + LOG_ERROR("hno:%u alloc hba phys buffer failed !\n", + PS3_HOST(instance)); + goto l_out; + } + + ret = ps3_sas_node_all_phys_add(instance, hba_node); + if (ret != PS3_SUCCESS) { + LOG_ERROR("hno:%u hba add phys NOK !\n", PS3_HOST(instance)); + goto l_out; + } + +l_out: + LOG_DEBUG("hno:%u quit !\n", PS3_HOST(instance)); + return ret; +} + +static struct ps3_sas_node * +ps3_sas_find_node_by_id(struct ps3_instance *instance, unsigned char encl_id) +{ + struct ps3_sas_node *ps3_sas_node = NULL; + unsigned long flags = 0; + + if (instance->sas_dev_context.ps3_hba_sas.encl_id == encl_id) + return &instance->sas_dev_context.ps3_hba_sas; + + spin_lock_irqsave(&instance->sas_dev_context.ps3_sas_node_lock, flags); + list_for_each_entry(ps3_sas_node, + &instance->sas_dev_context.ps3_sas_node_list, + list) { + if (ps3_sas_node->encl_id == encl_id) { + spin_unlock_irqrestore( + &instance->sas_dev_context.ps3_sas_node_lock, + flags); + return ps3_sas_node; + } + } + spin_unlock_irqrestore(&instance->sas_dev_context.ps3_sas_node_lock, + flags); + + return NULL; +} + +static struct ps3_sas_port * +ps3_sas_expander_parent_attach(struct ps3_instance *instance, + struct ps3_sas_node *exp_node) +{ + int ret = -PS3_FAILED; + struct ps3_sas_node *parent_node = NULL; + struct ps3_sas_port *ps3_sas_port = NULL; + unsigned char i = 0; + unsigned long flags = 0; + unsigned char is_exist_port = PS3_TRUE; + struct ps3_sas_phy *sas_phy = NULL; + struct ps3_sas_phy *sas_phy_next = NULL; + + parent_node = + ps3_sas_find_node_by_id(instance, exp_node->parent_encl_id); + if (parent_node == NULL) { + LOG_ERROR("hno:%u cannot find parent node[%d] !\n", + PS3_HOST(instance), exp_node->parent_encl_id); + goto l_out; + } + + ps3_sas_port = + ps3_sas_port_find(instance, parent_node, exp_node->sas_address); + if (ps3_sas_port == NULL) { + ps3_sas_port = (struct ps3_sas_port *)ps3_kzalloc( + instance, sizeof(struct ps3_sas_port)); + if (ps3_sas_port == NULL) { + LOG_ERROR( + "hno:%u alloc PS3 port on node[%d] failed !\n", + PS3_HOST(instance), parent_node->encl_id); + goto l_out; + } + INIT_LIST_HEAD(&ps3_sas_port->phy_list); + is_exist_port = PS3_FALSE; + } + + ps3_sanity_check_clean(instance, parent_node, exp_node->sas_address, + ps3_sas_port); + + spin_lock_irqsave(&instance->sas_dev_context.ps3_sas_node_lock, flags); + for (i = 0; i < parent_node->phy_count; i++) { + if ((parent_node->phys[i].remote_identify.sas_address == + exp_node->sas_address) && + (parent_node->phys[i].attach_port == NULL)) { + if (ps3_sas_port->phy_count == 0) { + ps3_sas_port->remote_identify = + parent_node->phys[i].remote_identify; + } + list_add_tail(&parent_node->phys[i].port_siblings, + &ps3_sas_port->phy_list); + parent_node->phys[i].attach_port = ps3_sas_port; + ps3_sas_port->phy_count++; + if (is_exist_port && ps3_sas_port->port != NULL) { + sas_port_add_phy(ps3_sas_port->port, + parent_node->phys[i].phy); + } + } + } + spin_unlock_irqrestore(&instance->sas_dev_context.ps3_sas_node_lock, + flags); + + if (!is_exist_port) { + if (ps3_sas_port->phy_count == 0) { + LOG_ERROR("hno:%u cannot find phy in parent[%d] !\n", + PS3_HOST(instance), parent_node->encl_id); + goto l_failed; + } + + ret = ps3_sas_port_create(instance, parent_node, ps3_sas_port); + if (ret != PS3_SUCCESS) { + LOG_ERROR( + "hno:%u create rphy on parent[%d] for exp[%d] !\n", + PS3_HOST(instance), parent_node->encl_id, + exp_node->encl_id); + goto l_failed; + } + + exp_node->dev = &ps3_sas_port->rphy->dev; + } +l_out: + return ps3_sas_port; + +l_failed: + list_for_each_entry_safe(sas_phy, sas_phy_next, + &ps3_sas_port->phy_list, port_siblings) { + sas_phy->attach_port = NULL; + list_del(&sas_phy->port_siblings); + } + kfree(ps3_sas_port); + + return NULL; +} + +static int ps3_sas_expander_node_add(struct ps3_instance *instance, + struct PS3ExpanderInfo *exp_info) +{ + int ret = PS3_SUCCESS; + struct ps3_sas_node *exp_node = NULL; + struct ps3_sas_port *ps3_sas_port = NULL; + unsigned char is_exist_expander = PS3_TRUE; + unsigned long flags = 0; + unsigned char old_parent_id = PS3_SAS_INVALID_ID; + + LOG_DEBUG("hno:%u enter !\n", PS3_HOST(instance)); + exp_node = ps3_sas_find_node_by_id(instance, exp_info->enclID); + if (exp_node != NULL && + unlikely(exp_node->parent_sas_address != exp_info->parentSasAddr || + exp_node->parent_encl_id != exp_info->parentId)) { + LOG_WARN( + "hno:%u SAS node encl_id[%d], change place\n" + "\tpar_sas_addr old[0x%016llx]-new[0x%016llx], par_encl_id old[%d]=new[%d]\n", + PS3_HOST(instance), exp_info->enclID, + exp_node->parent_sas_address, exp_info->parentSasAddr, + exp_node->parent_encl_id, exp_info->parentId); + old_parent_id = exp_node->parent_encl_id; + ps3_sas_expander_node_del(instance, exp_node); + ps3_sas_expander_event_update(instance, old_parent_id); + exp_node = NULL; + } + + if (exp_node == NULL) { + exp_node = (struct ps3_sas_node *)ps3_kzalloc( + instance, sizeof(struct ps3_sas_node)); + if (exp_node == NULL) { + LOG_ERROR("hno:%u alloc node[%d] failed !\n", + PS3_HOST(instance), exp_info->enclID); + goto l_out; + } + + exp_node->sas_address = le64_to_cpu(exp_info->sasAddr); + exp_node->encl_id = exp_info->enclID; + exp_node->phy_count = exp_info->phyCount; + exp_node->dev_type = exp_info->devType; + exp_node->parent_encl_id = exp_info->parentId; + exp_node->parent_sas_address = + le64_to_cpu(exp_info->parentSasAddr); + + exp_node->phys = (struct ps3_sas_phy *)ps3_kcalloc( + instance, exp_node->phy_count, + sizeof(struct ps3_sas_phy)); + if (exp_node->phys == NULL) { + LOG_ERROR("hno:%u alloc exp[%d] phys buffer failed !\n", + PS3_HOST(instance), exp_node->encl_id); + goto l_failed; + } + INIT_LIST_HEAD(&exp_node->sas_port_list); + is_exist_expander = PS3_FALSE; + } + + LOG_INFO("hno:%u ready add exp_node sas_address[0x%016llx]\n" + "\tencl_id[%d], phy_count[%d], parent_encl_id[%d]\n" + "\tparent_sas_address[0x%016llx] !\n", + PS3_HOST(instance), exp_node->sas_address, exp_node->encl_id, + exp_node->phy_count, exp_node->parent_encl_id, + exp_node->parent_sas_address); + + ps3_sas_port = ps3_sas_expander_parent_attach(instance, exp_node); + if (ps3_sas_port == NULL) { + LOG_ERROR("hno:%u attch exp[%d] on parent NOK !\n", + PS3_HOST(instance), exp_node->encl_id); + ret = -PS3_FAILED; + goto l_failed; + } + if (!is_exist_expander) { + ret = ps3_sas_node_all_phys_add(instance, exp_node); + if (ret != PS3_SUCCESS) { + LOG_ERROR("hno:%u expander[%d] add phys NOK !\n", + PS3_HOST(instance), exp_node->encl_id); + goto l_failed; + } + + spin_lock_irqsave(&instance->sas_dev_context.ps3_sas_node_lock, + flags); + list_add_tail(&exp_node->list, + &instance->sas_dev_context.ps3_sas_node_list); + spin_unlock_irqrestore( + &instance->sas_dev_context.ps3_sas_node_lock, flags); + LOG_WARN("hno:%u add exp_node sas_address[0x%016llx]\n" + "\tencl_id[%d], phy_count[%d], parent_encl_id[%d]\n" + "\tparent_sas_address[0x%016llx] end!\n", + PS3_HOST(instance), exp_node->sas_address, + exp_node->encl_id, exp_node->phy_count, + exp_node->parent_encl_id, + exp_node->parent_sas_address); + } + goto l_out; + +l_failed: + if (ps3_sas_port != NULL) { + ps3_sas_port_del(instance, ps3_sas_port); + ps3_sas_port = NULL; + } + + if (exp_node->phys != NULL) { + kfree(exp_node->phys); + exp_node->phys = NULL; + } + + if (exp_node != NULL) { + kfree(exp_node); + exp_node = NULL; + } +l_out: + LOG_DEBUG("hno:%u quit !\n", PS3_HOST(instance)); + return ret; +} + +static void ps3_sas_expander_del_list_build(struct ps3_instance *instance, + struct list_head *node_del_list) +{ + struct ps3_sas_node *exp_node = NULL; + struct ps3_sas_node *child_exp_node = NULL; + struct ps3_sas_port *ps3_sas_port = NULL; + unsigned long flags = 0; + + list_for_each_entry(exp_node, node_del_list, list) { + list_for_each_entry(ps3_sas_port, &exp_node->sas_port_list, + list) { + if ((ps3_sas_port->remote_identify.device_type != + SAS_EDGE_EXPANDER_DEVICE) && + (ps3_sas_port->remote_identify.device_type != + SAS_FANOUT_EXPANDER_DEVICE)) { + continue; + } + + child_exp_node = ps3_sas_find_node_by_sas_addr( + instance, + ps3_sas_port->remote_identify.sas_address); + if (child_exp_node != NULL) { + spin_lock_irqsave(&instance->sas_dev_context + .ps3_sas_node_lock, + flags); + list_move_tail(&child_exp_node->list, + node_del_list); + spin_unlock_irqrestore( + &instance->sas_dev_context + .ps3_sas_node_lock, + flags); + } + } + } +} + +static void ps3_sas_node_del(struct ps3_instance *instance, + struct ps3_sas_node *exp_node) +{ + struct ps3_sas_port *ps3_sas_port = NULL; + struct ps3_sas_port *tmp_port = NULL; + + list_for_each_entry_safe(ps3_sas_port, tmp_port, + &exp_node->sas_port_list, list) { + ps3_sas_port_del(instance, ps3_sas_port); + ps3_sas_port = NULL; + } + + kfree(exp_node->phys); +} + +static int ps3_sas_node_port_del(struct ps3_instance *instance, + struct ps3_sas_node *parent_node, + unsigned long long sas_addr) +{ + int ret = PS3_SUCCESS; + struct ps3_sas_port *ps3_port = NULL; + + ps3_port = ps3_sas_port_find(instance, parent_node, sas_addr); + if (ps3_port == NULL) { + LOG_ERROR("hno:%u cannot find port[%016llx] in node[%d] !\n", + PS3_HOST(instance), sas_addr, parent_node->encl_id); + ret = -PS3_FAILED; + goto l_out; + } + + ps3_sas_port_del(instance, ps3_port); + ps3_port = NULL; +l_out: + return ret; +} + +void ps3_sas_expander_node_del(struct ps3_instance *instance, + struct ps3_sas_node *exp_node) +{ + int ret = PS3_SUCCESS; + struct ps3_sas_node *parent_node = NULL; + struct ps3_sas_node *del_exp_node = NULL; + struct ps3_sas_node *tmp_exp_node = NULL; + struct list_head node_del_list = { NULL, NULL }; + unsigned long long sas_address = exp_node->sas_address; + unsigned long flags = 0; + + LOG_INFO( + "hno:%u enter !, encl_id[%d], parent_id[%d], sas_addr[%016llx]\n", + PS3_HOST(instance), exp_node->encl_id, exp_node->parent_encl_id, + exp_node->sas_address); + + parent_node = + ps3_sas_find_node_by_id(instance, exp_node->parent_encl_id); + if (parent_node == NULL) { + LOG_ERROR("hno:%u cannot find parent node[%d] !\n", + + PS3_HOST(instance), exp_node->parent_encl_id); + BUG(); + goto l_out; + } + + INIT_LIST_HEAD(&node_del_list); + spin_lock_irqsave(&instance->sas_dev_context.ps3_sas_node_lock, flags); + list_move_tail(&exp_node->list, &node_del_list); + spin_unlock_irqrestore(&instance->sas_dev_context.ps3_sas_node_lock, + flags); + ps3_sas_expander_del_list_build(instance, &node_del_list); + + list_for_each_entry_safe_reverse(del_exp_node, tmp_exp_node, + &node_del_list, list) { + LOG_DEBUG("hno:%u remove encl_id[%d], sas_addr[%016llx]\n", + PS3_HOST(instance), del_exp_node->encl_id, + del_exp_node->sas_address); + ps3_sas_node_del(instance, del_exp_node); + list_del(&del_exp_node->list); + kfree(del_exp_node); + } + + LOG_WARN("hno:%u remove sas_addr[%016llx] from parent[%d]\n", + PS3_HOST(instance), sas_address, parent_node->encl_id); + + ret = ps3_sas_node_port_del(instance, parent_node, sas_address); + if (ret != PS3_SUCCESS) { + LOG_ERROR( + "hno:%u cannot delete expander[%016llx] from parent[%d] !\n", + PS3_HOST(instance), sas_address, parent_node->encl_id); + BUG(); + goto l_out; + } +l_out: + LOG_DEBUG("hno:%u quit !\n", PS3_HOST(instance)); +} + +int ps3_sas_device_data_init(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + struct PS3Expanders *p_expanders = + (struct PS3Expanders *)instance->sas_dev_context.ps3_sas_buff; + struct PS3ExpanderInfo *exp_info = p_expanders->expanders; + unsigned char i = 0; + + if (!ps3_sas_is_support_smp(instance)) + goto l_out; + + LOG_DEBUG("hno:%u ready get init expander enter\n", PS3_HOST(instance)); + + + + memset(p_expanders, 0, PS3_SAS_REQ_BUFF_LEN); + ret = ps3_sas_expander_all_get(instance); + if (ret != PS3_SUCCESS) { + LOG_ERROR("hno:%u init get expander NOK\n", PS3_HOST(instance)); + goto l_out; + } + + LOG_INFO("hno:%u get expander list count[%d]\n", PS3_HOST(instance), + p_expanders->count); + + if (p_expanders->count == 0) { + LOG_ERROR("hno:%u expander init info count = 0\n", + PS3_HOST(instance)); + ret = -PS3_FAILED; + goto l_out; + ; + } + + ret = ps3_sas_hba_node_init(instance, exp_info, + p_expanders->hbaSasAddr); + if (ret != PS3_SUCCESS) { + LOG_ERROR("hno:%u init hba info NOK\n", PS3_HOST(instance)); + goto l_out; + } + + for (i = 1; i < p_expanders->count; i++) { + if (ps3_sas_expander_node_add(instance, &exp_info[i]) != + PS3_SUCCESS) { + LOG_WARN("hno:%u init add expander[%d] info NOK\n", + PS3_HOST(instance), exp_info[i].enclID); + } + } + + LOG_DEBUG("hno:%u SAS init end\n", PS3_HOST(instance)); +l_out: + return ret; +} + +int ps3_sas_device_data_exit(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + + if (!ps3_sas_is_support_smp(instance)) + goto l_out; + + LOG_INFO("hno:%u %s\n", PS3_HOST(instance), __func__); + + + (void)instance; +l_out: + return ret; +} + +static int ps3_sas_expander_dma_buf_alloc(struct ps3_instance *instance) +{ + struct ps3_sas_dev_context *ps3_sas_ctx = &instance->sas_dev_context; + + ps3_sas_ctx->ps3_sas_buff = ps3_dma_alloc_coherent( + instance, PS3_SAS_REQ_BUFF_LEN, + (unsigned long long *)&ps3_sas_ctx->ps3_sas_buff_dma_addr); + if (ps3_sas_ctx->ps3_sas_buff == NULL) { + LOG_ERROR("hno:%u alloc SAS req buffer failed !\n", + PS3_HOST(instance)); + goto l_fail; + } + return PS3_SUCCESS; +l_fail: + return -PS3_ENOMEM; +} + +static void ps3_sas_expander_dma_buf_free(struct ps3_instance *instance) +{ + struct ps3_sas_dev_context *ps3_sas_ctx = &instance->sas_dev_context; + + if (ps3_sas_ctx->ps3_sas_buff != NULL) { + ps3_dma_free_coherent(instance, PS3_SAS_REQ_BUFF_LEN, + ps3_sas_ctx->ps3_sas_buff, + ps3_sas_ctx->ps3_sas_buff_dma_addr); + + ps3_sas_ctx->ps3_sas_buff = NULL; + } +} + +static int ps3_sas_phy_dma_buf_alloc(struct ps3_instance *instance) +{ + struct ps3_sas_dev_context *ps3_sas_ctx = &instance->sas_dev_context; + + ps3_sas_ctx->ps3_sas_phy_buff = + (struct PS3PhyInfo *)ps3_dma_alloc_coherent( + instance, PS3_SAS_REQ_BUFF_LEN, + (unsigned long long *)&ps3_sas_ctx + ->ps3_sas_phy_buff_dma_addr); + if (ps3_sas_ctx->ps3_sas_phy_buff == NULL) { + LOG_ERROR("hno:%u alloc SAS req buffer failed !\n", + PS3_HOST(instance)); + goto l_fail; + } + return PS3_SUCCESS; +l_fail: + return -PS3_ENOMEM; +} + +static void ps3_sas_phy_dma_buf_free(struct ps3_instance *instance) +{ + struct ps3_sas_dev_context *ps3_sas_ctx = &instance->sas_dev_context; + + if (ps3_sas_ctx->ps3_sas_phy_buff != NULL) { + ps3_dma_free_coherent(instance, PS3_SAS_REQ_BUFF_LEN, + ps3_sas_ctx->ps3_sas_phy_buff, + ps3_sas_ctx->ps3_sas_phy_buff_dma_addr); + + ps3_sas_ctx->ps3_sas_phy_buff = NULL; + } +} + +int ps3_sas_device_mgr_init(struct ps3_instance *instance) +{ + int ret = -PS3_FAILED; + unsigned char i = 0; + + if (instance->ioc_adpter->sas_transport_get != NULL) { + instance->sas_dev_context.is_support_smp = PS3_TRUE; + } else { + instance->sas_dev_context.is_support_smp = PS3_FALSE; + ret = PS3_SUCCESS; + LOG_INFO("hno:%u the IOC is not support SAS expander !\n", + PS3_HOST(instance)); + goto l_out; + } + + sema_init(&instance->sas_dev_context.ps3_sas_smp_semaphore, 1); + + ret = ps3_sas_expander_dma_buf_alloc(instance); + if (ret != PS3_SUCCESS) { + LOG_ERROR("hno:%u alloc SAS expander dma buffer failed !\n", + PS3_HOST(instance)); + goto l_out; + } + + ret = ps3_sas_phy_dma_buf_alloc(instance); + if (ret != PS3_SUCCESS) { + LOG_ERROR("hno:%u alloc SAS phy dma buffer failed !\n", + PS3_HOST(instance)); + goto l_out; + } + + INIT_LIST_HEAD(&instance->sas_dev_context.ps3_sas_node_list); + spin_lock_init(&instance->sas_dev_context.ps3_sas_node_lock); + + memset(&instance->sas_dev_context.ps3_hba_sas, 0, + sizeof(struct ps3_sas_node)); + + for (i = 0; i < PS3_SAS_HBA_MAX_SAS_NUM; i++) { + instance->sas_dev_context.ps3_hba_sas_addr[i] = + PS3_SAS_INVALID_SAS_ADDR; + } + + INIT_LIST_HEAD(&instance->sas_dev_context.ps3_hba_sas.sas_port_list); + instance->sas_dev_context.ps3_hba_sas.parent_encl_id = + PS3_SAS_INVALID_ID; + instance->sas_dev_context.ps3_hba_sas.parent_sas_address = + PS3_SAS_INVALID_SAS_ADDR; + instance->sas_dev_context.ps3_hba_sas.dev = + &instance->host->shost_gendev; +l_out: + return ret; +} + +int ps3_sas_device_mgr_exit(struct ps3_instance *instance) +{ + if (ps3_sas_is_support_smp(instance)) { + ps3_sas_phy_dma_buf_free(instance); + ps3_sas_expander_dma_buf_free(instance); + } + + return PS3_SUCCESS; +} + + +static void ps3_sas_end_dev_del(struct ps3_instance *instance, + struct ps3_sas_node *parent_node, + unsigned char phy_id) +{ + struct ps3_sas_port *ps3_sas_port = NULL; + struct ps3_sas_port *ps3_sas_port_next = NULL; + struct ps3_sas_phy *ps3_sas_phy = NULL; + struct ps3_sas_phy *ps3_sas_phy_next = NULL; + unsigned char del_finish = PS3_FALSE; + unsigned long flags = 0; + struct ps3_pd_entry *pd_entry = NULL; + + (void)instance; + + list_for_each_entry_safe(ps3_sas_port, ps3_sas_port_next, + &parent_node->sas_port_list, list) { + list_for_each_entry_safe(ps3_sas_phy, ps3_sas_phy_next, + &ps3_sas_port->phy_list, + port_siblings) { + if (likely(ps3_sas_phy->phy_id == phy_id)) { + spin_lock_irqsave(&instance->sas_dev_context + .ps3_sas_node_lock, + flags); + list_del(&ps3_sas_phy->port_siblings); + ps3_sas_phy->attach_port = NULL; + spin_unlock_irqrestore( + &instance->sas_dev_context + .ps3_sas_node_lock, + flags); + + sas_port_delete_phy(ps3_sas_port->port, + ps3_sas_phy->phy); + + ps3_sas_port->phy_count--; + + LOG_DEBUG( + "hno:%u sas_port pdid[%u] phy_count[%u]\n" + "\tencl[%u] phy_id[%u] sas_addr[%016llx]\n" + "\tsas_remote_addr[%016llx] sas_phy delete\n", + PS3_HOST(instance), + ps3_sas_port->pd_flat_id, + ps3_sas_port->phy_count, + ps3_sas_phy->encl_id, + ps3_sas_phy->phy_id, + ps3_sas_phy->identify.sas_address, + ps3_sas_phy->remote_identify + .sas_address); + + memset(&ps3_sas_phy->remote_identify, 0, + sizeof(struct sas_identify)); + del_finish = PS3_TRUE; + break; + } + } + + if (del_finish == PS3_TRUE) { + if (ps3_sas_port->phy_count == 0) { + spin_lock_irqsave(&instance->sas_dev_context + .ps3_sas_node_lock, + flags); + list_del(&ps3_sas_port->list); + spin_unlock_irqrestore( + &instance->sas_dev_context + .ps3_sas_node_lock, + flags); + + LOG_INFO( + "hno:%u sas_port pdid[%u] phy_count[%u] delete start\n", + PS3_HOST(instance), + ps3_sas_port->pd_flat_id, + ps3_sas_port->phy_count); + + sas_port_delete(ps3_sas_port->port); + + pd_entry = ps3_dev_mgr_lookup_pd_info_by_id( + instance, ps3_sas_port->pd_flat_id); + if (pd_entry != NULL) + pd_entry->sas_rphy = NULL; + + LOG_INFO( + "hno:%u sas_port pdid[%u] phy_count[%u] delete end\n", + PS3_HOST(instance), + ps3_sas_port->pd_flat_id, + ps3_sas_port->phy_count); + kfree(ps3_sas_port); + } + + break; + } + } +} + +static unsigned char ps3_sas_addr_is_exist(struct PS3Expanders *p_expanders, + unsigned long long sas_addr) +{ + unsigned char ret = PS3_FALSE; + unsigned char i = 0; + + if (p_expanders == NULL) + goto l_out; + for (i = 1; i < p_expanders->count; i++) { + if (p_expanders->expanders[i].sasAddr == sas_addr) { + ret = PS3_TRUE; + break; + } + } +l_out: + return ret; +} + +static void ps3_sas_expander_port_clean(struct ps3_instance *instance, + struct ps3_sas_node *sas_node, + struct PS3Expanders *p_expanders) +{ + struct ps3_sas_port *ps3_sas_port = NULL; + struct ps3_sas_port *ps3_sas_port_next = NULL; + struct ps3_sas_phy *ps3_sas_phy = NULL; + struct ps3_sas_phy *ps3_sas_phy_next = NULL; + struct ps3_sas_node *exp_node = NULL; + unsigned char is_exist = PS3_FALSE; + unsigned char diff_phy_num = 0; + + list_for_each_entry_safe(ps3_sas_port, ps3_sas_port_next, + &sas_node->sas_port_list, list) { + if (ps3_sas_port->remote_identify.device_type != + SAS_EDGE_EXPANDER_DEVICE && + ps3_sas_port->remote_identify.device_type != + SAS_FANOUT_EXPANDER_DEVICE) { + continue; + } + is_exist = ps3_sas_addr_is_exist( + p_expanders, ps3_sas_port->remote_identify.sas_address); + diff_phy_num = ps3_sas_port->phy_count; + list_for_each_entry_safe(ps3_sas_phy, ps3_sas_phy_next, + &ps3_sas_port->phy_list, + port_siblings) { + if ((ps3_sas_phy->remote_identify.sas_address == + ps3_sas_port->remote_identify.sas_address) || + (ps3_sas_phy->remote_identify.sas_address == 0 && + is_exist)) { + continue; + } + + diff_phy_num--; + LOG_WARN("hno:%u phy[%d]'s remote addr[%016llx] != expander\n" + "\tport's rphy addr[0x%016llx]!\n", + PS3_HOST(instance), ps3_sas_phy->phy_id, + ps3_sas_phy->remote_identify.sas_address, + ps3_sas_port->remote_identify.sas_address); + } + if (diff_phy_num != 0) + continue; + + exp_node = ps3_sas_find_node_by_sas_addr( + instance, ps3_sas_port->remote_identify.sas_address); + if (exp_node == NULL) { + LOG_ERROR( + "hno:%u cannot find node sas_addr[%016llx] !\n", + PS3_HOST(instance), + ps3_sas_port->remote_identify.sas_address); + PS3_BUG(); + } else { + LOG_INFO( + "hno:%u del expander in sas port clean. encl_id[%d], sas_addr[%016llx]\n", + PS3_HOST(instance), exp_node->encl_id, + exp_node->sas_address); + ps3_sas_expander_node_del(instance, exp_node); + } + } +} + +int ps3_sas_expander_phys_refresh(struct ps3_instance *instance, + struct ps3_sas_node *sas_node) +{ + int ret = -PS3_FAILED; + struct PS3SasMgr sas_req_param; + struct PS3PhyInfo *phy_info = + instance->sas_dev_context.ps3_sas_phy_buff; + unsigned char i = 0; + unsigned long flags = 0; + + memset(&sas_req_param, 0, sizeof(sas_req_param)); + sas_req_param.enclID = sas_node->encl_id; + sas_req_param.sasAddr = cpu_to_le64(sas_node->sas_address); + sas_req_param.startPhyID = 0; + sas_req_param.phyCount = sas_node->phy_count; + + LOG_DEBUG("hno:%u ready get phys[%d] of encl_id[%d] !\n", + PS3_HOST(instance), sas_req_param.phyCount, + sas_req_param.enclID); + + memset(phy_info, 0, PS3_SAS_REQ_BUFF_LEN); + ret = ps3_sas_phy_get(instance, &sas_req_param); + if (ret != PS3_SUCCESS) { + LOG_ERROR("hno:%u init get expander NOK\n", PS3_HOST(instance)); + goto l_out; + } + + spin_lock_irqsave(&instance->sas_dev_context.ps3_sas_node_lock, flags); + LOG_INFO_IN_IRQ(instance, + "hno:%u ready update %d phys of encl_id[%d]!\n", + PS3_HOST(instance), sas_req_param.phyCount, + sas_req_param.enclID); + for (i = 0; i < sas_node->phy_count; i++) { + ps3_sas_node_phy_update(instance, &sas_node->phys[i], + &phy_info[i]); + } + spin_unlock_irqrestore(&instance->sas_dev_context.ps3_sas_node_lock, + flags); + ps3_sas_port_phy_update(instance, sas_node); +l_out: + return ret; +} + +static int ps3_sas_device_date_refresh(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + struct PS3Expanders *p_expanders = + (struct PS3Expanders *)instance->sas_dev_context.ps3_sas_buff; + struct PS3ExpanderInfo *exp_info = p_expanders->expanders; + struct ps3_sas_node *exp_node = NULL; + unsigned char i = 0; + + LOG_DEBUG("hno:%u SAS dev refresh enter\n", PS3_HOST(instance)); + + memset(p_expanders, 0, PS3_SAS_REQ_BUFF_LEN); + ret = ps3_sas_expander_all_get(instance); + if (ret != PS3_SUCCESS) { + LOG_ERROR("hno:%u init get expander NOK\n", PS3_HOST(instance)); + goto l_out; + } + + LOG_DEBUG("hno:%u ready refresh expander count[%d]\n", + PS3_HOST(instance), p_expanders->count); + if (p_expanders->count == 0) { + LOG_ERROR("hno:%u expander init info count = 0\n", + PS3_HOST(instance)); + BUG(); + } + + LOG_DEBUG("hno:%u ready refresh HBA\n", PS3_HOST(instance)); + + ret = ps3_sas_expander_phys_refresh( + instance, &instance->sas_dev_context.ps3_hba_sas); + if (ret != PS3_SUCCESS) { + LOG_ERROR("hno:%u refresh phys on HBA NOK!\n", + PS3_HOST(instance)); + goto l_out; + } + ps3_sas_expander_port_clean( + instance, &instance->sas_dev_context.ps3_hba_sas, p_expanders); + + LOG_DEBUG("hno:%u end refresh HBA\n", PS3_HOST(instance)); + + for (i = 1; i < p_expanders->count; i++) { + exp_node = + ps3_sas_find_node_by_id(instance, exp_info[i].enclID); + if (exp_node != NULL) { + if (likely(exp_node->parent_sas_address == + exp_info[i].parentSasAddr && + exp_node->parent_encl_id == + exp_info[i].parentId)) { + LOG_DEBUG("hno:%u ready refresh expander[%d]\n", + PS3_HOST(instance), + exp_info[i].enclID); + ret = ps3_sas_expander_phys_refresh(instance, + exp_node); + if (ret != PS3_SUCCESS) { + LOG_ERROR( + "hno:%u refresh phys on expander[%d] NOK!\n", + PS3_HOST(instance), + exp_info[i].enclID); + goto l_out; + } + ps3_sas_expander_port_clean(instance, exp_node, + p_expanders); + LOG_DEBUG("hno:%u end refresh expander[%d]\n", + PS3_HOST(instance), + exp_info[i].enclID); + } + } + + LOG_DEBUG("hno:%u ready add expander[%d]\n", PS3_HOST(instance), + exp_info[i].enclID); + ret = ps3_sas_expander_node_add(instance, &exp_info[i]); + LOG_DEBUG("hno:%u end add expander[%d], ret[%d]\n", + PS3_HOST(instance), exp_info[i].enclID, ret); + if (ret != PS3_SUCCESS) { + LOG_ERROR("hno:%u init add expander[%d] info NOK\n", + PS3_HOST(instance), exp_info[i].enclID); + goto l_out; + } + } + + LOG_DEBUG("hno:%u SAS refresh end\n", PS3_HOST(instance)); +l_out: + return ret; +} + +static int ps3_sas_expander_event_update(struct ps3_instance *instance, + unsigned char encl_id) +{ + int ret = -PS3_FAILED; + struct ps3_sas_node *sas_node = NULL; + + LOG_DEBUG("hno:%u enter SAS expander update encl_id[%d] !\n", + PS3_HOST(instance), encl_id); + sas_node = ps3_sas_find_node_by_id(instance, encl_id); + if (sas_node == NULL) { + LOG_ERROR("hno:%u cannot find PS3 node[%d] !\n", + PS3_HOST(instance), encl_id); + goto l_out; + } + + ret = ps3_sas_expander_phys_refresh(instance, sas_node); + if (ret != PS3_SUCCESS) { + LOG_ERROR("hno:%u refresh phys on node[%d] NOK!\n", + PS3_HOST(instance), encl_id); + goto l_out; + } +l_out: + LOG_DEBUG("hno:%u quit SAS expander update encl_id[%d] !\n", + PS3_HOST(instance), encl_id); + return ret; +} + +static int ps3_sas_expander_phy_update(struct ps3_instance *instance, + unsigned char encl_id, + unsigned char phy_id) +{ + int ret = -PS3_FAILED; + struct ps3_sas_node *sas_node = NULL; + struct PS3SasMgr sas_req_param; + struct PS3PhyInfo *phy_info = + instance->sas_dev_context.ps3_sas_phy_buff; + unsigned long flags = 0; + + LOG_DEBUG("hno:%u enter SAS expander update encl_id[%d] !\n", + PS3_HOST(instance), encl_id); + sas_node = ps3_sas_find_node_by_id(instance, encl_id); + if (sas_node == NULL) { + LOG_ERROR("hno:%u cannot find PS3 node[%d] !\n", + PS3_HOST(instance), encl_id); + goto l_out; + } + + memset(&sas_req_param, 0, sizeof(sas_req_param)); + sas_req_param.enclID = sas_node->encl_id; + sas_req_param.sasAddr = cpu_to_le64(sas_node->sas_address); + sas_req_param.startPhyID = phy_id; + sas_req_param.phyCount = 1; + + memset(phy_info, 0, PS3_SAS_REQ_BUFF_LEN); + ret = ps3_sas_phy_get(instance, &sas_req_param); + if (ret != PS3_SUCCESS) { + LOG_ERROR("hno:%u init get expander NOK\n", PS3_HOST(instance)); + goto l_out; + } + + spin_lock_irqsave(&instance->sas_dev_context.ps3_sas_node_lock, flags); + ps3_sas_node_phy_update(instance, &sas_node->phys[phy_id], phy_info); + spin_unlock_irqrestore(&instance->sas_dev_context.ps3_sas_node_lock, + flags); + +l_out: + LOG_DEBUG("hno:%u quit sas expander update encl_id[%d] !\n", + PS3_HOST(instance), encl_id); + return ret; +} + +static int ps3_sas_expander_event_add(struct ps3_instance *instance, + unsigned char encl_id) +{ + int ret = PS3_SUCCESS; + struct PS3ExpanderInfo *exp_info = + (struct PS3ExpanderInfo *)instance->sas_dev_context.ps3_sas_buff; + struct PS3SasMgr sas_req_param; + + memset(&sas_req_param, 0, sizeof(sas_req_param)); + sas_req_param.enclID = encl_id; + + LOG_WARN("hno:%u enter sas expander add encl_id[%d] !\n", + PS3_HOST(instance), encl_id); + + memset(exp_info, 0, PS3_SAS_REQ_BUFF_LEN); + ret = ps3_sas_expander_get(instance, &sas_req_param); + if (ret != PS3_SUCCESS) { + LOG_ERROR("hno:%u get expander[%d] NOK\n", PS3_HOST(instance), + encl_id); + goto l_out; + } + + if (exp_info->phyCount == 0) { + LOG_ERROR("hno:%u phy_count Invalid!\n", PS3_HOST(instance)); + ret = -PS3_FAILED; + goto l_out; + } + if (exp_info->enclID != encl_id) { + LOG_ERROR("hno:%u got expander[%d] is not [%d]\n", + PS3_HOST(instance), exp_info->enclID, encl_id); + PS3_BUG(); + ret = -PS3_FAILED; + goto l_out; + } + + ret = ps3_sas_expander_event_update(instance, exp_info->parentId); + if (ret != PS3_SUCCESS) { + LOG_ERROR("hno:%u update parent encl[%d] NOK\n", + PS3_HOST(instance), exp_info->parentId); + goto l_out; + } + + ret = ps3_sas_expander_node_add(instance, exp_info); + if (ret != PS3_SUCCESS) { + LOG_ERROR("hno:%u add expander[%d] NOK\n", PS3_HOST(instance), + encl_id); + goto l_out; + } +l_out: + LOG_WARN("hno:%u quit sas expander add encl_id[%d] !\n", + PS3_HOST(instance), encl_id); + return ret; +} + +static int ps3_sas_expander_event_del(struct ps3_instance *instance, + unsigned char encl_id) +{ + int ret = PS3_SUCCESS; + struct ps3_sas_node *exp_node = NULL; + unsigned char parentId = 0; + + LOG_WARN("hno:%u enter sas expander del encl_id[%d] !\n", + PS3_HOST(instance), encl_id); + + exp_node = ps3_sas_find_node_by_id(instance, encl_id); + if (exp_node == NULL) { + LOG_ERROR("hno:%u cannot find node[%d] !\n", PS3_HOST(instance), + encl_id); + ret = -PS3_FAILED; + goto l_out; + } + + parentId = exp_node->parent_encl_id; + ps3_sas_expander_node_del(instance, exp_node); + + ret = ps3_sas_expander_event_update(instance, parentId); + if (ret != PS3_SUCCESS) { + LOG_ERROR("hno:%u update parent encl[%d] NOK\n", + PS3_HOST(instance), parentId); + goto l_out; + } + +l_out: + LOG_WARN("hno:%u quit sas expander del encl_id[%d] !\n", + PS3_HOST(instance), encl_id); + return ret; +} + +int ps3_sas_expander_event_refresh(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + + ret = ps3_sas_device_date_refresh(instance); + if (ret != PS3_SUCCESS) { + LOG_ERROR("hno:%u refresh all SAS NOK\n", PS3_HOST(instance)); + goto l_out; + } + +l_out: + return ret; +} + +int ps3_sas_update_detail_proc(struct ps3_instance *instance, + struct PS3EventDetail *event_detail, + unsigned int event_cnt) +{ + int ret = PS3_SUCCESS; + int ret_map = PS3_SUCCESS; + unsigned int i = 0; + + LOG_DEBUG("hno:%u, event detail count[%d]\n", PS3_HOST(instance), + event_cnt); + + for (i = 0; i < event_cnt; i++) { + LOG_INFO( + "hno:%u, event detail %d eventCode is [%s], encl_Id[%d]\n", + PS3_HOST(instance), i, + mgrEvtCodeTrans(event_detail[i].eventCode), + event_detail[i].EnclId); + + switch (event_detail[i].eventCode) { + case PS3_EVT_CODE(MGR_EVT_SAS_EXPANDER_CHANGE): + case PS3_EVT_CODE(MGR_EVT_SAS_EXPANDER_IN): + ret = ps3_sas_expander_event_add( + instance, event_detail[i].EnclId); + break; + case PS3_EVT_CODE(MGR_EVT_SAS_EXPANDER_OUT): + ret = ps3_sas_expander_event_del( + instance, event_detail[i].EnclId); + break; + case PS3_EVT_CODE(MGR_EVT_SAS_EXPANDER_UPDATE): + ret = ps3_sas_expander_event_update( + instance, event_detail[i].EnclId); + break; + default: + break; + } + + if (ret != PS3_SUCCESS) + ret_map |= event_detail[i].eventType; + } + return ret_map; +} + +int ps3_sas_add_device(struct ps3_instance *instance, + struct ps3_pd_entry *pd_entry) +{ + int ret = -PS3_FAILED; + struct ps3_sas_node *parent_node = NULL; + struct sas_identify *remote_id = NULL; + + unsigned int softChan = PS3_CHANNEL(&pd_entry->disk_pos); + unsigned int devID = PS3_TARGET(&pd_entry->disk_pos); + unsigned int phyDiskID = PS3_PDID(&pd_entry->disk_pos); + + LOG_DEBUG( + "hno:%u enter %s pd[%u:%u:%u] on encl[%u], phy[%u]!\n", + PS3_HOST(instance), __func__, softChan, devID, phyDiskID, + pd_entry->encl_id, pd_entry->phy_id); + + parent_node = ps3_sas_find_node_by_id(instance, pd_entry->encl_id); + if (parent_node == NULL) { + LOG_ERROR( + "hno:%u add end device channel[%u:%u:%u] cannot found node[%d] !\n", + PS3_HOST(instance), softChan, devID, phyDiskID, + pd_entry->encl_id); + goto l_out; + } + if (pd_entry->phy_id >= parent_node->phy_count) { + LOG_ERROR( + "hno:%u add end device channel[%u:%u:%u] phyid[%u] exceed node phy_count[%u] !\n", + PS3_HOST(instance), softChan, devID, phyDiskID, + pd_entry->phy_id, parent_node->phy_count); + goto l_out; + } + + ret = ps3_sas_expander_phy_update(instance, pd_entry->encl_id, + pd_entry->phy_id); + if (ret != PS3_SUCCESS) { + LOG_ERROR( + "hno:%u update phy in add pd channel[%u:%u:%u] phyid[%u] on node[%u] NOK !\n", + PS3_HOST(instance), softChan, devID, phyDiskID, + pd_entry->phy_id, parent_node->encl_id); + goto l_out; + } + + remote_id = &parent_node->phys[pd_entry->phy_id].remote_identify; + if (remote_id->sas_address == PS3_SAS_INVALID_SAS_ADDR || + remote_id->device_type != SAS_END_DEVICE || + remote_id->sas_address == parent_node->parent_sas_address) { + LOG_ERROR( + "hno:%u add end device channel[%u:%u:%u] phyid[%u]\n" + "\tinvalid SAS addr[%016llx] or dev_type[%d] != SAS_END_DEVICE or\n" + "\tSAS addr = parent SAS addr[%016llx]!\n", + PS3_HOST(instance), softChan, devID, phyDiskID, + pd_entry->phy_id, remote_id->sas_address, + remote_id->device_type, + parent_node->parent_sas_address); + goto l_out; + } + + ret = ps3_sas_end_device_try_add(instance, parent_node, + &parent_node->phys[pd_entry->phy_id], + pd_entry); + if (ret != PS3_SUCCESS) { + LOG_ERROR( + "hno:%u add end device channel[%u:%u:%u] phyid[%u] on node[%u] NOK !\n", + PS3_HOST(instance), softChan, devID, phyDiskID, + pd_entry->phy_id, parent_node->encl_id); + goto l_out; + } + +l_out: + return ret; +} + +int ps3_sas_remove_device(struct ps3_instance *instance, + struct PS3DiskDevPos *diskPos, unsigned char encl_id, + unsigned char phy_id) +{ + int ret = -PS3_FAILED; + struct ps3_sas_node *parent_node = NULL; + struct sas_identify *remote_id = NULL; + unsigned int softChan = PS3_CHANNEL(diskPos); + unsigned int devID = PS3_TARGET(diskPos); + unsigned int phyDiskID = PS3_PDID(diskPos); + + LOG_WARN( + "hno:%u enter %s pd[%u:%u:%u] on encl[%u], phy[%u]!\n", + PS3_HOST(instance), __func__, softChan, devID, phyDiskID, encl_id, + phy_id); + + parent_node = ps3_sas_find_node_by_id(instance, encl_id); + if (parent_node == NULL) { + LOG_ERROR( + "hno:%u add end device channel[%u:%u:%u] cannot found node[%u] !\n", + PS3_HOST(instance), softChan, devID, phyDiskID, + encl_id); + goto l_out; + } + + if (phy_id >= parent_node->phy_count) { + LOG_ERROR( + "hno:%u add end device channel[%u:%u:%u] phyid[%u] exceed node phy_count[%u] !\n", + PS3_HOST(instance), softChan, devID, phyDiskID, phy_id, + parent_node->phy_count); + goto l_out; + } + + remote_id = &parent_node->phys[phy_id].remote_identify; + if (remote_id->sas_address != PS3_SAS_INVALID_SAS_ADDR && + remote_id->sas_address == parent_node->parent_sas_address) { + LOG_ERROR( + "hno:%u add end device channel[%u:%u:%u] phyid[%u]\n" + "\tSAS addr[%016llx] dev_type[%d] parent SAS addr[%016llx]!\n", + PS3_HOST(instance), softChan, devID, phyDiskID, phy_id, + remote_id->sas_address, remote_id->device_type, + parent_node->parent_sas_address); + goto l_out; + } + + ps3_sas_end_dev_del(instance, parent_node, phy_id); + + ret = ps3_sas_expander_phy_update(instance, encl_id, phy_id); + if (ret != PS3_SUCCESS) { + LOG_ERROR( + "hno:%u update phy in add pd channel[%u:%u:%u] phyid[%u] on node[%u] NOK !\n", + PS3_HOST(instance), softChan, devID, phyDiskID, phy_id, + encl_id); + goto l_out; + } +l_out: + LOG_WARN( + "hno:%u %s pd[%u:%u:%u] on encl[%u], phy[%u] end, ret[%d]!\n", + PS3_HOST(instance), __func__, softChan, devID, phyDiskID, encl_id, phy_id, + ret); + return ret; +} + +#if defined(PS3_SAS_LONG_LUN) +int ps3_sas_user_scan(struct Scsi_Host *host, unsigned int channel, + unsigned int id, unsigned int lun) +#else +int ps3_sas_user_scan(struct Scsi_Host *host, unsigned int channel, + unsigned int id, unsigned long long lun) +#endif +{ + struct PS3ChannelInfo *channel_info = NULL; + struct ps3_instance *instance = NULL; + struct ps3_pd_entry *pd_entry = NULL; + unsigned short max_dev_num = 0; + unsigned char i = 0; + unsigned short j = 0; + + if ((lun != 0) && (lun != SCAN_WILD_CARD)) + goto l_out; + + instance = (struct ps3_instance *)shost_priv(host); + channel_info = &instance->ctrl_info.channelInfo; + ps3_mutex_lock(&instance->dev_context.dev_scan_lock); + for (i = 0; i < channel_info->channelNum; i++) { + if ((channel_info->channels[i].channelType == + PS3_CHAN_TYPE_PD) && + (channel == SCAN_WILD_CARD || channel == i)) { + max_dev_num = channel_info->channels[i].maxDevNum; + for (j = 0; j < max_dev_num; j++) { + pd_entry = ps3_dev_mgr_lookup_pd_info(instance, + i, j); + if ((pd_entry != NULL) && + (pd_entry->sas_rphy != NULL)) { + if (id == SCAN_WILD_CARD || id == j) { + scsi_scan_target( + &pd_entry->sas_rphy->dev, + i, j, 0, + SCSI_SCAN_MANUAL); + LOG_DEBUG( + "hno:%u channel:%u target:%u scan\n", + PS3_HOST(instance), i, + j); + } + } + } + } + } + ps3_mutex_unlock(&instance->dev_context.dev_scan_lock); + +l_out: + return 0; +} + +#endif diff --git a/drivers/scsi/linkdata/ps3stor/ps3_device_manager_sas.h b/drivers/scsi/linkdata/ps3stor/ps3_device_manager_sas.h new file mode 100644 index 0000000000000000000000000000000000000000..e0deaa2396dc265f7a5cb96998fff873552a6615 --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/ps3_device_manager_sas.h @@ -0,0 +1,131 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _WINDOWS + +#ifndef _PS3_DEVICE_MANAGER_SAS_H_ +#define _PS3_DEVICE_MANAGER_SAS_H_ +#include + +#include +#include + +#include "ps3_htp_def.h" +#include "ps3_htp_dev.h" +#include "ps3_htp_event.h" +#include "ps3_device_manager.h" +#include "ps3_kernel_version.h" + +#define PS3_SAS_HBA_MAX_SAS_NUM 3 + +#define PS3_SAS_MAX_PHY_NUM (128) +#define PS3_SAS_INVALID_ID (0xFF) +#define PS3_SAS_INVALID_SAS_ADDR (0x0) +#define PS3_SAS_REQ_BUFF_LEN (4096) + +struct ps3_instance; + +struct ps3_sas_node { + struct list_head list; + struct device *dev; + struct ps3_sas_phy *phys; + struct list_head sas_port_list; + unsigned long long sas_address; + unsigned long long parent_sas_address; + unsigned char phy_count; + unsigned char encl_id; + unsigned char parent_encl_id; + unsigned char dev_type; + unsigned char reserved[4]; +}; + +struct ps3_sas_phy { + struct list_head port_siblings; + struct sas_identify identify; + struct sas_identify remote_identify; + struct sas_phy *phy; + struct ps3_sas_port *attach_port; + unsigned char phy_id; + unsigned char encl_id; + unsigned char slot_id; + unsigned char reserved[5]; +}; + +struct ps3_sas_port { + struct list_head list; + struct sas_identify remote_identify; + struct sas_rphy *rphy; + struct sas_port *port; + struct list_head phy_list; + unsigned short pd_flat_id; + unsigned char phy_count; + unsigned char reserved[5]; +}; + +struct ps3_sas_dev_context { + struct list_head ps3_sas_node_list; + spinlock_t ps3_sas_node_lock; + struct ps3_sas_node ps3_hba_sas; + unsigned long long ps3_hba_sas_addr[PS3_SAS_HBA_MAX_SAS_NUM]; + struct semaphore ps3_sas_smp_semaphore; + + dma_addr_t ps3_sas_buff_dma_addr; + dma_addr_t ps3_sas_phy_buff_dma_addr; + void *ps3_sas_buff; + struct PS3PhyInfo *ps3_sas_phy_buff; + unsigned char is_support_smp; + unsigned char reserved[7]; +}; + +unsigned char ps3_sas_is_support_smp(struct ps3_instance *instance); + +int ps3_sas_device_mgr_init(struct ps3_instance *instance); + +int ps3_sas_device_mgr_exit(struct ps3_instance *instance); + +int ps3_sas_device_data_init(struct ps3_instance *instance); + +int ps3_sas_device_data_exit(struct ps3_instance *instance); + +int ps3_sas_rphy_slot_get(struct ps3_instance *instance, + unsigned long long sas_addr, unsigned int *slot_id); + +unsigned long long +ps3_sas_rphy_parent_sas_addr_get(struct ps3_instance *instance, + unsigned long long sas_addr); + +unsigned char ps3_sas_encl_id_get(struct ps3_instance *instance, + unsigned long long sas_addr); + +int ps3_sas_expander_event_refresh(struct ps3_instance *instance); + +int ps3_sas_update_detail_proc(struct ps3_instance *instance, + struct PS3EventDetail *event_detail, + unsigned int event_cnt); + +int ps3_sas_add_device(struct ps3_instance *instance, + struct ps3_pd_entry *pd_entry); + +int ps3_sas_remove_device(struct ps3_instance *instance, + struct PS3DiskDevPos *diskPos, unsigned char encl_id, + unsigned char phy_id); + +void ps3_sas_expander_node_del(struct ps3_instance *instance, + struct ps3_sas_node *exp_node); + +struct ps3_sas_node * +ps3_sas_find_node_by_sas_addr(struct ps3_instance *instance, + unsigned long long sas_addr); + +void ps3_sas_node_phy_update(struct ps3_instance *instance, + struct ps3_sas_phy *ps3_phy, + struct PS3PhyInfo *phy_info); + +#if defined(PS3_SAS_LONG_LUN) +int ps3_sas_user_scan(struct Scsi_Host *host, unsigned int channel, + unsigned int id, unsigned int lun); +#else +int ps3_sas_user_scan(struct Scsi_Host *host, unsigned int channel, + unsigned int id, unsigned long long lun); +#endif + +#endif +#endif diff --git a/drivers/scsi/linkdata/ps3stor/ps3_device_update.c b/drivers/scsi/linkdata/ps3stor/ps3_device_update.c new file mode 100644 index 0000000000000000000000000000000000000000..8f9a0ef8fa6b18971ae2d9c514e25cf2a60efb80 --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/ps3_device_update.c @@ -0,0 +1,1823 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include "ps3_device_update.h" + +#ifdef _WINDOWS + +#include "ps3_dev_adp.h" + +#else +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "ps3_device_manager_sas.h" +#endif + +#include "ps3_event.h" +#include "ps3_htp_meta.h" +#include "ps3_htp_event.h" +#include "ps3_mgr_cmd.h" +#include "ps3_ioc_manager.h" +#include "ps3_driver_log.h" +#include "ps3_cmd_statistics.h" +#include "ps3_device_update.h" +#include "ps3_scsi_cmd_err.h" +#include "ps3_r1x_write_lock.h" +#include "ps3_ioc_state.h" +#include "ps3_kernel_version.h" + +static int ps3_pd_del(struct ps3_instance *instance, + struct PS3DiskDevPos *dev_pos, unsigned char config_flag); + +static int ps3_pd_add(struct ps3_instance *instance, + struct PS3DiskDevPos *dev_pos); + +unsigned int ps3_scsi_dev_magic(struct ps3_instance *instance, + struct scsi_device *sdev) +{ + unsigned int dev_magic_num = 0; + struct ps3_scsi_priv_data *p_priv_data = NULL; + + ps3_mutex_lock(&instance->dev_context.dev_priv_lock); + p_priv_data = PS3_SDEV_PRI_DATA(sdev); + if (p_priv_data != NULL) { + if (PS3_IS_VD_CHANNEL(instance, PS3_SDEV_CHANNEL(sdev)) || + ps3_sas_is_support_smp(instance)) { + dev_magic_num = p_priv_data->disk_pos.diskMagicNum; + } else { + dev_magic_num = p_priv_data->disk_pos.checkSum; + } + } + ps3_mutex_unlock(&instance->dev_context.dev_priv_lock); + + return dev_magic_num; +} + +unsigned char ps3_pd_scsi_visible_check(struct ps3_instance *instance, + struct PS3DiskDevPos *disk_pos, + unsigned char dev_type, + unsigned char config_flag, + unsigned char pd_state) +{ + unsigned char visible = PS3_DRV_TRUE; + + if (pd_state == DEVICE_STATE_OUTING) { + LOG_INFO( + "hno:%u PD[%u:%u:%u], dev_type[%s] state[%s] is outing\n", + PS3_HOST(instance), PS3_CHANNEL(disk_pos), + PS3_TARGET(disk_pos), PS3_PDID(disk_pos), + namePS3DevType((enum PS3DevType)dev_type), + getDeviceStateName((enum DeviceState)pd_state)); + visible = PS3_DRV_FALSE; + goto l_out; + } + + if (ps3_is_fake_pd(dev_type)) { + LOG_DEBUG( + "hno:%u PD[%u:%u:%u], dev_type[%s] device is visible all time\n", + PS3_HOST(instance), PS3_CHANNEL(disk_pos), + PS3_TARGET(disk_pos), PS3_PDID(disk_pos), + namePS3DevType((enum PS3DevType)dev_type)); + goto l_out; + } + +#if defined(PS3_SUPPORT_LINX80) + if (!instance->is_support_jbod && + ps3_check_pd_is_vd_member(config_flag)) { + LOG_DEBUG( + "hno:%u PD[%u:%u:%u] is vd component, config_flag[%s]\n", + PS3_HOST(instance), PS3_CHANNEL(disk_pos), + PS3_TARGET(disk_pos), PS3_PDID(disk_pos), + getPdStateName((enum MicPdState)config_flag, + instance->is_raid)); + visible = PS3_DRV_FALSE; + goto l_out; + } +#endif + + if (instance->is_support_jbod && config_flag != MIC_PD_STATE_JBOD) { + LOG_DEBUG( + "hno:%u PD[%u:%u:%u] config_flag is [%s], invisible\n", + PS3_HOST(instance), PS3_CHANNEL(disk_pos), + PS3_TARGET(disk_pos), PS3_PDID(disk_pos), + getPdStateName((enum MicPdState)config_flag, + instance->is_raid)); + visible = PS3_DRV_FALSE; + goto l_out; + } + +l_out: + LOG_DEBUG("hno:%u PD[%u:%u:%u] is %s\n", PS3_HOST(instance), + PS3_CHANNEL(disk_pos), PS3_TARGET(disk_pos), + PS3_PDID(disk_pos), visible ? "visible" : "invisible"); + + return visible; +} + +static inline unsigned char +ps3_vd_scsi_visible_check(struct ps3_instance *instance, + struct PS3DiskDevPos *disk_pos, + unsigned char is_hidden, unsigned char disk_state) +{ + unsigned char visible = PS3_DRV_TRUE; + + if (is_hidden) { + LOG_INFO("hno:%u vd[%u:%u:%u] is hidden\n", PS3_HOST(instance), + PS3_CHANNEL(disk_pos), PS3_TARGET(disk_pos), + PS3_VDID(disk_pos)); + visible = PS3_DRV_FALSE; + } + + if (disk_state == MIC_VD_STATE_OFFLINE) { + LOG_INFO("hno:%u vd[%u:%u:%u] state offline\n", + PS3_HOST(instance), PS3_CHANNEL(disk_pos), + PS3_TARGET(disk_pos), PS3_VDID(disk_pos)); + visible = PS3_DRV_FALSE; + } + + return visible; +} + +static unsigned char ps3_scsi_visible_check(struct ps3_instance *instance, + unsigned char channel, + unsigned short target_id, + unsigned char disk_type) +{ + struct PS3VDEntry *p_vd_entry = NULL; + struct ps3_pd_entry *p_pd_entry = NULL; + unsigned char visible = PS3_DRV_FALSE; + + if (disk_type == PS3_DISK_TYPE_PD) { + p_pd_entry = ps3_dev_mgr_lookup_pd_info(instance, channel, + target_id); + if (p_pd_entry) { + visible = ps3_pd_scsi_visible_check( + instance, &p_pd_entry->disk_pos, + p_pd_entry->dev_type, p_pd_entry->config_flag, + p_pd_entry->state); + } + } else if (disk_type == PS3_DISK_TYPE_VD) { + p_vd_entry = ps3_dev_mgr_lookup_vd_info(instance, channel, + target_id); + if (p_vd_entry) { + visible = ps3_vd_scsi_visible_check( + instance, &p_vd_entry->diskPos, + p_vd_entry->isHidden, p_vd_entry->diskState); + } + } + + if (p_pd_entry || p_vd_entry) { + LOG_INFO("hno:%u dev[%u:%u] visible is %s\n", + PS3_HOST(instance), channel, target_id, + visible ? "PS3_TRUE" : "PS3_FALSE"); + } + return visible; +} + +static inline void __ps3_scsi_add_device(struct ps3_instance *instance, + struct PS3DiskDevPos *disk_pos, + unsigned char dev_type) +{ + int ret = PS3_SUCCESS; + unsigned char channel = PS3_CHANNEL(disk_pos); + unsigned short id = PS3_TARGET(disk_pos); + + ret = ps3_scsi_add_device_ack(instance, disk_pos, dev_type); + if (ret != PS3_SUCCESS) { + LOG_WARN( + "hno:%u dev[%u:%u] magic[%#x] add scsi device ack NOK, ret %d\n", + PS3_HOST(instance), channel, id, disk_pos->diskMagicNum, + ret); + } else { + LOG_INFO("hno:%u dev_type[%d] id[%u:%u] add device begin\n", + PS3_HOST(instance), dev_type, channel, id); + + ret = ps3_scsi_add_device(instance, channel, id, 0); + + LOG_INFO("hno:%u dev_type[%d] id[%u:%u] add end, add ret %d\n", + PS3_HOST(instance), dev_type, channel, id, ret); + } +} + +static void _ps3_scsi_add_device(struct ps3_instance *instance, + struct PS3DiskDevPos *disk_pos, + unsigned char dev_type) +{ + struct scsi_device *sdev = NULL; + unsigned char channel = PS3_CHANNEL(disk_pos); + unsigned short target_id = PS3_TARGET(disk_pos); + unsigned short disk_id = PS3_VDID(disk_pos); + + LOG_DEBUG( + "hno:%u dev_type[%s] id[%u:%u] disk_id[%d] add scsi device start\n", + PS3_HOST(instance), namePS3DiskType((enum PS3DiskType)dev_type), + channel, target_id, disk_id); + + sdev = ps3_scsi_device_lookup(instance, channel, target_id, 0); + if (sdev == NULL) { + __ps3_scsi_add_device(instance, disk_pos, dev_type); + } else { + ps3_scsi_device_put(instance, sdev); + LOG_INFO( + "hno:%u channel[%u] target[%u] device already exists in os\n", + PS3_HOST(instance), channel, target_id); + } + + LOG_DEBUG( + "hno:%u dev_type[%s] id[%u:%u] disk_id[%d] add scsi device end\n", + PS3_HOST(instance), namePS3DiskType((enum PS3DiskType)dev_type), + channel, target_id, disk_id); +} + +static int _ps3_add_disk(struct ps3_instance *instance, unsigned char channel, + unsigned short target_id, unsigned char disk_type) +{ + int ret = PS3_SUCCESS; + struct ps3_pd_entry *p_pd_entry = NULL; + struct PS3VDEntry *p_vd_entry = NULL; + struct PS3DiskDevPos *p_diskPos = NULL; + unsigned char dev_type = PS3_DISK_TYPE_UNKNOWN; + + if (!ps3_scsi_visible_check(instance, channel, target_id, disk_type)) + goto l_out; + + if (disk_type == PS3_DISK_TYPE_PD) { + p_pd_entry = ps3_dev_mgr_lookup_pd_info(instance, channel, + target_id); + if (p_pd_entry) { +#ifndef _WINDOWS + if (ps3_sas_is_support_smp(instance) && + p_pd_entry->dev_type != PS3_DEV_TYPE_NVME_SSD && + p_pd_entry->dev_type != PS3_DEV_TYPE_VEP) { + ps3_sas_add_device(instance, p_pd_entry); + goto l_out; + } else { +#endif + p_diskPos = &p_pd_entry->disk_pos; + dev_type = PS3_DISK_TYPE_PD; +#ifndef _WINDOWS + } +#endif + } + } else if (disk_type == PS3_DISK_TYPE_VD) { + p_vd_entry = ps3_dev_mgr_lookup_vd_info(instance, channel, + target_id); + if (p_vd_entry) { + p_diskPos = &p_vd_entry->diskPos; + dev_type = PS3_DISK_TYPE_VD; + } + } + + _ps3_scsi_add_device(instance, p_diskPos, dev_type); +l_out: + return ret; +} +#ifndef _WINDOWS +static unsigned char ps3_sd_available_check(struct scsi_device *sdev) +{ + unsigned char ret = PS3_TRUE; + + if (dev_get_drvdata(&sdev->sdev_gendev) == NULL) + ret = PS3_FALSE; + return ret; +} + +void ps3_scsi_scan_host(struct ps3_instance *instance) +{ + struct PS3ChannelInfo *channel_info = &instance->ctrl_info.channelInfo; + unsigned short timeout = instance->ctrl_info.isotoneTimeOut; + unsigned char channelType = 0; + unsigned short maxDevNum = 0; + unsigned char i = 0; + unsigned short j = 0; + struct scsi_device *sdev = NULL; + unsigned char bootdrv_ok = PS3_FALSE; + unsigned short count = 0; + + LOG_WARN("hno:%u scan device begin, channel number %d\n", + PS3_HOST(instance), channel_info->channelNum); + for (i = 0; i < channel_info->channelNum; i++) { + channelType = channel_info->channels[i].channelType; + maxDevNum = channel_info->channels[i].maxDevNum; + + LOG_INFO("hno:%u channel[%u] is type %s,max dev num is:%d\n", + PS3_HOST(instance), i, + namePS3ChannelType((enum PS3ChannelType)channelType), + maxDevNum); + for (j = 0; j < maxDevNum; j++) { + _ps3_add_disk(instance, i, j, channelType); + if (timeout != 0 && !bootdrv_ok && i == 0 && + ps3_scsi_visible_check(instance, i, j, + channelType)) { + for (count = 0; count < timeout; count++) { + sdev = ps3_scsi_device_lookup(instance, + i, j, 0); + if (sdev == NULL) + continue; + if (!ps3_sd_available_check(sdev)) { + ps3_scsi_device_put(instance, + sdev); + ps3_msleep( + PS3_PS3_LOOP_TIME_INTERVAL_1000MS); + continue; + } + ps3_scsi_device_put(instance, sdev); + LOG_WARN( + "hno:%u bootdrive disk[%u:%u] drive-letter add complete\n", + PS3_HOST(instance), i, j); + break; + } + bootdrv_ok = PS3_TRUE; + } + } + } + + LOG_WARN("hno:%u scan device end, channel number %d\n", + PS3_HOST(instance), channel_info->channelNum); +} +#endif +static inline int _ps3_del_disk(struct ps3_instance *instance, + struct scsi_device *sdev) +{ + int ret = PS3_SUCCESS; + struct ps3_scsi_priv_data *p_priv_data = NULL; + + unsigned char dev_type = PS3_DISK_TYPE_UNKNOWN; + unsigned char channel = PS3_SDEV_CHANNEL(sdev); + unsigned short target_id = PS3_SDEV_TARGET(sdev); + struct PS3DiskDevPos disk_pos; + unsigned char encl_id = 0; + unsigned char phy_id = 0; + + if (PS3_IS_PD_CHANNEL(instance, channel)) { + dev_type = PS3_DISK_TYPE_PD; + } else if (PS3_IS_VD_CHANNEL(instance, channel)) { + dev_type = PS3_DISK_TYPE_VD; + } else { + LOG_ERROR("hno:%u dev id[%u:%u] channel NOK\n", + PS3_HOST(instance), channel, target_id); + PS3_BUG(); + goto l_out; + } + + ps3_mutex_lock(&instance->dev_context.dev_priv_lock); + p_priv_data = (struct ps3_scsi_priv_data *)sdev->hostdata; + if (p_priv_data != NULL) { + + + ps3_qos_disk_del(instance, p_priv_data); + if (ps3_sas_is_support_smp(instance) && + dev_type == PS3_DISK_TYPE_PD && + p_priv_data->dev_type != PS3_DEV_TYPE_NVME_SSD && + p_priv_data->dev_type != PS3_DEV_TYPE_VEP) { + memset(&disk_pos, 0, sizeof(struct PS3DiskDevPos)); + memcpy(&disk_pos, &p_priv_data->disk_pos, + sizeof(struct PS3DiskDevPos)); + encl_id = p_priv_data->encl_id; + phy_id = p_priv_data->phy_id; + ps3_mutex_unlock(&instance->dev_context.dev_priv_lock); + ps3_sas_remove_device(instance, &disk_pos, encl_id, + phy_id); + } else { + if (p_priv_data->dev_type == PS3_DEV_TYPE_VD && + p_priv_data->lock_mgr.hash_mgr != NULL) { + p_priv_data->lock_mgr.dev_deling = PS3_TRUE; + ps3_r1x_conflict_queue_clean( + p_priv_data, + PS3_SCSI_RESULT_HOST_STATUS( + DID_NO_CONNECT)); + } + ps3_mutex_unlock(&instance->dev_context.dev_priv_lock); + LOG_WARN("remove device channel[%u], id[%u] begin\n", + channel, target_id); + ps3_scsi_remove_device(instance, sdev); + LOG_WARN("remove device channel[%u], id[%u] end\n", + channel, target_id); + } + } else { + ps3_mutex_unlock(&instance->dev_context.dev_priv_lock); + LOG_DEBUG("hno:%u del disk, priv data NULL, [%u:%u],\n", + PS3_HOST(instance), PS3_SDEV_CHANNEL(sdev), + PS3_SDEV_TARGET(sdev)); + } +l_out: + return ret; +} + +static int ps3_del_disk(struct ps3_instance *instance, unsigned char channel, + unsigned short target_id) +{ + struct scsi_device *sdev = NULL; + int ret = PS3_SUCCESS; +#ifdef _WINDOWS + sdev = ps3_scsi_device_lookup_win(instance, channel, target_id); + if (sdev == NULL || sdev->add_ack != 1) { + LOG_INFO("hno:%u dev[%p] id[%u:%u] not exist scsi device\n", + PS3_HOST(instance), sdev, channel, target_id); + goto l_out; + } +#else + sdev = ps3_scsi_device_lookup(instance, channel, target_id, 0); + if (sdev == NULL) + goto l_out; +#endif + ret = _ps3_del_disk(instance, sdev); + ps3_scsi_device_put(instance, sdev); +l_out: + return ret; +} + +static struct PS3VDEntry *ps3_get_single_vd_info(struct ps3_instance *instance, + struct PS3Dev *dev) +{ + int ret = -PS3_FAILED; + union PS3DiskDev vd_id = { 0 }; + struct PS3VDEntry *p_vd_entry = NULL; + unsigned short virtDiskIdx = PS3_INVALID_DEV_ID; + + vd_id.ps3Dev.virtDiskID = dev->virtDiskID; + vd_id.ps3Dev.softChan = dev->softChan; + vd_id.ps3Dev.devID = dev->devID; + + ret = ps3_vd_info_sync_get(instance, vd_id.diskID, 1); + if (ret != PS3_SUCCESS) { + LOG_ERROR("hno:%u, sync get VD info NOK\n", PS3_HOST(instance)); + goto l_out; + } + if (instance->dev_context.vd_info_buf_sync->count != 1) { + LOG_ERROR("hno:%u, single add VD, has no info\n", + PS3_HOST(instance)); + goto l_out; + } + + p_vd_entry = instance->dev_context.vd_info_buf_sync->vds; + if (PS3_VDID_INVALID(&p_vd_entry->diskPos)) { + LOG_WARN("hno:%u, init single vd info NOK, vdid is 0\n", + PS3_HOST(instance)); + p_vd_entry = NULL; + goto l_out; + } + virtDiskIdx = get_offset_of_vdid(PS3_VDID_OFFSET(instance), + PS3_VDID(&p_vd_entry->diskPos)); + if (unlikely(virtDiskIdx > PS3_MAX_VD_COUNT(instance))) { + LOG_WARN( + "hno:%u, init single vd info NOK, vir_id[%d] > max[%d]\n", + PS3_HOST(instance), virtDiskIdx, + PS3_MAX_VD_COUNT(instance)); + p_vd_entry = NULL; + goto l_out; + } + if (PS3_CHANNEL(&p_vd_entry->diskPos) != dev->softChan || + PS3_TARGET(&p_vd_entry->diskPos) != dev->devID) { + LOG_ERROR( + "hno:%u single add VD[%u:%u:%u] != req VD[%u:%u:%u] magic[%#x] unmatched\n", + PS3_HOST(instance), dev->softChan, dev->devID, + dev->virtDiskID, PS3_CHANNEL(&p_vd_entry->diskPos), + PS3_TARGET(&p_vd_entry->diskPos), + PS3_VDID(&p_vd_entry->diskPos), + p_vd_entry->diskPos.diskMagicNum); + p_vd_entry = NULL; + goto l_out; + } + ps3_vd_busy_scale_get(p_vd_entry); + + LOG_DEBUG("hno:%u VD[%u:%u:%u] got single vd info success\n", + PS3_HOST(instance), dev->softChan, dev->devID, + dev->virtDiskID); +l_out: + return p_vd_entry; +} + +static int ps3_update_single_vd_info(struct ps3_instance *instance, + struct PS3Dev *dev) +{ + int ret = PS3_SUCCESS; + struct PS3VDEntry *p_vd_entry = NULL; + struct ps3_dev_context *p_dev_ctx = &instance->dev_context; + unsigned char vd_table_idx = p_dev_ctx->vd_table_idx & 1; + struct ps3_vd_table *p_vd_tb = &p_dev_ctx->vd_table[vd_table_idx]; + struct PS3VDEntry *p_vd_array = + p_dev_ctx->vd_entries_array[vd_table_idx]; + unsigned short virtDiskIdx = 0; + + p_vd_entry = ps3_get_single_vd_info(instance, dev); + if (p_vd_entry == NULL) { + LOG_ERROR("hno:%u sync get VD info NOK\n", PS3_HOST(instance)); + ret = -PS3_FAILED; + goto l_out; + } + + virtDiskIdx = get_offset_of_vdid(PS3_VDID_OFFSET(instance), + PS3_VDID(&p_vd_entry->diskPos)); + + if (p_vd_entry->isHidden) { + LOG_INFO("hno:%u single add VD[%u:%u] vd info is hidden\n", + PS3_HOST(instance), dev->softChan, dev->devID); + goto l_out; + } + + ps3_qos_vd_init(instance, p_vd_entry); + memcpy(&p_vd_array[virtDiskIdx], p_vd_entry, sizeof(struct PS3VDEntry)); + p_vd_tb->vd_idxs[dev->softChan][dev->devID] = + PS3_VDID(&p_vd_entry->diskPos); + + ps3_vd_info_show(instance, &p_vd_array[virtDiskIdx]); + + LOG_DEBUG("hno:%u, idx[%d], VD[%u:%u] single add, got vd info\n", + PS3_HOST(instance), p_dev_ctx->vd_table_idx, dev->softChan, + dev->devID); +l_out: + return ret; +} + +static unsigned char _ps3_add_disk_prepare(struct ps3_instance *instance, + struct PS3DiskDevPos *dev_pos) +{ + unsigned char is_need_add = PS3_TRUE; + struct scsi_device *sdev = NULL; + unsigned int sdev_magic = 0; + unsigned int new_magic = 0; + int ret = PS3_SUCCESS; + + sdev = ps3_scsi_device_lookup(instance, PS3_CHANNEL(dev_pos), + PS3_TARGET(dev_pos), 0); + if (sdev == NULL) + goto l_out; + + sdev_magic = ps3_scsi_dev_magic(instance, sdev); + if (PS3_IS_VD_CHANNEL(instance, PS3_SDEV_CHANNEL(sdev)) || + ps3_sas_is_support_smp(instance)) { + new_magic = dev_pos->diskMagicNum; + } else { + new_magic = dev_pos->checkSum; + } + + if (new_magic == sdev_magic) { + LOG_INFO("hno:%u check magic same [%u:%u] magic[%#x]\n", + PS3_HOST(instance), PS3_SDEV_CHANNEL(sdev), + PS3_SDEV_TARGET(sdev), sdev_magic); + is_need_add = PS3_FALSE; + } else { + LOG_WARN("hno:%u check dev[%u:%u] magic is diff[%#x != %#x]\n", + PS3_HOST(instance), PS3_SDEV_CHANNEL(sdev), + PS3_SDEV_TARGET(sdev), new_magic, sdev_magic); + ret = _ps3_del_disk(instance, sdev); + if (ret != PS3_SUCCESS) { + LOG_ERROR("hno:%u del dev[%u:%u] unexpect ret[%d]\n", + PS3_HOST(instance), PS3_SDEV_CHANNEL(sdev), + PS3_SDEV_TARGET(sdev), ret); + } + } + ps3_scsi_device_put(instance, sdev); +l_out: + return is_need_add; +} + +void ps3_check_vd_member_change(struct ps3_instance *instance, + struct ps3_pd_entry *local_entry) +{ + struct scsi_device *sdev = NULL; + + if (!ps3_sas_is_support_smp(instance)) + goto l_out; +#if defined(PS3_SUPPORT_LINX80) + (void)sdev; + if (ps3_check_pd_is_vd_member(local_entry->config_flag)) { + LOG_WARN( + "hno:%u change ready to component, remove device channel[%u], id[%u], begin\n", + PS3_HOST(instance), PS3_CHANNEL(&local_entry->disk_pos), + PS3_TARGET(&local_entry->disk_pos)); + ps3_pd_del(instance, &local_entry->disk_pos, + local_entry->config_flag); + LOG_WARN( + "hno:%u change ready to component, remove device channel[%u], id[%u] end\n", + PS3_HOST(instance), PS3_CHANNEL(&local_entry->disk_pos), + PS3_TARGET(&local_entry->disk_pos)); + } else { + ps3_mutex_lock(&instance->dev_context.dev_scan_lock); + sdev = ps3_scsi_device_lookup( + instance, PS3_CHANNEL(&local_entry->disk_pos), + PS3_TARGET(&local_entry->disk_pos), 0); + if (sdev) { + ps3_scsi_device_put(instance, sdev); + ps3_mutex_unlock(&instance->dev_context.dev_scan_lock); + } else { + ps3_mutex_unlock(&instance->dev_context.dev_scan_lock); + ps3_linx80_vd_member_change(instance, local_entry); + } + LOG_WARN( + "hno:%u change component to ready, add device channel[%u], id[%u], begin\n", + PS3_HOST(instance), PS3_CHANNEL(&local_entry->disk_pos), + PS3_TARGET(&local_entry->disk_pos)); + ps3_pd_add(instance, &local_entry->disk_pos); + LOG_WARN( + "hno:%u change component to ready, add device channel[%u], id[%u], end\n", + PS3_HOST(instance), PS3_CHANNEL(&local_entry->disk_pos), + PS3_TARGET(&local_entry->disk_pos)); + } +#else + + ps3_mutex_lock(&instance->dev_context.dev_scan_lock); + sdev = ps3_scsi_device_lookup(instance, + PS3_CHANNEL(&local_entry->disk_pos), + PS3_TARGET(&local_entry->disk_pos), 0); + if (sdev) { + if (ps3_check_pd_is_vd_member(local_entry->config_flag)) { + if (sdev->no_uld_attach != 1) { + sdev->no_uld_attach = 1; + ps3_qos_vd_member_change(instance, local_entry, + sdev, PS3_TRUE); + LOG_WARN( + "hno:%u pd[%u:%u] change ready to component start\n", + PS3_HOST(instance), sdev->channel, + sdev->id); + PS3_WARN_ON(scsi_device_reprobe(sdev)); + LOG_WARN( + "hno:%u pd[%u:%u] change ready to component end\n", + PS3_HOST(instance), sdev->channel, + sdev->id); + } + } else { + if (sdev->no_uld_attach != 0) { + sdev->no_uld_attach = 0; + ps3_qos_vd_member_change(instance, local_entry, + sdev, PS3_FALSE); + LOG_WARN( + "hno:%u pd[%u:%u] change component to ready start\n", + PS3_HOST(instance), sdev->channel, + sdev->id); + PS3_WARN_ON(scsi_device_reprobe(sdev)); + LOG_WARN( + "hno:%u pd[%u:%u] change component to ready end\n", + PS3_HOST(instance), sdev->channel, + sdev->id); + } + } + ps3_scsi_device_put(instance, sdev); + } + ps3_mutex_unlock(&instance->dev_context.dev_scan_lock); +#endif +l_out: + return; +} + +static struct PS3DiskDevPos *ps3_get_info_pos(struct ps3_instance *instance, + unsigned char channel, + unsigned short target_id, + unsigned char disk_type) +{ + struct PS3DiskDevPos *disk_Pos_ret = NULL; + struct ps3_pd_entry *pd_entry = NULL; + struct PS3VDEntry *vd_entry = NULL; + + if (disk_type == PS3_DISK_TYPE_PD) { + pd_entry = ps3_dev_mgr_lookup_pd_info(instance, channel, + target_id); + if (pd_entry != NULL) + disk_Pos_ret = &pd_entry->disk_pos; + } else if (disk_type == PS3_DISK_TYPE_VD) { + vd_entry = ps3_dev_mgr_lookup_vd_info(instance, channel, + target_id); + if (vd_entry != NULL) + disk_Pos_ret = &vd_entry->diskPos; + } + + return disk_Pos_ret; +} + +static inline int ps3_add_disk(struct ps3_instance *instance, + struct PS3DiskDevPos *dev_pos, + unsigned char disk_type) +{ + int ret = PS3_SUCCESS; + struct PS3DiskDevPos *dev_pos_newes = NULL; + + if (disk_type == PS3_DISK_TYPE_PD) { + ret = ps3_dev_mgr_pd_info_get(instance, PS3_CHANNEL(dev_pos), + PS3_TARGET(dev_pos), + PS3_PDID(dev_pos)); + } else if (disk_type == PS3_DISK_TYPE_VD) { + ret = ps3_update_single_vd_info(instance, + &dev_pos->diskDev.ps3Dev); + } + + if (ret != PS3_SUCCESS) { + LOG_ERROR("hno:%u single add dev[%u:%u] get info NOK\n", + PS3_HOST(instance), PS3_CHANNEL(dev_pos), + PS3_TARGET(dev_pos)); + goto l_out; + } + + dev_pos_newes = ps3_get_info_pos(instance, PS3_CHANNEL(dev_pos), + PS3_TARGET(dev_pos), disk_type); + if (dev_pos_newes == NULL) { + ret = -PS3_FAILED; + LOG_WARN("hno:%u update pos[%u:%u] invalid\n", + PS3_HOST(instance), PS3_CHANNEL(dev_pos), + PS3_TARGET(dev_pos)); + goto l_out; + } + + if (!_ps3_add_disk_prepare(instance, dev_pos_newes)) + goto l_out; + + ret = _ps3_add_disk(instance, PS3_CHANNEL(dev_pos_newes), + PS3_TARGET(dev_pos_newes), disk_type); + if (ret != PS3_SUCCESS) { + LOG_ERROR("hno:%u single add dev[%u:%u] NOK\n", + PS3_HOST(instance), PS3_CHANNEL(dev_pos_newes), + PS3_TARGET(dev_pos_newes)); + goto l_out; + } +l_out: + return ret; +} + +static int ps3_pd_add(struct ps3_instance *instance, + struct PS3DiskDevPos *dev_pos) +{ + int ret = -PS3_FAILED; + + ps3_mutex_lock(&instance->dev_context.dev_scan_lock); + + if (unlikely(!PS3_IS_PD_CHANNEL(instance, PS3_CHANNEL(dev_pos)))) { + LOG_ERROR( + "hno:%u pd[%u:%u] single add, but channel is not pd\n", + PS3_HOST(instance), PS3_CHANNEL(dev_pos), + PS3_TARGET(dev_pos)); + PS3_BUG(); + goto l_out; + } + + ret = ps3_add_disk(instance, dev_pos, PS3_DISK_TYPE_PD); + if (ret != PS3_SUCCESS) { + LOG_ERROR("hno:%u add PD NOK\n", PS3_HOST(instance)); + goto l_out; + } +l_out: + ps3_mutex_unlock(&instance->dev_context.dev_scan_lock); + + return ret; +} + +static unsigned char ps3_pd_ext_del_done_check(struct ps3_instance *instance, + struct PS3DiskDevPos *dev_pos) +{ + struct scsi_device *sdev = NULL; + unsigned char is_need_del_done = PS3_FALSE; + int ret = PS3_SUCCESS; + +#ifdef _WINDOWS + sdev = ps3_scsi_device_lookup_win(instance, PS3_CHANNEL(dev_pos), + PS3_TARGET(dev_pos)); + if (unlikely(sdev == NULL || sdev->add_ack != 1)) + is_need_del_done = TRUE; +#else + struct ps3_pd_entry *pd_entry = NULL; + + sdev = ps3_scsi_device_lookup(instance, PS3_CHANNEL(dev_pos), + PS3_TARGET(dev_pos), 0); + if (unlikely(sdev == NULL)) { + if (ps3_sas_is_support_smp(instance)) { + pd_entry = ps3_dev_mgr_lookup_pd_info_by_id( + instance, PS3_PDID(dev_pos)); + if (pd_entry != NULL && pd_entry->sas_rphy != NULL) { + ps3_sas_remove_device(instance, + &pd_entry->disk_pos, + pd_entry->encl_id, + pd_entry->phy_id); + } else { + LOG_ERROR( + "hno:%u, can not found PD [%u:%u:%u] pd_entry:%p or sas_rphy is NULL\n", + PS3_HOST(instance), + PS3_CHANNEL(dev_pos), + PS3_TARGET(dev_pos), PS3_PDID(dev_pos), + pd_entry); + } + } + is_need_del_done = PS3_TRUE; + } +#endif + if (is_need_del_done) { + LOG_WARN("hno:%u dev id[%u:%u] not exist scsi device\n", + PS3_HOST(instance), PS3_CHANNEL(dev_pos), + PS3_TARGET(dev_pos)); + + ret = ps3_scsi_remove_device_done(instance, dev_pos, + PS3_DISK_TYPE_PD); + if (ret != PS3_SUCCESS) { + LOG_ERROR( + "hno:%u dev[%u:%u:%u] magic[%#x] dev del done NOK %d\n", + PS3_HOST(instance), PS3_CHANNEL(dev_pos), + PS3_TARGET(dev_pos), PS3_PDID(dev_pos), + dev_pos->diskMagicNum, ret); + } + } else { + ps3_scsi_device_put(instance, sdev); + } + + return is_need_del_done; +} + +static int ps3_pd_del(struct ps3_instance *instance, + struct PS3DiskDevPos *dev_pos, unsigned char config_flag) +{ + int ret = -PS3_FAILED; + struct ps3_dev_context *p_dev_ctx = &instance->dev_context; + struct ps3_pd_table *p_pd_tb = &p_dev_ctx->pd_table; + unsigned short disk_idx = 0; + + ps3_mutex_lock(&instance->dev_context.dev_scan_lock); + + if (!ps3_dev_id_valid_check(instance, PS3_CHANNEL(dev_pos), + PS3_TARGET(dev_pos), PS3_DISK_TYPE_PD)) { + PS3_BUG(); + goto l_out; + } + + if (ps3_pd_ext_del_done_check(instance, dev_pos)) { + ret = PS3_SUCCESS; + goto l_clean_again; + } + + ret = ps3_del_disk(instance, PS3_CHANNEL(dev_pos), PS3_TARGET(dev_pos)); + if (ret != PS3_SUCCESS) { + LOG_ERROR( + "hno:%u unable to delete scsi device PD[%u:%u] ret %d\n", + PS3_HOST(instance), PS3_CHANNEL(dev_pos), + PS3_TARGET(dev_pos), ret); + goto l_out; + } +l_clean_again: + disk_idx = p_pd_tb->pd_idxs[PS3_CHANNEL(dev_pos)][PS3_TARGET(dev_pos)]; + p_pd_tb->pd_idxs[PS3_CHANNEL(dev_pos)][PS3_TARGET(dev_pos)] = + PS3_INVALID_VALUE; + + p_dev_ctx->pd_entries_array[disk_idx].config_flag = config_flag; + +l_out: + ps3_mutex_unlock(&instance->dev_context.dev_scan_lock); + return ret; +} + +static int ps3_vd_add(struct ps3_instance *instance, + struct PS3DiskDevPos *dev_pos) +{ + int ret = -PS3_FAILED; + struct PS3Dev *dev = &dev_pos->diskDev.ps3Dev; + + if (unlikely(!PS3_IS_VD_CHANNEL(instance, dev->softChan))) { + LOG_ERROR( + "hno:%u VD[%u:%u] single add, but channel is not vd\n", + PS3_HOST(instance), dev->softChan, dev->devID); + PS3_BUG(); + goto l_out; + } + + ret = ps3_add_disk(instance, dev_pos, PS3_DISK_TYPE_VD); + if (ret != PS3_SUCCESS) { + LOG_ERROR("hno:%u add VD NOK\n", PS3_HOST(instance)); + goto l_out; + } +l_out: + return ret; +} + +static int ps3_vd_del(struct ps3_instance *instance, unsigned char channel, + unsigned short target_id) +{ + int ret = -PS3_FAILED; + + if (!ps3_dev_id_valid_check(instance, channel, target_id, + PS3_DISK_TYPE_VD)) { + PS3_BUG(); + goto l_out; + } + + ret = ps3_del_disk(instance, channel, target_id); + if (ret != PS3_SUCCESS) { + LOG_ERROR( + "hno:%u unable to delete scsi device VD[%u:%u] ret %d\n", + PS3_HOST(instance), channel, target_id, ret); + goto l_out; + } + +l_out: + return ret; +} + +static int ps3_vd_hidden_change(struct ps3_instance *instance, + struct PS3Dev *dev) +{ + int ret = -PS3_FAILED; + struct PS3VDEntry *p_vd_entry = NULL; + struct PS3DiskDevPos disk_pos; + + p_vd_entry = ps3_get_single_vd_info(instance, dev); + if (unlikely(p_vd_entry == NULL)) { + LOG_ERROR("hno:%u sync get VD info NOK\n", PS3_HOST(instance)); + goto l_out; + } + + if (!p_vd_entry->isHidden) { + disk_pos = p_vd_entry->diskPos; + ret = ps3_vd_add(instance, &disk_pos); + } else { + ret = ps3_vd_del(instance, (unsigned char)dev->softChan, + dev->devID); + } + + if (ret != PS3_SUCCESS) { + LOG_ERROR( + "hno:%u vd hidden change NOK, dev[%u:%u:%u], ret[%d]\n", + PS3_HOST(instance), dev->softChan, dev->devID, + dev->virtDiskID, ret); + ret = -PS3_FAILED; + } + +l_out: + return ret; +} + +static inline unsigned char +ps3_check_pd_is_same(struct ps3_instance *instance, + struct PS3DiskDevPos *src_dev_pos, + struct PS3DiskDevPos *dest_dev_pos) +{ + unsigned char ret_valid = PS3_FALSE; + + if (likely(dest_dev_pos->diskMagicNum == src_dev_pos->diskMagicNum && + PS3_DISKID(dest_dev_pos) == PS3_DISKID(src_dev_pos))) { + LOG_DEBUG("hno:%u check pd id same [%u:%u:%u] magic[%#x]\n", + PS3_HOST(instance), PS3_CHANNEL(dest_dev_pos), + PS3_TARGET(dest_dev_pos), PS3_PDID(dest_dev_pos), + src_dev_pos->diskMagicNum); + ret_valid = PS3_TRUE; + } else { + LOG_FILE_WARN( + "hno:%u check mismatch, src dev[%u:%u:%u] magic[%#x]\n" + "\tdest dev[%u:%u:%u] magic[%#x]\n", + PS3_HOST(instance), PS3_CHANNEL(src_dev_pos), + PS3_TARGET(src_dev_pos), PS3_PDID(src_dev_pos), + src_dev_pos->diskMagicNum, PS3_CHANNEL(dest_dev_pos), + PS3_TARGET(dest_dev_pos), PS3_PDID(dest_dev_pos), + dest_dev_pos->diskMagicNum); + } + + return ret_valid; +} +static inline void +ps3_update_info_by_list_item(struct ps3_pd_entry *local_entry, + union PS3Device *list_item, unsigned char is_raid) +{ + if (unlikely((local_entry->disk_pos.diskMagicNum != + list_item->pd.diskPos.diskMagicNum) || + (PS3_DISKID(&local_entry->disk_pos) != + PS3_DISKID(&list_item->pd.diskPos)))) { + LOG_ERROR( + "Device is mismatch, local entry device[%u:%u:%u]\n" + "\tand magic[%#x], new device[%u:%u:%u] and magic[%#x]\n", + PS3_CHANNEL(&local_entry->disk_pos), + PS3_TARGET(&local_entry->disk_pos), + PS3_PDID(&local_entry->disk_pos), + local_entry->disk_pos.diskMagicNum, + PS3_CHANNEL(&list_item->pd.diskPos), + PS3_TARGET(&list_item->pd.diskPos), + PS3_PDID(&list_item->pd.diskPos), + list_item->pd.diskPos.diskMagicNum); + return; + } + + local_entry->config_flag = list_item->pd.configFlag; + local_entry->state = list_item->pd.diskState; + LOG_INFO("single pd info update PD[%u:%u] config_flag[%s], state[%s]\n", + PS3_CHANNEL(&list_item->pd.diskPos), + PS3_TARGET(&list_item->pd.diskPos), + getPdStateName((enum MicPdState)local_entry->config_flag, + is_raid), + getDeviceStateName((enum DeviceState)local_entry->state)); +} + +static int ps3_update_single_pd_info(struct ps3_instance *instance, + struct PS3DiskDevPos *dev_pos, + union PS3Device *list_item) +{ + int ret = PS3_SUCCESS; + unsigned char channel = dev_pos->diskDev.ps3Dev.softChan; + struct ps3_pd_entry *p_pd_entry = NULL; + + if (unlikely(!PS3_IS_PD_CHANNEL(instance, channel))) { + LOG_ERROR( + "hno:%u , PD[%u:%u] info update, but channel is not pd\n", + PS3_HOST(instance), PS3_CHANNEL(dev_pos), + PS3_TARGET(dev_pos)); + ret = -PS3_FAILED; + PS3_BUG(); + goto l_out; + } + + p_pd_entry = + ps3_dev_mgr_lookup_pd_info_by_id(instance, PS3_PDID(dev_pos)); + if (p_pd_entry == NULL) { + LOG_FILE_WARN( + "hno:%u single PD info update[%u:%u], cannot find entry\n", + PS3_HOST(instance), PS3_CHANNEL(dev_pos), + PS3_TARGET(dev_pos)); + } + + if (p_pd_entry != NULL && + !ps3_check_pd_is_same(instance, &p_pd_entry->disk_pos, dev_pos) && + list_item) { + ps3_update_info_by_list_item(p_pd_entry, list_item, + instance->is_raid); + } else { + ret = ps3_dev_mgr_pd_info_get(instance, PS3_CHANNEL(dev_pos), + PS3_TARGET(dev_pos), + PS3_PDID(dev_pos)); + if (ret != PS3_SUCCESS) { + LOG_ERROR("hno:%u, single add get PD[%u:%u] info NOK\n", + PS3_HOST(instance), PS3_CHANNEL(dev_pos), + PS3_TARGET(dev_pos)); + goto l_out; + } + + p_pd_entry = ps3_dev_mgr_lookup_pd_info_by_id( + instance, PS3_PDID(dev_pos)); + if (p_pd_entry == NULL) { + LOG_WARN( + "hno:%u single PD info update[%u:%u], cannot find new entry\n", + PS3_HOST(instance), PS3_CHANNEL(dev_pos), + PS3_TARGET(dev_pos)); + goto l_out; + } + } + + if (instance->ioc_adpter->check_vd_member_change != NULL) { + instance->ioc_adpter->check_vd_member_change(instance, + p_pd_entry); + } + +l_out: + return ret; +} + +static void ps3_dev_update_pre_check(struct PS3EventDetail *event_detail, + unsigned int event_cnt) +{ + unsigned char i = 0; + unsigned char vd_count_idx = 0XFF; + unsigned char pd_count_idx = 0XFF; + unsigned char pd_info_idx = 0XFF; + + for (i = 0; i < event_cnt; i++) { + switch (event_detail[i].eventCode) { + case PS3_EVT_CODE(MGR_EVT_MULITPILE_PD_IN): + case PS3_EVT_CODE(MGR_EVT_MULITPILE_PD_OUT): + case PS3_EVT_CODE(MGR_EVT_MULITPILE_JBOD): + case PS3_EVT_CODE(MGR_EVT_MULITPILE_READY): + case PS3_EVT_CODE(MGR_EVT_BACKPLANE_ON): + case PS3_EVT_CODE(MGR_EVT_BACKPLANE_OFF): + case PS3_EVT_CODE(MGR_EVT_MULITPILE_PD_STATE_CHANGE): + pd_count_idx = i; + break; + case PS3_EVT_CODE(MGR_EVT_MULITPILE_VD_IN): + case PS3_EVT_CODE(MGR_EVT_MULITPILE_VD_OUT): + vd_count_idx = i; + break; + default: + break; + } + } + + if (pd_count_idx == 0xFF && vd_count_idx == 0xFF && + pd_info_idx == 0xFF) { + return; + } + + LOG_DEBUG( + "detail update pd_count_idx[%d], vd_count_idx[%d], pd_info_idx[%d]\n", + pd_count_idx, vd_count_idx, pd_info_idx); + + for (i = 0; i < event_cnt; i++) { + switch (event_detail[i].eventType) { + case PS3_EVT_PD_COUNT: + if (pd_count_idx != 0xFF && pd_count_idx != i) { + LOG_DEBUG( + "detail update remove %d eventCode[%d]\n", + i, event_detail[i].eventCode); + event_detail[i].eventType = + PS3_EVT_ILLEGAL_TYPE; + event_detail[i].eventCode = + PS3_EVT_CODE(MGR_EVT_PD_COUNT_START); + } + break; + case PS3_EVT_VD_COUNT: + if (vd_count_idx != 0xFF && vd_count_idx != i) { + LOG_DEBUG( + "detail update remove %d eventCode[%d]\n", + i, event_detail[i].eventCode); + event_detail[i].eventType = + PS3_EVT_ILLEGAL_TYPE; + event_detail[i].eventCode = + PS3_EVT_CODE(MGR_EVT_VD_COUNT_START); + } + break; + case PS3_EVT_PD_ATTR: + if (pd_info_idx != 0xFF && pd_info_idx != i) { + LOG_DEBUG( + "detail update remove %d eventCode[%d]\n", + i, event_detail[i].eventCode); + event_detail[i].eventType = + PS3_EVT_ILLEGAL_TYPE; + event_detail[i].eventCode = + PS3_EVT_CODE(MGR_EVT_PD_ATTR_START); + } + + break; + default: + break; + } + } +} + +static unsigned char ps3_get_sdev_pose_by_chl_tid( + struct ps3_instance *instance, const unsigned char channel, + const unsigned short target_id, struct PS3DiskDevPos *disk_Pos) +{ + struct scsi_device *sdev = NULL; + struct ps3_scsi_priv_data *scsi_priv = NULL; + unsigned char ret = PS3_TRUE; + + sdev = ps3_scsi_device_lookup(instance, channel, target_id, 0); + if (unlikely(sdev == NULL)) + ret = PS3_FALSE; + if (ret) { + ps3_mutex_lock(&instance->dev_context.dev_priv_lock); + scsi_priv = PS3_SDEV_PRI_DATA(sdev); + if (scsi_priv != NULL) { + *disk_Pos = scsi_priv->disk_pos; + } else { + ret = PS3_FALSE; + LOG_DEBUG( + "hno:%u get sdev pos, priv data NULL, [%u:%u],\n", + PS3_HOST(instance), PS3_SDEV_CHANNEL(sdev), + PS3_SDEV_TARGET(sdev)); + } + ps3_mutex_unlock(&instance->dev_context.dev_priv_lock); + ps3_scsi_device_put(instance, sdev); + } + + return ret; +} + +static int _ps3_add_remove_multi_pd(struct ps3_instance *instance, + unsigned char channel, + unsigned short target_id) +{ + int ret = PS3_SUCCESS; + int ret_tmp = PS3_SUCCESS; + union PS3Device *p_dev = NULL; + struct PS3DiskDevPos pd_Pos; + unsigned char config_flag = MIC_PD_STATE_UNKNOWN; + + p_dev = ps3_dev_mgr_lookup_pd_list(instance, channel, target_id); + if (p_dev == NULL) { + if (ps3_get_sdev_pose_by_chl_tid(instance, channel, target_id, + &pd_Pos)) { + ret_tmp = ps3_pd_del(instance, &pd_Pos, + MIC_PD_STATE_UNKNOWN); + } + + goto l_out; + } + + if (ps3_pd_scsi_visible_check( + instance, &p_dev->pd.diskPos, + ps3_get_converted_dev_type(p_dev->pd.driverType, + p_dev->pd.mediumType), + p_dev->pd.configFlag, p_dev->pd.diskState)) { + ret_tmp = ps3_pd_add(instance, &p_dev->pd.diskPos); + } else { + ret_tmp = ps3_dev_mgr_pd_info_get(instance, channel, target_id, + PS3_PDID(&p_dev->pd.diskPos)); + if (ret_tmp != PS3_SUCCESS) { + LOG_ERROR("hno:%u get pd info NOK, [%u:%u], ret[%d]\n", + PS3_HOST(instance), channel, target_id, + ret_tmp); + ret = -PS3_FAILED; + } + config_flag = p_dev->pd.configFlag; + + ret_tmp = ps3_pd_del(instance, &p_dev->pd.diskPos, config_flag); + } +l_out: + + if (ret_tmp != PS3_SUCCESS) { + LOG_ERROR("hno:%u add or del pd NOK, [%u:%u], ret[%d]\n", + PS3_HOST(instance), channel, target_id, ret_tmp); + ret = -PS3_FAILED; + } + + return ret; +} + +static int ps3_add_remove_multi_pd(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + int ret_tmp = PS3_SUCCESS; + unsigned short i = 0; + unsigned short j = 0; + unsigned char chan_id = 0; + struct ps3_channel *pd_chan = instance->dev_context.channel_pd; + struct ps3_dev_context *p_dev_ctx = &instance->dev_context; + + LOG_DEBUG("hno:%u, ready to update full pd count\n", + PS3_HOST(instance)); + + ret = ps3_dev_mgr_pd_list_get(instance); + if (ret != PS3_SUCCESS) { + LOG_ERROR("hno:%u get pd list NOK\n", PS3_HOST(instance)); + goto l_out; + } + + for (i = 0; i < p_dev_ctx->pd_channel_count; i++) { + chan_id = pd_chan[i].channel; + for (j = 0; j < pd_chan[i].max_dev_num; j++) { + ret_tmp = + _ps3_add_remove_multi_pd(instance, chan_id, j); + if (ret_tmp != PS3_SUCCESS) + ret = -PS3_FAILED; + } + } + +l_out: + LOG_DEBUG("hno:%u, update full pd count end\n", PS3_HOST(instance)); + + return ret; +} + +static int ps3_add_remove_multi_vd(struct ps3_instance *instance) +{ + int ret_tmp = PS3_SUCCESS; + int ret = PS3_SUCCESS; + unsigned short i = 0; + unsigned short j = 0; + unsigned char chan_id = 0; + struct ps3_channel *vd_chan = instance->dev_context.channel_vd; + struct ps3_dev_context *p_dev_ctx = &instance->dev_context; + union PS3Device *p_dev = NULL; + + LOG_DEBUG("hno:%u ready to update full vd count\n", PS3_HOST(instance)); + + ret = ps3_dev_mgr_vd_list_get(instance); + if (ret != PS3_SUCCESS) { + LOG_ERROR("hno:%u get vd list NOK\n", PS3_HOST(instance)); + goto l_out; + } + + for (i = 0; i < p_dev_ctx->vd_channel_count; i++) { + chan_id = vd_chan[i].channel; + for (j = 0; j < vd_chan[i].max_dev_num; j++) { + p_dev = ps3_dev_mgr_lookup_vd_list(instance, chan_id, + j); + if (p_dev != NULL && + ps3_vd_scsi_visible_check( + instance, &p_dev->vd.diskPos, + p_dev->vd.isHidden, p_dev->vd.diskState)) { + ret_tmp = ps3_vd_add(instance, + &p_dev->pd.diskPos); + } else { + ret_tmp = ps3_vd_del(instance, chan_id, j); + } + + if (ret_tmp != PS3_SUCCESS) { + LOG_ERROR( + "hno:%u update vd[%u:%u] count NOK, ret[%d]\n", + PS3_HOST(instance), chan_id, j, + ret_tmp); + ret = -PS3_FAILED; + } + } + } +l_out: + LOG_DEBUG("hno:%u update full vd count end\n", PS3_HOST(instance)); + + return ret; +} + +static int ps3_update_multi_pd_info(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + int ret_tmp = PS3_SUCCESS; + unsigned short i = 0; + struct ps3_dev_context *p_dev_ctx = &instance->dev_context; + struct PS3DevList *p_pd_list = p_dev_ctx->pd_list_buf; + struct PS3Dev *p_dev = NULL; + + LOG_DEBUG("hno:%u ready to update full pd info\n", PS3_HOST(instance)); + + ret = ps3_pd_list_get(instance); + if (ret != PS3_SUCCESS) { + LOG_ERROR("hno:%u, dev mgr get pd list NOK\n", + PS3_HOST(instance)); + goto l_out; + } + + LOG_INFO("hno:%u get pd list count is %d\n", PS3_HOST(instance), + p_pd_list->count); + + for (i = 0; i < p_pd_list->count; i++) { + p_dev = PS3_DEV(&p_pd_list->devs[i].pd.diskPos); + + if (PS3_PDID_INVALID(&p_pd_list->devs[i].pd.diskPos)) { + LOG_WARN("hno:%u check pd list %d dev pdid is 0\n", + PS3_HOST(instance), i); + continue; + } + + if (!ps3_dev_id_valid_check(instance, + (unsigned char)p_dev->softChan, + p_dev->devID, PS3_DISK_TYPE_PD)) { + PS3_BUG(); + continue; + } + + LOG_INFO("hno:%u update pd info %d dev[%u:%u:%u], magic[%#x], state[%s]\n", + PS3_HOST(instance), i, p_dev->softChan, p_dev->devID, + p_dev->phyDiskID, + p_pd_list->devs[i].pd.diskPos.diskMagicNum, + getDeviceStateName((enum DeviceState)p_pd_list->devs[i] + .pd.diskState)); + + ret_tmp = ps3_update_single_pd_info( + instance, &p_pd_list->devs[i].pd.diskPos, + &p_pd_list->devs[i]); + if (ret_tmp != PS3_SUCCESS) { + LOG_ERROR("hno:%u NOK, %u:%u, ret[%d]\n", + PS3_HOST(instance), p_dev->softChan, + p_dev->devID, ret_tmp); + ret = -PS3_FAILED; + } + } + +l_out: + LOG_DEBUG("hno:%u update full pd info end ret[%d]\n", + PS3_HOST(instance), ret); + + return ret; +} + +static inline int ps3_dev_evt_update_pd_count(struct ps3_instance *instance, + unsigned int eventCode, + struct PS3DiskDevPos *dev_pos) +{ + int ret = PS3_SUCCESS; + + switch (eventCode) { + case PS3_EVT_CODE(MGR_EVT_DEVM_DISK_IN): + case PS3_EVT_CODE(MGR_EVT_DEVM_JBOD): + case PS3_EVT_CODE(MGR_EVT_DEVM_DISK_CHANGE): + ret = ps3_pd_add(instance, dev_pos); + break; + case PS3_EVT_CODE(MGR_EVT_DEVM_DISK_OUT): + ret = ps3_pd_del(instance, dev_pos, MIC_PD_STATE_UNKNOWN); + break; + case PS3_EVT_CODE(MGR_EVT_DEVM_READY): + case PS3_EVT_CODE(MGR_EVT_PD_PRE_READY): + ret = ps3_pd_del(instance, dev_pos, MIC_PD_STATE_READY); + break; + default: + ret = ps3_add_remove_multi_pd(instance); + break; + } + + return ret; +} + +static inline int ps3_dev_evt_update_vd_count(struct ps3_instance *instance, + unsigned int eventCode, + struct PS3DiskDevPos *dev_pos) +{ + int ret = PS3_SUCCESS; + + switch (eventCode) { + case PS3_EVT_CODE(MGR_EVT_VD_CREATED): + case PS3_EVT_CODE(MGR_EVT_VD_OPTIMAL): + case PS3_EVT_CODE(MGR_EVT_VD_PARTIAL_DEGRADE): + case PS3_EVT_CODE(MGR_EVT_VD_DEGRADE): + case PS3_EVT_CODE(MGR_EVT_VD_UNLOCK): + ret = ps3_vd_add(instance, dev_pos); + break; + case PS3_EVT_CODE(MGR_EVT_VD_DELETED): + case PS3_EVT_CODE(MGR_EVT_VD_OFFLINE): + ret = ps3_vd_del(instance, PS3_CHANNEL(dev_pos), + PS3_TARGET(dev_pos)); + break; + case PS3_EVT_CODE(MGR_EVT_VD_HIDDEN_CHANGE): + ret = ps3_vd_hidden_change(instance, PS3_DEV(dev_pos)); + break; + default: + ret = ps3_add_remove_multi_vd(instance); + break; + } + + return ret; +} + +static inline int ps3_dev_evt_update_pd_attr(struct ps3_instance *instance, + unsigned int eventCode, + struct PS3DiskDevPos *dev_pos) +{ + int ret = PS3_SUCCESS; + (void)eventCode; + + ret = ps3_update_single_pd_info(instance, dev_pos, NULL); + + return ret; +} + +int ps3_dev_update_detail_proc(struct ps3_instance *instance, + struct PS3EventDetail *event_detail, + unsigned int event_cnt) +{ + int ret = PS3_SUCCESS; + int ret_map = PS3_SUCCESS; + unsigned int i = 0; + + LOG_INFO("hno:%u, event detail count[%d]\n", PS3_HOST(instance), + event_cnt); + + ps3_dev_update_pre_check(event_detail, event_cnt); + + for (i = 0; i < event_cnt; i++) { + LOG_INFO( + "hno:%u event detail %d event type[%s], eventCode is [%s], dev[%u:%u:%u]\n", + PS3_HOST(instance), i, + nameMgrEvtType(event_detail[i].eventType), + mgrEvtCodeTrans(event_detail[i].eventCode), + PS3_CHANNEL(&event_detail[i].devicePos), + PS3_TARGET(&event_detail[i].devicePos), + PS3_VDID(&event_detail[i].devicePos)); + + switch (event_detail[i].eventType) { + case PS3_EVT_PD_COUNT: + ret = ps3_dev_evt_update_pd_count( + instance, event_detail[i].eventCode, + &event_detail[i].devicePos); + break; + case PS3_EVT_VD_COUNT: + ret = ps3_dev_evt_update_vd_count( + instance, event_detail[i].eventCode, + &event_detail[i].devicePos); + break; + case PS3_EVT_PD_ATTR: + ret = ps3_dev_evt_update_pd_attr( + instance, event_detail[i].eventCode, + &event_detail[i].devicePos); + break; + default: + break; + } + + if (ret != PS3_SUCCESS) + ret_map |= event_detail[i].eventType; + } + + return ret_map; +} + +int ps3_dev_update_full_proc(struct ps3_instance *instance, + enum MgrEvtType event_type) +{ + int ret = -PS3_FAILED; + + switch (event_type) { + case PS3_EVT_PD_COUNT: + ret = ps3_add_remove_multi_pd(instance); + break; + case PS3_EVT_VD_COUNT: + ret = ps3_add_remove_multi_vd(instance); + break; + case PS3_EVT_PD_ATTR: + ret = ps3_update_multi_pd_info(instance); + break; + default: + break; + } + + return ret; +} + +static int ps3_dev_vd_pending_resend(struct ps3_cmd *cmd) +{ + int ret = PS3_SUCCESS; + struct ps3_dev_context *p_dev_ctx = &cmd->instance->dev_context; + + memset(p_dev_ctx->vd_info_buf_async, 0, sizeof(struct PS3VDInfo)); + + ps3_vd_pending_filter_table_build( + (unsigned char *)p_dev_ctx->vd_info_buf_async); + + PS3_MGR_CMD_STAT_INC(cmd->instance, cmd); + + ret = ps3_async_cmd_send(cmd->instance, cmd); + if (ret != PS3_SUCCESS) { + LOG_FILE_ERROR( + "trace_id[0x%llx], hno:%u re send vd pending cmd failed\n", + cmd->trace_id, PS3_HOST(cmd->instance)); + } + + return ret; +} + +static unsigned char ps3_dev_vd_pending_data_switch(struct ps3_cmd *cmd) +{ + int i = 0; + unsigned char ret = PS3_FALSE; + struct ps3_dev_context *p_dev_ctx = &cmd->instance->dev_context; + unsigned char new_vd_table_idx = (p_dev_ctx->vd_table_idx + 1) & 1; + struct ps3_vd_table *p_new_vd_tb = + &p_dev_ctx->vd_table[new_vd_table_idx]; + struct PS3VDEntry *p_new_vd_array = + p_dev_ctx->vd_entries_array[new_vd_table_idx]; + unsigned char old_vd_table_idx = p_dev_ctx->vd_table_idx & 1; + struct ps3_vd_table *p_old_vd_tb = + &p_dev_ctx->vd_table[old_vd_table_idx]; + struct PS3VDEntry *p_old_vd_array = + p_dev_ctx->vd_entries_array[old_vd_table_idx]; + struct PS3VDEntry *p_vd_entry = p_dev_ctx->vd_info_buf_async->vds; + struct PS3VDEntry *vd_entry_old = NULL; + struct PS3Dev *p_dev = NULL; + unsigned short virtDiskIdx = PS3_INVALID_DEV_ID; + struct scsi_device *sdev = NULL; + + memcpy(p_new_vd_tb->vd_idxs_array, p_old_vd_tb->vd_idxs_array, + p_dev_ctx->total_vd_count * sizeof(unsigned short)); + memcpy(p_new_vd_array, p_old_vd_array, + (PS3_MAX_VD_COUNT(cmd->instance) + 1) * + sizeof(struct PS3VDEntry)); + + for (i = 0; i < p_dev_ctx->vd_info_buf_async->count; i++) { + if (PS3_VDID_INVALID(&p_vd_entry[i].diskPos)) { + LOG_WARN_IN_IRQ( + cmd->instance, + "hno:%u, init %d of %d vd info NOK, vdid is 0\n", + PS3_HOST(cmd->instance), i, + p_dev_ctx->vd_info_buf_sync->count); + continue; + } + p_dev = PS3_DEV(&p_vd_entry[i].diskPos); + if (!ps3_dev_id_valid_check(cmd->instance, + (unsigned char)p_dev->softChan, + p_dev->devID, PS3_DISK_TYPE_VD)) { + LOG_ERROR_IN_IRQ( + cmd->instance, + "tid[0x%llx], hno:%u vd pending %d err chan:%d, or devid>max, %d>%d\n", + cmd->trace_id, PS3_HOST(cmd->instance), i, + p_dev->softChan, p_dev->devID, + p_dev_ctx->max_dev_in_channel[p_dev->softChan]); + PS3_BUG_NO_SYNC(); + continue; + } + virtDiskIdx = get_offset_of_vdid(PS3_VDID_OFFSET(cmd->instance), + p_dev->virtDiskID); + if (unlikely(virtDiskIdx > PS3_MAX_VD_COUNT(cmd->instance))) { + LOG_WARN_IN_IRQ( + cmd->instance, + "hno:%u, init %d of %d vd info NOK, vir_id[%d] > max[%d]\n", + PS3_HOST(cmd->instance), i, + p_dev_ctx->vd_info_buf_sync->count, virtDiskIdx, + PS3_MAX_VD_COUNT(cmd->instance)); + continue; + } + LOG_INFO_IN_IRQ(cmd->instance, "hno:%u update vd[%u:%u] info\n", + PS3_HOST(cmd->instance), p_dev->softChan, + p_dev->devID); + + ps3_qos_vd_init(cmd->instance, &p_vd_entry[i]); + vd_entry_old = ps3_dev_mgr_lookup_vd_info_by_id( + cmd->instance, p_dev->virtDiskID); + ps3_qos_vd_attr_change(cmd->instance, vd_entry_old, + &p_vd_entry[i]); + p_new_vd_tb->vd_idxs[p_dev->softChan][p_dev->devID] = + p_dev->virtDiskID; + memcpy(&p_new_vd_array[virtDiskIdx], &p_vd_entry[i], + sizeof(struct PS3VDEntry)); + + sdev = ps3_scsi_device_lookup(cmd->instance, p_dev->softChan, + p_dev->devID, 0); + if (sdev != NULL) { + ps3_vd_busy_scale_get(&p_new_vd_array[virtDiskIdx]); + if (ps3_sdev_bdi_stable_writes_get(sdev)) { + if (!(p_vd_entry[i].bdev_bdi_cap & + PS3_STABLE_WRITES_MASK)) { + ps3_sdev_bdi_stable_writes_clear( + cmd->instance, sdev); + } + } else { + if (p_vd_entry[i].bdev_bdi_cap & + PS3_STABLE_WRITES_MASK) { + ps3_sdev_bdi_stable_writes_set( + cmd->instance, sdev); + } + } + ps3_scsi_device_put(cmd->instance, sdev); + } + + ps3_vd_info_show(cmd->instance, &p_new_vd_array[virtDiskIdx]); +#ifndef _WINDOWS + if (p_vd_entry[i].maxIOSize != 0) { + ps3_change_sdev_max_sector(cmd->instance, + &p_vd_entry[i]); + } else { + LOG_DEBUG( + "hno:%u vd[%u:%u] update max sector num is:0\n", + PS3_HOST(cmd->instance), p_dev->softChan, + p_dev->devID); + } +#endif + } + mb(); /* in order to force CPU ordering */ + if (p_dev_ctx->vd_table_idx == (PS3_VD_TABLE_NUM - 1)) + p_dev_ctx->vd_table_idx = 0; + else + p_dev_ctx->vd_table_idx++; + mb(); /* in order to force CPU ordering */ + + LOG_INFO_IN_IRQ( + cmd->instance, + "trace_id[0x%llx], hno:%u vd pending change cur_idx to [%d]\n", + cmd->trace_id, PS3_HOST(cmd->instance), + p_dev_ctx->vd_table_idx); + + return ret; +} + +int ps3_dev_vd_pending_proc(struct ps3_cmd *cmd, unsigned short reply_flags) +{ + unsigned long flags = 0; + unsigned long flags1 = 0; + int ret = PS3_SUCCESS; + struct ps3_dev_context *p_dev_ctx = NULL; + struct ps3_instance *instance = cmd->instance; + int cur_state = PS3_INSTANCE_STATE_INIT; + + p_dev_ctx = &instance->dev_context; + ps3_atomic_inc(&p_dev_ctx->subwork); + LOG_DEBUG( + "trace_id[0x%llx], hno:%u got a vd pending response, cur_idx[%d]\n", + cmd->trace_id, PS3_HOST(instance), p_dev_ctx->vd_table_idx); + + PS3_MGR_CMD_BACK_INC(instance, cmd, reply_flags); + if (unlikely(reply_flags == PS3_REPLY_WORD_FLAG_FAIL)) { + LOG_ERROR_IN_IRQ( + instance, + "trace_id[0x%llx], hno:%u vd pending cmd return failed\n", + cmd->trace_id, PS3_HOST(instance)); + ret = -PS3_FAILED; + goto l_failed; + } + + if (p_dev_ctx->vd_pending_cmd == NULL) { + LOG_INFO_IN_IRQ( + instance, + "trace_id[0x%llx], hno:%u vd pending is unsubscribed!\n", + cmd->trace_id, PS3_HOST(instance)); + ret = -PS3_FAILED; + goto l_out; + } + + if (!instance->state_machine.is_load) { + LOG_INFO_IN_IRQ( + instance, + "trace_id[0x%llx], hno:%u instance is suspend or instance unload!\n", + cmd->trace_id, PS3_HOST(instance)); + ret = -PS3_FAILED; + goto l_failed; + } + + cur_state = ps3_atomic_read(&instance->state_machine.state); + if (cur_state != PS3_INSTANCE_STATE_OPERATIONAL) { + LOG_INFO_IN_IRQ( + instance, + "trace_id[0x%llx], hno:%u instance is not operational!\n", + cmd->trace_id, PS3_HOST(instance)); + ret = -PS3_FAILED; + goto l_failed; + } + + LOG_INFO_IN_IRQ(instance, + "trace_id[0x%llx], hno:%u vd pending had [%d] vds\n", + cmd->trace_id, PS3_HOST(instance), + p_dev_ctx->vd_info_buf_async->count); + + ps3_dev_vd_pending_data_switch(cmd); + + ps3_spin_lock_irqsave(&cmd->cmd_state.lock, &flags); + cmd->cmd_state.state = PS3_CMD_STATE_PROCESS; + ps3_spin_unlock_irqrestore(&cmd->cmd_state.lock, flags); + + ps3_spin_lock_irqsave(&instance->recovery_context->recovery_lock, + &flags1); + if (ps3_atomic_read(&instance->dev_context.abort_vdpending_cmd) == 0) { + ret = ps3_dev_vd_pending_resend(cmd); + ps3_spin_unlock_irqrestore( + &instance->recovery_context->recovery_lock, flags1); + if (ret == PS3_SUCCESS) + goto l_out; + } else { + LOG_FILE_INFO("hno:%u vd pending cmd free, CFID:%d\n", + PS3_HOST(instance), cmd->index); + instance->dev_context.vd_pending_cmd = NULL; + ps3_mgr_cmd_free(instance, cmd); + ret = ps3_dev_mgr_vd_info_subscribe(instance); + if (ret != PS3_SUCCESS) { + LOG_INFO_IN_IRQ(instance, "hno:%u subscribe failed!\n", + PS3_HOST(instance)); + } + + ps3_atomic_set(&instance->dev_context.abort_vdpending_cmd, 0); + ps3_spin_unlock_irqrestore( + &instance->recovery_context->recovery_lock, flags1); + goto l_out; + } +l_failed: + ps3_spin_lock_irqsave(&cmd->cmd_state.lock, &flags); + cmd->cmd_state.state = PS3_CMD_STATE_DEAD; + ps3_spin_unlock_irqrestore(&cmd->cmd_state.lock, flags); + LOG_INFO_IN_IRQ(instance, + "trace_id[0x%llx], CFID[%d], hno:%u to dead\n", + cmd->trace_id, cmd->index, PS3_HOST(instance)); + +l_out: + ps3_atomic_dec(&p_dev_ctx->subwork); + LOG_INFO_IN_IRQ( + instance, + "trace_id[0x%llx], CFID[%d], hno:%u end proc a vd pending response, subwork:%d,ret:%d\n", + cmd->trace_id, cmd->index, PS3_HOST(instance), + ps3_atomic_read(&p_dev_ctx->subwork), ret); + return ret; +} + +#ifdef _WINDOWS +int ps3_device_check_and_ack(struct ps3_instance *instance, + unsigned char channel_type, unsigned char channel, + unsigned short target_id) +{ + int ret_tmp = PS3_SUCCESS; + int ret = FALSE; + struct ps3_pd_entry *p_pd_entry = NULL; + struct PS3VDEntry *p_vd_entry = NULL; + struct PS3DiskDevPos *p_diskPos = NULL; + unsigned char dev_type = PS3_DISK_TYPE_UNKNOWN; + + if (!ps3_scsi_visible_check(instance, channel, target_id, + channel_type)) { + goto l_out; + } + + if (channel_type == PS3_DISK_TYPE_PD) { + p_pd_entry = ps3_dev_mgr_lookup_pd_info(instance, channel, + target_id); + if (p_pd_entry) { + p_diskPos = &p_pd_entry->disk_pos; + dev_type = PS3_DISK_TYPE_PD; + } + } else if (channel_type == PS3_DISK_TYPE_VD) { + p_vd_entry = ps3_dev_mgr_lookup_vd_info(instance, channel, + target_id); + if (p_vd_entry) { + p_diskPos = &p_vd_entry->diskPos; + dev_type = PS3_DISK_TYPE_VD; + } + } + + if (p_diskPos == NULL) + goto l_out; + + ret_tmp = ps3_scsi_add_device_ack(instance, p_diskPos, dev_type); + if (ret_tmp != PS3_SUCCESS) { + LOG_ERROR( + "hno:%u dev[%u:%u] magic[%#x] pre scan ack NOK, ret %d\n", + PS3_HOST(instance), channel, target_id, + p_diskPos->diskMagicNum, ret_tmp); + } else { + LOG_INFO("hno:%u dev_type[%d] id[%u:%u] send ack success\n", + PS3_HOST(instance), dev_type, channel, target_id); + + ret = TRUE; + } +l_out: + return ret; +} +#endif diff --git a/drivers/scsi/linkdata/ps3stor/ps3_device_update.h b/drivers/scsi/linkdata/ps3stor/ps3_device_update.h new file mode 100644 index 0000000000000000000000000000000000000000..13f1e8a9943a5430c80573577833f3c2edae8270 --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/ps3_device_update.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _PS3_DEVICE_UPDATE_H_ +#define _PS3_DEVICE_UPDATE_H_ + +#ifndef _WINDOWS +#include +#endif + +#include "ps3_htp_event.h" +#include "ps3_instance_manager.h" + +unsigned char ps3_pd_scsi_visible_check(struct ps3_instance *instance, + struct PS3DiskDevPos *disk_pos, + unsigned char dev_type, + unsigned char config_flag, + unsigned char pd_state); + +int ps3_dev_update_detail_proc(struct ps3_instance *instance, + struct PS3EventDetail *event_detail, + unsigned int event_cnt); + +int ps3_dev_update_full_proc(struct ps3_instance *instance, + enum MgrEvtType event_type); + +int ps3_dev_vd_pending_proc(struct ps3_cmd *cmd, unsigned short reply_flags); + +#ifdef _WINDOWS + +int ps3_device_check_and_ack(struct ps3_instance *instance, + unsigned char channel_type, unsigned char channel, + unsigned short target_id); +#endif +unsigned int ps3_scsi_dev_magic(struct ps3_instance *instance, + struct scsi_device *sdev); + +void ps3_scsi_scan_host(struct ps3_instance *instance); + +void ps3_check_vd_member_change(struct ps3_instance *instance, + struct ps3_pd_entry *local_entry); + +#endif diff --git a/drivers/scsi/linkdata/ps3stor/ps3_drv_ver.h b/drivers/scsi/linkdata/ps3stor/ps3_drv_ver.h new file mode 100644 index 0000000000000000000000000000000000000000..88deccacb60fbaf76d2189208dec9c3fcfaf6eba --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/ps3_drv_ver.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _PS3_DRV_VER_H_ +#define _PS3_DRV_VER_H_ + +#define PS3_PRIVATE_VERSION "00.000.00.01" +#define PS3_DRV_VERSION "2.5.0.34" +#define PS3_DRV_COMMIT_ID "98da299 ce2f094" +#define PS3_DRV_BUILD_TIME "Mar 04 2025 11:13:45" +#define PS3_DRV_TOOLCHAIN_ID "" + +#define PS3_DRV_PRODUCT_SUPPORT "RAID/HBA" +#define PS3_DRV_LICENSE "GPL" +#define PS3_DRV_AUTHOR "ps3stor" +#define PS3_DRV_DESCRIPTION "ps3stor driver" + +#endif diff --git a/drivers/scsi/linkdata/ps3stor/ps3_dump.h b/drivers/scsi/linkdata/ps3stor/ps3_dump.h new file mode 100644 index 0000000000000000000000000000000000000000..321bfb13a2192a99c218bd5f6640bde0150345cf --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/ps3_dump.h @@ -0,0 +1,183 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _PS3_CRASH_DUMP_H_ +#define _PS3_CRASH_DUMP_H_ + +#ifndef _WINDOWS +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#else +#include "ps3_worker.h" +#endif +#include "ps3_platform_utils.h" +#include "ps3_htp.h" +#include "ps3_htp_def.h" + +#define PS3_DUMP_DATA_MAX_NUM 512 + +#define WAIT_DUMP_COLLECT (1000) +#define WAIT_DUMP_TIMES_DEFAULT (300) +#define WAIT_DUMP_TIMES_MIN (10) +#define WAIT_DUMP_TIMES_MAX (3600) + +#define WAIT_DUMP_DMA_DONE (200) +#ifndef _WINDOWS +#ifdef __KERNEL__ +#define PS3_DUMP_FILE_DIR "/var/log" +#else +#define PS3_DUMP_FILE_DIR "/tmp" +#endif +#else +#define PS3_DUMP_FILE_DIR "c:" +#endif +#define PS3_DUMP_FILE_CORE_PREFIX "core-" +#define PS3_DUMP_FILE_FWLOG_PREFIX "fwlog-" +#define PS3_DUMP_FILE_BAR_PREFIX "bar-" + +#define PS3_DUMP_STATUS_REG_DMA_FINISH_MASK (0x1 << 0) +#define PS3_DUMP_STATUS_REG_CRASH_DUMP_MASK (0x1 << 1) +#define PS3_DUMP_STATUS_REG_FW_DUMP_MASK (0x1 << 2) +#define PS3_DUMP_STATUS_REG_BAR_DUMP_MASK (0x1 << 3) +#define PS3_DUMP_STATUS_REG_CRASH_DUMP_TRIGGER_MASK (0x1 << 4) +#define PS3_DUMP_STATUS_REG_FW_DUMP_TRIGGER_MASK (0x2 << 4) +#define PS3_DUMP_STATUS_REG_BAR_DUMP_TRIGGER_MASK (0x3 << 4) +#define PS3_DUMP_STATUS_REG_ABORT_MASK (0x1 << 6) +#define PS3_DUMP_STATUS_REG_MASK (0x0F) +#define PS3_DUMP_STATUS_REG_INVALID_BITS_MASK (0x7F) + +#define PS3_REG_TEST(val, mask) (((val) & (mask)) ? PS3_TRUE : PS3_FALSE) +#define PS3_REG_SET(val, mask) (((val) |= (mask))) +#define PS3_REG_CLR(val, mask) ((val) &= (~(mask))) + +#define PS3_IOC_DUMP_SUPPORT(ins) (ins->dump_context.is_dump_support) + +enum ps3_dump_work_status { + PS3_DUMP_WORK_UNKNOWN, + PS3_DUMP_WORK_RUNNING, + PS3_DUMP_WORK_STOP, + PS3_DUMP_WORK_DONE, + PS3_DUMP_WORK_CANCEL, +}; + +enum ps3_dump_irq_handler_work_status { + PS3_DUMP_IRQ_HANDLER_WORK_UNKNOWN, + PS3_DUMP_IRQ_HANDLER_WORK_RUNNING, + PS3_DUMP_IRQ_HANDLER_WORK_STOP, + PS3_DUMP_IRQ_HANDLER_WORK_DONE, + PS3_DUMP_IRQ_HANDLER_WORK_CANCEL, +}; + +enum ps3_dump_env { + PS3_DUMP_ENV_UNKNOWN, + PS3_DUMP_ENV_CLI, + PS3_DUMP_ENV_NOTIFY, + PS3_DUMP_ENV_COUNT, +}; + +enum ps3_dump_file_status { + PS3_DUMP_FILE_UNKNOWN, + PS3_DUMP_FILE_OPEN, + PS3_DUMP_FILE_CLOSE, + PS3_DUMP_FILE_MAX, +}; + +#define PS3_DUMP_FILE_DIR_LEN (128) +#define PS3_DUMP_FILE_NAME_LEN (256) + +struct ps3_dump_file_info { + unsigned char filename[PS3_DUMP_FILE_NAME_LEN]; +#ifdef _WINDOWS + HANDLE fp; +#else + struct file *fp; +#endif + unsigned long long file_size; + unsigned int file_w_cnt; + unsigned int file_status; + unsigned int type; + unsigned char reserved[4]; +}; + +struct ps3_dump_context { + unsigned char dump_dir[PS3_DUMP_FILE_DIR_LEN]; + unsigned long long dump_data_size; + char dump_type; + int dump_state; + unsigned int dump_env; + unsigned int dump_work_status; + unsigned int dump_irq_handler_work_status; + unsigned long long copyed_data_size; + unsigned char *dump_dma_buf; + dma_addr_t dump_dma_addr; + unsigned int dump_dma_wait_times; +#ifdef _WINDOWS + struct ps3_delay_worker dump_work; + struct mutex dump_mutex; +#else + struct workqueue_struct *dump_work_queue; + struct delayed_work dump_work; + + struct workqueue_struct *dump_irq_handler_work_queue; + struct work_struct dump_irq_handler_work; + spinlock_t dump_irq_handler_lock; + unsigned int dump_enabled; + +#endif + struct mutex dump_lock; + struct ps3_instance *instance; + struct ps3_dump_file_info dump_out_file; + struct ps3_cmd *dump_pending_cmd; + + unsigned int dump_pending_send_times; + unsigned int dump_type_times; + unsigned int dump_state_times; + unsigned char is_dump_support; + unsigned char is_hard_recovered; + + unsigned char reserved[2]; +}; +static inline const char *ps3_dump_type_to_name(int dump_type) +{ + static const char * const name[] = { [PS3_DUMP_TYPE_UNKNOWN] = "NULL", + [PS3_DUMP_TYPE_CRASH] = "dump_crash", + [PS3_DUMP_TYPE_FW_LOG] = "fw_log", + [PS3_DUMP_TYPE_BAR_DATA] = "bar_data" }; + return name[dump_type]; +} + +extern int ps3_dump_init(struct ps3_instance *instance); +extern void ps3_dump_exit(struct ps3_instance *instance); +extern int ps3_dump_type_set(struct ps3_dump_context *ctxt, int type, + unsigned int env); +extern int ps3_dump_state_set(struct ps3_dump_context *ctxt, int state); + +#ifndef _WINDOWS +extern struct ps3_dump_context *dev_to_dump_context(struct device *cdev); + +void ps3_dump_work_stop(struct ps3_instance *instance); + +#endif + +int ps3_dump_dma_buf_alloc(struct ps3_instance *instance); + +void ps3_dump_dma_buf_free(struct ps3_instance *instance); + +void ps3_dump_detect(struct ps3_instance *instance); + +#ifndef _WINDOWS +irqreturn_t ps3_dump_irq_handler(int virq, void *dev_id); + +#endif +void ps3_dump_ctrl_set_int_ready(struct ps3_instance *instance); + +unsigned char ps3_dump_is_trigger_log(struct ps3_instance *instance); + +#endif diff --git a/drivers/scsi/linkdata/ps3stor/ps3_err_def.h b/drivers/scsi/linkdata/ps3stor/ps3_err_def.h new file mode 100644 index 0000000000000000000000000000000000000000..eaa1901928e7a99799b291bc72167173ab3ac1be --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/ps3_err_def.h @@ -0,0 +1,47 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef _PS3_ERR_DEF_H_ +#define _PS3_ERR_DEF_H_ + +#include "ps3_htp_def.h" + + +enum { + PS3_SUCCESS = 0x00, + PS3_FAILED = 0x01, + PS3_TIMEOUT = 0x02, + PS3_IN_IRQ_POLLING = 0x03, + PS3_DEV_UNKNOWN = 0x04, + PS3_RETRY = 0x05, + PS3_EBUSY = 0x06, + PS3_EINVAL = 0x07, + PS3_ENOMEM = 0x08, + PS3_ENODEV = 0x09, + PS3_ERESTARTSYS = 0x0a, + PS3_ENOTTY = 0x0b, + PS3_RESP_ERR = 0x0c, + PS3_RESP_INT = 0x0d, + PS3_ACTIVE_ERR = 0x0e, + PS3_CMD_NO_RESP = 0x0f, + PS3_IO_BLOCK = 0x10, + PS3_IO_REQUEUE = 0x11, + PS3_IO_CONFLICT_IN_Q = 0x12, + PS3_IO_CONFLICT = 0x13, + PS3_MGR_REC_FORCE = 0x14, + PS3_RECOVERED = 0x15, + PS3_IN_UNLOAD = 0x16, + PS3_NO_RECOVERED = 0x17, + PS3_IN_QOS_Q = 0x18, + PS3_IN_PCIE_ERR = 0x19, +}; + +#define PS3_DRV_TRUE (1) +#define PS3_DRV_FALSE (0) + +struct ps3_fault_context { + unsigned char ioc_busy; + unsigned char reserved[2]; + unsigned long last_time; +}; + +#endif diff --git a/drivers/scsi/linkdata/ps3stor/ps3_event.c b/drivers/scsi/linkdata/ps3stor/ps3_event.c new file mode 100644 index 0000000000000000000000000000000000000000..5f120f729bf193ae2fa18c433454cacdff424b43 --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/ps3_event.c @@ -0,0 +1,1349 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "ps3_event.h" +#ifndef _WINDOWS +#include +#include +#include +#include +#include +#include +#include +#else + +#endif +#include "ps3_platform_utils.h" +#include "ps3_instance_manager.h" +#include "ps3_inner_data.h" +#include "ps3_htp_event.h" +#include "ps3_mgr_cmd.h" +#include "ps3_htp.h" +#include "ps3_device_update.h" +#include "ps3_util.h" +#include "ps3_cmd_statistics.h" +unsigned short ps3_event_code_pd_count[] = { + PS3_EVT_CODE(MGR_EVT_DEVM_DISK_IN), + PS3_EVT_CODE(MGR_EVT_DEVM_DISK_OUT), + PS3_EVT_CODE(MGR_EVT_MULITPILE_PD_IN), + PS3_EVT_CODE(MGR_EVT_MULITPILE_PD_OUT), + PS3_EVT_CODE(MGR_EVT_DEVM_JBOD), + PS3_EVT_CODE(MGR_EVT_DEVM_READY), + PS3_EVT_CODE(MGR_EVT_MULITPILE_JBOD), + PS3_EVT_CODE(MGR_EVT_MULITPILE_READY), + PS3_EVT_CODE(MGR_EVT_BACKPLANE_ON), + PS3_EVT_CODE(MGR_EVT_BACKPLANE_OFF), + PS3_EVT_CODE(MGR_EVT_DEVM_DISK_CHANGE), + PS3_EVT_CODE(MGR_EVT_MULITPILE_PD_STATE_CHANGE), + PS3_EVT_CODE(MGR_EVT_PD_PRE_READY) +}; + +unsigned short ps3_event_code_pd_attr[] = { + PS3_EVT_CODE(MGR_EVT_PD_INFO_CHANGE), + PS3_EVT_CODE(MGR_EVT_PD_MARKED_READY), + PS3_EVT_CODE(MGR_EVT_PD_MARKED_ONLINE), + PS3_EVT_CODE(MGR_EVT_PD_MARKED_FAILED), + PS3_EVT_CODE(MGR_EVT_PD_MARKED_REBUILD), + PS3_EVT_CODE(MGR_EVT_PD_MARKED_REPLACE), + PS3_EVT_CODE(MGR_EVT_PD_R_GLOBAL_SPARE_ADDED), + PS3_EVT_CODE(MGR_EVT_PD_R_GLOBAL_SPARE_DELETED), + PS3_EVT_CODE(MGR_EVT_PD_R_DEDICATED_SPARE_ADDED), + PS3_EVT_CODE(MGR_EVT_PD_R_DEDICATED_SPARE_DELETED), + PS3_EVT_CODE(MGR_EVT_PD_MARKED_UNCONFIGUREDBAD), + PS3_EVT_CODE(MGR_EVT_PD_MARKED_OFFLINE) +}; + +unsigned short ps3_event_code_vd_attr[] = { + PS3_EVT_CODE(MGR_EVT_VD_SETTINGS_CHANGE), + PS3_EVT_CODE(MGR_EVT_VD_PD_CHANGE), + PS3_EVT_CODE(MGR_EVT_VD_STATE_CHANGE), + PS3_EVT_CODE(MGR_EVT_VD_ALTER_ATTR_BY_MIGRATION), + PS3_EVT_CODE(MGR_EVT_VD_EXPAND_FINISH) +}; + +struct ps3_event_type_desc_map { + enum MgrEvtType event_type; + const char *event_type_desc; +}; + +static struct ps3_event_type_desc_map g_event_type_desc_table[] = { + { PS3_EVT_PD_COUNT, "PS3_EVT_PD_COUNT" }, + { PS3_EVT_VD_COUNT, "PS3_EVT_VD_COUNT" }, + { PS3_EVT_CTRL_INFO, "PS3_EVT_CTRL_INFO" }, + { PS3_EVT_PD_ATTR, "PS3_EVT_PD_ATTR" }, + { PS3_EVT_VD_ATTR, "PS3_EVT_VD_ATTRs" }, + { PS3_EVT_SAS_INFO, "PS3_EVT_SAS_INFO" }, + { PS3_EVT_DG_INFO, "MGR_EVT_DG_DELETED" }, +}; + +static struct fasync_struct *g_async_queue; + +static void ps3_event_print_cmd(struct ps3_cmd *cmd, unsigned char is_send) +{ + LOG_INFO( + "trace_id[0x%llx], hno:%u event[%s] print cmd word type[%d]\n" + "\tdirect[%d], qmask[0x%x], CFID[%d], isr_sn[%d], vid[%d], pid[%d]\n", + cmd->trace_id, PS3_HOST(cmd->instance), + (is_send) ? "send" : "recv", cmd->cmd_word.type, + cmd->cmd_word.direct, cmd->cmd_word.qMask, + cmd->cmd_word.cmdFrameID, cmd->cmd_word.isrSN, + cmd->cmd_word.virtDiskID, cmd->cmd_word.phyDiskID); +} + +void ps3_vd_pending_filter_table_build(unsigned char *data) +{ + struct PS3EventFilter *event_filter = (struct PS3EventFilter *)data; + + memset(event_filter, 0, sizeof(struct PS3EventFilter)); + + event_filter->eventType = PS3_EVT_VD_ATTR_LOCAL; + event_filter->eventCodeCnt = ARRAY_SIZE(ps3_event_code_vd_attr); + + data += sizeof(struct PS3EventFilter); + memcpy(data, ps3_event_code_vd_attr, sizeof(ps3_event_code_vd_attr)); +} + +void ps3_event_filter_table_get_raid(unsigned char *data) +{ + struct PS3EventFilter event_filter; + + memset(&event_filter, 0, sizeof(struct PS3EventFilter)); + + event_filter.eventType = PS3_EVT_PD_COUNT_LOCAL; + event_filter.eventCodeCnt = ARRAY_SIZE(ps3_event_code_pd_count); + memcpy(data, &event_filter, sizeof(struct PS3EventFilter)); + data += sizeof(struct PS3EventFilter); + memcpy(data, ps3_event_code_pd_count, sizeof(ps3_event_code_pd_count)); + data += sizeof(ps3_event_code_pd_count); + + event_filter.eventType = PS3_EVT_VD_COUNT_LOCAL; + event_filter.eventCodeCnt = 0; + memcpy(data, &event_filter, sizeof(struct PS3EventFilter)); + data += sizeof(struct PS3EventFilter); + + event_filter.eventType = PS3_EVT_CTRL_INFO_LOCAL; + event_filter.eventCodeCnt = 0; + memcpy(data, &event_filter, sizeof(struct PS3EventFilter)); + data += sizeof(struct PS3EventFilter); + + event_filter.eventType = PS3_EVT_PD_ATTR_LOCAL; + event_filter.eventCodeCnt = ARRAY_SIZE(ps3_event_code_pd_attr); + memcpy(data, &event_filter, sizeof(struct PS3EventFilter)); + data += sizeof(struct PS3EventFilter); + memcpy(data, ps3_event_code_pd_attr, sizeof(ps3_event_code_pd_attr)); + data += sizeof(ps3_event_code_pd_attr); +} + +void ps3_event_filter_table_get_hba(unsigned char *data) +{ + struct PS3EventFilter event_filter; + + memset(&event_filter, 0, sizeof(struct PS3EventFilter)); + + event_filter.eventType = PS3_EVT_SAS_INFO_LOCAL; + event_filter.eventCodeCnt = 0; + memcpy(data, &event_filter, sizeof(struct PS3EventFilter)); + data += sizeof(struct PS3EventFilter); + + ps3_event_filter_table_get_raid(data); +} + +void ps3_event_filter_table_get_switch(unsigned char *data) +{ + struct PS3EventFilter event_filter; + + memset(&event_filter, 0, sizeof(struct PS3EventFilter)); + + event_filter.eventType = PS3_EVT_PD_COUNT_LOCAL; + event_filter.eventCodeCnt = ARRAY_SIZE(ps3_event_code_pd_count); + memcpy(data, &event_filter, sizeof(struct PS3EventFilter)); + data += sizeof(struct PS3EventFilter); + memcpy(data, ps3_event_code_pd_count, sizeof(ps3_event_code_pd_count)); + data += sizeof(ps3_event_code_pd_count); +} + +static int ps3_event_type_proc(struct ps3_instance *instance, + enum MgrEvtType event_type) +{ + int ret = PS3_SUCCESS; + + switch (event_type) { + case PS3_EVT_PD_COUNT: + case PS3_EVT_VD_COUNT: + case PS3_EVT_PD_ATTR: + case PS3_EVT_VD_ATTR: + ret = ps3_dev_update_full_proc(instance, event_type); + break; + case PS3_EVT_CTRL_INFO: + ret = ps3_ctrl_info_get(instance); + break; + default: + LOG_INFO("hno:%u Not cared event type %s\n", PS3_HOST(instance), + ps3_event_print(event_type)); + break; + } + + return ret; +} + +#ifndef _WINDOWS +int ps3_event_delay_set(struct ps3_instance *instance, unsigned int delay) +{ + struct ps3_event_context *event_ctx = NULL; + struct ps3_event_delay_work *delay_work = NULL; + + if (instance) { + event_ctx = &instance->event_context; + if (event_ctx->delay_work) { + delay_work = event_ctx->delay_work; + delay_work->event_delay = delay; + return PS3_SUCCESS; + } + } + + return -PS3_FAILED; +} +#endif + +static void ps3_event_resubscribe(struct ps3_instance *instance, + struct ps3_cmd *cmd, + unsigned int event_proc_result_bitmap) +{ + unsigned long flags = 0; + struct PS3MgrReqFrame *mgr_req_frame = NULL; + struct PS3MgrEvent *event_req_info = NULL; + + + if (!instance->state_machine.is_load) { + LOG_WARN_IN_IRQ( + instance, + "hno:%u instance is suspend or instance unload!\n", + PS3_HOST(instance)); + return; + } + + mgr_req_frame = (struct PS3MgrReqFrame *)cmd->req_frame; + event_req_info = (struct PS3MgrEvent *)&mgr_req_frame->value.event; + + LOG_INFO_IN_IRQ( + instance, + "trace_id[0x%llx], hno:%u old event proc result bitmap: [0x%x]\n" + "\tcurrent event proc result bitmap: [0x%x]\n", + cmd->trace_id, PS3_HOST(instance), + event_req_info->eventTypeMapProcResult, + event_proc_result_bitmap); + + event_req_info->eventTypeMapProcResult = event_proc_result_bitmap; + + LOG_INFO_IN_IRQ( + instance, + "trace_id[0x%llx], hno:%u subcribe event proc result bitmap: [0x%x]\n", + cmd->trace_id, PS3_HOST(instance), + event_req_info->eventTypeMapProcResult); + + if (instance->ioc_adpter->event_filter_table_get != NULL) { + instance->ioc_adpter->event_filter_table_get( + (unsigned char *)cmd->ext_buf); + } + wmb(); /* in order to force CPU ordering */ + PS3_MGR_CMD_STAT_INC(instance, cmd); + + ps3_spin_lock_irqsave(&cmd->cmd_state.lock, &flags); + cmd->cmd_state.state = PS3_CMD_STATE_PROCESS; + ps3_spin_unlock_irqrestore(&cmd->cmd_state.lock, flags); + + if (ps3_async_cmd_send(cmd->instance, cmd) != PS3_SUCCESS) { + LOG_INFO_IN_IRQ( + instance, + "trace_id[0x%llx],CFID[%d],hno:%u re send event cmd failed\n", + cmd->trace_id, cmd->index, PS3_HOST(cmd->instance)); + + ps3_spin_lock_irqsave(&cmd->cmd_state.lock, &flags); + cmd->cmd_state.state = PS3_CMD_STATE_DEAD; + ps3_spin_unlock_irqrestore(&cmd->cmd_state.lock, flags); + } +} + +static unsigned int +ps3_event_proc_without_details(struct ps3_instance *instance, + struct PS3EventInfo *event_info) +{ + int ret = -PS3_FAILED; + unsigned int event_proc_result_bitmap = 0; + unsigned int mask_bit = 0X00000001; + unsigned int event_type = event_info->eventTypeMap & mask_bit; + +#ifndef _WINDOWS + if (event_info->eventTypeMap & PS3_EVT_SAS_INFO) { + ret = ps3_sas_expander_event_refresh(instance); + if (ret != PS3_SUCCESS) { + LOG_ERROR( + "hno:%u Event type PS3_EVT_SAS_INFO proc NOK!\n", + PS3_HOST(instance)); + event_proc_result_bitmap |= PS3_EVT_SAS_INFO; + } + + event_info->eventTypeMap ^= PS3_EVT_SAS_INFO; + } +#endif + while (mask_bit != 0) { + if (event_type != 0) { + LOG_INFO( + "hno:%u Event type %s report without details!\n", + PS3_HOST(instance), + ps3_event_print((enum MgrEvtType)event_type)); + + ret = ps3_event_type_proc(instance, + (enum MgrEvtType)event_type); + + } else { + ret = PS3_SUCCESS; + } + + if (ret != PS3_SUCCESS) { + LOG_ERROR("hno:%u Event type %s proc NOK!\n", + PS3_HOST(instance), + ps3_event_print((enum MgrEvtType)event_type)); + event_proc_result_bitmap |= event_type; + } + + mask_bit = mask_bit << 1; + event_type = event_info->eventTypeMap & mask_bit; + } + + return event_proc_result_bitmap; +} + +static void ps3_event_details_pre_proc(struct ps3_instance *instance, + struct PS3EventInfo *event_info, + struct ps3_cmd *cmd) +{ + unsigned int idx = 0; + struct PS3MgrReqFrame *mgr_req_frame = NULL; + struct PS3MgrEvent *event_req_info = NULL; + struct PS3EventDetail *event_detail = NULL; + + mgr_req_frame = (struct PS3MgrReqFrame *)cmd->req_frame; + event_req_info = (struct PS3MgrEvent *)&mgr_req_frame->value.event; + + for (idx = 0; idx < event_info->eventCount; idx++) { + event_detail = &event_info->eventDetail[idx]; + if (event_detail->eventType & + event_req_info->eventTypeMapProcResult) { + LOG_INFO( + "trace_id[0x%llx], hno:%u Event type %s code %s with\n" + "\ttimestamp[%d] dev id[0x%x] magic %#x do not process in details!\n", + cmd->trace_id, PS3_HOST(instance), + ps3_event_print(event_detail->eventType), + mgrEvtCodeTrans(event_detail->eventCode), + event_detail->timestamp, + event_detail->devicePos.diskDev.diskID, + event_detail->devicePos.diskMagicNum); + + event_detail->eventType = PS3_EVT_ILLEGAL_TYPE; + continue; + } + + if (event_detail->eventType == PS3_EVT_CTRL_INFO) { + event_detail->eventType = PS3_EVT_ILLEGAL_TYPE; + continue; + } + + event_info->eventTypeMap &= (~event_detail->eventType); + LOG_INFO( + "trace_id[0x%llx], hno:%u Event type %s code %s reports\n" + "\tin details with timestamp %d dev id 0x%x magic %#x!\n", + cmd->trace_id, PS3_HOST(instance), + ps3_event_print(event_detail->eventType), + mgrEvtCodeTrans(event_detail->eventCode), + event_detail->timestamp, + event_detail->devicePos.diskDev.diskID, + event_detail->devicePos.diskMagicNum); + } + + event_info->eventTypeMap |= event_req_info->eventTypeMapProcResult; +} + +static unsigned int ps3_event_proc_with_details(struct ps3_instance *instance, + struct PS3EventInfo *event_info, + struct ps3_cmd *cmd) +{ + unsigned int event_proc_result_bitmap = 0; + + ps3_event_details_pre_proc(instance, event_info, cmd); +#ifndef _WINDOWS + event_proc_result_bitmap |= ps3_sas_update_detail_proc( + instance, event_info->eventDetail, event_info->eventCount); +#endif + event_proc_result_bitmap = ps3_dev_update_detail_proc( + instance, event_info->eventDetail, event_info->eventCount); + + return event_proc_result_bitmap; +} + +static void ps3_event_delay(struct ps3_instance *instance, + struct ps3_event_delay_work *delay_work) +{ + struct ps3_cmd *cmd = delay_work->cmd; + unsigned int event_delay = 0; + + event_delay = delay_work->event_delay; + LOG_WARN( + "trace_id[0x%llx], hno:%u Event handle will delay %d seconds\n", + cmd->trace_id, PS3_HOST(instance), event_delay); + + while (event_delay > 0) { + if (kthread_should_stop() || delay_work->event_delay == 0) { + LOG_WARN( + "trace_id[0x%llx], hno:%u Event handle delay cancel\n", + cmd->trace_id, PS3_HOST(instance)); + return; + } + + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ); + event_delay--; + } + LOG_WARN( + "trace_id[0x%llx], hno:%u Event handle delay %d seconds timeout\n", + cmd->trace_id, PS3_HOST(instance), delay_work->event_delay); +} + +void ps3_event_handle(struct ps3_event_delay_work *ps3_delay_work) +{ + struct ps3_cmd *cmd = ps3_delay_work->cmd; + struct ps3_instance *instance = cmd->instance; + struct PS3EventInfo *event_info = instance->event_context.event_info; + unsigned int event_proc_result_bitmap = PS3_EVT_ILLEGAL_TYPE; + struct ps3_event_delay_work *ps3_new_delay_work = NULL; + unsigned long flags = 0; + int ret; + + if (instance->event_context.event_cmd == NULL) { + LOG_WARN("trace_id[0x%llx], hno:%u Event is unsubscribed\n", + cmd->trace_id, PS3_HOST(instance)); + return; + } + + if (!instance->state_machine.is_load) { + LOG_WARN( + "trace_id[0x%llx], hno:%u instance is suspend or instance unload!\n", + cmd->trace_id, PS3_HOST(instance)); + return; + } + + LOG_INFO("trace_id[0x%llx], hno:%u event count[%d], bitmap[%08x]\n", + cmd->trace_id, PS3_HOST(instance), event_info->eventCount, + event_info->eventTypeMap); + + +#ifndef _WINDOWS + if (ps3_delay_work->event_delay) + ps3_event_delay(instance, ps3_delay_work); +#endif + if (event_info->eventCount <= PS3_EVENT_DETAIL_BUF_MAX) { + LOG_INFO( + "trace_id[0x%llx], hno:%u Event has valid details info!\n", + cmd->trace_id, PS3_HOST(instance)); + event_proc_result_bitmap |= + ps3_event_proc_with_details(instance, event_info, cmd); + } + + if (event_info->eventTypeMap) { + event_proc_result_bitmap |= + ps3_event_proc_without_details(instance, event_info); + } + + memset(event_info, 0, sizeof(*event_info)); + ps3_event_print_cmd(cmd, PS3_TRUE); + ps3_spin_lock_irqsave(&instance->recovery_context->recovery_lock, + &flags); + if (ps3_atomic_read(&instance->event_context.abort_eventcmd) == 0 && + ps3_atomic_read(&instance->hardreset_event) == 0) { + ps3_new_delay_work = + (ps3_delay_work == + &instance->event_context + .delay_work_pool[PS3_EVENT_WORKER_POOL_SIZE - + 1]) ? + (&instance->event_context.delay_work_pool[0]) : + (ps3_delay_work + 1); + instance->event_context.delay_work = ps3_new_delay_work; + ps3_event_resubscribe(instance, cmd, event_proc_result_bitmap); + + if (ps3_atomic_read(&instance->event_context.subwork) != 0) + ps3_atomic_dec(&instance->event_context.subwork); + ps3_spin_unlock_irqrestore( + &instance->recovery_context->recovery_lock, flags); + } else { + LOG_INFO_IN_IRQ( + instance, + "trace_id[0x%llx], hno:%u Event proc free cmd:%d, abort_eventcmd %d!\n", + cmd->trace_id, PS3_HOST(instance), cmd->index, + ps3_atomic_read(&instance->event_context.abort_eventcmd)); + instance->event_context.event_cmd = NULL; + ps3_mgr_cmd_free(instance, cmd); + ps3_new_delay_work = + (ps3_delay_work == + &instance->event_context + .delay_work_pool[PS3_EVENT_WORKER_POOL_SIZE - + 1]) ? + (&instance->event_context.delay_work_pool[0]) : + (ps3_delay_work + 1); + instance->event_context.delay_work = ps3_new_delay_work; + instance->event_req_info.eventTypeMapProcResult = + instance->event_req_info.eventTypeMap; + ret = ps3_event_subscribe(instance); + if (ret != PS3_SUCCESS) { + LOG_ERROR_IN_IRQ(instance, + "hno:%u event subscribe failed!\n", + PS3_HOST(instance)); + } + + if (ps3_atomic_read(&instance->hardreset_event) != 0) + ps3_atomic_set(&instance->hardreset_event, 0); + + if (ps3_atomic_read(&instance->event_context.abort_eventcmd) != 0) + ps3_atomic_set(&instance->event_context.abort_eventcmd, 0); + wmb(); /* in order to force CPU ordering */ + if (ps3_atomic_read(&instance->event_context.subwork) != 0) + ps3_atomic_dec(&instance->event_context.subwork); + ps3_spin_unlock_irqrestore( + &instance->recovery_context->recovery_lock, flags); + } +} +#ifdef _WINDOWS +static void ps3_event_polling(void *ins) +{ + struct ps3_instance *instance = (struct ps3_instance *)ins; + + ps3_event_handle(&instance->event_context.delay_work); +} +#else +static void ps3_event_polling(struct work_struct *work) +{ + struct ps3_event_delay_work *delay_work = ps3_container_of( + work, struct ps3_event_delay_work, event_work.work); + + ps3_event_handle(delay_work); +} +#endif +const char *ps3_event_print(enum MgrEvtType event_type) +{ + unsigned int idx = 0; + const char *ps3_event_type_name = NULL; + + for (idx = 0; idx < ARRAY_SIZE(g_event_type_desc_table); idx++) { + if (g_event_type_desc_table[idx].event_type == event_type) { + ps3_event_type_name = + g_event_type_desc_table[idx].event_type_desc; + break; + } + } + + return ps3_event_type_name; +} + +int ps3_event_context_init(struct ps3_instance *instance) +{ + int ret = -PS3_FAILED; + unsigned int idx = 0; + struct ps3_event_context *event_ctx = &instance->event_context; + struct ps3_event_delay_work *delay_work = NULL; + struct ps3_event_delay_work *delay_work_pool = NULL; + + memset(event_ctx, 0, sizeof(*event_ctx)); +#ifndef _WINDOWS + delay_work_pool = (struct ps3_event_delay_work *)ps3_kzalloc( + instance, sizeof(struct ps3_event_delay_work) * + PS3_EVENT_WORKER_POOL_SIZE); + if (delay_work_pool == NULL) { + LOG_ERROR("hno:%u delay work kzalloc failed!\n", + PS3_HOST(instance)); + goto l_out; + } + event_ctx->delay_work_pool = delay_work_pool; + for (idx = 0; idx < PS3_EVENT_WORKER_POOL_SIZE; idx++) { + delay_work = &delay_work_pool[idx]; + INIT_DELAYED_WORK(&delay_work->event_work, ps3_event_polling); + } + event_ctx->delay_work = &delay_work_pool[0]; + event_ctx->event_cmd = NULL; + event_ctx->event_abort_cmd = NULL; + ps3_atomic_set(&event_ctx->abort_eventcmd, 0); + event_ctx->flag = PS3_FALSE; +#else + delay_work = &event_ctx->delay_work; + if (ps3_worker_init(instance, &delay_work->event_work, "ps3_event_wk", + ps3_event_polling) != PS3_SUCCESS) { + LOG_ERROR("hno:%u worker init failed\n", PS3_HOST(instance)); + ret = -PS3_FAILED; + goto l_out; + } + +#endif + ps3_atomic_set(&event_ctx->subwork, 0); + + ret = PS3_SUCCESS; +l_out: + return ret; +} + +void ps3_event_context_exit(struct ps3_instance *instance) +{ + struct ps3_event_context *event_ctx = &instance->event_context; +#ifdef _WINDOWS + struct ps3_event_delay_work *delay_work = NULL; + + delay_work = &event_ctx->delay_work; + ps3_worker_exit(&delay_work->event_work); +#else + if (event_ctx->delay_work_pool != NULL) { + ps3_kfree(instance, event_ctx->delay_work_pool); + event_ctx->delay_work_pool = NULL; + event_ctx->delay_work = NULL; + } +#endif + memset(event_ctx, 0, sizeof(*event_ctx)); +} + +int ps3_event_subscribe(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + struct ps3_event_context *event_ctx = &instance->event_context; + struct PS3MgrEvent *event_req_info = &instance->event_req_info; + + if (!instance->is_need_event) { + LOG_WARN_IN_IRQ(instance, "hno:%u IOC no need event!!\n", + PS3_HOST(instance)); + ret = PS3_SUCCESS; + goto l_out; + } + + if (event_ctx->event_cmd != NULL) { + LOG_ERROR_IN_IRQ(instance, "hno:%u Event already subscribe!\n", + PS3_HOST(instance)); + ret = PS3_SUCCESS; + goto l_out; + } + + if (!instance->state_machine.is_load) { + LOG_WARN_IN_IRQ( + instance, + "hno:%u instance is suspend or instance unload!\n", + PS3_HOST(instance)); + ret = PS3_SUCCESS; + goto l_out; + } + + ret = ps3_event_register(instance, event_req_info); + if (ret != PS3_SUCCESS) { + LOG_WARN_IN_IRQ( + instance, + "hno:%u Failed to register event cmd, ret: %d\n", + PS3_HOST(instance), ret); + goto l_out; + } + + LOG_INFO_IN_IRQ(instance, + "hno:%u Success to subscribe event, bitmap[%08x]!\n", + PS3_HOST(instance), event_req_info->eventTypeMap); +l_out: + return ret; +} + +int ps3_event_unsubscribe(struct ps3_instance *instance) +{ + unsigned long flags = 0; + unsigned long flags1 = 0; + int ret = -PS3_FAILED; + struct ps3_cmd *cmd = NULL; + struct ps3_cmd *abort_cmd = NULL; + struct ps3_event_context *event_ctx = &instance->event_context; + + if (!ps3_check_ioc_state_is_normal_in_unload(instance)) { + ret = -PS3_FAILED; + goto l_out; + } + + ps3_spin_lock_irqsave(&instance->recovery_context->recovery_lock, + &flags1); + if (event_ctx->flag) { + ps3_spin_unlock_irqrestore( + &instance->recovery_context->recovery_lock, flags1); + goto l_out; + } + event_ctx->flag = PS3_TRUE; + cmd = event_ctx->event_cmd; + ps3_spin_unlock_irqrestore(&instance->recovery_context->recovery_lock, + flags1); + if (cmd == NULL) { + LOG_WARN("hno:%u Event is not register yet\n", + PS3_HOST(instance)); + ret = PS3_SUCCESS; + goto l_out; + } + + ps3_spin_lock_irqsave(&cmd->cmd_state.lock, &flags); + if (cmd->cmd_state.state == PS3_CMD_STATE_INIT) { + ps3_spin_unlock_irqrestore(&cmd->cmd_state.lock, flags); + LOG_WARN("hno:%u had been free,CFID:%d\n", PS3_HOST(instance), + cmd->index); + ps3_spin_lock_irqsave( + &instance->recovery_context->recovery_lock, &flags1); + event_ctx->event_cmd = NULL; + ps3_spin_unlock_irqrestore( + &instance->recovery_context->recovery_lock, flags1); + ret = PS3_SUCCESS; + goto l_out; + } else { + ps3_spin_unlock_irqrestore(&cmd->cmd_state.lock, flags); + } + + ps3_spin_lock_irqsave(&instance->recovery_context->recovery_lock, + &flags1); + ret = ps3_mgr_cmd_cancel_send(instance, cmd->index, + PS3_CANCEL_EVENT_CMD); + if (ret == -PS3_ENOMEM) { + ps3_spin_unlock_irqrestore( + &instance->recovery_context->recovery_lock, flags1); + LOG_INFO("hno:%u alloc failed\n", PS3_HOST(instance)); + ret = PS3_FAILED; + goto l_out; + } else if (ret != PS3_SUCCESS) { + ps3_spin_unlock_irqrestore( + &instance->recovery_context->recovery_lock, flags1); + LOG_INFO("hno:%u reqFrameId=%d cancel_cmd_frame_id[%u] free!\n", + PS3_HOST(instance), + ps3_cmd_frame_id( + instance->event_context.event_abort_cmd), + cmd->index); + abort_cmd = instance->event_context.event_abort_cmd; + instance->event_context.event_abort_cmd = NULL; + if (abort_cmd != NULL) + ps3_task_cmd_free(instance, abort_cmd); + ret = PS3_FAILED; + goto l_out; + } + ps3_atomic_set(&instance->event_context.abort_eventcmd, 1); + ps3_spin_unlock_irqrestore(&instance->recovery_context->recovery_lock, + flags1); + + ret = ps3_mgr_cmd_cancel_wait(instance, PS3_CANCEL_EVENT_CMD); + if (ret != PS3_SUCCESS) { + LOG_ERROR("hno:%u event cancel cmd NOK, ret:%d\n", + PS3_HOST(instance), ret); + goto l_out; + } + + ps3_spin_lock_irqsave(&instance->recovery_context->recovery_lock, + &flags1); + if (ps3_atomic_read(&instance->event_context.subwork) == 0) { + if (ps3_atomic_read(&instance->event_context.abort_eventcmd) != 0) { + ps3_atomic_set(&instance->event_context.abort_eventcmd, 0); + LOG_FILE_INFO("hno:%u event cmd free, CFID:%d\n", + PS3_HOST(instance), cmd->index); + event_ctx->event_cmd = NULL; + ps3_mgr_cmd_free(instance, cmd); + } + } + ps3_spin_unlock_irqrestore(&instance->recovery_context->recovery_lock, + flags1); +l_out: + ps3_spin_lock_irqsave(&instance->recovery_context->recovery_lock, + &flags1); + event_ctx->flag = PS3_FALSE; + ps3_spin_unlock_irqrestore(&instance->recovery_context->recovery_lock, + flags1); + return ret; +} +int ps3_soft_reset_event_resubscribe(struct ps3_instance *instance) +{ + unsigned long flags = 0; + unsigned long flags1 = 0; + int ret = -PS3_FAILED; + struct ps3_cmd *cmd = NULL; + struct ps3_cmd *abort_cmd = NULL; + struct ps3_event_context *event_ctx = &instance->event_context; + unsigned char is_need_resend = PS3_FALSE; + + if (!ps3_check_ioc_state_is_normal_in_unload(instance)) { + ret = -PS3_FAILED; + goto l_out; + } + + ps3_spin_lock_irqsave(&instance->recovery_context->recovery_lock, + &flags1); + if (event_ctx->flag) { + ps3_spin_unlock_irqrestore( + &instance->recovery_context->recovery_lock, flags1); + goto l_out; + } + event_ctx->flag = PS3_TRUE; + cmd = event_ctx->event_cmd; + ps3_spin_unlock_irqrestore(&instance->recovery_context->recovery_lock, + flags1); + if (cmd == NULL) { + LOG_WARN("hno:%u Event is not register yet\n", + PS3_HOST(instance)); + ps3_spin_lock_irqsave( + &instance->recovery_context->recovery_lock, &flags1); + is_need_resend = PS3_TRUE; + goto l_resend; + } + + ps3_spin_lock_irqsave(&cmd->cmd_state.lock, &flags); + if (cmd->cmd_state.state == PS3_CMD_STATE_INIT) { + ps3_spin_unlock_irqrestore(&cmd->cmd_state.lock, flags); + LOG_WARN("hno:%u had been free,CFID:%d\n", PS3_HOST(instance), + cmd->index); + ps3_spin_lock_irqsave( + &instance->recovery_context->recovery_lock, &flags1); + event_ctx->event_cmd = NULL; + ps3_spin_unlock_irqrestore( + &instance->recovery_context->recovery_lock, flags1); + ret = PS3_SUCCESS; + goto l_out; + } else { + ps3_spin_unlock_irqrestore(&cmd->cmd_state.lock, flags); + } + + ps3_spin_lock_irqsave(&instance->recovery_context->recovery_lock, + &flags1); + ret = ps3_mgr_cmd_cancel_send(instance, cmd->index, + PS3_CANCEL_EVENT_CMD); + if (ret == -PS3_ENOMEM) { + ps3_spin_unlock_irqrestore( + &instance->recovery_context->recovery_lock, flags1); + LOG_INFO("hno:%u alloc failed\n", PS3_HOST(instance)); + ret = PS3_FAILED; + goto l_out; + } else if (ret != PS3_SUCCESS) { + ps3_spin_unlock_irqrestore( + &instance->recovery_context->recovery_lock, flags1); + LOG_INFO("hno:%u reqFrameId=%d cancel_cmd_frame_id[%u] free!\n", + PS3_HOST(instance), + ps3_cmd_frame_id( + instance->event_context.event_abort_cmd), + cmd->index); + abort_cmd = instance->event_context.event_abort_cmd; + instance->event_context.event_abort_cmd = NULL; + if (abort_cmd != NULL) + ps3_task_cmd_free(instance, abort_cmd); + ret = PS3_FAILED; + goto l_out; + } + ps3_atomic_set(&instance->event_context.abort_eventcmd, 1); + ps3_spin_unlock_irqrestore(&instance->recovery_context->recovery_lock, + flags1); + + ret = ps3_mgr_cmd_cancel_wait(instance, PS3_CANCEL_EVENT_CMD); + if (ret != PS3_SUCCESS) { + LOG_ERROR("hno:%u event cancel cmd failed, ret:%d\n", + PS3_HOST(instance), ret); + goto l_out; + } + ps3_spin_lock_irqsave(&instance->recovery_context->recovery_lock, + &flags1); + if (ps3_atomic_read(&instance->event_context.subwork) == 0) { + if (ps3_atomic_read(&instance->event_context.abort_eventcmd) != 0) { + ps3_atomic_set(&instance->event_context.abort_eventcmd, 0); + LOG_FILE_INFO("hno:%u event cmd free, CFID:%d\n", + PS3_HOST(instance), cmd->index); + event_ctx->event_cmd = NULL; + ps3_mgr_cmd_free(instance, cmd); + + is_need_resend = PS3_TRUE; + } + } + +l_resend: + if (is_need_resend) { + instance->event_req_info.eventTypeMapProcResult = + instance->event_req_info.eventTypeMap; + ret = ps3_event_subscribe(instance); + if ((ret != PS3_SUCCESS) && (ret != -PS3_IN_UNLOAD)) { + LOG_FILE_ERROR("hno:%u event subscribe failed!\n", + PS3_HOST(instance)); + ret = PS3_FAILED; + } + } + ps3_spin_unlock_irqrestore(&instance->recovery_context->recovery_lock, + flags1); + +l_out: + ps3_spin_lock_irqsave(&instance->recovery_context->recovery_lock, + &flags1); + event_ctx->flag = PS3_FALSE; + ps3_spin_unlock_irqrestore(&instance->recovery_context->recovery_lock, + flags1); + return ret; +} + +int ps3_event_service(struct ps3_cmd *cmd, unsigned short reply_flags) +{ + unsigned long flags = 0; + unsigned long flags1 = 0; + int ret = PS3_SUCCESS; + struct ps3_instance *instance = cmd->instance; + struct ps3_event_context *event_ctx = &instance->event_context; +#ifdef _WINDOWS + struct ps3_event_delay_work *delay_work = &event_ctx->delay_work; +#else + struct ps3_event_delay_work *delay_work = event_ctx->delay_work; +#endif + int cur_state = PS3_INSTANCE_STATE_INIT; + + PS3_MGR_CMD_BACK_INC(instance, cmd, reply_flags); + ps3_event_print_cmd(cmd, PS3_FALSE); + + if (reply_flags != PS3_REPLY_WORD_FLAG_SUCCESS) { + LOG_ERROR_IN_IRQ( + instance, + "trace_id[0x%llx], hno:%u Event notify fetal error!\n", + cmd->trace_id, PS3_HOST(instance)); + ret = -PS3_FAILED; + goto l_failed; + } + ps3_spin_lock_irqsave(&instance->recovery_context->recovery_lock, + &flags1); + if (event_ctx->event_cmd == NULL) { + ps3_spin_unlock_irqrestore( + &instance->recovery_context->recovery_lock, flags1); + LOG_INFO_IN_IRQ( + instance, + "trace_id[0x%llx], hno:%u Event is unsubscribed!\n", + cmd->trace_id, PS3_HOST(instance)); + ret = -PS3_FAILED; + goto l_out; + } + ps3_spin_unlock_irqrestore(&instance->recovery_context->recovery_lock, + flags1); + + if (!instance->state_machine.is_load) { + LOG_INFO_IN_IRQ( + instance, + "trace_id[0x%llx], hno:%u instance is suspend or instance unload!\n", + cmd->trace_id, PS3_HOST(instance)); + ret = -PS3_FAILED; + goto l_failed; + } + + cur_state = ps3_atomic_read(&instance->state_machine.state); + if (cur_state != PS3_INSTANCE_STATE_OPERATIONAL) { + LOG_INFO_IN_IRQ( + instance, + "trace_id[0x%llx], hno:%u instance is not operational!\n", + cmd->trace_id, PS3_HOST(instance)); + ret = -PS3_FAILED; + goto l_failed; + } + + delay_work->cmd = cmd; +#ifdef _WINDOWS + if (ps3_worker_start(&delay_work->event_work) != PS3_SUCCESS) { + LOG_ERROR_IN_IRQ( + instance, + "trace_id[0x%llx], hno:%u Event start worker failed!\n", + cmd->trace_id, PS3_HOST(instance)); + ret = -PS3_FAILED; + goto l_failed; + } + ps3_atomic_inc(&instance->event_context.subwork); +#else + ps3_atomic_inc(&instance->event_context.subwork); + schedule_delayed_work(&delay_work->event_work, 0); +#endif + goto l_out; + +l_failed: + ps3_spin_lock_irqsave(&cmd->cmd_state.lock, &flags); + cmd->cmd_state.state = PS3_CMD_STATE_DEAD; + ps3_spin_unlock_irqrestore(&cmd->cmd_state.lock, flags); + LOG_INFO_IN_IRQ(instance, "trace_id[0x%llx],CFID[%d], hno:%u failed!\n", + cmd->trace_id, cmd->index, PS3_HOST(instance)); +l_out: + LOG_INFO_IN_IRQ(instance, "instance->event_context.subwork[%u]!\n", + ps3_atomic_read(&instance->event_context.subwork)); + return ret; +} + +int ps3_fasync(int fd, struct file *filp, int mode) +{ + return fasync_helper(fd, filp, mode, &g_async_queue); +} + +int ps3_webSubscribe_context_init(struct ps3_instance *instance) +{ + int ret = -PS3_FAILED; + struct ps3_webSubscribe_context *web_context = + &instance->webSubscribe_context; + + memset(web_context, 0, sizeof(*web_context)); + web_context->webSubscribe_cmd = NULL; + web_context->web_abort_cmd = NULL; + ps3_atomic_set(&web_context->subscribe_count, PS3_WEB_FLAG_INIT_VALUE); + ps3_atomic_set(&web_context->is_subscribe, PS3_WEB_FLAG_INIT_VALUE); + ps3_atomic_set(&web_context->is_abort, PS3_WEB_FLAG_INIT_VALUE); + ps3_atomic_set(&web_context->abort_webcmd, PS3_WEB_FLAG_INIT_VALUE); + ret = PS3_SUCCESS; + return ret; +} + +void ps3_webSubscribe_context_exit(struct ps3_instance *instance) +{ + struct ps3_webSubscribe_context *web_context = + &instance->webSubscribe_context; + + memset(web_context, 0, sizeof(*web_context)); +} + +static void ps3_web_resubscribe(struct ps3_instance *instance, + struct ps3_cmd *cmd) +{ + unsigned long flags = 0; + + PS3_MGR_CMD_STAT_INC(instance, cmd); + + ps3_spin_lock_irqsave(&cmd->cmd_state.lock, &flags); + cmd->cmd_state.state = PS3_CMD_STATE_PROCESS; + ps3_spin_unlock_irqrestore(&cmd->cmd_state.lock, flags); + + if (ps3_async_cmd_send(cmd->instance, cmd) != PS3_SUCCESS) { + LOG_ERROR_IN_IRQ( + instance, + "trace_id[0x%llx],CFID[%d],hno:%u resend web cmd NOK\n", + cmd->trace_id, cmd->index, PS3_HOST(cmd->instance)); + + ps3_spin_lock_irqsave(&cmd->cmd_state.lock, &flags); + cmd->cmd_state.state = PS3_CMD_STATE_DEAD; + ps3_spin_unlock_irqrestore(&cmd->cmd_state.lock, flags); + } +} + +int ps3_webSubscribe_service(struct ps3_cmd *cmd, unsigned short reply_flags) +{ + unsigned long flags = 0; + int ret = PS3_SUCCESS; + struct ps3_instance *instance = cmd->instance; + struct ps3_webSubscribe_context *web_context = + &instance->webSubscribe_context; + + int cur_state = PS3_INSTANCE_STATE_INIT; + + PS3_MGR_CMD_BACK_INC(instance, cmd, reply_flags); + ps3_event_print_cmd(cmd, PS3_FALSE); + + if (reply_flags == PS3_REPLY_WORD_FLAG_FAIL) { + LOG_ERROR_IN_IRQ( + instance, + "trace_id[0x%llx], hno:%u Web notify fetal error!\n", + cmd->trace_id, PS3_HOST(instance)); + ret = -PS3_FAILED; + goto l_failed; + } + + if (web_context->webSubscribe_cmd == NULL) { + LOG_INFO_IN_IRQ( + instance, + "trace_id[0x%llx], hno:%u Web is unsubscribed!\n", + cmd->trace_id, PS3_HOST(instance)); + ret = -PS3_FAILED; + goto l_out; + } + + if (!instance->state_machine.is_load) { + LOG_INFO_IN_IRQ( + instance, + "trace_id[0x%llx], hno:%u instance is suspend or instance unload!\n", + cmd->trace_id, PS3_HOST(instance)); + ret = -PS3_FAILED; + goto l_failed; + } + + cur_state = ps3_atomic_read(&instance->state_machine.state); + if (cur_state != PS3_INSTANCE_STATE_OPERATIONAL) { + LOG_INFO_IN_IRQ( + instance, + "trace_id[0x%llx], hno:%u instance is not operational!\n", + cmd->trace_id, PS3_HOST(instance)); + ret = -PS3_FAILED; + goto l_failed; + } + + kill_fasync(&g_async_queue, PS3_EVENT_NOTICE_SIG, POLL_IN); + + ps3_spin_lock_irqsave(&instance->recovery_context->recovery_lock, + &flags); + if (ps3_atomic_read(&instance->webSubscribe_context.abort_webcmd) == 0) { + ps3_web_resubscribe(instance, cmd); + ps3_spin_unlock_irqrestore( + &instance->recovery_context->recovery_lock, flags); + } else { + LOG_INFO_IN_IRQ( + instance, + "trace_id[0x%llx], hno:%u Web proc free cmd:%d, abort_webcmd %d!\n", + cmd->trace_id, PS3_HOST(instance), cmd->index, + ps3_atomic_read(&instance->webSubscribe_context.abort_webcmd)); + + instance->webSubscribe_context.webSubscribe_cmd = NULL; + ps3_mgr_cmd_free(instance, cmd); + ret = ps3_web_subscribe(instance); + if (ret != PS3_SUCCESS) { + LOG_ERROR_IN_IRQ(instance, + "hno:%u web subscribe failed!\n", + PS3_HOST(instance)); + } + ps3_atomic_set(&instance->webSubscribe_context.abort_webcmd, 0); + ps3_spin_unlock_irqrestore( + &instance->recovery_context->recovery_lock, flags); + } + goto l_out; + +l_failed: + ps3_spin_lock_irqsave(&cmd->cmd_state.lock, &flags); + cmd->cmd_state.state = PS3_CMD_STATE_DEAD; + ps3_spin_unlock_irqrestore(&cmd->cmd_state.lock, flags); + LOG_INFO_IN_IRQ(instance, "trace_id[0x%llx],CFID[%d], hno:%u failed!\n", + cmd->trace_id, cmd->index, PS3_HOST(instance)); +l_out: + return ret; +} + +int ps3_web_subscribe(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + + if (!instance->is_need_event) { + LOG_WARN_IN_IRQ(instance, "hno:%u ioc no need web server!!\n", + PS3_HOST(instance)); + ret = -PS3_FAILED; + goto l_out; + } + ret = ps3_web_register(instance); + if (ret != PS3_SUCCESS) { + LOG_WARN_IN_IRQ(instance, + "hno:%u Failed to register web cmd, ret: %d\n", + PS3_HOST(instance), ret); + goto l_out; + } + + LOG_INFO_IN_IRQ(instance, "hno:%u Success to subscribe web event\n", + PS3_HOST(instance)); +l_out: + return ret; +} + +int ps3_web_unsubscribe(struct ps3_instance *instance) +{ + unsigned long flags = 0; + int ret = -PS3_FAILED; + struct ps3_cmd *cmd = NULL; + struct ps3_cmd *abort_cmd = NULL; + struct ps3_webSubscribe_context *web_context = + &instance->webSubscribe_context; + + if (!ps3_check_ioc_state_is_normal_in_unload(instance)) { + ret = -PS3_FAILED; + goto l_out; + } + cmd = web_context->webSubscribe_cmd; + if (cmd == NULL) { + LOG_WARN("hno:%u web event is not register yet\n", + PS3_HOST(instance)); + ret = PS3_SUCCESS; + goto l_out; + } + + ps3_spin_lock_irqsave(&cmd->cmd_state.lock, &flags); + if (cmd->cmd_state.state == PS3_CMD_STATE_INIT) { + ps3_spin_unlock_irqrestore(&cmd->cmd_state.lock, flags); + LOG_WARN("hno:%u had been free,CFID:%d\n", PS3_HOST(instance), + cmd->index); + web_context->webSubscribe_cmd = NULL; + ret = PS3_SUCCESS; + goto l_out; + } else { + ps3_spin_unlock_irqrestore(&cmd->cmd_state.lock, flags); + } + + if (ps3_atomic_add_unless(&instance->webSubscribe_context.is_abort, 1, + 1) == 0) { + ret = PS3_SUCCESS; + goto l_out; + } + + ret = ps3_mgr_cmd_cancel_send(instance, cmd->index, PS3_CANCEL_WEB_CMD); + if (ret == -PS3_ENOMEM) { + LOG_INFO("hno:%u alloc failed\n", PS3_HOST(instance)); + ret = -PS3_FAILED; + ps3_atomic_set(&instance->webSubscribe_context.is_abort, 0); + goto l_out; + } else if (ret != PS3_SUCCESS) { + LOG_INFO("hno:%u reqFrameId=%d cancel_cmd_frame_id[%u] free!\n", + PS3_HOST(instance), + ps3_cmd_frame_id( + instance->webSubscribe_context.web_abort_cmd), + cmd->index); + abort_cmd = instance->webSubscribe_context.web_abort_cmd; + instance->webSubscribe_context.web_abort_cmd = NULL; + if (abort_cmd != NULL) + ps3_task_cmd_free(instance, abort_cmd); + ret = -PS3_FAILED; + ps3_atomic_set(&instance->webSubscribe_context.is_abort, 0); + goto l_out; + } + ret = ps3_mgr_cmd_cancel_wait(instance, PS3_CANCEL_WEB_CMD); + if (ret != PS3_SUCCESS) { + LOG_ERROR("hno:%u event cancel cmd NOK, ret:%d\n", + PS3_HOST(instance), ret); + ps3_atomic_set(&instance->webSubscribe_context.is_abort, 0); + goto l_out; + } + LOG_INFO("hno:%u web cmd free, CFID:%d\n", PS3_HOST(instance), + cmd->index); + web_context->webSubscribe_cmd = NULL; + ps3_mgr_cmd_free(instance, cmd); + ps3_atomic_set(&instance->webSubscribe_context.is_abort, 0); +l_out: + return ret; +} + +int ps3_soft_reset_web_resubscribe(struct ps3_instance *instance) +{ + unsigned long flags = 0; + unsigned long flags1 = 0; + int ret = -PS3_FAILED; + struct ps3_cmd *cmd = NULL; + struct ps3_cmd *abort_cmd = NULL; + struct ps3_webSubscribe_context *web_context = + &instance->webSubscribe_context; + unsigned char is_need_resend = PS3_FALSE; + + if (!ps3_check_ioc_state_is_normal_in_unload(instance)) { + ret = -PS3_FAILED; + goto l_out; + } + + cmd = web_context->webSubscribe_cmd; + if (cmd == NULL) { + LOG_WARN("hno:%u Web is not register yet\n", + PS3_HOST(instance)); + is_need_resend = PS3_TRUE; + ps3_spin_lock_irqsave( + &instance->recovery_context->recovery_lock, &flags1); + goto l_resend; + } + + ps3_spin_lock_irqsave(&cmd->cmd_state.lock, &flags); + if (cmd->cmd_state.state == PS3_CMD_STATE_INIT) { + ps3_spin_unlock_irqrestore(&cmd->cmd_state.lock, flags); + LOG_WARN("hno:%u had been free,CFID:%d\n", PS3_HOST(instance), + cmd->index); + web_context->webSubscribe_cmd = NULL; + ret = PS3_SUCCESS; + goto l_out; + } else { + ps3_spin_unlock_irqrestore(&cmd->cmd_state.lock, flags); + } + + if (ps3_atomic_add_unless(&instance->webSubscribe_context.is_abort, 1, + 1) == 0) { + ret = PS3_SUCCESS; + goto l_out; + } + + ps3_spin_lock_irqsave(&instance->recovery_context->recovery_lock, + &flags1); + ret = ps3_mgr_cmd_cancel_send(instance, cmd->index, PS3_CANCEL_WEB_CMD); + if (ret == -PS3_ENOMEM) { + ps3_spin_unlock_irqrestore( + &instance->recovery_context->recovery_lock, flags1); + LOG_INFO("hno:%u alloc failed\n", PS3_HOST(instance)); + ret = -PS3_FAILED; + ps3_atomic_set(&instance->webSubscribe_context.is_abort, 0); + goto l_out; + } else if (ret != PS3_SUCCESS) { + ps3_spin_unlock_irqrestore( + &instance->recovery_context->recovery_lock, flags1); + LOG_INFO("hno:%u reqFrameId=%d cancel_cmd_frame_id[%u] free!\n", + PS3_HOST(instance), + ps3_cmd_frame_id( + instance->webSubscribe_context.web_abort_cmd), + cmd->index); + abort_cmd = instance->webSubscribe_context.web_abort_cmd; + instance->webSubscribe_context.web_abort_cmd = NULL; + if (abort_cmd != NULL) + ps3_task_cmd_free(instance, abort_cmd); + ret = -PS3_FAILED; + ps3_atomic_set(&instance->webSubscribe_context.is_abort, 0); + goto l_out; + } + ps3_atomic_set(&instance->webSubscribe_context.abort_webcmd, 1); + ps3_spin_unlock_irqrestore(&instance->recovery_context->recovery_lock, + flags1); + + ret = ps3_mgr_cmd_cancel_wait(instance, PS3_CANCEL_WEB_CMD); + if (ret != PS3_SUCCESS) { + LOG_ERROR("hno:%u web cancel cmd failed, ret:%d\n", + PS3_HOST(instance), ret); + ps3_atomic_set(&instance->webSubscribe_context.is_abort, 0); + goto l_out; + } + ps3_spin_lock_irqsave(&instance->recovery_context->recovery_lock, + &flags1); + if (ps3_atomic_read(&instance->webSubscribe_context.abort_webcmd) != 0) { + ps3_atomic_set(&instance->webSubscribe_context.abort_webcmd, 0); + LOG_FILE_INFO("hno:%u web cmd free, CFID:%d\n", + PS3_HOST(instance), cmd->index); + web_context->webSubscribe_cmd = NULL; + ps3_mgr_cmd_free(instance, cmd); + is_need_resend = PS3_TRUE; + } + ps3_atomic_set(&instance->webSubscribe_context.is_abort, 0); + +l_resend: + if (is_need_resend) { + ret = ps3_web_subscribe(instance); + if ((ret != PS3_SUCCESS) && (ret != -PS3_IN_UNLOAD)) { + LOG_FILE_ERROR("hno:%u web subscribe failed!\n", + PS3_HOST(instance)); + ret = -PS3_FAILED; + } + } + ps3_spin_unlock_irqrestore(&instance->recovery_context->recovery_lock, + flags1); +l_out: + return ret; +} + +void ps3_web_cmd_clear(struct ps3_instance *instance) +{ + unsigned long flags = 0; + struct ps3_cmd *cmd = NULL; + + ps3_atomic_set(&instance->webSubscribe_context.abort_webcmd, 0); + cmd = instance->webSubscribe_context.webSubscribe_cmd; + + if (cmd == NULL) { + LOG_WARN("hno:%u web cmd has been cancel\n", + PS3_HOST(instance)); + goto l_out; + } + + ps3_spin_lock_irqsave(&cmd->cmd_state.lock, &flags); + if (cmd->cmd_state.state == PS3_CMD_STATE_INIT) { + ps3_spin_unlock_irqrestore(&cmd->cmd_state.lock, flags); + LOG_WARN("hno:%u free this cmd,CFID:%d\n", PS3_HOST(instance), + cmd->index); + instance->webSubscribe_context.webSubscribe_cmd = NULL; + goto l_out; + } else { + ps3_spin_unlock_irqrestore(&cmd->cmd_state.lock, flags); + } + + LOG_INFO("hno:%u web cmd free, CFID:%d\n", PS3_HOST(instance), + cmd->index); + instance->webSubscribe_context.webSubscribe_cmd = NULL; + ps3_mgr_cmd_free(instance, cmd); + +l_out: + return; +} diff --git a/drivers/scsi/linkdata/ps3stor/ps3_event.h b/drivers/scsi/linkdata/ps3stor/ps3_event.h new file mode 100644 index 0000000000000000000000000000000000000000..23d4627a7fd09ef792711a580395859c45420088 --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/ps3_event.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _PS3_EVENT_H_ +#define _PS3_EVENT_H_ + +#include "ps3_instance_manager.h" + +#define PS3_EVENT_WORKER_POOL_SIZE (3) +#define PS3_WEB_FLAG_INIT_VALUE (0) +int ps3_event_context_init(struct ps3_instance *instance); +void ps3_event_context_exit(struct ps3_instance *instance); +const char *ps3_event_print(enum MgrEvtType event_type); +int ps3_event_subscribe(struct ps3_instance *instance); +int ps3_event_unsubscribe(struct ps3_instance *instance); +int ps3_soft_reset_event_resubscribe(struct ps3_instance *instance); +void ps3_event_handle(struct ps3_event_delay_work *ps3_delay_work); + +int ps3_event_service(struct ps3_cmd *cmd, unsigned short reply_flags); +int ps3_event_delay_set(struct ps3_instance *instance, unsigned int delay); + +void ps3_event_filter_table_get_raid(unsigned char *data); + +void ps3_event_filter_table_get_hba(unsigned char *data); + +void ps3_event_filter_table_get_switch(unsigned char *data); + +void ps3_vd_pending_filter_table_build(unsigned char *data); + +int ps3_fasync(int fd, struct file *filp, int mode); +int ps3_webSubscribe_context_init(struct ps3_instance *instance); +void ps3_webSubscribe_context_exit(struct ps3_instance *instance); +int ps3_webSubscribe_service(struct ps3_cmd *cmd, unsigned short reply_flags); +int ps3_web_subscribe(struct ps3_instance *instance); +int ps3_web_unsubscribe(struct ps3_instance *instance); +int ps3_soft_reset_web_resubscribe(struct ps3_instance *instance); +void ps3_web_cmd_clear(struct ps3_instance *instance); + +#endif diff --git a/drivers/scsi/linkdata/ps3stor/ps3_inner_data.h b/drivers/scsi/linkdata/ps3stor/ps3_inner_data.h new file mode 100644 index 0000000000000000000000000000000000000000..f82340be48b55722188a2ae8aba19e1b29a25774 --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/ps3_inner_data.h @@ -0,0 +1,362 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _PS3_INNER_DATA_H_ +#define _PS3_INNER_DATA_H_ + +#ifndef _WINDOWS +#include +#include +#include +#include +#include +#include +#else +#include "ps3_worker.h" +#endif + +#include "ps3_htp_event.h" +#include "ps3_htp_dev.h" +#include "ps3_htp_pci.h" +#include "hwapi/include_v200/s1861_regs/s1861_global_baseaddr.h" +#include "hwapi/include_v200/s1861_regs/s1861_hil_reg0_ps3_request_queue_reg.h" +#include "hwapi/include_v200/s1861_regs/s1861_hil_reg0_ps3_register_f_reg.h" +#include "hwapi/include_v200/s1861_regs/s1861_hil_reg0_ps3_register_s_reg.h" + +#include "ps3_device_manager.h" +#include "ps3_htp_register_fifo.h" +#include "ps3_err_def.h" +#include "ps3_kernel_version.h" +#define PS3_DEFAULT_PAGE_SIZE_SHIFT (12) +#define PS3_CMD_SGE_FRAME_BUFFER_SIZE (4096) +#define PS3_DUMP_DMA_BUF_SIZE (0x100000) +#define PS3_DEFAULT_TASK_MGR_TIMEOUT (50) +#define PS3_DEFAULT_MGR_CMD_TIMEOUT (180) +#define PS3_CANCEL_MGR_CMD_TIMEOUT (30) +#define PS3_UNLOAD_MGR_CMD_TIMEOUT (60) +#define PS3_RAID_UNLOAD_MGR_CMD_TIMEOUT (360) +#define PS3_SOFT_RESET_WAIT_TIMEOUT (30) +#define PS3_SLEEP_TOLERATE_TIMEOUT (10) +#define PS3_RESET_WAIT_IO_REPLY_MAX_TIME (5) +#define PS3_REGISTER_SET_SIZE (147456) +#define PS3_MAX_DEBUG_REG_CNT (8) + +#define PS3_HARD_RESET_MAX_RETRY (2) + +#define PS3_MAX_REPLY_QUE_COUNT (128) +#define PS3_RAIDHBA_NUM_ADJUST_VALUE (1) + +#define PS3_DEVICE_IO_BUSY_THRESHOLD (8) + +#if defined(PS3_MAP_QUEUES) +#define MAP_QUEUES_RET_TYPE void +#define MAP_QUEUES_RET_VAL(x) +#else +#define MAP_QUEUES_RET_TYPE int +#define MAP_QUEUES_RET_VAL(x) (x) +#endif + +#define PS3_BLOCK_NUM_OF_32K (1 << 6) +#define PS3_VD_IO_16_OUTSTANDING (16) +#define PS3_VD_IO_1_OUTSTANDING (1) +enum { + PS3_SCSI_CMD_TIMEOUT_MIN = 10, + PS3_SCSI_CMD_TIMEOUT_DEFAULT = 90, + PS3_SCSI_CMD_TIMEOUT_MAX = 255, +}; + +enum { + PS3_DEVICE_QDEPTH_DEFAULT_VALUE = 16, +}; + + +enum PS3FuncID { + PS3_FUNC_ID_0 = 0, + PS3_FUNC_ID_1 = 1, + PS3_FUNC_UNLIMITED = 2, +}; + +enum { + PS3_SHIFT_BYTE = 8, + PS3_SHIFT_WORD = 16, + PS3_SHIFT_3BYTE = 24, + PS3_SHIFT_DWORD = 32, +}; + +enum { + PS3_SGL_MODE_SGE_COUNT_NO_DATA = 0, + PS3_SGL_MODE_SGE_COUNT_DIRECT = 1, +}; + +struct ps3_event_delay_work { +#ifdef _WINDOWS + struct ps3_worker event_work; +#else + struct delayed_work event_work; +#endif + struct ps3_cmd *cmd; + unsigned int event_delay; +}; + +struct ps3_event_context { + struct PS3EventInfo *event_info; + struct ps3_cmd *event_cmd; + struct ps3_cmd *event_abort_cmd; + atomic_t abort_eventcmd; +#ifdef _WINDOWS + struct ps3_event_delay_work delay_work; +#else + struct ps3_event_delay_work *delay_work; + struct ps3_event_delay_work *delay_work_pool; +#endif + unsigned long long event_phy_addr; + atomic_t subwork; + unsigned char flag; +}; + +struct ps3_webSubscribe_context { + struct ps3_cmd *webSubscribe_cmd; + struct ps3_cmd *web_abort_cmd; + atomic_t is_subscribe; + atomic_t is_abort; + atomic_t subscribe_count; + atomic_t abort_webcmd; +}; + +#ifndef _WINDOWS +struct ps3_refire_delay_work { + struct delayed_work refire_cmd_work; + struct ps3_cmd *refire_cmd; +}; +#endif + +struct ps3_cmd_attr_context { + int throttle_que_depth; + int cur_can_que; + unsigned int vd_io_threshold; + unsigned int nvme_page_size; + unsigned char is_support_direct_cmd; + unsigned char reserved[7]; +}; + +enum { + PS3_REG_OFFSET_REQUEST_QUE = + (HIL_REG0_PS3_REQUEST_QUEUE_PS3_REQUEST_QUEUE_ADDR - + HIL_REG0_PS3_REGISTER_F_BASEADDR), + PS3_REG_OFFSET_DOORBELL = (HIL_REG0_PS3_REGISTER_F_PS3_DOORBELL_ADDR - + HIL_REG0_PS3_REGISTER_F_BASEADDR), + PS3_REG_OFFSET_DOORBELL_IRQ_CLEAR = + (HIL_REG0_PS3_REGISTER_F_PS3_DOORBELL_IRQ_CLEAR_ADDR - + HIL_REG0_PS3_REGISTER_F_BASEADDR), + PS3_REG_OFFSET_DOORBELL_IRQ_MASK = + (HIL_REG0_PS3_REGISTER_F_PS3_DOORBELL_IRQ_MASK_ADDR - + HIL_REG0_PS3_REGISTER_F_BASEADDR), + PS3_REG_OFFSET_IRQ_CONTROL = + (HIL_REG0_PS3_REGISTER_F_PS3_IRQ_CONTROL_ADDR - + HIL_REG0_PS3_REGISTER_F_BASEADDR), + PS3_REG_OFFSET_SOFTRESET_KEY = + (HIL_REG0_PS3_REGISTER_F_PS3_SOFTRESET_KEY_ADDR - + HIL_REG0_PS3_REGISTER_F_BASEADDR), + PS3_REG_OFFSET_SOFTRESET_STATE = + (HIL_REG0_PS3_REGISTER_F_PS3_SOFTRESET_STATE_ADDR - + HIL_REG0_PS3_REGISTER_F_BASEADDR), + PS3_REG_OFFSET_SOFTRESET = (HIL_REG0_PS3_REGISTER_F_PS3_SOFTRESET_ADDR - + HIL_REG0_PS3_REGISTER_F_BASEADDR), + PS3_REG_OFFSET_SOFTRESET_IRQ_CLEAR = + (HIL_REG0_PS3_REGISTER_F_PS3_SOFTRESET_IRQ_CLEAR_ADDR - + HIL_REG0_PS3_REGISTER_F_BASEADDR), + PS3_REG_OFFSET_SOFTRESET_IRQ_MASK = + (HIL_REG0_PS3_REGISTER_F_PS3_SOFTRESET_IRQ_MASK_ADDR - + HIL_REG0_PS3_REGISTER_F_BASEADDR), + PS3_REG_OFFSET_SOFTRESET_KEY_SHIFT_REG_LOW = + (HIL_REG0_PS3_REGISTER_F_PS3_SOFTRESET_KEY_SHIFT_REG_LOW_ADDR - + HIL_REG0_PS3_REGISTER_F_BASEADDR), + PS3_REG_OFFSET_SOFTRESET_KEY_SHIFT_REG_HIGH = + (HIL_REG0_PS3_REGISTER_F_PS3_SOFTRESET_KEY_SHIFT_REG_HIGH_ADDR - + HIL_REG0_PS3_REGISTER_F_BASEADDR), + PS3_REG_OFFSET_SOFTRESET_TIME_CNT = + (HIL_REG0_PS3_REGISTER_F_PS3_SOFTRESET_TIME_CNT_ADDR - + HIL_REG0_PS3_REGISTER_F_BASEADDR), + PS3_REG_OFFSET_SOFTRESET_TIME_OUT_CNT = + (HIL_REG0_PS3_REGISTER_F_PS3_SOFTRESET_TIME_OUT_EN_ADDR - + HIL_REG0_PS3_REGISTER_F_BASEADDR), + PS3_REG_OFFSET_HARDRESET_KEY = + (HIL_REG0_PS3_REGISTER_F_PS3_HARDRESET_KEY_ADDR - + HIL_REG0_PS3_REGISTER_F_BASEADDR), + PS3_REG_OFFSET_HARDRESET_STATE = + (HIL_REG0_PS3_REGISTER_F_PS3_HARDRESET_STATE_ADDR - + HIL_REG0_PS3_REGISTER_F_BASEADDR), + PS3_REG_OFFSET_HARDRESET = (HIL_REG0_PS3_REGISTER_F_PS3_HARDRESET_ADDR - + HIL_REG0_PS3_REGISTER_F_BASEADDR), + PS3_REG_OFFSET_HARDRESET_KEY_SHIFT_REG_LOW = + (HIL_REG0_PS3_REGISTER_F_PS3_HARDRESET_KEY_SHIFT_REG_LOW_ADDR - + HIL_REG0_PS3_REGISTER_F_BASEADDR), + PS3_REG_OFFSET_HARDRESET_KEY_SHIFT_REG_HIGH = + (HIL_REG0_PS3_REGISTER_F_PS3_HARDRESET_KEY_SHIFT_REG_HIGH_ADDR - + HIL_REG0_PS3_REGISTER_F_BASEADDR), + PS3_REG_OFFSET_HARDRESET_TIME_CNT = + (HIL_REG0_PS3_REGISTER_F_PS3_HARDRESET_TIME_CNT_ADDR - + HIL_REG0_PS3_REGISTER_F_BASEADDR), + PS3_REG_OFFSET_HARDRESET_TIME_OUT_CNT = + (HIL_REG0_PS3_REGISTER_F_PS3_HARDRESET_TIME_OUT_EN_ADDR - + HIL_REG0_PS3_REGISTER_F_BASEADDR), + PS3_REG_OFFSET_KEY_GAP_CFG = + (HIL_REG0_PS3_REGISTER_F_PS3_KEY_GAP_CFG_ADDR - + HIL_REG0_PS3_REGISTER_F_BASEADDR), + PS3_REG_OFFSET_HARDRESET_IRQ_CLEAR = + (HIL_REG0_PS3_REGISTER_F_PS3_HARDRESET_IRQ_CLEAR_ADDR - + HIL_REG0_PS3_REGISTER_F_BASEADDR), + PS3_REG_OFFSET_HARDRESET_IRQ_MASK = + (HIL_REG0_PS3_REGISTER_F_PS3_HARDRESET_IRQ_MASK_ADDR - + HIL_REG0_PS3_REGISTER_F_BASEADDR), + PS3_REG_OFFSET_SOC_FW_STATE = + (HIL_REG0_PS3_REGISTER_F_PS3_SOC_FW_STATE_ADDR - + HIL_REG0_PS3_REGISTER_F_BASEADDR), + PS3_REG_OFFSET_MAX_FW_CMD = + (HIL_REG0_PS3_REGISTER_F_PS3_MAX_FW_CMD_ADDR - + HIL_REG0_PS3_REGISTER_F_BASEADDR), + PS3_REG_OFFSET_MAX_CHAIN_SIZE = + (HIL_REG0_PS3_REGISTER_F_PS3_MAX_CHAIN_SIZE_ADDR - + HIL_REG0_PS3_REGISTER_F_BASEADDR), + PS3_REG_OFFSET_MAX_VD_INFO_SIZE = + (HIL_REG0_PS3_REGISTER_F_PS3_MAX_VD_INFO_SIZE_ADDR - + HIL_REG0_PS3_REGISTER_F_BASEADDR), + PS3_REG_OFFSET_MAX_NVME_PAGE_SIZE = + (HIL_REG0_PS3_REGISTER_F_PS3_MAX_NVME_PAGE_SIZE_ADDR - + HIL_REG0_PS3_REGISTER_F_BASEADDR), + PS3_REG_OFFSET_FEATURE_SUPPORT = + (HIL_REG0_PS3_REGISTER_F_PS3_FEATURE_SUPPORT_ADDR - + HIL_REG0_PS3_REGISTER_F_BASEADDR), + PS3_REG_OFFSET_FIRMWARE_VERSION = + (HIL_REG0_PS3_REGISTER_F_PS3_FIRMWARE_VERSION_ADDR - + HIL_REG0_PS3_REGISTER_F_BASEADDR), + PS3_REG_OFFSET_REPLY_QUE = + (HIL_REG0_PS3_REGISTER_F_PS3_MAX_REPLYQUE_ADDR - + HIL_REG0_PS3_REGISTER_F_BASEADDR), + PS3_REG_OFFSET_HARDWARE_VERSION = + (HIL_REG0_PS3_REGISTER_F_PS3_HARDWARE_VERSION_ADDR - + HIL_REG0_PS3_REGISTER_F_BASEADDR), + PS3_REG_OFFSET_MGR_QUEUE_DEPTH = + (HIL_REG0_PS3_REGISTER_F_PS3_MGR_QUEUE_DEPTH_ADDR - + HIL_REG0_PS3_REGISTER_F_BASEADDR), + PS3_REG_OFFSET_CMD_QUEUE_DEPTH = + (HIL_REG0_PS3_REGISTER_F_PS3_CMD_QUEUE_DEPTH_ADDR - + HIL_REG0_PS3_REGISTER_F_BASEADDR), + PS3_REG_OFFSET_TFIFO_DEPTH = + (HIL_REG0_PS3_REGISTER_F_PS3_TFIFO_DEPTH_ADDR - + HIL_REG0_PS3_REGISTER_F_BASEADDR), + PS3_REG_OFFSET_MAX_SEC_R1X_CMDS = + (HIL_REG0_PS3_REGISTER_F_PS3_MAX_SEC_R1X_CMDS_ADDR - + HIL_REG0_PS3_REGISTER_F_BASEADDR), + PS3_REG_OFFSET_HIL_ADVICE2DIRECT_CNT0 = + (HIL_REG0_PS3_REGISTER_F_PS3_HIL_ADVICE2DIRECT_CNT0_ADDR - + HIL_REG0_PS3_REGISTER_F_BASEADDR), + PS3_REG_OFFSET_HIL_ADVICE2DIRECT_CNT1 = + (HIL_REG0_PS3_REGISTER_F_PS3_HIL_ADVICE2DIRECT_CNT1_ADDR - + HIL_REG0_PS3_REGISTER_F_BASEADDR), + PS3_REG_OFFSET_HIL_ADVICE2DIRECT_CNT2 = + (HIL_REG0_PS3_REGISTER_F_PS3_HIL_ADVICE2DIRECT_CNT2_ADDR - + HIL_REG0_PS3_REGISTER_F_BASEADDR), + PS3_REG_OFFSET_HIL_ADVICE2DIRECT_CNT3 = + (HIL_REG0_PS3_REGISTER_F_PS3_HIL_ADVICE2DIRECT_CNT3_ADDR - + HIL_REG0_PS3_REGISTER_F_BASEADDR), + PS3_REG_OFFSET_HIL_ADVICE2DIRECT_CNT_ALL = + (HIL_REG0_PS3_REGISTER_F_PS3_HIL_ADVICE2DIRECT_CNT_ALL_ADDR - + HIL_REG0_PS3_REGISTER_F_BASEADDR), + PS3_REG_OFFSET_IRQ_STATUS_RPT = + (HIL_REG0_PS3_REGISTER_F_PS3_IRQ_STATUS_RPT_ADDR - + HIL_REG0_PS3_REGISTER_F_BASEADDR), + PS3_REG_OFFSET_DUMP_CTRL = (HIL_REG0_PS3_REGISTER_F_PS3_DUMP_CTRL_ADDR - + HIL_REG0_PS3_REGISTER_F_BASEADDR), + PS3_REG_OFFSET_DUMP_CTRl_IRQ_CLEAR = + (HIL_REG0_PS3_REGISTER_F_PS3_DUMP_CTRL_IRQ_CLEAR_ADDR - + HIL_REG0_PS3_REGISTER_F_BASEADDR), + PS3_REG_OFFSET_DUMP_CTRl_IRQ_MASK = + (HIL_REG0_PS3_REGISTER_F_PS3_DUMP_CTRL_IRQ_MASK_ADDR - + HIL_REG0_PS3_REGISTER_F_BASEADDR), + PS3_REG_OFFSET_DUMP_STATUS = + (HIL_REG0_PS3_REGISTER_F_PS3_DUMP_STATUS_ADDR - + HIL_REG0_PS3_REGISTER_F_BASEADDR), + PS3_REG_OFFSET_DUMP_DATA_SIZE = + (HIL_REG0_PS3_REGISTER_F_PS3_DUMP_DATA_SIZE_ADDR - + HIL_REG0_PS3_REGISTER_F_BASEADDR), + PS3_REG_OFFSET_CMD_TRIGGER = + (HIL_REG0_PS3_REGISTER_F_PS3_CMD_TRIGGER_ADDR - + HIL_REG0_PS3_REGISTER_F_BASEADDR), + PS3_REG_OFFSET_CMD_TRIGGER_IRQ_CLEAR = + (HIL_REG0_PS3_REGISTER_F_PS3_CMD_TRIGGER_IRQ_CLEAR_ADDR - + HIL_REG0_PS3_REGISTER_F_BASEADDR), + PS3_REG_OFFSET_CMD_TRIGGER_IRQ_MASK = + (HIL_REG0_PS3_REGISTER_F_PS3_CMD_TRIGGER_IRQ_MASK_ADDR - + HIL_REG0_PS3_REGISTER_F_BASEADDR), + PS3_REG_OFFSET_REG_CMD_STATE = + (HIL_REG0_PS3_REGISTER_F_PS3_REG_CMD_STATE_ADDR - + HIL_REG0_PS3_REGISTER_F_BASEADDR), + PS3_REG_OFFSET_SOFTRESET_COUNTER = + (HIL_REG0_PS3_REGISTER_F_PS3_SOFTRESET_COUNTER_ADDR - + HIL_REG0_PS3_REGISTER_F_BASEADDR), + PS3_REG_OFFSET_DEBUG0 = (HIL_REG0_PS3_REGISTER_F_PS3_DEBUG0_ADDR - + HIL_REG0_PS3_REGISTER_F_BASEADDR), + PS3_REG_OFFSET_DEBUG0_IRQ_CLEAR = + (HIL_REG0_PS3_REGISTER_F_PS3_DEBUG0_IRQ_CLEAR_ADDR - + HIL_REG0_PS3_REGISTER_F_BASEADDR), + PS3_REG_OFFSET_DEBUG0_IRQ_MASK = + (HIL_REG0_PS3_REGISTER_F_PS3_DEBUG0_IRQ_MASK_ADDR - + HIL_REG0_PS3_REGISTER_F_BASEADDR), + PS3_REG_OFFSET_DEBUG1 = (HIL_REG0_PS3_REGISTER_F_PS3_DEBUG1_ADDR - + HIL_REG0_PS3_REGISTER_F_BASEADDR), + PS3_REG_OFFSET_DEBUG1_IRQ_CLEAR = + (HIL_REG0_PS3_REGISTER_F_PS3_DEBUG1_IRQ_CLEAR_ADDR - + HIL_REG0_PS3_REGISTER_F_BASEADDR), + PS3_REG_OFFSET_DEBUG1_IRQ_MASK = + (HIL_REG0_PS3_REGISTER_F_PS3_DEBUG1_IRQ_MASK_ADDR - + HIL_REG0_PS3_REGISTER_F_BASEADDR), + PS3_REG_OFFSET_DEBUG2 = (HIL_REG0_PS3_REGISTER_F_PS3_DEBUG2_ADDR - + HIL_REG0_PS3_REGISTER_F_BASEADDR), + PS3_REG_OFFSET_DEBUG2_IRQ_CLEAR = + (HIL_REG0_PS3_REGISTER_F_PS3_DEBUG2_IRQ_CLEAR_ADDR - + HIL_REG0_PS3_REGISTER_F_BASEADDR), + PS3_REG_OFFSET_DEBUG2_IRQ_MASK = + (HIL_REG0_PS3_REGISTER_F_PS3_DEBUG2_IRQ_MASK_ADDR - + HIL_REG0_PS3_REGISTER_F_BASEADDR), + PS3_REG_OFFSET_DEBUG3 = (HIL_REG0_PS3_REGISTER_F_PS3_DEBUG3_ADDR - + HIL_REG0_PS3_REGISTER_F_BASEADDR), + PS3_REG_OFFSET_DEBUG3_IRQ_CLEAR = + (HIL_REG0_PS3_REGISTER_F_PS3_DEBUG3_IRQ_CLEAR_ADDR - + HIL_REG0_PS3_REGISTER_F_BASEADDR), + PS3_REG_OFFSET_DEBUG3_IRQ_MASK = + (HIL_REG0_PS3_REGISTER_F_PS3_DEBUG3_IRQ_MASK_ADDR - + HIL_REG0_PS3_REGISTER_F_BASEADDR), + PS3_REG_OFFSET_DEBUG4 = (HIL_REG0_PS3_REGISTER_F_PS3_DEBUG4_ADDR - + HIL_REG0_PS3_REGISTER_F_BASEADDR), + PS3_REG_OFFSET_DEBUG4_IRQ_CLEAR = + (HIL_REG0_PS3_REGISTER_F_PS3_DEBUG4_IRQ_CLEAR_ADDR - + HIL_REG0_PS3_REGISTER_F_BASEADDR), + PS3_REG_OFFSET_DEBUG4_IRQ_MASK = + (HIL_REG0_PS3_REGISTER_F_PS3_DEBUG4_IRQ_MASK_ADDR - + HIL_REG0_PS3_REGISTER_F_BASEADDR), + PS3_REG_OFFSET_DEBUG5 = (HIL_REG0_PS3_REGISTER_F_PS3_DEBUG5_ADDR - + HIL_REG0_PS3_REGISTER_F_BASEADDR), + PS3_REG_OFFSET_DEBUG6 = (HIL_REG0_PS3_REGISTER_F_PS3_DEBUG6_ADDR - + HIL_REG0_PS3_REGISTER_F_BASEADDR), + PS3_REG_OFFSET_DEBUG7 = (HIL_REG0_PS3_REGISTER_F_PS3_DEBUG7_ADDR - + HIL_REG0_PS3_REGISTER_F_BASEADDR), + PS3_REG_OFFSET_DEBUG8 = (HIL_REG0_PS3_REGISTER_F_PS3_DEBUG8_ADDR - + HIL_REG0_PS3_REGISTER_F_BASEADDR), + PS3_REG_OFFSET_DEBUG9 = (HIL_REG0_PS3_REGISTER_F_PS3_DEBUG9_ADDR - + HIL_REG0_PS3_REGISTER_F_BASEADDR), + PS3_REG_OFFSET_DEBUG10 = (HIL_REG0_PS3_REGISTER_F_PS3_DEBUG10_ADDR - + HIL_REG0_PS3_REGISTER_F_BASEADDR), + PS3_REG_OFFSET_DEBUG11 = (HIL_REG0_PS3_REGISTER_F_PS3_DEBUG11_ADDR - + HIL_REG0_PS3_REGISTER_F_BASEADDR), + PS3_REG_OFFSET_DEBUG12 = (HIL_REG0_PS3_REGISTER_F_PS3_DEBUG12_ADDR - + HIL_REG0_PS3_REGISTER_F_BASEADDR), + PS3_REG_SESSION_ADDR = + (HIL_REG0_PS3_REGISTER_F_PS3_SESSIONCMD_ADDR_ADDR - + HIL_REG0_PS3_REGISTER_F_BASEADDR), + PS3_REG_SESSION_ADDR_IRQ_CLEAR = + (HIL_REG0_PS3_REGISTER_F_PS3_SESSIONCMD_ADDR_IRQ_CLEAR_ADDR - + HIL_REG0_PS3_REGISTER_F_BASEADDR), + PS3_REG_SESSION_ADDR_IRQ_MASK = + (HIL_REG0_PS3_REGISTER_F_PS3_SESSIONCMD_ADDR_IRQ_MASK_ADDR - + HIL_REG0_PS3_REGISTER_F_BASEADDR), +}; +#endif diff --git a/drivers/scsi/linkdata/ps3stor/ps3_instance_manager.c b/drivers/scsi/linkdata/ps3stor/ps3_instance_manager.c new file mode 100644 index 0000000000000000000000000000000000000000..85a6169f65a031b7a86a3ca774ce0f66c318c82b --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/ps3_instance_manager.c @@ -0,0 +1,692 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "ps3_instance_manager.h" +#include "ps3_driver_log.h" +#include "ps3_ioc_manager.h" + +#ifndef _WINDOWS +#include "ps3_debug.h" +#include +#include +#endif +#include "ps3_cli_debug.h" + +#ifdef PS3_HARDWARE_HAPS_V200 +#define PS3_INSTANCE_WAIT_TO_OPERATIONAL_MAX_SECS (180) +#define PS3_INSTANCE_WAIT_TO_NORMAL_MAX_SECS (3600) +#define PS3_INSTANCE_STATE_CHECK_INTERVAL_MS (1000) +#define PS3_INSTANCE_STATE_CHECK_NORMAL_INTERVAL_MS (3000) +#else +#define PS3_INSTANCE_WAIT_TO_OPERATIONAL_MAX_SECS (180) +#define PS3_INSTANCE_WAIT_TO_NORMAL_MAX_SECS (420) +#define PS3_INSTANCE_STATE_CHECK_INTERVAL_MS (1000) +#endif + +struct ps3_host_info g_ps3_host_info; + +#ifndef _WINDOWS +struct ps3_mgmt_info *ps3_mgmt_info_get(void) +{ + static struct ps3_mgmt_info g_mgmt_info; + + return &g_mgmt_info; +} + +struct ps3_instance *ps3_instance_lookup(unsigned short host_no) +{ + struct ps3_instance *instance = NULL; + struct list_head *pitem = NULL; + + list_for_each(pitem, &ps3_mgmt_info_get()->instance_list_head) { + instance = list_entry(pitem, struct ps3_instance, list_item); + if (instance->host->host_no == host_no) + return instance; + } + + return NULL; +} + +int ps3_instance_add(struct ps3_instance *instance) +{ + int ret = -PS3_FAILED; + struct list_head *pitem = NULL; + struct ps3_instance *peer_instance = NULL; + + if (instance == NULL) + return ret; + + ps3_mutex_lock(&ps3_mgmt_info_get()->ps3_mgmt_lock); + list_for_each(pitem, &ps3_mgmt_info_get()->instance_list_head) { + if (pitem == &instance->list_item) { + pitem = NULL; + goto l_repeat_add; + } + } + + list_for_each_entry(peer_instance, + &ps3_mgmt_info_get()->instance_list_head, + list_item) { + if (peer_instance != NULL && + ps3_get_pci_domain(peer_instance->pdev) == + ps3_get_pci_domain(instance->pdev) && + peer_instance->pdev->bus->number == + instance->pdev->bus->number && + PCI_SLOT(peer_instance->pdev->devfn) == + PCI_SLOT(instance->pdev->devfn)) { + peer_instance->recovery_context->instance_change = 1; + mb(); /* in order to force CPU ordering */ + ps3_recovery_cancel_work_sync(peer_instance); + instance->peer_instance = peer_instance; + peer_instance->peer_instance = instance; + mb(); /* in order to force CPU ordering */ + peer_instance->recovery_context->instance_change = 0; + } + } + + list_add(&instance->list_item, + &ps3_mgmt_info_get()->instance_list_head); + mutex_init(&instance->state_machine.lock); + ps3_mutex_init(&instance->task_mgr_reset_lock); + ps3_mutex_init(&instance->task_abort_lock); + atomic_set(&instance->state_machine.state, PS3_INSTANCE_STATE_INIT); + instance->state_machine.is_load = PS3_FALSE; + instance->state_machine.is_pci_err_recovery = PS3_FALSE; + + ret = PS3_SUCCESS; +l_repeat_add: + ps3_mutex_unlock(&ps3_mgmt_info_get()->ps3_mgmt_lock); + if (pitem == NULL) + LOG_WARN("hno:%u repeat add instance!\n", PS3_HOST(instance)); + return ret; +} + +int ps3_instance_remove(struct ps3_instance *instance) +{ + struct ps3_instance *peer_instance = NULL; + + if (instance == NULL) + return -PS3_FAILED; + + ps3_mutex_lock(&ps3_mgmt_info_get()->ps3_mgmt_lock); + list_del(&instance->list_item); + instance->peer_instance = NULL; + list_for_each_entry(peer_instance, + &ps3_mgmt_info_get()->instance_list_head, + list_item) { + if (peer_instance != NULL && + ps3_get_pci_domain(peer_instance->pdev) == + ps3_get_pci_domain(instance->pdev) && + peer_instance->pdev->bus->number == + instance->pdev->bus->number && + PCI_SLOT(peer_instance->pdev->devfn) == + PCI_SLOT(instance->pdev->devfn)) { + peer_instance->recovery_context->instance_change = 1; + mb(); /* in order to force CPU ordering */ + ps3_recovery_cancel_work_sync(peer_instance); + peer_instance->peer_instance = NULL; + mb(); /* in order to force CPU ordering */ + peer_instance->recovery_context->instance_change = 0; + } + } + mutex_destroy(&instance->state_machine.lock); + ps3_mutex_destroy(&instance->task_mgr_reset_lock); + ps3_mutex_destroy(&instance->task_abort_lock); + ps3_mutex_unlock(&ps3_mgmt_info_get()->ps3_mgmt_lock); + + return PS3_SUCCESS; +} + +void ps3_mgmt_info_init(void) +{ + ps3_mutex_init(&ps3_mgmt_info_get()->ps3_mgmt_lock); + INIT_LIST_HEAD(&ps3_mgmt_info_get()->instance_list_head); + + ps3_cli_debug_init(); +} + +void ps3_mgmt_exit(void) +{ + ps3_mutex_destroy(&ps3_mgmt_info_get()->ps3_mgmt_lock); +} + +#endif +void ps3_instance_init(struct ps3_instance *instance) +{ + memset(instance, 0, sizeof(struct ps3_instance)); + ps3_mutex_init(&instance->state_machine.lock); + ps3_atomic_set(&instance->state_machine.state, PS3_INSTANCE_STATE_INIT); + instance->state_machine.is_load = PS3_FALSE; + instance->state_machine.is_poweroff = PS3_FALSE; + instance->state_machine.is_pci_err_recovery = PS3_FALSE; +} + +int ps3_instance_state_transfer(struct ps3_instance *instance, + unsigned int exp_cur_state, + unsigned int dest_state) +{ + unsigned int cur_state = 0; + + ps3_mutex_lock(&instance->state_machine.lock); + cur_state = ps3_atomic_read(&instance->state_machine.state); + if (cur_state != exp_cur_state) + goto l_fail; + + switch (cur_state) { + case PS3_INSTANCE_STATE_INIT: + if ((dest_state == PS3_INSTANCE_STATE_READY) || + (dest_state == PS3_INSTANCE_STATE_RECOVERY)) { + goto l_success; + } else { + goto l_fail; + } + break; + case PS3_INSTANCE_STATE_READY: + if ((dest_state == PS3_INSTANCE_STATE_PRE_OPERATIONAL) || + (dest_state == PS3_INSTANCE_STATE_RECOVERY) || + (dest_state == PS3_INSTANCE_STATE_PCIE_RECOVERY)) { + goto l_success; + } else { + goto l_fail; + } + break; + case PS3_INSTANCE_STATE_PRE_OPERATIONAL: + if ((dest_state == PS3_INSTANCE_STATE_OPERATIONAL) || + (dest_state == PS3_INSTANCE_STATE_RECOVERY) || + (dest_state == PS3_INSTANCE_STATE_SOFT_RECOVERY)) { + goto l_success; + } else { + goto l_fail; + } + break; + case PS3_INSTANCE_STATE_OPERATIONAL: + if ((dest_state == PS3_INSTANCE_STATE_SOFT_RECOVERY) || + (dest_state == PS3_INSTANCE_STATE_RECOVERY) || + (dest_state == PS3_INSTANCE_STATE_SUSPEND) || + (dest_state == PS3_INSTANCE_STATE_QUIT)) { + goto l_success; + } else { + goto l_fail; + } + break; + case PS3_INSTANCE_STATE_SOFT_RECOVERY: + if ((dest_state == PS3_INSTANCE_STATE_PRE_OPERATIONAL) || + (dest_state == PS3_INSTANCE_STATE_RECOVERY)) { + goto l_success; + } else { + goto l_fail; + } + break; + case PS3_INSTANCE_STATE_RECOVERY: + if ((dest_state == PS3_INSTANCE_STATE_READY) || + (dest_state == PS3_INSTANCE_STATE_DEAD)) { + goto l_success; + } else { + goto l_fail; + } + break; + case PS3_INSTANCE_STATE_SUSPEND: + if (dest_state == PS3_INSTANCE_STATE_READY) + goto l_success; + else + goto l_fail; + break; + case PS3_INSTANCE_STATE_DEAD: + if (dest_state == PS3_INSTANCE_STATE_QUIT) + goto l_success; + else + goto l_fail; + break; + case PS3_INSTANCE_STATE_QUIT: + goto l_fail; + case PS3_INSTANCE_STATE_PCIE_RECOVERY: + if (dest_state == PS3_INSTANCE_STATE_RECOVERY) + goto l_success; + else + goto l_fail; + default: + goto l_fail; + } + +l_fail: + ps3_mutex_unlock(&instance->state_machine.lock); + LOG_ERROR("hno:%u state transfer NOK! [exp_cur_state:%s][cur_state:%s][dest_state:%s]\n", + PS3_HOST(instance), namePS3InstanceState(exp_cur_state), + namePS3InstanceState(cur_state), + namePS3InstanceState(dest_state)); + return -PS3_FAILED; +l_success: + ps3_atomic_set(&instance->state_machine.state, dest_state); + ps3_mutex_unlock(&instance->state_machine.lock); + LOG_INFO("hno:%u state transfer from %s to %s!\n", PS3_HOST(instance), + namePS3InstanceState(cur_state), + namePS3InstanceState(dest_state)); + return PS3_SUCCESS; +} +int ps3_instance_no_lock_state_transfer(struct ps3_instance *instance, + unsigned int dest_state) +{ + unsigned int cur_state = 0; + + cur_state = ps3_atomic_read(&instance->state_machine.state); + if (cur_state == dest_state) + goto l_success; + switch (cur_state) { + case PS3_INSTANCE_STATE_INIT: + if ((dest_state == PS3_INSTANCE_STATE_READY) || + (dest_state == PS3_INSTANCE_STATE_RECOVERY)) { + goto l_success; + } else { + goto l_fail; + } + break; + case PS3_INSTANCE_STATE_READY: + if ((dest_state == PS3_INSTANCE_STATE_PRE_OPERATIONAL) || + (dest_state == PS3_INSTANCE_STATE_RECOVERY)) { + goto l_success; + } else { + goto l_fail; + } + break; + case PS3_INSTANCE_STATE_PRE_OPERATIONAL: + if ((dest_state == PS3_INSTANCE_STATE_OPERATIONAL) || + (dest_state == PS3_INSTANCE_STATE_RECOVERY) || + (dest_state == PS3_INSTANCE_STATE_SOFT_RECOVERY)) { + goto l_success; + } else { + goto l_fail; + } + break; + case PS3_INSTANCE_STATE_OPERATIONAL: + if ((dest_state == PS3_INSTANCE_STATE_SOFT_RECOVERY) || + (dest_state == PS3_INSTANCE_STATE_RECOVERY) || + (dest_state == PS3_INSTANCE_STATE_SUSPEND) || + (dest_state == PS3_INSTANCE_STATE_QUIT)) { + goto l_success; + } else { + goto l_fail; + } + break; + case PS3_INSTANCE_STATE_SOFT_RECOVERY: + if ((dest_state == PS3_INSTANCE_STATE_PRE_OPERATIONAL) || + (dest_state == PS3_INSTANCE_STATE_RECOVERY)) { + goto l_success; + } else { + goto l_fail; + } + break; + case PS3_INSTANCE_STATE_RECOVERY: + if ((dest_state == PS3_INSTANCE_STATE_READY) || + (dest_state == PS3_INSTANCE_STATE_DEAD)) { + goto l_success; + } else { + goto l_fail; + } + break; + case PS3_INSTANCE_STATE_SUSPEND: + if (dest_state == PS3_INSTANCE_STATE_READY) + goto l_success; + else + goto l_fail; + break; + case PS3_INSTANCE_STATE_DEAD: + if (dest_state == PS3_INSTANCE_STATE_QUIT) + goto l_success; + else + goto l_fail; + break; + case PS3_INSTANCE_STATE_QUIT: + goto l_fail; + case PS3_INSTANCE_STATE_PCIE_RECOVERY: + if (dest_state == PS3_INSTANCE_STATE_RECOVERY) + goto l_success; + else + goto l_fail; + default: + goto l_fail; + } + +l_fail: + LOG_ERROR("hno:%u state transfer NOK! [cur_state:%s][dest_state:%s]\n", + PS3_HOST(instance), namePS3InstanceState(cur_state), + namePS3InstanceState(dest_state)); + return -PS3_FAILED; +l_success: + ps3_atomic_set(&instance->state_machine.state, dest_state); + LOG_INFO("hno:%u state transfer from %s to %s!\n", PS3_HOST(instance), + namePS3InstanceState(cur_state), + namePS3InstanceState(dest_state)); + return PS3_SUCCESS; +} + +void ps3_instance_state_transfer_to_dead_nolock(struct ps3_instance *instance) +{ + ps3_atomic_set(&instance->state_machine.state, PS3_INSTANCE_STATE_DEAD); +} + +void ps3_instance_state_transfer_to_dead(struct ps3_instance *instance) +{ + ps3_mutex_lock(&instance->state_machine.lock); + if (ps3_atomic_read(&instance->state_machine.state) != + PS3_INSTANCE_STATE_DEAD) { + ps3_atomic_set(&instance->state_machine.state, + PS3_INSTANCE_STATE_DEAD); + } + ps3_mutex_unlock(&instance->state_machine.lock); +} + +void ps3_instance_state_transfer_to_pcie_recovery(struct ps3_instance *instance) +{ + ps3_mutex_lock(&instance->state_machine.lock); + if (ps3_atomic_read(&instance->state_machine.state) != + PS3_INSTANCE_STATE_PCIE_RECOVERY) { + ps3_atomic_set(&instance->state_machine.state, + PS3_INSTANCE_STATE_PCIE_RECOVERY); + } + ps3_mutex_unlock(&instance->state_machine.lock); +} + +void ps3_instance_state_transfer_to_quit(struct ps3_instance *instance) +{ + ps3_atomic_set(&instance->state_machine.state, PS3_INSTANCE_STATE_QUIT); +} + +void ps3_instance_state_transfer_to_suspend(struct ps3_instance *instance) +{ + ps3_atomic_set(&instance->state_machine.state, + PS3_INSTANCE_STATE_SUSPEND); +} + +void ps3_instance_state_transition_to_recovery(struct ps3_instance *instance) +{ + ps3_atomic_set(&instance->state_machine.state, + PS3_INSTANCE_STATE_RECOVERY); +} + +int ps3_instance_wait_for_hard_reset_flag_done(struct ps3_instance *instance) +{ + unsigned int wait_cnt = PS3_INSTANCE_WAIT_TO_OPERATIONAL_MAX_SECS * 2; + int cur_state = PS3_INSTANCE_STATE_INIT; + unsigned int idx = 0; + int ret = PS3_SUCCESS; + + for (idx = 0; idx < wait_cnt; idx++) { + cur_state = ps3_atomic_read(&instance->state_machine.state); + if ((cur_state == PS3_INSTANCE_STATE_DEAD) || + (cur_state == PS3_INSTANCE_STATE_QUIT)) { + ret = -PS3_FAILED; + goto l_out; + } + ps3_mutex_lock(&instance->state_machine.lock); + if (instance->recovery_context->host_reset_state == + PS3_HOST_RESET_HARD_RESET_DONE && + ps3_atomic_read(&instance->state_machine.state) == + PS3_INSTANCE_STATE_OPERATIONAL) { + if (atomic_read( + &instance->cmd_statistics.io_outstanding) != + 0) { + ret = -PS3_RETRY; + } + ps3_mutex_unlock(&instance->state_machine.lock); + goto l_out; + } + ps3_mutex_unlock(&instance->state_machine.lock); + + ps3_msleep(PS3_INSTANCE_STATE_CHECK_INTERVAL_MS); + } + + if (idx >= wait_cnt) { + LOG_WARN("hno:%u wait pre_operational timeout!\n", + PS3_HOST(instance)); + } + ps3_mutex_lock(&instance->state_machine.lock); + if (instance->recovery_context->host_reset_state != + PS3_HOST_RESET_HARD_RESET_DONE) { + LOG_WARN("hno:%u wait host reset hard reset done NOK!\n", + PS3_HOST(instance)); + ret = -PS3_RETRY; + } + ps3_mutex_unlock(&instance->state_machine.lock); +l_out: + LOG_INFO("hno:%u wait hard reset flag %d!\n", PS3_HOST(instance), ret); + return ret; +} + +int ps3_instance_wait_for_dead_or_pre_operational(struct ps3_instance *instance) +{ + unsigned int wait_cnt = PS3_INSTANCE_WAIT_TO_OPERATIONAL_MAX_SECS * 3; + int cur_state = PS3_INSTANCE_STATE_INIT; + unsigned int idx = 0; + + LOG_INFO("hno:%u wait dead or pre_operational begin\n", + PS3_HOST(instance)); + for (idx = 0; idx < wait_cnt; idx++) { + cur_state = ps3_atomic_read(&instance->state_machine.state); + if ((cur_state == PS3_INSTANCE_STATE_PRE_OPERATIONAL) || + (cur_state == PS3_INSTANCE_STATE_OPERATIONAL) || + (cur_state == PS3_INSTANCE_STATE_DEAD) || + (cur_state == PS3_INSTANCE_STATE_QUIT)) { + break; + } + + ps3_msleep(PS3_INSTANCE_STATE_CHECK_INTERVAL_MS); + } + + if (idx >= wait_cnt) { + LOG_WARN("hno:%u wait dead or pre_operational timeout!\n", + PS3_HOST(instance)); + } + + if ((cur_state != PS3_INSTANCE_STATE_PRE_OPERATIONAL) && + (cur_state != PS3_INSTANCE_STATE_OPERATIONAL) && + (cur_state != PS3_INSTANCE_STATE_DEAD)) { + LOG_WARN("hno:%u wait dead or pre_operational NOK!\n", + PS3_HOST(instance)); + return -PS3_FAILED; + } + + LOG_INFO("hno:%u wait dead or pre_operational success!\n", + PS3_HOST(instance)); + return PS3_SUCCESS; +} + +int ps3_instance_wait_for_operational(struct ps3_instance *instance, + unsigned char is_hardreset) +{ + unsigned int wait_cnt = PS3_INSTANCE_WAIT_TO_OPERATIONAL_MAX_SECS * 3; + int cur_state = PS3_INSTANCE_STATE_INIT; + unsigned int idx = 0; + int recovery_state = PS3_RESET_LOG_INTERVAL; + + for (idx = 0; idx < wait_cnt; idx++) { + if (!instance->state_machine.is_load) { + LOG_INFO("hno:%u instance state not is_load\n", + PS3_HOST(instance)); + break; + } + + ps3_mutex_lock(&instance->state_machine.lock); + cur_state = ps3_atomic_read(&instance->state_machine.state); + recovery_state = instance->recovery_context->recovery_state; + if (((cur_state == PS3_INSTANCE_STATE_OPERATIONAL) || + (cur_state == PS3_INSTANCE_STATE_DEAD) || + (cur_state == PS3_INSTANCE_STATE_QUIT)) && + (is_hardreset ? + (recovery_state == PS3_HARD_RECOVERY_FINISH) : + PS3_TRUE)) { + LOG_INFO( + "hno:%u wait break, cur_state: %s, recovery_state: %d, is_hardreset: %d\n", + PS3_HOST(instance), + namePS3InstanceState(cur_state), recovery_state, + is_hardreset); + ps3_mutex_unlock(&instance->state_machine.lock); + break; + } + ps3_mutex_unlock(&instance->state_machine.lock); + + ps3_msleep(PS3_INSTANCE_STATE_CHECK_INTERVAL_MS); + } + + if (idx >= wait_cnt) { + LOG_WARN("hno:%u wait operational timeout!\n", + PS3_HOST(instance)); + } + + if (cur_state != PS3_INSTANCE_STATE_OPERATIONAL) { + LOG_WARN( + "hno:%u wait operational NOK, cur_state: %s, recovery_state: %d\n", + PS3_HOST(instance), namePS3InstanceState(cur_state), + recovery_state); + return -PS3_FAILED; + } + + LOG_INFO("hno:%u wait operational success!\n", PS3_HOST(instance)); + return PS3_SUCCESS; +} + +int ps3_instance_wait_for_normal(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + unsigned int wait_cnt = PS3_INSTANCE_WAIT_TO_NORMAL_MAX_SECS; + int cur_state = PS3_INSTANCE_STATE_INIT; + unsigned int idx = 0; + unsigned char is_pci_err_recovery = PS3_FALSE; + + for (idx = 0; idx < wait_cnt; idx++) { + is_pci_err_recovery = ps3_pci_err_recovery_get(instance); + cur_state = ps3_atomic_read(&instance->state_machine.state); + if (cur_state == PS3_INSTANCE_STATE_DEAD && + PS3_IOC_STATE_HALT_SUPPORT(instance) && + PS3_HALT_CLI_SUPPORT(instance)) { + goto l_out; + } + + if (ps3_state_is_normal(cur_state) || + cur_state == PS3_INSTANCE_STATE_QUIT || + cur_state == PS3_INSTANCE_STATE_DEAD || + is_pci_err_recovery) { + LOG_DEBUG( + "hno:%u cur state: %d, pci err recovery: %d\n", + PS3_HOST(instance), cur_state, + is_pci_err_recovery); + break; + } + + if (!(idx % PS3_RESET_LOG_INTERVAL)) { + LOG_DEBUG( + "hno:%u state[%s] waiting for reset to finish\n", + PS3_HOST(instance), + namePS3InstanceState(cur_state)); + } +#ifdef PS3_HARDWARE_HAPS_V200 + ps3_msleep(PS3_INSTANCE_STATE_CHECK_NORMAL_INTERVAL_MS); + + if (ps3_get_wait_cli_flag()) { + LOG_DEBUG("hno:%u not wait cmd\n", PS3_HOST(instance)); + break; + } +#else + ps3_msleep(PS3_INSTANCE_STATE_CHECK_INTERVAL_MS); +#endif + } + + if (!ps3_state_is_normal(cur_state) || is_pci_err_recovery) { + LOG_WARN( + "hno:%u cannot handle cmd, driver state: %s, pci recovery: %d\n", + PS3_HOST(instance), namePS3InstanceState(cur_state), + is_pci_err_recovery); + ret = -PS3_FAILED; + goto l_out; + } + +l_out: + return ret; +} + +int ps3_recovery_state_wait_for_normal(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + unsigned int wait_cnt = PS3_INSTANCE_WAIT_TO_NORMAL_MAX_SECS; + int recovery_state = PS3_RESET_LOG_INTERVAL; + unsigned int idx = 0; + unsigned char is_pci_err_recovery = PS3_FALSE; + + for (idx = 0; idx < wait_cnt; idx++) { + recovery_state = instance->recovery_context->recovery_state; + is_pci_err_recovery = ps3_pci_err_recovery_get(instance); + + if (recovery_state == PS3_HARD_RECOVERY_FINISH || + recovery_state == PS3_SOFT_RECOVERY_PROBE_PROCESS || + is_pci_err_recovery) { + LOG_DEBUG( + "hno:%u recovery state: %d, pci err recovery: %d\n", + PS3_HOST(instance), recovery_state, + is_pci_err_recovery); + goto l_out; + } +#ifdef PS3_HARDWARE_HAPS_V200 + ps3_msleep(PS3_INSTANCE_STATE_CHECK_NORMAL_INTERVAL_MS); + + if (ps3_get_wait_cli_flag()) { + LOG_DEBUG("hno:%u not wait cmd\n", PS3_HOST(instance)); + break; + } +#else + ps3_msleep(PS3_INSTANCE_STATE_CHECK_INTERVAL_MS); +#endif + } + + ret = -PS3_FAILED; + LOG_WARN("hno:%u wait recovery time out, recovery_state: %d\n", + PS3_HOST(instance), recovery_state); +l_out: + if (is_pci_err_recovery) { + LOG_WARN("hno:%u cannot handle cmd, pci recovery: %d\n", + PS3_HOST(instance), is_pci_err_recovery); + ret = -PS3_FAILED; + } + return ret; +} + +void ps3_host_info_get(void) +{ +#if defined(CONFIG_X86) || defined(CONFIG_X86_64) + struct cpuinfo_x86 *cpu = NULL; + + memset(&g_ps3_host_info, 0, sizeof(struct ps3_host_info)); + cpu = &cpu_data(0); + g_ps3_host_info.machine = PS3_HOST_MACHINE_X86; + if (strstr(cpu->x86_vendor_id, "Intel")) + g_ps3_host_info.vendor = PS3_HOST_VENDOR_INTEL; + else if (strstr(cpu->x86_vendor_id, "Hygon")) + g_ps3_host_info.vendor = PS3_HOST_VENDOR_HYGON; + else if (strstr(cpu->x86_vendor_id, "AMD")) + g_ps3_host_info.vendor = PS3_HOST_VENDOR_AMD; + g_ps3_host_info.processor_cnt = num_online_cpus(); +#else + memset(&g_ps3_host_info, 0, sizeof(struct ps3_host_info)); +#endif + + memset(g_ps3_host_info.release, 0, SYS_INFO_LEN + 1); + snprintf(g_ps3_host_info.release, SYS_INFO_LEN, "%s", + utsname()->release); + + LOG_DEBUG( + "host info: machine=%u,vendor=%u,processor_cnt=%u release=%s\n", + g_ps3_host_info.machine, g_ps3_host_info.vendor, + g_ps3_host_info.processor_cnt, g_ps3_host_info.release); +} + +unsigned short ps3_host_vendor_get(void) +{ + return g_ps3_host_info.vendor; +} + +char *ps3_host_release_get(void) +{ + return g_ps3_host_info.release; +} + +unsigned char ps3_is_last_func(struct ps3_instance *instance) +{ + return (!ps3_ioc_multi_func_support(instance) || + (ps3_get_pci_function(instance->pdev) == PS3_FUNC_ID_1)); +} diff --git a/drivers/scsi/linkdata/ps3stor/ps3_instance_manager.h b/drivers/scsi/linkdata/ps3stor/ps3_instance_manager.h new file mode 100644 index 0000000000000000000000000000000000000000..22f90951b204b4ed91f1b639a84b11eaa4358694 --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/ps3_instance_manager.h @@ -0,0 +1,536 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _PS3_INSTANCE_MANAGER_H_ +#define _PS3_INSTANCE_MANAGER_H_ + +#ifndef _WINDOWS +#include +#include +#include +#include +#include +#include +#include +#include "ps3_sas_transport.h" +#include "ps3_device_manager_sas.h" +#else +#include "ps3_pci.h" + +#endif + +#include "ps3_inner_data.h" +#include "ps3_irq.h" +#include "ps3_cmd_channel.h" +#include "ps3_platform_utils.h" +#include "ps3_cmd_channel.h" +#include "ps3_inner_data.h" +#include "ps3_debug.h" +#include "ps3_irq.h" +#include "ps3_recovery.h" +#include "ps3_dump.h" +#include "ps3_cmd_stat_def.h" +#include "ps3_watchdog.h" +#include "ps3_qos.h" + +enum PS3_INSTANCE_STATE_TYPE { + PS3_INSTANCE_STATE_INIT = 0, + PS3_INSTANCE_STATE_READY = 1, + PS3_INSTANCE_STATE_PRE_OPERATIONAL = 2, + PS3_INSTANCE_STATE_OPERATIONAL = 3, + PS3_INSTANCE_STATE_SOFT_RECOVERY = 4, + PS3_INSTANCE_STATE_RECOVERY = 5, + PS3_INSTANCE_STATE_SUSPEND = 6, + PS3_INSTANCE_STATE_DEAD = 7, + PS3_INSTANCE_STATE_QUIT = 8, + PS3_INSTANCE_STATE_PCIE_RECOVERY = 9, + PS3_INSTANCE_STATE_COUNT = 10 +}; +enum PS3_DEVICE_ERR_HANDLE_STATE_TYPE { + PS3_DEVICE_ERR_STATE_NORMAL = 0, + PS3_DEVICE_ERR_STATE_CLEAN = 1, + PS3_DEVICE_ERR_STATE_INIT = 2, +}; + +struct ps3_instance_state_machine { + struct mutex lock; + atomic_t state; + unsigned char is_load; + unsigned char is_suspend; + unsigned char is_poweroff; + unsigned char is_pci_err_recovery; + unsigned char can_hostreset; +}; + +static inline const char *namePS3InstanceState(int s) +{ + static const char * const myNames[] = { + [PS3_INSTANCE_STATE_INIT] = "PS3_INSTANCE_STATE_INIT", + [PS3_INSTANCE_STATE_READY] = "PS3_INSTANCE_STATE_READY", + [PS3_INSTANCE_STATE_PRE_OPERATIONAL] = + "PS3_INSTANCE_STATE_PRE_OPERATIONAL", + [PS3_INSTANCE_STATE_OPERATIONAL] = + "PS3_INSTANCE_STATE_OPERATIONAL", + [PS3_INSTANCE_STATE_SOFT_RECOVERY] = + "PS3_INSTANCE_STATE_SOFT_RECOVERY", + [PS3_INSTANCE_STATE_RECOVERY] = "PS3_INSTANCE_STATE_RECOVERY", + [PS3_INSTANCE_STATE_SUSPEND] = "PS3_INSTANCE_STATE_SUSPEND", + [PS3_INSTANCE_STATE_DEAD] = "PS3_INSTANCE_STATE_DEAD", + [PS3_INSTANCE_STATE_QUIT] = "PS3_INSTANCE_STATE_QUIT", + [PS3_INSTANCE_STATE_PCIE_RECOVERY] = + "PS3_INSTANCE_STATE_PCIE_RECOVERY" + }; + + if (s >= PS3_INSTANCE_STATE_COUNT) + return "PS3_INSTANCE_STATE_INVALID"; + + return myNames[s]; +} + +struct ps3_recovery_function { + int (*recovery_handle_cb)(struct ps3_instance *instance, + unsigned char reason); + int (*hardreset_handle_pre_cb)(struct ps3_instance *instance); + int (*hardreset_handle_wait_ready_cb)(struct ps3_instance *instance); + int (*hardreset_handle_init_running_cb)(struct ps3_instance *instance); + int (*hardreset_handle_post_cb)(struct ps3_instance *instance); + int (*hardreset_handle_finish_cb)(struct ps3_instance *instance); + int (*hardreset_handle_offline_cb)(struct ps3_instance *instance); + int (*softreset_handle_pre_cb)(struct ps3_instance *instance); + int (*softreset_handle_post_cb)(struct ps3_instance *instance); + int (*halt_handle_cb)(struct ps3_instance *instance); +}; + +struct ps3_instance { + struct list_head list_item; + struct ps3_instance *peer_instance; +#ifndef _WINDOWS + struct pci_dev *pdev; + struct Scsi_Host *host; +#else + unsigned long bus_number; + struct ps3_pci_context pci_dev_context; +#endif + struct Ps3Fifo __iomem *reg_set; + atomic_t watchdog_reg_read_fail_count; + + struct ps3_cmd_context cmd_context; + struct ps3_irq_context irq_context; + struct ps3_dev_context dev_context; +#ifndef _WINDOWS + struct ps3_sas_dev_context sas_dev_context; +#endif + struct ps3_event_context event_context; + struct ps3_webSubscribe_context webSubscribe_context; + + struct ps3_fault_context fault_context; + + struct ps3_watchdog_context watchdog_context; + struct ps3_dump_context dump_context; + struct ps3_debug_context debug_context; + struct ps3_cmd_statistics_context cmd_statistics; + struct PS3IocCtrlInfo ctrl_info; + struct PS3IocCtrlInfo *ctrl_info_buf; + +#ifdef _WINDOWS + atomic_t ioctl_count; +#else + struct semaphore ioctl_sem; +#endif + struct ps3_ioc_adp_template *ioc_adpter; + struct ps3_cmd_attr_context cmd_attr; + struct PS3MgrEvent event_req_info; + + spinlock_t req_queue_lock; + + dma_addr_t ctrl_info_buf_h; + dma_addr_t drv_info_buf_phys; + unsigned char *drv_info_buf; + dma_addr_t host_mem_info_buf_phys; + unsigned char *host_mem_info_buf; + unsigned int max_mgr_cmd_total_count; + unsigned int max_mgr_cmd_count; + unsigned int max_task_cmd_count; + unsigned int min_intr_count; + unsigned char reserve[4]; + unsigned int reply_fifo_depth_addition; + + unsigned long reg_bar; + unsigned char is_support_sync_cache; + unsigned char is_use_frontend_prp; + unsigned char dma_mask; + unsigned char is_support_jbod; + unsigned char use_clusting; + unsigned char is_adjust_register_count; + unsigned char is_scan_host_finish; + unsigned char is_probe_finish; + unsigned char is_probe_failed; + unsigned char is_suspend; + unsigned char is_resume; + unsigned char is_hard_reset; + unsigned char is_pci_reset; + unsigned char is_ioc_halt_support; + unsigned char is_shallow_soft_recovery_support; + unsigned char is_deep_soft_recovery_support; + unsigned char is_hard_recovery_support; + unsigned char is_halt_support_cli; + unsigned char is_half_hard_reset; + unsigned char is_host_added; + unsigned char is_need_event; + unsigned char is_raid1_direct_skip_mapblock_check; + unsigned char is_single_disk_raid0_direct_skip_strip_check; + unsigned char is_support_dump_ctrl; + unsigned char is_support_io_limit; + unsigned char task_manager_host_busy; + unsigned char hilMode; + unsigned char is_irq_prk_support; + unsigned char is_support_irq; + unsigned char is_raid; + unsigned char smp_affinity_enable; + unsigned char msix_combined; + unsigned char reserved[2]; + unsigned short unload_timeout; + unsigned short wait_ready_timeout; + unsigned short dev_id; + unsigned char dma_addr_bit_pos; + unsigned char pci_err_handle_state; + const char *product_model; + long long __percpu *scsi_cmd_deliver; + + unsigned long long ioc_fw_version; + struct mutex task_mgr_reset_lock; +#ifdef _WINDOWS + STOR_DPC device_reset_dpc; +#endif + unsigned char page_mode_change; + unsigned long long page_mode_addr_mask; + atomic_t is_err_scsi_processing; + atomic_t reg_op_count; + atomic_t host_reset_processing; + atomic_t watchdog_ref; + struct ps3_instance_state_machine state_machine; + struct ps3_recovery_context *recovery_context; + struct ps3_recovery_function recovery_function; +#ifndef _WINDOWS + struct work_struct recovery_irq_work; + struct workqueue_struct *recovery_irq_queue; + unsigned char recovery_irq_enable; + unsigned char is_print_special_log; + unsigned char reserved2[2]; + unsigned int hard_dog_mask; +#endif + atomic_t hardreset_event; + struct ps3_qos_context qos_context; + struct mutex task_abort_lock; + unsigned char r1x_mode; + unsigned long long start_pfn; + unsigned long long end_pfn; + unsigned long long so_start_addr; + unsigned long long so_end_addr; + int device_busy_threshold; + unsigned char is_pcie_err_detected; + unsigned char reserved3; +}; + +#ifndef _WINDOWS +struct ps3_mgmt_info { + struct mutex ps3_mgmt_lock; + struct list_head instance_list_head; +}; + +enum { + PS3_HOST_MACHINE_DEFAULT, + PS3_HOST_MACHINE_X86 = 0, + PS3_HOST_MACHINE_ARM, + PS3_HOST_MACHINE_MIPS, + PS3_HOST_MACHINE_COUNT, +}; + +enum { + PS3_HOST_VENDOR_DEFAULT, + PS3_HOST_VENDOR_INTEL = 0, + PS3_HOST_VENDOR_HYGON, + PS3_HOST_VENDOR_AMD, + PS3_HOST_VENDOR_COUNT, +}; + +#define SYS_INFO_LEN (64) +struct ps3_host_info { + unsigned char machine; + unsigned char vendor; + unsigned short cpu_cnt; + unsigned short core_cnt; + unsigned short processor_cnt; + char release[SYS_INFO_LEN + 1]; +}; + +struct ps3_mgmt_info *ps3_mgmt_info_get(void); + +struct ps3_instance *ps3_instance_lookup(unsigned short host_no); + +int ps3_instance_add(struct ps3_instance *instance); + +int ps3_instance_remove(struct ps3_instance *instance); + +void ps3_mgmt_info_init(void); + +void ps3_mgmt_exit(void); + +#endif + +void ps3_instance_init(struct ps3_instance *instance); + +int ps3_instance_state_transfer(struct ps3_instance *instance, + unsigned int exp_cur_state, + unsigned int dest_state); + +int ps3_instance_no_lock_state_transfer(struct ps3_instance *instance, + unsigned int dest_state); + +void ps3_instance_state_transfer_to_dead_nolock(struct ps3_instance *instance); + +void ps3_instance_state_transfer_to_dead(struct ps3_instance *instance); + +void ps3_instance_state_transfer_to_pcie_recovery(struct ps3_instance *instance); + +void ps3_instance_state_transfer_to_quit(struct ps3_instance *instance); + +void ps3_instance_state_transfer_to_suspend(struct ps3_instance *instance); + +void ps3_instance_state_transition_to_recovery(struct ps3_instance *instance); + +int ps3_instance_wait_for_operational(struct ps3_instance *instance, + unsigned char is_hardreset); + +int ps3_instance_wait_for_hard_reset_flag_done(struct ps3_instance *instance); + +int ps3_instance_wait_for_dead_or_pre_operational(struct ps3_instance *instance); + +static inline unsigned char +ps3_is_instance_state_normal(struct ps3_instance *instance, + unsigned char need_prk_err) +{ + int cur_state = ps3_atomic_read(&instance->state_machine.state); + + if (cur_state != PS3_INSTANCE_STATE_OPERATIONAL && + cur_state != PS3_INSTANCE_STATE_PRE_OPERATIONAL && + cur_state != PS3_INSTANCE_STATE_SOFT_RECOVERY) { + LOG_WARN_LIM_WITH_CHECK(instance, need_prk_err, + "hno:%u instance exception state %s\n", + PS3_HOST(instance), + namePS3InstanceState(cur_state)); + + return PS3_FALSE; + } + + return PS3_TRUE; +} + +static inline void ps3_pci_err_recovery_set(struct ps3_instance *instance, + unsigned char state) +{ + instance->state_machine.is_pci_err_recovery = state; +} + +static inline unsigned char +ps3_pci_err_recovery_get(struct ps3_instance *instance) +{ + return instance->state_machine.is_pci_err_recovery; +} + +static inline unsigned char +ps3_need_block_hard_reset_request(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + + if (ps3_pci_err_recovery_get(instance)) { + LOG_WARN_LIM( + "hno[%u], host in pci recovery during reset request\n", + PS3_HOST(instance)); + ret = -PS3_FAILED; + goto end; + } + ps3_mutex_lock(&instance->state_machine.lock); + if (instance->recovery_context->host_reset_state != + PS3_HOST_RESET_INIT) { + ps3_mutex_unlock(&instance->state_machine.lock); + LOG_WARN_LIM( + "hno[%u], host in host reset during reset request\n", + PS3_HOST(instance)); + ret = -PS3_FAILED; + goto end; + } + ps3_mutex_unlock(&instance->state_machine.lock); +end: + return ((ret == PS3_SUCCESS) ? PS3_FALSE : PS3_TRUE); +} + +static inline void +ps3_need_wait_hard_reset_request(struct ps3_instance *instance) +{ + do { + if (ps3_pci_err_recovery_get(instance)) { + LOG_WARN_LIM( + "hno[%u], host in pci recovery during reset request\n", + PS3_HOST(instance)); + ps3_msleep(100); + continue; + } + ps3_mutex_lock(&instance->state_machine.lock); + if (instance->recovery_context->host_reset_state != + PS3_HOST_RESET_INIT) { + ps3_mutex_unlock(&instance->state_machine.lock); + LOG_WARN_LIM( + "hno[%u], host in host reset during reset request\n", + PS3_HOST(instance)); + ps3_msleep(100); + continue; + } + ps3_mutex_unlock(&instance->state_machine.lock); + break; + } while (1); +} + +static inline unsigned char ps3_state_is_normal(int cur_state) +{ + return (cur_state != PS3_INSTANCE_STATE_OPERATIONAL && + cur_state != PS3_INSTANCE_STATE_PRE_OPERATIONAL) ? + PS3_FALSE : + PS3_TRUE; +} + +int ps3_instance_wait_for_normal(struct ps3_instance *instance); + +int ps3_recovery_state_wait_for_normal(struct ps3_instance *instance); + +struct ps3_ioc_adp_template { + int (*io_cmd_build)(struct ps3_cmd *cmd); + int (*mgr_cmd_build)(struct ps3_instance *instance, + struct ps3_cmd *cmd); + void (*init_cmd_send)(struct ps3_instance *instance, + struct PS3CmdWord *cmd_word); + void (*cmd_send)(struct ps3_instance *instance, + struct PS3CmdWord *cmd_word); + unsigned int (*ioc_state_get)(struct ps3_instance *instance); + int (*ioc_init_state_to_ready)(struct ps3_instance *instance); + int (*ioc_init_proc)(struct ps3_instance *instance); + void (*ioc_resource_prepare)(struct ps3_instance *instance); + int (*ioc_hard_reset)(struct ps3_instance *instance); + int (*ioc_shallow_soft_reset)(struct ps3_instance *instance); + int (*ioc_deep_soft_reset)(struct ps3_instance *instance); + int (*ioc_force_to_fault)(struct ps3_instance *instance); + int (*ioc_force_to_halt)(struct ps3_instance *instance); + int (*irq_init)(struct ps3_instance *instance); + void (*irq_enable)(struct ps3_instance *instance); + void (*irq_disable)(struct ps3_instance *instance); +#ifndef _WINDOWS + irqreturn_t (*isr)(int irq_no, void *data); + struct scsi_transport_template *(*sas_transport_get)(void); +#else + unsigned char (*isr)(void *instance, unsigned long irq_no); +#endif + void (*event_filter_table_get)(unsigned char *data); + void (*reg_write)(struct ps3_instance *instance, unsigned long long val, + void __iomem *reg); + unsigned long long (*reg_read)(struct ps3_instance *instance, + void __iomem *reg); + unsigned char (*is_need_direct_to_normal)(const struct ps3_cmd *cmd); + unsigned char (*max_replyq_count_get)(struct ps3_instance *instance, + unsigned int *max_replyq_count); + void (*check_vd_member_change)(struct ps3_instance *instance, + struct ps3_pd_entry *local_entry); + unsigned char (*scsih_stream_is_detect)(struct ps3_cmd *cmd); + unsigned char (*scsih_stream_is_direct)(const struct ps3_cmd *cmd); + unsigned int (*ioc_heartbeat_detect)(struct ps3_instance *instance); + void __iomem *(*reg_set)(struct pci_dev *pdev, unsigned long reg_bar); + unsigned char (*ioc_security_check)(struct ps3_instance *instance); + void (*io_cmd_rebuild)(struct ps3_cmd *cmd); + unsigned char (*rw_cmd_is_need_split)(struct ps3_cmd *cmd); + unsigned char (*write_direct_enable)(struct ps3_cmd *cmd); + unsigned char (*ssd_vd_qmask_calculate)(struct ps3_cmd *cmd); +}; +#define PS3_DEVICE_IS_SWITCH(id) \ + ((id == PCI_DEVICE_ID_PS3_SWITCH || \ + id == PCI_DEVICE_ID_PS3_SWITCH_FPGA)) + +#ifndef _WINDOWS +void ps3_ioc_adp_init(struct ps3_instance *instance, + const struct pci_device_id *id); + +void ps3_remove(struct pci_dev *pdev); + +#else +void ps3_ioc_adp_init(struct ps3_instance *instance); +#endif + +static inline int ps3_get_pci_function(struct pci_dev *pci) +{ + return PCI_FUNC(pci->devfn); +} + +static inline int ps3_get_pci_slot(struct pci_dev *pci) +{ + return PCI_SLOT(pci->devfn); +} + +static inline int ps3_get_pci_bus(struct pci_dev *pci) +{ + return pci->bus->number; +} + +static inline int ps3_get_pci_domain(struct pci_dev *pci) +{ + return pci_domain_nr(pci->bus); +} + +static inline bool ps3_is_latest_func(struct ps3_instance *instance) +{ + bool ret = PS3_TRUE; + struct ps3_instance *peer_instance = NULL; + + list_for_each_entry(peer_instance, + &ps3_mgmt_info_get()->instance_list_head, + list_item) { + if ((peer_instance != NULL) && + (ps3_get_pci_domain(peer_instance->pdev) == + ps3_get_pci_domain(instance->pdev)) && + (PCI_BUS_NUM(peer_instance->pdev->devfn) == + PCI_BUS_NUM(instance->pdev->devfn)) && + (PCI_SLOT(peer_instance->pdev->devfn) == + PCI_SLOT(instance->pdev->devfn)) && + (PCI_FUNC(peer_instance->pdev->devfn) != + PCI_FUNC(instance->pdev->devfn))) { + ret = PS3_FALSE; + } + } + + return ret; +} + +static inline void ps3_get_so_addr_ranger(struct ps3_instance *instance, + unsigned long long addr, + unsigned int offset) +{ + unsigned long long so_end_addr = (addr + offset) - 1; + + if (instance->so_start_addr == 0 && instance->so_end_addr == 0) { + instance->so_start_addr = addr; + instance->so_end_addr = so_end_addr; + goto l_out; + } + instance->so_start_addr = + ((addr < instance->so_start_addr) ? addr : + instance->so_start_addr); + instance->so_end_addr = + ((so_end_addr > instance->so_end_addr) ? so_end_addr : + instance->so_end_addr); +l_out: + return; +} + +void ps3_host_info_get(void); + +unsigned short ps3_host_vendor_get(void); + +char *ps3_host_release_get(void); + +unsigned char ps3_is_last_func(struct ps3_instance *instance); +#endif diff --git a/drivers/scsi/linkdata/ps3stor/ps3_io_trace.c b/drivers/scsi/linkdata/ps3stor/ps3_io_trace.c new file mode 100644 index 0000000000000000000000000000000000000000..04b0ef2939482585acc1819358045dec14e0eaf0 --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/ps3_io_trace.c @@ -0,0 +1,127 @@ +// SPDX-License-Identifier: GPL-2.0 +#ifndef _WINDOWS +#include +#include +#include +#endif +#include "ps3_instance_manager.h" +#include "ps3_scsih_cmd_parse.h" +#include "ps3_io_trace.h" +#include "ps3_kernel_version.h" + +static inline const char *ps3_io_trace_direct_name(enum ps3_io_trace_dtype type) +{ + static const char *const direct_name[] = { "cmd send", "cmd recv", + "unknown" }; + if (type >= PS3_IO_TRACE_DIRECT_COUNT) + return direct_name[PS3_IO_TRACE_DIRECT_COUNT]; + return direct_name[type]; +} +#ifndef _WINDOWS +static inline unsigned char ps3_is_scsi_read_cmd(struct scsi_cmnd *s_cmnd) +{ + return (s_cmnd != NULL && + s_cmnd->sc_data_direction == DMA_FROM_DEVICE && + (s_cmnd->cmnd[0] == READ_6 || s_cmnd->cmnd[0] == READ_10 || + s_cmnd->cmnd[0] == READ_12 || s_cmnd->cmnd[0] == READ_16)); +} + +static inline unsigned char ps3_is_scsi_write_cmd(struct scsi_cmnd *s_cmnd) +{ + return (s_cmnd != NULL && s_cmnd->sc_data_direction == DMA_TO_DEVICE && + (s_cmnd->cmnd[0] == WRITE_6 || s_cmnd->cmnd[0] == WRITE_10 || + s_cmnd->cmnd[0] == WRITE_12 || s_cmnd->cmnd[0] == WRITE_16)); +} + +static void ps3_scsi_sgl_crc(const struct ps3_cmd *cmd) +{ + unsigned int sge_count = 0; + unsigned int i = 0; + unsigned int crc = 0; + unsigned int seed = 0x1234; + char *pdata = NULL; + unsigned int buf_len = 0; + char buf[PS3_IO_TRACE_BUF_LEN] = { 0 }; + struct scatterlist *os_sgl = NULL; + struct ps3_instance *instance = cmd->instance; + struct scsi_cmnd *s_cmnd = cmd->scmd; + + if (instance->debug_context.io_trace_switch == PS3_FALSE) + goto l_out; + + sge_count = cmd->os_sge_map_count; + scsi_for_each_sg(s_cmnd, os_sgl, sge_count, i) { + pdata = (char *)sg_virt(os_sgl); + crc = crc32(seed, pdata, sg_dma_len(os_sgl)); + + memset(buf, '\0', sizeof(buf)); + buf_len = snprintf( + buf, PS3_IO_TRACE_BUF_LEN, + "hno:%u channel[%u], target[%u], trace_id[0x%llx], [%u of %u]sge\n" + "\tsge data addr[0x%llx] length[%u], D[%llx:%llx] CRC[0x%x]\n", + PS3_HOST(instance), s_cmnd->device->channel, + s_cmnd->device->id, cmd->trace_id, i + 1, sge_count, + (unsigned long long)sg_dma_address(os_sgl), + sg_dma_len(os_sgl), *(unsigned long long *)pdata, + *((unsigned long long *)(pdata + 8)), crc); + if (buf_len >= PS3_IO_TRACE_BUF_LEN) { + LOG_ERROR("buf_len > PS3_IO_TRACE_BUF_LEN\n"); + goto l_out; + } + DATA_DUMP(NULL, 0, buf); + } + +l_out: + return; +} +#endif +void ps3_scsih_io_trace(const struct ps3_cmd *cmd, enum ps3_io_trace_dtype type) +{ + int buf_len = 0; + char buf[PS3_IO_TRACE_BUF_LEN] = { 0 }; + unsigned long long lba = 0; + + if (cmd == NULL || cmd->scmd == NULL) { + LOG_WARN("cmd or scmd is null\n"); + goto l_out; + } + if (scsi_sg_count(cmd->scmd) == 0) + goto l_out; + +#if defined(PS3_CMD_CDB_CHECK) + if (cmd->scmd->cmnd == NULL || cmd->instance->host == NULL) { + LOG_WARN("cdb null\n"); + goto l_out; + } +#else + if (cmd->instance->host == NULL) { + LOG_WARN("cdb null\n"); + goto l_out; + } +#endif + + if ((type == PS3_IO_TRACE_DIRECT_SEND && + ps3_is_scsi_write_cmd(cmd->scmd)) || + (type == PS3_IO_TRACE_DIRECT_RECV && + ps3_is_scsi_read_cmd(cmd->scmd))) { + ps3_scsi_sgl_crc(cmd); + } + + ps3_scsih_lba_parse(cmd->scmd->cmnd, &lba); + memset(buf, '\0', sizeof(buf)); + buf_len = snprintf(buf, PS3_IO_TRACE_BUF_LEN, + "%s, trace_id[0x%llx], hno:%u lba[0x%llx]\n", + ps3_io_trace_direct_name(type), cmd->trace_id, + PS3_HOST(cmd->instance), lba); + if (buf_len >= PS3_IO_TRACE_BUF_LEN) { + LOG_ERROR("buf_len > PS3_IO_TRACE_BUF_LEN\n"); + goto l_out; + } + DATA_DUMP(sg_virt(cmd->scmd->sdb.table.sgl), + min_t(unsigned int, cmd->scmd->sdb.table.sgl[0].length, + PS3_IO_TRACE_PRINT_COUNT), + buf); + +l_out: + return; +} diff --git a/drivers/scsi/linkdata/ps3stor/ps3_io_trace.h b/drivers/scsi/linkdata/ps3stor/ps3_io_trace.h new file mode 100644 index 0000000000000000000000000000000000000000..a48221340dd215be765a025d2802d212eba8c117 --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/ps3_io_trace.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _PS3_IO_TRACE_H_ +#define _PS3_IO_TRACE_H_ + +#include "ps3_cmd_channel.h" + +#define PS3_IO_TRACE_PRINT_COUNT (32) +#define PS3_IO_TRACE_BUF_LEN (256) + +enum ps3_io_trace_dtype { + PS3_IO_TRACE_DIRECT_SEND, + PS3_IO_TRACE_DIRECT_RECV, + PS3_IO_TRACE_DIRECT_COUNT, +}; + +void ps3_scsih_io_trace(const struct ps3_cmd *cmd, + enum ps3_io_trace_dtype type); +#define PS3_IO_TRACE(cmd, type) \ + do { \ + if ((cmd)->instance->debug_context.io_trace_switch == \ + PS3_FALSE) { \ + break; \ + } \ + ps3_scsih_io_trace((cmd), (type)); \ + } while (0) + +#endif diff --git a/drivers/scsi/linkdata/ps3stor/ps3_ioc_adp.c b/drivers/scsi/linkdata/ps3stor/ps3_ioc_adp.c new file mode 100644 index 0000000000000000000000000000000000000000..fefaa6610fa08c7e240cfec8e097aa5eda028e9b --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/ps3_ioc_adp.c @@ -0,0 +1,459 @@ +// SPDX-License-Identifier: GPL-2.0 +#ifdef _WINDOWS +#include "ps3_def.h" +#endif + +#include "ps3_instance_manager.h" +#include "ps3_ioc_state.h" +#include "ps3_irq.h" +#include "ps3_ioc_manager.h" +#include "ps3_scsih_cmd_parse.h" +#include "ps3_scsih.h" +#include "ps3_inner_data.h" +#include "ps3_event.h" +#include "ps3_device_update.h" +#include "ps3_pci.h" +#include "ps3_module_para.h" +#include "ps3_scsih.h" + +#define MAX_MGR_CMD_COUNT (8) +#define MAX_TASK_CMD_COUNT (8) +#define MIN_MSIX_INT_COUNT (1) +#define PS3_HARD_DOG_MASK (0xF000000UL) + +#define SWITCH_MAX_MGR_CMD_COUNT (12) +#define SWITCH_MAX_TASK_CMD_COUNT (2) +#define SWITCH_MAX_MGR_CMD_TOTAL_COUNT (14) +#define SWITCH_MIN_MSI_INT_COUNT (1) +#define SWITCH_REPLY_FIFO_DEP_ADDITION (1) +#define PS3_HARD_DOG_MASK_SWITCH (0x1000000UL) + +struct ps3_ioc_adp_temp_entry { + unsigned short device_id; + struct ps3_ioc_adp_template *adp_template; +}; + +void ps3_ioc_resource_prepare_switch(struct ps3_instance *instance) +{ + struct PS3MgrEvent *event_req_info = &instance->event_req_info; + + memset(event_req_info, 0, sizeof(struct PS3MgrEvent)); + + event_req_info->eventTypeMap = PS3_EVT_PD_COUNT; + event_req_info->eventLevel = PS3_EVENT_LEVEL_INFO; + event_req_info->eventTypeMapProcResult = PS3_EVT_ILLEGAL_TYPE; + + instance->max_mgr_cmd_total_count = SWITCH_MAX_MGR_CMD_TOTAL_COUNT; + instance->max_mgr_cmd_count = SWITCH_MAX_MGR_CMD_COUNT; + instance->max_task_cmd_count = SWITCH_MAX_TASK_CMD_COUNT; + instance->min_intr_count = SWITCH_MIN_MSI_INT_COUNT; + instance->reply_fifo_depth_addition = SWITCH_REPLY_FIFO_DEP_ADDITION; + instance->is_support_jbod = PS3_TRUE; + instance->use_clusting = PS3_TRUE; + instance->is_use_frontend_prp = PS3_FALSE; + instance->is_adjust_register_count = PS3_FALSE; + instance->is_probe_finish = PS3_FALSE; + instance->is_probe_failed = PS3_FALSE; + instance->is_suspend = PS3_FALSE; + instance->is_resume = PS3_FALSE; + instance->is_hard_reset = PS3_FALSE; + instance->is_pci_reset = PS3_FALSE; + instance->ioc_fw_version = 0; + instance->hilMode = HIL_MODEL_SW; + instance->unload_timeout = PS3_DEFAULT_MGR_CMD_TIMEOUT; + instance->wait_ready_timeout = + PS3_FW_STATE_TO_READY_TMO_LOOP_COUNT_SWITCH; + instance->is_half_hard_reset = PS3_FALSE; + instance->is_need_event = PS3_FALSE; + instance->is_raid1_direct_skip_mapblock_check = PS3_FALSE; + instance->is_single_disk_raid0_direct_skip_strip_check = PS3_FALSE; + instance->is_support_dump_ctrl = PS3_FALSE; + instance->is_support_io_limit = PS3_FALSE; + instance->is_irq_prk_support = PS3_FALSE; + instance->is_support_irq = PS3_FALSE; + instance->is_raid = PS3_FALSE; + instance->hard_dog_mask = PS3_HARD_DOG_MASK_SWITCH; + instance->task_manager_host_busy = PS3_FALSE; + instance->is_print_special_log = PS3_FALSE; + instance->r1x_mode = PS3_R1X_MODE_NORMAL; + instance->smp_affinity_enable = ps3_smp_affinity_query(); + instance->page_mode_change = + ps3_host_vendor_get() == PS3_HOST_VENDOR_INTEL ? PS3_FALSE : + PS3_TRUE; + instance->page_mode_addr_mask = PS3_PAGE_MODE_ABOVE_3_ADDR_MASK; + ps3_mutex_init(&instance->task_mgr_reset_lock); + ps3_mutex_init(&instance->task_abort_lock); +} + +void ps3_ioc_resource_prepare_raid(struct ps3_instance *instance) +{ + struct PS3MgrEvent *event_req_info = &instance->event_req_info; + + memset(event_req_info, 0, sizeof(struct PS3MgrEvent)); + + event_req_info->eventTypeMap = PS3_EVT_PD_COUNT | PS3_EVT_VD_COUNT | + PS3_EVT_CTRL_INFO | PS3_EVT_PD_ATTR; + + event_req_info->eventLevel = PS3_EVENT_LEVEL_INFO; + event_req_info->eventTypeMapProcResult = PS3_EVT_ILLEGAL_TYPE; + + instance->max_mgr_cmd_total_count = MAX_MGR_CMD_TOTAL_COUNT; + instance->max_mgr_cmd_count = MAX_MGR_CMD_COUNT; + instance->max_task_cmd_count = MAX_TASK_CMD_COUNT; + instance->min_intr_count = MIN_MSIX_INT_COUNT; + instance->is_support_jbod = PS3_TRUE; + instance->use_clusting = (ps3_use_clustering_query() == PS3_TRUE); + instance->is_use_frontend_prp = PS3_FALSE; + instance->is_adjust_register_count = PS3_TRUE; + instance->is_probe_finish = PS3_FALSE; + instance->is_probe_failed = PS3_FALSE; + instance->is_suspend = PS3_FALSE; + instance->is_resume = PS3_FALSE; + instance->is_hard_reset = PS3_FALSE; + instance->is_pci_reset = PS3_FALSE; + instance->ioc_fw_version = 0; + instance->hilMode = HIL_MODEL_HW_ENHANCED; + instance->unload_timeout = PS3_RAID_UNLOAD_MGR_CMD_TIMEOUT; +#ifdef PS3_HARDWARE_HAPS_V200 + instance->wait_ready_timeout = + PS3_FW_STATE_TO_READY_TMO_LOOP_COUNT_RAID_HAPS; +#else + instance->wait_ready_timeout = + PS3_FW_STATE_TO_READY_TMO_LOOP_COUNT_RAID; +#endif + instance->is_half_hard_reset = PS3_FALSE; + instance->is_need_event = PS3_TRUE; + instance->is_raid1_direct_skip_mapblock_check = PS3_FALSE; + instance->is_single_disk_raid0_direct_skip_strip_check = PS3_FALSE; + instance->is_support_dump_ctrl = PS3_TRUE; + instance->is_support_io_limit = PS3_TRUE; + instance->is_irq_prk_support = PS3_FALSE; + instance->is_support_irq = PS3_FALSE; + instance->is_raid = PS3_TRUE; + instance->hard_dog_mask = PS3_HARD_DOG_MASK; + instance->is_print_special_log = PS3_FALSE; + instance->r1x_mode = PS3_R1X_MODE_NORMAL; + instance->task_manager_host_busy = PS3_FALSE; + instance->smp_affinity_enable = ps3_smp_affinity_query(); + instance->page_mode_change = + ps3_host_vendor_get() == PS3_HOST_VENDOR_INTEL ? PS3_FALSE : + PS3_TRUE; + instance->page_mode_addr_mask = PS3_PAGE_MODE_ABOVE_3_ADDR_MASK; + ps3_mutex_init(&instance->task_mgr_reset_lock); + ps3_mutex_init(&instance->task_abort_lock); + ps3_raid_qos_prepare(instance); +} + +void ps3_ioc_resource_prepare_hba(struct ps3_instance *instance) +{ + struct PS3MgrEvent *event_req_info = &instance->event_req_info; + + memset(event_req_info, 0, sizeof(struct PS3MgrEvent)); + + event_req_info->eventTypeMap = PS3_EVT_PD_COUNT | PS3_EVT_VD_COUNT | + PS3_EVT_CTRL_INFO | PS3_EVT_SAS_INFO | + PS3_EVT_PD_ATTR; + + event_req_info->eventLevel = PS3_EVENT_LEVEL_INFO; + event_req_info->eventTypeMapProcResult = PS3_EVT_ILLEGAL_TYPE; + + instance->max_mgr_cmd_total_count = MAX_MGR_CMD_TOTAL_COUNT; + instance->max_mgr_cmd_count = MAX_MGR_CMD_COUNT; + instance->max_task_cmd_count = MAX_TASK_CMD_COUNT; + instance->min_intr_count = MIN_MSIX_INT_COUNT; + instance->is_support_jbod = PS3_FALSE; + instance->use_clusting = (ps3_use_clustering_query() == PS3_TRUE); + instance->is_use_frontend_prp = PS3_TRUE; + instance->is_adjust_register_count = PS3_TRUE; + instance->is_probe_finish = PS3_FALSE; + instance->is_probe_failed = PS3_FALSE; + instance->is_suspend = PS3_FALSE; + instance->is_resume = PS3_FALSE; + instance->is_scan_host_finish = PS3_FALSE; + instance->is_hard_reset = PS3_FALSE; + instance->is_pci_reset = PS3_FALSE; + instance->is_halt_support_cli = PS3_FALSE; + ps3_atomic_set(&instance->reg_op_count, 0); + instance->ioc_fw_version = 0; + instance->hilMode = HIL_MODEL_HW_ENHANCED; + instance->unload_timeout = PS3_UNLOAD_MGR_CMD_TIMEOUT; + instance->wait_ready_timeout = PS3_FW_STATE_TO_READY_TMO_LOOP_COUNT_HBA; + instance->is_half_hard_reset = PS3_FALSE; + instance->is_need_event = PS3_TRUE; + instance->is_raid1_direct_skip_mapblock_check = PS3_TRUE; + instance->is_single_disk_raid0_direct_skip_strip_check = PS3_TRUE; + instance->is_support_dump_ctrl = PS3_TRUE; + instance->is_support_io_limit = PS3_FALSE; + instance->is_irq_prk_support = PS3_FALSE; + instance->is_support_irq = PS3_FALSE; + instance->is_raid = PS3_FALSE; + instance->hard_dog_mask = PS3_HARD_DOG_MASK; + instance->is_print_special_log = PS3_FALSE; + instance->smp_affinity_enable = ps3_smp_affinity_query(); + ps3_atomic_set(&instance->host_reset_processing, 0); + instance->task_manager_host_busy = PS3_FALSE; + instance->r1x_mode = PS3_R1X_MODE_NORMAL; + instance->page_mode_change = + ps3_host_vendor_get() == PS3_HOST_VENDOR_INTEL ? PS3_FALSE : + PS3_TRUE; + instance->page_mode_addr_mask = PS3_PAGE_MODE_ABOVE_3_ADDR_MASK; + ps3_mutex_init(&instance->task_mgr_reset_lock); + ps3_mutex_init(&instance->task_abort_lock); + ps3_hba_qos_prepare(instance); +} + +static unsigned char +ps3_replyq_count_raidhba_get(struct ps3_instance *instance, + unsigned int *max_replyq_count) +{ + unsigned char ret = PS3_TRUE; + + if (!ps3_max_replyq_count_get(instance, max_replyq_count)) { + LOG_ERROR("hno:%u ps3_max_replyq_count_get NOK!\n", + PS3_HOST(instance)); + ret = PS3_FALSE; + goto l_out; + } + *max_replyq_count += PS3_RAIDHBA_NUM_ADJUST_VALUE; + *max_replyq_count = *max_replyq_count > PS3_MAX_REPLY_QUE_COUNT ? + PS3_MAX_REPLY_QUE_COUNT : + *max_replyq_count; +l_out: + return ret; +} + +static unsigned char ps3_is_need_direct_to_normal_hba(const struct ps3_cmd *cmd) +{ + unsigned char ret = PS3_TRUE; + + if (ps3_direct_to_normal_query()) { + ret = PS3_TRUE; + goto l_out; + } + + if (cmd->io_attr.dev_type == PS3_DEV_TYPE_NVME_SSD) { + ret = PS3_TRUE; + goto l_out; + } + + if (cmd->io_attr.dev_type != PS3_DEV_TYPE_VD) { + ret = PS3_FALSE; + goto l_out; + } + + if (unlikely(cmd->io_attr.vd_entry == NULL)) { + ret = PS3_FALSE; + goto l_out; + } + + if (cmd->io_attr.vd_entry->isNvme) { + ret = PS3_TRUE; + goto l_out; + } + +l_out: + return ret; +} + +static unsigned char +ps3_is_need_direct_to_normal_raid(const struct ps3_cmd *cmd) +{ + (void)cmd; + return PS3_TRUE; +} + +static unsigned char +ps3_is_need_direct_to_normal_switch(const struct ps3_cmd *cmd) +{ + (void)cmd; + return PS3_FALSE; +} + +struct ps3_ioc_adp_template g_ps3_template_switch = { + .io_cmd_build = ps3_scsih_cmd_build, + .mgr_cmd_build = NULL, + .init_cmd_send = ps3_switch_init_cmd_send, + .cmd_send = ps3_switch_normal_cmd_send, + .ioc_state_get = ps3_ioc_state_get, + .ioc_init_state_to_ready = ps3_ioc_init_to_ready, + .ioc_init_proc = ps3_ioc_init_proc, + .ioc_resource_prepare = ps3_ioc_resource_prepare_switch, + .ioc_hard_reset = ps3_ioc_state_hard_reset, + .ioc_shallow_soft_reset = ps3_ioc_state_shallow_soft_reset, + .ioc_deep_soft_reset = ps3_ioc_state_deep_soft_reset, + .ioc_force_to_fault = ps3_ioc_state_force_to_fault, + .ioc_force_to_halt = ps3_ioc_state_force_to_halt, + .irq_init = ps3_irqs_init_switch, + .irq_enable = ps3_irqs_enable, + .irq_disable = ps3_irqs_disable, + .isr = ps3_irqs_service, +#ifndef _WINDOWS + .sas_transport_get = NULL, +#endif + .event_filter_table_get = ps3_event_filter_table_get_switch, + .reg_write = ps3_reg_write_u64, +#ifdef _WINDOWS + .reg_read = ps3_reg_read_u64, +#else + .reg_read = ps3_switch_ioc_reg_read, +#endif + .is_need_direct_to_normal = ps3_is_need_direct_to_normal_switch, + .max_replyq_count_get = ps3_max_replyq_count_get, + .check_vd_member_change = NULL, + .scsih_stream_is_detect = NULL, + .scsih_stream_is_direct = NULL, +#ifdef PS3_HARDWARE_ASIC + .ioc_heartbeat_detect = ps3_ioc_heartbeat_detect, +#else + .ioc_heartbeat_detect = NULL, +#endif + .reg_set = NULL, + .ioc_security_check = NULL, + .io_cmd_rebuild = ps3_scsih_direct_to_normal_req_frame_rebuild, + .rw_cmd_is_need_split = NULL, + .write_direct_enable = NULL, + .ssd_vd_qmask_calculate = NULL, +}; + +struct ps3_ioc_adp_template g_ps3_template_hba = { + .io_cmd_build = ps3_scsih_cmd_build, + .mgr_cmd_build = NULL, + + + .init_cmd_send = ps3_switch_init_cmd_send, + .cmd_send = ps3_ioc_cmd_send, + .ioc_state_get = ps3_ioc_state_get, + .ioc_init_state_to_ready = ps3_ioc_init_to_ready, + .ioc_init_proc = ps3_ioc_init_proc, + .ioc_resource_prepare = ps3_ioc_resource_prepare_hba, + .ioc_hard_reset = ps3_ioc_state_hard_reset, + .ioc_shallow_soft_reset = ps3_ioc_state_shallow_soft_reset, + .ioc_deep_soft_reset = ps3_ioc_state_deep_soft_reset, + .ioc_force_to_fault = ps3_ioc_state_force_to_fault, + .ioc_force_to_halt = ps3_ioc_state_force_to_halt, + .irq_init = ps3_irqs_init, + .irq_enable = ps3_irqs_enable, + .irq_disable = ps3_irqs_disable, + .isr = ps3_irqs_service, +#ifndef _WINDOWS + .sas_transport_get = ps3_sas_transport_get, +#endif + .event_filter_table_get = ps3_event_filter_table_get_hba, + .reg_write = ps3_reg_write_u64, + .reg_read = ps3_reg_read_u64, + .is_need_direct_to_normal = ps3_is_need_direct_to_normal_hba, + .max_replyq_count_get = ps3_replyq_count_raidhba_get, + .check_vd_member_change = ps3_check_vd_member_change, + .scsih_stream_is_detect = ps3_scsih_stream_is_detect, + .scsih_stream_is_direct = ps3_hba_scsih_stream_is_direct, +#ifdef PS3_HARDWARE_ASIC + .ioc_heartbeat_detect = ps3_ioc_heartbeat_detect, +#else + .ioc_heartbeat_detect = NULL, +#endif + .reg_set = ps3_reg_set_ioremap, + .ioc_security_check = NULL, + .io_cmd_rebuild = ps3_scsih_direct_to_normal_req_frame_rebuild, + .rw_cmd_is_need_split = ps3_scsih_rw_cmd_is_need_split_hba, + .write_direct_enable = NULL, + .ssd_vd_qmask_calculate = ps3_ssd_vd_qmask_calculate_hba, +}; + +struct ps3_ioc_adp_template g_ps3_template_raid = { + .io_cmd_build = ps3_scsih_cmd_build, + .mgr_cmd_build = NULL, + + .init_cmd_send = ps3_switch_init_cmd_send, + .cmd_send = ps3_ioc_cmd_send, + .ioc_state_get = ps3_ioc_state_get, + .ioc_init_state_to_ready = ps3_ioc_init_to_ready, + .ioc_init_proc = ps3_ioc_init_proc, + .ioc_resource_prepare = ps3_ioc_resource_prepare_raid, + .ioc_hard_reset = ps3_ioc_state_hard_reset, + .ioc_shallow_soft_reset = ps3_ioc_state_shallow_soft_reset, + .ioc_deep_soft_reset = ps3_ioc_state_deep_soft_reset, + .ioc_force_to_fault = ps3_ioc_state_force_to_fault, + .ioc_force_to_halt = ps3_ioc_state_force_to_halt, + .irq_init = ps3_irqs_init, + .irq_enable = ps3_irqs_enable, + .irq_disable = ps3_irqs_disable, + .isr = ps3_irqs_service, +#ifndef _WINDOWS + .sas_transport_get = NULL, +#endif + .event_filter_table_get = ps3_event_filter_table_get_raid, + .reg_write = ps3_reg_write_u64, + .reg_read = ps3_reg_read_u64, + .is_need_direct_to_normal = ps3_is_need_direct_to_normal_raid, + .max_replyq_count_get = ps3_replyq_count_raidhba_get, + .check_vd_member_change = NULL, + .scsih_stream_is_detect = ps3_scsih_stream_is_detect, + .scsih_stream_is_direct = ps3_raid_scsih_stream_is_direct, +#ifdef PS3_HARDWARE_ASIC + .ioc_heartbeat_detect = ps3_ioc_heartbeat_detect, +#else + .ioc_heartbeat_detect = NULL, +#endif + .reg_set = ps3_reg_set_ioremap, + .ioc_security_check = ps3_ioc_security_state_check, + .io_cmd_rebuild = ps3_scsih_direct_to_normal_req_frame_rebuild, + .rw_cmd_is_need_split = ps3_scsih_rw_cmd_is_need_split_raid, + .write_direct_enable = ps3_write_direct_enable, + .ssd_vd_qmask_calculate = NULL, +}; + +static struct ps3_ioc_adp_temp_entry ps3_ioc_adp_template_map[] = { + { PCI_DEVICE_ID_PS3_RAID, &g_ps3_template_raid }, + { PCI_DEVICE_ID_PS3_HBA, &g_ps3_template_hba }, + { PCI_DEVICE_ID_PS3_SWITCH, &g_ps3_template_switch }, + { PCI_DEVICE_ID_PS3_SWITCH_FPGA, &g_ps3_template_switch }, + { PCI_DEVICE_ID_STARS_IOC_2020_18i, &g_ps3_template_hba }, + { PCI_DEVICE_ID_STARS_ROC_2020_10i, &g_ps3_template_raid }, + { PCI_DEVICE_ID_PS3_RAID_FPGA, &g_ps3_template_raid }, + { PCI_DEVICE_ID_STARS_IOC_2213_16i, &g_ps3_template_hba }, +}; + +#ifndef _WINDOWS +void ps3_ioc_adp_init(struct ps3_instance *instance, + const struct pci_device_id *id) +{ + unsigned short i = 0; + unsigned short adp_num = sizeof(ps3_ioc_adp_template_map) / + sizeof(struct ps3_ioc_adp_temp_entry); + + for (i = 0; i < adp_num; i++) { + if (id->device == ps3_ioc_adp_template_map[i].device_id) { + instance->ioc_adpter = + ps3_ioc_adp_template_map[i].adp_template; + } + } + + instance->ioc_adpter->ioc_resource_prepare(instance); +} +#else +void ps3_ioc_adp_init(struct ps3_instance *instance) +{ + unsigned short i = 0; + unsigned short adp_num = sizeof(ps3_ioc_adp_template_map) / + sizeof(struct ps3_ioc_adp_temp_entry); + + for (i = 0; i < adp_num; i++) { + if (instance->pci_dev_context.device_id == + ps3_ioc_adp_template_map[i].device_id) { + instance->ioc_adpter = + ps3_ioc_adp_template_map[i].adp_template; + LOG_WARN( + "hno:%u pci dev type is [%s]\n", + PS3_HOST(instance), + namePciDevType( + ps3_ioc_adp_template_map[i].device_id)); +#ifdef _WINDOWS + instance->ioc_adpter->event_filter_table_get = + ps3_event_filter_table_get_raid; +#endif + } + } + + instance->ioc_adpter->ioc_resource_prepare(instance); +} +#endif diff --git a/drivers/scsi/linkdata/ps3stor/ps3_ioc_manager.c b/drivers/scsi/linkdata/ps3stor/ps3_ioc_manager.c new file mode 100644 index 0000000000000000000000000000000000000000..a62820cd626957c646384e90d971644063facbfe --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/ps3_ioc_manager.c @@ -0,0 +1,1131 @@ +// SPDX-License-Identifier: GPL-2.0 +#ifndef _WINDOWS + +#include +#include +#include +#include +#include +#else +#include "ps3_def.h" +#endif + +#include "ps3_cmd_statistics.h" +#include "ps3_ioc_manager.h" +#include "ps3_ioc_state.h" +#include "ps3_util.h" +#include "ps3_pci.h" +#include "ps3_htp_def.h" +#include "ps3_mgr_cmd.h" +#include "ps3_drv_ver.h" + +#define PS3_INIT_CMD_WAIT_MAX_TIMEOUT (180) +#define PS3_INIT_CMD_WAIT_INTERVAL (20) +#define PS3_INIT_CMD_CHECK_FAULT_INTERVAL (1000) +#if (defined PS3_HARDWARE_FPGA && defined PS3_MODEL_V200) +#define PS3_REG_READ_MAX_TRY_COUNT (60) +#else +#define PS3_REG_READ_MAX_TRY_COUNT (10) +#endif +#define PS3_REG_SWITCH_QEQUEST_QUEUE_OFFSET (0x10000) +#define PS3_IOC_INIT_PROC_FAIL_RETRY_COUNT (2) + +static int ps3_ioc_init_cmd_result_poll(struct ps3_instance *instance, + struct PS3InitReqFrame *init_frame_msg) +{ + unsigned int state = PS3_FW_STATE_UNDEFINED; + unsigned int wait_count = PS3_INIT_CMD_WAIT_MAX_TIMEOUT * 1000 / + PS3_INIT_CMD_WAIT_INTERVAL; + unsigned int count = 0; + int ret = -PS3_FAILED; + + state = instance->ioc_adpter->ioc_state_get(instance); + + while ((count < wait_count) && (state != PS3_FW_STATE_FAULT) && + (state != PS3_FW_STATE_CRITICAL)) { + rmb(); /* in order to force CPU ordering */ + ps3_msleep(PS3_INIT_CMD_WAIT_INTERVAL); + + if (ps3_pci_err_recovery_get(instance)) { + LOG_WARN("hno:%u pci recovery resetting\n", + PS3_HOST(instance)); + ret = -PS3_IN_PCIE_ERR; + goto l_out; + } + + if (init_frame_msg->respStatus != U32_MAX) + break; + + if (!(count % PS3_INIT_CMD_CHECK_FAULT_INTERVAL)) { + state = instance->ioc_adpter->ioc_state_get(instance) & + PS3_FW_STATE_MASK; + } + + count++; + } + + if (state == PS3_FW_STATE_FAULT || state == PS3_FW_STATE_CRITICAL) { + LOG_ERROR("hno:%u init cmd NOK since IOC state fault\n", + PS3_HOST(instance)); + goto l_out; + } + + if (count == wait_count) { + LOG_ERROR( + "hno:%u init cmd timeout, state=%#x, respStatus=%#x\n", + PS3_HOST(instance), state, init_frame_msg->respStatus); + goto l_out; + } + + if (init_frame_msg->respStatus != 0) { + LOG_ERROR("hno:%u init cmd NOK[%d]\n", PS3_HOST(instance), + init_frame_msg->respStatus); + goto l_out; + } + + ret = PS3_SUCCESS; + LOG_WARN("hno:%u init cmd response successfully\n", + PS3_HOST(instance)); + +l_out: + init_frame_msg->respStatus = U32_MAX; + return ret; +} + +static int ps3_ioc_init_cmd_issue(struct ps3_instance *instance, + struct PS3InitReqFrame *init_frame_msg) +{ + struct ps3_cmd_context *cmd_context = &instance->cmd_context; + struct PS3InitCmdWord cmd_word; + + memset(&cmd_word, 0, sizeof(struct PS3InitCmdWord)); + + cmd_word.lowAddr = + cpu_to_le32(lower_32_bits(cmd_context->init_frame_buf_phys)); + cmd_word.highAddr = + cpu_to_le32(upper_32_bits(cmd_context->init_frame_buf_phys)); + cmd_word.type = PS3_CMDWORD_TYPE_INIT; + cmd_word.direct = PS3_CMDWORD_DIRECT_NORMAL; + + instance->ioc_adpter->init_cmd_send(instance, + (struct PS3CmdWord *)&cmd_word); + + LOG_INFO("hno:%u init command: cmd.lowAddr[0x%x], cmd.highAddr[0x%x], cmd.type[%u]\n", + PS3_HOST(instance), cmd_word.lowAddr, cmd_word.highAddr, + cmd_word.type); + + return ps3_ioc_init_cmd_result_poll(instance, init_frame_msg); +} + +static int ps3_ioc_init_cmd_alloc(struct ps3_instance *instance) +{ + struct ps3_cmd_context *cmd_context = &instance->cmd_context; + int ret = PS3_SUCCESS; + + cmd_context->init_frame_buf = (unsigned char *)ps3_dma_alloc_coherent( + instance, sizeof(struct PS3InitReqFrame), + (unsigned long long *)&cmd_context->init_frame_buf_phys); + + if (!cmd_context->init_frame_buf) { + LOG_ERROR("hno:%u Failed to alloc init cmd dma buffer\n", + PS3_HOST(instance)); + ret = -PS3_FAILED; + } + + cmd_context->init_filter_table_buff = + (unsigned char *)ps3_dma_alloc_coherent( + instance, PS3_CMD_EXT_BUF_SIZE_MGR, + (unsigned long long *)&cmd_context + ->init_filter_table_phy_addr); + + if (!cmd_context->init_filter_table_buff) { + LOG_ERROR( + "hno:%u Failed to alloc init filter table dma buffer\n", + PS3_HOST(instance)); + ret = -PS3_FAILED; + } + + return ret; +} +#ifndef _WINDOWS + +static int ps3_ioc_sys_info_get(struct ps3_instance *instance) +{ + struct ps3_cmd_context *cmd_context = &instance->cmd_context; + struct PS3DrvSysInfo *drv_sys_info = NULL; + const char *sys_info = NULL; + unsigned int sys_info_len = 0; + int ret = -PS3_FAILED; + + cmd_context->init_frame_sys_info_buf = + (unsigned char *)ps3_dma_alloc_coherent( + instance, sizeof(struct PS3DrvSysInfo), + (unsigned long long *)&cmd_context + ->init_frame_sys_info_phys); + if (!cmd_context->init_frame_sys_info_buf) { + LOG_ERROR("hno:%u Failed to alloc sysinfo dma buffer\n", + PS3_HOST(instance)); + goto l_out; + } + + drv_sys_info = + (struct PS3DrvSysInfo *)cmd_context->init_frame_sys_info_buf; + + memset(drv_sys_info->systemID, 0, PS3_DRV_SYSTEM_ID_MAX_LEN); + + sys_info = dmi_get_system_info(DMI_PRODUCT_UUID); + if (!sys_info) { + LOG_INFO("hno:%u Failed to get sysinfo\n", PS3_HOST(instance)); + drv_sys_info->systemIDLen = 0; + drv_sys_info->version = 0; + ret = PS3_SUCCESS; + goto l_out; + } + + sys_info_len = strlen(sys_info) > PS3_DRV_SYSTEM_ID_MAX_LEN ? + PS3_DRV_SYSTEM_ID_MAX_LEN : + strlen(sys_info); + + memcpy(drv_sys_info->systemID, sys_info, sys_info_len); + drv_sys_info->systemIDLen = sys_info_len; + drv_sys_info->version = 0; + ret = PS3_SUCCESS; + +l_out: + return ret; +} + +#endif + +int ps3_ioc_init_cmd_context_init(struct ps3_instance *instance) +{ + if (ps3_ioc_init_cmd_alloc(instance) != PS3_SUCCESS) + goto l_failed; +#ifndef _WINDOWS + if (ps3_ioc_sys_info_get(instance) != PS3_SUCCESS) + goto l_failed; +#endif + return PS3_SUCCESS; +l_failed: + ps3_ioc_init_cmd_context_exit(instance); + return -PS3_FAILED; +} + +void ps3_ioc_init_cmd_context_exit(struct ps3_instance *instance) +{ + struct ps3_cmd_context *cmd_context = &instance->cmd_context; + + LOG_INFO("entry\n"); +#ifndef _WINDOWS + if (cmd_context->init_frame_sys_info_buf != NULL) { + LOG_INFO("free init_frame_sys_info_buf = %p\n", + cmd_context->init_frame_sys_info_buf); + ps3_dma_free_coherent(instance, sizeof(struct PS3DrvSysInfo), + cmd_context->init_frame_sys_info_buf, + cmd_context->init_frame_sys_info_phys); + cmd_context->init_frame_sys_info_buf = NULL; + } +#endif + + if (cmd_context->init_filter_table_buff != NULL) { + LOG_INFO("free init_filter_table_buff = %p\n", + cmd_context->init_filter_table_buff); + ps3_dma_free_coherent(instance, PS3_CMD_EXT_BUF_SIZE_MGR, + cmd_context->init_filter_table_buff, + cmd_context->init_filter_table_phy_addr); + + cmd_context->init_filter_table_buff = NULL; + cmd_context->init_filter_table_phy_addr = 0; + } + + if (cmd_context->init_frame_buf != NULL) { + LOG_INFO("free init_frame_buf = %p\n", + cmd_context->init_frame_buf); + ps3_dma_free_coherent(instance, sizeof(struct PS3InitReqFrame), + cmd_context->init_frame_buf, + cmd_context->init_frame_buf_phys); + cmd_context->init_frame_buf = NULL; + } +} + +int ps3_drv_info_buf_alloc(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + + instance->drv_info_buf = (unsigned char *)ps3_dma_alloc_coherent( + instance, sizeof(struct PS3DrvInfo), + (unsigned long long *)&instance->drv_info_buf_phys); + if (instance->drv_info_buf == NULL) { + LOG_ERROR("hno:%u Failed to alloc drv info dma buffer\n", + PS3_HOST(instance)); + ret = -PS3_FAILED; + } + return ret; +} + +void ps3_drv_info_buf_free(struct ps3_instance *instance) +{ + if (instance->drv_info_buf != NULL) { + LOG_INFO("drv_info_buf = %p\n", instance->drv_info_buf); + ps3_dma_free_coherent(instance, sizeof(struct PS3DrvInfo), + instance->drv_info_buf, + instance->drv_info_buf_phys); + instance->drv_info_buf = NULL; + } +} + +int ps3_host_mem_info_buf_alloc(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + + instance->host_mem_info_buf = (unsigned char *)ps3_dma_alloc_coherent( + instance, + (sizeof(struct PS3HostMemInfo) * PS3_HOST_MEM_INFO_NUM), + (unsigned long long *)&instance->host_mem_info_buf_phys); + if (instance->host_mem_info_buf == NULL) { + LOG_ERROR("hno:%u Failed to alloc host mem info dma buffer\n", + PS3_HOST(instance)); + ret = -PS3_FAILED; + } + return ret; +} + +void ps3_host_mem_info_buf_free(struct ps3_instance *instance) +{ + if (instance->host_mem_info_buf != NULL) { + LOG_INFO("free host_mem_info_buf = %p\n", + instance->host_mem_info_buf); + ps3_dma_free_coherent( + instance, + (sizeof(struct PS3HostMemInfo) * PS3_HOST_MEM_INFO_NUM), + instance->host_mem_info_buf, + instance->host_mem_info_buf_phys); + instance->host_mem_info_buf = NULL; + } +} + +int ps3_hard_reset_to_ready(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + + if (instance->peer_instance != NULL) { + if (instance->recovery_context->parall_hardreset_state == + PS3_PARALLEL_HARDRESET_STATE_PENDING) { + instance->recovery_context->parall_hardreset_state = + PS3_PARALLEL_HARDRESET_STATE_CONTINUE; + while (instance->recovery_context + ->parall_hardreset_state != + PS3_PARALLEL_HARDRESET_STATE_INIT) { + ps3_msleep( + PS3_PARALLEL_HARDRESET_STATE_WAIT_INIT_INTERVAL); + } + } else { + ret = ps3_hard_recovery_request(instance); + if (ret != PS3_SUCCESS) { + LOG_WARN( + "hno:%u hard recovery request failed\n", + PS3_HOST(instance)); + goto l_out; + } + } + ps3_recovery_cancel_work_sync(instance); + } else { + if (!instance->ioc_adpter->ioc_hard_reset) { + ret = -PS3_FAILED; + goto l_out; + } + + ret = instance->ioc_adpter->ioc_hard_reset(instance); + if (ret == -PS3_FAILED) + goto l_out; + ret = ps3_ioc_state_ready_wait(instance); + if (ret != PS3_SUCCESS) + goto l_out; + } + +l_out: + if (ret == PS3_SUCCESS) { + LOG_WARN("hno:%u ps3 fw state reset to ready successfully\n", + PS3_HOST(instance)); + } else { + LOG_ERROR("hno:%u PS3 fw state reset to ready NOK\n", + PS3_HOST(instance)); + } + + return ret; +} +int ps3_ioc_hard_reset_to_ready(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + + if (!instance->ioc_adpter->ioc_hard_reset) { + ret = -PS3_FAILED; + goto l_out; + } + + if (instance->recovery_context->heartbeat_recovery != + PS3_HEARTBEAT_HARDRESET_RECOVERY) { + ret = instance->ioc_adpter->ioc_hard_reset(instance); + if (ret != PS3_SUCCESS) + goto l_out; + } + + ret = ps3_ioc_state_transfer_to_ready(instance); + if (ret != PS3_SUCCESS) + goto l_out; + +l_out: + if (ret == PS3_SUCCESS) { + LOG_WARN("hno:%u ps3 fw state reset to ready successfully\n", + PS3_HOST(instance)); + } else { + LOG_ERROR("hno:%u PS3 fw state reset to ready NOK\n", + PS3_HOST(instance)); + } + + return ret; +} + +int ps3_ioc_init_to_ready(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + unsigned char is_unload_valid = PS3_FALSE; + + ret = ps3_ioc_state_transfer_to_ready(instance); + if (ret == PS3_SUCCESS) + goto l_out; + + ps3_check_debug0_valid_with_check(instance, &is_unload_valid, + PS3_CMD_TRIGGER_UNLOAD); + LOG_INFO( + "hno:%u PS3 fw state cannot directly init to ready successfully\n", + PS3_HOST(instance)); + if (ret != -PS3_NO_RECOVERED) { + if (ps3_is_need_hard_reset(instance)) + ret = ps3_hard_reset_to_ready(instance); + } + +l_out: + if (ret == PS3_SUCCESS) { + LOG_INFO("hno:%u PS3 fw state init to ready successfully\n", + PS3_HOST(instance)); + if (is_unload_valid) + ret = ps3_ioc_notify_unload(instance); + } else { + LOG_ERROR("hno:%u PS3 fw state init to ready NOK\n", + PS3_HOST(instance)); + } + + return ret; +} + +static void ps3_drv_info_prepare(struct ps3_instance *instance) +{ + struct PS3DrvInfo *drv_info = NULL; + + drv_info = (struct PS3DrvInfo *)instance->drv_info_buf; + memset(drv_info, 0, sizeof(struct PS3DrvInfo)); + + snprintf(drv_info->drvName, PS3_DRV_NAME_MAX_LEN, PS3_DRV_AUTHOR); + snprintf(drv_info->drvVersion, PS3_DRV_VERSION_MAX_LEN, + PS3_DRV_VERSION); + drv_info->domain_support = PS3_TRUE; + drv_info->domain = (unsigned int)ps3_get_pci_domain(instance->pdev); + drv_info->bus = ps3_get_pci_bus(instance->pdev); + drv_info->dev = PCI_SLOT(instance->pdev->devfn); + drv_info->func = PCI_FUNC(instance->pdev->devfn); + drv_info->compatVer = PS3_COMPAT_VER_1; + LOG_DEBUG("hno:%u driver information dump:\n" + "\t drvName = %s, drvVersion = %s\n" + "\t domain = %x bus = %llx, dev = %x, func = %x\n", + PS3_HOST(instance), drv_info->drvName, drv_info->drvVersion, + drv_info->domain, drv_info->bus, drv_info->dev, + drv_info->func); +} + +static inline bool ps3_pgdat_is_empty(pg_data_t *pgdat) +{ + return !pgdat->node_start_pfn && !pgdat->node_spanned_pages; +} + +struct pglist_data *ps3_first_online_pgdat(void) +{ + return NODE_DATA(first_online_node); +} + +struct pglist_data *ps3_next_online_pgdat(struct pglist_data *pgdat) +{ + int nid = next_online_node(pgdat->node_id); + + if (nid == MAX_NUMNODES) + return NULL; + return NODE_DATA(nid); +} + +static unsigned char ps3_get_numa_mem_addr(struct ps3_instance *instance) +{ + unsigned char ret = PS3_FALSE; + int node = -1; + struct pglist_data *pgdata = NULL; + + node = dev_to_node(&instance->pdev->dev); + if (node < 0) + goto l_out; + pgdata = NODE_DATA(node); + if (pgdata == NULL || ps3_pgdat_is_empty(pgdata)) + goto l_out; + + instance->start_pfn = pgdata->node_start_pfn << PAGE_SHIFT; + instance->end_pfn = ((pgdat_end_pfn(pgdata) << PAGE_SHIFT) - 1); + + LOG_INFO( + "host_no:%u numa:%d, addr range:[0x%llx - 0x%llx], so addr range:[0x%llx - 0x%llx]\n", + PS3_HOST(instance), node, instance->start_pfn, + instance->end_pfn, instance->so_start_addr, + instance->so_end_addr); + ret = PS3_TRUE; +l_out: + return ret; +} + +static void ps3_get_all_numa_mem_addr(struct ps3_instance *instance) +{ + struct pglist_data *pgdata = NULL; + int node = -1; + unsigned char is_in_range = PS3_FALSE; + unsigned long long start_pfn = 0; + unsigned long long end_pfn = 0; + + if (ps3_get_numa_mem_addr(instance)) + goto l_out; + for_each_ps3_online_pgdat(pgdata) + { + if (ps3_pgdat_is_empty(pgdata)) + continue; + start_pfn = pgdata->node_start_pfn << PAGE_SHIFT; + end_pfn = ((pgdat_end_pfn(pgdata) << PAGE_SHIFT) - 1); + if (PCIE_DMA_HOST_ADDR_BIT_POS_CLEAR_NEW( + instance->dma_addr_bit_pos, + instance->so_start_addr) >= start_pfn && + PCIE_DMA_HOST_ADDR_BIT_POS_CLEAR_NEW( + instance->dma_addr_bit_pos, + instance->so_end_addr) <= end_pfn) { + node = pgdata->node_id; + is_in_range = PS3_TRUE; + break; + } + } + if (is_in_range) { + instance->start_pfn = start_pfn; + instance->end_pfn = end_pfn; + } else { + instance->start_pfn = 0; + instance->end_pfn = 0; + } + LOG_INFO( + "host_no:%u numa:%d, addr range:[0x%llx - 0x%llx], so addr range:[0x%llx - 0x%llx]\n", + PS3_HOST(instance), node, instance->start_pfn, + instance->end_pfn, instance->so_start_addr, + instance->so_end_addr); +l_out: + return; +} + +static void ps3_host_mem_info_prepare(struct ps3_instance *instance) +{ + struct PS3HostMemInfo *host_mem_info = NULL; + + host_mem_info = (struct PS3HostMemInfo *)instance->host_mem_info_buf; + memset(host_mem_info, 0, sizeof(struct PS3HostMemInfo)); + + host_mem_info->startAddr = instance->start_pfn; + host_mem_info->endAddr = instance->end_pfn; + host_mem_info->type = PS3_MEM_TYPE_SO; + LOG_DEBUG("hno:%u host mem info dump:\n" + "\t startAddr = 0x%llx, endAddr = 0x%llx, type = %u\n", + PS3_HOST(instance), host_mem_info->startAddr, + host_mem_info->endAddr, host_mem_info->type); +} + +static void ps3_ioc_init_cmd_prepare(struct ps3_instance *instance) +{ + struct ps3_cmd_context *cmd_context = &instance->cmd_context; + struct ps3_irq_context *irq_context = &instance->irq_context; + struct ps3_cmd_attr_context *cmd_attr = &instance->cmd_attr; + struct ps3_dump_context *dump_context = &instance->dump_context; + struct PS3InitReqFrame *init_frame_msg = NULL; + + init_frame_msg = (struct PS3InitReqFrame *)cmd_context->init_frame_buf; + + memset(&init_frame_msg->reqHead, 0, sizeof(struct PS3ReqFrameHead)); + init_frame_msg->reqHead.cmdType = PS3_CMD_INIT_IOC; + init_frame_msg->reqHead.timeout = PS3_INIT_CMD_WAIT_MAX_TIMEOUT; + + init_frame_msg->ver = 0; + init_frame_msg->length = sizeof(struct PS3InitReqFrame); + init_frame_msg->operater = PS3_CMD_OPERATOR_TYPE_HOST; + init_frame_msg->pageSize = (unsigned char)cmd_attr->nvme_page_size; + init_frame_msg->msixVector = + (unsigned short)irq_context->valid_msix_vector_count; + init_frame_msg->pciIrqType = (unsigned short)irq_context->pci_irq_type; + init_frame_msg->osType = PS3_LINUX_FRAME; + + init_frame_msg->timeStamp = cpu_to_le64(ps3_1970_now_ms_get()); + init_frame_msg->reqFrameBufBaseAddr = + cpu_to_le64(cmd_context->req_frame_buf_phys); + + init_frame_msg->replyFifoDescBaseAddr = + cpu_to_le64(irq_context->reply_fifo_desc_buf_phys); + + init_frame_msg->respFrameBaseAddr = + cpu_to_le64(cmd_context->response_frame_buf_phys); + + init_frame_msg->reqFrameMaxNum = + (unsigned short)cmd_context->max_cmd_count; + init_frame_msg->respFrameMaxNum = + (unsigned short)cmd_context->max_cmd_count; + init_frame_msg->bufSizePerRespFrame = PS3_RESP_FRAME_BUFFER_SIZE; + + + if (ps3_hil_mode_query() > HIL_MODEL_SW_ASSIST) + init_frame_msg->hilMode = instance->hilMode; + else + init_frame_msg->hilMode = (unsigned char)ps3_hil_mode_query(); + +#ifndef _WINDOWS + init_frame_msg->systemInfoBufAddr = + cmd_context->init_frame_sys_info_phys; +#endif + if (!reset_devices) { + init_frame_msg->dumpDmaBufAddr = + cpu_to_le64(dump_context->dump_dma_addr); + init_frame_msg->dumpDmaBufLen = + cpu_to_le32(PS3_DUMP_DMA_BUF_SIZE); + init_frame_msg->dumpIsrSN = + cpu_to_le32(irq_context->dump_isrSN); + } +#ifndef _WINDOWS + init_frame_msg->debugMemArrayNum = + cpu_to_le32(instance->debug_context.debug_mem_array_num); + init_frame_msg->debugMemArrayAddr = + (instance->debug_context.debug_mem_array_num != 0) ? + cpu_to_le64(instance->debug_context.debug_mem_buf_phy) : + 0; +#endif + memset(cmd_context->init_filter_table_buff, 0, + PS3_CMD_EXT_BUF_SIZE_MGR); + if (instance->ioc_adpter->event_filter_table_get != NULL) { + init_frame_msg->filterTableAddr = + cpu_to_le64(cmd_context->init_filter_table_phy_addr); + init_frame_msg->filterTableLen = + cpu_to_le32(PS3_CMD_EXT_BUF_SIZE_MGR); + instance->ioc_adpter->event_filter_table_get( + cmd_context->init_filter_table_buff); + } + ps3_drv_info_prepare(instance); + init_frame_msg->drvInfoBufAddr = + cpu_to_le64(instance->drv_info_buf_phys); + init_frame_msg->drvInfoBufLen = cpu_to_le16(sizeof(struct PS3DrvInfo)); + ps3_get_all_numa_mem_addr(instance); + if (instance->end_pfn != 0) { + ps3_host_mem_info_prepare(instance); + init_frame_msg->hostMemInfoBaseAddr = + cpu_to_le64(instance->host_mem_info_buf_phys); + init_frame_msg->hostMemInfoNum = + cpu_to_le32(PS3_HOST_MEM_INFO_NUM); + } else { + init_frame_msg->hostMemInfoBaseAddr = 0; + init_frame_msg->hostMemInfoNum = 0; + } + LOG_DEBUG( + "hno:%u init frame information dump:\n" + "\t initFramePhysicAddr = 0x%llx, initFrameLen = %d, version = %d\n" + "\t pciIrqType = %d, msixVectorCount = %d, reqHead.cmdType = %d\n" + "\t reqFrameBufBase = 0x%llx, reqFrameMaxNum = %d\n" + "\t responseFrameBase = 0x%llx, responseFrameMaxNum = %d, responseFrameSize = %d\n" + "\t replyFifoDescBase = 0x%llx, hostSystemInfoBuf = %llx\n" + "\t filterTableAddr = 0x%llx, filterTableLen = %d\n" + "\t drvInfoBufAddr = 0x%llx, drvInfoBufLen = %d\n" + "\t hostMemInfoBaseAddr = 0x%llx, hostMemInfoNum = %d, function = %d\n", + PS3_HOST(instance), + cpu_to_le64(cmd_context->init_frame_buf_phys), + init_frame_msg->length, init_frame_msg->ver, + init_frame_msg->pciIrqType, init_frame_msg->msixVector, + init_frame_msg->reqHead.cmdType, + init_frame_msg->reqFrameBufBaseAddr, + init_frame_msg->reqFrameMaxNum, + init_frame_msg->respFrameBaseAddr, + init_frame_msg->reqFrameMaxNum, + init_frame_msg->bufSizePerRespFrame, + init_frame_msg->replyFifoDescBaseAddr, + init_frame_msg->systemInfoBufAddr, + init_frame_msg->filterTableAddr, init_frame_msg->filterTableLen, + init_frame_msg->drvInfoBufAddr, init_frame_msg->drvInfoBufLen, + init_frame_msg->hostMemInfoBaseAddr, + init_frame_msg->hostMemInfoNum, + ps3_get_pci_function(instance->pdev)); +} + +static int ps3_ioc_init_cmd_proc(struct ps3_instance *instance) +{ + struct PS3MgrEvent *event_req_info = &instance->event_req_info; + struct ps3_cmd_context *cmd_context = &instance->cmd_context; + struct PS3InitReqFrame *init_frame_msg = NULL; + + ps3_ioc_init_cmd_prepare(instance); + + init_frame_msg = (struct PS3InitReqFrame *)cmd_context->init_frame_buf; + init_frame_msg->eventTypeMap = event_req_info->eventTypeMap; + + init_frame_msg->respStatus = U32_MAX; + + ps3_all_reply_fifo_init(instance); + LOG_WARN( + "hno:%u start to send init cmd![init:0x%llx, respstats:0x%llx]\n", + PS3_HOST(instance), + (unsigned long long)cmd_context->init_frame_buf_phys, + (unsigned long long)cmd_context->init_frame_buf_phys + + offsetof(struct PS3InitReqFrame, respStatus)); + + return ps3_ioc_init_cmd_issue(instance, init_frame_msg); +} +int ps3_ioc_init_proc(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + int state = PS3_FW_STATE_UNDEFINED; + int retry = 0; + + ps3_ioc_can_hardreset_set(instance, PS3_IOC_CANNOT_HARDRESET); + while (retry < PS3_IOC_INIT_PROC_FAIL_RETRY_COUNT) { + ret = ps3_ioc_init_cmd_proc(instance); + if (ret != PS3_SUCCESS) + goto l_clean; + + ret = ps3_ioc_state_transfer_wait_to_running(instance); + if (ret != PS3_SUCCESS) { + if (ret == -PS3_IN_PCIE_ERR) + goto l_out; + goto l_clean; + } + + goto l_out; +l_clean: + state = instance->ioc_adpter->ioc_state_get(instance); + if (state == PS3_FW_STATE_WAIT || + state == PS3_FW_STATE_RUNNING) { + if (ps3_soc_unload(instance, PS3_TRUE, + PS3_UNLOAD_SUB_TYPE_REMOVE, + PS3_SUSPEND_TYPE_NONE) == + PS3_SUCCESS) { + LOG_INFO( + "device[%d] unload success,exit init proc.\n", + instance->pdev->dev.id); + retry++; + continue; + } + } + ret = ps3_init_fail_hard_reset_with_doorbell(instance); + if (ret != PS3_SUCCESS) { + LOG_ERROR("device[%d] hard reset NOK,exit init proc.\n", + instance->pdev->dev.id); + goto l_out; + } + retry++; + } + if (retry == PS3_IOC_INIT_PROC_FAIL_RETRY_COUNT) + ret = -PS3_FAILED; + LOG_INFO("device[%d] doorbell success,exit init proc [%d].\n", + instance->pdev->dev.id, ret); + +l_out: + return ret; +} + +void ps3_ioc_legacy_irqs_enable(struct ps3_instance *instance) +{ + LOG_INFO("hno:%u Enable legacy!\n", PS3_HOST(instance)); +#ifndef _WINDOWS + pci_intx(instance->pdev, 1); +#else + ps3_pci_intx(instance, 1); +#endif +} + +void ps3_ioc_legacy_irqs_disable(struct ps3_instance *instance) +{ + LOG_INFO("hno:%u Disable legacy!\n", PS3_HOST(instance)); +#ifndef _WINDOWS + pci_intx(instance->pdev, 0); +#else + ps3_pci_intx(instance, 0); +#endif +} + +void ps3_ioc_msi_enable(struct ps3_instance *instance) +{ + unsigned int pos = 0; + unsigned short control = 0; + + pos = ps3_pci_find_capability(instance, PCI_CAP_ID_MSI); + if (pos == 0) { + LOG_INFO("hno:%u Find PCI_CAP_ID_MSI failed!\n", + PS3_HOST(instance)); + return; + } + + ps3_pci_read_config_word(instance, pos + PCI_MSI_FLAGS, &control); + if (!(control & PCI_MSI_FLAGS_ENABLE)) { + LOG_INFO("hno:%u Enable Msi!\n", PS3_HOST(instance)); + ps3_pci_write_config_word(instance, pos + PCI_MSI_FLAGS, + control | PCI_MSI_FLAGS_ENABLE); + } +} + +void ps3_ioc_msi_disable(struct ps3_instance *instance) +{ + unsigned int pos = 0; + unsigned short control = 0; + + pos = ps3_pci_find_capability(instance, PCI_CAP_ID_MSI); + if (pos == 0) { + LOG_ERROR("hno:%u Find PCI_CAP_ID_MSI failed!\n", + PS3_HOST(instance)); + return; + } + + ps3_pci_read_config_word(instance, pos + PCI_MSI_FLAGS, &control); + if (control & PCI_MSI_FLAGS_ENABLE) { + LOG_INFO("hno:%u Disable Msi!\n", PS3_HOST(instance)); + ps3_pci_write_config_word(instance, pos + PCI_MSI_FLAGS, + control & ~PCI_MSI_FLAGS_ENABLE); + } +} + +void ps3_ioc_msix_enable(struct ps3_instance *instance) +{ + unsigned int pos = 0; + unsigned short control = 0; + + pos = ps3_pci_find_capability(instance, PCI_CAP_ID_MSIX); + if (pos == 0) { + LOG_ERROR("hno:%u Find PCI_CAP_ID_MSIX failed!\n", + PS3_HOST(instance)); + return; + } + + ps3_pci_read_config_word(instance, pos + PCI_MSIX_FLAGS, &control); + if (!(control & PCI_MSIX_FLAGS_ENABLE)) { + LOG_INFO("hno:%u Enable Msix!\n", PS3_HOST(instance)); + ps3_pci_write_config_word(instance, pos + PCI_MSIX_FLAGS, + control | PCI_MSIX_FLAGS_ENABLE); + } +} + +void ps3_ioc_msix_disable(struct ps3_instance *instance) +{ + unsigned int pos = 0; + unsigned short control = 0; + + pos = ps3_pci_find_capability(instance, PCI_CAP_ID_MSIX); + if (pos == 0) { + LOG_ERROR("hno:%u Find PCI_CAP_ID_MSIX failed!\n", + PS3_HOST(instance)); + return; + } + + ps3_pci_read_config_word(instance, pos + PCI_MSIX_FLAGS, &control); + if (control & PCI_MSIX_FLAGS_ENABLE) { + LOG_INFO("hno:%u disable Msix!\n", PS3_HOST(instance)); + ps3_pci_write_config_word(instance, pos + PCI_MSIX_FLAGS, + control & ~PCI_MSIX_FLAGS_ENABLE); + } +} + +unsigned char ps3_ioc_is_legacy_irq_existed(struct ps3_instance *instance) +{ + unsigned short status = 0; + unsigned char is_legacy_irq_existed = PS3_FALSE; + + ps3_pci_read_config_word(instance, PCI_COMMAND, &status); + if (status & PCI_COMMAND_INTX_DISABLE) { + LOG_INFO("hno:%u Legacy irq is disabled!\n", + PS3_HOST(instance)); + goto l_out; + } + + ps3_pci_read_config_word(instance, PCI_STATUS, &status); + if (status & PCI_STATUS_INTERRUPT) { + LOG_INFO("hno:%u Legacy irq is existed!\n", + PS3_HOST(instance)); + is_legacy_irq_existed = PS3_TRUE; + } + +l_out: + return is_legacy_irq_existed; +} + +void ps3_ioc_cmd_send(struct ps3_instance *instance, + struct PS3CmdWord *cmd_word) +{ + union PS3DefaultCmdWord *cmd_word_default = NULL; + + PS3_CMD_WORD_STAT_INC(instance, cmd_word); + + cmd_word_default = (union PS3DefaultCmdWord *)cmd_word; + + PS3_IOC_REG_WRITE(instance, cmd_fifo.request_fifo, ps3RequestQueue, + cmd_word_default->words); +} + +void ps3_ioc_scsi_cmd_send(struct ps3_instance *instance, + struct PS3CmdWord *cmd_word) +{ + union PS3DefaultCmdWord *cmd_word_default = NULL; + + PS3_CMD_WORD_STAT_INC(instance, cmd_word); + cmd_word_default = (union PS3DefaultCmdWord *)cmd_word; + + if (instance->is_pci_reset == PS3_FALSE) { + ps3_ioc_reg_write(instance, cmd_word_default->words, + &instance->reg_set->cmd_fifo.request_fifo + .ps3RequestQueue); + } else { + LOG_FILE_ERROR("hno:%u register %p,write blocked by pci err\n", + PS3_HOST(instance), + &instance->reg_set->cmd_fifo.request_fifo + .ps3RequestQueue); + } +} + +void ps3_switch_normal_cmd_send(struct ps3_instance *instance, + struct PS3CmdWord *cmd_word) +{ + union PS3CmdWordU32 cmd_word_u32; + union PS3DefaultCmdWord cmd_word_default; + struct ps3_cmd *cmd = NULL; + + cmd = ps3_cmd_find(instance, cmd_word->cmdFrameID); + + PS3_CMD_WORD_STAT_INC(instance, cmd_word); + + cmd_word_u32.cmdWord.cmdFrameID = cmd_word->cmdFrameID; + cmd_word_u32.cmdWord.type = cmd_word->type; + cmd_word_u32.cmdWord.isrSN = cmd_word->isrSN; + cmd_word_u32.cmdWord.noReplyWord = + cmd->req_frame->frontendReq.reqHead.noReplyWord; + + cmd_word_default.words = 0; + cmd_word_default.u.low = cmd_word_u32.val; + PS3_IOC_REG_WRITE(instance, reg_f.Excl_reg, reserved0, + cmd_word_default.words); +} + +void ps3_switch_init_cmd_send(struct ps3_instance *instance, + struct PS3CmdWord *cmd_word) +{ + union PS3DefaultCmdWord *cmd_word_default = NULL; + + PS3_CMD_WORD_STAT_INC(instance, cmd_word); + + cmd_word_default = (union PS3DefaultCmdWord *)cmd_word; + ps3_atomic_inc(&(instance)->reg_op_count); + mb(); /* in order to force CPU ordering */ + if ((instance)->is_hard_reset == PS3_FALSE && + (instance)->is_pci_reset == PS3_FALSE) { + PS3_REG_SESSION_ADDR_WRITE( + instance, cmd_word_default->words, + &instance->reg_set->reg_f.Excl_reg.ps3SessioncmdAddr); + } else { + LOG_ERROR( + "hno:%u register %p,write blocked by hardreset(%d) or pci recovery(%d)\n", + PS3_HOST(instance), + &instance->reg_set->reg_f.Excl_reg.ps3SessioncmdAddr, + (instance)->is_hard_reset, instance->is_pci_reset); + } + ps3_atomic_dec(&(instance)->reg_op_count); + + LOG_DEBUG("hno:%u init qommand: cmd.words = 0x%llx, session_reg_offset = %#lx\n", + PS3_HOST(instance), cmd_word_default->words, + (unsigned long)offsetof(struct HilReg0Ps3RegisterF, + ps3SessioncmdAddr)); +} +#ifndef _WINDOWS +unsigned long long ps3_switch_ioc_reg_read(struct ps3_instance *instance, + void __iomem *reg) +{ + unsigned long long value = 0; + + value = (((unsigned long long)readl(reg + 0x4UL) << 32) | + (unsigned long long)readl(reg)); + if (&instance->reg_set->cmd_fifo.request_fifo.ps3RequestQueue == reg) + reg = reg - PS3_REG_SWITCH_QEQUEST_QUEUE_OFFSET; + ps3_reg_dump(instance, reg, value, PS3_TRUE); + return value; +} +#endif +inline void ps3_ioc_reg_write(struct ps3_instance *instance, + unsigned long long val, void __iomem *reg) +{ + ps3_reg_dump(instance, reg, val, PS3_FALSE); + if (instance->ioc_adpter->reg_write) { + instance->ioc_adpter->reg_write(instance, val, reg); + } else { + LOG_FILE_ERROR("hno:%u no register write\n", + PS3_HOST(instance)); + } +} +void ps3_ioc_hardreset_reg_write(struct ps3_instance *instance, + unsigned long long val, void __iomem *reg, + unsigned char is_warn_prk) +{ + ps3_reg_dump(instance, reg, val, PS3_FALSE); + if (instance->ioc_adpter->reg_write) { + if (is_warn_prk) { + LOG_WARN( + "hno:%u register write reset,val:0x%llx,reg=%p\n", + PS3_HOST(instance), val, reg); + } + instance->ioc_adpter->reg_write(instance, val, reg); + } else { + LOG_ERROR("hno:%u no register write\n", PS3_HOST(instance)); + } +} + +inline unsigned long long ps3_ioc_reg_read(struct ps3_instance *instance, + void __iomem *reg) +{ + unsigned long long value = 0; + + if (instance->ioc_adpter->reg_read) + value = instance->ioc_adpter->reg_read(instance, reg); + else + LOG_ERROR("hno:%u no register read\n", PS3_HOST(instance)); + + ps3_reg_dump(instance, reg, value, PS3_TRUE); + return value; +} +unsigned long long ps3_ioc_hardreset_reg_read(struct ps3_instance *instance, + void __iomem *reg) +{ + unsigned long long value = 0; + + if (instance->ioc_adpter->reg_read) + value = instance->ioc_adpter->reg_read(instance, reg); + else + LOG_ERROR("hno:%u no register read\n", PS3_HOST(instance)); + + + return value; +} +inline unsigned long long +ps3_ioc_reg_read_with_check(struct ps3_instance *instance, void __iomem *reg) +{ + unsigned char try_count = 0; + unsigned long long reg_value = ps3_ioc_reg_read(instance, reg); + + while (reg_value == U64_MAX && + try_count != PS3_REG_READ_MAX_TRY_COUNT) { + ps3_msleep(PS3_REG_READ_INTERVAL_MS); + try_count++; + reg_value = ps3_ioc_reg_read(instance, reg); + } + + return reg_value; +} + +static unsigned long long ps3_ioc_reg_safe_read(struct ps3_instance *instance, + void __iomem *reg) +{ + unsigned int fw_cur_state = PS3_FW_STATE_UNDEFINED; + unsigned int count = 0; + unsigned int retry_cnt = 0; + unsigned long long tmp_value = U64_MAX; + unsigned long long value = U64_MAX; + unsigned char is_first = PS3_TRUE; + + for (; retry_cnt < PS3_REG_READ_SAFE_RETRY_NUM; retry_cnt++) { + tmp_value = ps3_ioc_reg_read_with_check(instance, reg); + if (tmp_value == U64_MAX) { + value = U64_MAX; + goto l_out; + } + fw_cur_state = instance->ioc_adpter->ioc_state_get(instance); + if (!ps3_ioc_state_valid_check(fw_cur_state)) { + for (; count < PS3_REG_READ_RETRY_NUM; count++) { + fw_cur_state = + instance->ioc_adpter->ioc_state_get( + instance); + if (ps3_ioc_state_valid_check(fw_cur_state)) { + value = ps3_ioc_reg_read_with_check( + instance, reg); + goto l_out; + } + ps3_msleep(PS3_LOOP_TIME_INTERVAL_20MS); + } + value = U64_MAX; + LOG_WARN_LIM("hno:%u wait ioc to valid state NOK\n", + PS3_HOST(instance)); + goto l_out; + } + if (is_first) { + value = tmp_value; + is_first = PS3_FALSE; + continue; + } + if (value != tmp_value) { + LOG_WARN_LIM( + "hno:%u reg value not equal old-new[%llu, %llu]\n", + PS3_HOST(instance), value, tmp_value); + value = U64_MAX; + goto l_out; + } + ps3_msleep(PS3_LOOP_TIME_INTERVAL_50MS); + } +l_out: + return value; +} + +unsigned long long ps3_ioc_reg_retry_safe_read(struct ps3_instance *instance, + void __iomem *reg) +{ + unsigned int retry_cnt = PS3_REG_READ_SAFE_RETRY_NUM; + unsigned long long reg_value = 0; + + while (retry_cnt--) { + reg_value = ps3_ioc_reg_safe_read(instance, reg); + if (reg_value != U64_MAX) + break; + ps3_msleep(PS3_LOOP_TIME_INTERVAL_20MS); + } + return reg_value; +} + +unsigned char ps3_feature_support_reg_get(struct ps3_instance *instance) +{ + unsigned char ret = PS3_FALSE; + union HilReg0Ps3RegisterFPs3FeatureSupport *ps3_feature_support = NULL; + unsigned long long value = 0; + + PS3_IOC_REG_READ_SAFE_WITH_RETRY(instance, reg_f.Excl_reg, + ps3FeatureSupport, value); + if (value == U64_MAX) { + LOG_ERROR("hno:%u read reg ps3FeatureSupport NOK!\n", + PS3_HOST(instance)); + goto l_out; + } + ps3_feature_support = + (union HilReg0Ps3RegisterFPs3FeatureSupport *)&value; + instance->is_ioc_halt_support = + (ps3_feature_support->reg.fwHaltSupport == 1); + instance->is_shallow_soft_recovery_support = + (ps3_feature_support->reg.shallowSoftRecoverySupport == 1); + instance->is_deep_soft_recovery_support = + (ps3_feature_support->reg.deepSoftRecoverySupport == 1); + instance->is_hard_recovery_support = + (ps3_feature_support->reg.hardRecoverySupport == 1); + instance->cmd_context.sgl_mode_support = + (ps3_feature_support->reg.sglModeSupport == 1); + ret = PS3_TRUE; +l_out: + return ret; +} diff --git a/drivers/scsi/linkdata/ps3stor/ps3_ioc_manager.h b/drivers/scsi/linkdata/ps3stor/ps3_ioc_manager.h new file mode 100644 index 0000000000000000000000000000000000000000..25bd0d1d255be234d162e838ed127d285b7f875b --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/ps3_ioc_manager.h @@ -0,0 +1,925 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _PS3_IOC_MANAGER_H_ +#define _PS3_IOC_MANAGER_H_ + +#include "ps3_instance_manager.h" +#include "ps3_inner_data.h" +#include "ps3_module_para.h" +#include "ps3_ioc_state.h" + +#define PS3_REG_SESSION_ADDR_WRITE(instance, val, reg) \ + do { \ + ps3_reg_dump(instance, reg, val, PS3_FALSE); \ + ps3_ioc_reg_write(instance, val, reg); \ + } while (0) + +#define PS3_MAX_CMD_COUNT_DURING_RESET_DEVICES (100) +#define PS3_REG_READ_INTERVAL_MS (10) +#if (defined PS3_HARDWARE_FPGA && defined PS3_MODEL_V200) +#define PS3_REG_WRITE_RETRY_NUM (60) +#else +#define PS3_REG_WRITE_RETRY_NUM (10) +#endif +#define PS3_REG_WRITE_INTERVAL_MS (10) +#define PS3_REG_READ_RETRY_NUM (5000) +#define PS3_ATU_SUPPORT_READ_RETRY_NUM (3) +#define PS3_REG_READ_SAFE_RETRY_NUM (3) +#define PS3_HOST_MEM_INFO_NUM (1) +#define PS3_IOC_STATE_HALT_SUPPORT(ins) (ins->is_ioc_halt_support) + +#define PS3_IOC_SHALLOW_SOFT_RECOVERY_SUPPORT(ins) \ + (ins->is_shallow_soft_recovery_support) + +#define PS3_IOC_DEEP_SOFT_RECOVERY_SUPPORT(ins) \ + ((ins->is_deep_soft_recovery_support) && \ + ps3_deep_soft_reset_enable_query()) + +#define PS3_IOC_HARD_RECOVERY_SUPPORT(ins) \ + (PS3_INSTANCE_ABNORMAL_FORCE_HARD_RECOVERY(ins) || \ + ins->is_hard_recovery_support) + +#define PS3_HALT_CLI_SUPPORT(ins) (ins->is_halt_support_cli) + +#define PS3_IOC_REG_READ_WITH_CHECK(instance, reg_type, reg_name, read_value) \ + do { \ + ps3_atomic_inc(&(instance)->reg_op_count); \ + mb(); /* in order to force CPU ordering */ \ + if ((instance)->is_hard_reset == PS3_FALSE && \ + (instance)->is_pci_reset == PS3_FALSE) { \ + read_value = ps3_ioc_reg_read_with_check( \ + instance, \ + &(instance)->reg_set->reg_type.reg_name); \ + } else { \ + LOG_FILE_ERROR( \ + "hno:%u register %p,read blocked by hardreset(%d)\n" \ + "\tor pci err(%d)\n", \ + PS3_HOST(instance), \ + &(instance)->reg_set->reg_type.reg_name, \ + (instance)->is_hard_reset, \ + (instance)->is_pci_reset); \ + read_value = U64_MAX; \ + } \ + ps3_atomic_dec(&(instance)->reg_op_count); \ + } while (0) + +#define PS3_IOC_REG_READ_SAFE_WITH_RETRY(instance, reg_type, reg_name, \ + read_value) \ + do { \ + ps3_atomic_inc(&(instance)->reg_op_count); \ + mb(); /* in order to force CPU ordering */ \ + if ((instance)->is_hard_reset == PS3_FALSE && \ + (instance)->is_pci_reset == PS3_FALSE) { \ + read_value = ps3_ioc_reg_retry_safe_read( \ + instance, \ + &(instance)->reg_set->reg_type.reg_name); \ + } else { \ + LOG_FILE_ERROR( \ + "hno:%u register %p,read blocked by hardreset(%d)\n" \ + "\tor pci err(%d)\n", \ + PS3_HOST(instance), \ + &(instance)->reg_set->reg_type.reg_name, \ + (instance)->is_hard_reset, \ + (instance)->is_pci_reset); \ + read_value = U64_MAX; \ + } \ + ps3_atomic_dec(&(instance)->reg_op_count); \ + } while (0) + +#define PS3_IOC_REG_READ_OFFSET(instance, offset, read_value) \ + do { \ + ps3_atomic_inc(&(instance)->reg_op_count); \ + mb(); /* in order to force CPU ordering */ \ + if ((instance)->is_hard_reset == PS3_FALSE && \ + (instance)->is_pci_reset == PS3_FALSE) { \ + read_value = ps3_ioc_reg_read( \ + instance, \ + (unsigned char *)(instance)->reg_set + \ + (offset)); \ + } else { \ + LOG_FILE_ERROR( \ + "hno:%u register %p, read blocked by hardreset(%d)\n" \ + "\tor pci err(%d)\n", \ + PS3_HOST(instance), \ + (unsigned char *)(instance)->reg_set + \ + (offset), \ + (instance)->is_hard_reset, \ + instance->is_pci_reset); \ + read_value = U64_MAX; \ + } \ + ps3_atomic_dec(&(instance)->reg_op_count); \ + } while (0) + +#define PS3_IOC_REG_READ_OFFSET_WITCH_CHECK(instance, offset, read_value) \ + do { \ + ps3_atomic_inc(&(instance)->reg_op_count); \ + mb(); /* in order to force CPU ordering */ \ + if ((instance)->is_hard_reset == PS3_FALSE && \ + (instance)->is_pci_reset == PS3_FALSE) { \ + read_value = ps3_ioc_reg_read_with_check( \ + instance, \ + (unsigned char *)(instance)->reg_set + \ + (offset)); \ + } else { \ + LOG_WARN( \ + "hno:%u register %p, read blocked by hardreset(%d)\n" \ + "\tor pci recovery(%d)\n", \ + PS3_HOST(instance), \ + (unsigned char *)(instance)->reg_set + \ + (offset), \ + (instance)->is_hard_reset, \ + instance->is_pci_reset); \ + read_value = U64_MAX; \ + } \ + ps3_atomic_dec(&(instance)->reg_op_count); \ + } while (0) + +#define PS3_IOC_REG_WRITE(instance, reg_type, reg_name, value) \ + do { \ + ps3_atomic_inc(&(instance)->reg_op_count); \ + mb(); /* in order to force CPU ordering */ \ + if ((instance)->is_hard_reset == PS3_FALSE && \ + (instance)->is_pci_reset == PS3_FALSE) { \ + ps3_ioc_reg_write( \ + instance, value, \ + &(instance)->reg_set->reg_type.reg_name); \ + } else { \ + LOG_FILE_ERROR( \ + "hno:%u register %p,write blocked by hardreset(%d)\n" \ + "\tor pci err(%d)\n", \ + PS3_HOST(instance), \ + &(instance)->reg_set->reg_type.reg_name, \ + (instance)->is_hard_reset, \ + instance->is_pci_reset); \ + } \ + ps3_atomic_dec(&(instance)->reg_op_count); \ + } while (0) + +#define PS3_IOC_REG_WRITE_OFFSET(instance, offset, value) \ + do { \ + ps3_atomic_inc(&(instance)->reg_op_count); \ + mb(); /* in order to force CPU ordering */ \ + if ((instance)->is_hard_reset == PS3_FALSE && \ + (instance)->is_pci_reset == PS3_FALSE) { \ + ps3_ioc_reg_write( \ + instance, value, \ + (unsigned char *)(instance)->reg_set + \ + (offset)); \ + } else { \ + LOG_FILE_ERROR( \ + "hno:%u register %p,write blocked by hardreset(%d)\n" \ + "\tor pci err(%d)\n", \ + PS3_HOST(instance), \ + (unsigned char *)(instance)->reg_set + \ + (offset), \ + (instance)->is_hard_reset, \ + instance->is_pci_reset); \ + } \ + ps3_atomic_dec(&(instance)->reg_op_count); \ + } while (0) + +#define PS3_IOC_REG_WRITE_WITH_CHECK(instance, reg_type, reg_name, value) \ + do { \ + ps3_atomic_inc(&(instance)->reg_op_count); \ + mb(); /* in order to force CPU ordering */ \ + if ((instance)->is_hard_reset == PS3_FALSE && \ + (instance)->is_pci_reset == PS3_FALSE) { \ + ps3_ioc_reg_write_with_check( \ + instance, value, \ + &(instance)->reg_set->reg_type.reg_name); \ + } else { \ + LOG_WARN( \ + "hno:%u register %p,write blocked by hardreset(%d)\n" \ + "\tor pci recovery(%d)\n", \ + PS3_HOST(instance), \ + &(instance)->reg_set->reg_type.reg_name, \ + (instance)->is_hard_reset, \ + instance->is_pci_reset); \ + } \ + ps3_atomic_dec(&(instance)->reg_op_count); \ + } while (0) + +#define PS3_IOC_REG_WRITE_OFFSET_WITH_CHECK(instance, offset, value) \ + do { \ + ps3_atomic_inc(&(instance)->reg_op_count); \ + mb(); /* in order to force CPU ordering */ \ + if ((instance)->is_hard_reset == PS3_FALSE && \ + (instance)->is_pci_reset == PS3_FALSE) { \ + ps3_ioc_reg_write_with_check( \ + instance, value, \ + (unsigned char *)(instance)->reg_set + \ + (offset)); \ + } else { \ + LOG_WARN( \ + "hno:%u register %p,write blocked by hardreset(%d)\n" \ + "\tor pci recovery(%d)\n", \ + PS3_HOST(instance), \ + (unsigned char *)(instance)->reg_set + \ + (offset), \ + (instance)->is_hard_reset, \ + instance->is_pci_reset); \ + } \ + ps3_atomic_dec(&(instance)->reg_op_count); \ + } while (0) + +int ps3_ioc_init_cmd_context_init(struct ps3_instance *instance); +void ps3_ioc_init_cmd_context_exit(struct ps3_instance *instance); +void ps3_ioc_cmd_send(struct ps3_instance *instance, + struct PS3CmdWord *cmd_word); +void ps3_switch_init_cmd_send(struct ps3_instance *instance, + struct PS3CmdWord *cmd_word); +void ps3_switch_normal_cmd_send(struct ps3_instance *instance, + struct PS3CmdWord *cmd_word); +void ps3_ioc_legacy_irqs_enable(struct ps3_instance *instance); +void ps3_ioc_legacy_irqs_disable(struct ps3_instance *instance); +void ps3_ioc_msi_enable(struct ps3_instance *instance); +void ps3_ioc_msi_disable(struct ps3_instance *instance); +void ps3_ioc_msix_enable(struct ps3_instance *instance); +void ps3_ioc_msix_disable(struct ps3_instance *instance); +unsigned char ps3_ioc_is_legacy_irq_existed(struct ps3_instance *instance); +int ps3_drv_info_buf_alloc(struct ps3_instance *instance); + +void ps3_drv_info_buf_free(struct ps3_instance *instance); + +int ps3_host_mem_info_buf_alloc(struct ps3_instance *instance); + +void ps3_host_mem_info_buf_free(struct ps3_instance *instance); + +int ps3_ioc_init_to_ready(struct ps3_instance *instance); + +int ps3_ioc_hard_reset_to_ready(struct ps3_instance *instance); + +int ps3_ioc_init_proc(struct ps3_instance *instance); + +void ps3_ioc_reg_write(struct ps3_instance *instance, unsigned long long val, + void __iomem *reg); + +void ps3_ioc_hardreset_reg_write(struct ps3_instance *instance, + unsigned long long val, void __iomem *reg, + unsigned char is_warn_prk); + +unsigned long long ps3_ioc_hardreset_reg_read(struct ps3_instance *instance, + void __iomem *reg); + +unsigned long long ps3_ioc_reg_read(struct ps3_instance *instance, + void __iomem *reg); + +unsigned long long ps3_ioc_reg_read_with_check(struct ps3_instance *instance, + void __iomem *reg); + +static inline unsigned char ps3_ioc_state_valid_check(unsigned int fw_cur_state) +{ + return ((fw_cur_state > PS3_FW_STATE_START) && + (fw_cur_state != PS3_FW_STATE_MASK)); +} + +unsigned long long ps3_ioc_reg_retry_safe_read(struct ps3_instance *instance, + void __iomem *reg); + +static inline void ps3_ioc_reg_write_with_check(struct ps3_instance *instance, + unsigned long long val, + void __iomem *reg) +{ + unsigned char try_count = 0; + + ps3_reg_dump(instance, reg, val, PS3_FALSE); + if (instance->ioc_adpter->reg_write) { + while (try_count != PS3_REG_WRITE_RETRY_NUM) { + instance->ioc_adpter->reg_write(instance, val, reg); + if (instance->ioc_adpter->reg_read(instance, reg) == + val) { + break; + } + try_count++; + ps3_msleep(PS3_REG_WRITE_INTERVAL_MS); + } + } else { + LOG_ERROR("hno:%u no register write\n", PS3_HOST(instance)); + } + + if (try_count == PS3_REG_WRITE_RETRY_NUM) + ps3_instance_state_transfer_to_dead(instance); +} + +unsigned long long ps3_switch_ioc_reg_read(struct ps3_instance *instance, + void __iomem *reg); + +static inline void +ps3_ioc_mgr_req_queue_lock_init(struct ps3_instance *instance) +{ + ps3_spin_lock_init(&instance->req_queue_lock); +} + +static inline unsigned char +ps3_ioc_mgr_max_fw_cmd_get(struct ps3_instance *instance, + unsigned int *max_cmd_count) +{ + unsigned char ret = PS3_TRUE; + union HilReg0Ps3RegisterFPs3MaxFwCmd *reg_max_fw_cmd = NULL; + unsigned long long value = 0; + + PS3_IOC_REG_READ_SAFE_WITH_RETRY(instance, reg_f.Excl_reg, ps3MaxFwCmd, + value); + if (value == U64_MAX) { + LOG_ERROR("hno:%u read reg ps3MaxFwCmd NOK!\n", + PS3_HOST(instance)); + *max_cmd_count = 0; + ret = PS3_FALSE; + goto l_out; + } + + reg_max_fw_cmd = (union HilReg0Ps3RegisterFPs3MaxFwCmd *)&value; + *max_cmd_count = (unsigned int)reg_max_fw_cmd->reg.ps3MaxFwCmd; + + if (instance->is_adjust_register_count) + *max_cmd_count += 1; + if (reset_devices && + *max_cmd_count > PS3_MAX_CMD_COUNT_DURING_RESET_DEVICES) { + *max_cmd_count = PS3_MAX_CMD_COUNT_DURING_RESET_DEVICES; + } +l_out: + return ret; +} + +static inline unsigned int +ps3_ioc_mgr_max_msix_vectors_get(struct ps3_instance *instance) +{ + unsigned long long reg_max_msix_vectors = PS3_MAX_REPLY_QUE_COUNT; + (void)instance; + return (unsigned int)(reg_max_msix_vectors & + PS3_FW_MAX_MSIX_VECTORS_MASK); +} + +static inline unsigned char +ps3_ioc_mgr_max_chain_size_get(struct ps3_instance *instance, + unsigned int *max_chain_size) +{ + unsigned char ret = PS3_TRUE; + union HilReg0Ps3RegisterFPs3MaxChainSize *max_chain_size_u = NULL; + unsigned long long value = 0; + + PS3_IOC_REG_READ_SAFE_WITH_RETRY(instance, reg_f.Excl_reg, + ps3MaxChainSize, value); + if (value == U64_MAX) { + LOG_ERROR("hno:%u read reg ps3MaxChainSize NOK!\n", + PS3_HOST(instance)); + *max_chain_size = 0; + ret = PS3_FALSE; + goto l_out; + } + + max_chain_size_u = (union HilReg0Ps3RegisterFPs3MaxChainSize *)&value; + *max_chain_size = (unsigned int)max_chain_size_u->reg.ps3MaxChainSize; +l_out: + return ret; +} + +static inline unsigned char +ps3_ioc_mgr_max_vd_info_size_get(struct ps3_instance *instance, + unsigned int *vd_info_size) +{ + unsigned char ret = PS3_TRUE; + union HilReg0Ps3RegisterFPs3MaxVdInfoSize *vd_info_size_u = NULL; + unsigned long long value = 0; + + PS3_IOC_REG_READ_WITH_CHECK(instance, reg_f.Excl_reg, ps3MaxVdInfoSize, + value); + if (value == U64_MAX) { + LOG_ERROR("hno:%u read reg ps3MaxVdInfoSize NOK!\n", + PS3_HOST(instance)); + *vd_info_size = 0; + ret = PS3_FALSE; + goto l_out; + } + vd_info_size_u = (union HilReg0Ps3RegisterFPs3MaxVdInfoSize *)&value; + + *vd_info_size = (unsigned int)vd_info_size_u->reg.ps3MaxVdInfoSize; +l_out: + return ret; +} + +static inline unsigned char +ps3_ioc_mgr_max_nvme_page_size_get(struct ps3_instance *instance, + unsigned int *max_nvme_page_size) +{ + unsigned char ret = PS3_TRUE; + union HilReg0Ps3RegisterFPs3MaxNvmePageSize *max_nvme_page_size_u = + NULL; + unsigned long long value = 0; + + PS3_IOC_REG_READ_SAFE_WITH_RETRY(instance, reg_f.Excl_reg, + ps3MaxNvmePageSize, value); + if (value == U64_MAX) { + LOG_ERROR("hno:%u read reg ps3MaxNvmePageSize NOK!\n", + PS3_HOST(instance)); + *max_nvme_page_size = 0; + ret = PS3_FALSE; + goto l_out; + } + + max_nvme_page_size_u = + (union HilReg0Ps3RegisterFPs3MaxNvmePageSize *)&value; + *max_nvme_page_size = + (unsigned int)max_nvme_page_size_u->reg.ps3MaxNvmePageSize; +l_out: + return ret; +} + +static inline unsigned char +ps3_ioc_mgr_is_dma64_support(struct ps3_instance *instance, + unsigned char *is_dma64_support) +{ + unsigned char ret = PS3_TRUE; + union HilReg0Ps3RegisterFPs3FeatureSupport *ps3_feature_support = NULL; + unsigned long long value = 0; + + PS3_IOC_REG_READ_SAFE_WITH_RETRY(instance, reg_f.Excl_reg, + ps3FeatureSupport, value); + if (value == U64_MAX) { + LOG_ERROR("hno:%u read reg ps3FeatureSupport NOK!\n", + PS3_HOST(instance)); + *is_dma64_support = PS3_FALSE; + ret = PS3_FALSE; + goto l_out; + } + + ps3_feature_support = + (union HilReg0Ps3RegisterFPs3FeatureSupport *)&value; + *is_dma64_support = (ps3_feature_support->reg.dmaBit64Support != 0); +l_out: + return ret; +} + +static inline unsigned char +ps3_max_replyq_count_get(struct ps3_instance *instance, + unsigned int *max_replyq_count) +{ + unsigned char ret = PS3_TRUE; + unsigned long long value = 0; + + PS3_IOC_REG_READ_SAFE_WITH_RETRY(instance, reg_f.Excl_reg, + ps3MaxReplyque, value); + if (value == U64_MAX) { + LOG_ERROR("hno:%u read reg ps3MaxReplyque NOK!\n", + PS3_HOST(instance)); + *max_replyq_count = 0; + ret = PS3_FALSE; + goto l_out; + } + + *max_replyq_count = value & 0xffff; +l_out: + return ret; +} + +static inline unsigned char +ps3_ioc_fw_version_get(struct ps3_instance *instance) +{ + unsigned char ret = PS3_TRUE; + union HilReg0Ps3RegisterFPs3FirmwareVersion *pver = NULL; + unsigned long long fw_version_last = instance->ioc_fw_version; + unsigned long long ver = 0; + + PS3_IOC_REG_READ_SAFE_WITH_RETRY(instance, reg_f.Excl_reg, + ps3FirmwareVersion, ver); + if (ver == U64_MAX) { + LOG_ERROR("hno:%u read reg ps3FirmwareVersion NOK!\n", + PS3_HOST(instance)); + instance->ioc_fw_version = fw_version_last; + ret = PS3_FALSE; + goto l_out; + } + + pver = (union HilReg0Ps3RegisterFPs3FirmwareVersion *)&ver; + instance->ioc_fw_version = (unsigned long long)pver->reg.ps3FmVer; +l_out: + return ret; +} + +static inline unsigned char +ps3_ioc_sgl_mode_support(struct ps3_instance *instance, + unsigned char *is_sgl_mode_support) +{ + unsigned char ret = PS3_TRUE; + union HilReg0Ps3RegisterFPs3FeatureSupport *ps3_feature_support = NULL; + unsigned long long value = 0; + + PS3_IOC_REG_READ_SAFE_WITH_RETRY(instance, reg_f.Excl_reg, + ps3FeatureSupport, value); + if (value == U64_MAX) { + LOG_ERROR("hno:%u read reg ps3FeatureSupport NOK!\n", + PS3_HOST(instance)); + *is_sgl_mode_support = PS3_FALSE; + ret = PS3_FALSE; + goto l_out; + } + + ps3_feature_support = + (union HilReg0Ps3RegisterFPs3FeatureSupport *)&value; + *is_sgl_mode_support = (ps3_feature_support->reg.sglModeSupport == 1); +l_out: + return ret; +} + +static inline unsigned char +ps3_ioc_dump_support_get(struct ps3_instance *instance) +{ + unsigned char ret = PS3_TRUE; + unsigned char last_dump_support = + instance->dump_context.is_dump_support; + union HilReg0Ps3RegisterFPs3FeatureSupport *ps3_feature_support = NULL; + unsigned long long value = 0; + + PS3_IOC_REG_READ_WITH_CHECK(instance, reg_f.Excl_reg, ps3FeatureSupport, + value); + if (value == U64_MAX) { + LOG_ERROR("hno:%u read reg ps3FeatureSupport NOK!\n", + PS3_HOST(instance)); + instance->dump_context.is_dump_support = last_dump_support; + ret = PS3_FALSE; + goto l_out; + } + + ps3_feature_support = + (union HilReg0Ps3RegisterFPs3FeatureSupport *)&value; + instance->dump_context.is_dump_support = + (ps3_feature_support->reg.dumpCrashSupport == 1); +l_out: + return ret; +} + +static inline unsigned char +ps3_ioc_state_halt_support_get(struct ps3_instance *instance) +{ + unsigned char ret = PS3_TRUE; + unsigned char last_halt_support = instance->is_ioc_halt_support; + union HilReg0Ps3RegisterFPs3FeatureSupport *ps3_feature_support = NULL; + unsigned long long value = 0; + + PS3_IOC_REG_READ_WITH_CHECK(instance, reg_f.Excl_reg, ps3FeatureSupport, + value); + if (value == U64_MAX) { + LOG_ERROR("hno:%u read reg ps3FeatureSupport NOK!\n", + PS3_HOST(instance)); + instance->is_ioc_halt_support = last_halt_support; + ret = PS3_FALSE; + goto l_out; + } + + ps3_feature_support = + (union HilReg0Ps3RegisterFPs3FeatureSupport *)&value; + instance->is_ioc_halt_support = + (ps3_feature_support->reg.fwHaltSupport == 1); +l_out: + return ret; +} + +static inline unsigned char +ps3_ioc_recovery_count_get(struct ps3_instance *instance, + unsigned int *recovery_count) +{ + (void)instance; + *recovery_count = 0; + return PS3_TRUE; +} + +static inline unsigned int ps3_ioc_state_get(struct ps3_instance *instance) +{ + unsigned long long ioc_state = 0; + + PS3_IOC_REG_READ_WITH_CHECK(instance, reg_f.Excl_reg, ps3SocFwState, + ioc_state); + return (unsigned int)(ioc_state & PS3_FW_STATE_MASK); +} + +static inline unsigned char +ps3_ioc_state_get_with_check(struct ps3_instance *instance, + unsigned int *ioc_state) +{ + unsigned char ret = PS3_TRUE; + unsigned long long value = 0; + + PS3_IOC_REG_READ_WITH_CHECK(instance, reg_f.Excl_reg, ps3SocFwState, + value); + if (value == U64_MAX) { + LOG_ERROR("hno:%u read reg ps3SocFwState NOK!\n", + PS3_HOST(instance)); + *ioc_state = 0; + ret = PS3_FALSE; + goto l_out; + } + + *ioc_state = (unsigned int)value & PS3_FW_STATE_MASK; +l_out: + return ret; +} + +static inline unsigned char +ps3_get_doorbell_done_with_check(struct ps3_instance *instance, + unsigned char *is_doorbell_done) +{ + unsigned char ret = PS3_TRUE; + unsigned long long value = 0; + + PS3_IOC_REG_READ_WITH_CHECK(instance, reg_f.Excl_reg, ps3RegCmdState, + value); + if (value == U64_MAX) { + LOG_ERROR("hno:%u read reg ps3RegCmdState NOK!\n", + PS3_HOST(instance)); + *is_doorbell_done = PS3_FALSE; + ret = PS3_FALSE; + goto l_out; + } + + *is_doorbell_done = value & PS3_DOORBELL_DONE; + + if (*is_doorbell_done) { + value &= ~PS3_DOORBELL_DONE; + if (instance->peer_instance != NULL && + instance->peer_instance->reg_set != NULL) { + PS3_IOC_REG_WRITE_WITH_CHECK(instance, reg_f.Excl_reg, + ps3RegCmdState, value); + PS3_IOC_REG_WRITE_WITH_CHECK(instance->peer_instance, + reg_f.Excl_reg, + ps3RegCmdState, value); + } else { + PS3_IOC_REG_WRITE_WITH_CHECK(instance, reg_f.Excl_reg, + ps3RegCmdState, value); + } + } +l_out: + return ret; +} + +static inline unsigned char +ps3_get_max_r1x_cmds_with_check(struct ps3_instance *instance, + unsigned short *max_r1x_cmds) +{ + unsigned char ret = PS3_TRUE; + union HilReg0Ps3RegisterFPs3MaxSecR1xCmds *ps3_max_sec_r1x_cmds = NULL; + unsigned long long value = 0; + + PS3_IOC_REG_READ_SAFE_WITH_RETRY(instance, reg_f.Excl_reg, + ps3MaxSecR1xCmds, value); + if (value == U64_MAX) { + LOG_ERROR("hno:%u read reg ps3Debug6 NOK!\n", + PS3_HOST(instance)); + *max_r1x_cmds = 0; + ret = PS3_FALSE; + goto l_out; + } + + ps3_max_sec_r1x_cmds = + (union HilReg0Ps3RegisterFPs3MaxSecR1xCmds *)&value; + *max_r1x_cmds = ps3_max_sec_r1x_cmds->reg.ps3MaxSecR1xCmds; +l_out: + return ret; +} + +static inline unsigned char +ps3_check_debug0_valid_with_check(struct ps3_instance *instance, + unsigned char *is_doorbell_valid, + unsigned int check_mask) +{ + unsigned char ret = PS3_TRUE; + unsigned long long value = 0; + + PS3_IOC_REG_READ_WITH_CHECK(instance, reg_f.Excl_reg, ps3CmdTrigger, + value); + if (value == U64_MAX) { + LOG_ERROR("hno:%u read reg ps3CmdTrigger NOK!\n", + PS3_HOST(instance)); + *is_doorbell_valid = PS3_FALSE; + ret = PS3_FALSE; + goto l_out; + } + + *is_doorbell_valid = (value & check_mask); +l_out: + return ret; +} + +static inline unsigned char +ps3_ioc_heartbeat_get(struct ps3_instance *instance, + unsigned long long *heartbeat_value) +{ + unsigned char ret = PS3_TRUE; + static unsigned char last_fault_state = PS3_TRUE; + + *heartbeat_value = ps3_ioc_reg_read_with_check( + instance, &instance->reg_set->reg_f.Excl_reg.ps3SocFwState); + if (*heartbeat_value == U64_MAX) { + if (last_fault_state) { + LOG_ERROR("hno:%u read reg ps3SocFwState NOK!\n", + PS3_HOST(instance)); + last_fault_state = PS3_FALSE; + } + *heartbeat_value = 0; + ret = PS3_FALSE; + goto l_out; + } else { + last_fault_state = PS3_TRUE; + LOG_DEBUG( + "hno:%u read reg ps3SocFwState success value[%llu]!\n", + PS3_HOST(instance), *heartbeat_value); + } + +l_out: + return ret; +} + +static inline unsigned char +ps3_ioc_recovery_support_get(struct ps3_instance *instance) +{ + unsigned char ret = PS3_TRUE; + unsigned char last_shallow_soft_recovery_support = + instance->is_shallow_soft_recovery_support; + unsigned char last_deep_soft_recovery_support = + instance->is_deep_soft_recovery_support; + unsigned char last_hard_recovery_support = + instance->is_hard_recovery_support; + union HilReg0Ps3RegisterFPs3FeatureSupport *ps3_feature_support = NULL; + unsigned long long value = 0; + + PS3_IOC_REG_READ_WITH_CHECK(instance, reg_f.Excl_reg, ps3FeatureSupport, + value); + if (value == U64_MAX) { + LOG_ERROR("hno:%u read reg ps3FeatureSupport NOK!\n", + PS3_HOST(instance)); + instance->is_shallow_soft_recovery_support = + last_shallow_soft_recovery_support; + instance->is_deep_soft_recovery_support = + last_deep_soft_recovery_support; + instance->is_hard_recovery_support = last_hard_recovery_support; + ret = PS3_FALSE; + goto l_out; + } + + ps3_feature_support = + (union HilReg0Ps3RegisterFPs3FeatureSupport *)&value; + instance->is_shallow_soft_recovery_support = + (ps3_feature_support->reg.shallowSoftRecoverySupport == 1); + instance->is_deep_soft_recovery_support = + (ps3_feature_support->reg.deepSoftRecoverySupport == 1); + instance->is_hard_recovery_support = + (ps3_feature_support->reg.hardRecoverySupport == 1); +l_out: + return ret; +} + +static inline unsigned char +ps3_ioc_multi_func_support(struct ps3_instance *instance) +{ + unsigned char ret = PS3_TRUE; + union HilReg0Ps3RegisterFPs3FeatureSupport *ps3_feature_support = NULL; + unsigned long long value = 0; + + PS3_IOC_REG_READ_SAFE_WITH_RETRY(instance, reg_f.Excl_reg, + ps3FeatureSupport, value); + if (value == U64_MAX) { + LOG_ERROR("hno:%u read reg ps3FeatureSupport failed!\n", + PS3_HOST(instance)); + ret = PS3_FALSE; + goto l_out; + } + + ps3_feature_support = + (union HilReg0Ps3RegisterFPs3FeatureSupport *)&value; + ret = (ps3_feature_support->reg.multiDevfnSupport == 1); +l_out: + return ret; +} + +static inline unsigned char +ps3_ioc_security_state_check(struct ps3_instance *instance) +{ + unsigned long long value = 0; + unsigned char ret = PS3_FALSE; + + PS3_IOC_REG_READ_SAFE_WITH_RETRY(instance, reg_f.Excl_reg, ps3Debug7, + value); + if (value == U64_MAX || (value & 0x1)) { + ret = PS3_TRUE; + LOG_ERROR( + "hno:%u read register NOK or ioc is security [%llu]\n", + PS3_HOST(instance), value); + } + +#ifdef PS3_HARDWARE_SIM + ret = PS3_FALSE; +#endif + + return ret; +} + +static inline unsigned char +ps3_ioc_atu_support_get(struct ps3_instance *instance, unsigned char *bit_pos) +{ + unsigned char ret = PS3_TRUE; + union HilRegPs3RegisterFPs3AtuSupport *ps3_atu_support = NULL; + unsigned long long value = PS3_BIT_POS_DEFAULT; + + PS3_IOC_REG_READ_WITH_CHECK(instance, reg_f.Excl_reg, ps3Debug8, value); + if (value == U64_MAX) { + LOG_ERROR("hno:%u read reg ps3AtuSupport NOK!\n", + PS3_HOST(instance)); + *bit_pos = PS3_BIT_POS_DEFAULT; + ret = PS3_FALSE; + goto l_out; + } + ps3_atu_support = (union HilRegPs3RegisterFPs3AtuSupport *)&value; + *bit_pos = ps3_atu_support->reg.bitPos; +l_out: + return ret; +} + +static inline unsigned char +ps3_ioc_atu_support_safe_get(struct ps3_instance *instance, + unsigned char *bit_pos) +{ + unsigned char ret = PS3_FALSE; + unsigned int fw_cur_state = PS3_FW_STATE_UNDEFINED; + unsigned int count = 0; + unsigned int retry_cnt = 0; + unsigned char tmp_bit_pos = 0; + unsigned char is_first = PS3_TRUE; + + for (; retry_cnt < PS3_ATU_SUPPORT_READ_RETRY_NUM; retry_cnt++) { + if (!ps3_ioc_atu_support_get(instance, &tmp_bit_pos)) + goto l_out; + fw_cur_state = instance->ioc_adpter->ioc_state_get(instance); + if (!ps3_ioc_state_valid_check(fw_cur_state)) { + for (; count < PS3_REG_READ_RETRY_NUM; count++) { + fw_cur_state = + instance->ioc_adpter->ioc_state_get( + instance); + if (ps3_ioc_state_valid_check(fw_cur_state)) { + if (!ps3_ioc_atu_support_get( + instance, &tmp_bit_pos)) { + goto l_out; + } + *bit_pos = tmp_bit_pos; + ret = PS3_TRUE; + goto l_out; + } + ps3_msleep(PS3_LOOP_TIME_INTERVAL_20MS); + } + goto l_out; + } + if (is_first) { + *bit_pos = tmp_bit_pos; + is_first = PS3_FALSE; + continue; + } + if (*bit_pos != tmp_bit_pos) + goto l_out; + ps3_msleep(PS3_LOOP_TIME_INTERVAL_50MS); + } + ret = PS3_TRUE; +l_out: + return ret; +} + +static inline unsigned char +ps3_ioc_atu_support_retry_read(struct ps3_instance *instance, + unsigned char *bit_pos) +{ + unsigned int retry_cnt = PS3_ATU_SUPPORT_READ_RETRY_NUM; + unsigned char ret = PS3_FALSE; + + while (retry_cnt--) { + if (ps3_ioc_atu_support_safe_get(instance, bit_pos)) { + ret = PS3_TRUE; + break; + } + ps3_msleep(PS3_LOOP_TIME_INTERVAL_20MS); + } + return ret; +} + +static inline void ps3_ioc_can_hardreset_set(struct ps3_instance *instance, + unsigned char enable) +{ + unsigned long long can_hardreset = 0; + union HilRegPs3RegisterFPs3CanHardReset *ps3_can_hardreset = NULL; + + ps3_can_hardreset = + (union HilRegPs3RegisterFPs3CanHardReset *)&can_hardreset; + ps3_can_hardreset->reg.canHardReset = enable; + PS3_IOC_REG_WRITE(instance, reg_f.Excl_reg, ps3Debug9, + ps3_can_hardreset->val); +} + +unsigned char ps3_feature_support_reg_get(struct ps3_instance *instance); + +void ps3_ioc_scsi_cmd_send(struct ps3_instance *instance, + struct PS3CmdWord *cmd_word); + +struct pglist_data *ps3_first_online_pgdat(void); + +struct pglist_data *ps3_next_online_pgdat(struct pglist_data *pgdat); + +#define for_each_ps3_online_pgdat(pgdat) \ + for (pgdat = ps3_first_online_pgdat(); pgdat; \ + pgdat = ps3_next_online_pgdat(pgdat)) + +#endif diff --git a/drivers/scsi/linkdata/ps3stor/ps3_ioc_state.c b/drivers/scsi/linkdata/ps3stor/ps3_ioc_state.c new file mode 100644 index 0000000000000000000000000000000000000000..87bda496624f09bac17cee0c25996608ed1a7f71 --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/ps3_ioc_state.c @@ -0,0 +1,855 @@ +// SPDX-License-Identifier: GPL-2.0 +#ifndef _WINDOWS +#include +#include +#include +#endif + +#include "ps3_ioc_state.h" +#include "ps3_driver_log.h" +#include "ps3_ioc_manager.h" +#include "ps3_module_para.h" + +#define PS3_SOFTRESET_MASK (0xFF) +#define PS3_HARDRESET_MASK (0xFF) + +enum ps3_reset_type { + PS3_FW_HARD_RESET = 0, + PS3_FW_SHALLOW_SOFT_RESET = 1, + PS3_FW_DEEP_SOFT_RESET = 2, +}; + +static inline const char *namePS3ResetType(int s) +{ + static const char * const myNames[] = { + [PS3_FW_HARD_RESET] = "PS3_FW_HARD_RESET", + [PS3_FW_SHALLOW_SOFT_RESET] = "PS3_FW_SHALLOW_SOFT_RESET", + [PS3_FW_DEEP_SOFT_RESET] = "PS3_FW_DEEP_SOFT_RESET" + }; + + if (s > PS3_FW_DEEP_SOFT_RESET) + return "PS3_RESET_TYPE_INVALID"; + + return myNames[s]; +} + +struct ps3_reset_key_map { + unsigned int reset_key_offset; + unsigned int reset_state_offset; + unsigned int reset_offset; + unsigned int reset_type; + unsigned int reset_status_offset; + unsigned int reset_status_mask; +}; + +struct ps3_state_desc_map { + unsigned int state; + const char *state_desc; +}; + +static struct ps3_reset_key_map g_reset_key_table[] = { + [PS3_FW_HARD_RESET] = { offsetof(struct HilReg0Ps3RegisterF, + ps3HardresetKey), + offsetof(struct HilReg0Ps3RegisterF, + ps3HardresetState), + offsetof(struct HilReg0Ps3RegisterF, + ps3Hardreset), + PS3_FW_HARD_RESET_ACT, + offsetof(struct HilReg0Ps3RegisterF, + ps3Hardreset), + PS3_HARDRESET_MASK }, + [PS3_FW_SHALLOW_SOFT_RESET] = { offsetof(struct HilReg0Ps3RegisterF, + ps3SoftresetKey), + offsetof(struct HilReg0Ps3RegisterF, + ps3SoftresetState), + offsetof(struct HilReg0Ps3RegisterF, + ps3Softreset), + PS3_FW_STATE_ACT_SHALLOW_SOFT_RESET, + offsetof(struct HilReg0Ps3RegisterF, + ps3Softreset), + PS3_SOFTRESET_MASK }, + [PS3_FW_DEEP_SOFT_RESET] = { offsetof(struct HilReg0Ps3RegisterF, + ps3SoftresetKey), + offsetof(struct HilReg0Ps3RegisterF, + ps3SoftresetState), + offsetof(struct HilReg0Ps3RegisterF, + ps3Softreset), + PS3_FW_STATE_ACT_DEEP_SOFT_RESET, + offsetof(struct HilReg0Ps3RegisterF, + ps3Softreset), + PS3_SOFTRESET_MASK }, +}; + +static struct ps3_state_desc_map g_state_desc[] = { + { PS3_FW_STATE_UNDEFINED, "PS3_FW_STATE_UNDEFINED" }, + { PS3_FW_STATE_START, "PS3_FW_STATE_START" }, + { PS3_FW_STATE_READY, "PS3_FW_STATE_READY" }, + { PS3_FW_STATE_WAIT, "PS3_FW_STATE_WAIT" }, + { PS3_FW_STATE_RUNNING, "PS3_FW_STATE_RUNNING" }, + { PS3_FW_STATE_FLUSHING, "PS3_FW_STATE_FLUSHING" }, + { PS3_FW_STATE_RESET, "PS3_FW_STATE_RESET" }, + { PS3_FW_STATE_FAULT, "PS3_FW_STATE_FAULT" }, + { PS3_FW_STATE_CRITICAL, "PS3_FW_STATE_CRITICAL" }, + { PS3_FW_STATE_HALT, "PS3_FW_STATE_HALT" } +}; + +static void ps3_reset_key_write(struct ps3_instance *instance, + unsigned int offset) +{ + static unsigned int reset_key_array[] = { + PS3_FW_DIAG_1ST_KEY, PS3_FW_DIAG_2ND_KEY, PS3_FW_DIAG_3RD_KEY, + PS3_FW_DIAG_4TH_KEY, PS3_FW_DIAG_5TH_KEY, PS3_FW_DIAG_6TH_KEY, + PS3_FW_DIAG_7TH_KEY, PS3_FW_DIAG_8TH_KEY, PS3_FW_DIAG_9TH_KEY + }; + unsigned int idx = 0; + + for (idx = 0; idx < ARRAY_SIZE(reset_key_array); idx++) { + PS3_IOC_REG_WRITE_OFFSET( + instance, offset, + (unsigned long long)reset_key_array[idx]); + } +} +static void ps3_hardreset_key_write(struct ps3_instance *instance, + unsigned char *reset_key_vir_addr, + unsigned long long *timeval) +{ + static unsigned int reset_key_array[] = { + PS3_FW_DIAG_1ST_KEY, PS3_FW_DIAG_2ND_KEY, PS3_FW_DIAG_3RD_KEY, + PS3_FW_DIAG_4TH_KEY, PS3_FW_DIAG_5TH_KEY, PS3_FW_DIAG_6TH_KEY, + PS3_FW_DIAG_7TH_KEY, PS3_FW_DIAG_8TH_KEY, PS3_FW_DIAG_9TH_KEY + }; + unsigned int idx = 0; + + timeval[PS3_START_WRITE_KEY_REG] = ps3_1970_now_ms_get(); + for (idx = 0; idx < ARRAY_SIZE(reset_key_array); idx++) { + ps3_ioc_hardreset_reg_write( + instance, (unsigned long long)reset_key_array[idx], + reset_key_vir_addr, 0); + } + timeval[PS3_END_WRITE_KEY_REG] = ps3_1970_now_ms_get(); +} +static int ps3_reset_key_state_check(struct ps3_instance *instance, + unsigned int offset) +{ + unsigned int reset_key_state = 0; + unsigned int read_count = 0; + const unsigned int retry_max = 50; + int ret = PS3_SUCCESS; + unsigned long long value = 0; + + do { + if (read_count >= retry_max) { + LOG_ERROR("hno:%u PS3 reset key state is still disabled after 5 sec\n", + PS3_HOST(instance)); + ret = -PS3_FAILED; + break; + } + + if (read_count != 0) + ps3_msleep(PS3_LOOP_TIME_INTERVAL_100MS); + + PS3_IOC_REG_READ_OFFSET(instance, offset, value); + reset_key_state = (unsigned int)value; + + read_count++; + } while (!(reset_key_state & PS3_FW_DIAG_ENABLE) || + (reset_key_state == U32_MAX)); + + if (ret == PS3_SUCCESS) { + LOG_INFO( + "hno:%u PS3 reset key state is enabled after %d msecs\n", + PS3_HOST(instance), + (read_count - 1) * PS3_LOOP_TIME_INTERVAL_100MS); + } + + return ret; +} +static int +ps3_hardreset_key_state_check(struct ps3_instance *instance, + unsigned char *reset_key_state_vir_addr, + unsigned long long *timeval) +{ + unsigned int reset_key_state = 0; + unsigned int read_count = 0; + const unsigned int retry_max = 900; + int ret = PS3_SUCCESS; + + timeval[PS3_START_WAIT_KEY_READY_REG] = ps3_1970_now_ms_get(); + do { + if (read_count >= retry_max) { + ret = -PS3_FAILED; + break; + } + + if (read_count != 0) + ps3_mdelay(PS3_LOOP_TIME_INTERVAL_1MS); + + reset_key_state = (unsigned int)ps3_ioc_hardreset_reg_read( + instance, reset_key_state_vir_addr); + + read_count++; + } while (!(reset_key_state & PS3_FW_DIAG_ENABLE) || + ((U32_MAX & reset_key_state) == U32_MAX)); + timeval[PS3_END_WAIT_KEY_READY_REG] = ps3_1970_now_ms_get(); + + return ret; +} + +static int ps3_after_reset_request_check(struct ps3_instance *instance, + enum ps3_reset_type reset_type) +{ + unsigned int fw_state = instance->ioc_adpter->ioc_state_get(instance); + unsigned int read_count = 0; +#ifdef PS3_HARDWARE_HAPS_V200 + const unsigned int retry_max = 3600; +#else + const unsigned int retry_max = 1800; +#endif + int ret = -PS3_FAILED; + + while (read_count < retry_max) { + if ((fw_state == PS3_FW_STATE_START) || + (fw_state == PS3_FW_STATE_READY)) { + ret = PS3_SUCCESS; + break; + } + if ((reset_type == PS3_FW_SHALLOW_SOFT_RESET) || + (reset_type == PS3_FW_DEEP_SOFT_RESET)) { + if (fw_state == PS3_FW_STATE_RUNNING) { + ret = PS3_SUCCESS; + break; + } + } + + if (ps3_pci_err_recovery_get(instance)) { + LOG_WARN("hno:%u pci recovery resetting\n", + PS3_HOST(instance)); + ret = -PS3_IN_PCIE_ERR; + break; + } +#ifdef PS3_HARDWARE_HAPS_V200 + ps3_msleep(PS3_LOOP_TIME_INTERVAL_3000MS); +#else + ps3_msleep(PS3_LOOP_TIME_INTERVAL_100MS); +#endif + fw_state = instance->ioc_adpter->ioc_state_get(instance); + read_count++; + } + + if (ret != PS3_SUCCESS) { + LOG_ERROR( + "hno:%u PS3 IOC state is not valid 180 secs after IOC reset, fw state is %s\n", + PS3_HOST(instance), ps3_ioc_state_print(fw_state)); + } + + LOG_INFO("hno:%u fw state is %s\n", PS3_HOST(instance), + ps3_ioc_state_print(fw_state)); + + return ret; +} + +static int ps3_reset_request_completion_check(struct ps3_instance *instance, + unsigned int offset, + unsigned int completion_mask) +{ + unsigned int read_count = 0; + const unsigned int retry_max = 1000; + int ret = PS3_SUCCESS; + unsigned long long value = 0; + unsigned int completion = 0; + + PS3_IOC_REG_READ_OFFSET(instance, offset, value); + completion = (unsigned int)value; + while (completion & completion_mask || completion == U32_MAX) { + if (read_count > retry_max) { + LOG_ERROR( + "hno:%u PS3 reset flag is not clear 100 secs after reset request\n", + PS3_HOST(instance)); + ret = -PS3_FAILED; + break; + } + + if (ps3_pci_err_recovery_get(instance)) { + LOG_WARN("hno:%u pci recovery resetting\n", + PS3_HOST(instance)); + ret = -PS3_FAILED; + break; + } + + ps3_msleep(PS3_LOOP_TIME_INTERVAL_100MS); + PS3_IOC_REG_READ_OFFSET(instance, offset, value); + completion = (unsigned int)value; + read_count++; + } + + if (ret == PS3_SUCCESS) { + LOG_INFO( + "hno:%u PS3 reset complete %d msecs after reset complete\n", + PS3_HOST(instance), + read_count * PS3_LOOP_TIME_INTERVAL_100MS); + } + + return ret; +} + +const char *ps3_ioc_state_print(unsigned int state) +{ + unsigned int idx = 0; + unsigned int fw_state = state & PS3_FW_STATE_MASK; + const char *ps3_state_name = "invalid state"; + + for (idx = 0; idx < ARRAY_SIZE(g_state_desc); idx++) { + if (g_state_desc[idx].state == fw_state) { + ps3_state_name = g_state_desc[idx].state_desc; + break; + } + } + + return ps3_state_name; +} + +static inline void +ps3_ioc_state_trigger_transition(struct ps3_instance *instance, + unsigned int action) +{ + PS3_IOC_REG_WRITE(instance, reg_f.Excl_reg, ps3Doorbell, + (unsigned long long)action); +} + +static inline void ps3_ioc_debug0_trigger(struct ps3_instance *instance, + unsigned int mask_value) +{ + PS3_IOC_REG_WRITE(instance, reg_f.Excl_reg, ps3CmdTrigger, + (unsigned long long)mask_value); +} + +int ps3_ioc_state_fault_wait(struct ps3_instance *instance) +{ + unsigned int fw_cur_state = PS3_FW_STATE_UNDEFINED; + int ret = PS3_SUCCESS; + unsigned int count = 0; + + fw_cur_state = instance->ioc_adpter->ioc_state_get(instance); + while (count < PS3_FW_STATE_TO_FAULT_TMO_LOOP_COUNT) { + if (fw_cur_state == PS3_FW_STATE_FAULT || + fw_cur_state == PS3_FW_STATE_CRITICAL) { + LOG_INFO("hno:%u within 180s fw transfer to %s\n", + PS3_HOST(instance), + ps3_ioc_state_print(fw_cur_state)); + break; + } + + ps3_msleep(PS3_LOOP_TIME_INTERVAL_20MS); + fw_cur_state = instance->ioc_adpter->ioc_state_get(instance); + count++; + } + + if (fw_cur_state != PS3_FW_STATE_FAULT) { + LOG_ERROR("hno:%u fw state[%s] is not fault\n", + PS3_HOST(instance), + ps3_ioc_state_print(fw_cur_state)); + ret = -PS3_FAILED; + } else { + LOG_INFO("hno:%u fw state transition to %s\n", + PS3_HOST(instance), ps3_ioc_state_print(fw_cur_state)); + } + + return ret; +} + +int ps3_ioc_state_ready_wait(struct ps3_instance *instance) +{ + unsigned int fw_cur_state = PS3_FW_STATE_UNDEFINED; + int ret = PS3_SUCCESS; + unsigned int count = 0; + unsigned char is_unload_valid = PS3_FALSE; + + ps3_check_debug0_valid_with_check(instance, &is_unload_valid, + PS3_CMD_TRIGGER_UNLOAD); + fw_cur_state = instance->ioc_adpter->ioc_state_get(instance); + while (count < instance->wait_ready_timeout) { + if (ps3_pci_err_recovery_get(instance)) { + LOG_WARN("hno:%u pci recovery resetting\n", + PS3_HOST(instance)); + ret = -PS3_IN_PCIE_ERR; + goto l_out; + } + + if (fw_cur_state == PS3_FW_STATE_READY && !is_unload_valid) { + LOG_INFO("hno:%u within 180s fw transfer to %s\n", + PS3_HOST(instance), + ps3_ioc_state_print(fw_cur_state)); + break; + } +#ifdef PS3_HARDWARE_HAPS_V200 + ps3_msleep(PS3_LOOP_TIME_INTERVAL_3000MS); +#else + ps3_msleep(PS3_LOOP_TIME_INTERVAL_20MS); +#endif + ps3_check_debug0_valid_with_check(instance, &is_unload_valid, + PS3_CMD_TRIGGER_UNLOAD); + fw_cur_state = instance->ioc_adpter->ioc_state_get(instance); + count++; + } + + if (fw_cur_state != PS3_FW_STATE_READY || is_unload_valid) { + LOG_ERROR( + "hno:%u fw state[%s] is not ready, or unload[%d] not done\n", + PS3_HOST(instance), ps3_ioc_state_print(fw_cur_state), + is_unload_valid); + ret = -PS3_FAILED; + } else { + LOG_INFO("hno:%u fw state transition to %s\n", + PS3_HOST(instance), ps3_ioc_state_print(fw_cur_state)); + } + +l_out: + return ret; +} + +int ps3_ioc_state_transfer_to_ready(struct ps3_instance *instance) +{ + unsigned int fw_cur_state = PS3_FW_STATE_UNDEFINED; + int ret = PS3_SUCCESS; + + if (!ps3_ioc_state_get_with_check(instance, &fw_cur_state)) { + ret = -PS3_FAILED; + goto l_out; + } + + LOG_INFO("hno:%u fw state is %s(0x%x)\n", PS3_HOST(instance), + ps3_ioc_state_print(fw_cur_state), fw_cur_state); + + switch (fw_cur_state) { + case PS3_FW_STATE_UNDEFINED: + case PS3_FW_STATE_RESET: + case PS3_FW_STATE_START: + case PS3_FW_STATE_FLUSHING: + ret = ps3_ioc_state_ready_wait(instance); + if (ret != PS3_SUCCESS) { + if (ret != -PS3_IN_PCIE_ERR) + ret = -PS3_NO_RECOVERED; + LOG_ERROR("hno:%u fw state to ready NOK\n", + PS3_HOST(instance)); + } + break; + case PS3_FW_STATE_READY: + break; + case PS3_FW_STATE_WAIT: + case PS3_FW_STATE_RUNNING: + LOG_WARN("hno:%u fw state is wait/running\n", + PS3_HOST(instance)); + ret = -PS3_FAILED; + break; + case PS3_FW_STATE_FAULT: + case PS3_FW_STATE_CRITICAL: + case PS3_FW_STATE_HALT: + LOG_ERROR("hno:%u fw state is fault/halt, to ready NOK\n", + PS3_HOST(instance)); + ret = -PS3_FAILED; + break; + + default: + ret = ps3_ioc_state_ready_wait(instance); + break; + } +l_out: + return ret; +} + +int ps3_ioc_state_transfer_wait_to_running(struct ps3_instance *instance) +{ + unsigned int fw_cur_state = PS3_FW_STATE_UNDEFINED; + int ret = PS3_SUCCESS; + unsigned int count = 0; + + fw_cur_state = instance->ioc_adpter->ioc_state_get(instance); + LOG_INFO("hno:%u fw state is %s(0x%x)\n", PS3_HOST(instance), + ps3_ioc_state_print(fw_cur_state), fw_cur_state); + + while (count < PS3_FW_STATE_TO_RUNNING_TMO_LOOP_COUNT) { + if ((fw_cur_state == PS3_FW_STATE_RUNNING) || + (fw_cur_state == PS3_FW_STATE_FAULT) || + (fw_cur_state == PS3_FW_STATE_HALT) || + (fw_cur_state == PS3_FW_STATE_CRITICAL)) { + break; + } + if (ps3_pci_err_recovery_get(instance)) { + LOG_WARN("hno:%u pci recovery resetting\n", + PS3_HOST(instance)); + ret = -PS3_IN_PCIE_ERR; + goto l_out; + } + + ps3_mutex_lock(&instance->state_machine.lock); + if (PS3_IS_INTERRUPT_SOFT_RECOVERY(instance)) { + LOG_WARN("hno:%u soft reset proc is interrupt!\n", + PS3_HOST(instance)); + ret = -PS3_FAILED; + ps3_mutex_unlock(&instance->state_machine.lock); + break; + } + ps3_mutex_unlock(&instance->state_machine.lock); + + ps3_msleep(PS3_LOOP_TIME_INTERVAL_20MS); + fw_cur_state = instance->ioc_adpter->ioc_state_get(instance); + count++; + } + if (fw_cur_state != PS3_FW_STATE_RUNNING) { + LOG_ERROR("hno:%u fw state transition NOK, state is %s\n", + PS3_HOST(instance), + ps3_ioc_state_print(fw_cur_state)); + if (ps3_pci_err_recovery_get(instance)) { + LOG_WARN("hno:%u pci recovery resetting\n", + PS3_HOST(instance)); + ret = -PS3_IN_PCIE_ERR; + } else { + ret = -PS3_FAILED; + } + } + + LOG_INFO("hno:%u fw state transit to %s\n", PS3_HOST(instance), + ps3_ioc_state_print(fw_cur_state)); +l_out: + return ret; +} + +static int ps3_ioc_state_reset_request(struct ps3_instance *instance, + enum ps3_reset_type reset_type) +{ + int ret = -PS3_FAILED; + unsigned int ioc_reset_type = 0; + unsigned int reset_status_mask = 0; + unsigned int cur_state = 0; + + ioc_reset_type = g_reset_key_table[reset_type].reset_type; + reset_status_mask = g_reset_key_table[reset_type].reset_status_mask; + + cur_state = ps3_atomic_read(&instance->state_machine.state); + + LOG_INFO( + "hno:%u %s, key_offset: 0x%x, state_offset: 0x%x\n" + "\treset_offset: 0x%x, status_offset: 0x%x, IOC reset_type: 0x%8x\n" + "\tstatus mask: 0x%8x cur_state[%d]\n", + PS3_HOST(instance), namePS3ResetType(reset_type), + g_reset_key_table[reset_type].reset_key_offset, + g_reset_key_table[reset_type].reset_state_offset, + g_reset_key_table[reset_type].reset_offset, + g_reset_key_table[reset_type].reset_status_offset, + g_reset_key_table[reset_type].reset_type, + g_reset_key_table[reset_type].reset_status_mask, cur_state); + preempt_disable(); + ps3_reset_key_write(instance, + g_reset_key_table[reset_type].reset_key_offset); + preempt_enable(); + + ps3_reset_key_state_check( + instance, g_reset_key_table[reset_type].reset_state_offset); + + PS3_IOC_REG_WRITE_OFFSET(instance, + g_reset_key_table[reset_type].reset_offset, + (unsigned long long)ioc_reset_type); + + if (ps3_hard_reset_waiting_query()) + ps3_msleep(ps3_hard_reset_waiting_query()); + + if (ps3_reset_request_completion_check( + instance, g_reset_key_table[reset_type].reset_status_offset, + reset_status_mask) != PS3_SUCCESS) { + goto l_out; + } + + if (ps3_after_reset_request_check(instance, reset_type) != + PS3_SUCCESS) { + goto l_out; + } + + ret = PS3_SUCCESS; + +l_out: + LOG_INFO("hno:%u PS3 %s complete, ret:%d\n", PS3_HOST(instance), + namePS3ResetType(reset_type), ret); + return ret; +} + +static int ps3_ioc_state_hardreset_request(struct ps3_instance *instance, + enum ps3_reset_type reset_type) +{ + int ret = -PS3_FAILED; + unsigned char *reset_key_addr = NULL; + unsigned char *reset_state_addr = NULL; + unsigned char *reset_addr = NULL; + unsigned int ioc_reset_type = 0; + unsigned int cur_state = 0; + unsigned char *reg_start = (unsigned char *)instance->reg_set; + unsigned int read_count = 0; + const unsigned int retry_max = 180; + unsigned long flags = 0; + unsigned long long timeval[PS3_RESET_MAX_COUNT] = { 0 }; + + reset_key_addr = + reg_start + g_reset_key_table[reset_type].reset_key_offset; + reset_state_addr = + reg_start + g_reset_key_table[reset_type].reset_state_offset; + reset_addr = reg_start + g_reset_key_table[reset_type].reset_offset; + ioc_reset_type = g_reset_key_table[reset_type].reset_type; + + cur_state = ps3_atomic_read(&instance->state_machine.state); + + LOG_INFO( + "hno:%u %s, key_offset: 0x%x, state_offset: 0x%x\n" + "\treset_offset: 0x%x, status_offset: 0x%x, IOC reset_type: 0x%8x\n" + "\tstatus mask: 0x%8x cur_state[%d]\n", + PS3_HOST(instance), namePS3ResetType(reset_type), + g_reset_key_table[reset_type].reset_key_offset, + g_reset_key_table[reset_type].reset_state_offset, + g_reset_key_table[reset_type].reset_offset, + g_reset_key_table[reset_type].reset_status_offset, + g_reset_key_table[reset_type].reset_type, + g_reset_key_table[reset_type].reset_status_mask, cur_state); + instance->is_hard_reset = PS3_TRUE; + mb(); /* in order to force CPU ordering */ + while (ps3_atomic_read(&instance->reg_op_count) != 0) { + ps3_msleep(PS3_LOOP_TIME_INTERVAL_100MS); + + if (read_count++ > retry_max) { + LOG_INFO("hno:%u %s, wait reg op over:%d ms,failed\n", + PS3_HOST(instance), + namePS3ResetType(reset_type), + read_count * PS3_LOOP_TIME_INTERVAL_100MS); + ret = -PS3_FAILED; + goto l_out; + } + } + ps3_wait_scsi_cmd_done(instance, PS3_TRUE); + ps3_wait_mgr_cmd_done(instance, PS3_TRUE); + if (instance->peer_instance != NULL) { + ps3_wait_scsi_cmd_done(instance->peer_instance, PS3_TRUE); + ps3_wait_mgr_cmd_done(instance->peer_instance, PS3_TRUE); + } + + if (ps3_pci_err_recovery_get(instance)) { + LOG_INFO("hno:%u pcie recovery proceess\n", PS3_HOST(instance)); + ret = -PS3_IN_PCIE_ERR; + goto l_out; + } + spin_lock_irqsave(&instance->recovery_context->ps3_hardreset_lock, + flags); + ps3_hardreset_key_write(instance, reset_key_addr, timeval); + + ret = ps3_hardreset_key_state_check(instance, reset_state_addr, + timeval); + if (ret != PS3_SUCCESS) { + spin_unlock_irqrestore( + &instance->recovery_context->ps3_hardreset_lock, flags); + LOG_INFO("hno:%u %s, key check failed, ret:%d\n", + PS3_HOST(instance), namePS3ResetType(reset_type), ret); + goto l_out; + } + + if (ps3_pci_err_recovery_get(instance)) { + spin_unlock_irqrestore( + &instance->recovery_context->ps3_hardreset_lock, flags); + LOG_WARN("hno:%u pcie recovery proceess\n", PS3_HOST(instance)); + ret = -PS3_IN_PCIE_ERR; + goto l_out; + } + + timeval[PS3_START_WRITE_HARDRESET_REG] = ps3_1970_now_ms_get(); + ps3_ioc_hardreset_reg_write(instance, + (unsigned long long)ioc_reset_type, + reset_addr, PS3_TRUE); + timeval[PS3_END_WRITE_HARDRESET_REG] = ps3_1970_now_ms_get(); + spin_unlock_irqrestore(&instance->recovery_context->ps3_hardreset_lock, + flags); + LOG_INFO("hno:%u %s, key_offset: key state success\n" + "\tthen write hardreset ioc_reset_type:%u, reset_addr:%p\n", + PS3_HOST(instance), namePS3ResetType(reset_type), + ioc_reset_type, reset_addr); + LOG_INFO("hno:%u time:%lld-%lld-%lld-%lld-%lld-%lld\n", + PS3_HOST(instance), timeval[PS3_START_WRITE_KEY_REG], + timeval[PS3_END_WRITE_KEY_REG], + timeval[PS3_START_WAIT_KEY_READY_REG], + timeval[PS3_END_WAIT_KEY_READY_REG], + timeval[PS3_START_WRITE_HARDRESET_REG], + timeval[PS3_END_WRITE_HARDRESET_REG]); + + instance->recovery_context->hardreset_count++; + if (ps3_hard_reset_waiting_query()) + ps3_msleep(ps3_hard_reset_waiting_query()); + LOG_INFO("hno:%u %s, after sleep:%d ms\n", PS3_HOST(instance), + namePS3ResetType(reset_type), ps3_hard_reset_waiting_query()); + + instance->is_hard_reset = PS3_FALSE; + ret = ps3_after_reset_request_check(instance, reset_type); + if (ret != PS3_SUCCESS) + goto l_out; + + ret = PS3_SUCCESS; + +l_out: + instance->is_hard_reset = PS3_FALSE; + LOG_INFO("hno:%u PS3 %s complete, ret:%d\n", PS3_HOST(instance), + namePS3ResetType(reset_type), ret); + return ret; +} + +int ps3_ioc_state_hard_reset(struct ps3_instance *instance) +{ + if (ps3_use_hard_reset_reg_query()) { + return ps3_ioc_state_hardreset_request(instance, + PS3_FW_HARD_RESET); + } else { + return ps3_ioc_state_reset_request(instance, + PS3_FW_SHALLOW_SOFT_RESET); + } +} + +int ps3_ioc_state_shallow_soft_reset(struct ps3_instance *instance) +{ + return ps3_ioc_state_reset_request(instance, PS3_FW_SHALLOW_SOFT_RESET); +} + +int ps3_ioc_state_deep_soft_reset(struct ps3_instance *instance) +{ + return ps3_ioc_state_reset_request(instance, PS3_FW_DEEP_SOFT_RESET); +} + +static int ps3_trigger_ioc_state_change_by_doorbell( + struct ps3_instance *instance, unsigned int expect_fw_state, + unsigned int doorbell_trigger, u32 time_out) +{ + unsigned int fw_cur_state = PS3_FW_STATE_UNDEFINED; + unsigned char is_doorbell_done = PS3_TRUE; + int ret = PS3_SUCCESS; + unsigned int count = 0; + + LOG2_WARN("hno:%u expect fw state:%s, doorbell_trigger is %d\n", + PS3_HOST(instance), ps3_ioc_state_print(expect_fw_state), + doorbell_trigger); + + fw_cur_state = instance->ioc_adpter->ioc_state_get(instance); + if (fw_cur_state == expect_fw_state) + goto l_out; + + if (ps3_pci_err_recovery_get(instance)) { + LOG_WARN("hno:%u pci recovery resetting\n", PS3_HOST(instance)); + ret = -PS3_IN_PCIE_ERR; + goto l_out; + } + ps3_ioc_state_trigger_transition(instance, doorbell_trigger); + + fw_cur_state = instance->ioc_adpter->ioc_state_get(instance); + if (doorbell_trigger == PS3_REG_DOORBELL_STATE_TO_FAULT) + ps3_get_doorbell_done_with_check(instance, &is_doorbell_done); + while (count < time_out) { + if (fw_cur_state == expect_fw_state && is_doorbell_done) + break; + + ps3_msleep(PS3_LOOP_TIME_INTERVAL_20MS); + fw_cur_state = instance->ioc_adpter->ioc_state_get(instance); + if (fw_cur_state == PS3_FW_STATE_MASK) { + LOG_ERROR( + "hno:%u break because get fw_cur_state NOK.\n", + PS3_HOST(instance)); + break; + } + + if (doorbell_trigger == PS3_REG_DOORBELL_STATE_TO_FAULT && + !is_doorbell_done) { + if (ps3_get_doorbell_done_with_check( + instance, &is_doorbell_done) == PS3_FALSE) { + LOG_ERROR( + "hno:%u break because get doorbell_done NOK.\n", + PS3_HOST(instance)); + break; + } + } + + count++; + } + if (fw_cur_state != expect_fw_state) { + LOG_ERROR( + "hno:%u fw state transition NOK, is_doorbell_done %d state is %s\n", + PS3_HOST(instance), is_doorbell_done, + ps3_ioc_state_print(fw_cur_state)); + ret = -PS3_FAILED; + } +l_out: + return ret; +} + +int ps3_ioc_notify_unload(struct ps3_instance *instance) +{ + unsigned char is_unload_valid = PS3_FALSE; + int ret = PS3_SUCCESS; + unsigned int count = 0; + unsigned char unload_type = PS3_CMD_TRIGGER_UNLOAD; + + LOG_WARN("hno:%u trigger ioc unload reg!!\n", PS3_HOST(instance)); + + if (instance->state_machine.is_suspend) + unload_type = PS3_CMD_TRIGGER_UNLOAD_SUSPEND; + + ps3_ioc_debug0_trigger(instance, unload_type); + + ps3_check_debug0_valid_with_check(instance, &is_unload_valid, + unload_type); + while (count < PS3_FW_STATE_TO_UNLOAD_TMO_LOOP_COUNT) { + if (!is_unload_valid) + break; + + ps3_msleep(PS3_LOOP_TIME_INTERVAL_20MS); + ps3_check_debug0_valid_with_check(instance, &is_unload_valid, + unload_type); + count++; + } + + if (is_unload_valid) { + LOG_ERROR("hno:%u do not wait unload done\n", + PS3_HOST(instance)); + ret = -PS3_FAILED; + } + + return ret; +} + +int ps3_ioc_state_force_to_fault(struct ps3_instance *instance) +{ + return ps3_trigger_ioc_state_change_by_doorbell( + instance, PS3_FW_STATE_FAULT, PS3_REG_DOORBELL_STATE_TO_FAULT, + PS3_FW_STATE_TO_FAULT_TMO_LOOP_COUNT); +} + +int ps3_ioc_state_force_to_halt(struct ps3_instance *instance) +{ + return ps3_trigger_ioc_state_change_by_doorbell( + instance, PS3_FW_STATE_HALT, PS3_REG_DOORBELL_STATE_TO_HALT, + PS3_FW_STATE_TO_HALT_TMO_LOOP_COUNT); +} + +#ifdef PS3_HARDWARE_ASIC +unsigned int ps3_ioc_heartbeat_detect(struct ps3_instance *instance) +{ + unsigned int ret = PS3_FALSE; + unsigned long long heartbeat_value = 0; + + (void)instance; + + if (!ps3_enable_heartbeat_query()) + return ret; + + if (!ps3_ioc_heartbeat_get(instance, &heartbeat_value)) { + LOG_DEBUG("hno:%u probably Linkdown\n", PS3_HOST(instance)); + return ret; + } + + if (heartbeat_value & instance->hard_dog_mask) { + LOG_DEBUG("hno:%u heartbeat detect success\n", + PS3_HOST(instance)); + ret = PS3_TRUE; + } + + return ret; +} +#endif diff --git a/drivers/scsi/linkdata/ps3stor/ps3_ioc_state.h b/drivers/scsi/linkdata/ps3stor/ps3_ioc_state.h new file mode 100644 index 0000000000000000000000000000000000000000..4cea1a7b8963a45855d242af96d5e55fee428d4c --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/ps3_ioc_state.h @@ -0,0 +1,85 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _PS3_IOC_STATE_H_ +#define _PS3_IOC_STATE_H_ + +#include "ps3_instance_manager.h" + +enum { + PS3_FW_STATE_TO_READY_TMO_LOOP_COUNT_RAID = 18000, + PS3_FW_STATE_TO_READY_TMO_LOOP_COUNT_HBA = 18000, + PS3_WAIT_FOR_OUTSTANDING_IO_COMPLETE = 9000, + PS3_FW_STATE_TO_READY_TMO_LOOP_COUNT_SWITCH = 15000, + PS3_FW_STATE_TO_READY_TMO_LOOP_COUNT_RAID_HAPS = 3600, + PS3_FW_STATE_TO_RUNNING_TMO_LOOP_COUNT = 9000, + PS3_FW_STATE_TO_FAULT_TMO_LOOP_COUNT = 1500, + PS3_FW_STATE_TO_HALT_TMO_LOOP_COUNT = 9000, + PS3_FW_STATE_TO_UNLOAD_TMO_LOOP_COUNT = 500, + PS3_WAIT_EVENT_CMD_LOOP_COUNT = 100, + PS3_LOOP_TIME_INTERVAL_1MS = 1, + PS3_LOOP_TIME_INTERVAL_20MS = 20, + PS3_LOOP_TIME_INTERVAL_50MS = 50, + PS3_LOOP_TIME_INTERVAL_100MS = 100, + PS3_LOOP_TIME_INTERVAL_3000MS = 3000, + PS3_WRITE_HARD_RESET_WAIT_TIME_500MS = 500, + PS3_RECOVERY_WAIT_PROBE_FINISH_LOOP_COUNT = 9000, + PS3_RECOVERY_WAIT_LOOP_TIME_INTERVAL_20MS = 20, + PS3_PS3_LOOP_TIME_INTERVAL_1000MS = 1000, + PS3_TRANS_DEAD_IOC_FAILED_MAX_COUNT = 10, +}; +enum ps3_reset_time_type { + PS3_START_WRITE_KEY_REG = 0, + PS3_END_WRITE_KEY_REG = 1, + PS3_START_WAIT_KEY_READY_REG = 2, + PS3_END_WAIT_KEY_READY_REG = 3, + PS3_START_WRITE_HARDRESET_REG = 4, + PS3_END_WRITE_HARDRESET_REG = 5, + PS3_RESET_MAX_COUNT = 6, +}; + +#define PS3_HARD_RESET_FORCE_STOP_MAX_TIME_FIXED \ + (PS3_RECOVERY_WAIT_PROBE_FINISH_LOOP_COUNT * \ + PS3_RECOVERY_WAIT_LOOP_TIME_INTERVAL_20MS / HZ + \ + PS3_FW_STATE_TO_FAULT_TMO_LOOP_COUNT * PS3_LOOP_TIME_INTERVAL_20MS / \ + HZ + \ + PS3_LOOP_TIME_INTERVAL_100MS * PS3_LOOP_TIME_INTERVAL_100MS / HZ + \ + PS3_WRITE_HARD_RESET_WAIT_TIME_500MS / HZ + \ + PS3_SLEEP_TOLERATE_TIMEOUT) + +#ifdef PS3_HARDWARE_HAPS_V200 +#define PS3_HARD_RESET_FORCE_STOP_MAX_TIME(ins) \ + (PS3_HARD_RESET_FORCE_STOP_MAX_TIME_FIXED + \ + (ins)->wait_ready_timeout * PS3_LOOP_TIME_INTERVAL_3000MS / HZ) + +#else +#define PS3_HARD_RESET_FORCE_STOP_MAX_TIME(ins) \ + (PS3_HARD_RESET_FORCE_STOP_MAX_TIME_FIXED + \ + (ins)->wait_ready_timeout * PS3_LOOP_TIME_INTERVAL_20MS / HZ) +#endif + +int ps3_ioc_state_transfer_to_ready(struct ps3_instance *instance); + +int ps3_ioc_state_transfer_wait_to_running(struct ps3_instance *instance); + +int ps3_ioc_state_hard_reset(struct ps3_instance *instance); + +int ps3_ioc_state_shallow_soft_reset(struct ps3_instance *instance); + +int ps3_ioc_state_deep_soft_reset(struct ps3_instance *instance); + +int ps3_ioc_state_force_to_fault(struct ps3_instance *instance); + +int ps3_ioc_state_force_to_halt(struct ps3_instance *instance); + +int ps3_ioc_notify_unload(struct ps3_instance *instance); + +#ifdef PS3_HARDWARE_ASIC +unsigned int ps3_ioc_heartbeat_detect(struct ps3_instance *instance); +#endif + +int ps3_ioc_state_ready_wait(struct ps3_instance *instance); + +int ps3_ioc_state_fault_wait(struct ps3_instance *instance); + +const char *ps3_ioc_state_print(unsigned int state); + +#endif diff --git a/drivers/scsi/linkdata/ps3stor/ps3_ioctl.c b/drivers/scsi/linkdata/ps3stor/ps3_ioctl.c new file mode 100644 index 0000000000000000000000000000000000000000..90cbc5a2fd7a700b8b3dcded90a0f10f9bb66bd7 --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/ps3_ioctl.c @@ -0,0 +1,825 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "ps3_ioctl.h" + +#ifdef _WINDOWS +#include + +#define PS3_FUNC_FROM_CTL_CODE(ctrlCode) \ + (((unsigned long)(ctrlCode & 0x3FFC)) >> 2) +#define PS3_ACC_FROM_CTL_CODE(ctrlCode) \ + (((unsigned long)(ctrlCode & 0xC000)) >> 14) +#else + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#endif + +#include "ps3_cmd_statistics.h" +#include "ps3_htp.h" +#include "ps3_htp_def.h" +#include "ps3_mgr_cmd.h" +#include "ps3_driver_log.h" +#include "ps3_mgr_channel.h" +#include "ps3_mgr_cmd_err.h" +#include "ps3_util.h" +#include "ps3_event.h" + +int ps3_ioctl_init(struct ps3_instance *instance, int cmd_num) +{ +#ifdef _WINDOWS + (void)cmd_num; + ps3_atomic_set(&instance->ioctl_count, 0); +#else + sema_init(&instance->ioctl_sem, cmd_num); +#endif + ps3_atomic_set(&instance->cmd_statistics.cli_cnt, + PS3_CMD_STAT_INIT_VALUE); + return PS3_SUCCESS; +} + +static int ps3_ioctl_capbility_pre_check(struct ps3_instance *instance, + struct PS3IoctlSyncCmd *ioc) +{ + int ret = PS3_SUCCESS; + + if (ioc->sgeCount > PS3_MAX_IOCTL_SGE_NUM || ioc->sgeCount == 0) { + LOG_WARN( + "hno:%u ioctl req, SGE count [%d] > max limit [%d], or SGE count = 0\n", + PS3_HOST(instance), ioc->sgeCount, + PS3_MAX_IOCTL_SGE_NUM); + ret = -PS3_EINVAL; + goto l_out; + } + + (void)instance; +l_out: + return ret; +} + +void ps3_ioctl_buff_bit_pos_update(struct ps3_cmd *cmd) +{ + unsigned char i = 0; + struct PS3Sge *pSge = (struct PS3Sge *)cmd->req_frame->mgrReq.sgl; + struct ps3_instance *instance = cmd->instance; + unsigned char bit_pos = 0; + + if (cmd->transient == NULL) + goto l_out; + + if (cmd->transient->sge_num == 0) + goto l_out; + + for (i = 0; i < cmd->transient->sge_num; i++) { + if (cmd->transient->transient_buff[i] != NULL) { + if (pSge->ext == 1) + pSge = (struct PS3Sge *)cmd->ext_buf; + bit_pos = ps3_dma_addr_bit_pos_check( + (dma_addr_t)le64_to_cpu(pSge->addr)); + if (bit_pos != instance->dma_addr_bit_pos) { + pSge->addr = cpu_to_le64( + PCIE_DMA_HOST_ADDR_BIT_POS_CLEAR_NEW( + bit_pos, + le64_to_cpu(pSge->addr))); + pSge->addr = cpu_to_le64( + PCIE_DMA_HOST_ADDR_BIT_POS_SET_NEW( + instance->dma_addr_bit_pos, + le64_to_cpu(pSge->addr))); + } + } + pSge++; + } +l_out: + return; +} + +void ps3_ioctl_buff_release(struct ps3_cmd *cmd) +{ + unsigned char i = 0; + struct PS3Sge *pSge = (struct PS3Sge *)cmd->req_frame->mgrReq.sgl; + struct ps3_instance *instance = cmd->instance; + + if (cmd->transient == NULL) + return; + + if (cmd->transient->sge_num == 0) + return; + + for (i = 0; i < cmd->transient->sge_num; i++) { + if (cmd->transient->transient_buff[i] != NULL) { + if (pSge->ext == 1) + pSge = (struct PS3Sge *)cmd->ext_buf; + ps3_dma_free_coherent( + instance, cpu_to_le32(pSge->length), + cmd->transient->transient_buff[i], + (dma_addr_t)cpu_to_le64(pSge->addr)); + + cmd->transient->transient_buff[i] = NULL; + memset(pSge, 0, sizeof(struct PS3Sge)); + } + pSge++; + } + memset((void *)cmd->req_frame, 0, sizeof(union PS3ReqFrame)); + cmd->transient->sge_num = 0; +} + +static int ps3_ioctl_sge_fill(struct ps3_instance *instance, + unsigned char *base_addr, struct PS3Sge *psrc_sge, + struct PS3Sge *pdst_sge, void **transient_buff) +{ + int ret = PS3_SUCCESS; + dma_addr_t transient_addr = 0; + + *transient_buff = + ps3_dma_alloc_coherent(instance, psrc_sge->length, + (unsigned long long *)&transient_addr); + + if (*transient_buff == NULL) { + LOG_ERROR( + "hno:%u Failed to alloc kernel SGL buffer for IOCTL\n", + PS3_HOST(instance)); + ret = -PS3_ENOMEM; + goto l_out; + } + + pdst_sge->length = cpu_to_le32(psrc_sge->length); + pdst_sge->ext = 0; + pdst_sge->lastSge = 0; + pdst_sge->addr = (unsigned long long)cpu_to_le64(transient_addr); +#ifdef _WINDOWS + LOG_DEBUG( + "===base_addr[%p],psrc_sge->addr[%d], psrc_sge->length[%d]\n", + base_addr, psrc_sge->addr, psrc_sge->length); + memcpy(*transient_buff, (void *)(base_addr + psrc_sge->addr), + psrc_sge->length); +#else + (void)base_addr; + if (copy_from_user(*transient_buff, + (void __user *)(uintptr_t)psrc_sge->addr, + psrc_sge->length)) { + LOG_ERROR("hno:%u, copy from user err\n", PS3_HOST(instance)); + ret = -PS3_FAILED; + goto l_out; + } +#endif +l_out: + return ret; +} + +static int ps3_ioctl_sgl_fill(struct ps3_instance *instance, + struct ps3_cmd *cmd, struct PS3IoctlSyncCmd *ioc) +{ + unsigned char i = 0; + int ret = PS3_SUCCESS; + struct PS3Sge *pdst_sge = NULL; + struct PS3Sge *psrc_sge = + (struct PS3Sge *)((unsigned int *)ioc + ioc->sglOffset); + unsigned char pre_sge_count = + (unsigned char)((ioc->sgeCount > PS3_FRAME_REQ_SGE_NUM_MGR) ? + (PS3_FRAME_REQ_SGE_NUM_MGR - 1) : + ioc->sgeCount); + + if (cmd->transient->sge_num != 0) { + LOG_ERROR("trace_id[0x%llx], hno:%u got cmd NOK: %d\n", + cmd->trace_id, PS3_HOST(instance), cmd->index); + PS3_BUG(); + ret = -PS3_FAILED; + goto l_out; + } + cmd->transient->sge_num = ioc->sgeCount; + + cmd->req_frame->mgrReq.sgeOffset = + offsetof(struct PS3MgrReqFrame, sgl) >> + PS3_MGR_CMD_SGL_OFFSET_DWORD_SHIFT; + cmd->req_frame->mgrReq.sgeCount = ioc->sgeCount; + + pdst_sge = cmd->req_frame->mgrReq.sgl; + + for (i = 0; i < pre_sge_count; i++) { + LOG_DEBUG("trace_id[0x%llx], hno:%u sge[%u] size : %d\n", + cmd->trace_id, PS3_HOST(instance), i, + psrc_sge->length); + + pdst_sge->length = 0; + + if (psrc_sge->length > 0) { + ret = ps3_ioctl_sge_fill( + instance, (unsigned char *)ioc, psrc_sge, + pdst_sge, &cmd->transient->transient_buff[i]); + if (ret != PS3_SUCCESS) { + LOG_ERROR( + "trace_id[0x%llx], hno:%u sge[%u] fill data error(%d)\n", + cmd->trace_id, PS3_HOST(instance), i, ret); + goto l_out; + } + } + + pdst_sge++; + psrc_sge++; + } + + if (ioc->sgeCount > PS3_FRAME_REQ_SGE_NUM_MGR) { + LOG_FILE_INFO( + "hno:%u ioctl req num %d is bigger than req frame num %d\n", + PS3_HOST(instance), ioc->sgeCount, + PS3_FRAME_REQ_SGE_NUM_MGR); + pdst_sge->addr = + (unsigned long long)cpu_to_le64(cmd->ext_buf_phys); + pdst_sge->length = 0; + pdst_sge->ext = 1; + + pdst_sge = (struct PS3Sge *)cmd->ext_buf; + cmd->req_frame->mgrReq.sgeCount++; + + for (i = PS3_FRAME_REQ_SGE_NUM_MGR - 1; i < ioc->sgeCount; + i++) { + LOG_DEBUG( + "trace_id[0x%llx], hno:%u sge[%u] size : %d\n", + cmd->trace_id, PS3_HOST(instance), i, + psrc_sge->length); + + pdst_sge->length = 0; + + if (psrc_sge->length > 0) { + ret = ps3_ioctl_sge_fill( + instance, (unsigned char *)ioc, + psrc_sge, pdst_sge, + &cmd->transient->transient_buff[i]); + if (ret != PS3_SUCCESS) { + LOG_ERROR( + "trace_id[0x%llx], hno:%u sge[%u] fill data error(%d)\n", + cmd->trace_id, + PS3_HOST(instance), i, ret); + goto l_out; + } + } + + pdst_sge++; + psrc_sge++; + } + } + + pdst_sge--; + pdst_sge->lastSge = 1; +l_out: + return ret; +} + +static int ps3_ioctl_complete(struct ps3_instance *instance, + struct ps3_cmd *cmd, struct PS3IoctlSyncCmd *ioc) +{ + int ret = PS3_SUCCESS; + unsigned int i = 0; + + struct PS3Sge *psrc_sge = + (struct PS3Sge *)((unsigned int *)ioc + ioc->sglOffset); + + for (i = 0; i < ioc->sgeCount; i++) { + LOG_DEBUG( + "trace_id[0x%llx], hno:%u CFID [%d], transient_buff[%u] address: %p, data size: %d\n", + cmd->trace_id, PS3_HOST(instance), cmd->index, i, + cmd->transient->transient_buff[i], psrc_sge->length); + + if (psrc_sge->length > 0) { +#ifdef _WINDOWS + memcpy((void *)((unsigned char *)ioc + psrc_sge->addr), + cmd->transient->transient_buff[i], + psrc_sge->length); +#else + if (copy_to_user( + (void __user *)(uintptr_t)psrc_sge->addr, + cmd->transient->transient_buff[i], + psrc_sge->length)) { + LOG_ERROR("hno:%u copy to user err\n", + PS3_HOST(instance)); + ret = -PS3_FAILED; + goto l_out; + } +#endif + } + psrc_sge++; + } +#ifndef _WINDOWS +l_out: +#endif + LOG_FILE_INFO( + "trace_id[0x%llx], ioctl complete hno:%u CFID [%u], ret[%d]\n", + cmd->trace_id, PS3_HOST(instance), cmd->index, ret); + return ret; +} + +static int ps3_ioctl_mgr_handle(struct ps3_instance *instance, + struct PS3IoctlSyncCmd *ioc) +{ + int ret = PS3_SUCCESS; + struct ps3_cmd *cmd = NULL; + int send_result = PS3_SUCCESS; + unsigned char retry_count = 0; + + while (retry_count < PS3_ERR_MGR_CMD_FAULT_RETRY_MAX) { + ps3_atomic_inc(&instance->cmd_statistics.cmd_delivering); + ret = ps3_mgr_cmd_send_pre_check(instance, PS3_FALSE); + if (ret != PS3_SUCCESS) { + ps3_atomic_dec( + &instance->cmd_statistics.cmd_delivering); + if (ret == -PS3_RECOVERED) { + if (++retry_count < + PS3_ERR_MGR_CMD_FAULT_RETRY_MAX) { + ret = ps3_instance_wait_for_normal( + instance); + } + } + if (ret != PS3_SUCCESS) { + LOG_WARN_LIM( + "tid[0x%llx], hno:%u ioctl cmd pre check NOK ret:%d!\n", + ioc->traceId, PS3_HOST(instance), ret); + goto l_no_free_cmd; + } + continue; + } + break; + } + cmd = ps3_mgr_cmd_alloc(instance); + if (cmd == NULL) { + LOG_WARN("hno:%u ioctl req, not get a cmd packet\n", + PS3_HOST(instance)); + ret = -PS3_FAILED; + ps3_atomic_dec(&instance->cmd_statistics.cmd_delivering); + goto l_no_free_cmd; + } + LOG_DEBUG("trace_id[0x%llx], hno:%u CFID [%u], get mgr cmd succeed\n", + cmd->trace_id, PS3_HOST(instance), cmd->index); + + cmd->time_out = 0; + cmd->is_interrupt = PS3_DRV_TRUE; + + if (ioc->traceId != 0) { + ps3_cmd_trace_id_replace(cmd, ioc->traceId); + LOG_DEBUG( + "trace_id[0x%llx], hno:%u CFID [%u], change trace id from cli\n", + cmd->trace_id, PS3_HOST(instance), cmd->index); + } + + memset(&cmd->req_frame->mgrReq, 0, sizeof(struct PS3MgrReqFrame)); + + memset(cmd->transient->transient_buff, 0, + sizeof(void *) * PS3_MAX_IOCTL_SGE_NUM); + ret = ps3_ioctl_sgl_fill(instance, cmd, ioc); + if (ret != PS3_SUCCESS) { + ps3_atomic_dec(&instance->cmd_statistics.cmd_delivering); + goto l_out; + } + ps3_ioctl_req_frame_build(cmd); + ps3_mgr_cmd_word_build(cmd); + + send_result = ps3_cmd_send_sync(instance, cmd); + ps3_atomic_dec(&instance->cmd_statistics.cmd_delivering); + if (send_result == PS3_SUCCESS) + send_result = ps3_cmd_wait_sync(instance, cmd); + ret = ps3_mgr_complete_proc(instance, cmd, send_result); + if (ret == -PS3_CMD_NO_RESP) { + LOG_ERROR( + "trace_id[0x%llx], hno:%u ioctl request no response!\n", + cmd->trace_id, PS3_HOST(instance)); + goto l_no_free_cmd; + } + + if (ret != PS3_SUCCESS) { + LOG_ERROR( + "trace_id[0x%llx], hno:%u ioctl send request NOK!%d\n", + cmd->trace_id, PS3_HOST(instance), ret); + goto l_out; + } + + ret = ps3_ioctl_complete(instance, cmd, ioc); + + LOG_INFO( + "trace_id[0x%llx], hno:%u CFID [%u], ioctl cmd_type[%d], sub_type[%d] end\n", + cmd->trace_id, PS3_HOST(instance), cmd->index, + ioc->msg.header.cmdType, ioc->msg.header.cmdSubType); + +l_out: + ps3_ioctl_buff_release(cmd); + ps3_mgr_cmd_free(instance, cmd); +l_no_free_cmd: + return ret; +} + +#ifdef _WINDOWS + +static unsigned char ps3_ioctl_windows_pre_check(struct ps3_instance *instance, + struct _PS3_IO_CONTROL ioctl) +{ + unsigned char ret = PS3_TRUE; + unsigned char i = 0; + unsigned char *base_addr = (unsigned char *)&ioctl->ps3Ioctl; + unsigned char *last_addr = base_addr + ioctl->SrbHeader.Length; + + if (ioctl->ps3Ioctl.data == NULL || ioctl->SrbHeader.Length == 0) { + LOG_WARN( + "hno:%u ioctl req, data is [%p], or data len is [%llu]\n", + PS3_HOST(instance), ioctl->ps3Ioctl.data, + ioctl->SrbHeader.Length); + ret = PS3_FALSE; + goto l_out; + } + + if (ps3_ioctl_capbility_pre_check(instance, &ioctl->ps3Ioctl) != + PS3_SUCCESS) { + ret = PS3_FALSE; + goto l_out; + } + + LOG_INFO("hno:%u base_addr[%p], Length[%d], last_addr[%p]\n", + PS3_HOST(instance), base_addr, ioctl->SrbHeader.Length, + last_addr); + + for (i = 0; i < ioctl->ps3Ioctl.sgeCount; i++) { + if (base_addr + ioctl->ps3Ioctl.Sgl[i].addr + + ioctl->ps3Ioctl.Sgl[i].length > + last_addr) { + LOG_ERROR( + "hno:%u ioctl req, sge[%d] len[%d] > total len[%d], sge offset[%p], base addr[%p]\n", + PS3_HOST(instance), i, + ioctl->ps3Ioctl.Sgl[i].length, + ioctl->SrbHeader.Length, + ioctl->ps3Ioctl.Sgl[i].addr, &ioctl->ps3Ioctl); + ret = PS3_FALSE; + goto l_out; + } + } + + (void)instance; + +l_out: + return ret; +} + +int ps3_ioctl_callback_proc(struct ps3_cmd *cmd, unsigned char reply_flags) +{ + unsigned long flags = 0; + int ret = PS3_SUCCESS; + struct ps3_instance *instance = cmd->instance; + int send_result = -PS3_RESP_ERR; + SCSI_REQUEST_BLOCK *srb = cmd->srb; + struct _PS3_IO_CONTROL ps3_ioctl = NULL; + + LOG_DEBUG( + "hno:%u trace_id[0x%llx], got a ioctl response, reply_flags[0x%x]\n", + PS3_HOST(instance), cmd->trace_id, reply_flags); + + PS3_MGR_CMD_BACK_INC(instance, cmd, reply_flags); + + if (unlikely(reply_flags != PS3_REPLY_WORD_FLAG_SUCCESS) && + cmd->retry_cnt < PS3_ERR_MGR_CMD_FAULT_RETRY_MAX) { + LOG_ERROR("trace_id[0x%llx], hno:%u ioctl cmd return failed\n", + cmd->trace_id, PS3_HOST(instance)); + + ret = ps3_err_mgr_cmd_proc(instance, send_result, cmd); + if (ret == -PS3_CMD_NO_RESP) + ret = ps3_mgr_cmd_no_resp_proc(instance, cmd); + + if (ret != -PS3_RETRY) { + LOG_WARN("hno:%u CFID:%d retry, retries:%d, ret:%d\n", + PS3_HOST(instance), cmd->index, cmd->retry_cnt, + ret); + goto l_out; + } + + ps3_spin_lock_irqsave(&cmd->cmd_state.lock, &flags); + cmd->cmd_state.state = PS3_CMD_STATE_PROCESS; + ps3_spin_unlock_irqrestore(&cmd->cmd_state.lock, flags); + cmd->retry_cnt++; + if (cmd->retry_cnt >= PS3_ERR_MGR_CMD_FAULT_RETRY_MAX) { + LOG_ERROR("hno:%u retry time full.\n", + PS3_HOST(instance)); + ret = -PS3_FAILED; + goto l_out; + } + LOG_INFO("hno:%u mgr CFID[%d] retry:%u\n", PS3_HOST(instance), + cmd->index, cmd->retry_cnt); + + ret = ps3_srb_send(instance, srb); + if (ret != PS3_SUCCESS) { + LOG_ERROR( + "trace_id[0x%llx], hno:%u ioctl send request failed!%d\n", + cmd->trace_id, PS3_HOST(instance), ret); + goto l_out; + } + + goto l_retry; + } + +l_out: + SrbSetSrbStatus(srb, SRB_STATUS_ERROR); + if ((cmd->resp_frame->normalRespFrame.respStatus == + PS3_DRV_MGR_RESTART_COMMAND_RSP) && + (reply_flags == PS3_REPLY_WORD_FLAG_FAIL)) { + reply_flags = PS3_REPLY_WORD_FLAG_SUCCESS; + } + if (likely(reply_flags == PS3_REPLY_WORD_FLAG_SUCCESS)) { + ps3_ioctl = (struct _PS3_IO_CONTROL)SrbGetDataBuffer(srb); + ps3_ioctl_complete(instance, cmd, &ps3_ioctl->ps3Ioctl); + SrbSetSrbStatus(srb, SRB_STATUS_SUCCESS); + } + + ps3_ioctl_buff_release(cmd); + ps3_mgr_cmd_free(instance, cmd); + StorPortNotification(RequestComplete, instance, srb); + ps3_atomic_dec(&instance->ioctl_count); + +l_retry: + LOG_DEBUG( + "hno:%u trace_id[0x%llx], end proc a ioctl response, ret[%d]\n", + PS3_HOST(instance), cmd->trace_id, ret); + + return ret; +} + +unsigned char ps3_ioctl_build_io(_In_ struct ps3_instance *instance, + _In_ PSCSI_REQUEST_BLOCK Srb) +{ + unsigned char ret = PS3_FALSE; + struct _PS3_IO_CONTROL ps3_ioctl = + (struct _PS3_IO_CONTROL)SrbGetDataBuffer(Srb); + + LOG_INFO( + "ioctl parase: Type[0x%x], Method[0x%x], Function[0x%x], Access[0x%x]\n", + DEVICE_TYPE_FROM_CTL_CODE(ps3_ioctl->SrbHeader.ControlCode), + METHOD_FROM_CTL_CODE(ps3_ioctl->SrbHeader.ControlCode), + PS3_FUNC_FROM_CTL_CODE(ps3_ioctl->SrbHeader.ControlCode), + PS3_ACC_FROM_CTL_CODE(ps3_ioctl->SrbHeader.ControlCode)); + + if (memcmp(ps3_ioctl->SrbHeader.Signature, PS3_IOCTL_SIG, + sizeof(PS3_IOCTL_SIG)) != 0) { + LOG_ERROR("hno:%u ioctl signature error\n", PS3_HOST(instance)); + SrbSetSrbStatus(Srb, SRB_STATUS_INVALID_REQUEST); + goto l_out; + } + + if (!instance->state_machine.is_load) { + LOG_WARN( + "trace_id[0x%llx], hno:%u instance state not is_load\n", + ps3_ioctl->ps3Ioctl.traceId, PS3_HOST(instance)); + SrbSetSrbStatus(Srb, SRB_STATUS_NO_HBA); + goto l_out; + } + + if (!ps3_atomic_add_unless(&instance->ioctl_count, 1, + PS3_MAX_IOCTL_CMDS)) { + LOG_WARN("hno:%u ioctl concurrency full\n", PS3_HOST(instance)); + + SrbSetSrbStatus(Srb, SRB_STATUS_BUSY); + goto l_out; + } + + if (!ps3_is_instance_state_allow_cmd_execute(instance)) { + SrbSetSrbStatus(Srb, SRB_STATUS_NO_DEVICE); + goto l_out; + } + + ret = ps3_ioctl_windows_pre_check(instance, ps3_ioctl); + if (!ret) { + LOG_WARN("hno:%u ioctl capblity not support\n", + PS3_HOST(instance)); + SrbSetSrbStatus(Srb, SRB_STATUS_INVALID_REQUEST); + ps3_atomic_dec(&instance->ioctl_count); + goto l_out; + } + + if (ps3_ioctl_mgr_handle(instance, &ps3_ioctl->ps3Ioctl, Srb) != + PS3_SUCCESS) { + LOG_ERROR("hno:%u ioctl handle err\n", PS3_HOST(instance)); + SrbSetSrbStatus(Srb, SRB_STATUS_ERROR); + ps3_atomic_dec(&instance->ioctl_count); + goto l_out; + } + + ret = PS3_TRUE; + SrbSetSrbStatus(Srb, SRB_STATUS_SUCCESS); +l_out: + if (!ret) + StorPortNotification(RequestComplete, instance, Srb); + return ret; +} + +unsigned char ps3_ioctl_start_io(_In_ struct ps3_instance *instance, + _In_ PSCSI_REQUEST_BLOCK Srb) +{ + struct ps3_cmd *cmd = ps3_cmd_from_srb_extension_get(Srb); + unsigned char ret = PS3_FALSE; + int ret_tmp = PS3_SUCCESS; + + SrbSetSrbStatus(Srb, SRB_STATUS_ERROR); + + ret_tmp = ps3_srb_send(instance, Srb); + if (ret_tmp != PS3_SUCCESS) { + LOG_ERROR( + "trace_id[0x%llx], hno:%u ioctl send request failed!%d\n", + cmd->trace_id, PS3_HOST(instance), ret_tmp); + goto l_out; + } + + ret = PS3_TRUE; + SrbSetSrbStatus(Srb, SRB_STATUS_SUCCESS); +l_out: + if (!ret) { + ps3_ioctl_buff_release(cmd); + ps3_mgr_cmd_free(instance, cmd); + StorPortNotification(RequestComplete, instance, Srb); + ps3_atomic_dec(&instance->ioctl_count); + } + return ret; +} + +#else + +static int ps3_ioctl_mgr_sync(unsigned long arg) +{ + int ret = PS3_SUCCESS; + struct PS3IoctlSyncCmd *ioc = NULL; + struct ps3_instance *instance = NULL; + struct PS3IoctlSyncCmd __user *user_ioc = + (struct PS3IoctlSyncCmd __user *)arg; + + ioc = (struct PS3IoctlSyncCmd *)memdup_user( + (const void __user *)user_ioc, sizeof(*ioc)); + if (IS_ERR(ioc)) { + LOG_ERROR("ioctl memdup_user err\n"); + return PTR_ERR(ioc); + } + + LOG_INFO("ioctl ps3_ioctl_sync_cmd: trace_id[0x%llx], hno:%u\n" + "\tcmd_type[%d], sgl_offset[%d], sge_count[%d], result[%d]\n", + ioc->traceId, ioc->hostId, ioc->msg.header.cmdType, + ioc->sglOffset, ioc->sgeCount, ioc->resultCode); + + ps3_mutex_lock(&ps3_mgmt_info_get()->ps3_mgmt_lock); + instance = ps3_instance_lookup(ioc->hostId); + if (instance == NULL) { + ps3_mutex_unlock(&ps3_mgmt_info_get()->ps3_mgmt_lock); + LOG_WARN("can found instance, host no is %d\n", ioc->hostId); + ret = -ENODEV; + goto l_out_free_ioc; + } + + if (!instance->state_machine.is_load) { + ps3_mutex_unlock(&ps3_mgmt_info_get()->ps3_mgmt_lock); + LOG_WARN("trace_id[0x%llx], hno:%u instance state [%d]\n", + ioc->traceId, PS3_HOST(instance), + instance->state_machine.is_load); + ret = -ENODEV; + goto l_out_free_ioc; + } + + if (!instance->is_scan_host_finish) { + ps3_mutex_unlock(&ps3_mgmt_info_get()->ps3_mgmt_lock); + LOG_WARN( + "trace_id[0x%llx], hno:%u instance scan host not finish\n", + ioc->traceId, PS3_HOST(instance)); + ret = -EAGAIN; + goto l_out_free_ioc; + } + + ps3_atomic_inc(&instance->cmd_statistics.cli_cnt); + ps3_mutex_unlock(&ps3_mgmt_info_get()->ps3_mgmt_lock); + + LOG_FILE_INFO("hno:%u ioctl ready get sem\n", PS3_HOST(instance)); + if (down_interruptible(&instance->ioctl_sem)) { + LOG_WARN("hno:%u ioctl concurrency full\n", PS3_HOST(instance)); + ret = -ERESTARTSYS; + goto l_out_dec_cnt; + } + + ret = ps3_instance_wait_for_normal(instance); + if (ret != PS3_SUCCESS) { + LOG_WARN( + "trace_id[0x%llx], hno:%u instance state not allowed ioctl req\n", + ioc->traceId, PS3_HOST(instance)); + ret = -ENODEV; + goto l_out_up; + } + + LOG_INFO("hno:%u ioctl got sem\n", PS3_HOST(instance)); + + if (ioc->msg.header.cmdType == PS3_IOCTL_CMD_WEB_SUBSCRIBE) { + if (ps3_atomic_read( + &instance->webSubscribe_context.is_subscribe) != + 1) { + if (ps3_atomic_add_unless(&instance->webSubscribe_context + .subscribe_count, + 1, 1) != 0) { + ret = ps3_web_subscribe(instance); + if (ret != PS3_SUCCESS) { + ps3_atomic_set( + &instance->webSubscribe_context + .subscribe_count, + 0); + ret = -EBUSY; + goto l_out_up; + } + ps3_atomic_set(&instance->webSubscribe_context + .is_subscribe, + 1); + goto l_out_dec_cnt; + } else { + ret = -EAGAIN; + goto l_out_up; + } + } else { + ret = PS3_SUCCESS; + goto l_out_up; + } + } else { + ret = ps3_ioctl_capbility_pre_check(instance, ioc); + if (ret != PS3_SUCCESS) { + LOG_WARN("hno:%u ioctl capblity not support\n", + PS3_HOST(instance)); + ret = -EINVAL; + goto l_out_up; + } + ret = ps3_ioctl_mgr_handle(instance, ioc); + if (ret != PS3_SUCCESS) { + LOG_ERROR("hno:%u ioctl handle NOK\n", + PS3_HOST(instance)); + ret = -EBUSY; + } + } + +l_out_up: + up(&instance->ioctl_sem); +l_out_dec_cnt: + ps3_atomic_dec(&instance->cmd_statistics.cli_cnt); +l_out_free_ioc: + kfree(ioc); + return ret; +} + +long ps3_ioctl_fops(struct file *file, unsigned int cmd, unsigned long arg) +{ + int ret = -ENOTTY; + + if (file == NULL || arg == 0) + goto l_out; + + switch (cmd) { + case PS3_CMD_IOCTL_SYNC_CMD: + ret = ps3_ioctl_mgr_sync(arg); + break; + default: + break; + } +l_out: + return ret; +} + +void ps3_clean_mgr_cmd(struct ps3_instance *instance) +{ + struct ps3_cmd_context *context = &instance->cmd_context; + unsigned int mgr_cmd_idx = 0; + unsigned int mgr_start_idx = context->max_scsi_cmd_count; + unsigned int mgr_end_idx = + context->max_scsi_cmd_count + instance->max_mgr_cmd_count; + struct ps3_cmd *cmd = NULL; + + for (mgr_cmd_idx = mgr_start_idx; mgr_cmd_idx < mgr_end_idx; + mgr_cmd_idx++) { + cmd = context->cmd_buf[mgr_cmd_idx]; + if (cmd->cmd_word.type == PS3_CMDWORD_TYPE_MGR) { + LOG_WARN("hno:%u mgr cmd[%d] complete force!\n", + PS3_HOST(instance), cmd->index); + + cmd->resp_frame->normalRespFrame.respStatus = + PS3_MGR_REC_FORCE; + cmd->cmd_state.state = PS3_CMD_STATE_COMPLETE; + complete(&cmd->sync_done); + } + } +} + +void ps3_ioctl_clean(struct ps3_instance *instance) +{ + if (ps3_atomic_read(&instance->state_machine.state) != + PS3_INSTANCE_STATE_QUIT) { + goto l_out; + } + + while (ps3_atomic_read(&instance->cmd_statistics.cli_cnt) != 0) { + LOG_INFO("hno:%u ioctls not finish\n", PS3_HOST(instance)); + ps3_clean_mgr_cmd(instance); + ps3_msleep(1000); + } +l_out: + return; +} + +#endif diff --git a/drivers/scsi/linkdata/ps3stor/ps3_ioctl.h b/drivers/scsi/linkdata/ps3stor/ps3_ioctl.h new file mode 100644 index 0000000000000000000000000000000000000000..a373a2a007f0ec4154d84e8a015b3dabed663761 --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/ps3_ioctl.h @@ -0,0 +1,64 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _PS3_IOCTL_H_ +#define _PS3_IOCTL_H_ + +#ifdef _WINDOWS +#include "ps3_def.h" +#endif +#include "ps3_instance_manager.h" +#include "ps3_cmd_channel.h" +#include "ps3_htp_ioctl.h" + +#define PS3_MAX_IOCTL_CMDS 3 + +#ifdef _WINDOWS +unsigned char ps3_ioctl_start_io(_In_ struct ps3_instance *instance, + _In_ PSCSI_REQUEST_BLOCK Srb); +unsigned char ps3_ioctl_build_io(_In_ struct ps3_instance *instance, + _In_ PSCSI_REQUEST_BLOCK Srb); +#else +long ps3_ioctl_fops(struct file *file, unsigned int cmd, unsigned long arg); +#endif + +int ps3_ioctl_init(struct ps3_instance *instance, int cmd_num); + +void ps3_ioctl_buff_release(struct ps3_cmd *cmd); + +static inline void ps3_ioctl_req_frame_build(struct ps3_cmd *cmd) +{ + struct PS3MgrReqFrame *mgr_req = &cmd->req_frame->mgrReq; + + mgr_req->reqHead.traceID = cmd->trace_id; + mgr_req->reqHead.cmdType = PS3_CMD_IOCTL; + mgr_req->reqHead.cmdSubType = 0; + mgr_req->reqHead.cmdFrameID = cmd->index; + mgr_req->reqHead.timeout = 0; + mgr_req->timeout = 0; + mgr_req->syncFlag = 1; +} + +static inline void ps3_ioctl_cmd_word_build(struct ps3_instance *instance, + struct ps3_cmd *cmd, + unsigned short cmd_frame_id) +{ + struct PS3CmdWord *cmd_word = &cmd->cmd_word; + + memset(cmd_word, 0, sizeof(*cmd_word)); + + (void)instance; + + cmd_word->type = PS3_CMDWORD_TYPE_MGR; + cmd_word->direct = PS3_CMDWORD_DIRECT_NORMAL; + cmd_word->cmdFrameID = cmd_frame_id; +#ifndef _WINDOWS + cmd_word->isrSN = ps3_msix_index_get(cmd, 1); +#endif +} + +int ps3_ioctl_callback_proc(struct ps3_cmd *cmd, unsigned char reply_flags); + +void ps3_ioctl_clean(struct ps3_instance *instance); + +void ps3_ioctl_buff_bit_pos_update(struct ps3_cmd *cmd); + +#endif diff --git a/drivers/scsi/linkdata/ps3stor/ps3_irq.c b/drivers/scsi/linkdata/ps3stor/ps3_irq.c new file mode 100644 index 0000000000000000000000000000000000000000..8ec1baa113d80375ec60864415e873a852fc4df2 --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/ps3_irq.c @@ -0,0 +1,1677 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "ps3_irq.h" + +#ifndef _WINDOWS +#include +#include +#include +#include +#endif + +#include "ps3_instance_manager.h" +#include "ps3_inner_data.h" +#include "ps3_cmd_complete.h" +#include "ps3_ioc_manager.h" +#include "ps3_util.h" +#include "ps3_dump.h" +#include "ps3_scsih.h" + +#ifndef _WINDOWS + +static const unsigned int PS3_INTERRUPT_CMD_DISABLE_ALL_MASK = 0x02; +static const unsigned int PS3_INTERRUPT_CMD_ENABLE_MSIX = 0x01; +static const unsigned int PS3_INTERRUPT_MASK_DISABLE = 0x00000002; +static const unsigned int PS3_INTERRUPT_STATUS_EXIST_IRQ = 0x00000001; +static const unsigned int PS3_INTERRUPT_CLEAR_IRQ = 0x00000001; + +static const unsigned int PS3_SSD_IOPS_MSIX_VECTORS = 8; +static const unsigned int PS3_HDD_IOPS_MSIX_VECTORS = 8; +static const unsigned int PS3_IRQ_POLL_SCHED_THRESHOLD = 1024; +static const unsigned int PS3_HIGH_IOPS_VECTOR_BATCH_COUNT_SHIFT = 5; +static const unsigned int PS3_BALANCE_MODE_MIN_CPU_NUM = 16; +static const int PS3_SSD_MAX_BUSY_THRESHOLD = 40; +static const unsigned int PS3_IOPS_MSIX_VECTORS = 12; +static const int PS3_HYGON_BUSY_ADJUST_THRESHOLD = 32; +static const unsigned int PS3_MSIX_COMBINED_MODE_MSIX_VECTORS = 16; + +#define PS3_IRQCTX_HOST(irq_context) \ + (ps3_container_of((irq_context), struct ps3_instance, irq_context) \ + ->host->host_no) + +#define PS3_MGR_CMD_MSIX_INDEX(irq_context) \ + ((irq_context)->high_iops_msix_vectors) +#define IS_HIGN_OPS_IRQ(irq_context, irq) \ + (((irq_context)->high_iops_msix_vectors > (irq)->isrSN)) + +#define PS3_MULTI_DATA_DISK_BUSY_THRESHOLD(var, base) ((var) > (base)) +static void msix_irq_mode_check(struct ps3_irq_context *irq_context, + unsigned int max_vectors) +{ + unsigned int online_cpu_num = num_online_cpus(); + unsigned short speed = 0; + unsigned short lnksta = 0; + unsigned int iops_msix_cnt = 0; + + if (!ps3_ioc_multi_func_support(irq_context->instance) || + ps3_get_pci_function(irq_context->instance->pdev) == + PS3_FUNC_ID_1) { + iops_msix_cnt = PS3_IOPS_MSIX_VECTORS; + } else { + iops_msix_cnt = 0; + } + + pcie_capability_read_word(irq_context->instance->pdev, PCI_EXP_LNKSTA, + &lnksta); + speed = lnksta & PCI_EXP_LNKSTA_CLS; + if ((iops_msix_cnt > 0) && + (online_cpu_num >= PS3_BALANCE_MODE_MIN_CPU_NUM) && + (iops_msix_cnt < max_vectors) && (speed >= 0x4)) { + irq_context->high_iops_msix_vectors = iops_msix_cnt; + irq_context->valid_msix_vector_count = min( + irq_context->high_iops_msix_vectors + online_cpu_num, + max_vectors); + irq_context->is_support_balance = PS3_TRUE; + irq_context->is_balance_current_perf_mode = PS3_TRUE; + } else { + irq_context->high_iops_msix_vectors = 0; + irq_context->is_support_balance = PS3_FALSE; + irq_context->is_balance_current_perf_mode = PS3_FALSE; + irq_context->valid_msix_vector_count = + min(online_cpu_num, max_vectors); + } +} + +static inline unsigned char ps3_is_linx80_os(void) +{ + unsigned char ret = PS3_FALSE; +#if (!defined(PS3_OS_MANAGED_IRQ_SUPPORT)) + ret = PS3_TRUE; +#endif + + return ret; +} + +static inline unsigned char ps3_support_irq_affinity(void) +{ + unsigned char ret = PS3_FALSE; + +#if defined(DRIVER_SUPPORT_KERNEL_IRQ_AFFINITY) + ret = PS3_TRUE; +#endif + + if (!ret) { + if (strstr(ps3_host_release_get(), "an8")) + ret = PS3_TRUE; + } + + return ret; +} + +static unsigned int __ps3_irq_vectors_alloc(struct pci_dev *pdev, + struct ps3_irq_context *irq_context) +{ + int msix_vectors = 0; + unsigned int irq_flags = PCI_IRQ_MSIX | PCI_IRQ_MSI; + struct ps3_instance *instance = irq_context->instance; + unsigned int ps3_irq_mode = ps3_pci_irq_mode_query(); + struct irq_affinity *descp = NULL; +#if defined(PS3_OS_MANAGED_IRQ_SUPPORT) + struct irq_affinity desc = { + .pre_vectors = irq_context->high_iops_msix_vectors + }; + descp = &desc; +#endif + + switch (ps3_irq_mode) { + case PS3_PCI_IRQ_MODE_LEGACY: + irq_flags = PCI_IRQ_LEGACY; + break; + case PS3_PCI_IRQ_MODE_MSI: + irq_flags = PCI_IRQ_MSI; + break; + case PS3_PCI_IRQ_MODE_MSIX: + irq_flags = PCI_IRQ_MSIX; + break; + default: + break; + } + + if (ps3_support_irq_affinity() || ps3_is_linx80_os()) { + if (irq_context->instance->smp_affinity_enable) + irq_flags |= PCI_IRQ_AFFINITY; + else + descp = NULL; + } else { + if (irq_context->is_support_balance == PS3_TRUE && + irq_context->is_balance_current_perf_mode == PS3_TRUE) { + descp = NULL; + } else { + if (irq_context->instance->smp_affinity_enable) + irq_flags |= PCI_IRQ_AFFINITY; + else + descp = NULL; + } + } + + LOG_INFO( + "host_no:%u pci_irq_mode:%d specified, msix_vectors:%d max:%d\n", + PS3_IRQCTX_HOST(irq_context), ps3_irq_mode, + irq_context->high_iops_msix_vectors, + irq_context->valid_msix_vector_count); +#if defined(PS3_OS_MANAGED_IRQ_SUPPORT) + msix_vectors = pci_alloc_irq_vectors_affinity( + pdev, + max(instance->min_intr_count, + irq_context->high_iops_msix_vectors), + irq_context->valid_msix_vector_count, irq_flags, descp); +#else + msix_vectors = + pci_alloc_irq_vectors(pdev, + max(instance->min_intr_count, + irq_context->high_iops_msix_vectors), + irq_context->valid_msix_vector_count, + irq_flags); +#endif + + if (msix_vectors <= 0) { + LOG_WARN("host_no:%u alloc msi irq fail! msix_vectors:%d\n", + PS3_IRQCTX_HOST(irq_context), msix_vectors); + msix_vectors = 0; + } else { + if (pdev->msix_enabled == 1) { + irq_context->pci_irq_type = PS3_PCI_IRQ_MSIX; + LOG_DEBUG("host_no:%u alloc msix count:%d\n", + PS3_IRQCTX_HOST(irq_context), msix_vectors); + } else if (pdev->msi_enabled == 1) { + irq_context->pci_irq_type = PS3_PCI_IRQ_MSI; + LOG_DEBUG("host_no:%u alloc msi count:%d\n", + PS3_IRQCTX_HOST(irq_context), msix_vectors); + } else { + LOG_ERROR( + "host_no:%u msi/msix all disable, msix_vectors:%d\n", + PS3_IRQCTX_HOST(irq_context), msix_vectors); + } + } + + return (unsigned int)msix_vectors; +} + +static void +ps3_iops_vectors_affinity_hint_set(struct pci_dev *pdev, + struct ps3_irq_context *irq_context) +{ + unsigned int msix_vector = 0; + unsigned int os_vector = 0; + int node = -1; + const struct cpumask *mask = NULL; + int cpu_id = 0; + + if (!ps3_support_irq_affinity()) + goto l_out; + + if (!irq_context->is_support_balance) + goto l_out; + + if (!irq_context->instance->smp_affinity_enable) + goto l_out; + + node = dev_to_node(&pdev->dev); + if (node >= 0) + mask = cpumask_of_node(node); + + if (mask == NULL) + mask = cpu_online_mask; + + for_each_cpu_and(cpu_id, mask, cpu_online_mask) { + LOG_DEBUG("host_no:%u affinity_hint cpu_id:%d, node:%d\n", + PS3_IRQCTX_HOST(irq_context), cpu_id, node); + } + + for (msix_vector = 0; msix_vector < irq_context->high_iops_msix_vectors; + msix_vector++) { + os_vector = pci_irq_vector(pdev, msix_vector); + irq_set_affinity_hint(os_vector, mask); + } +l_out: + return; +} + +static unsigned int ps3_irq_vectors_alloc(struct pci_dev *pdev, + struct ps3_irq_context *irq_context, + unsigned int max_vectors) +{ + unsigned int msix_vectors = 0; + int nr_entries = 0; + + msix_irq_mode_check(irq_context, max_vectors); + + msix_vectors = __ps3_irq_vectors_alloc(pdev, irq_context); + if ((msix_vectors != irq_context->valid_msix_vector_count) && + (irq_context->is_support_balance)) { + pci_free_irq_vectors(pdev); + irq_context->is_support_balance = PS3_FALSE; + irq_context->is_balance_current_perf_mode = PS3_FALSE; + irq_context->high_iops_msix_vectors = 0; + irq_context->valid_msix_vector_count = + min(num_online_cpus(), max_vectors); + msix_vectors = __ps3_irq_vectors_alloc(pdev, irq_context); + LOG_DEBUG("host_no:%u alloc resource for normal perf mode!\n", + PS3_IRQCTX_HOST(irq_context)); + } + if (msix_vectors > 0) { + irq_context->valid_msix_vector_count = msix_vectors; + ps3_iops_vectors_affinity_hint_set(pdev, irq_context); + } else { + irq_context->high_iops_msix_vectors = 0; + irq_context->is_support_balance = PS3_FALSE; + irq_context->is_balance_current_perf_mode = PS3_FALSE; + nr_entries = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_LEGACY); + if (nr_entries != 1) { + LOG_ERROR("host_no:%u alloc irq fail!\n", + PS3_IRQCTX_HOST(irq_context)); + irq_context->valid_msix_vector_count = 0; + } else { + irq_context->valid_msix_vector_count = 1; + irq_context->pci_irq_type = PS3_PCI_IRQ_LEGACY; + LOG_DEBUG("host_no:%u alloc legacy irq\n", + PS3_IRQCTX_HOST(irq_context)); + } + } + return irq_context->valid_msix_vector_count; +} + +static inline unsigned char +ps3_irq_set_affinity_hint(struct pci_dev *pdev, unsigned int msix_vector, + const struct cpumask *mask, + unsigned char smp_affinity_enable) +{ + unsigned int os_vector = pci_irq_vector(pdev, msix_vector); + unsigned char ret = PS3_TRUE; + + if (smp_affinity_enable) { + if (irq_set_affinity_hint(os_vector, mask)) + ret = PS3_FALSE; + } + return ret; +} + +static void __ps3_cpu_msix_table_init(struct pci_dev *pdev, + struct ps3_irq_context *irq_context) +{ + const struct cpumask *mask = NULL; + int cpu_id = 0; + unsigned int normal_msix_index = irq_context->high_iops_msix_vectors; + unsigned char smp_affinify_enable = + irq_context->instance->smp_affinity_enable; + + for (; normal_msix_index < irq_context->valid_msix_vector_count; + normal_msix_index++) { + mask = pci_irq_get_affinity(pdev, normal_msix_index); + if (mask == NULL) + goto l_get_affinity_failed; + + for_each_cpu_and(cpu_id, mask, cpu_online_mask) { + if (cpu_id >= irq_context->cpu_msix_table_sz) + break; + irq_context->cpu_msix_table[cpu_id] = normal_msix_index; + LOG_DEBUG( + "host_no:%u affinity cpu_id:%d, msix_index:%d\n", + PS3_IRQCTX_HOST(irq_context), cpu_id, + normal_msix_index); + } + } + + return; + +l_get_affinity_failed: + cpu_id = cpumask_first(cpu_online_mask); + normal_msix_index = irq_context->high_iops_msix_vectors; + for (; normal_msix_index < irq_context->valid_msix_vector_count; + normal_msix_index++) { + mask = get_cpu_mask(cpu_id); + if (!ps3_irq_set_affinity_hint(pdev, normal_msix_index, mask, + smp_affinify_enable)) { + LOG_ERROR("host_no:%u set affinity failed, %d.\n", + PS3_IRQCTX_HOST(irq_context), + normal_msix_index); + } else { + irq_context->cpu_msix_table[cpu_id] = normal_msix_index; + LOG_DEBUG( + "host_no:%u affinity cpu_id:%d, msix_index:%d\n", + PS3_IRQCTX_HOST(irq_context), cpu_id, + normal_msix_index); + } + cpu_id = cpumask_next(cpu_id, cpu_online_mask); + } +} + +static int ps3_cpu_msix_table_init(struct pci_dev *pdev, + struct ps3_irq_context *irq_context) +{ + int last_cpu_id = 0; + int cpu_id = 0; + + for_each_online_cpu(cpu_id) { + last_cpu_id = cpu_id; + } + irq_context->cpu_msix_table_sz = last_cpu_id + 1; + + irq_context->cpu_msix_table = + kcalloc(irq_context->cpu_msix_table_sz, sizeof(int), GFP_KERNEL); + if (irq_context->cpu_msix_table == NULL) { + LOG_ERROR("host_no:%u kcalloc fail!\n", + PS3_IRQCTX_HOST(irq_context)); + return -PS3_FAILED; + } + + __ps3_cpu_msix_table_init(pdev, irq_context); + + return PS3_SUCCESS; +} + +static int ps3_reply_fifo_desc_alloc(struct pci_dev *pdev, + struct ps3_irq_context *irq_context) +{ + size_t reply_fifo_desc_buf_size = sizeof(struct PS3ReplyFifoDesc) * + irq_context->valid_msix_vector_count; + + irq_context->reply_fifo_desc_buf_pool = + (struct dma_pool *)ps3_dma_pool_create( + "PS3 reply fifo desc pool", &pdev->dev, + reply_fifo_desc_buf_size, + sizeof(struct PS3ReplyFifoDesc), 0); + if (irq_context->reply_fifo_desc_buf_pool == NULL) { + LOG_ERROR("host_no:%u ps3_dma_pool_create fail!\n", + PS3_IRQCTX_HOST(irq_context)); + goto l_desc_buf_pool_failed; + } + + irq_context->reply_fifo_desc_buf = + (struct PS3ReplyFifoDesc *)ps3_dma_pool_zalloc( + irq_context->instance, + irq_context->reply_fifo_desc_buf_pool, GFP_KERNEL, + &irq_context->reply_fifo_desc_buf_phys); + if (irq_context->reply_fifo_desc_buf == NULL) { + LOG_ERROR("host_no:%u ps3_dma_pool_create fail!\n", + PS3_IRQCTX_HOST(irq_context)); + goto l_desc_buf_alloc_failed; + } + + return PS3_SUCCESS; + +l_desc_buf_alloc_failed: + ps3_dma_pool_destroy(irq_context->reply_fifo_desc_buf_pool); + irq_context->reply_fifo_desc_buf_pool = NULL; +l_desc_buf_pool_failed: + return -PS3_FAILED; +} + +static void ps3_reply_fifo_desc_free(struct ps3_irq_context *irq_context) +{ + if (irq_context->reply_fifo_desc_buf != NULL) { + ps3_dma_pool_free(irq_context->reply_fifo_desc_buf_pool, + irq_context->reply_fifo_desc_buf, + irq_context->reply_fifo_desc_buf_phys); + irq_context->reply_fifo_desc_buf = NULL; + irq_context->reply_fifo_desc_buf_phys = 0; + } + + if (irq_context->reply_fifo_desc_buf_pool != NULL) { + ps3_dma_pool_destroy(irq_context->reply_fifo_desc_buf_pool); + irq_context->reply_fifo_desc_buf_pool = NULL; + } +} + +static int __ps3_reply_fifo_alloc(struct pci_dev *pdev, + struct ps3_irq_context *irq_context) +{ + struct PS3ReplyWord **virt_base = + irq_context->reply_fifo_virt_base_addr_buf; + dma_addr_t *phys_base = irq_context->reply_fifo_phys_base_addr_buf; + size_t reply_fifo_size = + sizeof(struct PS3ReplyWord) * irq_context->reply_fifo_depth; + unsigned int i = 0; + + (void)pdev; + for (; i < irq_context->valid_msix_vector_count; i++) { + virt_base[i] = (struct PS3ReplyWord *)ps3_dma_pool_alloc( + irq_context->instance, irq_context->reply_fifo_pool, + GFP_KERNEL, &phys_base[i]); + if (virt_base[i] == NULL) { + LOG_ERROR("host_no:%u ps3_dma_pool_zalloc fail!\n", + PS3_IRQCTX_HOST(irq_context)); + goto l_alloc_failed; + } + memset(virt_base[i], 0xff, reply_fifo_size); + ps3_get_so_addr_ranger(irq_context->instance, phys_base[i], + reply_fifo_size); + LOG_DEBUG("host_no:%u reply_fifo index:%u phy addr:0x%llx!\n", + PS3_IRQCTX_HOST(irq_context), i, + (unsigned long long)phys_base[i]); + } + + return PS3_SUCCESS; + +l_alloc_failed: + for (; i > 0; i--) { + ps3_dma_pool_free(irq_context->reply_fifo_pool, + virt_base[i - 1], phys_base[i - 1]); + virt_base[i - 1] = NULL; + phys_base[i - 1] = 0; + } + return -PS3_FAILED; +} + +static int ps3_reply_fifo_alloc(struct pci_dev *pdev, + struct ps3_irq_context *irq_context) +{ + size_t reply_fifo_size = + sizeof(struct PS3ReplyWord) * irq_context->reply_fifo_depth; + + irq_context->reply_fifo_pool = + ps3_dma_pool_create("PS3 reply fifo pool", &pdev->dev, + reply_fifo_size, DMA_ALIGN_BYTES_4K, 0); + if (irq_context->reply_fifo_pool == NULL) { + LOG_ERROR("host_no:%u ps3_dma_pool_create fail!\n", + PS3_IRQCTX_HOST(irq_context)); + goto l_reply_fifo_pool_failed; + } + + if (__ps3_reply_fifo_alloc(pdev, irq_context) != PS3_SUCCESS) + goto l_reply_fifo_alloc_failed; + + return PS3_SUCCESS; + +l_reply_fifo_alloc_failed: + ps3_dma_pool_destroy(irq_context->reply_fifo_pool); + irq_context->reply_fifo_pool = NULL; +l_reply_fifo_pool_failed: + return -PS3_FAILED; +} + +static void ps3_reply_fifo_free(struct ps3_irq_context *irq_context) +{ + struct PS3ReplyWord **virt_base = + irq_context->reply_fifo_virt_base_addr_buf; + dma_addr_t *phys_base = irq_context->reply_fifo_phys_base_addr_buf; + unsigned int i = 0; + + if (irq_context->reply_fifo_pool == NULL) + goto l_out; + + for (; i < irq_context->valid_msix_vector_count; i++) { + if (virt_base[i] == NULL) + continue; + + ps3_dma_pool_free(irq_context->reply_fifo_pool, virt_base[i], + phys_base[i]); + virt_base[i] = NULL; + phys_base[i] = 0; + } + + ps3_dma_pool_destroy(irq_context->reply_fifo_pool); + irq_context->reply_fifo_pool = NULL; +l_out: + return; +} + +static int ps3_irq_resource_alloc(struct pci_dev *pdev, + struct ps3_irq_context *irq_context) +{ + if (ps3_reply_fifo_desc_alloc(pdev, irq_context) != PS3_SUCCESS) + goto l_desc_alloc_failed; + + if (ps3_reply_fifo_alloc(pdev, irq_context) != PS3_SUCCESS) + goto l_reply_fifo_alloc_failed; + + return PS3_SUCCESS; + +l_reply_fifo_alloc_failed: + ps3_reply_fifo_desc_free(irq_context); +l_desc_alloc_failed: + return -PS3_FAILED; +} + +static void ps3_irq_resource_free(struct ps3_irq_context *irq_context) +{ + ps3_reply_fifo_free(irq_context); + ps3_reply_fifo_desc_free(irq_context); +} + +static void ps3_reply_fifo_desc_set(struct pci_dev *pdev, + struct ps3_irq_context *irq_context) +{ + struct PS3ReplyFifoDesc *desc_buf = irq_context->reply_fifo_desc_buf; + dma_addr_t *phys_base = irq_context->reply_fifo_phys_base_addr_buf; + unsigned int i = 0; + + for (; i < irq_context->valid_msix_vector_count; i++) { + desc_buf[i].ReplyFifoBaseAddr = cpu_to_le64(phys_base[i]); + desc_buf[i].irqNo = cpu_to_le32(pci_irq_vector(pdev, i)); + desc_buf[i].depthReplyFifo = irq_context->reply_fifo_depth; + + if (irq_context->is_support_balance && + i < irq_context->high_iops_msix_vectors) { + desc_buf[i].isrAccMode = PS3_ISR_ACC_MODE_IOPS_VER0; + } else { + desc_buf[i].isrAccMode = PS3_ISR_ACC_MODE_LATENCY; + } + } +} + +static inline void __ps3_irqs_exit(struct ps3_irq *irq, + struct ps3_irq_context *irq_context) +{ + irq_set_affinity_hint(irq->irqNo, NULL); + + if (irq->is_irq_poll_disabled == PS3_FALSE) { + ps3_irq_poll_disable(&irq->irqpoll); + irq->is_irq_poll_disabled = PS3_TRUE; + } else { + LOG_INFO("host_no:%u irq poll(%u) already disabled!\n", + PS3_HOST(irq_context->instance), irq->isrSN); + } + + free_irq(irq->irqNo, irq); +} + +int ps3_irqs_init(struct ps3_instance *instance) +{ + struct ps3_irq_context *irq_context = &instance->irq_context; + struct PS3ReplyWord **virt_base = + irq_context->reply_fifo_virt_base_addr_buf; + struct pci_dev *pdev = instance->pdev; + struct ps3_irq *irqs = NULL; + unsigned int i = 0; + unsigned int dump_irq_index; + int retval; + + irq_context->irqs = (struct ps3_irq *)ps3_kcalloc( + instance, irq_context->valid_msix_vector_count, + sizeof(struct ps3_irq)); + if (irq_context->irqs == NULL) { + LOG_ERROR("host_no:%u kcalloc fail!\n", + PS3_IRQCTX_HOST(irq_context)); + goto l_out; + } + irqs = irq_context->irqs; + + for (i = 0; i < irq_context->valid_msix_vector_count; i++) { + irqs[i].irqNo = pci_irq_vector(pdev, i); + irqs[i].isrSN = i; + irqs[i].reply_fifo_virt_base_addr = virt_base[i]; + irqs[i].instance = instance; + irqs[i].irq_poll_sched_threshold = PS3_IRQ_POLL_SCHED_THRESHOLD; + irqs[i].is_irq_poll_disabled = PS3_FALSE; + irqs[i].is_sched_irq_poll = PS3_FALSE; + irqs[i].is_enable_irq = PS3_TRUE; + atomic_set(&irqs[i].is_busy, 0); + irqs[i].last_reply_idx = 0; + snprintf(irqs[i].name, PS3_IRQ_NAME_LENGTH, "ps3_irq_%d_%d", + instance->host->host_no, i); + retval = request_irq(irqs[i].irqNo, instance->ioc_adpter->isr, + IRQF_SHARED, irqs[i].name, &(irqs[i])); + if (retval) { + LOG_ERROR("host_no:%u request_irq failed! SN:%d\n", + PS3_HOST(instance), i); + + goto l_failed; + } + + ps3_irq_poll_init(&irqs[i].irqpoll, + PS3_IRQ_POLL_SCHED_THRESHOLD, + ps3_irqpoll_service); + } + + dump_irq_index = PS3_MGR_CMD_MSIX_INDEX(irq_context); + retval = request_irq(irqs[dump_irq_index].irqNo, ps3_dump_irq_handler, + IRQF_SHARED, "ps3_dump_irq", (void *)instance); + if (retval) { + LOG_ERROR("host_no:%u request dump irq failed! SN:%d\n", + PS3_HOST(instance), dump_irq_index); + goto l_failed; + } + irq_context->dump_isrSN = dump_irq_index; + + instance->ioc_adpter->irq_disable(instance); + + return PS3_SUCCESS; +l_failed: + for (; i > 0; i--) + __ps3_irqs_exit(&irqs[i - 1], irq_context); + + if (irq_context->irqs != NULL) { + ps3_kfree(instance, irq_context->irqs); + irq_context->irqs = NULL; + } +l_out: + return -PS3_FAILED; +} + +int ps3_irqs_init_switch(struct ps3_instance *instance) +{ + struct ps3_irq_context *irq_context = &instance->irq_context; + struct PS3ReplyWord **virt_base = + irq_context->reply_fifo_virt_base_addr_buf; + struct pci_dev *pdev = instance->pdev; + struct ps3_irq *irqs = NULL; + struct ps3_irq_recovery *irqs_recovery = NULL; + unsigned int i = 0; + unsigned int dump_irq_index; + unsigned int watch_irq_index = PS3_SWITCH_IRQ_INDEX; + + irq_context->irqs = (struct ps3_irq *)ps3_kcalloc( + instance, irq_context->valid_msix_vector_count, + sizeof(struct ps3_irq)); + if (irq_context->irqs == NULL) { + LOG_ERROR("host_no:%u kcalloc fail!\n", + PS3_IRQCTX_HOST(irq_context)); + goto l_out; + } + irqs = irq_context->irqs; + + for (i = 0; i < irq_context->valid_msix_vector_count; i++) { + irqs[i].irqNo = pci_irq_vector(pdev, i); + irqs[i].isrSN = i; + irqs[i].reply_fifo_virt_base_addr = virt_base[i]; + irqs[i].instance = instance; + irqs[i].irq_poll_sched_threshold = PS3_IRQ_POLL_SCHED_THRESHOLD; + irqs[i].is_irq_poll_disabled = PS3_FALSE; + irqs[i].is_sched_irq_poll = PS3_FALSE; + irqs[i].is_enable_irq = PS3_TRUE; + atomic_set(&irqs[i].is_busy, 0); + irqs[i].last_reply_idx = 0; + snprintf(irqs[i].name, PS3_IRQ_NAME_LENGTH, "ps3_irq_%d_%d", + instance->host->host_no, i); + + if (request_irq(irqs[i].irqNo, instance->ioc_adpter->isr, + IRQF_SHARED, irqs[i].name, &(irqs[i]))) { + LOG_ERROR("host_no:%u request_irq failed! SN:%d\n", + PS3_HOST(instance), i); + + goto l_failed; + } + + ps3_irq_poll_init(&irqs[i].irqpoll, + PS3_IRQ_POLL_SCHED_THRESHOLD, + ps3_irqpoll_service); + } + + dump_irq_index = PS3_MGR_CMD_MSIX_INDEX(irq_context); + if (request_irq(irqs[dump_irq_index].irqNo, ps3_dump_irq_handler, + IRQF_SHARED, "ps3_dump_irq", (void *)instance)) { + LOG_ERROR("host_no:%u request dump irq failed! SN:%d\n", + PS3_HOST(instance), dump_irq_index); + goto l_failed; + } + irq_context->dump_isrSN = dump_irq_index; + irq_context->irq_recovery = (struct ps3_irq_recovery *)ps3_kcalloc( + instance, 1, sizeof(struct ps3_irq_recovery)); + if (irq_context->irq_recovery == NULL) { + LOG_ERROR("host_no:%u kcalloc irq_recovery fail!\n", + PS3_IRQCTX_HOST(irq_context)); + goto l_free_dump; + } + irqs_recovery = irq_context->irq_recovery; + irqs_recovery->irqNo = pci_irq_vector(pdev, 0); + irqs_recovery->isrSN = watch_irq_index; + irqs_recovery->instance = instance; + + if (request_irq(irqs_recovery[watch_irq_index].irqNo, + ps3_recovery_irq_handler, IRQF_SHARED, + "ps3_watchdog_irq", (void *)irqs_recovery)) { + LOG_ERROR("host_no:%u request watchdog irq failed! SN:%d\n", + PS3_HOST(instance), watch_irq_index); + goto l_free_dump; + } + instance->ioc_adpter->irq_disable(instance); + + return PS3_SUCCESS; +l_free_dump: + free_irq(irqs[dump_irq_index].irqNo, instance); +l_failed: + for (; i > 0; i--) + __ps3_irqs_exit(&irqs[i - 1], irq_context); + + if (irq_context->irqs != NULL) { + ps3_kfree(instance, irq_context->irqs); + irq_context->irqs = NULL; + } + + if (irq_context->irq_recovery != NULL) { + ps3_kfree(instance, irq_context->irq_recovery); + irq_context->irq_recovery = NULL; + } +l_out: + return -PS3_FAILED; +} + +void ps3_irqs_exit(struct ps3_instance *instance) +{ + struct ps3_irq_context *irq_context = &instance->irq_context; + struct ps3_irq *irqs = irq_context->irqs; + struct pci_dev *pdev = instance->pdev; + unsigned int i = 0; + unsigned int dump_irq_index; + unsigned int watch_irq_index = PS3_SWITCH_IRQ_INDEX; + struct ps3_irq_recovery *irq_recovery; + + if (irqs == NULL) + goto l_out; + + dump_irq_index = PS3_MGR_CMD_MSIX_INDEX(irq_context); + irq_set_affinity_hint(irqs[dump_irq_index].irqNo, NULL); + free_irq(irqs[dump_irq_index].irqNo, instance); + + if (irq_context->irq_recovery != NULL) { + irq_set_affinity_hint( + irq_context->irq_recovery[watch_irq_index].irqNo, NULL); + free_irq(irq_context->irq_recovery[watch_irq_index].irqNo, + irq_context->irq_recovery); + irq_recovery = irq_context->irq_recovery; + irq_context->irq_recovery = NULL; + ps3_kfree(instance, irq_recovery); + } + + for (i = 0; i < irq_context->valid_msix_vector_count; i++) + __ps3_irqs_exit(&irqs[i], irq_context); + + irq_context->irqs = NULL; + kfree(irqs); + + if (pdev->msix_enabled || pdev->msi_enabled) + pci_free_irq_vectors(pdev); + +l_out: + return; +} + +static unsigned char ps3_irq_max_vectors_calc(struct ps3_instance *instance, + int *max_vectors) +{ + unsigned char ret = PS3_TRUE; + unsigned int max_replyq_count = 0; + + if (!instance->ioc_adpter->max_replyq_count_get(instance, + &max_replyq_count)) { + LOG_ERROR("host_no:%u get max replyq count NOK\n", + PS3_HOST(instance)); + ret = PS3_FALSE; + goto l_out; + } + + if (max_replyq_count > PS3_MSIX_COMBINED_MODE_MSIX_VECTORS) + instance->msix_combined = PS3_TRUE; + + LOG_INFO("reqlyq max_replyq_count:%d\n", max_replyq_count); + + *max_vectors = pci_msix_vec_count(instance->pdev); + if (*max_vectors <= 0) { + *max_vectors = pci_msi_vec_count(instance->pdev); + if (*max_vectors < 0) { + *max_vectors = 0; + goto l_out; + } + } + *max_vectors = min_t(int, *max_vectors, max_replyq_count); + +l_out: + return ret; +} + +int ps3_irq_context_init(struct ps3_instance *instance) +{ + struct ps3_irq_context *irq_context = &instance->irq_context; + struct pci_dev *pdev = instance->pdev; + int max_vectors = 0; + + if (!ps3_irq_max_vectors_calc(instance, &max_vectors)) + goto l_irq_vectors_alloc_failed; + + if (!ps3_ioc_mgr_max_fw_cmd_get(instance, + &irq_context->reply_fifo_depth)) { + goto l_irq_vectors_alloc_failed; + } + + irq_context->reply_fifo_depth += instance->reply_fifo_depth_addition; + irq_context->instance = instance; + + LOG_INFO("max_vectors:%d replyQ_depth:%d\n", max_vectors, + irq_context->reply_fifo_depth); + + if (max_vectors <= 0) { + LOG_ERROR("host_no:%u IOC max_vectors invliad:%d\n", + PS3_IRQCTX_HOST(irq_context), max_vectors); + goto l_irq_vectors_alloc_failed; + } + + if (!instance->msix_combined) + instance->smp_affinity_enable = PS3_FALSE; + + if (ps3_irq_vectors_alloc(pdev, irq_context, max_vectors) < + instance->min_intr_count) { + LOG_ERROR( + "host_no:%u alloc irq NOK![cpunum:%d][irq_type:%d][min_intr:%d]\n", + PS3_IRQCTX_HOST(irq_context), num_online_cpus(), + irq_context->pci_irq_type, instance->min_intr_count); + if (irq_context->valid_msix_vector_count > 0) { + pci_free_irq_vectors(pdev); + irq_context->high_iops_msix_vectors = 0; + irq_context->valid_msix_vector_count = 0; + } + goto l_irq_vectors_alloc_failed; + } + + if (ps3_cpu_msix_table_init(pdev, irq_context) != PS3_SUCCESS) + goto l_cpu_msix_table_init_failed; + + if (ps3_irq_resource_alloc(pdev, irq_context) != PS3_SUCCESS) + goto l_irq_resource_alloc_failed; + + ps3_reply_fifo_desc_set(pdev, irq_context); + instance->is_support_irq = PS3_TRUE; + + return PS3_SUCCESS; + +l_irq_resource_alloc_failed: + kfree(irq_context->cpu_msix_table); + irq_context->cpu_msix_table = NULL; +l_cpu_msix_table_init_failed: + pci_free_irq_vectors(pdev); + irq_context->high_iops_msix_vectors = 0; + irq_context->valid_msix_vector_count = 0; +l_irq_vectors_alloc_failed: + memset(irq_context, 0, sizeof(struct ps3_irq_context)); + return -PS3_FAILED; +} + +int ps3_irq_context_exit(struct ps3_instance *instance) +{ + struct ps3_irq_context *irq_context = &instance->irq_context; + struct pci_dev *pdev = instance->pdev; + + if (!instance->is_pcie_err_detected) + ps3_irq_resource_free(irq_context); + + if (irq_context->cpu_msix_table != NULL) { + kfree(irq_context->cpu_msix_table); + irq_context->cpu_msix_table = NULL; + } + + if (pdev->msix_enabled || pdev->msi_enabled) + pci_free_irq_vectors(pdev); + + if (!instance->is_pcie_err_detected) + memset(irq_context, 0, sizeof(struct ps3_irq_context)); + return PS3_SUCCESS; +} + +static inline unsigned char ps3_is_hdd_cmd(struct ps3_cmd *cmd) +{ + unsigned char is_hdd_io = PS3_FALSE; + const struct PS3VDEntry *vd_entry = NULL; + + if (cmd->io_attr.dev_type == PS3_DEV_TYPE_VD) { + vd_entry = cmd->io_attr.vd_entry; + if (!vd_entry->isNvme && !vd_entry->isSsd) + is_hdd_io = PS3_TRUE; + } else { + if (ps3_is_hdd_pd(cmd->io_attr.pd_entry->dev_type)) + is_hdd_io = PS3_TRUE; + } + + return is_hdd_io; +} + +static inline unsigned char ps3_sdev_is_high_load(struct ps3_cmd *cmd, + unsigned short scale) +{ + int busy_base = cmd->instance->device_busy_threshold; + int busy_threshold = busy_base * scale; + int device_busy = 0; +#if defined DRIVER_SUPPORT_PRIV_BUSY + struct ps3_scsi_priv_data *device_priv_data = + (struct ps3_scsi_priv_data *)cmd->scmd->device->hostdata; + + if (device_priv_data == NULL) + return PS3_FALSE; + device_busy = atomic_read(&device_priv_data->sdev_priv_busy); +#else + device_busy = atomic_read(&cmd->scmd->device->device_busy); +#endif + + if (PS3_MULTI_DATA_DISK_BUSY_THRESHOLD(busy_threshold, busy_base) && + !ps3_is_hdd_cmd(cmd)) { + if (ps3_host_vendor_get() == PS3_HOST_VENDOR_HYGON && + busy_threshold <= PS3_HYGON_BUSY_ADJUST_THRESHOLD) { + busy_threshold = busy_base; + } else if (busy_threshold > PS3_SSD_MAX_BUSY_THRESHOLD) { + busy_threshold = PS3_SSD_MAX_BUSY_THRESHOLD; + } + } + + if (device_busy > busy_threshold) + return PS3_TRUE; + return PS3_FALSE; +} + +static inline unsigned int +ps3_iops_msix_index_get(struct ps3_irq_context *irq_context) +{ + unsigned int msix_index = 0; + unsigned int ioc_count = + atomic_add_return(1, &irq_context->high_iops_io_count); + unsigned int batch_num = + ioc_count >> PS3_HIGH_IOPS_VECTOR_BATCH_COUNT_SHIFT; + + msix_index = batch_num % PS3_IOPS_MSIX_VECTORS; + return msix_index; +} + +unsigned int ps3_msix_index_get(struct ps3_cmd *cmd, unsigned short scale) +{ + int processor_id = 0; + unsigned int msix_index = 0; + struct ps3_irq_context *irq_context = &cmd->instance->irq_context; + + if (irq_context->valid_msix_vector_count == 1) { + msix_index = 0; + goto l_out; + } + + if (cmd->cmd_word.type == PS3_CMDWORD_TYPE_MGR) { + msix_index = PS3_MGR_CMD_MSIX_INDEX(irq_context); + goto l_out; + } + + if (PS3_CMD_TYPE_IS_RW(cmd->cmd_word.type) && + (irq_context->is_balance_current_perf_mode) && + ps3_sdev_is_high_load(cmd, scale)) { + msix_index = ps3_iops_msix_index_get(irq_context); + } else if (cmd->instance->host->nr_hw_queues > 1) { + msix_index = blk_mq_unique_tag_to_hwq(blk_mq_unique_tag( + SCMD_GET_REQUEST(cmd->scmd))) + + irq_context->high_iops_msix_vectors; + } else { + processor_id = raw_smp_processor_id(); + msix_index = irq_context->cpu_msix_table[processor_id]; + + if (msix_index == PS3_MGR_CMD_MSIX_INDEX(irq_context)) + msix_index++; + } + +l_out: + return msix_index; +} +void ps3_perf_update(struct ps3_instance *instance, unsigned char iocPerfMode) +{ + struct ps3_irq_context *irq_context = &instance->irq_context; + unsigned char support_balance = irq_context->is_support_balance; + unsigned char *is_balance = &irq_context->is_balance_current_perf_mode; + + if (support_balance) { + if (iocPerfMode == PS3_PERF_MODE_BALANCE) + *is_balance = PS3_TRUE; + else + *is_balance = PS3_FALSE; + } else { + *is_balance = PS3_FALSE; + } +} +void ps3_irqs_enable(struct ps3_instance *instance) +{ + struct ps3_irq_context *irq_context = &instance->irq_context; + + LOG_DEBUG("host_no:%u irq enable\n", PS3_HOST(instance)); + + switch (irq_context->pci_irq_type) { + case PS3_PCI_IRQ_LEGACY: + ps3_ioc_legacy_irqs_enable(instance); + break; + case PS3_PCI_IRQ_MSI: + ps3_ioc_msi_enable(instance); + break; + case PS3_PCI_IRQ_MSIX: + ps3_ioc_msix_enable(instance); + break; + default: + LOG_ERROR("host_no:%u irq type is NOK :%d\n", + PS3_HOST(instance), irq_context->pci_irq_type); + break; + } + irq_context->is_enable_interrupts = PS3_DRV_TRUE; + mb(); /* in order to force CPU ordering */ +} + +void ps3_irqs_disable(struct ps3_instance *instance) +{ + struct ps3_irq_context *irq_context = &instance->irq_context; + + LOG_DEBUG("host_no:%u irq disable\n", PS3_HOST(instance)); + + if (irq_context->is_enable_interrupts == PS3_DRV_FALSE) + goto l_out; + + irq_context->is_enable_interrupts = PS3_DRV_FALSE; + mb(); /* in order to force CPU ordering */ + switch (irq_context->pci_irq_type) { + case PS3_PCI_IRQ_LEGACY: + ps3_ioc_legacy_irqs_disable(instance); + break; + case PS3_PCI_IRQ_MSI: + ps3_ioc_msi_disable(instance); + break; + case PS3_PCI_IRQ_MSIX: + ps3_ioc_msix_disable(instance); + break; + default: + LOG_ERROR("host_no:%u irq type is NOK :%d\n", + PS3_HOST(instance), irq_context->pci_irq_type); + break; + } + +l_out: + return; +} + +irqreturn_t ps3_irqs_service(int irq_so, void *priv) +{ + irqreturn_t ret = IRQ_NONE; + int complete_num = 0; + struct ps3_irq *irq = (struct ps3_irq *)priv; + struct ps3_irq_context *irq_context = NULL; + + if (irq == NULL) { + LOG_ERROR_IN_IRQ(irq->instance, "irq is null !\n"); + ret = IRQ_NONE; + goto l_out; + } + irq_context = &irq->instance->irq_context; + + if ((unsigned int)irq_so != irq->irqNo) { + LOG_ERROR_IN_IRQ(irq->instance, + "irq_so:%d != irq->irqNo:%d !\n", irq_so, + irq->irqNo); + ret = IRQ_NONE; + goto l_out; + } + + if (!irq_context->is_enable_interrupts) { + LOG_INFO_IN_IRQ(irq->instance, + "host_no:%u interrupt has disabled !\n", + PS3_HOST(irq->instance)); + ret = IRQ_NONE; + goto l_out; + } + + if (irq->is_sched_irq_poll) { + LOG_WARN_IN_IRQ(irq->instance, + "host_no:%u had enter into irq poll !\n", + PS3_HOST(irq->instance)); + ret = IRQ_HANDLED; + goto l_out; + } + + complete_num = ps3_cmd_complete(irq); + if (complete_num <= 0) { + LOG_DEBUG("host_no:%u there is no completed ps3_cmd !\n", + PS3_HOST(irq->instance)); + ret = IRQ_NONE; + } else { + ret = IRQ_HANDLED; + } + +l_out: + return ret; +} + +int ps3_irqpoll_service(struct irq_poll *irqpoll, int budget) +{ + int complete_num = 0; + struct ps3_irq *irq = + ps3_container_of(irqpoll, struct ps3_irq, irqpoll); + + if (irq->is_enable_irq) { + disable_irq(irq->irqNo); + irq->is_enable_irq = PS3_DRV_FALSE; + } + + complete_num = ps3_cmd_complete(irq); + if (complete_num < budget) { + ps3_irq_poll_complete(irqpoll); + irq->is_sched_irq_poll = PS3_DRV_FALSE; + enable_irq(irq->irqNo); + irq->is_enable_irq = PS3_DRV_TRUE; + } + + return complete_num; +} + +void ps3_irqpolls_enable(struct ps3_instance *instance) +{ + struct ps3_irq_context *irq_context = &instance->irq_context; + struct ps3_irq *irqs = irq_context->irqs; + unsigned int i = 0; + + if (irqs == NULL) + return; + + for (; i < irq_context->valid_msix_vector_count; i++) { + if (irqs[i].is_irq_poll_disabled == PS3_TRUE) { + ps3_irq_poll_enable(&irqs[i].irqpoll); + irqs[i].is_irq_poll_disabled = PS3_FALSE; + } else { + LOG_INFO("host_no:%u irq poll(%d) not disabled!\n", + PS3_HOST(instance), i); + } + } +} + +void ps3_irqs_sync(struct ps3_instance *instance) +{ + struct ps3_irq_context *irq_context = &instance->irq_context; + struct ps3_irq *irqs = irq_context->irqs; + unsigned int i = 0; + + if (irqs == NULL) + goto l_out; + for (; i < irq_context->valid_msix_vector_count; i++) { + synchronize_irq(irqs[i].irqNo); + + if (irqs[i].is_irq_poll_disabled == PS3_FALSE) { + ps3_irq_poll_disable(&irqs[i].irqpoll); + irqs[i].is_irq_poll_disabled = PS3_TRUE; + } else { + LOG_INFO("host_no:%u irq poll(%d) already disabled!\n", + PS3_HOST(instance), i); + } + } +l_out: + return; +} + +#else + +static void ps3_reply_fifo_desc_free(struct ps3_instance *instance); +static void ps3_reply_fifo_free(struct ps3_instance *instance); +static void ps3_irq_resource_free(struct ps3_instance *instance); + +static unsigned char ps3_irq_max_vectors_calc(struct ps3_instance *instance, + unsigned int *max_vectors) +{ + unsigned char ret = PS3_TRUE; + unsigned int max_replyq_count = 0; + + if (!instance->ioc_adpter->max_replyq_count_get(instance, + &max_replyq_count)) { + ret = PS3_FALSE; + goto l_out; + } + + LOG_INFO("reqlyq max_replyq_count:%u\n", max_replyq_count); + + *max_vectors = instance->pci_dev_context.irq_vec_count; + + *max_vectors = PS3_MIN(*max_vectors, max_replyq_count); + *max_vectors = + PS3_MIN(*max_vectors, (unsigned int)num_online_cpus() + 1); +l_out: + return ret; +} + +static int ps3_reply_fifo_desc_alloc(struct ps3_instance *instance) +{ + struct ps3_irq_context *irq_context = &instance->irq_context; + + irq_context->reply_fifo_desc_buf_size = + sizeof(struct PS3ReplyFifoDesc) * + irq_context->valid_msix_vector_count; + + irq_context->reply_fifo_desc_buf = + (struct PS3ReplyFifoDesc *)ps3_dma_alloc_coherent( + instance, irq_context->reply_fifo_desc_buf_size, + (unsigned long long *)&irq_context + ->reply_fifo_desc_buf_phys); + if (irq_context->reply_fifo_desc_buf == NULL) { + LOG_ERROR("host_no:%u dma alloc fail!\n", PS3_HOST(instance)); + goto l_failed; + } + + return PS3_SUCCESS; +l_failed: + ps3_reply_fifo_desc_free(instance); + return -PS3_FAILED; +} + +static void ps3_reply_fifo_desc_free(struct ps3_instance *instance) +{ + struct ps3_irq_context *irq_context = &instance->irq_context; + + if (irq_context->reply_fifo_desc_buf != NULL) { + ps3_dma_free_coherent(instance, + irq_context->reply_fifo_desc_buf_size, + irq_context->reply_fifo_desc_buf, + irq_context->reply_fifo_desc_buf_phys); + + irq_context->reply_fifo_desc_buf = NULL; + irq_context->reply_fifo_desc_buf_phys = 0; + } +} + +static int ps3_reply_fifo_alloc(struct ps3_instance *instance) +{ + struct ps3_irq_context *irq_context = &instance->irq_context; + struct PS3ReplyWord **virt_base = + irq_context->reply_fifo_virt_base_addr_buf; + dma_addr_t *phys_base = irq_context->reply_fifo_phys_base_addr_buf; + unsigned int i = 0; + + irq_context->reply_fifo_size = + sizeof(struct PS3ReplyWord) * irq_context->reply_fifo_depth; + + for (; i < irq_context->valid_msix_vector_count; i++) { + virt_base[i] = (struct PS3ReplyWord *)ps3_dma_alloc_coherent( + instance, irq_context->reply_fifo_size, + (unsigned long long *)&phys_base[i]); + if (virt_base[i] == NULL) { + LOG_ERROR("host_no:%u ps3_dma_pool_zalloc fail!\n", + PS3_HOST(instance)); + goto l_failed; + } + memset(virt_base[i], 0xff, irq_context->reply_fifo_size); + } + + return PS3_SUCCESS; + +l_failed: + ps3_reply_fifo_free(instance); + return -PS3_FAILED; +} + +static void ps3_reply_fifo_free(struct ps3_instance *instance) +{ + struct ps3_irq_context *irq_context = &instance->irq_context; + struct PS3ReplyWord **virt_base = + irq_context->reply_fifo_virt_base_addr_buf; + dma_addr_t *phys_base = irq_context->reply_fifo_phys_base_addr_buf; + unsigned int i = 0; + + for (; i < irq_context->valid_msix_vector_count; i++) { + if (virt_base[i] == NULL) + continue; + + ps3_dma_free_coherent(instance, irq_context->reply_fifo_size, + virt_base[i], phys_base[i]); + + virt_base[i] = NULL; + phys_base[i] = 0; + } +} + +static inline int ps3_irq_group_affinity_alloc(struct ps3_instance *instance) +{ + struct ps3_irq_context *irq_context = &instance->irq_context; + unsigned int size = + sizeof(GROUP_AFFINITY) * irq_context->valid_msix_vector_count; + + irq_context->group_affinity = ps3_kzalloc(instance, size); + if (irq_context->group_affinity == NULL) { + LOG_ERROR("host_no:%u, group_affinity alloc failed\n", + PS3_HOST(instance)); + return -PS3_FAILED; + } + return PS3_SUCCESS; +} + +static inline void ps3_irq_group_affinity_free(struct ps3_instance *instance) +{ + if (instance->irq_context.group_affinity != NULL) { + ps3_kfree(instance, instance->irq_context.group_affinity); + instance->irq_context.group_affinity = NULL; + } +} + +static int ps3_irq_resource_alloc(struct ps3_instance *instance) +{ + if (ps3_reply_fifo_desc_alloc(instance) != PS3_SUCCESS) + goto l_failed; + + if (ps3_reply_fifo_alloc(instance) != PS3_SUCCESS) + goto l_failed; + + if (ps3_irq_group_affinity_alloc(instance) != PS3_SUCCESS) + goto l_failed; + + return PS3_SUCCESS; + +l_failed: + ps3_irq_resource_free(instance); + return -PS3_FAILED; +} + +static void ps3_irq_resource_free(struct ps3_instance *instance) +{ + ps3_irq_group_affinity_free(instance); + ps3_reply_fifo_free(instance); + ps3_reply_fifo_desc_free(instance); +} + +static void ps3_reply_fifo_desc_set(struct ps3_instance *instance) +{ + struct ps3_irq_context *irq_context = &instance->irq_context; + struct PS3ReplyFifoDesc *desc_buf = irq_context->reply_fifo_desc_buf; + dma_addr_t *phys_base = irq_context->reply_fifo_phys_base_addr_buf; + unsigned int i = 0; + + for (; i < irq_context->valid_msix_vector_count; i++) { + desc_buf[i].ReplyFifoBaseAddr = cpu_to_le64(phys_base[i]); + desc_buf[i].irqNo = i; + desc_buf[i].depthReplyFifo = + (unsigned short)irq_context->reply_fifo_depth; + + if (i < irq_context->high_iops_msix_vectors) + desc_buf[i].isHighIops = PS3_TRUE; + else + desc_buf[i].isHighIops = PS3_FALSE; + } +} + +int ps3_irq_context_init(struct ps3_instance *instance) +{ + struct ps3_irq_context *irq_context = &instance->irq_context; + unsigned int max_vectors = 0; + + if (!ps3_irq_max_vectors_calc(instance, &max_vectors)) + goto l_failed; + + if (!ps3_ioc_mgr_max_fw_cmd_get(instance, + &irq_context->reply_fifo_depth)) { + goto l_failed; + } + + irq_context->reply_fifo_depth += instance->reply_fifo_depth_addition; + irq_context->instance = instance; + + irq_context->high_iops_msix_vectors = 0; + + LOG_INFO("max_vectors:%d replyQ_depth:%d\n", max_vectors, + irq_context->reply_fifo_depth); + + if (max_vectors <= instance->min_intr_count) { + LOG_ERROR( + "host_no:%u alloc irq failed![cpunum:%d][irq_type:%d][min_intr:%d]\n", + PS3_HOST(instance), num_online_cpus(), + irq_context->pci_irq_type, instance->min_intr_count); + goto l_failed; + } + + irq_context->pci_irq_type = instance->pci_dev_context.pci_irq_type; + irq_context->valid_msix_vector_count = max_vectors; + if (ps3_irq_resource_alloc(instance) != PS3_SUCCESS) + goto l_failed; + + ps3_reply_fifo_desc_set(instance); + + return PS3_SUCCESS; + +l_failed: + ps3_irq_context_exit(instance); + return -PS3_FAILED; +} + +int ps3_irq_context_exit(struct ps3_instance *instance) +{ + ps3_irq_resource_free(instance); + + return PS3_SUCCESS; +} + +unsigned char ps3_irqs_service(void *priv, unsigned long irq_no) +{ + struct ps3_instance *instance = (struct ps3_instance *)priv; + unsigned int isrSN = (unsigned int)irq_no; + + if (isrSN >= instance->irq_context.valid_msix_vector_count) + goto l_out; + + if (!instance->irq_context.is_enable_interrupts) + goto l_out; + + StorPortIssueDpc(instance, &instance->irq_context.irqs[isrSN].dpc, + &instance->irq_context.irqs[isrSN].isrSN, NULL); + +l_out: + return PS3_TRUE; +} + +static void ps3_irq_dpc_routine(PSTOR_DPC dpc, void *context, void *isr_no, + void *arg) +{ + struct ps3_instance *instance = (struct ps3_instance *)context; + unsigned int isrSN = *((unsigned int *)isr_no); + struct ps3_irq *irqs = &instance->irq_context.irqs[isrSN]; + + (void)arg; + (void)dpc; + LOG_DEBUG("%d\n", isrSN); + irqs->is_dpc_running = PS3_TRUE; + + ps3_cmd_complete(irqs); + + irqs->is_dpc_running = PS3_FALSE; +} + +int ps3_irqs_dpc_init(struct ps3_instance *instance) +{ + struct ps3_irq_context *irq_context = &instance->irq_context; + struct ps3_irq *irqs = NULL; + unsigned int i = 0; + + irqs = irq_context->irqs; + if (irq_context->irqs == NULL) { + LOG_ERROR("host_no:%u irqs fail!\n", PS3_HOST(instance)); + goto l_out; + } + + for (i = 0; i < irq_context->valid_msix_vector_count; i++) { + irqs[i].is_dpc_running = PS3_FALSE; + StorPortInitializeDpc(instance, &irqs[i].dpc, + ps3_irq_dpc_routine); + } + instance->ioc_adpter->irq_disable(instance); + return PS3_SUCCESS; + +l_out: + return -PS3_FAILED; +} + +int ps3_irqs_init(struct ps3_instance *instance) +{ + struct ps3_irq_context *irq_context = &instance->irq_context; + struct PS3ReplyWord **virt_base = + irq_context->reply_fifo_virt_base_addr_buf; + struct ps3_irq *irqs = NULL; + unsigned int i = 0; + + irq_context->irqs = (struct ps3_irq *)ps3_kcalloc( + instance, irq_context->valid_msix_vector_count, + sizeof(struct ps3_irq)); + if (irq_context->irqs == NULL) { + LOG_ERROR("host_no:%u kcalloc fail!\n", PS3_HOST(instance)); + goto l_out; + } + irqs = irq_context->irqs; + + for (i = 0; i < irq_context->valid_msix_vector_count; i++) { + memset(&irqs[i], 0, sizeof(irqs[i])); + irqs[i].isrSN = i; + irqs[i].reply_fifo_virt_base_addr = virt_base[i]; + irqs[i].instance = instance; + ps3_atomic_set(&irqs[i].is_busy, 0); + irqs[i].last_reply_idx = 0; + } + + return PS3_SUCCESS; + +l_out: + return -PS3_FAILED; +} + +int ps3_irqs_init_switch(struct ps3_instance *instance) +{ + struct ps3_irq_context *irq_context = &instance->irq_context; + struct PS3ReplyWord **virt_base = + irq_context->reply_fifo_virt_base_addr_buf; + struct ps3_irq *irqs = NULL; + unsigned int i = 0; + + irq_context->irqs = (struct ps3_irq *)ps3_kcalloc( + instance, irq_context->valid_msix_vector_count, + sizeof(struct ps3_irq)); + if (irq_context->irqs == NULL) { + LOG_ERROR("host_no:%u kcalloc fail!\n", PS3_HOST(instance)); + goto l_out; + } + irqs = irq_context->irqs; + + for (i = 0; i < irq_context->valid_msix_vector_count; i++) { + memset(&irqs[i], 0, sizeof(irqs[i])); + irqs[i].isrSN = i; + irqs[i].reply_fifo_virt_base_addr = virt_base[i]; + irqs[i].instance = instance; + ps3_atomic_set(&irqs[i].is_busy, 0); + irqs[i].last_reply_idx = 0; + } + + return PS3_SUCCESS; + +l_out: + return -PS3_FAILED; +} + +static void ps3_irq_dpc_sync(struct ps3_instance *instance, + struct ps3_irq *irqs) +{ + unsigned long status = STOR_STATUS_SUCCESS; + unsigned char cancel_ret = PS3_TRUE; + + if (irqs == NULL) { + LOG_ERROR("host_no:%u irqs fail!\n", PS3_HOST(instance)); + goto l_out; + } + + status = StorPortCancelDpc(instance, &irqs->dpc, &cancel_ret); + if (status != STOR_STATUS_SUCCESS) { + LOG_ERROR("host_no:%u, cancel dpc failed,status:0x%x\n", + PS3_HOST(instance), status); + } + + if (cancel_ret) + goto l_out; + + while (irqs->is_dpc_running) + ps3_msleep(10); +l_out: + return; +} + +void ps3_irqs_exit(struct ps3_instance *instance) +{ + struct ps3_irq_context *irq_context = &instance->irq_context; + struct ps3_irq *irqs = irq_context->irqs; + + if (irqs == NULL) + goto l_out; + instance->ioc_adpter->irq_disable(instance); + + ps3_kfree(instance, irqs); + irq_context->irqs = NULL; + +l_out: + return; +} + +void ps3_irqs_enable(struct ps3_instance *instance) +{ + struct ps3_irq_context *irq_context = &instance->irq_context; + + LOG_DEBUG("host_no:%u irq enable\n", PS3_HOST(instance)); + + switch (irq_context->pci_irq_type) { + case PS3_PCI_IRQ_LEGACY: + ps3_ioc_legacy_irqs_enable(instance); + break; + case PS3_PCI_IRQ_MSI: + ps3_ioc_msi_enable(instance); + break; + case PS3_PCI_IRQ_MSIX: + ps3_ioc_msix_enable(instance); + break; + default: + LOG_ERROR("host_no:%u irq type is failed :%d\n", + PS3_HOST(instance), irq_context->pci_irq_type); + break; + } + irq_context->is_enable_interrupts = PS3_DRV_TRUE; + mb(); /* in order to force CPU ordering */ +} + +void ps3_irqs_disable(struct ps3_instance *instance) +{ + struct ps3_irq_context *irq_context = &instance->irq_context; + struct ps3_irq *irqs = irq_context->irqs; + unsigned int i = 0; + + LOG_DEBUG("host_no:%u irq disable\n", PS3_HOST(instance)); + + if (irq_context->is_enable_interrupts == PS3_DRV_FALSE) + goto l_out; + switch (irq_context->pci_irq_type) { + case PS3_PCI_IRQ_LEGACY: + ps3_ioc_legacy_irqs_disable(instance); + break; + case PS3_PCI_IRQ_MSI: + ps3_ioc_msi_disable(instance); + break; + case PS3_PCI_IRQ_MSIX: + ps3_ioc_msix_disable(instance); + break; + default: + LOG_ERROR("host_no:%u irq type is failed :%d\n", + PS3_HOST(instance), irq_context->pci_irq_type); + break; + } + + if (irqs == NULL) { + LOG_ERROR("host_no:%u irqs fail!\n", PS3_HOST(instance)); + goto l_out; + } + for (i = 0; i < irq_context->valid_msix_vector_count; i++) + ps3_irq_dpc_sync(instance, &irqs[i]); + irq_context->is_enable_interrupts = PS3_DRV_FALSE; + mb(); /* in order to force CPU ordering */ + +l_out: + return; +} + +#endif + +void ps3_all_reply_fifo_init(struct ps3_instance *instance) +{ + unsigned int reply_fifo_size = sizeof(struct PS3ReplyWord) * + instance->irq_context.reply_fifo_depth; + struct ps3_irq *irq = NULL; + unsigned int i = 0; + + LOG_DEBUG("host_no:%u start to reply fifo init!\n", PS3_HOST(instance)); + if (instance->irq_context.irqs == NULL) + return; + + for (; i < instance->irq_context.valid_msix_vector_count; ++i) { + irq = instance->irq_context.irqs + i; + memset(irq->reply_fifo_virt_base_addr, 0xff, reply_fifo_size); + irq->last_reply_idx = 0; + } + LOG_DEBUG("host_no:%u end to reply fifo init!\n", PS3_HOST(instance)); +} diff --git a/drivers/scsi/linkdata/ps3stor/ps3_irq.h b/drivers/scsi/linkdata/ps3stor/ps3_irq.h new file mode 100644 index 0000000000000000000000000000000000000000..73dda1b0ee8a25cdbb0f7363327edebc943b67f4 --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/ps3_irq.h @@ -0,0 +1,198 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _PS3_IRQ_H_ +#define _PS3_IRQ_H_ + +#ifndef _WINDOWS +#include +#include +#include + +#include +#endif + +#include "ps3_htp_def.h" +#include "ps3_err_def.h" +#include "ps3_cmd_channel.h" +#include "ps3_inner_data.h" +#include "ps3_kernel_version.h" + +#define PS3_IRQ_NAME_LENGTH (32) +#define PS3_SWITCH_IRQ_INDEX (0) + +struct ps3_instance; + +enum { + PS3_PCI_IRQ_MODE_NONE_SPE = 0, + PS3_PCI_IRQ_MODE_LEGACY = 1, + PS3_PCI_IRQ_MODE_MSI = 2, + PS3_PCI_IRQ_MODE_MSIX = 3, +}; + +struct ps3_irq { + char name[PS3_IRQ_NAME_LENGTH]; + struct PS3ReplyWord *reply_fifo_virt_base_addr; + struct ps3_instance *instance; + + unsigned int irqNo; + unsigned int isrSN; + unsigned short last_reply_idx; + unsigned char reserved0[6]; +#ifdef _WINDOWS + STOR_DPC dpc; +#else + struct irq_poll irqpoll; + unsigned char is_irq_poll_disabled; + unsigned int irq_poll_sched_threshold; + unsigned char is_sched_irq_poll; + unsigned char is_enable_irq; + unsigned char reserved1[2]; +#endif + atomic_t is_busy; +}; + +struct ps3_irq_recovery { + unsigned int irqNo; + unsigned int isrSN; + unsigned char reserved0[8]; + struct ps3_instance *instance; +}; + +struct ps3_irq_context { + struct ps3_instance *instance; + unsigned int reply_fifo_depth; + unsigned int valid_msix_vector_count; + unsigned int high_iops_msix_vectors; + unsigned int dump_isrSN; + unsigned int reply_fifo_desc_buf_size; +#ifndef _WINDOWS + struct dma_pool *reply_fifo_desc_buf_pool; +#endif + unsigned int reply_fifo_size; + dma_addr_t reply_fifo_desc_buf_phys; + struct PS3ReplyFifoDesc *reply_fifo_desc_buf; +#ifndef _WINDOWS + struct dma_pool *reply_fifo_pool; +#endif + struct PS3ReplyWord + *reply_fifo_virt_base_addr_buf[PS3_MAX_REPLY_QUE_COUNT]; + dma_addr_t reply_fifo_phys_base_addr_buf[PS3_MAX_REPLY_QUE_COUNT]; + + struct ps3_irq *irqs; +#ifndef _WINDOWS + unsigned int *cpu_msix_table; + atomic_t high_iops_io_count; + int cpu_msix_table_sz; +#endif + + unsigned char is_enable_interrupts; + unsigned char is_support_balance; + unsigned char is_balance_current_perf_mode; + unsigned char pci_irq_type; + +#ifdef _WINDOWS + PGROUP_AFFINITY group_affinity; +#endif + struct ps3_irq_recovery *irq_recovery; +}; + +int ps3_irq_context_init(struct ps3_instance *instance); + +int ps3_irq_context_exit(struct ps3_instance *instance); + +int ps3_irqs_dpc_init(struct ps3_instance *instance); + +int ps3_irqs_init(struct ps3_instance *instance); + +int ps3_irqs_init_switch(struct ps3_instance *instance); + +void ps3_irqs_exit(struct ps3_instance *instance); +#ifndef _WINDOWS +unsigned int ps3_msix_index_get(struct ps3_cmd *cmd, unsigned short pd_count); + +void ps3_perf_update(struct ps3_instance *instance, unsigned char iocPerfMode); +#endif +void ps3_irqs_enable(struct ps3_instance *instance); + +void ps3_irqs_disable(struct ps3_instance *instance); +#ifndef _WINDOWS +void ps3_irqpolls_enable(struct ps3_instance *instance); + +void ps3_irqs_sync(struct ps3_instance *instance); +#endif +#ifndef _WINDOWS +irqreturn_t ps3_irqs_service(int irq_so, void *priv); +#else +unsigned char ps3_irqs_service(void *priv, unsigned long irq_no); +#endif + +#ifndef _WINDOWS +int ps3_irqpoll_service(struct irq_poll *irqpoll, int budget); +#endif +static inline unsigned char ps3_irq_busy_add(struct ps3_irq *irq) +{ + return ps3_atomic_add_unless(&irq->is_busy, 1, 1); +} + +static inline void ps3_irq_busy_dec(struct ps3_irq *irq) +{ + ps3_atomic_dec(&irq->is_busy); +} + +static inline unsigned char +ps3_irq_is_enable(const struct ps3_irq_context *irq_ctx) +{ + return (irq_ctx->is_enable_interrupts == PS3_DRV_TRUE); +} + +void ps3_all_reply_fifo_init(struct ps3_instance *instance); + +#ifndef _WINDOWS +static inline void ps3_irq_poll_sched(struct irq_poll *iop) +{ +#ifdef CONFIG_IRQ_POLL + irq_poll_sched(iop); +#else + (void)iop; +#endif +} + +static inline void ps3_irq_poll_init(struct irq_poll *iop, int weight, + irq_poll_fn *func) +{ +#ifdef CONFIG_IRQ_POLL + irq_poll_init(iop, weight, func); +#else + (void)iop; + (void)weight; + (void)func; +#endif +} + +static inline void ps3_irq_poll_complete(struct irq_poll *iop) +{ +#ifdef CONFIG_IRQ_POLL + irq_poll_complete(iop); +#else + (void)iop; +#endif +} + +static inline void ps3_irq_poll_enable(struct irq_poll *iop) +{ +#ifdef CONFIG_IRQ_POLL + irq_poll_enable(iop); +#else + (void)iop; +#endif +} + +static inline void ps3_irq_poll_disable(struct irq_poll *iop) +{ +#ifdef CONFIG_IRQ_POLL + irq_poll_disable(iop); +#else + (void)iop; +#endif +} +#endif +#endif diff --git a/drivers/scsi/linkdata/ps3stor/ps3_kernel_version.h b/drivers/scsi/linkdata/ps3stor/ps3_kernel_version.h new file mode 100644 index 0000000000000000000000000000000000000000..0b4f1dc95a122dc14b1dc3a5e5dc9cbe7b8cd646 --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/ps3_kernel_version.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _PS3_KERNEL_VERSION_H_ +#define _PS3_KERNEL_VERSION_H_ + +#define PS3_STRSCPY +#define PS3_KMAP_LOCAL +#define PS3_KUNMAP_LOCAL +#define DRIVER_SUPPORT_PRIV_BUSY +#define PS3_HOST_ATTRS +#define PS3_TRACK_QUEUE_DEPTH +#define PS3_RESET_TIMER +#define PS3_FALLTHROUGH +#define PS3_SCMD_IO_DONE +#define PS3_SCMD_GET_REQUEST +#define PS3_SYPPORT_BIO_ITER +#define PS3_AER_CLEAR_STATUS +#define PS3_LINUX_SIGNAL +#define PS3_SUPPORT_TAGSET +#define DRIVER_SUPPORT_KERNEL_IRQ_AFFINITY +#define PS3_OS_MANAGED_IRQ_SUPPORT +#define PS3_MAP_QUEUES +#include +#ifndef PS3_TAGSET_SUPPORT +#define PS3_TAGSET_SUPPORT +#endif +#define PS3_VFS_WRITE +#define PS3_FORCE_UACCESS +#define PS3_FSNOTIFY_FILE +#define PS3_KERNEL_WRITE_FILE +#define PS3_BLK_QUEUE_FLAG_CLEAR + +#endif diff --git a/drivers/scsi/linkdata/ps3stor/ps3_load.c b/drivers/scsi/linkdata/ps3stor/ps3_load.c new file mode 100644 index 0000000000000000000000000000000000000000..43d1d771395d66a338dfe1b7729d4e48c694a34f --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/ps3_load.c @@ -0,0 +1,259 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "ps3_load.h" +#include "ps3_instance_manager.h" +#include "ps3_ioc_manager.h" +#include "ps3_mgr_cmd_err.h" +#include "ps3_mgr_cmd.h" +#include "ps3_cmd_channel.h" +#include "ps3_event.h" +#include "ps3_cmd_statistics.h" +#include "ps3_ioctl.h" + +#ifdef _WINDOWS + +static int ps3_init_ioc_prepare(struct ps3_instance *instance); +static void ps3_init_ioc_prepare_exit(struct ps3_instance *instance); +static int ps3_init_ioc_complete(struct ps3_instance *instance); +static void ps3_init_ioc_complete_exit(struct ps3_instance *instance); +static int ps3_pci_init_complete(struct ps3_instance *instance); +static void ps3_pci_init_complete_exit(struct ps3_instance *instance); + +int ps3_firmware_init(struct ps3_instance *instance) +{ + ps3_ioc_adp_init(instance); + + if (instance->ioc_adpter->ioc_init_state_to_ready(instance) != + PS3_SUCCESS) { + goto l_failed; + } + ps3_atomic_set(&instance->state_machine.state, + PS3_INSTANCE_STATE_READY); + + if (ps3_pci_init_complete(instance) != PS3_SUCCESS) + goto l_failed; + + if (ps3_init_ioc_prepare(instance) != PS3_SUCCESS) + goto l_failed; + + if (instance->ioc_adpter->ioc_init_proc(instance) != PS3_SUCCESS) + goto l_failed; + ps3_atomic_set(&instance->state_machine.state, + PS3_INSTANCE_STATE_PRE_OPERATIONAL); + + if (ps3_ctrl_info_get(instance) != PS3_SUCCESS) + goto l_failed; + + if (ps3_init_ioc_complete(instance) != PS3_SUCCESS) + goto l_failed; + + ps3_ioctl_init(instance, PS3_MAX_IOCTL_CMDS); + + return PS3_SUCCESS; + +l_failed: + DbgPrint("fireware init failed\n"); + ps3_firmware_exit(instance); + return -PS3_FAILED; +} + +void ps3_firmware_exit(struct ps3_instance *instance) +{ + ps3_pci_init_complete_exit(instance); + ps3_init_ioc_complete_exit(instance); + ps3_init_ioc_prepare_exit(instance); +} + +void ps3_remove(struct ps3_instance *instance) +{ + unsigned long flags = 0; + + LOG_INFO("hno:%u %s\n", PS3_HOST(instance), __func__); + instance->state_machine.is_load = PS3_FALSE; + instance->ioc_adpter->irq_disable(instance); + + ps3_watchdog_stop(instance); + ps3_recovery_context_exit(instance); + + ps3_event_unsubscribe(instance); + ps3_spin_lock_irqsave(&instance->recov ery_context->recovery_lock, + &flags); + ps3_atomic_set(&instance->event_context.abort_eventcmd, 0); + ps3_atomic_set(&instance->dev_context.abort_vdpending_cmd, 0); + ps3_spin_unlock_irqrestore(&instance->recovery_context->recovery_lock, + flags); + ps3_dev_mgr_vd_info_unsubscribe(instance); + +#ifndef _WINDOWS + ps3_sas_device_data_exit(instance); +#endif + ps3_device_mgr_data_exit(instance); +#ifndef _WINDOWS + if (ps3_sas_is_support_smp(instance)) + sas_remove_host(instance->host); + else + scsi_remove_host(instance->host); +#else + ps3_device_unload_done(instance); +#endif + if (ps3_soc_unload(instance, PS3_FALSE, PS3_UNLOAD_SUB_TYPE_REMOVE, + PS3_SUSPEND_TYPE_NONE) != PS3_SUCCESS) { + LOG_ERROR("hno:%u unload failed.\n", PS3_HOST(instance)); + if (ps3_ioc_hard_reset_to_ready(instance) != PS3_SUCCESS) { + LOG_ERROR("hno:%u hard reset failed.\n", + PS3_HOST(instance)); + } + } + +#ifndef _WINDOWS + ps3_irqs_sync(instance); + ps3_irqpolls_enable(instance); +#endif + ps3_instance_state_transfer_to_quit(instance); + + ps3_firmware_exit(instance); + ps3_pci_exit(instance); +#ifndef _WINDOWS + pci_set_drvdata(instance->pdev, NULL); + ps3_instance_put(instance); + scsi_host_put(instance->host); + + ps3_dma_dump_mapping(pdev); +#endif +} + +static void ps3_cmd_attr_context_init(struct ps3_instance *instance) +{ + unsigned int cmd_qdepth = 0; + + instance->cmd_attr.cur_can_que = + instance->cmd_context.max_scsi_cmd_count - + instance->cmd_context.max_r1x_cmd_count; + +#ifndef _WINDOWS + + cmd_qdepth = ps3_throttle_qdepth_query(); + if ((cmd_qdepth != 0) && cmd_qdepth <= instance->cmd_attr.cur_can_que) { + instance->cmd_attr.throttle_que_depth = cmd_qdepth; + } else { + instance->cmd_attr.throttle_que_depth = + PS3_DEVICE_QDEPTH_DEFAULT_VALUE; + } +#else + instance->cmd_attr.throttle_que_depth = PS3_DEVICE_QDEPTH_DEFAULT_VALUE; +#endif + + instance->cmd_attr.vd_io_threshold = 0; + instance->cmd_attr.is_support_direct_cmd = PS3_FALSE; +} + +static int ps3_init_ioc_prepare(struct ps3_instance *instance) +{ + ps3_ioc_mgr_req_queue_lock_init(instance); + ps3_err_fault_context_init(instance); + + if (!ps3_ioc_fw_version_get(instance)) + goto l_fail; + + if (!ps3_ioc_state_halt_support_get(instance)) + goto l_fail; + + if (!ps3_ioc_recovery_support_get(instance)) + goto l_fail; + + if (ps3_recovery_context_init(instance) != PS3_SUCCESS) + goto l_fail; + + if (ps3_ioc_init_cmd_context_init(instance) != PS3_SUCCESS) + goto l_fail; + + if (ps3_ctrl_info_buf_alloc(instance) != PS3_SUCCESS) + goto l_fail; + + if (ps3_cmd_context_init(instance) != PS3_SUCCESS) + goto l_fail; + + ps3_cmd_attr_context_init(instance); + + if (ps3_event_context_init(instance) != PS3_SUCCESS) + goto l_fail; + + if (ps3_cmd_statistics_init(instance) != PS3_SUCCESS) + goto l_fail; + + if (ps3_dump_init(instance) != PS3_SUCCESS) + goto l_fail; + + return PS3_SUCCESS; +l_fail: + ps3_init_ioc_prepare_exit(instance); + return -PS3_FAILED; +} + +static void ps3_init_ioc_prepare_exit(struct ps3_instance *instance) +{ + ps3_ioc_init_cmd_context_exit(instance); + ps3_err_fault_context_exit(instance); + ps3_ctrl_info_buf_free(instance); + ps3_cmd_statistics_exit(instance); + ps3_event_context_exit(instance); + ps3_recovery_context_exit(instance); + ps3_cmd_context_exit(instance); + ps3_dump_dma_buf_free(instance); + ps3_dump_exit(instance); +#ifndef _WINDOWS + (void)ps3_debug_mem_free(instance); +#endif +} + +static int ps3_init_ioc_complete(struct ps3_instance *instance) +{ + if (ps3_mgr_cmd_init(instance) != PS3_SUCCESS) + goto l_failed; + + if (ps3_device_mgr_init(instance) != PS3_SUCCESS) + goto l_failed; +#ifndef _WINDOWS + if (ps3_sas_device_mgr_init(instance) != PS3_SUCCESS) + goto l_failed; +#endif + return PS3_SUCCESS; +l_failed: + ps3_init_ioc_complete_exit(instance); + return -PS3_FAILED; +} + +static void ps3_init_ioc_complete_exit(struct ps3_instance *instance) +{ +#ifndef _WINDOWS + ps3_sas_device_mgr_exit(instance); +#endif + ps3_device_mgr_exit(instance); + ps3_mgr_cmd_exit(instance); +} + +static int ps3_pci_init_complete(struct ps3_instance *instance) +{ + if (ps3_irq_context_init(instance) != PS3_SUCCESS) + goto l_failed; + if (instance->ioc_adpter->irq_init) { + if (instance->ioc_adpter->irq_init(instance) != PS3_SUCCESS) + goto l_failed; + } else { + if (ps3_irqs_init(instance) != PS3_SUCCESS) + goto l_failed; + } + + return PS3_SUCCESS; + +l_failed: + ps3_pci_init_complete_exit(instance); + return -PS3_FAILED; +} + +static void ps3_pci_init_complete_exit(struct ps3_instance *instance) +{ + ps3_irqs_exit(instance); + ps3_irq_context_exit(instance); +} + +#endif diff --git a/drivers/scsi/linkdata/ps3stor/ps3_load.h b/drivers/scsi/linkdata/ps3stor/ps3_load.h new file mode 100644 index 0000000000000000000000000000000000000000..f885a6e4028012d173402b1f4f1600490d2c58e1 --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/ps3_load.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _PS3_LOAD_H_ +#define _PS3_LOAD_H_ + +#ifdef _WINDOWS + +#include "ps3_def.h" + +struct ps3_instance; + +int ps3_firmware_init(struct ps3_instance *instance); + +void ps3_firmware_exit(struct ps3_instance *instance); + +void ps3_remove(struct ps3_instance *instance); + +#endif +#endif diff --git a/drivers/scsi/linkdata/ps3stor/ps3_mgr_channel.c b/drivers/scsi/linkdata/ps3stor/ps3_mgr_channel.c new file mode 100644 index 0000000000000000000000000000000000000000..22541123937db949e907fa40c9569600b09f58a0 --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/ps3_mgr_channel.c @@ -0,0 +1,439 @@ +// SPDX-License-Identifier: GPL-2.0 +#ifdef _WINDOWS +#include "ps3_def.h" + +#else + +#define WINDOWS_DELAY + +#include +#include +#include +#include +#include +#include + +#include "ps3_scsih.h" +#include "ps3_scsih_cmd_parse.h" +#endif + +#include "ps3_htp.h" +#include "ps3_mgr_channel.h" +#include "ps3_cmd_complete.h" +#include "ps3_platform_utils.h" +#include "ps3_util.h" +#include "ps3_mgr_cmd_err.h" +#include "ps3_mgr_cmd.h" +#include "ps3_cmd_complete.h" +#include "ps3_ioc_manager.h" +#include "ps3_driver_log.h" +#include "ps3_instance_manager.h" +#include "ps3_cmd_statistics.h" +#include "ps3_kernel_version.h" + +#ifndef _WINDOWS +#if defined(PS3_LINUX_SIGNAL) +#include +#else +#include +#endif +#endif + +#define CMD_MAX_RETRY_COUNT (10) + +#define SEND_IOCTL_CMD_CHECK(instance, cmd) \ + ((PS3_MGR_CMD_TYPE(cmd) == PS3_CMD_IOCTL) && (instance)->is_support_irq) + +static inline unsigned char is_interrupt_signal(struct task_struct *p) +{ + if (sigismember(&p->pending.signal, SIGINT) || + sigismember(&p->pending.signal, SIGKILL) || + sigismember(&p->pending.signal, SIGQUIT)) { + return PS3_TRUE; + } + return PS3_FALSE; +} + +void ps3_wait_cmd_for_completion_interrupt(struct ps3_instance *instance, + struct ps3_cmd *cmd) +{ + int ret = PS3_SUCCESS; + unsigned long flags = 0; + unsigned char is_send_cancel = PS3_FALSE; + int cur_state = PS3_INSTANCE_STATE_INIT; + int retry_count = 0; + + while (1) { + ret = wait_for_completion_interruptible(&cmd->sync_done); + if (ret == PS3_SUCCESS) { + LOG_DEBUG( + "host_no:%u wait_for_completion_interrupted success\n", + PS3_HOST(instance)); + if ((cmd->resp_frame->normalRespFrame.respStatus == + PS3_DRV_MGR_BUSY) && + is_send_cancel) { + cmd->resp_frame->normalRespFrame.respStatus = + PS3_MGR_REC_FORCE; + } + break; + } + + ps3_spin_lock_irqsave(&cmd->cmd_state.lock, &flags); + + ret = signal_pending(current); + if (!ret) { + ps3_spin_unlock_irqrestore(&cmd->cmd_state.lock, flags); + ps3_msleep(100); + continue; + } + ret = is_interrupt_signal(current); + if (ret) { + cur_state = + ps3_atomic_read(&instance->state_machine.state); + if (cur_state == PS3_INSTANCE_STATE_QUIT || + cur_state == PS3_INSTANCE_STATE_DEAD) { + LOG_INFO_IN_IRQ( + instance, "host_no:%u cur_state:%s\n", + PS3_HOST(instance), + namePS3InstanceState(cur_state)); + ps3_spin_unlock_irqrestore(&cmd->cmd_state.lock, + flags); + break; + } + + ps3_spin_unlock_irqrestore(&cmd->cmd_state.lock, flags); + + LOG_INFO_LIM( + "host_no:%u wait_for_completion_interrupted by SIG INT OR KILL, QUIT\n", + PS3_HOST(instance)); + if (is_send_cancel) { + ps3_msleep(100); + continue; + } + is_send_cancel = PS3_TRUE; +cancel: + ret = ps3_mgr_cmd_cancel(instance, cmd->index); + if (ret != PS3_SUCCESS) { + if (ret == -PS3_RECOVERED) { + LOG_INFO_LIM( + "host_no:%u cancel cmd %u send failed, wait\n", + PS3_HOST(instance), cmd->index); + ps3_msleep(100); + continue; + } else if (ret == -PS3_EBUSY) { + ps3_msleep(100); + if (retry_count++ < + CMD_MAX_RETRY_COUNT) { + goto cancel; + } + continue; + } + LOG_INFO( + "host_no:%u cancel cmd %u failed, QUIT, ret:%d\n", + PS3_HOST(instance), cmd->index, ret); + } else { + ps3_spin_lock_irqsave(&cmd->cmd_state.lock, + &flags); + cmd->cmd_state.state = PS3_CMD_STATE_COMPLETE; + ps3_spin_unlock_irqrestore(&cmd->cmd_state.lock, + flags); + } + break; + } + ps3_spin_unlock_irqrestore(&cmd->cmd_state.lock, flags); + ps3_msleep(100); + } +} + +static int ps3_mgr_cmd_send(struct ps3_instance *instance, struct ps3_cmd *cmd) +{ + int ret = PS3_SUCCESS; + int cur_state = PS3_INSTANCE_STATE_INIT; + + if (!instance->state_machine.is_load) { + if (PS3_MGR_CMD_TYPE(cmd) != PS3_CMD_MANAGEMENT) { + LOG_WARN( + "host_no:%u instance state is unloading or suspend\n", + PS3_HOST(instance)); + ret = -PS3_FAILED; + goto l_out; + } + } + if (!ps3_is_instance_state_allow_cmd_execute(instance)) { + LOG_WARN("host_no:%u cannot send block cmd\n", + PS3_HOST(instance)); + ret = -PS3_RECOVERED; + if (PS3_MGR_CMD_TYPE(cmd) == PS3_CMD_IOCTL) { + cur_state = + ps3_atomic_read(&instance->state_machine.state); + if (cur_state == PS3_INSTANCE_STATE_QUIT || + cur_state == PS3_INSTANCE_STATE_DEAD) { + LOG_WARN("host_no:%u cur_state:%s\n", + PS3_HOST(instance), + namePS3InstanceState(cur_state)); + goto l_out; + } + cmd->resp_frame->normalRespFrame.respStatus = + PS3_DRV_MGR_BUSY; + LOG_WARN( + "host_no:%u ioctl cannot send block cmd:%u,resp:%d, will retry\n", + PS3_HOST(instance), cmd->index, + cmd->resp_frame->normalRespFrame.respStatus); + ret = -PS3_RESP_ERR; + } + goto l_out; + } + + if (ps3_pci_err_recovery_get(instance)) { + LOG_WARN( + "host_no:%u cannot send block cmd due to pci recovery\n", + PS3_HOST(instance)); + ret = -PS3_FAILED; + goto l_out; + } + + PS3_MGR_CMD_STAT_INC(instance, cmd); + + LOG_FILE_INFO("host_no:%u CFID:%d trace_id:0x%llx ready send\n", + PS3_HOST(instance), cmd->cmd_word.cmdFrameID, + cmd->trace_id); + LOG_DEBUG("host_no:%u req cmd info:\n" + "\t reqFrameBufBase = 0x%llx, function = %d\n", + PS3_HOST(instance), + cpu_to_le64(instance->cmd_context.req_frame_buf_phys), + ps3_get_pci_function(instance->pdev)); + + instance->ioc_adpter->cmd_send(instance, &cmd->cmd_word); +l_out: + return ret; +} +static int ps3_blocked_unload_cmd_send(struct ps3_instance *instance, + struct ps3_cmd *cmd) +{ + int ret = PS3_SUCCESS; + + PS3_MGR_CMD_STAT_INC(instance, cmd); + + instance->ioc_adpter->cmd_send(instance, &cmd->cmd_word); + ret = ps3_block_cmd_wait(instance, cmd, 0); + LOG_INFO("host_no:%u CFID:%d trace_id:0x%llx ret:%d\n", + PS3_HOST(instance), cmd->cmd_word.cmdFrameID, cmd->trace_id, + ret); + return ret; +} + +static int ps3_blocked_cmd_wake(struct ps3_cmd *cmd) +{ + unsigned long flags = 0; + + ps3_spin_lock_irqsave(&cmd->cmd_state.lock, &flags); + if (cmd->cmd_state.state == PS3_CMD_STATE_DEAD) { + LOG_WARN_IN_IRQ( + cmd->instance, + "host_no:%u CFID:%d trace_id:0x%llx dead free\n", + PS3_HOST(cmd->instance), cmd->index, cmd->trace_id); + + PS3_MGR_CMD_BACK_INC(cmd->instance, cmd, 0); + ps3_mgr_cmd_free_nolock(cmd->instance, cmd); + goto l_out; + } + + if (cmd->cmd_state.state == PS3_CMD_STATE_PROCESS) { + cmd->cmd_state.state = PS3_CMD_STATE_COMPLETE; + + complete(&cmd->sync_done); + + PS3_MGR_CMD_BACK_INC(cmd->instance, cmd, 0); + } else { + LOG_ERROR_IN_IRQ( + cmd->instance, + "host_no:%u CFID:%d trace_id:0x%llx repeat reply\n", + PS3_HOST(cmd->instance), cmd->index, cmd->trace_id); + } + +l_out: + ps3_spin_unlock_irqrestore(&cmd->cmd_state.lock, flags); + return PS3_SUCCESS; +} + +static int ps3_polled_cmd_send(struct ps3_instance *instance, + struct ps3_cmd *cmd) +{ + int ret = PS3_SUCCESS; + + if (!instance->state_machine.is_load) { + if (PS3_MGR_CMD_TYPE(cmd) != PS3_CMD_MANAGEMENT) { + LOG_WARN( + "host_no:%u instance state is unloading or suspend\n", + PS3_HOST(instance)); + ret = -PS3_FAILED; + goto l_out; + } + } + if (!ps3_is_instance_state_allow_cmd_execute(instance)) { + ret = -PS3_RECOVERED; + goto l_out; + } + + if (ps3_pci_err_recovery_get(instance)) { + LOG_WARN( + "host_no:%u cannot send block cmd due to pci recovery\n", + PS3_HOST(instance)); + ret = -PS3_FAILED; + goto l_out; + } + + PS3_MGR_CMD_STAT_INC(instance, cmd); + instance->ioc_adpter->cmd_send(instance, &cmd->cmd_word); +l_out: + return ret; +} +static int ps3_polled_unload_cmd_send(struct ps3_instance *instance, + struct ps3_cmd *cmd) +{ + int ret = -PS3_FAILED; + + PS3_MGR_CMD_STAT_INC(instance, cmd); + instance->ioc_adpter->cmd_send(instance, &cmd->cmd_word); + + ret = ps3_cmd_reply_polling(instance, cmd, 0, PS3_TRUE); + if (ret != -PS3_TIMEOUT) { + PS3_MGR_CMD_BACK_INC(instance, cmd, + (ret == PS3_SUCCESS) ? + PS3_REPLY_WORD_FLAG_SUCCESS : + PS3_REPLY_WORD_FLAG_FAIL); + } + return ret; +} + +static int ps3_blocked_cmd_cb(struct ps3_cmd *cmd, unsigned short reply_flags) +{ + if ((cmd->req_frame->mgrReq.reqHead.noReplyWord == + PS3_CMD_WORD_NEED_REPLY_WORD) && + (reply_flags == PS3_REPLY_WORD_FLAG_SUCCESS)) { + cmd->resp_frame->normalRespFrame.respStatus = SCSI_STATUS_GOOD; + } + + return ps3_blocked_cmd_wake(cmd); +} + +int ps3_cmd_send_sync(struct ps3_instance *instance, struct ps3_cmd *cmd) +{ + if ((cmd->is_force_polling == 0 && + ps3_irq_is_enable(&cmd->instance->irq_context)) || + SEND_IOCTL_CMD_CHECK(instance, cmd)) { + cmd->cmd_receive_cb = ps3_blocked_cmd_cb; + cmd->req_frame->mgrReq.reqHead.noReplyWord = + PS3_CMD_WORD_NEED_REPLY_WORD; + return ps3_mgr_cmd_send(instance, cmd); + } + cmd->cmd_receive_cb = NULL; + cmd->req_frame->mgrReq.reqHead.noReplyWord = + PS3_CMD_WORD_NO_REPLY_WORD; + return ps3_polled_cmd_send(instance, cmd); +} + +int ps3_cmd_wait_sync(struct ps3_instance *instance, struct ps3_cmd *cmd) +{ + int ret = PS3_SUCCESS; + + if (cmd->req_frame->mgrReq.reqHead.noReplyWord == + PS3_CMD_WORD_NEED_REPLY_WORD) { + ret = ps3_block_cmd_wait(instance, cmd, 0); + } else { + ret = ps3_cmd_reply_polling(instance, cmd, 0, PS3_FALSE); + if (ret != -PS3_TIMEOUT) { + PS3_MGR_CMD_BACK_INC( + instance, cmd, + (ret == PS3_SUCCESS) ? + PS3_REPLY_WORD_FLAG_SUCCESS : + PS3_REPLY_WORD_FLAG_FAIL); + } + } + LOG_FILE_INFO("host_no:%u CFID:%d trace_id:0x%llx recv ret:%d\n", + PS3_HOST(instance), cmd->cmd_word.cmdFrameID, + cmd->trace_id, ret); + return ret; +} + +int ps3_unload_cmd_send_sync(struct ps3_instance *instance, struct ps3_cmd *cmd) +{ + if (cmd->is_force_polling == 0 && + ps3_irq_is_enable(&cmd->instance->irq_context)) { + cmd->cmd_receive_cb = ps3_blocked_cmd_cb; + cmd->req_frame->mgrReq.reqHead.noReplyWord = + PS3_CMD_WORD_NEED_REPLY_WORD; + return ps3_blocked_unload_cmd_send(instance, cmd); + } + cmd->cmd_receive_cb = NULL; + cmd->req_frame->mgrReq.reqHead.noReplyWord = PS3_CMD_WORD_NO_REPLY_WORD; + return ps3_polled_unload_cmd_send(instance, cmd); +} + +int ps3_cmd_no_block_send(struct ps3_instance *instance, struct ps3_cmd *cmd) +{ + int ret = PS3_SUCCESS; + + cmd->cmd_receive_cb = ps3_blocked_cmd_cb; + cmd->req_frame->mgrReq.reqHead.noReplyWord = + PS3_CMD_WORD_NEED_REPLY_WORD; + + if (!ps3_is_instance_state_allow_cmd_execute(instance)) { + ret = -PS3_FAILED; + goto l_out; + } + + PS3_MGR_CMD_STAT_INC(instance, cmd); + + instance->ioc_adpter->cmd_send(instance, &cmd->cmd_word); +l_out: + return ret; +} + +int ps3_block_cmd_wait(struct ps3_instance *instance, struct ps3_cmd *cmd, + unsigned long timeout) +{ + int ret = PS3_SUCCESS; + + if (cmd->is_interrupt) { +#ifdef WINDOWS_DELAY + ps3_wait_cmd_for_completion_interrupt(instance, cmd); +#endif + } else { + ret = ps3_wait_cmd_for_completion_timeout(instance, cmd, + timeout); + if (ret == -PS3_TIMEOUT) { + LOG_ERROR( + "host_no:%u CFID:%d trace_id:0x%llx time out\n", + PS3_HOST(instance), cmd->cmd_word.cmdFrameID, + cmd->trace_id); + goto l_out; + } + } + + if (cmd->cmd_state.state != PS3_CMD_STATE_COMPLETE) { + LOG_INFO( + "host_no:%u CFID:%d trace_id:0x%llx not complete,state:%u\n", + PS3_HOST(instance), cmd->cmd_word.cmdFrameID, + cmd->trace_id, cmd->cmd_state.state); + ret = -PS3_TIMEOUT; + goto l_out; + } + + if (ps3_cmd_resp_status(cmd) != SCSI_STATUS_GOOD) { + ret = -PS3_RESP_ERR; + LOG_INFO("host_no:%u CFID:%d trace_id:0x%llx resp err:0x%x\n", + PS3_HOST(instance), cmd->cmd_word.cmdFrameID, + cmd->trace_id, ps3_cmd_resp_status(cmd)); + } +l_out: + return ret; +} + +int ps3_cmd_send_async(struct ps3_instance *instance, struct ps3_cmd *cmd, + int (*cmd_receive_cb)(struct ps3_cmd *, unsigned short)) +{ + cmd->cmd_receive_cb = cmd_receive_cb; + PS3_MGR_CMD_STAT_INC(instance, cmd); + return ps3_async_cmd_send(instance, cmd); +} diff --git a/drivers/scsi/linkdata/ps3stor/ps3_mgr_channel.h b/drivers/scsi/linkdata/ps3stor/ps3_mgr_channel.h new file mode 100644 index 0000000000000000000000000000000000000000..32e20a71af0550e13f4c2c67af06f59d1e026ac2 --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/ps3_mgr_channel.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _PS3_MGR_CHANNEL_H_ +#define _PS3_MGR_CHANNEL_H_ + +#include "ps3_cmd_channel.h" + +int ps3_cmd_send_sync(struct ps3_instance *instance, struct ps3_cmd *cmd); +int ps3_unload_cmd_send_sync(struct ps3_instance *instance, + struct ps3_cmd *cmd); +int ps3_block_cmd_wait(struct ps3_instance *instance, struct ps3_cmd *cmd, + unsigned long timeout); +int ps3_cmd_send_async(struct ps3_instance *instance, struct ps3_cmd *cmd, + int (*cmd_receive_cb)(struct ps3_cmd *, unsigned short)); + +int ps3_cmd_wait_sync(struct ps3_instance *instance, struct ps3_cmd *cmd); + +int ps3_cmd_no_block_send(struct ps3_instance *instance, struct ps3_cmd *cmd); + +#ifndef _WINDOWS +void ps3_wait_cmd_for_completion_interrupt(struct ps3_instance *instance, + struct ps3_cmd *cmd); +#endif + +static inline union PS3RespFrame *ps3_cmd_resp_frame_get(struct ps3_cmd *cmd) +{ + return cmd->resp_frame; +} +static inline unsigned int ps3_cmd_resp_status(struct ps3_cmd *cmd) +{ + return le32_to_cpu(cmd->resp_frame->normalRespFrame.respStatus); +}; + +#endif diff --git a/drivers/scsi/linkdata/ps3stor/ps3_mgr_cmd.c b/drivers/scsi/linkdata/ps3stor/ps3_mgr_cmd.c new file mode 100644 index 0000000000000000000000000000000000000000..ff6fe9d64102108e2f588c3fc244d4ce414f07d3 --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/ps3_mgr_cmd.c @@ -0,0 +1,2019 @@ +// SPDX-License-Identifier: GPL-2.0 +#ifndef _WINDOWS +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#endif + +#include "ps3_mgr_cmd.h" +#include "ps3_event.h" +#include "ps3_device_update.h" +#include "ps3_device_manager.h" +#include "ps3_cmd_complete.h" +#include "ps3_mgr_channel.h" +#include "ps3_mgr_cmd_err.h" +#include "ps3_scsih.h" +#include "ps3_util.h" +#include "ps3_ioc_manager.h" +#include "ps3_ioc_state.h" +#include "ps3_ioctl.h" + +static int ps3_mgr_cmd_sync_proc(struct ps3_instance *instance, + struct ps3_cmd *cmd, unsigned short time_out); + +int ps3_ctrl_info_buf_alloc(struct ps3_instance *instance) +{ + instance->ctrl_info_buf = + (struct PS3IocCtrlInfo *)ps3_dma_alloc_coherent( + instance, sizeof(struct PS3IocCtrlInfo), + (unsigned long long *)&instance->ctrl_info_buf_h); + + if (instance->ctrl_info_buf == NULL) { + LOG_ERROR("host_no:%u alloc ctrl info buffer failed !\n", + PS3_HOST(instance)); + goto l_fail; + } + return PS3_SUCCESS; +l_fail: + return -PS3_ENOMEM; +} + +void ps3_ctrl_info_buf_free(struct ps3_instance *instance) +{ + if (instance->ctrl_info_buf != NULL) { + LOG_INFO("ctrl_info_buf = %p\n", instance->ctrl_info_buf); + ps3_dma_free_coherent(instance, sizeof(struct PS3IocCtrlInfo), + instance->ctrl_info_buf, + instance->ctrl_info_buf_h); + instance->ctrl_info_buf = NULL; + } + + memset(&instance->ctrl_info, 0, sizeof(struct PS3IocCtrlInfo)); +} + +static int ps3_mgr_pd_list_init(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + struct ps3_dev_context *dev_context = &instance->dev_context; + + dev_context->pd_list_buf = (struct PS3DevList *)ps3_dma_alloc_coherent( + instance, + PS3_MAX_PD_COUNT(instance) * sizeof(struct PS3PhyDevice) + + sizeof(struct PS3DevList), + (unsigned long long *)&dev_context->pd_list_buf_phys); + if (dev_context->pd_list_buf == NULL) { + LOG_ERROR("host_no:%u alloc pd list buffer failed !\n", + PS3_HOST(instance)); + ret = -PS3_ENOMEM; + } + + return ret; +} + +static int ps3_mgr_vd_list_init(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + struct ps3_dev_context *dev_context = &instance->dev_context; + + dev_context->vd_list_buf = (struct PS3DevList *)ps3_dma_alloc_coherent( + instance, + PS3_MAX_VD_COUNT(instance) * sizeof(struct PS3VirtDevice) + + sizeof(struct PS3DevList), + (unsigned long long *)&dev_context->vd_list_buf_phys); + if (dev_context->vd_list_buf == NULL) { + LOG_ERROR("host_no:%u alloc vd list buffer failed !\n", + PS3_HOST(instance)); + ret = -PS3_ENOMEM; + } + return ret; +} + +static int ps3_mgr_pd_info_init(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + struct ps3_dev_context *dev_context = &instance->dev_context; + + dev_context->pd_info_buf = (struct PS3PDInfo *)ps3_dma_alloc_coherent( + instance, sizeof(struct PS3PDInfo), + (unsigned long long *)&dev_context->pd_info_buf_phys); + if (dev_context->pd_info_buf == NULL) { + LOG_ERROR("host_no:%u alloc pd info buffer failed !\n", + PS3_HOST(instance)); + ret = -PS3_ENOMEM; + } + return ret; +} + +static inline void ps3_mgr_pd_list_exit(struct ps3_instance *instance) +{ + struct ps3_dev_context *dev_context = &instance->dev_context; + + if (dev_context->pd_list_buf != NULL) { + ps3_dma_free_coherent( + instance, + PS3_MAX_PD_COUNT(instance) * + sizeof(struct PS3PhyDevice) + + sizeof(struct PS3DevList), + dev_context->pd_list_buf, + dev_context->pd_list_buf_phys); + dev_context->pd_list_buf = NULL; + } +} + +static inline void ps3_mgr_vd_list_exit(struct ps3_instance *instance) +{ + struct ps3_dev_context *dev_context = &instance->dev_context; + + if (dev_context->vd_list_buf != NULL) { + ps3_dma_free_coherent( + instance, + PS3_MAX_VD_COUNT(instance) * + sizeof(struct PS3VirtDevice) + + sizeof(struct PS3DevList), + dev_context->vd_list_buf, + dev_context->vd_list_buf_phys); + dev_context->vd_list_buf = NULL; + } +} + +static inline void ps3_mgr_pd_info_exit(struct ps3_instance *instance) +{ + struct ps3_dev_context *dev_context = &instance->dev_context; + + if (dev_context->pd_info_buf != NULL) { + ps3_dma_free_coherent(instance, sizeof(struct PS3PDInfo), + dev_context->pd_info_buf, + dev_context->pd_info_buf_phys); + dev_context->pd_info_buf = NULL; + } +} + +static inline void ps3_mgr_vd_info_exit(struct ps3_instance *instance) +{ + struct ps3_dev_context *dev_context = &instance->dev_context; + + if (dev_context->vd_info_buf_sync != NULL) { + ps3_dma_free_coherent( + instance, + PS3_MAX_VD_COUNT(instance) * sizeof(struct PS3VDEntry) + + sizeof(struct PS3VDInfo), + dev_context->vd_info_buf_sync, + dev_context->vd_info_buf_phys_sync); + dev_context->vd_info_buf_sync = NULL; + } + + if (dev_context->vd_info_buf_async != NULL) { + ps3_dma_free_coherent( + instance, + PS3_MAX_VD_COUNT(instance) * sizeof(struct PS3VDEntry) + + sizeof(struct PS3VDInfo), + dev_context->vd_info_buf_async, + dev_context->vd_info_buf_phys_async); + dev_context->vd_info_buf_async = NULL; + } +} + +static int ps3_mgr_vd_info_init(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + struct ps3_dev_context *dev_context = &instance->dev_context; + + dev_context->vd_table_idx = 0; + dev_context + ->vd_info_buf_sync = (struct PS3VDInfo *)ps3_dma_alloc_coherent( + instance, + PS3_MAX_VD_COUNT(instance) * sizeof(struct PS3VDEntry) + + sizeof(struct PS3VDInfo), + (unsigned long long *)&dev_context->vd_info_buf_phys_sync); + if (dev_context->vd_info_buf_sync == NULL) { + LOG_ERROR("host_no:%u alloc vd sync info buffer failed !\n", + PS3_HOST(instance)); + ret = -PS3_ENOMEM; + goto l_out; + } + + dev_context->vd_info_buf_async = + (struct PS3VDInfo *)ps3_dma_alloc_coherent( + instance, + PS3_MAX_VD_COUNT(instance) * sizeof(struct PS3VDEntry) + + sizeof(struct PS3VDInfo), + (unsigned long long *)&dev_context + ->vd_info_buf_phys_async); + if (dev_context->vd_info_buf_async == NULL) { + LOG_ERROR("host_no:%u alloc vd async info buffer failed !\n", + PS3_HOST(instance)); + ret = -PS3_ENOMEM; + goto l_failed; + } + + return ret; + +l_failed: + ps3_mgr_vd_info_exit(instance); +l_out: + return ret; +} + +int ps3_mgr_cmd_init(struct ps3_instance *instance) +{ + LOG_INFO("host_no:%u, Soc VD max is:%d\n", PS3_HOST(instance), + PS3_MAX_VD_COUNT(instance)); + LOG_INFO("host_no:%u, Soc PD max is:%d\n", PS3_HOST(instance), + PS3_MAX_PD_COUNT(instance)); + + if (PS3_MAX_PD_COUNT(instance) > 0) { + if (ps3_mgr_pd_list_init(instance) != PS3_SUCCESS) + goto free_mgr_cmd; + + if (ps3_mgr_pd_info_init(instance) != PS3_SUCCESS) + goto free_mgr_cmd; + } + + if (PS3_MAX_VD_COUNT(instance) > 0) { + if (ps3_mgr_vd_list_init(instance) != PS3_SUCCESS) + goto free_mgr_cmd; + + if (ps3_mgr_vd_info_init(instance) != PS3_SUCCESS) + goto free_mgr_cmd; + } + + return PS3_SUCCESS; + +free_mgr_cmd: + ps3_mgr_cmd_exit(instance); + return -PS3_ENOMEM; +} + +void ps3_mgr_cmd_exit(struct ps3_instance *instance) +{ + ps3_mgr_pd_list_exit(instance); + ps3_mgr_vd_list_exit(instance); + ps3_mgr_pd_info_exit(instance); + ps3_mgr_vd_info_exit(instance); +} + +void ps3_mgr_cmd_word_build(struct ps3_cmd *cmd) +{ + struct PS3CmdWord *cmd_word = &cmd->cmd_word; + + memset(cmd_word, 0, sizeof(*cmd_word)); + + cmd_word->type = PS3_CMDWORD_TYPE_MGR; + cmd_word->direct = PS3_CMDWORD_DIRECT_NORMAL; + cmd_word->cmdFrameID = ps3_cmd_frame_id(cmd); +#ifndef _WINDOWS + cmd_word->isrSN = ps3_msix_index_get(cmd, 1); +#else + cmd_word->isrSN = 0; +#endif +} + +static void ps3_mgr_print_cmd(struct ps3_cmd *cmd, const char *cmd_type, + unsigned char is_send) +{ + LOG_DEBUG( + "host_no:%u t_id:0x%llx mgr:%s:%s cmd word type:%d\n" + "\tdirect:%d qmask:0x%x CFID:%d isr_sn:%d vid:%d pid:%d function:%d\n", + PS3_HOST(cmd->instance), cmd->trace_id, + (is_send) ? "send" : "recv", cmd_type, cmd->cmd_word.type, + cmd->cmd_word.direct, cmd->cmd_word.qMask, + cmd->cmd_word.cmdFrameID, cmd->cmd_word.isrSN, + cmd->cmd_word.virtDiskID, cmd->cmd_word.phyDiskID, + ps3_get_pci_function(cmd->instance->pdev)); +} + +static inline void ps3_mgr_req_head_init(struct ps3_cmd *cmd, + struct PS3ReqFrameHead *req_header, + unsigned char cmdSubType) +{ + req_header->timeout = PS3_DEFAULT_MGR_CMD_TIMEOUT; + req_header->traceID = ps3_cmd_trace_id(cmd); + req_header->cmdType = PS3_CMD_MANAGEMENT; + req_header->cmdSubType = cmdSubType; + req_header->cmdFrameID = ps3_cmd_frame_id(cmd); + req_header->control = 0; + req_header->reqFrameFormat = PS3_REQFRAME_FORMAT_FRONTEND; + req_header->noReplyWord = PS3_CMD_WORD_NEED_REPLY_WORD; +} + +static inline void +ps3_mgr_req_frame_sge_build(struct PS3MgrReqFrame *mgr_req_frame, + dma_addr_t dma_addr, unsigned int dma_len) +{ + struct PS3Sge *p_sge = mgr_req_frame->sgl; + + mgr_req_frame->sgeOffset = offsetof(struct PS3MgrReqFrame, sgl) >> + PS3_MGR_CMD_SGL_OFFSET_DWORD_SHIFT; + mgr_req_frame->sgeCount = 1; + + p_sge->addr = cpu_to_le64(dma_addr); + p_sge->length = cpu_to_le32(dma_len); + p_sge->lastSge = 1; + p_sge->ext = 0; +} + +static int ps3_mgr_unload_cmd_send(struct ps3_cmd *cmd, unsigned short time_out) +{ + int ret = PS3_SUCCESS; + enum PS3MgrCmdSubType cmd_type = + (enum PS3MgrCmdSubType)cmd->req_frame->mgrReq.reqHead.cmdSubType; + + ps3_mgr_cmd_word_build(cmd); + cmd->time_out = time_out; + cmd->is_interrupt = PS3_DRV_FALSE; + ps3_mgr_print_cmd(cmd, namePS3MgrCmdSubType(cmd_type), PS3_DRV_TRUE); + ret = ps3_unload_cmd_send_sync(cmd->instance, cmd); + ps3_mgr_print_cmd(cmd, namePS3MgrCmdSubType(cmd_type), PS3_DRV_FALSE); + + if (ret == PS3_SUCCESS) { + LOG_INFO("host_no:%u send %s success\n", + PS3_HOST(cmd->instance), + namePS3MgrCmdSubType(cmd_type)); + } else { + LOG_ERROR("host_no:%u send %s %s respStatus:%d\n", + PS3_HOST(cmd->instance), + namePS3MgrCmdSubType(cmd_type), + (ret == -PS3_TIMEOUT) ? "timeout" : "failed", + ps3_cmd_resp_status(cmd)); + } + + return ret; +} + +int ps3_pd_list_get(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + int send_result = PS3_SUCCESS; + struct ps3_cmd *cmd = NULL; + struct PS3MgrReqFrame *mgr_req_frame = NULL; + unsigned int pd_list_size = + PS3_MAX_PD_COUNT(instance) * sizeof(struct PS3PhyDevice) + + sizeof(struct PS3DevList); + + LOG_INFO("host_no:%u enter !\n", PS3_HOST(instance)); + ps3_atomic_inc(&instance->cmd_statistics.cmd_delivering); + if (ps3_mgr_cmd_send_pre_check(instance, PS3_TRUE) != PS3_SUCCESS) { + LOG_WARN_LIM("hno:%u pd list cmd pre check NOK\n", + PS3_HOST(instance)); + ps3_atomic_dec(&instance->cmd_statistics.cmd_delivering); + ret = -PS3_EBUSY; + goto l_out; + } + cmd = ps3_mgr_cmd_alloc(instance); + if (cmd == NULL) { + LOG_ERROR("host_no:%u mgr cmd get NOK !\n", PS3_HOST(instance)); + ps3_atomic_dec(&instance->cmd_statistics.cmd_delivering); + ret = -PS3_EBUSY; + goto l_out; + } + + mgr_req_frame = &cmd->req_frame->mgrReq; + memset(instance->dev_context.pd_list_buf, 0, pd_list_size); + memset(mgr_req_frame, 0, sizeof(*mgr_req_frame)); + + ps3_mgr_req_head_init(cmd, &mgr_req_frame->reqHead, + PS3_MGR_CMD_GET_PD_LIST); + mgr_req_frame->timeout = 0; + mgr_req_frame->syncFlag = 1; + mgr_req_frame->value.dev.devID.diskDev.diskID = 0; + mgr_req_frame->value.dev.num = PS3_MAX_PD_COUNT(instance); + + ps3_mgr_req_frame_sge_build(mgr_req_frame, + instance->dev_context.pd_list_buf_phys, + pd_list_size); + + send_result = ps3_mgr_cmd_sync_proc(instance, cmd, + PS3_DEFAULT_MGR_CMD_TIMEOUT); + ps3_atomic_dec(&instance->cmd_statistics.cmd_delivering); + if (send_result == PS3_SUCCESS) { + send_result = ps3_cmd_wait_sync(instance, cmd); + ps3_mgr_print_cmd(cmd, + namePS3MgrCmdSubType(PS3_MGR_CMD_GET_PD_LIST), + PS3_DRV_FALSE); + } + ret = ps3_mgr_complete_proc(instance, cmd, send_result); +l_out: + if ((ret != -PS3_CMD_NO_RESP) && (cmd != NULL)) + ps3_mgr_cmd_free(instance, cmd); + + LOG_INFO("host_no:%u exit !\n", PS3_HOST(instance)); + return ret; +} + +int ps3_vd_list_get(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + int send_result = PS3_SUCCESS; + struct ps3_cmd *cmd = NULL; + struct PS3MgrReqFrame *mgr_req_frame = NULL; + unsigned int vd_list_size = + PS3_MAX_VD_COUNT(instance) * sizeof(struct PS3VirtDevice) + + sizeof(struct PS3DevList); + + LOG_INFO("host_no:%u enter !\n", PS3_HOST(instance)); + ps3_atomic_inc(&instance->cmd_statistics.cmd_delivering); + if (ps3_mgr_cmd_send_pre_check(instance, PS3_TRUE) != PS3_SUCCESS) { + LOG_WARN_LIM("hno:%u vd list cmd pre check NOK\n", + PS3_HOST(instance)); + ps3_atomic_dec(&instance->cmd_statistics.cmd_delivering); + ret = -PS3_EBUSY; + goto l_out; + } + cmd = ps3_mgr_cmd_alloc(instance); + if (cmd == NULL) { + LOG_ERROR("host_no:%u mgr cmd get NOK !\n", PS3_HOST(instance)); + ps3_atomic_dec(&instance->cmd_statistics.cmd_delivering); + ret = -PS3_EBUSY; + goto l_out; + } + + mgr_req_frame = &cmd->req_frame->mgrReq; + memset(instance->dev_context.vd_list_buf, 0, vd_list_size); + memset(mgr_req_frame, 0, sizeof(*mgr_req_frame)); + + ps3_mgr_req_head_init(cmd, &mgr_req_frame->reqHead, + PS3_MGR_CMD_GET_VD_LIST); + mgr_req_frame->timeout = 0; + mgr_req_frame->syncFlag = 1; + mgr_req_frame->value.dev.devID.diskDev.diskID = 0; + mgr_req_frame->value.dev.num = PS3_MAX_VD_COUNT(instance); + + ps3_mgr_req_frame_sge_build(mgr_req_frame, + instance->dev_context.vd_list_buf_phys, + vd_list_size); + + send_result = ps3_mgr_cmd_sync_proc(instance, cmd, + PS3_DEFAULT_MGR_CMD_TIMEOUT); + ps3_atomic_dec(&instance->cmd_statistics.cmd_delivering); + if (send_result == PS3_SUCCESS) { + send_result = ps3_cmd_wait_sync(instance, cmd); + ps3_mgr_print_cmd(cmd, + namePS3MgrCmdSubType(PS3_MGR_CMD_GET_VD_LIST), + PS3_DRV_FALSE); + } + ret = ps3_mgr_complete_proc(instance, cmd, send_result); +l_out: + if ((ret != -PS3_CMD_NO_RESP) && (cmd != NULL)) + ps3_mgr_cmd_free(instance, cmd); + LOG_INFO("host_no:%u exit !\n", PS3_HOST(instance)); + return ret; +} + +int ps3_pd_info_get(struct ps3_instance *instance, unsigned short channel, + unsigned short target_id, unsigned short pd_disk_id) +{ + int ret = PS3_SUCCESS; + int send_result = PS3_SUCCESS; + struct ps3_cmd *cmd = NULL; + struct PS3MgrReqFrame *mgr_req_frame = NULL; + unsigned int pd_info_size = sizeof(struct PS3PDInfo); + + LOG_DEBUG("host_no:%u enter !\n", PS3_HOST(instance)); + ps3_atomic_inc(&instance->cmd_statistics.cmd_delivering); + if (ps3_mgr_cmd_send_pre_check(instance, PS3_TRUE) != PS3_SUCCESS) { + LOG_WARN_LIM("hno:%u pd info cmd pre check NOK\n", + PS3_HOST(instance)); + ps3_atomic_dec(&instance->cmd_statistics.cmd_delivering); + ret = -PS3_EBUSY; + goto l_out; + } + cmd = ps3_mgr_cmd_alloc(instance); + if (cmd == NULL) { + LOG_ERROR("host_no:%u mgr cmd get NOK !\n", PS3_HOST(instance)); + ps3_atomic_dec(&instance->cmd_statistics.cmd_delivering); + ret = -PS3_EBUSY; + goto l_out; + } + + mgr_req_frame = &cmd->req_frame->mgrReq; + memset(instance->dev_context.pd_info_buf, 0, pd_info_size); + memset(mgr_req_frame, 0, sizeof(*mgr_req_frame)); + + ps3_mgr_req_head_init(cmd, &mgr_req_frame->reqHead, + PS3_MGR_CMD_GET_PD_INFO); + + mgr_req_frame->timeout = 0; + mgr_req_frame->syncFlag = 1; + + mgr_req_frame->value.dev.num = 1; + mgr_req_frame->value.dev.devID.diskDev.ps3Dev.softChan = channel; + mgr_req_frame->value.dev.devID.diskDev.ps3Dev.devID = target_id; + mgr_req_frame->value.dev.devID.diskDev.ps3Dev.phyDiskID = pd_disk_id; + + ps3_mgr_req_frame_sge_build(mgr_req_frame, + instance->dev_context.pd_info_buf_phys, + pd_info_size); + + LOG_INFO("host_no:%u ready send, reqFrameId=%d, [%d:%d:%d]!\n", + PS3_HOST(instance), ps3_cmd_frame_id(cmd), channel, target_id, + pd_disk_id); + + send_result = ps3_mgr_cmd_sync_proc(instance, cmd, + PS3_DEFAULT_MGR_CMD_TIMEOUT); + ps3_atomic_dec(&instance->cmd_statistics.cmd_delivering); + if (send_result == PS3_SUCCESS) { + send_result = ps3_cmd_wait_sync(instance, cmd); + ps3_mgr_print_cmd(cmd, + namePS3MgrCmdSubType(PS3_MGR_CMD_GET_PD_INFO), + PS3_DRV_FALSE); + } + ret = ps3_mgr_complete_proc(instance, cmd, send_result); + LOG_INFO("host_no:%u get pd info [%d:%d:%d] finished!:ret = %d\n", + PS3_HOST(instance), channel, target_id, pd_disk_id, ret); +l_out: + if ((ret != -PS3_CMD_NO_RESP) && (cmd != NULL)) + ps3_mgr_cmd_free(instance, cmd); + + + LOG_DEBUG("host_no:%u exit ret[%d]!\n", PS3_HOST(instance), ret); + return ret; +} + +int ps3_vd_info_sync_get(struct ps3_instance *instance, unsigned int disk_id, + unsigned short vd_num) +{ + int ret = PS3_SUCCESS; + int send_result = PS3_SUCCESS; + struct ps3_cmd *cmd = NULL; + struct PS3MgrReqFrame *mgr_req_frame = NULL; + unsigned int vd_info_size = + PS3_MAX_VD_COUNT(instance) * sizeof(struct PS3VDEntry) + + sizeof(struct PS3VDInfo); + struct ps3_dev_context *dev_context = &instance->dev_context; + + LOG_DEBUG("host_no:%u enter !\n", PS3_HOST(instance)); + ps3_atomic_inc(&instance->cmd_statistics.cmd_delivering); + if (ps3_mgr_cmd_send_pre_check(instance, PS3_TRUE) != PS3_SUCCESS) { + LOG_WARN_LIM("hno:%u vd info sync cmd pre check NOK\n", + PS3_HOST(instance)); + ps3_atomic_dec(&instance->cmd_statistics.cmd_delivering); + ret = -PS3_EBUSY; + goto l_out; + } + cmd = ps3_mgr_cmd_alloc(instance); + if (cmd == NULL) { + LOG_ERROR("host_no:%u mgr cmd get NOK !\n", PS3_HOST(instance)); + ps3_atomic_dec(&instance->cmd_statistics.cmd_delivering); + ret = -PS3_EBUSY; + goto l_out; + } + + mgr_req_frame = &cmd->req_frame->mgrReq; + memset(dev_context->vd_info_buf_sync, 0, vd_info_size); + memset(mgr_req_frame, 0, sizeof(*mgr_req_frame)); + + ps3_mgr_req_head_init(cmd, &mgr_req_frame->reqHead, + PS3_MGR_CMD_GET_VD_INFO); + mgr_req_frame->timeout = 0; + mgr_req_frame->syncFlag = 1; + mgr_req_frame->value.dev.devID.diskDev.diskID = disk_id; + mgr_req_frame->value.dev.num = vd_num; + + ps3_mgr_req_frame_sge_build(mgr_req_frame, + dev_context->vd_info_buf_phys_sync, + vd_info_size); + + LOG_INFO( + "host_no:%u ready send, reqFrameId=%d, disk_id=0x%x, num=%d !\n", + PS3_HOST(instance), ps3_cmd_frame_id(cmd), disk_id, vd_num); + + send_result = ps3_mgr_cmd_sync_proc(instance, cmd, + PS3_DEFAULT_MGR_CMD_TIMEOUT); + ps3_atomic_dec(&instance->cmd_statistics.cmd_delivering); + if (send_result == PS3_SUCCESS) { + send_result = ps3_cmd_wait_sync(instance, cmd); + ps3_mgr_print_cmd(cmd, + namePS3MgrCmdSubType(PS3_MGR_CMD_GET_VD_INFO), + PS3_DRV_FALSE); + } + ret = ps3_mgr_complete_proc(instance, cmd, send_result); + LOG_INFO( + "host_no:%u get vd info, reqFrameId=%d, disk_id=0x%x, num=%d finish!\n", + PS3_HOST(instance), ps3_cmd_frame_id(cmd), disk_id, vd_num); +l_out: + if ((ret != -PS3_CMD_NO_RESP) && (cmd != NULL)) + ps3_mgr_cmd_free(instance, cmd); + LOG_DEBUG("host_no:%u exit !\n", PS3_HOST(instance)); + return ret; +} + +int ps3_vd_info_async_get(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + struct ps3_cmd *cmd = NULL; + struct PS3MgrReqFrame *mgr_req_frame = NULL; + unsigned int vd_info_size = + PS3_MAX_VD_COUNT(instance) * sizeof(struct PS3VDEntry) + + sizeof(struct PS3VDInfo); + struct ps3_dev_context *dev_context = &instance->dev_context; + + LOG_DEBUG("host_no:%u enter !\n", PS3_HOST(instance)); + + cmd = dev_context->vd_pending_cmd; + if (cmd == NULL) { + cmd = ps3_mgr_cmd_alloc(instance); + if (cmd == NULL) { + LOG_FILE_ERROR("host_no:%u mgr cmd get failed !\n", + PS3_HOST(instance)); + ret = -PS3_EBUSY; + goto l_out; + } + + mgr_req_frame = &cmd->req_frame->mgrReq; + memset(dev_context->vd_info_buf_async, 0, vd_info_size); + memset(mgr_req_frame, 0, sizeof(*mgr_req_frame)); + + ps3_mgr_req_head_init(cmd, &mgr_req_frame->reqHead, + PS3_MGR_CMD_GET_VD_INFO); + mgr_req_frame->reqHead.timeout = 0; + mgr_req_frame->timeout = 0; + mgr_req_frame->syncFlag = 0; + mgr_req_frame->pendingFlag = 1; + + ps3_mgr_req_frame_sge_build(mgr_req_frame, + dev_context->vd_info_buf_phys_async, + vd_info_size); + + ps3_vd_pending_filter_table_build( + (unsigned char *)dev_context->vd_info_buf_async); + + ps3_mgr_cmd_word_build(cmd); + LOG_FILE_INFO( + "host_no:%u ready send, reqFrameId=%d, t_id:0x%llx!\n", + PS3_HOST(instance), ps3_cmd_frame_id(cmd), + cmd->trace_id); + ps3_mgr_print_cmd(cmd, "vd info async", PS3_DRV_TRUE); + dev_context->vd_pending_cmd = cmd; + ret = ps3_cmd_send_async(instance, cmd, + ps3_dev_vd_pending_proc); + ps3_mgr_print_cmd(cmd, "vd info async", PS3_DRV_FALSE); + if (ret != PS3_SUCCESS) { + LOG_FILE_ERROR( + "host_no:%u send error, reqFrameId=%d, t_id:0x%llx ret:%d!\n", + PS3_HOST(instance), ps3_cmd_frame_id(cmd), + cmd->trace_id, ret); + dev_context->vd_pending_cmd = NULL; + ps3_mgr_cmd_free(instance, cmd); + } + } else { + LOG_FILE_INFO("host_no:%u vd info already subscribed\n", + PS3_HOST(instance)); + } +l_out: + LOG_DEBUG("host_no:%u exit !\n", PS3_HOST(instance)); + return ret; +} + +static inline void ps3_ctrl_info_capability_dump(struct ps3_instance *instance) +{ + LOG_WARN( + "host_no:%u ctrl info--supportUnevenSpans:%d supportJbodSecure:%d\n" + "\tsupportNvmePassthru:%d supportDirectCmd:%d\n" + "\tsupportAcceleration:%d supportSataDirectCmd:%d supportSataNcq:%d\n" + "\tioTimeOut:%u cancelTimeOut:%u isotoneTimeOut:%u\n", + PS3_HOST(instance), + instance->ctrl_info.capabilities.supportUnevenSpans, + instance->ctrl_info.capabilities.supportJbodSecure, + instance->ctrl_info.capabilities.supportNvmePassthru, + instance->ctrl_info.capabilities.supportDirectCmd, + instance->ctrl_info.capabilities.supportAcceleration, + instance->ctrl_info.capabilities.supportSataDirectCmd, + instance->ctrl_info.capabilities.supportSataNcq, + instance->ctrl_info.ioTimeOut, + instance->ctrl_info.cancelTimeOut, + instance->ctrl_info.isotoneTimeOut); +} + +static void ps3_ctrl_info_update(struct ps3_instance *instance) +{ + unsigned char is_need_dump_info = + (memcmp(&instance->ctrl_info, instance->ctrl_info_buf, + sizeof(instance->ctrl_info)) != 0); + + memcpy(&instance->ctrl_info, instance->ctrl_info_buf, + sizeof(instance->ctrl_info)); + + if (is_need_dump_info) + ps3_ctrl_info_capability_dump(instance); + + if (instance->ctrl_info.capabilities.supportDirectCmd) { + instance->cmd_attr.is_support_direct_cmd = PS3_DRV_TRUE; + LOG_INFO("host_no:%u change is_support_direct_cmd to :%d !\n", + PS3_HOST(instance), + instance->cmd_attr.is_support_direct_cmd); + } else { + instance->cmd_attr.is_support_direct_cmd = PS3_DRV_FALSE; + LOG_INFO("host_no:%u change is_support_direct_cmd to :%d !\n", + PS3_HOST(instance), + instance->cmd_attr.is_support_direct_cmd); + } + + instance->cmd_attr.vd_io_threshold = + le32_to_cpu(instance->ctrl_info.vdIOThreshold); + + LOG_INFO("host_no:%u change vdIOThreshold to :%d !\n", + PS3_HOST(instance), instance->cmd_attr.vd_io_threshold); + ps3_perf_update(instance, instance->ctrl_info.iocPerfMode); + if (instance->ctrl_info.vdQueueNum == 0) { + LOG_ERROR("host_no:%u ctrl info update vd Queue Num is 0!\n", + PS3_HOST(instance)); + instance->ctrl_info.vdQueueNum = 1; + } + LOG_DEBUG("host_no:%u offsetOfVDID:%u\n", PS3_HOST(instance), + instance->ctrl_info.offsetOfVDID); + + LOG_INFO("host_no:%u change is_balance_current_perf_mode to :%d !\n", + PS3_HOST(instance), + instance->irq_context.is_balance_current_perf_mode); +} + +int ps3_ctrl_info_get(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + int send_result = PS3_SUCCESS; + struct ps3_cmd *cmd = NULL; + struct PS3MgrReqFrame *mgr_req_frame = NULL; + + LOG_DEBUG("host_no:%u enter !\n", PS3_HOST(instance)); + ps3_atomic_inc(&instance->cmd_statistics.cmd_delivering); + if (ps3_mgr_cmd_send_pre_check(instance, PS3_TRUE) != PS3_SUCCESS) { + LOG_WARN_LIM("hno:%u ctrl info cmd pre check NOK\n", + PS3_HOST(instance)); + ps3_atomic_dec(&instance->cmd_statistics.cmd_delivering); + ret = -PS3_EBUSY; + goto l_out; + } + cmd = ps3_mgr_cmd_alloc(instance); + if (cmd == NULL) { + LOG_ERROR("host_no:%u mgr cmd get NOK !\n", PS3_HOST(instance)); + ps3_atomic_dec(&instance->cmd_statistics.cmd_delivering); + ret = -PS3_EBUSY; + goto l_out; + } + + mgr_req_frame = &cmd->req_frame->mgrReq; + memset(instance->ctrl_info_buf, 0, sizeof(struct PS3IocCtrlInfo)); + memset(mgr_req_frame, 0, sizeof(*mgr_req_frame)); + + ps3_mgr_req_head_init(cmd, &mgr_req_frame->reqHead, + PS3_MGR_CMD_GET_CTRL_INFO); + mgr_req_frame->timeout = 0; + mgr_req_frame->syncFlag = 1; + cmd->is_force_polling = 1; + + ps3_mgr_req_frame_sge_build(mgr_req_frame, instance->ctrl_info_buf_h, + sizeof(struct PS3IocCtrlInfo)); + + send_result = ps3_mgr_cmd_sync_proc(instance, cmd, + PS3_DEFAULT_MGR_CMD_TIMEOUT); + ps3_atomic_dec(&instance->cmd_statistics.cmd_delivering); + if (send_result == PS3_SUCCESS) { + send_result = ps3_cmd_wait_sync(instance, cmd); + ps3_mgr_print_cmd( + cmd, namePS3MgrCmdSubType(PS3_MGR_CMD_GET_CTRL_INFO), + PS3_DRV_FALSE); + } + ret = ps3_mgr_complete_proc(instance, cmd, send_result); + if (ret != PS3_SUCCESS) { + LOG_WARN("host_no:%u get ctrl info NOK!\n", PS3_HOST(instance)); + goto l_out; + } + ps3_ctrl_info_update(instance); +l_out: + if ((ret != -PS3_CMD_NO_RESP) && (cmd != NULL)) + ps3_mgr_cmd_free(instance, cmd); + LOG_DEBUG("host_no:%u exit !\n", PS3_HOST(instance)); + return ret; +} + +int ps3_soc_unload(struct ps3_instance *instance, unsigned char is_polling, + unsigned char type, unsigned char suspend_type) +{ + int ret = PS3_SUCCESS; + struct ps3_cmd *cmd = NULL; + struct PS3MgrReqFrame *mgr_req_frame = NULL; + + LOG_DEBUG("host_no:%u enter !\n", PS3_HOST(instance)); + + if (!ps3_check_ioc_state_is_normal_in_unload(instance)) { + ret = -PS3_FAILED; + goto l_out; + } + + ps3_wait_scsi_cmd_done(instance, PS3_TRUE); + ps3_wait_mgr_cmd_done(instance, PS3_TRUE); + + cmd = ps3_mgr_cmd_alloc(instance); + if (cmd == NULL) { + LOG_ERROR("host_no:%u mgr cmd get NOK !\n", PS3_HOST(instance)); + ret = -PS3_EBUSY; + goto l_out; + } + + mgr_req_frame = &cmd->req_frame->mgrReq; + memset(mgr_req_frame, 0, sizeof(*mgr_req_frame)); + + ps3_mgr_req_head_init(cmd, &mgr_req_frame->reqHead, PS3_MGR_CMD_UNLOAD); + mgr_req_frame->reqHead.timeout = instance->unload_timeout; + mgr_req_frame->sgeCount = 0; + mgr_req_frame->timeout = 0; + mgr_req_frame->syncFlag = 1; + mgr_req_frame->osType = PS3_LINUX_FRAME; + mgr_req_frame->value.unLoadType = type; + mgr_req_frame->suspend_type = suspend_type; + cmd->is_force_polling = is_polling; + + LOG_WARN( + "host_no:%u ready send unload, reqFrameId=%d suspend_type:%d!\n", + PS3_HOST(instance), ps3_cmd_frame_id(cmd), suspend_type); + ret = ps3_mgr_unload_cmd_send(cmd, instance->unload_timeout); + LOG_WARN("host_no:%u reqFrameId=%d finished ret:%d!\n", + PS3_HOST(instance), ps3_cmd_frame_id(cmd), ret); +l_out: + if (cmd != NULL) + ps3_mgr_cmd_free(instance, cmd); + LOG_DEBUG("host_no:%u exit !\n", PS3_HOST(instance)); + return ret; +} + +struct ps3_cmd *ps3_dump_notify_cmd_build(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + struct ps3_cmd *cmd = NULL; + struct PS3MgrReqFrame *mgr_req_frame = NULL; + struct ps3_dump_context *dump_context = &instance->dump_context; + + LOG_DEBUG("host_no:%u enter !\n", PS3_HOST(instance)); + cmd = ps3_mgr_cmd_alloc(instance); + if (cmd == NULL) { + LOG_ERROR("host_no:%u mgr cmd get failed !\n", + PS3_HOST(instance)); + goto l_out; + } + + mgr_req_frame = &cmd->req_frame->mgrReq; + memset(dump_context->dump_dma_buf, 0, PS3_DUMP_DMA_BUF_SIZE); + memset(mgr_req_frame, 0, sizeof(*mgr_req_frame)); + + ps3_mgr_req_head_init(cmd, &mgr_req_frame->reqHead, + PS3_MGR_CMD_AUTODUMP_NOTIFY); + + mgr_req_frame->timeout = 0; + mgr_req_frame->syncFlag = 0; + mgr_req_frame->pendingFlag = 1; + + ps3_mgr_req_frame_sge_build(mgr_req_frame, dump_context->dump_dma_addr, + PS3_DUMP_DMA_BUF_SIZE); + + ps3_mgr_cmd_word_build(cmd); + + LOG_DEBUG("host_no:%u reqFrameId=%d !\n", PS3_HOST(instance), + ps3_cmd_frame_id(cmd)); + +l_out: + LOG_DEBUG("host_no:%u exit ! ret %d\n", PS3_HOST(instance), ret); + return cmd; +} + +int ps3_scsi_remove_device_done(struct ps3_instance *instance, + struct PS3DiskDevPos *disk_pos, + unsigned char dev_type) +{ + int ret = PS3_SUCCESS; + int send_result = PS3_SUCCESS; + struct ps3_cmd *cmd = NULL; + struct PS3MgrReqFrame *mgr_req_frame = NULL; + + LOG_DEBUG("host_no:%u enter !\n", PS3_HOST(instance)); + ps3_atomic_inc(&instance->cmd_statistics.cmd_delivering); + if (ps3_mgr_cmd_send_pre_check(instance, PS3_TRUE) != PS3_SUCCESS) { + LOG_WARN_LIM("hno:%u remove done cmd pre check NOK\n", + PS3_HOST(instance)); + ps3_atomic_dec(&instance->cmd_statistics.cmd_delivering); + ret = -PS3_EBUSY; + goto l_out; + } + cmd = ps3_mgr_cmd_alloc(instance); + if (cmd == NULL) { + LOG_ERROR("host_no:%u mgr cmd get NOK !\n", PS3_HOST(instance)); + ps3_atomic_dec(&instance->cmd_statistics.cmd_delivering); + ret = -PS3_EBUSY; + goto l_out; + } + + mgr_req_frame = &cmd->req_frame->mgrReq; + memset(mgr_req_frame, 0, sizeof(*mgr_req_frame)); + + ps3_mgr_req_head_init(cmd, &mgr_req_frame->reqHead, + PS3_MGR_CMD_DEV_DEL_DONE); + mgr_req_frame->timeout = 0; + mgr_req_frame->syncFlag = 1; + mgr_req_frame->reqHead.devID.ps3Dev = disk_pos->diskDev.ps3Dev; + mgr_req_frame->value.dev.devID.diskDev = disk_pos->diskDev; + mgr_req_frame->value.dev.devID.diskMagicNum = disk_pos->diskMagicNum; + mgr_req_frame->value.dev.devType = dev_type; + + LOG_INFO( + "host_no:%u ready send, t_id:0x%llx reqFrameId=%d, dev_type=%d,\n" + "\t[%d:%d:%d:%u]!\n", + PS3_HOST(instance), cmd->trace_id, ps3_cmd_frame_id(cmd), + dev_type, PS3_CHANNEL(disk_pos), PS3_TARGET(disk_pos), + PS3_VDID(disk_pos), disk_pos->diskMagicNum); + + send_result = ps3_mgr_cmd_sync_proc(instance, cmd, + PS3_DEFAULT_MGR_CMD_TIMEOUT); + ps3_atomic_dec(&instance->cmd_statistics.cmd_delivering); + if (send_result == PS3_SUCCESS) { + send_result = ps3_cmd_wait_sync(instance, cmd); + ps3_mgr_print_cmd( + cmd, namePS3MgrCmdSubType(PS3_MGR_CMD_DEV_DEL_DONE), + PS3_DRV_FALSE); + } + ret = ps3_mgr_complete_proc(instance, cmd, send_result); + if (ret != PS3_SUCCESS) { + LOG_INFO( + "host_no:%u send [%u:%u:%u:%u][type:%d] del done failed!:ret = %d\n", + PS3_HOST(instance), PS3_CHANNEL(disk_pos), + PS3_TARGET(disk_pos), PS3_VDID(disk_pos), + disk_pos->diskMagicNum, dev_type, ret); + } + +l_out: + if ((ret != -PS3_CMD_NO_RESP) && (cmd != NULL)) + ps3_mgr_cmd_free(instance, cmd); + + LOG_INFO("host_no:%u send [%d:%d:%d:%u] finish!:ret = %d\n", + PS3_HOST(instance), PS3_CHANNEL(disk_pos), + PS3_TARGET(disk_pos), PS3_VDID(disk_pos), + disk_pos->diskMagicNum, ret); + + LOG_DEBUG("host_no:%u exit !\n", PS3_HOST(instance)); + return ret; +} + +int ps3_scsi_add_device_ack(struct ps3_instance *instance, + struct PS3DiskDevPos *disk_pos, + unsigned char dev_type) +{ + int ret = PS3_SUCCESS; + int send_result = PS3_SUCCESS; + struct ps3_cmd *cmd = NULL; + struct PS3MgrReqFrame *mgr_req_frame = NULL; + + LOG_DEBUG("host_no:%u enter !\n", PS3_HOST(instance)); + ps3_atomic_inc(&instance->cmd_statistics.cmd_delivering); + if (ps3_mgr_cmd_send_pre_check(instance, PS3_TRUE) != PS3_SUCCESS) { + LOG_WARN_LIM("hno:%u add ack cmd pre check NOK\n", + PS3_HOST(instance)); + ps3_atomic_dec(&instance->cmd_statistics.cmd_delivering); + ret = -PS3_EBUSY; + goto l_out; + } + cmd = ps3_mgr_cmd_alloc(instance); + if (cmd == NULL) { + LOG_ERROR("host_no:%u mgr cmd get NOK !\n", PS3_HOST(instance)); + ps3_atomic_dec(&instance->cmd_statistics.cmd_delivering); + ret = -PS3_EBUSY; + goto l_out; + } + + mgr_req_frame = &cmd->req_frame->mgrReq; + memset(mgr_req_frame, 0, sizeof(*mgr_req_frame)); + + ps3_mgr_req_head_init(cmd, &mgr_req_frame->reqHead, + PS3_MGR_CMD_DEV_ADD_ACK); + mgr_req_frame->timeout = 0; + mgr_req_frame->syncFlag = 1; + mgr_req_frame->reqHead.devID.ps3Dev = disk_pos->diskDev.ps3Dev; + mgr_req_frame->value.dev.devID.diskDev = disk_pos->diskDev; + mgr_req_frame->value.dev.devID.diskMagicNum = disk_pos->diskMagicNum; + mgr_req_frame->value.dev.devType = dev_type; + + LOG_INFO("t_id:0x%llx host_no:%u ready send, CFID:%d dev_type:%d\n" + "\t[%u:%u:%u:%u]!\n", + cmd->trace_id, PS3_HOST(instance), ps3_cmd_frame_id(cmd), + dev_type, PS3_CHANNEL(disk_pos), PS3_TARGET(disk_pos), + PS3_VDID(disk_pos), disk_pos->diskMagicNum); + + send_result = ps3_mgr_cmd_sync_proc(instance, cmd, + PS3_DEFAULT_MGR_CMD_TIMEOUT); + ps3_atomic_dec(&instance->cmd_statistics.cmd_delivering); + if (send_result == PS3_SUCCESS) { + send_result = ps3_cmd_wait_sync(instance, cmd); + ps3_mgr_print_cmd(cmd, + namePS3MgrCmdSubType(PS3_MGR_CMD_DEV_ADD_ACK), + PS3_DRV_FALSE); + } + ret = ps3_mgr_complete_proc(instance, cmd, send_result); + if (ret != PS3_SUCCESS) { + LOG_ERROR( + "host_no:%u send [%u:%u:%u:%u][type:%d] add ack NOK!:ret = %d\n", + PS3_HOST(instance), PS3_CHANNEL(disk_pos), + PS3_TARGET(disk_pos), PS3_VDID(disk_pos), + disk_pos->diskMagicNum, dev_type, ret); + } + +l_out: + if ((ret != -PS3_CMD_NO_RESP) && (cmd != NULL)) + ps3_mgr_cmd_free(instance, cmd); + + LOG_INFO("host_no:%u send [%u:%u:%u:%u] finish!:ret = %d\n", + PS3_HOST(instance), PS3_CHANNEL(disk_pos), + PS3_TARGET(disk_pos), PS3_VDID(disk_pos), + disk_pos->diskMagicNum, ret); + + LOG_DEBUG("host_no:%u exit !\n", PS3_HOST(instance)); + return ret; +} + +int ps3_mgr_cmd_cancel(struct ps3_instance *instance, + unsigned short cancel_cmd_frame_id) +{ + int ret = PS3_SUCCESS; + int send_result = PS3_SUCCESS; + struct ps3_cmd *cmd = NULL; + struct PS3MgrReqFrame *mgr_req_frame = NULL; + unsigned short cancel_time_out = PS3_CANCEL_MGR_CMD_TIMEOUT; + + LOG_DEBUG("host_no:%u enter, be cancel CFID:%d !\n", PS3_HOST(instance), + cancel_cmd_frame_id); + ps3_atomic_inc(&instance->cmd_statistics.cmd_delivering); + if (ps3_mgr_cmd_send_pre_check(instance, PS3_TRUE) != PS3_SUCCESS) { + LOG_WARN_LIM("hno:%u mgr cancel cmd pre check NOK\n", + PS3_HOST(instance)); + ps3_atomic_dec(&instance->cmd_statistics.cmd_delivering); + ret = -PS3_EBUSY; + goto l_out; + } + cmd = ps3_task_cmd_alloc(instance); + if (cmd == NULL) { + LOG_FILE_ERROR("host_no:%u task cmd get failed !\n", + PS3_HOST(instance)); + ps3_atomic_dec(&instance->cmd_statistics.cmd_delivering); + ret = -PS3_EBUSY; + goto l_out; + } + + if (instance->ctrl_info.cancelTimeOut > cancel_time_out) + cancel_time_out = instance->ctrl_info.cancelTimeOut; + + mgr_req_frame = &cmd->req_frame->mgrReq; + memset(mgr_req_frame, 0, sizeof(*mgr_req_frame)); + + ps3_mgr_req_head_init(cmd, &mgr_req_frame->reqHead, PS3_MGR_CMD_CANCEL); + mgr_req_frame->reqHead.timeout = cancel_time_out; + mgr_req_frame->sgeCount = 0; + mgr_req_frame->timeout = 0; + mgr_req_frame->syncFlag = 1; + mgr_req_frame->abortFlag = 0; + mgr_req_frame->value.originalCmdFrameID = cancel_cmd_frame_id; + + LOG_FILE_INFO( + "host_no:%u ready send, reqFrameId=%d cancel_cmd_frame_id:%u !\n", + PS3_HOST(instance), ps3_cmd_frame_id(cmd), cancel_cmd_frame_id); + + send_result = ps3_mgr_cmd_sync_proc(instance, cmd, cancel_time_out); + ps3_atomic_dec(&instance->cmd_statistics.cmd_delivering); + if (send_result == PS3_SUCCESS) { + send_result = ps3_cmd_wait_sync(instance, cmd); + ps3_mgr_print_cmd(cmd, namePS3MgrCmdSubType(PS3_MGR_CMD_CANCEL), + PS3_DRV_FALSE); + } + ret = ps3_mgr_complete_proc(instance, cmd, send_result); + LOG_FILE_INFO( + "host_no:%u reqFrameId=%d cancel_cmd_frame_id:%u finished!\n", + PS3_HOST(instance), ps3_cmd_frame_id(cmd), cancel_cmd_frame_id); +l_out: + if ((ret != -PS3_CMD_NO_RESP) && (cmd != NULL)) + ps3_task_cmd_free(instance, cmd); + LOG_DEBUG("host_no:%u exit !\n", PS3_HOST(instance)); + return ret; +} +int ps3_mgr_cmd_cancel_send(struct ps3_instance *instance, + unsigned short cancel_cmd_frame_id, + unsigned char type) +{ + int ret = PS3_SUCCESS; + struct PS3MgrReqFrame *mgr_req_frame = NULL; + enum PS3MgrCmdSubType sub_type; + struct ps3_cmd *cmd = NULL; + + LOG_DEBUG("host_no:%u enter, be cancel CFID:%d !\n", PS3_HOST(instance), + cancel_cmd_frame_id); + + cmd = ps3_task_cmd_alloc(instance); + if (cmd == NULL) { + LOG_FILE_ERROR("host_no:%u task cmd get failed !\n", + PS3_HOST(instance)); + ret = -PS3_ENOMEM; + goto l_out; + } + + mgr_req_frame = &cmd->req_frame->mgrReq; + memset(mgr_req_frame, 0, sizeof(*mgr_req_frame)); + + ps3_mgr_req_head_init(cmd, &mgr_req_frame->reqHead, PS3_MGR_CMD_CANCEL); + mgr_req_frame->sgeCount = 0; + mgr_req_frame->timeout = 0; + mgr_req_frame->syncFlag = 1; + mgr_req_frame->abortFlag = 0; + mgr_req_frame->value.originalCmdFrameID = cancel_cmd_frame_id; + + LOG_FILE_INFO( + "host_no:%u ready send, reqFrameId=%d cancel_cmd_frame_id:%u !\n", + PS3_HOST(instance), ps3_cmd_frame_id(cmd), cancel_cmd_frame_id); + + sub_type = (enum PS3MgrCmdSubType)PS3_MGR_CMD_SUBTYPE(cmd); + + cmd->time_out = PS3_DEFAULT_MGR_CMD_TIMEOUT; + cmd->is_interrupt = PS3_DRV_FALSE; + ps3_mgr_cmd_word_build(cmd); + + ps3_mgr_print_cmd(cmd, namePS3MgrCmdSubType(sub_type), PS3_DRV_TRUE); + if (type == PS3_CANCEL_EVENT_CMD) + instance->event_context.event_abort_cmd = cmd; + else if (type == PS3_CANCEL_WEB_CMD) + instance->webSubscribe_context.web_abort_cmd = cmd; + else + instance->dev_context.vdpending_abort_cmd = cmd; + ret = ps3_cmd_no_block_send(instance, cmd); + if (ret != PS3_SUCCESS) { + LOG_FILE_INFO( + "host_no:%u CFID:%d trace_id:0x%llx send failed\n", + PS3_HOST(instance), ps3_cmd_frame_id(cmd), + cmd->trace_id); + } + ps3_mgr_print_cmd(cmd, namePS3MgrCmdSubType(sub_type), PS3_DRV_FALSE); +l_out: + return ret; +} +int ps3_mgr_cmd_cancel_wait(struct ps3_instance *instance, unsigned char type) +{ + int ret = PS3_SUCCESS; + struct ps3_cmd *cmd = NULL; + struct ps3_cmd *abort_cmd = NULL; + unsigned long flags = 0; + int send_result; + + if (type == PS3_CANCEL_EVENT_CMD) { + cmd = instance->event_context.event_abort_cmd; + if (cmd == NULL) { + LOG_INFO("host_no:%u event abort cmd null!\n", + PS3_HOST(instance)); + ret = -PS3_FAILED; + goto l_out; + } + } else if (type == PS3_CANCEL_WEB_CMD) { + cmd = instance->webSubscribe_context.web_abort_cmd; + if (cmd == NULL) { + LOG_INFO("host_no:%u web abort cmd null!\n", + PS3_HOST(instance)); + ret = -PS3_FAILED; + goto l_out; + } + } else { + cmd = instance->dev_context.vdpending_abort_cmd; + if (cmd == NULL) { + LOG_INFO("host_no:%u vdpending abort cmd null!\n", + PS3_HOST(instance)); + ret = -PS3_FAILED; + goto l_out; + } + } + send_result = ps3_block_cmd_wait(instance, cmd, 0); + + ps3_spin_lock_irqsave(&cmd->cmd_state.lock, &flags); + if (cmd->cmd_state.state != PS3_CMD_STATE_COMPLETE) { + ps3_spin_unlock_irqrestore(&cmd->cmd_state.lock, flags); + ret = -PS3_RECOVERED; + goto l_out; + } + ps3_spin_unlock_irqrestore(&cmd->cmd_state.lock, flags); + + ret = ps3_mgr_complete_proc(instance, cmd, send_result); + + LOG_INFO("host_no:%u reqFrameId=%d finished!\n", PS3_HOST(instance), + ps3_cmd_frame_id(cmd)); + if ((ret != -PS3_CMD_NO_RESP) && (cmd != NULL)) { + if (type == PS3_CANCEL_EVENT_CMD) { + abort_cmd = instance->event_context.event_abort_cmd; + instance->event_context.event_abort_cmd = NULL; + if (abort_cmd != NULL) + ps3_task_cmd_free(instance, abort_cmd); + } else if (type == PS3_CANCEL_WEB_CMD) { + abort_cmd = + instance->webSubscribe_context.web_abort_cmd; + instance->webSubscribe_context.web_abort_cmd = NULL; + if (abort_cmd != NULL) + ps3_task_cmd_free(instance, abort_cmd); + } else { + abort_cmd = instance->dev_context.vdpending_abort_cmd; + instance->dev_context.vdpending_abort_cmd = NULL; + if (abort_cmd != NULL) + ps3_task_cmd_free(instance, abort_cmd); + } + } +l_out: + return ret; +} + +int ps3_event_register(struct ps3_instance *instance, struct PS3MgrEvent *event) +{ + int ret = PS3_SUCCESS; + struct ps3_cmd *cmd = NULL; + struct PS3MgrReqFrame *mgr_req_frame = NULL; + + LOG_DEBUG("host_no:%u enter !\n", PS3_HOST(instance)); + + cmd = ps3_mgr_cmd_alloc(instance); + if (cmd == NULL) { + LOG_ERROR_IN_IRQ(instance, "host_no:%u mgr cmd get failed !\n", + PS3_HOST(instance)); + ret = -PS3_EBUSY; + goto l_out; + } + + mgr_req_frame = &cmd->req_frame->mgrReq; + memset(mgr_req_frame, 0, sizeof(*mgr_req_frame)); + + ps3_mgr_req_head_init(cmd, &mgr_req_frame->reqHead, + PS3_MGR_CMD_SUBSCRIBE_EVENT); + mgr_req_frame->reqHead.timeout = 0; + mgr_req_frame->timeout = 0; + mgr_req_frame->syncFlag = 0; + mgr_req_frame->pendingFlag = 1; + + mgr_req_frame->value.event.eventTypeMap = event->eventTypeMap; + mgr_req_frame->value.event.eventTypeMapProcResult = + event->eventTypeMapProcResult; + mgr_req_frame->value.event.eventLevel = event->eventLevel; + + instance->event_context.event_info = + (struct PS3EventInfo *)cmd->ext_buf; + + ps3_mgr_req_frame_sge_build(mgr_req_frame, cmd->ext_buf_phys, + cmd->instance->cmd_context.ext_buf_size); + + memset(cmd->ext_buf, 0, cmd->instance->cmd_context.ext_buf_size); + if (instance->ioc_adpter->event_filter_table_get != NULL) { + instance->ioc_adpter->event_filter_table_get( + (unsigned char *)cmd->ext_buf); + } + + ps3_mgr_cmd_word_build(cmd); + LOG_INFO_IN_IRQ(instance, "host_no:%u ready send, reqFrameId=%d !\n", + PS3_HOST(instance), ps3_cmd_frame_id(cmd)); + + ps3_mgr_print_cmd(cmd, "event register", PS3_DRV_TRUE); + instance->event_context.event_cmd = cmd; + ret = ps3_cmd_send_async(instance, cmd, ps3_event_service); + if (ret != PS3_SUCCESS) { + instance->event_context.event_cmd = NULL; + ps3_mgr_cmd_free(instance, cmd); + } + ps3_mgr_print_cmd(cmd, "event register", PS3_DRV_FALSE); +l_out: + LOG_DEBUG("host_no:%u exit !\n", PS3_HOST(instance)); + return ret; +} + +int ps3_web_register(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + struct ps3_cmd *cmd = NULL; + struct PS3MgrReqFrame *mgr_req_frame = NULL; + struct ps3_webSubscribe_context *web_context = + &instance->webSubscribe_context; + + cmd = web_context->webSubscribe_cmd; + + if (cmd == NULL) { + ps3_atomic_inc(&instance->cmd_statistics.cmd_delivering); + if (ps3_mgr_cmd_send_pre_check(instance, PS3_TRUE) != + PS3_SUCCESS) { + LOG_WARN_LIM("hno:%u web cmd pre check NOK\n", + PS3_HOST(instance)); + ps3_atomic_dec( + &instance->cmd_statistics.cmd_delivering); + ret = -PS3_FAILED; + goto l_out; + } + cmd = ps3_mgr_cmd_alloc(instance); + if (cmd == NULL) { + LOG_WARN_IN_IRQ( + instance, + "host_no:%u ioctl req, Failed to get a cmd packet\n", + PS3_HOST(instance)); + ps3_atomic_dec( + &instance->cmd_statistics.cmd_delivering); + ret = -PS3_FAILED; + goto l_out; + } + mgr_req_frame = &cmd->req_frame->mgrReq; + memset(mgr_req_frame, 0, sizeof(struct PS3MgrReqFrame)); + + ps3_mgr_req_head_init(cmd, &mgr_req_frame->reqHead, + PS3_MGR_CMD_WEBSUBSCRIBE_EVENT); + mgr_req_frame->reqHead.timeout = 0; + mgr_req_frame->timeout = 0; + mgr_req_frame->syncFlag = 0; + mgr_req_frame->pendingFlag = 1; + ps3_mgr_cmd_word_build(cmd); + LOG_INFO_IN_IRQ(instance, + "host_no:%u ready send, reqFrameId=%d !\n", + PS3_HOST(instance), ps3_cmd_frame_id(cmd)); + ps3_mgr_print_cmd(cmd, "web event register", PS3_DRV_TRUE); + + web_context->webSubscribe_cmd = cmd; + + ret = ps3_cmd_send_async(instance, cmd, + ps3_webSubscribe_service); + ps3_atomic_dec(&instance->cmd_statistics.cmd_delivering); + if (ret != PS3_SUCCESS) { + web_context->webSubscribe_cmd = NULL; + ps3_mgr_cmd_free(instance, cmd); + goto l_out; + } + } else { + LOG_INFO_IN_IRQ(instance, + "host_no:%u web event already subscribed\n", + PS3_HOST(instance)); + } +l_out: + LOG_DEBUG("host_no:%u exit !\n", PS3_HOST(instance)); + return ret; +} + +static int ps3_scsi_task_abort_sync_proc(struct ps3_instance *instance, + struct ps3_cmd *cmd, + struct ps3_cmd *abort_cmd, + struct ps3_scsi_priv_data *priv_data) +{ + int ret = PS3_SUCCESS; + + cmd->cmd_word_value = abort_cmd->cmd_word_value; + if (cmd->cmd_word.type == PS3_CMDWORD_TYPE_INIT) { + ret = -PS3_FAILED; + goto end; + } + cmd->cmd_word.direct = PS3_CMDWORD_DIRECT_NORMAL; + cmd->cmd_word.cmdFrameID = ps3_cmd_frame_id(cmd); + cmd->cmd_word.type = PS3_CMDWORD_TYPE_ABORT; + cmd->time_out = priv_data->task_abort_timeout; + cmd->is_interrupt = PS3_DRV_FALSE; + + ret = ps3_cmd_send_sync(instance, cmd); +end: + return ret; +} + +static inline void +ps3_mgr_scsi_req_head_init(struct ps3_cmd *cmd, + struct PS3ReqFrameHead *req_header, + unsigned char cmdSubType, unsigned int disk_id, + struct ps3_scsi_priv_data *priv_data) +{ + if (cmdSubType == PS3_TASK_CMD_SCSI_TASK_RESET) + req_header->timeout = priv_data->task_reset_timeout; + else + req_header->timeout = priv_data->task_abort_timeout; + req_header->traceID = ps3_cmd_trace_id(cmd); + req_header->cmdType = PS3_CMD_SCSI_TASK_MANAGEMENT; + req_header->cmdSubType = cmdSubType; + req_header->cmdFrameID = ps3_cmd_frame_id(cmd); + req_header->control = 0; + req_header->devID.diskID = disk_id; +} + +struct ps3_cmd * +ps3_scsi_task_mgr_reset_build(struct ps3_instance *instance, + struct ps3_scsi_priv_data *priv_data) +{ + struct ps3_cmd *cmd = NULL; + struct PS3MgrTaskReqFrame *mgr_task_req_frame = NULL; + unsigned int disk_id = priv_data->disk_pos.diskDev.diskID; + + LOG_DEBUG("host_no:%u enter !\n", PS3_HOST(instance)); + + if ((priv_data->dev_type == PS3_DEV_TYPE_UNKNOWN) || + (disk_id == PS3_INVALID_DEV_ID)) { + LOG_ERROR("host_no:%u unknown dev type !\n", PS3_HOST(instance)); + goto l_out; + } + + cmd = ps3_task_cmd_alloc(instance); + if (cmd == NULL) { + LOG_ERROR("host_no:%u task cmd get NOK !\n", + PS3_HOST(instance)); + goto l_out; + } + + mgr_task_req_frame = &cmd->req_frame->taskReq; + mgr_task_req_frame->taskID = 0; + memset(mgr_task_req_frame, 0, sizeof(*mgr_task_req_frame)); + ps3_mgr_scsi_req_head_init(cmd, &mgr_task_req_frame->reqHead, + PS3_TASK_CMD_SCSI_TASK_RESET, disk_id, + priv_data); + + LOG_WARN( + "host_no:%u ready send reset, CFID:%u t_id:0x%llx [%u:%u:%u]!\n", + PS3_HOST(instance), ps3_cmd_frame_id(cmd), cmd->trace_id, + PS3_CHANNEL(&priv_data->disk_pos), + PS3_TARGET(&priv_data->disk_pos), + PS3_VDID(&priv_data->disk_pos)); + + ps3_mgr_cmd_word_build(cmd); + + if (priv_data->dev_type == PS3_DEV_TYPE_VD) { + cmd->cmd_word.virtDiskID = + (unsigned char) + priv_data->disk_pos.diskDev.ps3Dev.virtDiskID; + } else { + cmd->cmd_word.phyDiskID = + priv_data->disk_pos.diskDev.ps3Dev.phyDiskID; + } + + cmd->time_out = priv_data->task_reset_timeout; + cmd->is_interrupt = PS3_FALSE; + +l_out: + return cmd; +} + +#ifndef _WINDOWS +int ps3_scsi_task_mgr_abort(struct ps3_instance *instance, + struct ps3_scsi_priv_data *priv_data, + unsigned short aborted_cmd_frame_id, + struct scsi_cmnd *scmd) +{ + int ret = PS3_SUCCESS; + int send_result = PS3_SUCCESS; + struct ps3_cmd *cmd = NULL; + struct ps3_cmd *aborted_cmd = NULL; + struct PS3MgrTaskReqFrame *mgr_task_req_frame = NULL; + unsigned int disk_id = priv_data->disk_pos.diskDev.diskID; + + LOG_DEBUG("host_no:%u enter !\n", PS3_HOST(instance)); + if ((priv_data->dev_type == PS3_DEV_TYPE_UNKNOWN) || + (disk_id == PS3_INVALID_DEV_ID)) { + LOG_ERROR("host_no:%u unknown dev type !\n", PS3_HOST(instance)); + ret = -PS3_FAILED; + goto l_out; + } + + aborted_cmd = ps3_cmd_find(instance, aborted_cmd_frame_id); + if (aborted_cmd == NULL || aborted_cmd->scmd == NULL) { + LOG_ERROR("host_no:%u there is no aborted cmd CFID:%u\n", + PS3_HOST(instance), aborted_cmd_frame_id); + + ret = PS3_SUCCESS; + goto l_out; + } + ps3_atomic_inc(&instance->cmd_statistics.cmd_delivering); + if (ps3_mgr_cmd_send_pre_check(instance, PS3_FALSE) != PS3_SUCCESS) { + LOG_WARN_LIM("hno:%u aborted cmd pre check NOK CFID:%d\n", + PS3_HOST(instance), aborted_cmd_frame_id); + ps3_atomic_dec(&instance->cmd_statistics.cmd_delivering); + ret = -PS3_EBUSY; + goto l_out; + } + cmd = ps3_task_cmd_alloc(instance); + if (cmd == NULL) { + LOG_ERROR("host_no:%u task cmd get NOK !\n", + PS3_HOST(instance)); + ps3_atomic_dec(&instance->cmd_statistics.cmd_delivering); + ret = -PS3_EBUSY; + goto l_out; + } + + LOG_DEBUG( + "t_id:0x%llx aborted_t_id:0x%llx host_no:%u mgr abort CFID:%d\n", + cmd->trace_id, aborted_cmd->trace_id, PS3_HOST(instance), + cmd->index); + + mgr_task_req_frame = &cmd->req_frame->taskReq; + memset(mgr_task_req_frame, 0, sizeof(*mgr_task_req_frame)); + + ps3_mgr_scsi_req_head_init(cmd, &mgr_task_req_frame->reqHead, + PS3_TASK_CMD_SCSI_TASK_ABORT, disk_id, + priv_data); + mgr_task_req_frame->taskID = aborted_cmd_frame_id; + mgr_task_req_frame->abortedCmdType = aborted_cmd->cmd_word.direct; + mgr_task_req_frame->reqHead.reqFrameFormat = + PS3_REQFRAME_FORMAT_FRONTEND; + + LOG_WARN( + "host_no:%u ready send abort, CFID:%d t_id:0x%llx aborted cmd info:\n" + "\tCFID:%d t_id:0x%llx op:0x%x is_retry_cmd:%d timeout:%d dev[%u:%u:%u:%u],\n" + "\tcmdword[0x%08llx]{type:%d direct:%d que:%d isr_sn:%d}!\n ", + PS3_HOST(instance), ps3_cmd_frame_id(cmd), cmd->trace_id, + aborted_cmd_frame_id, aborted_cmd->trace_id, + scmd->cmd_len > 0 ? scmd->cmnd[0] : 0xff, scmd->retries, + SCMD_GET_REQUEST(scmd)->timeout, + PS3_CHANNEL(&priv_data->disk_pos), + PS3_TARGET(&priv_data->disk_pos), + PS3_VDID(&priv_data->disk_pos), + priv_data->disk_pos.diskMagicNum, aborted_cmd->cmd_word_value, + aborted_cmd->cmd_word.type, aborted_cmd->cmd_word.direct, + aborted_cmd->cmd_word.qMask, aborted_cmd->cmd_word.isrSN); + + ps3_scsih_print_req(aborted_cmd, LEVEL_INFO); + + send_result = ps3_scsi_task_abort_sync_proc(instance, cmd, aborted_cmd, + priv_data); + ps3_atomic_dec(&instance->cmd_statistics.cmd_delivering); + if (send_result == PS3_SUCCESS) + send_result = ps3_cmd_wait_sync(instance, cmd); + ret = ps3_mgr_complete_proc(instance, cmd, send_result); + LOG_WARN( + "host_no:%u abort finish, CFID:%d aborted CFID:%d aborted cmdword[0x%08llx],\n" + "\t[%u:%u:%u:%u]!\n", + PS3_HOST(instance), ps3_cmd_frame_id(cmd), aborted_cmd_frame_id, + aborted_cmd->cmd_word_value, PS3_CHANNEL(&priv_data->disk_pos), + PS3_TARGET(&priv_data->disk_pos), + PS3_VDID(&priv_data->disk_pos), + priv_data->disk_pos.diskMagicNum); + +l_out: + if ((ret != -PS3_CMD_NO_RESP) && (cmd != NULL)) + ps3_task_cmd_free(instance, cmd); + LOG_WARN("host_no:%u exit !, ret:%d\n", PS3_HOST(instance), ret); + return ret; +} +#endif + +#ifndef _WINDOWS +static inline void ps3_sas_info_reqframe_build(struct ps3_cmd *cmd, + enum PS3MgrCmdSubType sub_type, + dma_addr_t *sge_addr, + struct PS3SasMgr *sas_req) +{ + struct PS3MgrReqFrame *mgrReq = &cmd->req_frame->mgrReq; + + mgrReq->reqHead.timeout = PS3_DEFAULT_MGR_CMD_TIMEOUT; + mgrReq->reqHead.traceID = cmd->trace_id; + mgrReq->reqHead.cmdType = PS3_CMD_SAS_MANAGEMENT; + mgrReq->reqHead.cmdSubType = sub_type; + mgrReq->reqHead.cmdFrameID = cmd->index; + mgrReq->reqHead.control = 0; + mgrReq->syncFlag = 1; + mgrReq->timeout = 0; + mgrReq->sgeOffset = offsetof(struct PS3MgrReqFrame, sgl) >> + PS3_MGR_CMD_SGL_OFFSET_DWORD_SHIFT; + mgrReq->sgeCount = 1; + mgrReq->sgl[0].length = cpu_to_le32(PS3_SAS_REQ_BUFF_LEN); + mgrReq->sgl[0].addr = cpu_to_le64(*sge_addr); + mgrReq->sgl[0].lastSge = 1; + mgrReq->sgl[0].ext = 0; + + if (sas_req != NULL) { + mgrReq->value.sasMgr.sasAddr = cpu_to_le64(sas_req->sasAddr); + mgrReq->value.sasMgr.enclID = sas_req->enclID; + mgrReq->value.sasMgr.startPhyID = sas_req->startPhyID; + mgrReq->value.sasMgr.phyCount = sas_req->phyCount; + } +} + +static int __ps3_sas_info_get(struct ps3_instance *instance, + enum PS3MgrCmdSubType sub_type, + dma_addr_t *sge_addr, struct PS3SasMgr *sas_req) +{ + int ret = 0; + int send_result = PS3_SUCCESS; + struct ps3_cmd *cmd = NULL; + + ps3_atomic_inc(&instance->cmd_statistics.cmd_delivering); + if (ps3_mgr_cmd_send_pre_check(instance, PS3_FALSE) != PS3_SUCCESS) { + LOG_WARN_LIM("hno:%u sas info cmd pre check NOK\n", + PS3_HOST(instance)); + ps3_atomic_dec(&instance->cmd_statistics.cmd_delivering); + ret = -PS3_FAILED; + goto l_out; + } + cmd = ps3_mgr_cmd_alloc(instance); + if (cmd == NULL) { + LOG_WARN("host_no:%u not get a cmd packet\n", + PS3_HOST(instance)); + ps3_atomic_dec(&instance->cmd_statistics.cmd_delivering); + ret = -PS3_FAILED; + goto l_out; + } + + ps3_sas_info_reqframe_build(cmd, sub_type, sge_addr, sas_req); + ps3_mgr_cmd_word_build(cmd); + + LOG_INFO("host_no:%u ready send t_id:0x%llx CFID:%u req type:%s\n", + PS3_HOST(instance), cmd->trace_id, cmd->index, + namePS3MgrCmdSubType((enum PS3MgrCmdSubType)sub_type)); + + send_result = ps3_mgr_cmd_sync_proc(instance, cmd, + PS3_DEFAULT_MGR_CMD_TIMEOUT); + ps3_atomic_dec(&instance->cmd_statistics.cmd_delivering); + if (send_result == PS3_SUCCESS) { + send_result = ps3_cmd_wait_sync(instance, cmd); + ps3_mgr_print_cmd(cmd, namePS3MgrCmdSubType(sub_type), + PS3_DRV_FALSE); + } + ret = ps3_mgr_complete_proc(instance, cmd, send_result); + if (ret != PS3_SUCCESS) { + LOG_ERROR( + "host_no:%u %d respStatus NOK CFID:%d respStatus:%d\n", + PS3_HOST(cmd->instance), ret, cmd->cmd_word.cmdFrameID, + ps3_cmd_resp_status(cmd)); + } + + LOG_INFO("host_no:%u t_id:0x%llx CFID :%u type:%s end, ret:%d\n", + PS3_HOST(instance), cmd->trace_id, cmd->index, + namePS3MgrCmdSubType((enum PS3MgrCmdSubType)sub_type), ret); +l_out: + if ((ret != -PS3_CMD_NO_RESP) && (cmd != NULL)) + ps3_mgr_cmd_free(instance, cmd); + + return ret; +} + +int ps3_sas_expander_all_get(struct ps3_instance *instance) +{ + LOG_DEBUG("host_no:%u\n", PS3_HOST(instance)); + + return __ps3_sas_info_get( + instance, PS3_SAS_GET_EXPANDERS, + &instance->sas_dev_context.ps3_sas_buff_dma_addr, NULL); +} + +int ps3_sas_phy_get(struct ps3_instance *instance, struct PS3SasMgr *sas_req) +{ + LOG_DEBUG("host_no:%u\n", PS3_HOST(instance)); + + if (sas_req->phyCount == 0) { + LOG_WARN("host_no:%u unexpect phyCount:%d !\n", + PS3_HOST(instance), sas_req->phyCount); + return -PS3_FAILED; + } + return __ps3_sas_info_get( + instance, PS3_SAS_GET_PHY_INFO, + &instance->sas_dev_context.ps3_sas_phy_buff_dma_addr, sas_req); +} + +int ps3_sas_expander_get(struct ps3_instance *instance, + struct PS3SasMgr *sas_req) +{ + LOG_DEBUG("host_no:%u\n", PS3_HOST(instance)); + + return __ps3_sas_info_get( + instance, PS3_SAS_GET_EXPANDER_INFO, + &instance->sas_dev_context.ps3_sas_buff_dma_addr, sas_req); +} +#endif +static int ps3_mgr_cmd_sync_proc(struct ps3_instance *instance, + struct ps3_cmd *cmd, unsigned short time_out) +{ + enum PS3MgrCmdSubType sub_type = + (enum PS3MgrCmdSubType)PS3_MGR_CMD_SUBTYPE(cmd); + + cmd->time_out = time_out; + cmd->is_interrupt = PS3_DRV_FALSE; + ps3_mgr_cmd_word_build(cmd); + + ps3_mgr_print_cmd(cmd, namePS3MgrCmdSubType(sub_type), PS3_DRV_TRUE); + + + return ps3_cmd_send_sync(instance, cmd); +} + +static int ps3_no_resp_cmd_dead_proc(struct ps3_instance *instance, + struct ps3_cmd *cmd) +{ + unsigned long flags = 0; + int ret = PS3_SUCCESS; + + ps3_spin_lock_irqsave(&cmd->cmd_state.lock, &flags); + if (cmd->cmd_state.state == PS3_CMD_STATE_INIT || + cmd->cmd_state.state == PS3_CMD_STATE_COMPLETE) { + ps3_spin_unlock_irqrestore(&cmd->cmd_state.lock, flags); + LOG_WARN("host_no:%u CFID:%d finished\n", PS3_HOST(instance), + cmd->index); + ret = -PS3_FAILED; + goto l_out; + } + + cmd->cmd_state.state = PS3_CMD_STATE_DEAD; + ps3_spin_unlock_irqrestore(&cmd->cmd_state.lock, flags); + LOG_WARN("host_no:%u cmd_dead_proc entry reset due to cmd not resp!\n", + PS3_HOST(instance)); + + if (!instance->is_probe_finish || !instance->state_machine.is_load) { + ps3_need_wait_hard_reset_request(instance); + ret = ps3_hard_recovery_request_with_retry(instance); + if (ret == PS3_SUCCESS) { + ps3_instance_wait_for_dead_or_pre_operational(instance); + } else { + LOG_WARN("host_no:%u hard recovery request NOK\n", + PS3_HOST(instance)); + } + + } else { + ps3_need_wait_hard_reset_request(instance); + ps3_recovery_request_with_retry(instance); + } + + LOG_ERROR("t_id:0x%llx CFID:%d host_no:%u state %u, recovery request\n", + cmd->trace_id, cmd->index, PS3_HOST(instance), + cmd->cmd_state.state); + + ret = -PS3_CMD_NO_RESP; +l_out: + return ret; +} + +static int ps3_no_resp_cmd_wait_proc(struct ps3_instance *instance, + struct ps3_cmd *cmd) +{ + unsigned long flags = 0; + int ret = PS3_SUCCESS; + unsigned char is_probe_finish = PS3_FALSE; + + ps3_spin_lock_irqsave(&cmd->cmd_state.lock, &flags); + if (cmd->cmd_state.state == PS3_CMD_STATE_INIT || + cmd->cmd_state.state == PS3_CMD_STATE_COMPLETE) { + ps3_spin_unlock_irqrestore(&cmd->cmd_state.lock, flags); + LOG_WARN("host_no:%u CFID:%d finished, retries:%d\n", + PS3_HOST(instance), cmd->index, cmd->retry_cnt); + ret = -PS3_FAILED; + goto l_out; + } else { + if (cmd->req_frame->mgrReq.reqHead.noReplyWord == + PS3_CMD_WORD_NEED_REPLY_WORD) { + init_completion(&cmd->sync_done); + } + ps3_spin_unlock_irqrestore(&cmd->cmd_state.lock, flags); + } + LOG_WARN( + "host_no:%u cmd_wait_proc entry reset due to cmd not resp, CFID:%d t_id:0x%llx !\n", + PS3_HOST(instance), cmd->index, cmd->trace_id); + + is_probe_finish = instance->is_probe_finish; + ps3_need_wait_hard_reset_request(instance); + if (!is_probe_finish) + ret = ps3_hard_recovery_request_with_retry(instance); + else + ret = ps3_recovery_request_with_retry(instance); + + if (ret == -PS3_FAILED) { + ret = -PS3_CMD_NO_RESP; + LOG_ERROR("host_no:%u recovery request NOK\n", + PS3_HOST(instance)); + goto l_dead; + } + + if (!is_probe_finish) + ps3_instance_wait_for_dead_or_pre_operational(instance); + + LOG_WARN("host_no:%u CFID:%d wait again, retries:%d\n", + PS3_HOST(instance), cmd->index, cmd->retry_cnt); + + if (cmd->req_frame->mgrReq.reqHead.noReplyWord == + PS3_CMD_WORD_NEED_REPLY_WORD) { + ret = ps3_block_cmd_wait(instance, cmd, 0); + } else { + ret = ps3_cmd_reply_polling_when_recovery(instance, cmd, 0); + } + + ret = ps3_err_mgr_cmd_proc(instance, ret, cmd); + if (ret == -PS3_CMD_NO_RESP) { + LOG_WARN("host_no:%u CFID:%d wait again, retries:%d\n", + PS3_HOST(instance), cmd->index, cmd->retry_cnt); + } +l_dead: + ps3_spin_lock_irqsave(&cmd->cmd_state.lock, &flags); + if (cmd->cmd_state.state != PS3_CMD_STATE_INIT && + cmd->cmd_state.state != PS3_CMD_STATE_COMPLETE) { + LOG_FILE_ERROR("host_no:%u CFID:%d is dead\n", + PS3_HOST(instance), cmd->index); + cmd->cmd_state.state = PS3_CMD_STATE_DEAD; + } + ps3_spin_unlock_irqrestore(&cmd->cmd_state.lock, flags); + +l_out: + LOG_INFO("host_no:%u noresp cmd deal ret:%d\n", PS3_HOST(instance), + ret); + return ret; +} +static int ps3_no_resp_scsi_task_cmd_wait_proc(struct ps3_instance *instance, + struct ps3_cmd *cmd) +{ + unsigned long flags = 0; + int ret = PS3_SUCCESS; + + ps3_spin_lock_irqsave(&cmd->cmd_state.lock, &flags); + if (cmd->cmd_state.state == PS3_CMD_STATE_INIT || + cmd->cmd_state.state == PS3_CMD_STATE_COMPLETE) { + ps3_spin_unlock_irqrestore(&cmd->cmd_state.lock, flags); + LOG_WARN("host_no:%u CFID:%d finished, retries:%d\n", + PS3_HOST(instance), cmd->index, cmd->retry_cnt); + ret = -PS3_FAILED; + goto l_out; + } else { + if (cmd->req_frame->frontendReq.reqHead.noReplyWord == + PS3_CMD_WORD_NEED_REPLY_WORD) { + init_completion(&cmd->sync_done); + } + ps3_spin_unlock_irqrestore(&cmd->cmd_state.lock, flags); + } + + ps3_need_wait_hard_reset_request(instance); + LOG_WARN( + "host_no:%u scsi_task_cmd_wait_proc entry reset due to cmd not resp, CFID:%d t_id:0x%llx !\n", + PS3_HOST(instance), cmd->index, cmd->trace_id); + ret = ps3_hard_recovery_request_with_retry(instance); + if (ret == -PS3_FAILED) { + ret = -PS3_CMD_NO_RESP; + LOG_ERROR("host_no:%u hard recovery request NOK\n", + PS3_HOST(instance)); + goto l_dead; + } + + if (cmd->req_frame->frontendReq.reqHead.noReplyWord == + PS3_CMD_WORD_NEED_REPLY_WORD) { + ret = ps3_block_cmd_wait( + instance, cmd, + PS3_HARD_RESET_FORCE_STOP_MAX_TIME(instance)); + } else { + ret = ps3_cmd_reply_polling_when_recovery( + instance, cmd, + PS3_HARD_RESET_FORCE_STOP_MAX_TIME(instance)); + } + ret = ps3_err_mgr_cmd_proc(instance, ret, cmd); + if (ret == -PS3_CMD_NO_RESP) { + LOG_ERROR( + "host_no:%u hard recovery request,cmd timeout again\n", + PS3_HOST(instance)); + goto l_dead; + } else { + goto l_out; + } + +l_dead: + ps3_spin_lock_irqsave(&cmd->cmd_state.lock, &flags); + if (cmd->cmd_state.state != PS3_CMD_STATE_INIT && + cmd->cmd_state.state != PS3_CMD_STATE_COMPLETE) { + LOG_FILE_ERROR("host_no:%u CFID:%d is dead\n", + PS3_HOST(instance), cmd->index); + cmd->cmd_state.state = PS3_CMD_STATE_DEAD; + } + ps3_spin_unlock_irqrestore(&cmd->cmd_state.lock, flags); + +l_out: + LOG_INFO("host_no:%u noresp cmd deal ret:%d\n", PS3_HOST(instance), + ret); + return ret; +} + +int ps3_mgr_cmd_no_resp_proc(struct ps3_instance *instance, struct ps3_cmd *cmd) +{ + int ret = PS3_SUCCESS; + + switch (PS3_MGR_CMD_TYPE(cmd)) { + case PS3_CMD_MANAGEMENT: + ret = ps3_no_resp_cmd_dead_proc(instance, cmd); + break; + case PS3_CMD_SCSI_TASK_MANAGEMENT: + ret = ps3_no_resp_scsi_task_cmd_wait_proc(instance, cmd); + break; + case PS3_CMD_IOCTL: + ret = ps3_no_resp_cmd_wait_proc(instance, cmd); + break; + case PS3_CMD_SAS_MANAGEMENT: + if ((PS3_MGR_CMD_SUBTYPE(cmd) == PS3_SAS_SMP_REQUEST) || + (PS3_MGR_CMD_SUBTYPE(cmd) == PS3_SAS_GET_LINK_ERR) || + (PS3_MGR_CMD_SUBTYPE(cmd) == PS3_SAS_PHY_CTRL)) { + ret = ps3_no_resp_cmd_wait_proc(instance, cmd); + } else { + ret = ps3_no_resp_cmd_dead_proc(instance, cmd); + } + break; + default: + LOG_ERROR( + "host_no:%u UNEXPEXT!!!! no response proc cmd_type:%d, sub_type:%d\n", + PS3_HOST(instance), PS3_MGR_CMD_TYPE(cmd), + PS3_MGR_CMD_SUBTYPE(cmd)); + ret = -PS3_FAILED; + break; + } + + return ret; +} + +int ps3_mgr_complete_proc(struct ps3_instance *instance, struct ps3_cmd *cmd, + int send_result) +{ + int ret = PS3_SUCCESS; + unsigned long flags = 0; + + while (cmd->retry_cnt < PS3_ERR_MGR_CMD_FAULT_RETRY_MAX) { + if (send_result == PS3_SUCCESS) { + LOG_FILE_INFO( + "host_no:%u externel mgr CFID:%d respStatus:%d success\n", + PS3_HOST(instance), cmd->index, + ps3_cmd_resp_status(cmd)); + ret = PS3_SUCCESS; + break; + } + + if (send_result == -PS3_RECOVERED) { + LOG_INFO( + "host_no:%u mgr CFID:%d respStatus:%d cannot send because recovery process\n", + PS3_HOST(instance), cmd->index, + ps3_cmd_resp_status(cmd)); + ret = -PS3_RECOVERED; + break; + } + + ret = ps3_err_mgr_cmd_proc(instance, send_result, cmd); + if (ret == -PS3_CMD_NO_RESP) + ret = ps3_mgr_cmd_no_resp_proc(instance, cmd); + + if (ret != -PS3_RETRY) { + if ((PS3_MGR_CMD_TYPE(cmd) == PS3_CMD_MANAGEMENT) && + (PS3_MGR_CMD_SUBTYPE(cmd) == + PS3_MGR_CMD_DEV_DEL_DONE)) { + LOG_FILE_INFO( + "host_no:%u CFID:%d retry, retries:%d, ret:%d\n", + PS3_HOST(instance), cmd->index, + cmd->retry_cnt, ret); + } else { + LOG_INFO( + "host_no:%u cmd:%s CFID:%d retry, retries:%d, ret:%d\n", + PS3_HOST(instance), + namePS3MgrCmdSubType( + (enum PS3MgrCmdSubType) + PS3_MGR_CMD_SUBTYPE( + cmd)), + cmd->index, cmd->retry_cnt, ret); + } + break; + } + + cmd->retry_cnt++; + if (cmd->retry_cnt >= PS3_ERR_MGR_CMD_FAULT_RETRY_MAX) + break; + LOG_INFO("host_no:%u mgr CFID:%d retry:%u\n", + PS3_HOST(instance), cmd->index, cmd->retry_cnt); + ps3_atomic_inc(&instance->cmd_statistics.cmd_delivering); + send_result = ps3_mgr_cmd_send_check(instance, cmd); + if (send_result != PS3_SUCCESS) { + ps3_atomic_dec( + &instance->cmd_statistics.cmd_delivering); + continue; + } + cmd->resp_frame->normalRespFrame.respStatus = 0xff; + init_completion(&cmd->sync_done); + ps3_ioctl_buff_bit_pos_update(cmd); + ps3_spin_lock_irqsave(&cmd->cmd_state.lock, &flags); + cmd->cmd_state.state = PS3_CMD_STATE_PROCESS; + ps3_spin_unlock_irqrestore(&cmd->cmd_state.lock, flags); + send_result = ps3_cmd_send_sync(instance, cmd); + ps3_atomic_dec(&instance->cmd_statistics.cmd_delivering); + if (send_result == PS3_SUCCESS) + send_result = ps3_cmd_wait_sync(instance, cmd); + }; + + if (cmd->retry_cnt >= PS3_ERR_MGR_CMD_FAULT_RETRY_MAX) { + ret = -PS3_FAILED; + LOG_ERROR("host_no:%u cmd request NOK.\n", PS3_HOST(instance)); + } + + return ret; +} + +unsigned char +ps3_check_ioc_state_is_normal_in_unload(struct ps3_instance *instance) +{ + unsigned char ret = PS3_TRUE; + unsigned int ioc_state = PS3_FW_STATE_UNDEFINED; + + if (instance->state_machine.is_load) + goto l_out; + + if (!ps3_ioc_state_get_with_check(instance, &ioc_state)) { + ret = PS3_FALSE; + goto l_out; + } + + if (ioc_state != PS3_FW_STATE_RUNNING) { + LOG_ERROR("host_no:%d ioc_state:%d is not running.\n", + PS3_HOST(instance), ioc_state); + ret = PS3_FALSE; + goto l_out; + } +l_out: + return ret; +} diff --git a/drivers/scsi/linkdata/ps3stor/ps3_mgr_cmd.h b/drivers/scsi/linkdata/ps3stor/ps3_mgr_cmd.h new file mode 100644 index 0000000000000000000000000000000000000000..778dd7bc178d66886b9b3a219a126d297aeeabdc --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/ps3_mgr_cmd.h @@ -0,0 +1,95 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _PS3_MANAGEMENT_CMD_H_ +#define _PS3_MANAGEMENT_CMD_H_ + +#include "ps3_instance_manager.h" +#include "ps3_device_manager.h" + +#define PS3_MGR_BASE_DATA_SIZE (64) + +#define PS3_MGR_CMD_SGL_OFFSET_DWORD_SHIFT (2) + +#define PS3_MGR_CMD_TYPE(cmd) ((cmd)->req_frame->mgrReq.reqHead.cmdType) +#define PS3_MGR_CMD_SUBTYPE(cmd) ((cmd)->req_frame->mgrReq.reqHead.cmdSubType) +enum { + PS3_CANCEL_EVENT_CMD = 1, + PS3_CANCEL_VDPENDING_CMD, + PS3_CANCEL_WEB_CMD, +}; +int ps3_ctrl_info_buf_alloc(struct ps3_instance *instance); + +void ps3_ctrl_info_buf_free(struct ps3_instance *instance); + +int ps3_mgr_cmd_init(struct ps3_instance *instance); + +void ps3_mgr_cmd_exit(struct ps3_instance *instance); + +int ps3_pd_list_get(struct ps3_instance *instance); + +int ps3_vd_list_get(struct ps3_instance *instance); + +int ps3_pd_info_get(struct ps3_instance *instance, unsigned short channel, + unsigned short target_id, unsigned short pd_disk_id); + +int ps3_vd_info_sync_get(struct ps3_instance *instance, unsigned int disk_id, + unsigned short vd_num); + +int ps3_vd_info_async_get(struct ps3_instance *instance); + +int ps3_ctrl_info_get(struct ps3_instance *instance); + +int ps3_soc_unload(struct ps3_instance *instance, unsigned char is_polling, + unsigned char type, unsigned char suspend_type); + +int ps3_scsi_remove_device_done(struct ps3_instance *instance, + struct PS3DiskDevPos *disk_pos, + unsigned char dev_type); + +int ps3_scsi_add_device_ack(struct ps3_instance *instance, + struct PS3DiskDevPos *disk_pos, + unsigned char dev_type); + +int ps3_mgr_cmd_cancel(struct ps3_instance *instance, + unsigned short cancel_cmd_frame_id); + +int ps3_event_register(struct ps3_instance *instance, + struct PS3MgrEvent *event); + +int ps3_web_register(struct ps3_instance *instance); + +int ps3_scsi_task_mgr_abort(struct ps3_instance *instance, + struct ps3_scsi_priv_data *priv_data, + unsigned short aborted_cmd_frame_id, + struct scsi_cmnd *scmd); + +void ps3_mgr_cmd_word_build(struct ps3_cmd *cmd); + +int ps3_sas_expander_all_get(struct ps3_instance *instance); + +int ps3_sas_phy_get(struct ps3_instance *instance, struct PS3SasMgr *sas_req); + +int ps3_sas_expander_get(struct ps3_instance *instance, + struct PS3SasMgr *sas_req); + +int ps3_mgr_complete_proc(struct ps3_instance *instance, struct ps3_cmd *cmd, + int send_result); + +struct ps3_cmd *ps3_dump_notify_cmd_build(struct ps3_instance *instance); + +struct ps3_cmd * +ps3_scsi_task_mgr_reset_build(struct ps3_instance *instance, + struct ps3_scsi_priv_data *priv_data); + +int ps3_mgr_cmd_no_resp_proc(struct ps3_instance *instance, + struct ps3_cmd *cmd); + +unsigned char +ps3_check_ioc_state_is_normal_in_unload(struct ps3_instance *instance); + +int ps3_mgr_cmd_cancel_send(struct ps3_instance *instance, + unsigned short cancel_cmd_frame_id, + unsigned char type); + +int ps3_mgr_cmd_cancel_wait(struct ps3_instance *instance, unsigned char type); + +#endif diff --git a/drivers/scsi/linkdata/ps3stor/ps3_mgr_cmd_err.c b/drivers/scsi/linkdata/ps3stor/ps3_mgr_cmd_err.c new file mode 100644 index 0000000000000000000000000000000000000000..145e63eaf3a3de79013e484c0a0d419d7b010fed --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/ps3_mgr_cmd_err.c @@ -0,0 +1,268 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "ps3_mgr_cmd_err.h" + +#ifndef _WINDOWS +#include +#include +#include +#include +#include + +#include +#endif + +#include "ps3_htp.h" + +#include "ps3_mgr_channel.h" +#include "ps3_mgr_cmd.h" +#include "ps3_driver_log.h" +#include "ps3_recovery.h" + +enum ps3_fault_action { + PS3_FAULT_STRATEGY_NONE = 0, + PS3_FAULT_STRATEGY_RETRY, +}; + +enum ps3_reset_type { + PS3_RESET_TYPE_SOFT, + PS3_RESET_TYPE_HARD, +}; + +struct ps3_fault_strategy { + unsigned int resp_status; + enum ps3_fault_action fault_action; +}; + +static struct ps3_fault_strategy ps3_fault_strategy_desc[] = { + { PS3_DRV_MGR_UNRUNING, PS3_FAULT_STRATEGY_NONE }, + { PS3_DRV_MGR_INVAL_CMD, PS3_FAULT_STRATEGY_NONE }, + { PS3_DRV_MGR_NORESOURCE, PS3_FAULT_STRATEGY_RETRY }, + { PS3_DRV_MGR_INVAL_PARAM, PS3_FAULT_STRATEGY_NONE }, + { PS3_DRV_MGR_DEV_NOEXIST, PS3_FAULT_STRATEGY_NONE }, + { PS3_DRV_MGR_DEV_DATA_ERR, PS3_FAULT_STRATEGY_NONE }, + { PS3_DRV_MGR_EVT_REPEAT, PS3_FAULT_STRATEGY_NONE }, + { PS3_DRV_MGR_EVT_CANCEL_ERR, PS3_FAULT_STRATEGY_NONE }, + { PS3_DRV_MGR_FLUSH_FAILED, PS3_FAULT_STRATEGY_RETRY }, + { PS3_DRV_MGR_BUSY, PS3_FAULT_STRATEGY_RETRY }, + { PS3_DRV_MGR_TIMEOUT, PS3_FAULT_STRATEGY_RETRY }, + { PS3_DRV_MGR_SMP_BACKEND_ERR, PS3_FAULT_STRATEGY_NONE }, + { PS3_DRV_MGR_LINK_GET_BACKEND_ERR, PS3_FAULT_STRATEGY_NONE }, + { PS3_DRV_MGR_PHY_CTL_BACKEND_ERR, PS3_FAULT_STRATEGY_NONE }, + { PS3_DRV_MGR_RESTART_COMMAND_RSP, PS3_FAULT_STRATEGY_NONE }, + { PS3_DRV_MGR_TM_FAILED, PS3_FAULT_STRATEGY_NONE }, + { U32_MAX, PS3_FAULT_STRATEGY_NONE } +}; + +static inline const char *ps3_err_action_print(enum ps3_fault_action action) +{ + static const char * const action_string[] = { + [PS3_FAULT_STRATEGY_NONE] = "PS3_FAULT_STRATEGY_NONE", + [PS3_FAULT_STRATEGY_RETRY] = "PS3_FAULT_STRATEGY_RETRY" + }; + + return action_string[action]; +} + +const char *ps3_err_mgr_fault_proc_result_print(int result) +{ + const char *proc_result_string = NULL; + + switch (result) { + case PS3_SUCCESS: + proc_result_string = "SUCCESS"; + break; + case -PS3_RETRY: + proc_result_string = "NEED RETRY"; + break; + case -PS3_FAILED: + proc_result_string = "FAILED"; + break; + case -PS3_TIMEOUT: + proc_result_string = "TIMEOUT"; + break; + case -PS3_RESP_ERR: + proc_result_string = "RESPONSE ERROR"; + break; + case -PS3_RESP_INT: + proc_result_string = "RESPONSE INTERRUPTED"; + break; + case -PS3_CMD_NO_RESP: + proc_result_string = "PS3_CMD_NO_RESP"; + break; + default: + proc_result_string = "INVALID RESULT"; + break; + } + + return proc_result_string; +} + +static enum ps3_fault_action +ps3_err_fault_strategy_lookup(struct ps3_instance *instance, + unsigned int resp_status, unsigned int retry_cnt) +{ + unsigned int idx = 0; + enum ps3_fault_action fault_action = PS3_FAULT_STRATEGY_RETRY; + + for (idx = 0; idx < ARRAY_SIZE(ps3_fault_strategy_desc); idx++) { + if (ps3_fault_strategy_desc[idx].resp_status == resp_status) { + fault_action = + ps3_fault_strategy_desc[idx].fault_action; + break; + } + } + + if ((fault_action == PS3_FAULT_STRATEGY_RETRY) && (retry_cnt > 0)) + fault_action = PS3_FAULT_STRATEGY_NONE; + + LOG_INFO("host_no:%u error_no:%d fault_action is %s!\n", + PS3_HOST(instance), resp_status, + ps3_err_action_print(fault_action)); + return fault_action; +} + +static int ps3_err_fault_strategy_exec(struct ps3_instance *instance, + enum ps3_fault_action fault_action) +{ + int proc_result = -PS3_FAILED; + + switch (fault_action) { + case PS3_FAULT_STRATEGY_RETRY: + ps3_msleep(PS3_ERR_MGR_CMD_DELAY_TIME_BEFORE_RERTY); + proc_result = -PS3_RETRY; + break; + default: + break; + } + + LOG_INFO("host_no:%u proc result for mgr cmd fault is %s!\n", + PS3_HOST(instance), + ps3_err_mgr_fault_proc_result_print(proc_result)); + return proc_result; +} + +int ps3_err_mgr_cmd_failed_check(struct ps3_instance *instance, + struct ps3_cmd *cmd) +{ + int ret = PS3_SUCCESS; + enum ps3_fault_action fault_action = PS3_FAULT_STRATEGY_NONE; + unsigned int resp_status = cmd->resp_frame->normalRespFrame.respStatus; + + if (!ps3_is_instance_state_allow_cmd_execute(instance)) { + ret = -PS3_FAILED; + goto l_out; + } + + fault_action = ps3_err_fault_strategy_lookup(instance, resp_status, + cmd->retry_cnt); + ret = ps3_err_fault_strategy_exec(instance, fault_action); +l_out: + return ret; +} + +void ps3_err_fault_context_init(struct ps3_instance *instance) +{ + struct ps3_fault_context *fault_context = &instance->fault_context; + + memset(fault_context, 0, sizeof(*fault_context)); + ps3_atomic_set(&instance->is_err_scsi_processing, 0); +} + +void ps3_err_fault_context_exit(struct ps3_instance *instance) +{ + struct ps3_fault_context *fault_context = &instance->fault_context; + + memset(fault_context, 0, sizeof(*fault_context)); +} + +int ps3_err_mgr_cmd_proc(struct ps3_instance *instance, int fault_type, + struct ps3_cmd *cmd) +{ + int proc_result = PS3_SUCCESS; + int cur_state = PS3_INSTANCE_STATE_INIT; + + LOG_INFO("host_no:%u fault_type: %s, retries:%d!\n", PS3_HOST(instance), + ps3_err_mgr_fault_proc_result_print(fault_type), + cmd->retry_cnt); + + switch (fault_type) { + case PS3_SUCCESS: + break; + case -PS3_RESP_ERR: + if (PS3_MGR_CMD_TYPE(cmd) == PS3_CMD_IOCTL) { + LOG_INFO( + "host_no:%u CFID:%d trace_id:0x%llx cli return err:%u\n", + PS3_HOST(instance), cmd->cmd_word.cmdFrameID, + cmd->trace_id, ps3_cmd_resp_status(cmd)); + if (instance->is_probe_finish) { + if (ps3_cmd_resp_status(cmd) == + PS3_DRV_MGR_BUSY) { + LOG_INFO( + "host_no:%u CFID:%d trace_id:0x%llx cli return busy\n", + PS3_HOST(instance), + cmd->cmd_word.cmdFrameID, + cmd->trace_id); + if (ps3_instance_wait_for_normal( + instance) == PS3_SUCCESS) { + cmd->req_frame->mgrReq.value + .isRetry = PS3_TRUE; + proc_result = -PS3_RETRY; + } else { + proc_result = -PS3_FAILED; + } + } else if (ps3_cmd_resp_status(cmd) == + PS3_DRV_MGR_RESTART_COMMAND_RSP) { + LOG_WARN( + "host_no:%u CFID:%d trace_id:0x%llx cli cx restart return success\n", + PS3_HOST(instance), + cmd->cmd_word.cmdFrameID, + cmd->trace_id); + ps3_need_wait_hard_reset_request( + instance); + if (ps3_recovery_state_wait_for_normal( + instance) != PS3_SUCCESS) { + proc_result = -PS3_FAILED; + } else if (ps3_hard_recovery_request_with_retry(instance) + != PS3_SUCCESS) { + LOG_WARN( + "host_no:%u hard recovery request NOK\n", + PS3_HOST(instance)); + } + } else { + cur_state = ps3_atomic_read( + &instance->state_machine.state); + if (cur_state == + PS3_INSTANCE_STATE_QUIT || + cur_state == + PS3_INSTANCE_STATE_DEAD) { + proc_result = -PS3_ENODEV; + } else { + proc_result = -PS3_FAILED; + } + } + } else { + proc_result = -PS3_FAILED; + } + } else { + proc_result = + ps3_err_mgr_cmd_failed_check(instance, cmd); + } + break; + case -PS3_TIMEOUT: + proc_result = -PS3_CMD_NO_RESP; + break; + default: + proc_result = -PS3_FAILED; + break; + } + + if (proc_result != -PS3_CMD_NO_RESP) + cmd->cmd_state.reset_flag = 0; + + LOG_INFO("host_no:%u proc_result: %s, retries:%d!\n", + PS3_HOST(instance), + ps3_err_mgr_fault_proc_result_print(proc_result), + cmd->retry_cnt); + + return proc_result; +} diff --git a/drivers/scsi/linkdata/ps3stor/ps3_mgr_cmd_err.h b/drivers/scsi/linkdata/ps3stor/ps3_mgr_cmd_err.h new file mode 100644 index 0000000000000000000000000000000000000000..6fca182f7fcac4da77cf0a100711f06df9dae54a --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/ps3_mgr_cmd_err.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _PS3_MGR_CMD_ERR_H_ +#define _PS3_MGR_CMD_ERR_H_ + +#ifndef _WINDOWS +#include +#include +#include "linux/kernel.h" +#endif + +#include "ps3_htp_def.h" +#include "ps3_instance_manager.h" + +#define PS3_ERR_MGR_CMD_FAULT_RETRY_MAX (3) +#define PS3_ERR_MGR_CMD_DELAY_TIME_BEFORE_RERTY (500) + +void ps3_err_fault_context_init(struct ps3_instance *instance); +void ps3_err_fault_context_exit(struct ps3_instance *instance); + +int ps3_err_mgr_cmd_proc(struct ps3_instance *instance, int fault_type, + struct ps3_cmd *cmd); + +int ps3_err_mgr_cmd_failed_check(struct ps3_instance *instance, + struct ps3_cmd *cmd); + +const char *ps3_err_mgr_fault_proc_result_print(int result); + +#endif diff --git a/drivers/scsi/linkdata/ps3stor/ps3_module_para.c b/drivers/scsi/linkdata/ps3stor/ps3_module_para.c new file mode 100644 index 0000000000000000000000000000000000000000..b5e8bcb4b09f5221e8666d8ad135855c13bb6c47 --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/ps3_module_para.c @@ -0,0 +1,629 @@ +// SPDX-License-Identifier: GPL-2.0 +#ifndef _WINDOWS +#include "linux/moduleparam.h" +#endif +#include "ps3_inner_data.h" +#include "ps3_module_para.h" +#include "ps3_driver_log.h" +#include "ps3_drv_ver.h" +#include "ps3_ioc_state.h" +#include "ps3_kernel_version.h" + +#ifndef _WINDOWS +static unsigned int cli_ver = PS3_IOCTL_VERSION; +module_param(cli_ver, uint, 0444); +MODULE_PARM_DESC(cli_ver, + "The version for communication between driver and CLI"); +#endif + +static unsigned int g_throttle_que_depth = PS3_DEVICE_QDEPTH_DEFAULT_VALUE; +#ifndef _WINDOWS +module_param(g_throttle_que_depth, uint, 0644); +MODULE_PARM_DESC( + g_throttle_que_depth, + "IOC queue depth when throttled due to SCSI cmd timeout. Default: 16"); +#endif +static unsigned int g_debug_mem_size; +#ifndef _WINDOWS +#if defined(PS3_SUPPORT_DEBUG) || \ + (defined(PS3_CFG_RELEASE) && defined(PS3_CFG_OCM_DBGBUG)) || \ + (defined(PS3_CFG_RELEASE) && defined(PS3_CFG_OCM_RELEASE)) +module_param(g_debug_mem_size, uint, 0644); +MODULE_PARM_DESC( + g_debug_mem_size, + "Allocate DMA memory for IOC debugging little than 65535KB. Default: 0KB"); +#endif +#endif + +static unsigned int g_use_clustering = 1; +#ifndef _WINDOWS +module_param(g_use_clustering, uint, 0644); +MODULE_PARM_DESC( + g_use_clustering, + "SCSI mid-layer bio merge feature enable/disable. Default: enable(1)"); +#endif +static unsigned int g_scsi_cmd_timeout; +#ifndef _WINDOWS +module_param(g_scsi_cmd_timeout, uint, 0644); +MODULE_PARM_DESC( + g_scsi_cmd_timeout, + "SCSI cmd timeout (10-255s). Default: 0(No specify default 90s)"); +#endif + +extern unsigned int g_ps3_r1x_lock_flag; +extern unsigned int g_ps3_r1x_lock_enable; +#ifndef _WINDOWS + +module_param(g_ps3_r1x_lock_enable, uint, 0644); +MODULE_PARM_DESC( + g_ps3_r1x_lock_enable, + "R1x write conflict check feature enable/disable. Default: enable(1)"); +#endif + +#if defined(PS3_SUPPORT_DEBUG) || \ + (defined(PS3_CFG_RELEASE) && defined(PS3_CFG_OCM_DBGBUG)) || \ + (defined(PS3_CFG_RELEASE) && defined(PS3_CFG_OCM_RELEASE)) +extern unsigned int g_ps3_qos_hdd_pd_quota; +extern unsigned int g_ps3_qos_nvme_pd_quota; +#ifndef _WINDOWS +module_param(g_ps3_qos_hdd_pd_quota, uint, 0644); +MODULE_PARM_DESC(g_ps3_qos_hdd_pd_quota, "Qos pd quota. Default: 40"); +module_param(g_ps3_qos_nvme_pd_quota, uint, 0644); +MODULE_PARM_DESC(g_ps3_qos_nvme_pd_quota, "Qos nvme pd quota. Default: 127"); +#endif + +extern unsigned int g_ps3_r1x_rb_diff_cmds; +#ifndef _WINDOWS +module_param(g_ps3_r1x_rb_diff_cmds, uint, 0644); +MODULE_PARM_DESC(g_ps3_r1x_rb_diff_cmds, + "R1x read balancing outstanding threshold. Default: 4"); +#endif + +#endif + +static unsigned int g_direct_to_normal_enable = 1; +#ifndef _WINDOWS +module_param(g_direct_to_normal_enable, uint, 0644); +MODULE_PARM_DESC(g_direct_to_normal_enable, + "Direct to normal feature enable/disable. Default: enable(1)"); +#endif + +static unsigned int g_hba_check_time = 10; +#ifndef _WINDOWS +#if defined(PS3_SUPPORT_DEBUG) || \ + (defined(PS3_CFG_RELEASE) && defined(PS3_CFG_OCM_DBGBUG)) || \ + (defined(PS3_CFG_RELEASE) && defined(PS3_CFG_OCM_RELEASE)) +module_param(g_hba_check_time, uint, 0644); +MODULE_PARM_DESC(g_hba_check_time, + "HBA device id check time out. Default: 10s"); +#endif +#endif + +static unsigned int g_task_reset_delay_time = 50; +#ifndef _WINDOWS +#if defined(PS3_SUPPORT_DEBUG) || \ + (defined(PS3_CFG_RELEASE) && defined(PS3_CFG_OCM_DBGBUG)) || \ + (defined(PS3_CFG_RELEASE) && defined(PS3_CFG_OCM_RELEASE)) +module_param(g_task_reset_delay_time, uint, 0644); +MODULE_PARM_DESC(g_task_reset_delay_time, + "Task reset delay time (0-1000ms). Default: 50ms"); +#endif +#endif + +static unsigned int g_r1x_ring_size = 16; +#ifndef _WINDOWS +#if defined(PS3_SUPPORT_DEBUG) || \ + (defined(PS3_CFG_RELEASE) && defined(PS3_CFG_OCM_DBGBUG)) || \ + (defined(PS3_CFG_RELEASE) && defined(PS3_CFG_OCM_RELEASE)) +module_param(g_r1x_ring_size, uint, 0644); +MODULE_PARM_DESC( + g_r1x_ring_size, + "If R1X direct read more than g_r1x_ring_size, read from spare. Default: 16MB"); +#endif +#endif + +static unsigned int g_direct_check_stream_enable = PS3_TRUE; +#ifndef _WINDOWS +module_param(g_direct_check_stream_enable, uint, 0644); +MODULE_PARM_DESC( + g_direct_check_stream_enable, + "Direct detect stream or not feature enable/disable. Default: enable(1)"); +#endif + +static int g_device_busy_threshold = PS3_DEVICE_IO_BUSY_THRESHOLD; +#ifndef _WINDOWS +module_param(g_device_busy_threshold, int, 0644); +MODULE_PARM_DESC(g_device_busy_threshold, + "Device busy threshold value. Default: 8"); +#endif + +#ifndef __cplusplus +static char g_log_path[80] = { 0 }; +#ifndef _WINDOWS +module_param_string(g_log_path, g_log_path, 80, 0644); +MODULE_PARM_DESC( + g_log_path, + "The log path of host driver will be saved little than 80 chars. Default: /var/log"); +#endif +#endif + +static unsigned int g_log_file_size = 200; +#ifdef PS3_CFG_RELEASE +static unsigned int g_log_level = LEVEL_INFO; +#else +static unsigned int g_log_level = LEVEL_DEBUG; +#endif + +#if defined(PS3_SUPPORT_DEBUG) || \ + (defined(PS3_CFG_RELEASE) && defined(PS3_CFG_OCM_DBGBUG)) || \ + (defined(PS3_CFG_RELEASE) && defined(PS3_CFG_OCM_RELEASE)) +#ifndef _WINDOWS +module_param(g_log_file_size, uint, 0644); +MODULE_PARM_DESC(g_log_file_size, + "Single driver log file size (10-200MB). Default: 200MB"); + +module_param(g_log_level, uint, 0644); +MODULE_PARM_DESC(g_log_level, + "Specify driver log level." +#ifdef PS3_CFG_RELEASE + "0 - error, 1 - warn, 2 - info, 3 - debug. Default: 2"); +#else + "0 - error, 1 - warn, 2 - info, 3 - debug. Default: 3"); +#endif +#endif +#endif + +static unsigned int g_r1x_time_out = 3000; +#ifndef _WINDOWS +#if defined(PS3_SUPPORT_DEBUG) || \ + (defined(PS3_CFG_RELEASE) && defined(PS3_CFG_OCM_DBGBUG)) || \ + (defined(PS3_CFG_RELEASE) && defined(PS3_CFG_OCM_RELEASE)) + +module_param(g_r1x_time_out, uint, 0644); +MODULE_PARM_DESC(g_r1x_time_out, + "R1X conflict in queue after time. Default: 3000ms"); +#endif +#endif + +static unsigned int g_r1x_conflict_queue_enable = PS3_TRUE; +#ifndef _WINDOWS +#if defined(PS3_SUPPORT_DEBUG) || \ + (defined(PS3_CFG_RELEASE) && defined(PS3_CFG_OCM_DBGBUG)) || \ + (defined(PS3_CFG_RELEASE) && defined(PS3_CFG_OCM_RELEASE)) +module_param(g_r1x_conflict_queue_enable, uint, 0644); +MODULE_PARM_DESC( + g_r1x_conflict_queue_enable, + "R1X conflict queue function feature enable/disable. Default: enable(1)"); +#endif +#endif + +#ifndef _WINDOWS +#endif +static unsigned int g_log_space_size; + +static unsigned int g_log_tty; +#ifndef _WINDOWS +#if defined(PS3_SUPPORT_DEBUG) || \ + (defined(PS3_CFG_RELEASE) && defined(PS3_CFG_OCM_DBGBUG)) || \ + (defined(PS3_CFG_RELEASE) && defined(PS3_CFG_OCM_RELEASE)) +module_param(g_log_tty, uint, 0644); +MODULE_PARM_DESC( + g_log_tty, + "Allow driver log output to tty console feature enable/disable. Default: disable(0)"); +#endif +#endif + +#if defined PS3_HARDWARE_ASIC +static unsigned int g_hard_reset_enable = 1; +static unsigned int g_deep_soft_reset_enable; +#elif (((defined PS3_HARDWARE_FPGA || defined PS3_HARDWARE_ASIC) && \ + defined PS3_MODEL_V200) || \ + defined(PS3_HARDWARE_HAPS_V200)) +static unsigned int g_hard_reset_enable = 1; +static unsigned int g_deep_soft_reset_enable; +#else +static unsigned int g_hard_reset_enable; +static unsigned int g_deep_soft_reset_enable; +#endif +#ifndef _WINDOWS +#if defined(PS3_SUPPORT_DEBUG) || \ + (defined(PS3_CFG_RELEASE) && defined(PS3_CFG_OCM_DBGBUG)) || \ + (defined(PS3_CFG_RELEASE) && defined(PS3_CFG_OCM_RELEASE)) +module_param(g_hard_reset_enable, uint, 0644); +MODULE_PARM_DESC(g_hard_reset_enable, + "Hard reset feature enable/disable. Default: enable(1)"); +module_param(g_deep_soft_reset_enable, uint, 0644); +MODULE_PARM_DESC(g_deep_soft_reset_enable, + "Deep soft reset feature enable/disable. Default: disable(0)"); +#endif +#endif + +static unsigned int g_aer_handle_support = 1; +#ifndef _WINDOWS +module_param(g_aer_handle_support, uint, 0644); +MODULE_PARM_DESC( + g_aer_handle_support, + "Driver aer handle support feature enable/disable. Default: enable(1)"); +#endif + +#ifndef __cplusplus +static char g_version_verbose[512] = { 0 }; +#ifndef _WINDOWS +module_param_string(g_version_verbose, g_version_verbose, 511, 0444); +MODULE_PARM_DESC(g_version_verbose, + "Display detailed version information about ps3stor driver"); +#endif +#endif + +static unsigned int g_hard_reset_waiting; +static unsigned int g_use_hard_reset_reg = 1; +static unsigned int g_use_hard_reset_max_retry = PS3_HARD_RESET_MAX_RETRY; + +#ifndef _WINDOWS +#if defined(PS3_SUPPORT_DEBUG) || \ + (defined(PS3_CFG_RELEASE) && defined(PS3_CFG_OCM_DBGBUG)) || \ + (defined(PS3_CFG_RELEASE) && defined(PS3_CFG_OCM_RELEASE)) +module_param(g_hard_reset_waiting, uint, 0644); +MODULE_PARM_DESC(g_hard_reset_waiting, + "Allow to access PCIe Config/BAR after xxx ms. Default: 0"); +module_param(g_use_hard_reset_reg, uint, 0644); +MODULE_PARM_DESC( + g_use_hard_reset_reg, + "Write hard reset reg triggered feature enable/disable. Default: enable(1)"); +module_param(g_use_hard_reset_max_retry, uint, 0644); +MODULE_PARM_DESC(g_use_hard_reset_max_retry, + "Hard reset retry max count. Default: 2"); +#endif +#endif + +#if ((defined PS3_HARDWARE_FPGA && defined PS3_MODEL_V200) || \ + defined(PS3_HARDWARE_HAPS_V200) || defined(PS3_HARDWARE_ASIC)) +static unsigned int g_enable_heartbeat = 1; +module_param(g_enable_heartbeat, uint, 0644); +MODULE_PARM_DESC(g_enable_heartbeat, + "Heartbeat query feature enable/disable. Default: enable(1)"); +#else +static unsigned int g_enable_heartbeat; +module_param(g_enable_heartbeat, uint, 0644); +MODULE_PARM_DESC(g_enable_heartbeat, + "Heartbeat query feature enable/disable. Default: disable(0)"); +#endif + +static unsigned int g_hil_mode = 0xFFFF; +#ifndef _WINDOWS +#if defined(PS3_SUPPORT_DEBUG) || \ + (defined(PS3_CFG_RELEASE) && defined(PS3_CFG_OCM_DBGBUG)) || \ + (defined(PS3_CFG_RELEASE) && defined(PS3_CFG_OCM_RELEASE)) +module_param(g_hil_mode, uint, 0644); +MODULE_PARM_DESC( +g_hil_mode, +"Set HIL operational mode.\n" +"\t0 - SW mode, 1 - HW mode, 2 - Enhanced HW mode, 3 - SW assist mode. Default: 65535(0xFFFF)"); +#endif +#endif + +static unsigned int g_available_func_id = 0xFF; +#ifndef _WINDOWS +#if defined(PS3_SUPPORT_DEBUG) || \ + (defined(PS3_CFG_RELEASE) && defined(PS3_CFG_OCM_DBGBUG)) || \ + (defined(PS3_CFG_RELEASE) && defined(PS3_CFG_OCM_RELEASE)) +module_param(g_available_func_id, uint, 0644); +MODULE_PARM_DESC(g_available_func_id, + "Set function id.\n" + "\t0 - Func0, 1 - Func1, 2 - Unlimited. Default: 255(0xFF)"); +#endif +#endif + +static unsigned int g_pci_irq_mode = PS3_PCI_IRQ_MODE_NONE_SPE; +#ifndef _WINDOWS +#if defined(PS3_SUPPORT_DEBUG) || \ + (defined(PS3_CFG_RELEASE) && defined(PS3_CFG_OCM_DBGBUG)) || \ + (defined(PS3_CFG_RELEASE) && defined(PS3_CFG_OCM_RELEASE)) +module_param(g_pci_irq_mode, uint, 0644); +MODULE_PARM_DESC(g_pci_irq_mode, + "Specify pci irq mode.\n" + "\t0 - none specify, 1 - legacy, 2 - msi, 3 - msix. Default: 0"); +#endif +#endif + +#if defined(PS3_TAGSET_SUPPORT) + +#if defined(PS3_SUPPORT_TAGSET) +static int g_ps3_tagset_enable = 1; +module_param(g_ps3_tagset_enable, int, 0444); +MODULE_PARM_DESC(g_ps3_tagset_enable, + "Shared host tagset enable/disable. Default: enable(1)"); +#else +static int g_ps3_tagset_enable; +module_param(g_ps3_tagset_enable, int, 0444); +MODULE_PARM_DESC(g_ps3_tagset_enable, + "Shared host tagset enable/disable. Default: disable(0)"); +#endif + +#endif + +static unsigned int g_smp_affinity_enable = 1; +module_param(g_smp_affinity_enable, int, 0444); +MODULE_PARM_DESC(g_smp_affinity_enable, + "SMP affinity feature enable/disable. Default: enable(1)"); + +void ps3_version_verbose_fill(void) +{ +#ifndef __cplusplus + int len = 0; + int total_len = sizeof(g_version_verbose) - 1; + + memset(g_version_verbose, 0, sizeof(g_version_verbose)); + len += snprintf(g_version_verbose + len, total_len - len, "%-20s:%s\n", + "version", PS3_DRV_VERSION); + + len += snprintf(g_version_verbose + len, total_len - len, "%-20s:%s\n", + "commit_id", PS3_DRV_COMMIT_ID); + + len += snprintf(g_version_verbose + len, total_len - len, "%-20s:%s\n", + "toolchain_id", PS3_DRV_TOOLCHAIN_ID); + + len += snprintf(g_version_verbose + len, total_len - len, "%-20s:%s\n", + "build_time", PS3_DRV_BUILD_TIME); + + len += snprintf(g_version_verbose + len, total_len - len, "%-20s:%s\n", + "product_support", PS3_DRV_PRODUCT_SUPPORT); +#endif +} + +unsigned int ps3_throttle_qdepth_query(void) +{ + return g_throttle_que_depth; +} + +void ps3_debug_mem_size_modify(unsigned int size) +{ + g_debug_mem_size = size; +} + +unsigned int ps3_debug_mem_size_query(void) +{ + return g_debug_mem_size; +} + +unsigned short ps3_use_clustering_query(void) +{ + return (unsigned short)g_use_clustering; +} + +void ps3_scsi_cmd_timeout_modify(unsigned int val) +{ + g_scsi_cmd_timeout = val; +} + +void ps3_scsi_cmd_timeout_adjust(void) +{ + if (g_scsi_cmd_timeout != 0 && + (g_scsi_cmd_timeout < PS3_SCSI_CMD_TIMEOUT_MIN || + g_scsi_cmd_timeout > PS3_SCSI_CMD_TIMEOUT_MAX)) { + g_scsi_cmd_timeout = PS3_SCSI_CMD_TIMEOUT_DEFAULT; + } +} + +unsigned int ps3_scsi_cmd_timeout_query(void) +{ + return g_scsi_cmd_timeout; +} + +unsigned int ps3_r1x_lock_flag_quiry(void) +{ + return g_ps3_r1x_lock_flag; +} + +void ps3_r1x_lock_flag_modify(unsigned int val) +{ + g_ps3_r1x_lock_flag = val; +} + +unsigned int ps3_direct_to_normal_query(void) +{ + return g_direct_to_normal_enable; +} + +void ps3_direct_to_normal_modify(unsigned int val) +{ + g_direct_to_normal_enable = val; +} + +unsigned int ps3_hba_check_time_query(void) +{ + return g_hba_check_time; +} + +unsigned int ps3_task_reset_delay_time_query(void) +{ + return g_task_reset_delay_time; +} + +unsigned long long ps3_r1x_ring_size_query(void) +{ + unsigned long long ring_size_byte = (unsigned long long)g_r1x_ring_size; + + ring_size_byte = MB_TO_BYTE(ring_size_byte); + return ring_size_byte; +} + +void ps3_r1x_ring_size_modify(unsigned int size) +{ + g_r1x_ring_size = size; +} + +unsigned int ps3_direct_check_stream_query(void) +{ + return g_direct_check_stream_enable; +} + +void ps3_direct_check_stream_modify(unsigned int val) +{ + g_direct_check_stream_enable = val; +} + +int ps3_device_busy_threshold_query(void) +{ + return g_device_busy_threshold; +} + +void ps3_device_busy_threshold_modify(int busy) +{ + g_device_busy_threshold = busy; +} + +void ps3_log_level_modify(unsigned int level) +{ + ps3_level_set(level); +} + +char *ps3_log_path_query(void) +{ +#ifndef __cplusplus + return g_log_path; +#else + return NULL; +#endif +} + +unsigned int ps3_log_space_size_query(void) +{ + return g_log_space_size; +} + +unsigned int ps3_log_file_size_query(void) +{ + return g_log_file_size; +} + +unsigned int ps3_log_level_query(void) +{ + return g_log_level; +} + +void ps3_log_file_size_modify(unsigned int size) +{ + g_log_file_size = size; +} + +unsigned int ps3_log_tty_query(void) +{ + return g_log_tty; +} + +void ps3_log_tty_modify(unsigned int enable) +{ + g_log_tty = enable; +} + +void ps3_hard_reset_enable_modify(unsigned int val) +{ + g_hard_reset_enable = val; +} + +unsigned int ps3_hard_reset_enable_query(void) +{ + return g_hard_reset_enable; +} +unsigned int ps3_deep_soft_reset_enable_query(void) +{ + return g_deep_soft_reset_enable; +} +void ps3_deep_soft_reset_enable_modify(unsigned int val) +{ + g_deep_soft_reset_enable = val; +} + +unsigned int ps3_aer_handle_support_query(void) +{ + return g_aer_handle_support; +} + +void ps3_aer_handle_support_set(unsigned int aer_handle_support) +{ + g_aer_handle_support = aer_handle_support; +} + +unsigned int ps3_hard_reset_waiting_query(void) +{ + return g_hard_reset_waiting; +} + +unsigned int ps3_use_hard_reset_reg_query(void) +{ + return g_use_hard_reset_reg; +} +unsigned int ps3_use_hard_reset_max_retry(void) +{ + return g_use_hard_reset_max_retry; +} + +unsigned int ps3_enable_heartbeat_query(void) +{ + return g_enable_heartbeat; +} + +unsigned int ps3_enable_heartbeat_set(unsigned int val) +{ + return g_enable_heartbeat = val; +} + +unsigned int ps3_hil_mode_query(void) +{ + return g_hil_mode; +} + +void ps3_hil_mode_modify(unsigned int val) +{ + g_hil_mode = val; +} + +unsigned int ps3_available_func_id_query(void) +{ + return g_available_func_id; +} + +void ps3_available_func_id_modify(unsigned int val) +{ + g_available_func_id = val; +} + +unsigned int ps3_r1x_tmo_query(void) +{ + if (unlikely(g_r1x_time_out > 360000)) + g_r1x_time_out = 360000; + return g_r1x_time_out * HZ / 1000; +} + +unsigned int ps3_r1x_conflict_queue_support_query(void) +{ + return g_r1x_conflict_queue_enable; +} + +unsigned int ps3_pci_irq_mode_query(void) +{ + return g_pci_irq_mode; +} + +unsigned int ps3_cli_ver_query(void) +{ + return cli_ver; +} +#if defined(PS3_TAGSET_SUPPORT) + +void ps3_tagset_enable_modify(unsigned char enable) +{ + g_ps3_tagset_enable = enable; +} + +unsigned char ps3_tagset_enable_query(void) +{ + return (g_ps3_tagset_enable == 1) ? PS3_TRUE : PS3_FALSE; +} +#endif + +unsigned char ps3_smp_affinity_query(void) +{ + return (g_smp_affinity_enable == 1) ? PS3_TRUE : PS3_FALSE; +} diff --git a/drivers/scsi/linkdata/ps3stor/ps3_module_para.h b/drivers/scsi/linkdata/ps3stor/ps3_module_para.h new file mode 100644 index 0000000000000000000000000000000000000000..48d322d988c41b7b1075a99f7ae2890222854143 --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/ps3_module_para.h @@ -0,0 +1,102 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _PS3_MODULE_PARA_H_ +#define _PS3_MODULE_PARA_H_ + +#define MB_TO_BYTE(MB) ((MB) << 20) +#define PS3_MAX_FUNC_ID (2) + +unsigned int ps3_throttle_qdepth_query(void); + +void ps3_debug_mem_size_modify(unsigned int size); + +unsigned int ps3_debug_mem_size_query(void); + +unsigned int ps3_sata_direct_query(void); + +void ps3_sata_direct_modify(unsigned int val); + +unsigned short ps3_use_clustering_query(void); + +void ps3_scsi_cmd_timeout_modify(unsigned int val); + +unsigned int ps3_scsi_cmd_timeout_query(void); + +void ps3_scsi_cmd_timeout_adjust(void); + +unsigned int ps3_r1x_lock_flag_quiry(void); + +void ps3_r1x_lock_flag_modify(unsigned int val); + +unsigned int ps3_direct_to_normal_query(void); + +void ps3_direct_to_normal_modify(unsigned int val); + +unsigned int ps3_hba_check_time_query(void); + +unsigned int ps3_task_reset_delay_time_query(void); + +unsigned long long ps3_r1x_ring_size_query(void); + +void ps3_r1x_ring_size_modify(unsigned int size); + +unsigned int ps3_direct_check_stream_query(void); + +int ps3_device_busy_threshold_query(void); + +void ps3_device_busy_threshold_modify(int busy); + +void ps3_log_level_modify(unsigned int level); + +char *ps3_log_path_query(void); + +unsigned int ps3_log_space_size_query(void); + +unsigned int ps3_log_file_size_query(void); + +void ps3_log_file_size_modify(unsigned int size); + +unsigned int ps3_log_level_query(void); + +unsigned int ps3_log_tty_query(void); + +void ps3_log_tty_modify(unsigned int enable); + +void ps3_hard_reset_enable_modify(unsigned int val); +void ps3_deep_soft_reset_enable_modify(unsigned int val); + +unsigned int ps3_hard_reset_enable_query(void); +unsigned int ps3_deep_soft_reset_enable_query(void); + +unsigned int ps3_log_level_query(void); + +unsigned int ps3_aer_handle_support_query(void); +void ps3_aer_handle_support_set(unsigned int aer_handle_support); + +void ps3_version_verbose_fill(void); + +unsigned int ps3_hard_reset_waiting_query(void); +unsigned int ps3_use_hard_reset_reg_query(void); +unsigned int ps3_use_hard_reset_max_retry(void); +unsigned int ps3_enable_heartbeat_query(void); +unsigned int ps3_enable_heartbeat_set(unsigned int val); +unsigned int ps3_hil_mode_query(void); +void ps3_hil_mode_modify(unsigned int val); +unsigned int ps3_available_func_id_query(void); +void ps3_available_func_id_modify(unsigned int val); +void ps3_direct_check_stream_modify(unsigned int val); +unsigned int ps3_r1x_tmo_query(void); +unsigned int ps3_r1x_conflict_queue_support_query(void); +unsigned int ps3_pci_irq_mode_query(void); + +#if defined(PS3_TAGSET_SUPPORT) + +void ps3_tagset_enable_modify(unsigned char enable); + +unsigned char ps3_tagset_enable_query(void); +#endif +unsigned char ps3_smp_affinity_query(void); + +#ifndef _WINDOWS +#endif + +#endif diff --git a/drivers/scsi/linkdata/ps3stor/ps3_nvme_resp_to_scsi.c b/drivers/scsi/linkdata/ps3stor/ps3_nvme_resp_to_scsi.c new file mode 100644 index 0000000000000000000000000000000000000000..d4fb135c985368a507be579317646156d62570c3 --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/ps3_nvme_resp_to_scsi.c @@ -0,0 +1,397 @@ +// SPDX-License-Identifier: GPL-2.0 +#include + +#include "ps3_htp_nvme_spec.h" +#include "ps3_nvme_resp_to_scsi.h" +#include "ps3_instance_manager.h" +#include "ps3_htp_def.h" +#include "ps3_htp.h" + +enum { + SENSE_KEY_NO_SENSE = 0x00, + SENSE_KEY_RECOVERED_ERROR = 0x01, + SENSE_KEY_NOT_READY = 0x02, + SENSE_KEY_MEDIUM_ERROR = 0x03, + SENSE_KEY_HARDWARE_ERROR = 0x04, + SENSE_KEY_ILLEGAL_REQUEST = 0x05, + SENSE_KEY_UNIT_ATTENTION = 0x06, + SENSE_KEY_DATA_PROTECT = 0x07, + SENSE_KEY_BLANK_CHECK = 0x08, + SENSE_KEY_VENDOR_SPECIFIC = 0x09, + SENSE_KEY_COPY_ABORTED = 0x0a, + SENSE_KEY_ABORTED_COMMAND = 0x0b, + SENSE_KEY_VOLUME_OVERFLOW = 0x0d, + SENSE_KEY_MISCOMPARE = 0x0e, +}; + +enum { + SCSI_ASC_NO_ADDITIONAL_SENSE = 0x00, + SCSI_ASC_PERIPHERAL_DEVICE_WRITE_FAULT = 0x03, + SCSI_ASC_LOGICAL_UNIT_NOT_READY = 0x04, + SCSI_ASC_WARNING = 0x0b, + SCSI_ASC_LOGICAL_BLOCK_GUARD_CHECK_FAILED = 0x10, + SCSI_ASC_LOGICAL_BLOCK_APPTAG_CHECK_FAILED = 0x10, + SCSI_ASC_LOGICAL_BLOCK_REFTAG_CHECK_FAILED = 0x10, + SCSI_ASC_UNRECOVERED_READ_ERROR = 0x11, + SCSI_ASC_MISCOMPARE_DURING_VERIFY_OPERATION = 0x1d, + SCSI_ASC_INVALID_COMMAND_OPERATION_CODE = 0x20, + SCSI_ASC_ACCESS_DENIED = 0x20, + SCSI_ASC_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE = 0x21, + SCSI_ASC_INVALID_FIELD_IN_CDB = 0x24, + SCSI_ASC_LOGICAL_UNIT_NOT_SUPPORTED = 0x25, + SCSI_ASC_WRITE_PROTECTED = 0x27, + SCSI_ASC_FORMAT_COMMAND_FAILED = 0x31, + SCSI_ASC_SAVING_PARAMETERS_NOT_SUPPORTED = 0x39, + SCSI_ASC_INTERNAL_TARGET_FAILURE = 0x44, + SCSI_ASC_DATA_PHASE_ERROR = 0x4b, +}; + +enum ScsiAscq { + SCSI_ASCQ_CAUSE_NOT_REPORTABLE = 0x00, + SCSI_ASCQ_BECOMING_READY = 0x01, + SCSI_ASCQ_FORMAT_COMMAND_FAILED = 0x01, + SCSI_ASCQ_LOGICAL_BLOCK_GUARD_CHECK_FAILED = 0x01, + SCSI_ASCQ_LOGICAL_BLOCK_APPTAG_CHECK_FAILED = 0x02, + SCSI_ASCQ_NO_ACCESS_RIGHTS = 0x02, + SCSI_ASCQ_LOGICAL_BLOCK_REFTAG_CHECK_FAILED = 0x03, + SCSI_ASCQ_SANITIZE_COMMAND_FAILED = 0x03, + SCSI_ASCQ_LOGICAL_UNIT_NOT_READY_FORMAT_IN_PROGRESS = 0x04, + SCSI_ASCQ_DATA_OFFSET_ERROR = 0x05, + SCSI_ASCQ_POWER_LOSS_EXPECTED = 0x08, + SCSI_ASCQ_INVALID_LU_IDENTIFIER = 0x09, + SCSI_ASCQ_SDBP_INCOMING_BUFFER_OVERFLOW = 0x0C, + SCSI_ASCQ_WARNING_MICROCODE_DIGITAL_SIGNATURE_VALIDATION_FAILURE = 0x13, + SCSI_ASCQ_ERASE_OPEARTION_IN_PROGRESS = 0x18, + SCSI_ASCQ_LOGICAL_UNIT_NOT_READY_SANITIZE_IN_PROGRESS = 0x1b, +}; + +#define PS3_MEDIUM_ERROR_LEN (4) +static void +ps3_nvme_generic_error_to_scsi_status(unsigned char nvmeSc, + struct ps3_nvme_scsi_status *cpl) +{ + switch (nvmeSc) { + case NVME_SC_SUCCESS: + cpl->status = SCSI_STATUS_GOOD; + cpl->senseKey = SENSE_KEY_NO_SENSE; + cpl->asc = SCSI_ASC_NO_ADDITIONAL_SENSE; + cpl->ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE; + break; + case NVME_SC_INVALID_OPCODE: + cpl->status = SCSI_STATUS_CHECK_CONDITION; + cpl->senseKey = SENSE_KEY_ILLEGAL_REQUEST; + cpl->asc = SCSI_ASC_INVALID_COMMAND_OPERATION_CODE; + cpl->ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE; + break; + case NVME_SC_INVALID_FIELD: + cpl->status = SCSI_STATUS_CHECK_CONDITION; + cpl->senseKey = SENSE_KEY_ILLEGAL_REQUEST; + cpl->asc = SCSI_ASC_INVALID_FIELD_IN_CDB; + cpl->ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE; + break; + case NVME_SC_DATA_TRANSFER_ERROR: + case NVME_SC_CAPACITY_EXCEEDED: + cpl->status = SCSI_STATUS_CHECK_CONDITION; + cpl->senseKey = SENSE_KEY_MEDIUM_ERROR; + cpl->asc = SCSI_ASC_NO_ADDITIONAL_SENSE; + cpl->ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE; + break; + case NVME_SC_ABORTED_POWER_LOSS: + cpl->status = SCSI_STATUS_CHECK_CONDITION; + cpl->senseKey = SENSE_KEY_ABORTED_COMMAND; + cpl->asc = SCSI_ASC_WARNING; + cpl->ascq = SCSI_ASCQ_POWER_LOSS_EXPECTED; + break; + case NVME_SC_INTERNAL_DEVICE_ERROR: + cpl->status = SCSI_STATUS_CHECK_CONDITION; + cpl->senseKey = SENSE_KEY_HARDWARE_ERROR; + cpl->asc = SCSI_ASC_INTERNAL_TARGET_FAILURE; + cpl->ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE; + break; + case NVME_SC_ABORTED_BY_REQUEST: + case NVME_SC_ABORTED_SQ_DELETION: + case NVME_SC_ABORTED_FAILED_FUSED: + case NVME_SC_ABORTED_MISSING_FUSED: + cpl->status = SCSI_STATUS_CHECK_CONDITION; + cpl->senseKey = SENSE_KEY_ABORTED_COMMAND; + cpl->asc = SCSI_ASC_NO_ADDITIONAL_SENSE; + cpl->ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE; + break; + case NVME_SC_INVALID_NAMESPACE_OR_FORMAT: + cpl->status = SCSI_STATUS_CHECK_CONDITION; + cpl->senseKey = SENSE_KEY_ILLEGAL_REQUEST; + cpl->asc = SCSI_ASC_ACCESS_DENIED; + cpl->ascq = SCSI_ASCQ_INVALID_LU_IDENTIFIER; + break; + case NVME_SC_LBA_OUT_OF_RANGE: + cpl->status = SCSI_STATUS_CHECK_CONDITION; + cpl->senseKey = SENSE_KEY_ILLEGAL_REQUEST; + cpl->asc = SCSI_ASC_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; + cpl->ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE; + break; + case NVME_SC_NAMESPACE_NOT_READY: + cpl->status = SCSI_STATUS_CHECK_CONDITION; + cpl->senseKey = SENSE_KEY_NOT_READY; + cpl->asc = SCSI_ASC_LOGICAL_UNIT_NOT_READY; + cpl->ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE; + break; + case NVME_SC_RESERVATION_CONFLICT: + cpl->status = SCSI_STATUS_RESERVATION_CONFLICT; + cpl->senseKey = SENSE_KEY_NO_SENSE; + cpl->asc = SCSI_ASC_NO_ADDITIONAL_SENSE; + cpl->ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE; + break; + case NVME_SC_COMMAND_ID_CONFLICT: + case NVME_SC_COMMAND_SEQUENCE_ERROR: + case NVME_SC_INVALID_SGL_SEG_DESCRIPTOR: + case NVME_SC_INVALID_NUM_SGL_DESCIRPTORS: + case NVME_SC_DATA_SGL_LENGTH_INVALID: + case NVME_SC_METADATA_SGL_LENGTH_INVALID: + case NVME_SC_SGL_DESCRIPTOR_TYPE_INVALID: + case NVME_SC_INVALID_CONTROLLER_MEM_BUF: + case NVME_SC_INVALID_PRP_OFFSET: + cpl->status = SCSI_STATUS_CHECK_CONDITION; + cpl->senseKey = SENSE_KEY_ILLEGAL_REQUEST; + cpl->asc = SCSI_ASC_DATA_PHASE_ERROR; + cpl->ascq = SCSI_ASCQ_DATA_OFFSET_ERROR; + break; + case NVME_SC_ATOMIC_WRITE_UNIT_EXCEEDED: + case NVME_SC_INVALID_SGL_OFFSET: + case NVME_SC_HOSTID_INCONSISTENT_FORMAT: + case NVME_SC_KEEP_ALIVE_EXPIRED: + case NVME_SC_KEEP_ALIVE_INVALID: + case NVME_SC_FORMAT_IN_PROGRESS: + cpl->status = SCSI_STATUS_CHECK_CONDITION; + cpl->senseKey = SENSE_KEY_NOT_READY; + cpl->asc = SCSI_ASC_LOGICAL_UNIT_NOT_READY; + cpl->ascq = SCSI_ASCQ_LOGICAL_UNIT_NOT_READY_FORMAT_IN_PROGRESS; + break; + case NVME_SC_SANITIZE_FAILED: + cpl->status = SCSI_STATUS_CHECK_CONDITION; + cpl->senseKey = SENSE_KEY_MEDIUM_ERROR; + cpl->asc = SCSI_ASC_FORMAT_COMMAND_FAILED; + cpl->ascq = SCSI_ASCQ_SANITIZE_COMMAND_FAILED; + break; + case NVME_SC_SANITIZE_IN_PROGRESS: + cpl->status = SCSI_STATUS_CHECK_CONDITION; + cpl->senseKey = SENSE_KEY_NOT_READY; + cpl->asc = SCSI_ASC_LOGICAL_UNIT_NOT_READY; + cpl->ascq = + SCSI_ASCQ_LOGICAL_UNIT_NOT_READY_SANITIZE_IN_PROGRESS; + break; + case NVME_SC_NAMESPACE_IS_WRITE_PROTECTED: + cpl->status = SCSI_STATUS_CHECK_CONDITION; + cpl->senseKey = SENSE_KEY_DATA_PROTECT; + cpl->asc = SCSI_ASC_WRITE_PROTECTED; + cpl->ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE; + break; + case NVME_SC_COMMAND_INTERRUPTED: + case NVME_SC_TRANSIENT_TRANSPORT_ERROR: + cpl->status = SCSI_STATUS_CHECK_CONDITION; + cpl->senseKey = SENSE_KEY_ABORTED_COMMAND; + cpl->asc = SCSI_ASC_NO_ADDITIONAL_SENSE; + cpl->ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE; + break; + default: + cpl->status = SCSI_STATUS_CHECK_CONDITION; + cpl->senseKey = SENSE_KEY_ILLEGAL_REQUEST; + cpl->asc = SCSI_ASC_NO_ADDITIONAL_SENSE; + cpl->ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE; + break; + } +} + +static void ps3_nvme_spec_error_to_scsi_status(unsigned char nvmeSc, + struct ps3_nvme_scsi_status *cpl) +{ + switch (nvmeSc) { + case NVME_CSC_COMPLETION_QUEUE_INVALID: + case NVME_CSC_ABORT_COMMAND_LIMIT_EXCEEDED: + cpl->status = SCSI_STATUS_CHECK_CONDITION; + cpl->senseKey = SENSE_KEY_ILLEGAL_REQUEST; + cpl->asc = SCSI_ASC_NO_ADDITIONAL_SENSE; + cpl->ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE; + break; + case NVME_CSC_INVALID_FORMAT: + cpl->status = SCSI_STATUS_CHECK_CONDITION; + cpl->senseKey = SENSE_KEY_ILLEGAL_REQUEST; + cpl->asc = SCSI_ASC_FORMAT_COMMAND_FAILED; + cpl->ascq = SCSI_ASCQ_FORMAT_COMMAND_FAILED; + break; + case NVME_CSC_CONFLICTING_ATTRIBUTES: + cpl->status = SCSI_STATUS_CHECK_CONDITION; + cpl->senseKey = SENSE_KEY_ILLEGAL_REQUEST; + cpl->asc = SCSI_ASC_INVALID_FIELD_IN_CDB; + cpl->ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE; + break; + case NVME_CSC_ATTEMPTED_WRITE_TO_RO_RANGE: + cpl->status = SCSI_STATUS_CHECK_CONDITION; + cpl->senseKey = SENSE_KEY_DATA_PROTECT; + cpl->asc = SCSI_ASC_WRITE_PROTECTED; + cpl->ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE; + break; + case NVME_CSC_INVALID_QUEUE_IDENTIFIER: + case NVME_CSC_MAXIMUM_QUEUE_SIZE_EXCEEDED: + case NVME_CSC_ASYNC_EVENT_REQUEST_LIMIT_EXCEEDED: + case NVME_CSC_FIRMWARE_REQ_NVM_RESET: + case NVME_CSC_INVALID_PROTECTION_INFO: + case NVME_CSC_FIRMWARE_REQ_MAX_TIME_VIOLATION: + case NVME_CSC_FIRMWARE_ACTIVATION_PROHIBITED: + case NVME_CSC_BOOT_PARTITION_WRITE_PROHIBITED: + case NVME_CSC_INVALID_FIRMWARE_SLOT: + cpl->status = SCSI_STATUS_CHECK_CONDITION; + cpl->senseKey = SENSE_KEY_ABORTED_COMMAND; + cpl->asc = SCSI_ASC_NO_ADDITIONAL_SENSE; + cpl->ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE; + break; + case NVME_CSC_INVALID_FIRMWARE_IMAGE: + cpl->status = SCSI_STATUS_CHECK_CONDITION; + cpl->senseKey = SENSE_KEY_ABORTED_COMMAND; + cpl->asc = SCSI_ASC_WARNING; + cpl->ascq = + SCSI_ASCQ_WARNING_MICROCODE_DIGITAL_SIGNATURE_VALIDATION_FAILURE; + break; + case NVME_CSC_INVALID_INTERRUPT_VECTOR: + case NVME_CSC_INVALID_LOG_PAGE: + case NVME_CSC_FIRMWARE_REQ_CONVENTIONAL_RESET: + case NVME_CSC_INVALID_QUEUE_DELETION: + case NVME_CSC_FEATURE_ID_NOT_SAVEABLE: + case NVME_CSC_FEATURE_NOT_CHANGEABLE: + case NVME_CSC_FEATURE_NOT_NAMESPACE_SPECIFIC: + case NVME_CSC_FIRMWARE_REQ_RESET: + case NVME_CSC_OVERLAPPING_RANGE: + cpl->status = SCSI_STATUS_CHECK_CONDITION; + cpl->senseKey = SENSE_KEY_ILLEGAL_REQUEST; + cpl->asc = SCSI_ASC_INVALID_FIELD_IN_CDB; + cpl->ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE; + break; + case NVME_CSC_NAMESPACE_INSUFFICIENT_CAPACITY: + case NVME_CSC_NAMESPACE_ID_UNAVAILABLE: + case NVME_CSC_NAMESPACE_ALREADY_ATTACHED: + case NVME_CSC_NAMESPACE_IS_PRIVATE: + case NVME_CSC_NAMESPACE_NOT_ATTACHED: + case NVME_CSC_THINPROVISIONING_NOT_SUPPORTED: + case NVME_CSC_CONTROLLER_LIST_INVALID: + + default: + cpl->status = SCSI_STATUS_CHECK_CONDITION; + cpl->senseKey = SENSE_KEY_ILLEGAL_REQUEST; + cpl->asc = SCSI_ASC_NO_ADDITIONAL_SENSE; + cpl->ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE; + break; + } +} + +static void +ps3_nvme_media_error_to_scsi_status(unsigned char nvmeSc, + struct ps3_nvme_scsi_status *cpl) +{ + switch (nvmeSc) { + case NVME_MSC_WRITE_FAULTS: + cpl->status = SCSI_STATUS_CHECK_CONDITION; + cpl->senseKey = SENSE_KEY_MEDIUM_ERROR; + cpl->asc = SCSI_ASC_PERIPHERAL_DEVICE_WRITE_FAULT; + cpl->ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE; + break; + case NVME_MSC_UNRECOVERED_READ_ERROR: + cpl->status = SCSI_STATUS_CHECK_CONDITION; + cpl->senseKey = SENSE_KEY_MEDIUM_ERROR; + cpl->asc = SCSI_ASC_UNRECOVERED_READ_ERROR; + cpl->ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE; + break; + case NVME_MSC_GUARD_CHECK_ERROR: + cpl->status = SCSI_STATUS_CHECK_CONDITION; + cpl->senseKey = SENSE_KEY_ABORTED_COMMAND; + cpl->asc = SCSI_ASC_LOGICAL_BLOCK_GUARD_CHECK_FAILED; + cpl->ascq = SCSI_ASCQ_LOGICAL_BLOCK_GUARD_CHECK_FAILED; + break; + case NVME_MSC_APPLICATION_TAG_CHECK_ERROR: + cpl->status = SCSI_STATUS_CHECK_CONDITION; + cpl->senseKey = SENSE_KEY_ABORTED_COMMAND; + cpl->asc = SCSI_ASC_LOGICAL_BLOCK_APPTAG_CHECK_FAILED; + cpl->ascq = SCSI_ASCQ_LOGICAL_BLOCK_APPTAG_CHECK_FAILED; + break; + case NVME_MSC_REFERENCE_TAG_CHECK_ERROR: + cpl->status = SCSI_STATUS_CHECK_CONDITION; + cpl->senseKey = SENSE_KEY_ABORTED_COMMAND; + cpl->asc = SCSI_ASC_LOGICAL_BLOCK_REFTAG_CHECK_FAILED; + cpl->ascq = SCSI_ASCQ_LOGICAL_BLOCK_REFTAG_CHECK_FAILED; + break; + case NVME_MSC_COMPARE_FAILURE: + cpl->status = SCSI_STATUS_CHECK_CONDITION; + cpl->senseKey = SENSE_KEY_MISCOMPARE; + cpl->asc = SCSI_ASC_MISCOMPARE_DURING_VERIFY_OPERATION; + cpl->ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE; + break; + case NVME_MSC_ACCESS_DENIED: + cpl->status = SCSI_STATUS_CHECK_CONDITION; + cpl->senseKey = SENSE_KEY_DATA_PROTECT; + cpl->asc = SCSI_ASC_ACCESS_DENIED; + cpl->ascq = SCSI_ASCQ_NO_ACCESS_RIGHTS; + break; + case NVME_MSC_DEALLOCATED_OR_UNWRITTEN_BLOCK: + cpl->status = SCSI_STATUS_CHECK_CONDITION; + cpl->senseKey = SENSE_KEY_MEDIUM_ERROR; + cpl->asc = SCSI_ASC_UNRECOVERED_READ_ERROR; + cpl->ascq = SCSI_ASCQ_SDBP_INCOMING_BUFFER_OVERFLOW; + break; + default: + cpl->status = SCSI_STATUS_CHECK_CONDITION; + cpl->senseKey = SENSE_KEY_ILLEGAL_REQUEST; + cpl->asc = SCSI_ASC_NO_ADDITIONAL_SENSE; + cpl->ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE; + break; + } +} + +void ps3_nvme_error_to_scsi_status(struct PS3NvmeCmdStatus status, + struct ps3_nvme_scsi_status *cpl) +{ + unsigned char sct = status.sct; + unsigned char sc = status.sc; + + switch (sct) { + case NVME_SCT_GENERIC: + ps3_nvme_generic_error_to_scsi_status(sc, cpl); + break; + case NVME_SCT_COMMAND_SPECIFIC: + ps3_nvme_spec_error_to_scsi_status(sc, cpl); + break; + case NVME_SCT_MEDIA_ERROR: + ps3_nvme_media_error_to_scsi_status(sc, cpl); + break; + case NVME_SCT_VENDOR_SPECIFIC: + default: + cpl->status = SCSI_STATUS_CHECK_CONDITION; + cpl->senseKey = SENSE_KEY_ILLEGAL_REQUEST; + cpl->asc = SCSI_ASC_NO_ADDITIONAL_SENSE; + cpl->ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE; + break; + } +} + +void ps3_nvme_resp_to_scsi_status(struct ps3_cmd *cmd) +{ + struct PS3NvmeCmdStatus status; + struct ps3_nvme_scsi_status cpl; + + status.cmdStatus = cmd->reply_word.retStatus; + LOG_FILE_ERROR("trace_id:0x%llx host_no:%d tag:%d nvme status:\n" + "\tsc:%d sct:%d crd:%d more:%d dnr:%d p:%d\n", + cmd->trace_id, PS3_HOST(cmd->instance), cmd->index, + status.sc, status.sct, status.crd, status.m, status.dnr, + status.p); + + memset(&cpl, 0, sizeof(struct ps3_nvme_scsi_status)); + ps3_nvme_error_to_scsi_status(status, &cpl); + + memset(cmd->resp_frame->sasRespFrame.data, 0, PS3_SENSE_BUFFER_SIZE); + scsi_build_sense_buffer(0, cmd->resp_frame->sasRespFrame.data, + cpl.senseKey, cpl.asc, cpl.ascq); + if (cmd->reply_word.mode == PS3_REPLY_WORD_MODE_DIRECT_OK && + cpl.senseKey == SENSE_KEY_MEDIUM_ERROR) { + memset(&cmd->resp_frame->sasRespFrame.data[3], 0xff, + PS3_MEDIUM_ERROR_LEN); + } + cmd->resp_frame->sasRespFrame.status = cpl.status; +} diff --git a/drivers/scsi/linkdata/ps3stor/ps3_nvme_resp_to_scsi.h b/drivers/scsi/linkdata/ps3stor/ps3_nvme_resp_to_scsi.h new file mode 100644 index 0000000000000000000000000000000000000000..5a9883e8664ccddbc2e8495772077bb30a200ced --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/ps3_nvme_resp_to_scsi.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#include "ps3_cmd_channel.h" + +struct ps3_nvme_scsi_status { + unsigned char status; + unsigned char senseKey; + unsigned char asc; + unsigned char ascq; +}; + +void ps3_nvme_error_to_scsi_status(struct PS3NvmeCmdStatus status, + struct ps3_nvme_scsi_status *cpl); +void ps3_nvme_resp_to_scsi_status(struct ps3_cmd *cmd); diff --git a/drivers/scsi/linkdata/ps3stor/ps3_pci.c b/drivers/scsi/linkdata/ps3stor/ps3_pci.c new file mode 100644 index 0000000000000000000000000000000000000000..daef086b7fcd24e46c8d91eb2cdf99d598beacd5 --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/ps3_pci.c @@ -0,0 +1,475 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include "ps3_instance_manager.h" +#include "ps3_pci.h" + +#define PS3_VALID_MEMORY_BAR_INDEX 2 +#ifdef _WINDOWS +#define PS3_CHECK_BAR_64BIT(bar) ((bar) & 0x4) +#define PS3_CHECK_BAR_MEMORYSPACE(bar) (!((bar) & 0x1)) +#define PS3_BAR_BASE_ADDR_MARSK (0xFFFFFFF0) +#define PS3_GET_BAR_BASE_ADDR(addr) \ + ((unsigned long long)((addr) & PS3_BAR_BASE_ADDR_MARSK)) + +static int ps3_pci_map_reg(struct ps3_instance *instance, + PPORT_CONFIGURATION_INFORMATION config); +static int ps3_pci_info_get(struct ps3_instance *instance, + PPORT_CONFIGURATION_INFORMATION config); +static void ps3_pci_unmap_reg(struct ps3_instance *instance); +static void ps3_pci_irq_type_get(struct ps3_instance *instance, + PPCI_COMMON_CONFIG pci_config); +static unsigned short ps3_pci_msix_vec_count(struct ps3_instance *instance, + PPCI_COMMON_CONFIG pci_config); +static unsigned short ps3_pci_msi_vec_count(struct ps3_instance *instance, + PPCI_COMMON_CONFIG pci_config); +int ps3_pci_init(struct ps3_instance *instance, void *config) +{ + int ret = PS3_SUCCESS; + PPORT_CONFIGURATION_INFORMATION config_info = + (PPORT_CONFIGURATION_INFORMATION)config; + + ret = ps3_pci_info_get(instance, config_info); + if (ret != PS3_SUCCESS) + goto l_out; + + ret = ps3_pci_map_reg(instance, config_info); + if (ret != PS3_SUCCESS) + goto l_out; + +l_out: + return ret; +} + +void ps3_pci_exit(struct ps3_instance *instance) +{ + ps3_pci_unmap_reg(instance); +} + +static int ps3_pci_info_get(struct ps3_instance *instance, + PPORT_CONFIGURATION_INFORMATION config) +{ + PCI_COMMON_CONFIG pci_config = { 0 }; + unsigned long len = 0; + unsigned long base_addr = 0; +#ifdef PS3_HARDWARE_ASIC + unsigned int check_count = ps3_hba_check_time_query() * 10; +#endif + if (config->AdapterInterfaceType != PCIBus) { + LOG_ERROR("there is not pcibus, type:%d\n", + config->AdapterInterfaceType); + return -PS3_FAILED; + } + + len = StorPortGetBusData(instance, PCIConfiguration, + config->SystemIoBusNumber, + (unsigned long)config->SlotNumber, + (void *)&pci_config, sizeof(pci_config)); + + if (len == 0 || len == 2) { + LOG_ERROR("get bus data failed,cfg length:%d\n", len); + return -PS3_FAILED; + } + +#ifdef PS3_HARDWARE_ASIC + while (pci_config.DeviceID == PCI_DEVICE_ID_PS3_RAID_FPGA && + check_count > 0) { + check_count--; + ps3_msleep(100); + + len = StorPortGetBusData(instance, PCIConfiguration, + config->SystemIoBusNumber, + (unsigned long)config->SlotNumber, + (void *)&pci_config, + sizeof(pci_config)); + + if (len == 0 || len == 2) { + LOG_ERROR("get bus data failed,cfg length:%d\n", len); + return -PS3_FAILED; + } + + LOG_INFO("get real device id is[0x%x]\n", pci_config.DeviceID); + }; +#endif + + instance->pci_dev_context.slot_number = + (unsigned long long)config->SlotNumber; + instance->pci_dev_context.device_id = pci_config.DeviceID; + instance->pci_dev_context.vendor_id = pci_config.VendorID; + instance->pci_dev_context.sub_vendor_id = + pci_config.u.type0.SubVendorID; + instance->pci_dev_context.sub_device_id = + pci_config.u.type0.SubSystemID; + base_addr = + pci_config.u.type0.BaseAddresses[PS3_VALID_MEMORY_BAR_INDEX]; + if (!PS3_CHECK_BAR_MEMORYSPACE(base_addr)) { + LOG_ERROR("Bar%d is not memory space\n", + PS3_VALID_MEMORY_BAR_INDEX); + return -PS3_FAILED; + } + + if (PS3_CHECK_BAR_64BIT(base_addr)) { + instance->pci_dev_context.bar_base_addr = + PS3_GET_BAR_BASE_ADDR(base_addr); + base_addr = + pci_config.u.type0 + .BaseAddresses[PS3_VALID_MEMORY_BAR_INDEX + 1]; + instance->pci_dev_context.bar_base_addr |= + ((unsigned long long)base_addr) << 32; + } else { + instance->pci_dev_context.bar_base_addr = + PS3_GET_BAR_BASE_ADDR(base_addr); + } + + ps3_pci_irq_type_get(instance, &pci_config); + + if (instance->pci_dev_context.pci_irq_type == PS3_PCI_IRQ_MSIX) { + instance->pci_dev_context.irq_vec_count = + ps3_pci_msix_vec_count(instance, &pci_config); + } else if (instance->pci_dev_context.pci_irq_type == PS3_PCI_IRQ_MSI) { + instance->pci_dev_context.irq_vec_count = + ps3_pci_msi_vec_count(instance, &pci_config); + } else { + instance->pci_dev_context.irq_vec_count = 1; + } + + LOG_INFO( + "vid:%x, devid:%x, sub_vid:%x, sub_devid:%x, bar_base:0x%llx, irq_type:%d, vec_count:%d\n", + instance->pci_dev_context.vendor_id, + instance->pci_dev_context.device_id, + instance->pci_dev_context.sub_vendor_id, + instance->pci_dev_context.sub_device_id, + instance->pci_dev_context.bar_base_addr, + instance->pci_dev_context.pci_irq_type, + instance->pci_dev_context.irq_vec_count); + + return PS3_SUCCESS; +}; + +static int ps3_pci_map_reg(struct ps3_instance *instance, + PPORT_CONFIGURATION_INFORMATION config) +{ + int ret = -PS3_FAILED; + unsigned long i = 0; + + if (config->NumberOfAccessRanges <= 0) { + LOG_ERROR("valid access range is 0\n"); + goto l_out; + } + + if (config->AdapterInterfaceType != PCIBus) { + LOG_ERROR("adapter interface type is not PCIBus\n"); + goto l_out; + } + + for (i = 0; i < config->NumberOfAccessRanges; i++) { + LOG_DEBUG("Bar%llu:0x%llx, memory:%d, len:0x%x, ex:0x%llx\n", i, + (*(config->AccessRanges))[i].RangeStart.QuadPart, + (*(config->AccessRanges))[i].RangeInMemory, + (*(config->AccessRanges))[i].RangeLength, + instance->pci_dev_context.bar_base_addr); + if ((unsigned long long)(*(config->AccessRanges))[i] + .RangeStart.QuadPart != + instance->pci_dev_context.bar_base_addr) { + continue; + } + + if (!(*(config->AccessRanges))[i].RangeInMemory) { + LOG_INFO( + "access range number continue:%d, is not memery\n", + i); + continue; + } + + instance->reg_set = (struct Ps3Fifo *)StorPortGetDeviceBase( + instance, config->AdapterInterfaceType, + config->SystemIoBusNumber, + (*(config->AccessRanges))[i].RangeStart, + (*(config->AccessRanges))[i].RangeLength, 0); + + if (instance->reg_set != NULL) + ret = PS3_SUCCESS; + + break; + } + +l_out: + LOG_INFO("map reg:%d,reg:%p\n", ret, instance->reg_set); + return ret; +} + +static void ps3_pci_unmap_reg(struct ps3_instance *instance) +{ + if (instance->reg_set != NULL) { + StorPortFreeDeviceBase(instance, instance->reg_set); + instance->reg_set = NULL; + } +} + +static int __ps3_pci_find_capability(PPCI_COMMON_CONFIG pci_config, int cap_id) +{ + int pos = 0; + unsigned char *config_base_addr = (unsigned char *)pci_config; + unsigned char cap_offset = 0; + PPCI_CAPABILITIES_HEADER cap_header = NULL; + + if (PCI_CONFIGURATION_TYPE(pci_config)) { + LOG_ERROR("there is not agent device\n"); + goto l_out; + } + + if ((pci_config->Status & PCI_STATUS_CAPABILITIES_LIST) == 0) { + LOG_ERROR("capability pointer invalid\n"); + goto l_out; + } + + cap_offset = pci_config->u.type0.CapabilitiesPtr; + while (cap_offset != 0) { + cap_header = (PPCI_CAPABILITIES_HEADER)(config_base_addr + + cap_offset); + if (cap_header->CapabilityID == 0) { + cap_offset = cap_header->Next; + continue; + } + + if (cap_header->CapabilityID == (unsigned char)cap_id) { + pos = cap_offset; + break; + } + cap_offset = cap_header->Next; + }; + +l_out: + return pos; +} + +static void ps3_pci_irq_type_get(struct ps3_instance *instance, + PPCI_COMMON_CONFIG pci_config) +{ + unsigned char *config_base_addr = (unsigned char *)pci_config; + unsigned short data_u16 = 0; + int pos = 0; + + instance->pci_dev_context.pci_irq_type = PS3_PCI_IRQ_LEGACY; + pos = __ps3_pci_find_capability(pci_config, PCI_CAP_ID_MSIX); + if (pos != 0) { + data_u16 = *((unsigned short *)(config_base_addr + pos + + PCI_MSIX_FLAGS)); + + for (size_t i = 0; i < 4; i++) { + unsigned char tmp = *( + (unsigned char *)(config_base_addr + pos + i)); + + LOG_DEBUG("%d:%d:%x\n", pos, i, tmp); + } + + if ((data_u16 & PCI_MSIX_FLAGS_ENABLE) == + PCI_MSIX_FLAGS_ENABLE) { + instance->pci_dev_context.pci_irq_type = + PS3_PCI_IRQ_MSIX; + goto l_out; + } + } + + pos = __ps3_pci_find_capability(pci_config, PCI_CAP_ID_MSI); + if (pos != 0) { + data_u16 = *((unsigned short *)(config_base_addr + pos + + PCI_MSI_FLAGS)); + if ((data_u16 & PCI_MSI_FLAGS_ENABLE) == PCI_MSI_FLAGS_ENABLE) { + instance->pci_dev_context.pci_irq_type = + PS3_PCI_IRQ_MSI; + goto l_out; + } + } + +l_out: + return; +} + +void ps3_pci_intx(struct ps3_instance *instance, unsigned char enable) +{ + unsigned short pci_command = 0; + unsigned short pci_command_new = 0; + + if (ps3_pci_read_config_word(instance, PCI_COMMAND, &pci_command) != + PS3_SUCCESS) { + goto l_out; + } + + if (enable) + pci_command_new = pci_command & ~PCI_COMMAND_INTX_DISABLE; + else + pci_command_new = pci_command | PCI_COMMAND_INTX_DISABLE; + + if (pci_command_new != pci_command) { + ps3_pci_write_config_word(instance, PCI_COMMAND, + pci_command_new); + } + +l_out: + return; +} + +static unsigned short ps3_pci_msix_vec_count(struct ps3_instance *instance, + PPCI_COMMON_CONFIG pci_config) +{ + unsigned char *config_base_addr = (unsigned char *)pci_config; + int pos = 0; + unsigned short msix_vec_count = 0; + unsigned short data_u16 = 0; + + (void)instance; + pos = __ps3_pci_find_capability(pci_config, PCI_CAP_ID_MSIX); + if (pos != 0) + data_u16 = *((unsigned short *)(config_base_addr + pos + + PCI_MSIX_FLAGS)); + + msix_vec_count = ((data_u16 & PCI_MSIX_FLAGS_QSIZE) + 1); + + return msix_vec_count; +} + +static unsigned short ps3_pci_msi_vec_count(struct ps3_instance *instance, + PPCI_COMMON_CONFIG pci_config) +{ + unsigned char *config_base_addr = (unsigned char *)pci_config; + int pos = 0; + unsigned short msi_vec_count = 0; + unsigned short data_u16 = 0; + + (void)instance; + pos = __ps3_pci_find_capability(pci_config, PCI_CAP_ID_MSI); + if (pos != 0) + data_u16 = *((unsigned short *)(config_base_addr + pos + + PCI_MSI_FLAGS)); + + msi_vec_count = 1 << ((data_u16 & PCI_MSI_FLAGS_QMASK) >> 1); + + return data_u16; +} + +#endif + +int ps3_pci_find_capability(struct ps3_instance *instance, int cap_id) +{ + int pos = 0; +#ifdef _WINDOWS + PCI_COMMON_CONFIG pci_config = { 0 }; + unsigned int len = 0; + + len = (unsigned int)StorPortGetBusData( + instance, PCIConfiguration, (unsigned long)instance->bus_number, + (unsigned long)instance->pci_dev_context.slot_number, + (void *)&pci_config, (unsigned long)sizeof(pci_config)); + + if (len == 0 || len == 2) { + LOG_ERROR("get bus data failed,cfg length:%d\n", len); + goto l_out; + } + + pos = __ps3_pci_find_capability(&pci_config, cap_id); + +l_out: +#else + pos = pci_find_capability(instance->pdev, cap_id); +#endif + return pos; +} + +int ps3_pci_read_config_word(struct ps3_instance *instance, unsigned int offset, + unsigned short *val) +{ + int ret = -PS3_FAILED; +#ifdef _WINDOWS + PCI_COMMON_CONFIG pci_config = { 0 }; + unsigned int len = 0; + unsigned char *config_base_addr = (unsigned char *)&pci_config; + + len = (unsigned int)StorPortGetBusData( + instance, PCIConfiguration, (unsigned long)instance->bus_number, + (unsigned long)instance->pci_dev_context.slot_number, + (void *)&pci_config, (unsigned long)sizeof(pci_config)); + + if (len == 0 || len == 2) { + LOG_ERROR("get bus data failed,cfg length:%d\n", len); + goto l_out; + } + + *val = *((unsigned short *)(config_base_addr + offset)); + ret = PS3_SUCCESS; + +l_out: +#else + ret = pci_read_config_word(instance->pdev, offset, val); +#endif + LOG_INFO("read config word :%d\n", ret); + return ret; +} + +int ps3_pci_write_config_word(struct ps3_instance *instance, + unsigned int offset, unsigned short val) +{ + int ret = -PS3_FAILED; +#ifdef _WINDOWS + unsigned int len = 0; + + len = (unsigned int)StorPortSetBusDataByOffset( + instance, PCIConfiguration, (unsigned long)instance->bus_number, + (unsigned long)instance->pci_dev_context.slot_number, + (void *)&val, (unsigned long)offset, + (unsigned long)sizeof(unsigned short)); + + if (len == sizeof(unsigned short)) + ret = PS3_SUCCESS; +#else + ret = pci_write_config_word(instance->pdev, offset, val); +#endif + LOG_INFO("write config word :%d\n", ret); + return ret; +} + +void ps3_reg_write_u64(struct ps3_instance *instance, unsigned long long val, + void *reg) +{ +#ifndef _WINDOWS +#if defined(writeq) && defined(CONFIG_64BIT) + (void)instance; + writeq(val, reg); +#else + unsigned long flags; + + ps3_spin_lock_irqsave(&instance->req_queue_lock, &flags); + writel((unsigned int)(val & 0xffffffff), reg); + writel((unsigned int)(val >> 32), reg + 0x4UL); + ps3_spin_unlock_irqrestore(&instance->req_queue_lock, flags); +#endif +#else + StorPortWriteRegisterUlong64(instance, reg, val); +#endif +} + +unsigned long long ps3_reg_read_u64(struct ps3_instance *instance, void *reg) +{ + unsigned long long value = 0; + (void)instance; +#ifndef _WINDOWS +#if defined(readq) && defined(CONFIG_64BIT) + value = readq(reg); +#else + value = (((unsigned long long)readl(reg + 0x4UL) << 32) | + (unsigned long long)readl(reg)); +#endif +#else + + value = StorPortReadRegisterUlong64(instance, reg); + +#endif + return value; +} + +void __iomem *ps3_reg_set_ioremap(struct pci_dev *pdev, unsigned long reg_bar) +{ + resource_size_t base_addr = 0; + + base_addr = pci_resource_start(pdev, reg_bar); + return ioremap(base_addr, PS3_REGISTER_SET_SIZE); +} diff --git a/drivers/scsi/linkdata/ps3stor/ps3_pci.h b/drivers/scsi/linkdata/ps3stor/ps3_pci.h new file mode 100644 index 0000000000000000000000000000000000000000..fe555e1f8ff4d6bdd482b28b5b7849d4ddc310dc --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/ps3_pci.h @@ -0,0 +1,66 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _PS3_PCI_H_ +#define _PS3_PCI_H_ + +#ifdef _WINDOWS +#include "ps3_def.h" + +#define PCI_CAP_ID_MSIX PCI_CAPABILITY_ID_MSIX +#define PCI_CAP_ID_MSI PCI_CAPABILITY_ID_MSI + +#define PCI_MSIX_FLAGS_ENABLE 0x8000 +#define PCI_MSIX_FLAGS 2 +#define PCI_MSI_FLAGS 2 +#define PCI_MSI_FLAGS_ENABLE 0x0001 +#define PCI_COMMAND_INTX_DISABLE 0x400 +#define PCI_COMMAND 0x04 +#define PCI_STATUS 0x06 +#define PCI_STATUS_INTERRUPT 0x08 +#define PCI_MSIX_FLAGS_QSIZE 0x07FF +#define PCI_MSI_FLAGS_QMASK 0x000e +#endif + +#ifndef _WINDOWS +#if defined PS3_HARDWARE_ASIC +#include "ps3_ioc_manager.h" +#endif +#endif + +struct ps3_instance; + +#ifdef _WINDOWS +struct ps3_pci_context { + unsigned long long slot_number; + unsigned short vendor_id; + unsigned short device_id; + unsigned short sub_vendor_id; + unsigned short sub_device_id; + unsigned long long bar_base_addr; + unsigned short irq_vec_count; + unsigned short valid_irq_count; + unsigned char pci_irq_type; +}; + +int ps3_pci_init(struct ps3_instance *instance, void *config); + +void ps3_pci_exit(struct ps3_instance *instance); + +void ps3_pci_intx(struct ps3_instance *instance, unsigned char enable); + +#endif +int ps3_pci_find_capability(struct ps3_instance *instance, int cap_id); + +int ps3_pci_read_config_word(struct ps3_instance *instance, unsigned int offset, + unsigned short *val); + +int ps3_pci_write_config_word(struct ps3_instance *instance, + unsigned int offset, unsigned short val); + +void ps3_reg_write_u64(struct ps3_instance *instance, unsigned long long val, + void *reg); + +unsigned long long ps3_reg_read_u64(struct ps3_instance *instance, void *reg); + +void __iomem *ps3_reg_set_ioremap(struct pci_dev *pdev, unsigned long reg_bar); + +#endif diff --git a/drivers/scsi/linkdata/ps3stor/ps3_platform_utils.c b/drivers/scsi/linkdata/ps3stor/ps3_platform_utils.c new file mode 100644 index 0000000000000000000000000000000000000000..6768864e5619c38b6ca167472889324138bbe2ce --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/ps3_platform_utils.c @@ -0,0 +1,407 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "ps3_platform_utils.h" +#include "ps3_instance_manager.h" +#include "ps3_mgr_channel.h" +#include "ps3_driver_log.h" +#ifndef _WINDOWS +#include +#endif +#include "ps3_kernel_version.h" + +#ifdef _WINDOWS +int ps3_dma_free(struct ps3_instance *instance, size_t length, void *buffer) +{ + int ret = PS3_SUCCESS; + unsigned long status; + + if (buffer == NULL || length == 0) { + ret = -PS3_FAILED; + goto l_out; + } + + status = StorPortFreeContiguousMemorySpecifyCache(instance, buffer, + length, MmCached); + + if (status != STOR_STATUS_SUCCESS) + ret = -PS3_FAILED; +l_out: + return ret; +} + +int ps3_dma_alloc(struct ps3_instance *instance, size_t length, void **buffer, + unsigned long long *phy_addr) +{ + int ret = PS3_SUCCESS; + unsigned long len; + unsigned long status; + PHYSICAL_ADDRESS minPhysicalAddress; + PHYSICAL_ADDRESS maxPhysicalAddress; + PHYSICAL_ADDRESS boundaryPhysicalAddress; + STOR_PHYSICAL_ADDRESS PhysicalAddress; + + minPhysicalAddress.QuadPart = 0; + maxPhysicalAddress.QuadPart = 0xFFFFFFFFFFFF; + boundaryPhysicalAddress.QuadPart = 0; + + status = StorPortAllocateContiguousMemorySpecifyCacheNode( + instance, length, minPhysicalAddress, maxPhysicalAddress, + boundaryPhysicalAddress, MmCached, MM_ANY_NODE_OK, buffer); + + if (status != STOR_STATUS_SUCCESS) { + LOG_ERROR("alloc dma buffer failed, length:%d, status:0x%x\n", + length, status); + ret = -PS3_FAILED; + goto l_out; + } + + PhysicalAddress = + StorPortGetPhysicalAddress(instance, NULL, *buffer, &len); + *phy_addr = (unsigned long long)PhysicalAddress.QuadPart; + if (PhysicalAddress.QuadPart == 0) { + LOG_ERROR("dma buffer remap fail\n"); + ps3_dma_free(instance, length, *buffer); + *buffer = NULL; + ret = -PS3_FAILED; + } + +l_out: + return ret; +} + +#endif + +void *ps3_kcalloc(struct ps3_instance *instance, unsigned int blocks, + unsigned int block_size) +{ + void *ret = NULL; +#ifndef _WINDOWS + (void)instance; + ret = kcalloc(blocks, block_size, GFP_KERNEL); +#else + unsigned long status = StorPortAllocatePool( + instance, blocks * block_size, 'd3sp', &ret); + + if (status != STOR_STATUS_SUCCESS) { + LOG_ERROR("host_no:%d, memory alloc failed, status0x%x\n", + PS3_HOST(instance), status); + ret = NULL; + } else { + memset(ret, 0, blocks * block_size); + } + +#endif + + if (ret == NULL) { + LOG_ERROR("host_no:%u, memory:%u %u alloc failed\n", + PS3_HOST(instance), blocks, block_size); + } + + return ret; +} + +void ps3_kfree(struct ps3_instance *instance, void *buffer) +{ +#ifndef _WINDOWS + (void)instance; + if (buffer != NULL) + kfree(buffer); +#else + unsigned long status = StorPortFreePool(instance, buffer); + + if (status != STOR_STATUS_SUCCESS) { + LOG_ERROR("host_no:%u, memory free failed, status0x%x\n", + PS3_HOST(instance), status); + } + +#endif +} + +void *ps3_kzalloc(struct ps3_instance *instance, unsigned int size) +{ + return ps3_kcalloc(instance, 1, size); +} + +void ps3_vfree(struct ps3_instance *instance, void *buffer) +{ +#ifndef _WINDOWS + (void)instance; + vfree(buffer); +#else + unsigned long status = StorPortFreePool(instance, buffer); + + if (status != STOR_STATUS_SUCCESS) { + LOG_ERROR("host_no:%u, memory free failed, status0x%x\n", + PS3_HOST(instance), status); + } + +#endif +} + +void *ps3_vzalloc(struct ps3_instance *instance, unsigned int size) +{ + void *ret = NULL; +#ifndef _WINDOWS + (void)instance; + ret = vzalloc(size); +#else + unsigned long status = + StorPortAllocatePool(instance, size, 'd3sp', &ret); + + if (status != STOR_STATUS_SUCCESS) { + LOG_ERROR("host_no:%d, memory alloc failed, status0x%x\n", + PS3_HOST(instance), status); + ret = NULL; + } else { + memset(ret, 0, size); + } + +#endif + + if (ret == NULL) { + LOG_ERROR("host_no:%u, memory:%u alloc failed\n", + PS3_HOST(instance), size); + } + + return ret; +} + +int ps3_wait_for_completion_timeout(void *sync_done, unsigned long time_out) +{ + int ret = PS3_SUCCESS; +#ifdef _WINDOWS + NTSTATUS wait_ret = STATUS_SUCCESS; + LARGE_INTEGER win_timeout = { 0 }; + + if (time_out > 0) { + win_timeout.QuadPart = (long long)(time_out * (-10000000LL)); + wait_ret = KeWaitForSingleObject( + sync_done, Executive, KernelMode, FALSE, &win_timeout); + } else { + wait_ret = KeWaitForSingleObject(sync_done, Executive, + KernelMode, FALSE, NULL); + } + + if (wait_ret == STATUS_TIMEOUT) + ret = -PS3_TIMEOUT; +#else + unsigned short timeout = 0; + + if (time_out > 0) { + timeout = wait_for_completion_timeout( + (struct completion *)sync_done, time_out * HZ); + if (timeout == 0) + ret = -PS3_TIMEOUT; + } else { + wait_for_completion((struct completion *)sync_done); + } +#endif + return ret; +} + +int ps3_wait_cmd_for_completion_timeout(struct ps3_instance *instance, + struct ps3_cmd *cmd, + unsigned long timeout) +{ + int ret = PS3_SUCCESS; + unsigned long time_out; +#ifdef _WINDOWS + (void)instance; + time_out = max_t(unsigned long, cmd->time_out, timeout); + ret = ps3_wait_for_completion_timeout(&cmd->sync_done, time_out); +#else + if (cmd->time_out == 0 && cmd->is_interrupt) { + ps3_wait_cmd_for_completion_interrupt(instance, cmd); + } else { + time_out = max_t(unsigned long, cmd->time_out, timeout); + ret = ps3_wait_for_completion_timeout(&cmd->sync_done, + time_out); + } +#endif + return ret; +} + +int ps3_scsi_device_get(struct ps3_instance *instance, struct scsi_device *sdev) +{ +#ifdef _WINDOWS + return ps3_scsi_device_get_win(instance, sdev); +#else + (void)instance; + return scsi_device_get(sdev); +#endif +} + +void ps3_scsi_device_put(struct ps3_instance *instance, + struct scsi_device *sdev) +{ +#ifdef _WINDOWS + ps3_scsi_device_put_win(instance, sdev); +#else + (void)instance; + scsi_device_put(sdev); +#endif +} + +#ifndef _WINDOWS + +#if defined(PS3_SCSI_DEVICE_LOOKUP) +struct scsi_device *__ps3_scsi_device_lookup_check(struct Scsi_Host *shost, + unsigned int channel, + unsigned int id, + unsigned int lun) +{ + struct scsi_device *sdev = NULL; + + list_for_each_entry(sdev, &shost->__devices, siblings) { + if (sdev->sdev_state == SDEV_DEL) + continue; + if (sdev->channel == channel && sdev->id == id && + sdev->lun == lun) + return sdev; + } + + return NULL; +} + +struct scsi_device *ps3_scsi_device_lookup_check(struct Scsi_Host *shost, + unsigned int channel, + unsigned int id, + unsigned int lun) +{ + struct scsi_device *sdev = NULL; + unsigned long flags = 0; + + spin_lock_irqsave(shost->host_lock, flags); + sdev = __ps3_scsi_device_lookup_check(shost, channel, id, lun); + if (sdev && scsi_device_get(sdev)) + sdev = NULL; + spin_unlock_irqrestore(shost->host_lock, flags); + + return sdev; +} + +#endif + +#endif + +struct scsi_device *ps3_scsi_device_lookup(struct ps3_instance *instance, + unsigned char channel, + unsigned short target_id, + unsigned char lun) +{ +#ifdef _WINDOWS + (void)lun; + struct scsi_device *sdev = ps3_scsi_device_lookup_win( + instance, channel, (unsigned char)target_id); + if (sdev != NULL && sdev->unit_start == 1) + return sdev; + return NULL; +#else +#if defined(PS3_SCSI_DEVICE_LOOKUP) + return ps3_scsi_device_lookup_check(instance->host, channel, target_id, + lun); +#else + return scsi_device_lookup(instance->host, channel, target_id, lun); +#endif +#endif +} + +void ps3_scsi_remove_device(struct ps3_instance *instance, + struct scsi_device *sdev) +{ +#ifdef _WINDOWS + ps3_scsi_remove_device_win(instance, sdev); +#else + (void)instance; + scsi_remove_device(sdev); +#endif +} + +int ps3_scsi_add_device(struct ps3_instance *instance, unsigned char channel, + unsigned short target_id, unsigned char lun) +{ +#ifdef _WINDOWS + (void)lun; + return ps3_scsi_add_device_win(instance, channel, + (unsigned char)target_id); +#else + return scsi_add_device(instance->host, channel, target_id, lun); +#endif +} + +unsigned long long ps3_now_ms_get(void) +{ +#ifdef _WINDOWS + LARGE_INTEGER timestamp; + + KeQuerySystemTime(×tamp); + return timestamp.QuadPart / 10000; +#else + return ktime_to_ms(ktime_get_real()); +#endif +} + +unsigned long long ps3_1970_now_ms_get(void) +{ +#ifdef _WINDOWS + unsigned long long timestamp; + LARGE_INTEGER timestamp1970; + TIME_FIELDS timefiled; + + timefiled.Year = 1970; + timefiled.Month = 1; + timefiled.Day = 1; + timefiled.Hour = 0; + timefiled.Minute = 0; + timefiled.Second = 0; + timefiled.Milliseconds = 0; + + RtlTimeFieldsToTime(&timefiled, ×tamp1970); + timestamp = ps3_now_ms_get(); + return timestamp - (timestamp1970.QuadPart / 10000); +#else + return ps3_now_ms_get(); +#endif +} +#ifdef _WINDOWS +int ps3_now_format_get(char *buff, int buf_len) +{ +#ifdef _WINDOWS + LARGE_INTEGER timestamp; + + KeQuerySystemTime(×tamp); + LARGE_INTEGER localtime; + TIME_FIELDS timefiled; + + ExSystemTimeToLocalTime(×tamp, &localtime); + RtlTimeToTimeFields(&localtime, &timefiled); + + return snprintf(buff, buf_len, "%04ld-%02d-%02d_%02d:%02d:%02d.%03d", + timefiled.Year, timefiled.Month, timefiled.Day, + timefiled.Hour, timefiled.Minute, timefiled.Second, + timefiled.Milliseconds); +#else + struct timeval tv; + struct tm td; + + do_gettimeofday(&tv); + time_to_tm(tv.tv_sec, -sys_tz.tz_minuteswest * 60, &td); + + return snprintf(buff, buf_len, "%04ld-%02d-%02d_%02d:%02d:%02d", + td.tm_year + 1900, td.tm_mon + 1, td.tm_mday, + td.tm_hour, td.tm_min, td.tm_sec); +#endif +} +#endif +unsigned long long ps3_tick_count_get(void) +{ +#ifdef _WINDOWS + LARGE_INTEGER tick_count; + LARGE_INTEGER tick_frequency; + + tick_count = KeQueryPerformanceCounter(&tick_frequency); + return (unsigned long long)(tick_count.QuadPart * 1000000 / + tick_frequency.QuadPart); +#else + return (unsigned long long)ktime_to_us(ktime_get_real()); +#endif +} diff --git a/drivers/scsi/linkdata/ps3stor/ps3_platform_utils.h b/drivers/scsi/linkdata/ps3stor/ps3_platform_utils.h new file mode 100644 index 0000000000000000000000000000000000000000..e0905cb94da23c215c4f01ef5f4311959c933edc --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/ps3_platform_utils.h @@ -0,0 +1,525 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _PS3_PLATFORM_UTILS_H_ +#define _PS3_PLATFORM_UTILS_H_ + +#ifdef _WINDOWS +#include "ps3_def.h" +#else +#include +#include +#include +#include +#include "linux/kernel.h" +#endif + +#include "ps3_err_def.h" + +struct ps3_instance; +struct scsi_device; +struct ps3_cmd; + +#define scsi_cmnd_cdb(scmnd) ((scmnd)->cmnd) +#define scsi_device_private_data(scmnd) (PS3_SDEV_PRI_DATA((scmnd)->device)) +#ifdef _WINDOWS +#define scsi_host_data(scmnd) ((struct ps3_instance *)((scmnd)->instance)) +#else +#define scsi_host_data(scmnd) \ + ((struct ps3_instance *)((scmnd)->device->host->hostdata)) +#endif + +#ifdef _WINDOWS +#define ps3_container_of(ptr, type, member) \ + ((type *)((char *)ptr - offsetof(type, member))) +#else +#define ps3_container_of container_of +#endif +#define MAX_MDELAY (1) +#ifndef PS3_FALSE +#define PS3_FALSE (0) +#endif +#ifndef PS3_TRUE +#define PS3_TRUE (1) +#endif +#ifndef PS3_MAX +#define PS3_MAX(a, b) (((a) > (b)) ? (a) : (b)) +#endif +#ifndef PS3_MIN +#define PS3_MIN(a, b) (((a) < (b)) ? (a) : (b)) +#endif +#ifndef PS3_DESC +#define PS3_DESC(a) 1 +#endif + +static inline void ps3_mutex_init(struct mutex *mutex_lock) +{ +#ifdef _WINDOWS + ExInitializeFastMutex(&mutex_lock->mutex); +#else + mutex_init(mutex_lock); +#endif +} + +static inline void ps3_mutex_destroy(struct mutex *mutex_lock) +{ +#ifdef _WINDOWS + (void)mutex_lock; +#else + mutex_destroy(mutex_lock); +#endif +} + +static inline int ps3_mutex_lock(struct mutex *mtx_lock) +{ +#ifdef _WINDOWS + if (KeGetCurrentIrql() <= APC_LEVEL) { + ExAcquireFastMutex(&mtx_lock->mutex); + + return PS3_SUCCESS; + } + return -PS3_FAILED; +#else + mutex_lock(mtx_lock); + return PS3_SUCCESS; +#endif +} +static inline int ps3_mutex_trylock(struct mutex *mutex_lock) +{ + int ret = PS3_SUCCESS; +#ifdef _WINDOWS + if (KeGetCurrentIrql() > APC_LEVEL) { + ret = -PS3_FAILED; + goto l_out; + } + + if (!ExTryToAcquireFastMutex(&mutex_lock->mutex)) { + ret = -PS3_FAILED; + goto l_out; + } +l_out: +#else + ret = mutex_trylock(mutex_lock); +#endif + return ret; +} + +static inline int ps3_mutex_unlock(struct mutex *mutex_lock) +{ +#ifdef _WINDOWS + if (KeGetCurrentIrql() <= APC_LEVEL) { + ExReleaseFastMutex(&mutex_lock->mutex); + + return PS3_SUCCESS; + } + + return -PS3_FAILED; +#else + mutex_unlock(mutex_lock); + return PS3_SUCCESS; + +#endif +} + +static inline int ps3_atomic_read(atomic_t *value) +{ +#ifdef _WINDOWS + return value->value; +#else + return atomic_read(value); +#endif +} + +static inline int ps3_atomic_dec(atomic_t *value) +{ +#ifdef _WINDOWS + return (int)InterlockedDecrement((LONG *)(&value->value)); +#else + atomic_dec(value); + return PS3_SUCCESS; +#endif +} + +static inline int ps3_atomic_add(int i, atomic_t *value) +{ +#ifdef _WINDOWS + return (int)_InlineInterlockedAdd((LONG *)&value->value, (LONG)i); +#else + atomic_add(i, value); + return PS3_SUCCESS; +#endif +} + +static inline int ps3_atomic_sub(int i, atomic_t *value) +{ +#ifdef _WINDOWS + return (int)_InlineInterlockedAdd((LONG *)&value->value, (LONG)-i); +#else + atomic_sub(i, value); + return PS3_SUCCESS; +#endif +} + +static inline int ps3_atomic_cmpxchg(atomic_t *value, int old, int cur) +{ +#ifdef _WINDOWS + return (int)InterlockedCompareExchange((LONG *)&value->value, (LONG)cur, + (LONG)old); +#else + return atomic_cmpxchg(value, cur, old); +#endif +} + +static inline unsigned char ps3_atomic_add_unless(atomic_t *value, int a, int u) +{ +#ifdef _WINDOWS + + int c = 0; + int old = 0; + + c = value->value; + while (c != u && (old = ps3_atomic_cmpxchg(value, c, c + a)) != c) + c = old; + + return c != u; +#else + return atomic_add_unless(value, a, u); +#endif +} + +static inline int ps3_atomic_inc(atomic_t *value) +{ +#ifdef _WINDOWS + return (int)InterlockedIncrement((LONG *)(&value->value)); +#else + atomic_inc(value); + return PS3_SUCCESS; +#endif +} + +static inline int ps3_atomic_inc_return(atomic_t *value) +{ +#ifdef _WINDOWS + return (int)InterlockedIncrement((LONG *)(&value->value)); +#else + return atomic_inc_return(value); +#endif +} + +static inline int ps3_atomic_dec_return(atomic_t *value) +{ +#ifdef _WINDOWS + return (int)InterlockedDecrement((LONG *)(&value->value)); +#else + return atomic_dec_return(value); +#endif +} + +static inline long long ps3_atomic64_inc(atomic64_t *value) +{ +#ifdef _WINDOWS + return (long long)InterlockedIncrement64((LONG64 *)(&value->value)); +#else + atomic64_inc(value); + return PS3_SUCCESS; +#endif +} + +static inline long long ps3_atomic64_inc_return(atomic64_t *value) +{ +#ifdef _WINDOWS + return (long long)InterlockedIncrement64((LONG64 *)(&value->value)); +#else + return atomic64_inc_return(value); +#endif +} + +static inline long long ps3_atomic64_read(atomic64_t *value) +{ +#ifdef _WINDOWS + return value->value; +#else + return atomic64_read(value); +#endif +} + +static inline void ps3_atomic64_set(atomic64_t *value, long long i) +{ +#ifdef _WINDOWS + value->value = i; +#else + atomic64_set(value, i); +#endif +} + +static inline void ps3_atomic_set(atomic_t *value, int i) +{ +#ifdef _WINDOWS + value->value = i; +#else + atomic_set(value, i); +#endif +} + +static inline long long ps3_atomic64_add(long long i, atomic64_t *value) +{ +#ifdef _WINDOWS + return (long long)_InlineInterlockedAdd64((LONG64 *)&value->value, + (LONG64)i); +#else + atomic64_add(i, value); + return PS3_SUCCESS; +#endif +} + +static inline long long ps3_atomic64_dec(atomic64_t *value) +{ +#ifdef _WINDOWS + return (long long)InterlockedDecrement64((LONG64 *)&value->value); +#else + atomic64_dec(value); + return PS3_SUCCESS; +#endif +} + +static inline void ps3_spin_lock_init(spinlock_t *lock) +{ +#ifdef _WINDOWS + KeInitializeSpinLock(&lock->lock); +#else + spin_lock_init(lock); +#endif +} + +static inline void ps3_spin_lock(spinlock_t *lock, unsigned long *flag) +{ +#ifdef _WINDOWS + KeAcquireSpinLock(&lock->lock, (PKIRQL)flag); +#else + (void)flag; + spin_lock(lock); +#endif +} + +static inline void ps3_spin_lock_irqsave(spinlock_t *lock, unsigned long *flag) +{ +#ifdef _WINDOWS + KeAcquireSpinLock(&lock->lock, (PKIRQL)flag); +#else + spin_lock_irqsave(lock, *flag); +#endif +} + +static inline void ps3_spin_unlock(spinlock_t *lock, unsigned long flag) +{ +#ifdef _WINDOWS + KeReleaseSpinLock(&lock->lock, (KIRQL)flag); +#else + (void)flag; + spin_unlock(lock); +#endif +} + +static inline void ps3_spin_unlock_irqrestore(spinlock_t *lock, + unsigned long flag) +{ +#ifdef _WINDOWS + KeReleaseSpinLock(&lock->lock, (KIRQL)flag); +#else + spin_unlock_irqrestore(lock, flag); +#endif +} + +int ps3_wait_for_completion_timeout(void *sync_done, unsigned long Timeout); +int ps3_wait_cmd_for_completion_timeout(struct ps3_instance *instance, + struct ps3_cmd *cmd, + unsigned long timeout); + +#ifdef _WINDOWS + +#define complete(x) KeSetEvent(x, IO_NO_INCREMENT, FALSE) +#define init_completion(x) KeInitializeEvent(x, SynchronizationEvent, FALSE) + +static inline int list_empty(const struct list_head *head) +{ + return head->Blink == head; +} + +#define list_entry(ptr, type, member) CONTAINING_RECORD(ptr, type, member) + +#define list_for_each(pos, head) \ + for (pos = (head)->Blink; pos != (head); pos = pos->Blink) + +#define list_first_entry(ptr, type, member) \ + list_entry((ptr)->Blink, type, member) +#define list_next_entry(pos, type, member) \ + list_entry((pos)->member.Blink, type, member) +#define list_for_each_entry(pos, type, head, member) \ + for (pos = list_first_entry(head, type, member); \ + &pos->member != (head); pos = list_next_entry(pos, type, member)) +#define list_for_each_entry_safe(pos, type, tmp, head, member) \ + for (pos = list_first_entry(head, type, member), \ + tmp = list_next_entry(pos, type, member); \ + &pos->member != (head); \ + pos = tmp, tmp = list_next_entry(tmp, type, member)) + +static inline void INIT_LIST_HEAD(struct list_head *list) +{ + InitializeListHead((PLIST_ENTRY)list); +} + +static inline void list_del(struct list_head *entry) +{ + RemoveEntryList((PLIST_ENTRY)entry); +} + +static inline void list_del_init(struct list_head *entry) +{ + list_del(entry); + INIT_LIST_HEAD(entry); +} + +static inline void list_add_tail(struct list_head *entry, + struct list_head *head) +{ + InsertTailList((PLIST_ENTRY)head, (PLIST_ENTRY)entry); +} + +static inline struct list_head *list_remove_head(struct list_head *head) +{ + return (struct list_head *)RemoveHeadList((PLIST_ENTRY)head); +} + +inline int kstrtou16(const char *s, unsigned int base, unsigned short *res) +{ + unsigned long tmp = 0; + int ret = RtlCharToInteger(s, base, &tmp); + + if (ret != STATUS_SUCCESS) + goto l_out; + + if (tmp != (unsigned long long)(unsigned short)tmp) { + ret = -34; + goto l_out; + } + + *res = (unsigned short)tmp; +l_out: + return ret; +} + +inline int kstrtoint(const char *s, unsigned int base, int *res) +{ + unsigned long tmp = 0; + int ret = RtlCharToInteger(s, base, &tmp); + + if (ret != STATUS_SUCCESS) + goto l_out; + + if (tmp != (unsigned long long)(int)tmp) { + ret = -34; + goto l_out; + } + + *res = (int)tmp; +l_out: + return ret; +} + +inline int kstrtouint(const char *s, unsigned int base, unsigned int *res) +{ + unsigned long tmp = 0; + int ret = RtlCharToInteger(s, base, &tmp); + + if (ret != STATUS_SUCCESS) + goto l_out; + + if (tmp != (unsigned long long)(unsigned int)tmp) { + ret = -34; + goto l_out; + } + + *res = (unsigned int)tmp; +l_out: + return ret; +} + +inline int kstrtou64(const char *s, unsigned long long base, + unsigned long long *res) +{ + unsigned long tmp = 0; + int ret = RtlCharToInteger(s, base, &tmp); + + if (ret != STATUS_SUCCESS) + goto l_out; + + if (tmp != (unsigned long long)tmp) { + ret = -34; + goto l_out; + } + + *res = (unsigned long)tmp; +l_out: + return ret; +} + +int ps3_dma_free(struct ps3_instance *instance, size_t length, void *buffer); + +int ps3_dma_alloc(struct ps3_instance *instance, size_t length, void **buffer, + unsigned long long *phy_addr); + +#endif + +static inline void ps3_msleep(unsigned int ms) +{ +#ifdef _WINDOWS + StorPortStallExecution((unsigned long)ms * 1000); +#else + msleep(ms); +#endif +} + +static inline void ps3_mdelay(unsigned int ms) +{ +#ifndef _WINDOWS + unsigned int count = (ms / MAX_MDELAY); + unsigned int remain = (ms % MAX_MDELAY); + + do { + udelay(1000 * MAX_MDELAY); + count--; + } while (count); + + if (remain != 0) + udelay(remain * 1000); +#else + StorPortStallExecution((unsigned long)ms * 1000); +#endif +} +void *ps3_kcalloc(struct ps3_instance *instance, unsigned int blocks, + unsigned int block_size); +void ps3_kfree(struct ps3_instance *instance, void *buffer); +void *ps3_kzalloc(struct ps3_instance *instance, unsigned int size); + +void ps3_vfree(struct ps3_instance *instance, void *buffer); +void *ps3_vzalloc(struct ps3_instance *instance, unsigned int size); + +int ps3_scsi_device_get(struct ps3_instance *instance, + struct scsi_device *sdev); +void ps3_scsi_device_put(struct ps3_instance *instance, + struct scsi_device *sdev); +struct scsi_device *ps3_scsi_device_lookup(struct ps3_instance *instance, + unsigned char channel, + unsigned short target_id, + unsigned char lun); +void ps3_scsi_remove_device(struct ps3_instance *instance, + struct scsi_device *sdev); +int ps3_scsi_add_device(struct ps3_instance *instance, unsigned char channel, + unsigned short target_id, unsigned char lun); + +unsigned long long ps3_now_ms_get(void); +#ifdef _WINDOWS +int ps3_now_format_get(char *buff, int buf_len); +#endif +unsigned long long ps3_1970_now_ms_get(void); +unsigned long long ps3_tick_count_get(void); + +#endif diff --git a/drivers/scsi/linkdata/ps3stor/ps3_qos.c b/drivers/scsi/linkdata/ps3stor/ps3_qos.c new file mode 100644 index 0000000000000000000000000000000000000000..ee20204772621967b9b7e49be638dafd4c8cb0a2 --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/ps3_qos.c @@ -0,0 +1,4302 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include +#include +#include +#include "ps3_ioc_manager.h" +#include "ps3_util.h" +#include "ps3_instance_manager.h" +#include "ps3_htp_def.h" +#include "ps3_scsih.h" +#include "ps3_mgr_cmd.h" +#include "ps3_driver_log.h" +#include "ps3_htp_dev.h" +#include "ps3_scsih_cmd_parse.h" +#include "ps3_scsi_cmd_err.h" +#include "ps3_r1x_write_lock.h" +#include "ps3_cmd_statistics.h" +#include "ps3_scsih_raid_engine.h" +#include "ps3_ioc_state.h" +#include "ps3_kernel_version.h" + +#define PS3_QOS_PD_IS_VD_MEMBER(qos_pd_mgr) ((qos_pd_mgr)->vd_id > 0) + +#define PS3_QOS_JBOD_PD_WAITQ_ID(qos_pd_mgr) ((qos_pd_mgr)->waitq_cnt - 1) + +#define PS3_QOS_VD_HDD_DELAY_THD_MS 500 + +#define PS3_QOS_VD_SDD_DELAY_THD_MS 1 + +static inline unsigned char ps3_tfifo_depth_get(struct ps3_instance *instance, + unsigned long long *depth) +{ + unsigned char ret = PS3_TRUE; + + PS3_IOC_REG_READ_SAFE_WITH_RETRY(instance, reg_f.Excl_reg, + ps3TfifoDepth, *depth); + if (*depth == U64_MAX) { + LOG_INFO("host_no:%u read reg ps3TfifoDepth failed!\n", + PS3_HOST(instance)); + *depth = 0; + ret = PS3_FALSE; + goto l_out; + } + + *depth &= 0xffff; + +l_out: + return ret; +} + +static inline unsigned char ps3_cmdq_depth_get(struct ps3_instance *instance, + unsigned long long *depth) +{ + unsigned char ret = PS3_TRUE; + + PS3_IOC_REG_READ_SAFE_WITH_RETRY(instance, reg_f.Excl_reg, + ps3CmdQueueDepth, *depth); + + if (*depth == U64_MAX) { + LOG_INFO("host_no:%u read reg ps3CmdQueueDepth failed!\n", + PS3_HOST(instance)); + *depth = 0; + ret = PS3_FALSE; + goto l_out; + } + + *depth &= 0xffff; + +l_out: + return ret; +} + +static inline unsigned char ps3_mgrq_depth_get(struct ps3_instance *instance, + unsigned long long *depth) +{ + unsigned char ret = PS3_TRUE; + + PS3_IOC_REG_READ_SAFE_WITH_RETRY(instance, reg_f.Excl_reg, + ps3MgrQueueDepth, *depth); + + if (*depth == U64_MAX) { + LOG_INFO("host_no:%u read reg ps3MgrQueueDepth failed!\n", + PS3_HOST(instance)); + *depth = 0; + ret = PS3_FALSE; + goto l_out; + } + + *depth &= 0xffff; + +l_out: + return ret; +} + +static inline struct ps3_qos_pd_mgr * +ps3_qos_pd_mgr_get(struct ps3_instance *instance, unsigned short disk_id) +{ + return &instance->qos_context.pd_ctx.qos_pd_mgrs[disk_id]; +} + +static inline bool ps3_is_nvme_direct_cmd(struct ps3_qos_pd_mgr *qos_pd_mgr, + struct ps3_cmd *cmd) +{ + return (qos_pd_mgr->dev_type == PS3_DEV_TYPE_NVME_SSD && + cmd->io_attr.direct_flag == PS3_CMDWORD_DIRECT_OK); +} + +static void ps3_qos_update_pd_quota(struct ps3_cmd *cmd) +{ + struct ps3_qos_pd_mgr *qos_pd_mgr = NULL; + unsigned short i = 0; + unsigned short disk_id = 0; + int pd_used_quota = 0; + atomic_t *pd_rsc = NULL; + + for (i = 0; i < cmd->target_pd_count; i++) { + if (cmd->target_pd[i].get_quota) { + disk_id = cmd->target_pd[i].flat_disk_id; + qos_pd_mgr = ps3_qos_pd_mgr_get(cmd->instance, disk_id); + if (ps3_is_nvme_direct_cmd(qos_pd_mgr, cmd)) + pd_rsc = &qos_pd_mgr->direct_used_quota; + else + pd_rsc = &qos_pd_mgr->pd_used_quota; + pd_used_quota = ps3_atomic_dec_return(pd_rsc); + LOG_DEBUG( + "update pd quota. host_no:%u t_id:0x%llx CFID:%u direct:%u pid:%u used_quota:%d\n", + PS3_HOST(cmd->instance), cmd->trace_id, + cmd->index, cmd->cmd_word.direct, disk_id, + pd_used_quota); + break; + } + } +} + +static inline struct ps3_qos_vd_mgr *ps3_qos_vd_mgr_get(struct ps3_cmd *cmd) +{ + unsigned short disk_id = 0; + unsigned short flat_disk_id = 0; + struct ps3_instance *instance = cmd->instance; + + if (cmd->io_attr.dev_type == PS3_DEV_TYPE_VD) { + disk_id = PS3_VDID(&cmd->io_attr.vd_entry->diskPos); + flat_disk_id = + get_offset_of_vdid(PS3_VDID_OFFSET(instance), disk_id); + return &instance->qos_context.vd_ctx.qos_vd_mgrs[flat_disk_id]; + } else { + return &instance->qos_context.vd_ctx + .qos_vd_mgrs[instance->qos_context.max_vd_count]; + } +} + +static inline struct ps3_qos_vd_mgr * +ps3_qos_vd_mgr_get_by_id(struct ps3_instance *instance, unsigned short disk_id) +{ + struct ps3_qos_vd_context *qos_vd_ctx = NULL; + unsigned short flat_disk_id = 0; + + qos_vd_ctx = &instance->qos_context.vd_ctx; + flat_disk_id = get_offset_of_vdid(PS3_VDID_OFFSET(instance), disk_id); + return &qos_vd_ctx->qos_vd_mgrs[flat_disk_id]; +} + +static void ps3_qos_update_vd_quota(struct ps3_cmd *cmd) +{ + struct ps3_qos_vd_mgr *qos_vd_mgr = NULL; + + if (cmd->io_attr.dev_type != PS3_DEV_TYPE_VD) { + qos_vd_mgr = ps3_qos_vd_mgr_get(cmd); + ps3_atomic_inc(&qos_vd_mgr->vd_quota); + } +} + +static inline unsigned char ps3_qos_enable(struct ps3_instance *instance) +{ + return instance->qos_context.qos_switch; +} + +static void ps3_qos_cmd_resend_fail(struct ps3_cmd *cmd, int ret) +{ + struct ps3_scsi_priv_data *pri_data = NULL; + struct ps3_instance *instance = cmd->instance; + struct scsi_cmnd *s_cmd = cmd->scmd; + + if (ret == -PS3_RECOVERED || ret == -PS3_RETRY) + s_cmd->result = PS3_SCSI_RESULT_HOST_STATUS(DID_RESET); + else + s_cmd->result = PS3_SCSI_RESULT_HOST_STATUS(DID_NO_CONNECT); + + if (cmd->is_got_r1x == 1) { + pri_data = (struct ps3_scsi_priv_data *)s_cmd->device->hostdata; + ps3_r1x_write_unlock(&pri_data->lock_mgr, cmd); + } + + LOG_INFO_IN_IRQ(instance, + "t_id:0x%llx hno:%u tag:%d cmd send err ret:%d\n", + cmd->trace_id, PS3_HOST(instance), cmd->index, ret); + ps3_scsi_dma_unmap(cmd); + s_cmd = cmd->scmd; + PS3_DEV_IO_START_ERR_INC(instance, cmd); + PS3_IO_OUTSTAND_DEC(cmd->instance, s_cmd); + PS3_IO_BACK_ERR_INC(cmd->instance, s_cmd); + PS3_VD_OUTSTAND_DEC(cmd->instance, s_cmd); + PS3_DEV_BUSY_DEC(s_cmd); + ps3_scsi_cmd_free(cmd); + SCMD_IO_DONE(s_cmd); +} + +struct qos_wait_queue * +ps3_qos_cmd_waitq_get(struct ps3_qos_tg_context *qos_tg_ctx, + struct ps3_cmd *cmd) +{ + struct qos_wait_queue *wait_q = NULL; + unsigned short disk_id = 0; + struct ps3_qos_context *qos_ctx = NULL; + + if (cmd->cmd_word.type == PS3_CMDWORD_TYPE_MGR) { + wait_q = &qos_tg_ctx->mgr_cmd_wait_q; + } else { + if (cmd->io_attr.dev_type == PS3_DEV_TYPE_VD) { + disk_id = get_offset_of_vdid( + PS3_VDID_OFFSET(cmd->instance), + PS3_VDID(&cmd->io_attr.vd_entry->diskPos)); + wait_q = &qos_tg_ctx->vd_cmd_waitqs[disk_id]; + } else { + qos_ctx = &cmd->instance->qos_context; + wait_q = + &qos_tg_ctx + ->vd_cmd_waitqs[qos_ctx->max_vd_count]; + } + } + + return wait_q; +} + +static void ps3_hba_qos_cmd_update(struct ps3_cmd *cmd) +{ + struct ps3_qos_tg_context *qos_tg_ctx = + &cmd->instance->qos_context.tg_ctx; + struct ps3_qos_vd_mgr *qos_vd_mgr = NULL; + + if (cmd->cmd_word.type == PS3_CMDWORD_TYPE_MGR) { + if (ps3_atomic_dec_return(&qos_tg_ctx->mgr_share_used) >= 0) { + ps3_atomic_inc(&qos_tg_ctx->share_free_cnt); + } else { + ps3_atomic_inc(&qos_tg_ctx->mgr_share_used); + ps3_atomic_inc(&qos_tg_ctx->mgr_free_cnt); + } + } else { + ps3_qos_update_pd_quota(cmd); + if (cmd->cmd_word.direct != PS3_CMDWORD_DIRECT_OK) { + qos_vd_mgr = ps3_qos_vd_mgr_get(cmd); + if (cmd->io_attr.dev_type != PS3_DEV_TYPE_VD) + ps3_atomic_inc(&qos_vd_mgr->vd_quota); + + if (ps3_atomic_dec_return( + &qos_vd_mgr->share_cmd_used) >= 0) { + ps3_atomic_inc(&qos_tg_ctx->share_free_cnt); + } else { + ps3_atomic_inc(&qos_vd_mgr->share_cmd_used); + ps3_atomic_inc(&qos_vd_mgr->exclusive_cmd_cnt); + } + } + } + LOG_DEBUG("qos cmd update. host_no:%u dev_t:%u share:%u\n", + PS3_HOST(cmd->instance), cmd->io_attr.dev_type, + ps3_atomic_read(&qos_tg_ctx->share_free_cnt)); +} + +static inline unsigned char +ps3_qos_share_cmdword_dec(struct ps3_qos_tg_context *qos_tg_ctx) +{ + unsigned char can_get = PS3_FALSE; + + if (ps3_atomic_dec_return(&qos_tg_ctx->share_free_cnt) >= 0) { + can_get = PS3_TRUE; + } else { + ps3_atomic_inc(&qos_tg_ctx->share_free_cnt); + can_get = PS3_FALSE; + } + + return can_get; +} + +static unsigned char +ps3_qos_share_cmdword_get(struct ps3_qos_tg_context *qos_tg_ctx, + struct ps3_cmd *cmd, struct qos_wait_queue *wait_q) +{ + unsigned char can_get = PS3_FALSE; + unsigned long flag = 0; + struct ps3_qos_vd_mgr *qos_vd_mgr = NULL; + + can_get = ps3_qos_share_cmdword_dec(qos_tg_ctx); + if (!can_get) { + ps3_spin_lock_irqsave(&qos_tg_ctx->lock, &flag); + list_add_tail(&cmd->qos_list, &wait_q->wait_list); + wait_q->count++; + qos_tg_ctx->total_wait_cmd_cnt++; + cmd->qos_waitq_flag = PS3_QOS_CMD_IN_FRAME; + ps3_spin_unlock_irqrestore(&qos_tg_ctx->lock, flag); + LOG_DEBUG( + "insert qos tag waitq.host_no:%u:t_id:0x%llx CFID:%u diskid:%u waitq:%u\n", + PS3_HOST(cmd->instance), cmd->trace_id, cmd->index, + cmd->io_attr.disk_id, wait_q->count); + PS3_QOS_STAT_START(cmd->instance, cmd, PS3_QOS_TAG_QUEUE); + } else { + if (cmd->cmd_word.type == PS3_CMDWORD_TYPE_MGR) { + ps3_atomic_inc(&qos_tg_ctx->mgr_share_used); + } else { + qos_vd_mgr = ps3_qos_vd_mgr_get(cmd); + ps3_atomic_inc(&qos_vd_mgr->share_cmd_used); + } + } + + return can_get; +} + +static bool ps3_qos_mgr_cmdword_get(struct ps3_cmd *cmd) +{ + bool can_get = PS3_FALSE; + struct ps3_instance *instance = cmd->instance; + struct ps3_qos_tg_context *qos_tg_ctx = &instance->qos_context.tg_ctx; + + if (ps3_atomic_dec_return(&qos_tg_ctx->mgr_free_cnt) >= 0) { + can_get = PS3_TRUE; + } else { + ps3_atomic_inc(&qos_tg_ctx->mgr_free_cnt); + can_get = PS3_FALSE; + } + + return can_get; +} + +unsigned char ps3_qos_vd_cmdword_get(struct ps3_cmd *cmd) +{ + unsigned char can_get = PS3_FALSE; + struct ps3_qos_vd_mgr *qos_vd_mgr = NULL; + + qos_vd_mgr = ps3_qos_vd_mgr_get(cmd); + if (ps3_atomic_dec_return(&qos_vd_mgr->exclusive_cmd_cnt) >= 0) { + can_get = PS3_TRUE; + } else { + ps3_atomic_inc(&qos_vd_mgr->exclusive_cmd_cnt); + can_get = PS3_FALSE; + } + + return can_get; +} + +unsigned char ps3_qos_exclusive_cmdword_get(struct ps3_cmd *cmd) +{ + unsigned char can_get = PS3_FALSE; + + if (cmd->cmd_word.type == PS3_CMDWORD_TYPE_MGR) + can_get = ps3_qos_mgr_cmdword_get(cmd); + else + can_get = ps3_qos_vd_cmdword_get(cmd); + + return can_get; +} + +unsigned char ps3_qos_tg_decision(struct ps3_cmd *cmd) +{ + unsigned char can_get = PS3_FALSE; + struct ps3_instance *instance = cmd->instance; + unsigned long flag = 0; + struct qos_wait_queue *cmd_wait_q = NULL; + struct ps3_qos_tg_context *qos_tg_ctx = &instance->qos_context.tg_ctx; + + PS3_QOS_STAT_START(instance, cmd, PS3_QOS_TAG_PRO); + cmd_wait_q = ps3_qos_cmd_waitq_get(qos_tg_ctx, cmd); + if (cmd_wait_q->count == 0) { + can_get = ps3_qos_exclusive_cmdword_get(cmd); + if (can_get) + goto out; + } + + if (likely(qos_tg_ctx->total_wait_cmd_cnt == 0)) { + can_get = + ps3_qos_share_cmdword_get(qos_tg_ctx, cmd, cmd_wait_q); + } else { + ps3_spin_lock_irqsave(&qos_tg_ctx->lock, &flag); + if (qos_tg_ctx->total_wait_cmd_cnt > 0) { + list_add_tail(&cmd->qos_list, &cmd_wait_q->wait_list); + cmd_wait_q->count++; + qos_tg_ctx->total_wait_cmd_cnt++; + cmd->qos_waitq_flag = PS3_QOS_CMD_IN_FRAME; + ps3_spin_unlock_irqrestore(&qos_tg_ctx->lock, flag); + can_get = PS3_FALSE; + LOG_DEBUG( + "insert qos tag waitq.host_no:%u:t_id:0x%llx CFID:%u diskid:%u waitq:%u\n", + PS3_HOST(instance), cmd->trace_id, cmd->index, + cmd->io_attr.disk_id, cmd_wait_q->count); + PS3_QOS_STAT_START(instance, cmd, PS3_QOS_TAG_QUEUE); + } else { + ps3_spin_unlock_irqrestore(&qos_tg_ctx->lock, flag); + can_get = ps3_qos_share_cmdword_get(qos_tg_ctx, cmd, + cmd_wait_q); + } + } +out: + PS3_QOS_STAT_END(instance, cmd, PS3_QOS_TAG_PRO); + return can_get; +} + +static inline bool ps3_qos_pd_quota_add(struct qos_wait_queue *waitq) +{ + unsigned char can_get = PS3_TRUE; + + if (ps3_atomic_inc_return(waitq->used_rsc) > *waitq->free_rsc) { + ps3_atomic_dec(waitq->used_rsc); + can_get = PS3_FALSE; + } + + return can_get; +} + +static void ps3_qos_pd_in_q(struct ps3_cmd *cmd, struct qos_wait_queue *waitq, + unsigned short pd_idx) +{ + unsigned long flag = 0; + + ps3_spin_lock_irqsave(waitq->rsc_lock, &flag); + list_add_tail(&cmd->qos_list, &waitq->wait_list); + waitq->count++; + (*waitq->total_waited_cnt)++; + cmd->qos_waitq_flag = PS3_QOS_CMD_IN_PD; + cmd->first_over_quota_pd_idx = pd_idx; + ps3_spin_unlock_irqrestore(waitq->rsc_lock, flag); + PS3_QOS_STAT_START(cmd->instance, cmd, PS3_QOS_PD_QUEUE); +} + +static struct qos_wait_queue * +ps3_qos_pd_waitq_get(struct ps3_qos_pd_mgr *qos_pd_mgr, struct ps3_cmd *cmd) +{ + unsigned short que_id = 0; + struct ps3_instance *instance = NULL; + + instance = cmd->instance; + if (cmd->io_attr.dev_type == PS3_DEV_TYPE_VD) { + que_id = get_offset_of_vdid( + PS3_VDID_OFFSET(instance), + PS3_VDID(&cmd->io_attr.vd_entry->diskPos)); + } else { + if (ps3_is_nvme_direct_cmd(qos_pd_mgr, cmd)) + que_id = 0; + else + que_id = instance->qos_context.max_vd_count; + } + + return &qos_pd_mgr->waitqs[que_id]; +} + +static unsigned char ps3_qos_pd_quota_get(struct ps3_qos_pd_mgr *qos_pd_mgr, + struct ps3_cmd *cmd, + unsigned short pd_idx, + struct qos_wait_queue *waitq) +{ + unsigned char can_get = PS3_FALSE; + + can_get = ps3_qos_pd_quota_add(waitq); + if (!can_get) { + ps3_qos_pd_in_q(cmd, waitq, pd_idx); + LOG_DEBUG( + "insert qos pd quota waitq.host_no:%u:t_id:0x%llx CFID:%u waitq[%u,%u] did[%u,%u]\n", + PS3_HOST(cmd->instance), cmd->trace_id, cmd->index, + waitq->id, waitq->count, qos_pd_mgr->disk_id, pd_idx); + } else { + cmd->target_pd[pd_idx].get_quota = PS3_TRUE; + } + + return can_get; +} + +static bool ps3_qos_pd_quota_req(struct ps3_qos_pd_mgr *qos_pd_mgr, + struct ps3_cmd *cmd, unsigned short pd_idx) +{ + bool can_get = PS3_FALSE; + unsigned long flag = 0; + struct qos_wait_queue *waitq = NULL; + + waitq = ps3_qos_pd_waitq_get(qos_pd_mgr, cmd); + if (likely((*waitq->total_waited_cnt) == 0)) { + can_get = ps3_qos_pd_quota_get(qos_pd_mgr, cmd, pd_idx, waitq); + } else { + ps3_spin_lock_irqsave(waitq->rsc_lock, &flag); + if ((*waitq->total_waited_cnt) > 0) { + list_add_tail(&cmd->qos_list, &waitq->wait_list); + waitq->count++; + (*waitq->total_waited_cnt)++; + cmd->qos_waitq_flag = PS3_QOS_CMD_IN_PD; + cmd->first_over_quota_pd_idx = pd_idx; + ps3_spin_unlock_irqrestore(waitq->rsc_lock, flag); + can_get = PS3_FALSE; + LOG_DEBUG( + "insert qos pd quota waitq.host_no:%u:t_id:0x%llx CFID:%u waitq[%u,%u] did[%u,%u]\n", + PS3_HOST(cmd->instance), cmd->trace_id, + cmd->index, waitq->id, waitq->count, + qos_pd_mgr->disk_id, pd_idx); + PS3_QOS_STAT_START(cmd->instance, cmd, + PS3_QOS_PD_QUEUE); + } else { + ps3_spin_unlock_irqrestore(waitq->rsc_lock, flag); + can_get = ps3_qos_pd_quota_get(qos_pd_mgr, cmd, pd_idx, + waitq); + } + } + + return can_get; +} + +static bool ps3_qos_pd_quota_check(struct ps3_qos_pd_mgr *qos_pd_mgr, + struct ps3_cmd *cmd) +{ + bool can_get = PS3_FALSE; + unsigned long flag = 0; + struct qos_wait_queue *waitq = NULL; + + waitq = ps3_qos_pd_waitq_get(qos_pd_mgr, cmd); + if (likely((*waitq->total_waited_cnt) == 0)) { + can_get = ps3_qos_pd_quota_add(waitq); + } else { + ps3_spin_lock_irqsave(waitq->rsc_lock, &flag); + if ((*waitq->total_waited_cnt) > 0) { + ps3_spin_unlock_irqrestore(waitq->rsc_lock, flag); + can_get = PS3_FALSE; + } else { + ps3_spin_unlock_irqrestore(waitq->rsc_lock, flag); + can_get = ps3_qos_pd_quota_add(waitq); + } + } + + return can_get; +} + +static inline bool ps3_qos_vd_quota_dec(struct ps3_qos_vd_mgr *qos_vd_mgr) +{ + unsigned char can_get = PS3_FALSE; + + if (ps3_atomic_dec_return(&qos_vd_mgr->vd_quota) >= 0) { + can_get = PS3_TRUE; + } else { + ps3_atomic_inc(&qos_vd_mgr->vd_quota); + can_get = PS3_FALSE; + } + + return can_get; +} + +static void ps3_qos_insert_vd_quota_waitq(struct ps3_qos_vd_mgr *qos_vd_mgr, + struct ps3_cmd *cmd) +{ + unsigned long flag = 0; + struct qos_wait_queue *wait_q = NULL; + + wait_q = &qos_vd_mgr->vd_quota_wait_q; + ps3_spin_lock_irqsave(wait_q->rsc_lock, &flag); + list_add_tail(&cmd->qos_list, &wait_q->wait_list); + wait_q->count++; + cmd->qos_waitq_flag = PS3_QOS_CMD_IN_VD; + ps3_spin_unlock_irqrestore(wait_q->rsc_lock, flag); + PS3_QOS_STAT_START(cmd->instance, cmd, PS3_QOS_VD_QUEUE); + LOG_DEBUG( + "insert qos vd quota waitq.host_no:%u:tid:0x%llx CFID:%u diskid:%u waitq:%u\n", + PS3_HOST(cmd->instance), cmd->trace_id, cmd->index, + cmd->io_attr.disk_id, wait_q->count); +} + +static bool ps3_qos_vd_quota_get_waitq(struct ps3_qos_vd_mgr *qos_vd_mgr, + struct ps3_cmd *cmd) +{ + unsigned char can_get = PS3_FALSE; + + can_get = ps3_qos_vd_quota_dec(qos_vd_mgr); + if (!can_get) + ps3_qos_insert_vd_quota_waitq(qos_vd_mgr, cmd); + + return can_get; +} + +static bool ps3_qos_vd_quota_check(struct ps3_cmd *cmd) +{ + bool can_get = PS3_FALSE; + unsigned long flag = 0; + struct ps3_qos_vd_mgr *qos_vd_mgr = NULL; + struct qos_wait_queue *wait_q = NULL; + + qos_vd_mgr = ps3_qos_vd_mgr_get(cmd); + wait_q = &qos_vd_mgr->vd_quota_wait_q; + if (likely(wait_q->count == 0)) { + can_get = ps3_qos_vd_quota_get_waitq(qos_vd_mgr, cmd); + } else { + ps3_spin_lock_irqsave(wait_q->rsc_lock, &flag); + if (wait_q->count > 0) { + list_add_tail(&cmd->qos_list, &wait_q->wait_list); + wait_q->count++; + cmd->qos_waitq_flag = PS3_QOS_CMD_IN_VD; + ps3_spin_unlock_irqrestore(wait_q->rsc_lock, flag); + can_get = PS3_FALSE; + LOG_DEBUG( + "insert qos vd quota waitq.host_no:%u:tid:0x%llx CFID:%u\n" + "\tdiskid:%u waitq:%u\n", + PS3_HOST(cmd->instance), cmd->trace_id, + cmd->index, cmd->io_attr.disk_id, + wait_q->count); + PS3_QOS_STAT_START(cmd->instance, cmd, + PS3_QOS_VD_QUEUE); + } else { + ps3_spin_unlock_irqrestore(wait_q->rsc_lock, flag); + can_get = ps3_qos_vd_quota_get_waitq(qos_vd_mgr, cmd); + } + } + + return can_get; +} + +static inline unsigned char +ps3_qos_pd_notify_judge(struct ps3_qos_pd_mgr *qos_pd_mgr) +{ + unsigned char can_notify = PS3_FALSE; + + can_notify = qos_pd_mgr->total_wait_cmd_cnt && + (ps3_atomic_read(&qos_pd_mgr->pd_used_quota) < + qos_pd_mgr->pd_quota); + if (PS3_QOS_PD_IS_VD_MEMBER(qos_pd_mgr) || can_notify) { + goto _out; + } else { + can_notify = qos_pd_mgr->total_waited_direct_cmd && + (ps3_atomic_read(&qos_pd_mgr->direct_used_quota) < + qos_pd_mgr->direct_quota); + } +_out: + return can_notify; +} + +static unsigned char +ps3_qos_single_pd_notify(struct ps3_qos_pd_context *qos_pd_ctx, + struct ps3_qos_pd_mgr *qos_pd_mgr) +{ + struct workqueue_struct *workq = NULL; + unsigned char notified = PS3_FALSE; + + if (ps3_atomic_read(&qos_pd_mgr->valid) == 1) { + if (ps3_qos_pd_notify_judge(qos_pd_mgr)) { + workq = qos_pd_ctx->work_queues[qos_pd_mgr->workq_id]; + queue_work(workq, &qos_pd_mgr->resend_work); + notified = PS3_TRUE; + } + } + + return notified; +} + +static void ps3_qos_pd_notify(struct ps3_instance *instance) +{ + unsigned short id = 0; + struct ps3_qos_pd_context *qos_pd_ctx = NULL; + struct ps3_qos_pd_mgr *qos_pd_mgr = NULL; + + qos_pd_ctx = &instance->qos_context.pd_ctx; + for (id = 1; id <= instance->qos_context.max_pd_count; id++) { + qos_pd_mgr = ps3_qos_pd_mgr_get(instance, id); + ps3_qos_single_pd_notify(qos_pd_ctx, qos_pd_mgr); + } +} + +static unsigned char +ps3_qos_single_vd_notify(struct ps3_qos_vd_context *qos_vd_ctx, + struct ps3_qos_vd_mgr *qos_vd_mgr) +{ + struct workqueue_struct *workq = NULL; + unsigned char notify = PS3_FALSE; + + if (qos_vd_mgr->valid) { + if (qos_vd_mgr->vd_quota_wait_q.count > 0 && + ps3_atomic_read(&qos_vd_mgr->vd_quota) > 0) { + workq = qos_vd_ctx->work_queues[qos_vd_mgr->workq_id]; + queue_work(workq, &qos_vd_mgr->resend_work); + notify = PS3_TRUE; + } + } + + return notify; +} + +static unsigned char ps3_qos_vd_notify(struct ps3_instance *instance) +{ + unsigned char notify = PS3_FALSE; + unsigned short id = 0; + struct ps3_qos_vd_context *qos_vd_ctx = NULL; + struct ps3_qos_vd_mgr *qos_vd_mgr = NULL; + + qos_vd_ctx = &instance->qos_context.vd_ctx; + for (id = 1; id <= instance->qos_context.max_vd_count; id++) { + qos_vd_mgr = &qos_vd_ctx->qos_vd_mgrs[id]; + if (ps3_qos_single_vd_notify(qos_vd_ctx, qos_vd_mgr)) + notify = PS3_TRUE; + } + + return notify; +} + +static unsigned char ps3_qos_tag_rsc_available(struct ps3_instance *instance) +{ + unsigned char rsc_avail = PS3_FALSE; + unsigned short i = 0; + struct ps3_qos_vd_mgr *qos_vd_mgr = NULL; + struct ps3_qos_tg_context *qos_tg_ctx = &instance->qos_context.tg_ctx; + + if (ps3_atomic_read(&qos_tg_ctx->share_free_cnt) > 0) { + rsc_avail = PS3_TRUE; + goto _lout; + } + + if (qos_tg_ctx->mgr_cmd_wait_q.count > 0 && + ps3_atomic_read(&qos_tg_ctx->mgr_free_cnt) > 0) { + rsc_avail = PS3_TRUE; + goto _lout; + } + + for (i = 1; i <= instance->qos_context.max_vd_count; i++) { + qos_vd_mgr = &instance->qos_context.vd_ctx.qos_vd_mgrs[i]; + if (ps3_atomic_read(&qos_vd_mgr->exclusive_cmd_cnt) > 0 && + qos_tg_ctx->vd_cmd_waitqs[i].count > 0) { + rsc_avail = PS3_TRUE; + break; + } + } + +_lout: + return rsc_avail; +} + +static unsigned char ps3_qos_tg_notify(struct ps3_instance *instance) +{ + unsigned char notified = PS3_FALSE; + struct ps3_qos_tg_context *qos_tg_ctx = &instance->qos_context.tg_ctx; + + if (qos_tg_ctx->total_wait_cmd_cnt > 0 && + ps3_qos_tag_rsc_available(instance)) { + queue_work(qos_tg_ctx->work_queue, &qos_tg_ctx->resend_work); + notified = PS3_TRUE; + } + + return notified; +} + +unsigned char ps3_qos_all_pd_rc_get(struct ps3_cmd *cmd) +{ + cmd->target_pd[cmd->first_over_quota_pd_idx].get_quota = PS3_TRUE; + PS3_QOS_STAT_END(cmd->instance, cmd, PS3_QOS_PD_QUEUE); + LOG_DEBUG( + "resend cmd judge:host_no:%u t_id:0x%llx cmd:%u cmd_t:%u dev_t:%u ret[%u,%u]\n", + PS3_HOST(cmd->instance), cmd->trace_id, cmd->index, + cmd->cmd_word.type, cmd->io_attr.dev_type, + cmd->first_over_quota_pd_idx, 1); + return PS3_TRUE; +} + +static void ps3_vd_quota_waitq_clean(struct ps3_qos_vd_mgr *qos_vd_mgr, + struct ps3_scsi_priv_data *priv_data, + int resp_status) +{ + unsigned long flag = 0; + struct ps3_cmd *cmd = NULL; + struct ps3_cmd *cmd_next = NULL; + + ps3_spin_lock_irqsave(qos_vd_mgr->vd_quota_wait_q.rsc_lock, &flag); + if (qos_vd_mgr->vd_quota_wait_q.count > 0) { + list_for_each_entry_safe( + cmd, cmd_next, &qos_vd_mgr->vd_quota_wait_q.wait_list, + qos_list) { + if (priv_data == NULL || + priv_data == scsi_device_private_data(cmd->scmd)) { + list_del(&cmd->qos_list); + qos_vd_mgr->vd_quota_wait_q.count--; + LOG_DEBUG( + "qos clean vd quota waitq. hno:%u t_id:0x%llx cmd:%d vd_id:%u\n", + PS3_HOST(qos_vd_mgr->instance), + cmd->trace_id, cmd->index, + qos_vd_mgr->id); + ps3_qos_update_pd_quota(cmd); + ps3_scsih_drv_io_reply_scsi( + cmd->scmd, cmd, resp_status, PS3_FALSE); + } + } + } + ps3_spin_unlock_irqrestore(qos_vd_mgr->vd_quota_wait_q.rsc_lock, flag); + LOG_FILE_INFO("clean qos device vd quota waitq host_no:%u diskid:%u\n", + PS3_HOST(qos_vd_mgr->instance), qos_vd_mgr->id); +} + +static void ps3_qos_vd_quota_waitq_resend(struct ps3_qos_vd_mgr *qos_vd_mgr) +{ + struct ps3_cmd *cmd = NULL; + struct ps3_cmd *cmd_next = NULL; + unsigned long flag = 0; + int ret = PS3_SUCCESS; + unsigned char waitq_cleared = PS3_FALSE; + struct ps3_instance *instance = qos_vd_mgr->instance; + + ps3_spin_lock_irqsave(qos_vd_mgr->vd_quota_wait_q.rsc_lock, &flag); + if (qos_vd_mgr->vd_quota_wait_q.count > 0 && + ps3_atomic_read(&qos_vd_mgr->vd_quota) > 0) { + list_for_each_entry_safe( + cmd, cmd_next, &qos_vd_mgr->vd_quota_wait_q.wait_list, + qos_list) { + if (ps3_atomic_dec_return(&qos_vd_mgr->vd_quota) < 0) { + ps3_atomic_inc(&qos_vd_mgr->vd_quota); + break; + } + list_del(&cmd->qos_list); + qos_vd_mgr->vd_quota_wait_q.count--; + PS3_QOS_STAT_END(instance, cmd, PS3_QOS_VD_QUEUE); + if (ps3_qos_tg_decision(cmd)) { + ret = ps3_scsi_cmd_send(instance, cmd, + PS3_FALSE); + if (unlikely(ret != PS3_SUCCESS)) { + ps3_qos_cmd_update(instance, cmd); + ps3_qos_cmd_resend_fail(cmd, ret); + } else { + cmd->qos_waitq_flag = 0; + LOG_DEBUG( + "qos vd quota waitq resend:host_no:%u vid:%u t_id:0x%llx CFID:%u type:%u\n", + PS3_HOST(instance), + qos_vd_mgr->id, cmd->trace_id, + cmd->index, cmd->cmd_word.type); + } + } + } + + if (qos_vd_mgr->vd_quota_wait_q.count == 0) + waitq_cleared = PS3_TRUE; + } + ps3_spin_unlock_irqrestore(qos_vd_mgr->vd_quota_wait_q.rsc_lock, flag); + if (waitq_cleared) { + LOG_DEBUG("qos vd quota waitq cleared.host_no:%u vd:%u\n", + PS3_HOST(instance), qos_vd_mgr->id); + } + + qos_vd_mgr->last_sched_jiffies = jiffies; + ps3_qos_pd_notify(instance); +} + +static void ps3_qos_vd_resend_work(struct work_struct *work) +{ + struct ps3_qos_vd_mgr *qos_vd_mgr = + ps3_container_of(work, struct ps3_qos_vd_mgr, resend_work); + ps3_qos_vd_quota_waitq_resend(qos_vd_mgr); +} + +static void ps3_qos_waitq_clean(struct ps3_qos_pd_mgr *qos_pd_mgr, + struct qos_wait_queue *waitq, int resp_status) +{ + unsigned long flag = 0; + struct ps3_cmd *cmd = NULL; + + ps3_spin_lock_irqsave(waitq->rsc_lock, &flag); + while (waitq->count > 0) { + cmd = list_first_entry(&waitq->wait_list, struct ps3_cmd, + qos_list); + LOG_DEBUG( + "qos clean pd waitq. hno:%u t_id:0x%llx CFID:%d pid:%u que_id:%u\n", + PS3_HOST(qos_pd_mgr->instance), cmd->trace_id, + cmd->index, qos_pd_mgr->disk_id, waitq->id); + list_del(&cmd->qos_list); + waitq->count--; + (*waitq->total_waited_cnt)--; + ps3_scsih_drv_io_reply_scsi(cmd->scmd, cmd, resp_status, + PS3_FALSE); + } + ps3_spin_unlock_irqrestore(waitq->rsc_lock, flag); +} + +void ps3_pd_quota_waitq_clear_all(struct ps3_qos_pd_mgr *qos_pd_mgr, + int resp_status) +{ + unsigned short i = 0; + struct qos_wait_queue *waitq = NULL; + + for (i = 0; i < qos_pd_mgr->waitq_cnt; i++) { + waitq = &qos_pd_mgr->waitqs[i]; + ps3_qos_waitq_clean(qos_pd_mgr, waitq, resp_status); + } + + LOG_DEBUG("clear all qos pd quota waitq host_no:%u did:%u vid:%u\n", + PS3_HOST(qos_pd_mgr->instance), qos_pd_mgr->disk_id, + qos_pd_mgr->vd_id); +} + +void ps3_pd_quota_waitq_clean(struct ps3_qos_pd_mgr *qos_pd_mgr, + unsigned short que_id, int resp_status) +{ + struct qos_wait_queue *waitq = NULL; + + if (que_id == 0) { + ps3_pd_quota_waitq_clear_all(qos_pd_mgr, resp_status); + } else { + waitq = &qos_pd_mgr->waitqs[que_id]; + ps3_qos_waitq_clean(qos_pd_mgr, waitq, resp_status); + } + + LOG_DEBUG("clean qos pd quota waitq host_no:%u did:%u vid:%u\n", + PS3_HOST(qos_pd_mgr->instance), qos_pd_mgr->disk_id, + qos_pd_mgr->vd_id); +} + +static unsigned char ps3_hba_qos_pd_resend_check(struct ps3_cmd *cmd) +{ + unsigned char can_get = PS3_FALSE; + + if (cmd->io_attr.dev_type != PS3_DEV_TYPE_VD) { + if (!ps3_qos_vd_quota_check(cmd)) + goto _out; + } + can_get = ps3_qos_tg_decision(cmd); +_out: + return can_get; +} + +static unsigned char ps3_qos_pd_resend_check(struct ps3_instance *instance, + struct ps3_cmd *cmd) +{ + unsigned char can_get = PS3_FALSE; + + if (cmd->io_attr.direct_flag == PS3_CMDWORD_DIRECT_OK) { + cmd->target_pd[cmd->first_over_quota_pd_idx].get_quota = + PS3_TRUE; + PS3_QOS_STAT_END(cmd->instance, cmd, PS3_QOS_PD_QUEUE); + can_get = PS3_TRUE; + } else { + if (!ps3_qos_all_pd_rc_get(cmd)) + goto _out; + can_get = instance->qos_context.opts.qos_pd_resend_check(cmd); + } +_out: + return can_get; +} + +static unsigned char ps3_qos_vd_seq_check(struct ps3_cmd *cmd) +{ + struct PS3VDEntry *vd_entry = NULL; + unsigned short vd_id = 0; + unsigned char seq_changed = PS3_FALSE; + + if (cmd->cmd_word.direct == PS3_CMDWORD_DIRECT_ADVICE) { + vd_id = PS3_VDID(&cmd->io_attr.vd_entry->diskPos); + vd_entry = + ps3_dev_mgr_lookup_vd_info_by_id(cmd->instance, vd_id); + if (!vd_entry || + vd_entry->virtDiskSeq != + cmd->req_frame->hwReq.reqHead.virtDiskSeq) { + seq_changed = PS3_TRUE; + LOG_DEBUG("qos pd waitq vd seq check:host_no:%u\n" + "\tt_id:0x%llx CFID:%u seq[%u %u]\n", + PS3_HOST(cmd->instance), cmd->trace_id, + cmd->index, + cmd->req_frame->hwReq.reqHead.virtDiskSeq, + (!vd_entry) ? 0 : vd_entry->virtDiskSeq); + ps3_qos_update_pd_quota(cmd); + ps3_scsih_drv_io_reply_scsi(cmd->scmd, cmd, + SCSI_STATUS_TASK_ABORTED, + PS3_FALSE); + } + } + + return seq_changed; +} + +static unsigned int ps3_qos_pd_resend_cmd(struct ps3_qos_pd_mgr *qos_pd_mgr, + struct qos_wait_queue *waitq, + int count) +{ + int ret = PS3_SUCCESS; + struct ps3_cmd *cmd = NULL; + struct ps3_instance *instance = qos_pd_mgr->instance; + unsigned int can_send = 0; + unsigned int que_cnt = 0; + int avail_rsc = 0; + int used_rsc = ps3_atomic_read(waitq->used_rsc); + + if (used_rsc < *waitq->free_rsc) + avail_rsc = *waitq->free_rsc - used_rsc; + can_send = PS3_MIN(avail_rsc, count); + que_cnt = waitq->count; + while (can_send > 0 && waitq->count > 0) { + cmd = list_first_entry(&waitq->wait_list, struct ps3_cmd, + qos_list); + list_del(&cmd->qos_list); + waitq->count--; + (*waitq->total_waited_cnt)--; + if (ps3_qos_vd_seq_check(cmd)) + continue; + can_send--; + ps3_atomic_inc(waitq->used_rsc); + if (ps3_qos_pd_resend_check(instance, cmd)) { + ret = ps3_scsi_cmd_send(instance, cmd, PS3_FALSE); + if (unlikely(ret != PS3_SUCCESS)) { + ps3_qos_cmd_update(instance, cmd); + ps3_qos_cmd_resend_fail(cmd, ret); + } else { + cmd->qos_waitq_flag = 0; + LOG_DEBUG( + "qos pd quota waitq resend:host_no:%u qid:%u pid:%u\n" + "\tt_id:0x%llx CFID:%u resend[%u %u]\n", + PS3_HOST(instance), waitq->id, + qos_pd_mgr->disk_id, cmd->trace_id, + cmd->index, waitq->has_resend, + waitq->can_resend); + } + } + } + + return (que_cnt - waitq->count); +} + +static void ps3_qos_pd_waitq_resend(struct ps3_qos_pd_mgr *qos_pd_mgr, + struct qos_wait_queue *waitq) +{ + unsigned long flag = 0; + unsigned char waitq_cleared = PS3_FALSE; + + if (waitq->count > 0) { + ps3_spin_lock_irqsave(waitq->rsc_lock, &flag); + if (ps3_atomic_read(waitq->used_rsc) < *waitq->free_rsc) + ps3_qos_pd_resend_cmd(qos_pd_mgr, waitq, waitq->count); + + if (*waitq->total_waited_cnt == 0) + waitq_cleared = PS3_TRUE; + ps3_spin_unlock_irqrestore(waitq->rsc_lock, flag); + + if (waitq_cleared) { + LOG_DEBUG( + "qos pd quota waitq is cleard. host_no:%u did:%u qid:%u\n", + PS3_HOST(qos_pd_mgr->instance), + qos_pd_mgr->disk_id, waitq->id); + } + } +} + +static void ps3_qos_pd_jbod_resend(struct ps3_qos_pd_mgr *qos_pd_mgr) +{ + struct ps3_instance *instance = qos_pd_mgr->instance; + struct qos_wait_queue *waitq = NULL; + unsigned short que_id = 0; + + if (qos_pd_mgr->dev_type == PS3_DEV_TYPE_NVME_SSD) { + waitq = &qos_pd_mgr->waitqs[0]; + ps3_qos_pd_waitq_resend(qos_pd_mgr, waitq); + } + + que_id = instance->qos_context.max_vd_count; + waitq = &qos_pd_mgr->waitqs[que_id]; + ps3_qos_pd_waitq_resend(qos_pd_mgr, waitq); +} + +void ps3_qos_pd_waitq_ratio_update(struct ps3_qos_pd_mgr *qos_pd_mgr) +{ + unsigned short min_waitq_cnt = 0; + unsigned short i = 0; + struct qos_wait_queue *waitq = NULL; + unsigned long min_cmd_jiffies = 0; + struct ps3_cmd *cmd = NULL; + unsigned short poll_que_id = 1; + + if (qos_pd_mgr->poll_que_id != qos_pd_mgr->poll_start_que_id) + return; + + waitq = &qos_pd_mgr->waitqs[qos_pd_mgr->poll_que_id]; + if (waitq->has_resend > 0) + return; + + for (i = 1; i < qos_pd_mgr->waitq_cnt - 1; i++) { + waitq = &qos_pd_mgr->waitqs[i]; + if (waitq->count > 0) { + cmd = list_first_entry(&waitq->wait_list, + struct ps3_cmd, qos_list); + if (min_waitq_cnt == 0) { + min_waitq_cnt = waitq->count; + min_cmd_jiffies = cmd->scmd->jiffies_at_alloc; + } else { + if (min_waitq_cnt > waitq->count) + min_waitq_cnt = waitq->count; + + if (min_cmd_jiffies > + cmd->scmd->jiffies_at_alloc) { + min_cmd_jiffies = + cmd->scmd->jiffies_at_alloc; + poll_que_id = i; + } + } + } + } + + for (i = 1; i < qos_pd_mgr->waitq_cnt - 1; i++) { + waitq = &qos_pd_mgr->waitqs[i]; + if (waitq->count > 0) { + waitq->can_resend = + waitq->count / min_waitq_cnt > 0 ? + waitq->count / min_waitq_cnt : + 1; + waitq->has_resend = 0; + } + } + + qos_pd_mgr->poll_que_id = poll_que_id; + qos_pd_mgr->poll_start_que_id = poll_que_id; + + LOG_DEBUG("qos pd ratio update:host_no:%u pid:%u first:%u\n", + PS3_HOST(qos_pd_mgr->instance), qos_pd_mgr->disk_id, + poll_que_id); +} + +static void ps3_qos_pd_vd_member_resend(struct ps3_qos_pd_mgr *qos_pd_mgr) +{ + unsigned long flag = 0; + struct ps3_instance *instance = qos_pd_mgr->instance; + struct qos_wait_queue *waitq = NULL; + unsigned long timeout_jiffies = 0; + unsigned char waitq_cleared = PS3_FALSE; + unsigned char vd_expired = PS3_FALSE; + unsigned int pd_wait_count = 0; + + ps3_spin_lock_irqsave(&qos_pd_mgr->rc_lock, &flag); + pd_wait_count = + qos_pd_mgr->waitqs[instance->qos_context.max_vd_count].count; + while ((ps3_atomic_read(&qos_pd_mgr->pd_used_quota) < + qos_pd_mgr->pd_quota) && + qos_pd_mgr->total_wait_cmd_cnt > pd_wait_count) { + ps3_qos_pd_waitq_ratio_update(qos_pd_mgr); + waitq = &qos_pd_mgr->waitqs[qos_pd_mgr->poll_que_id]; + if (waitq->count == 0) + goto _next_waitq; + + if (waitq->has_resend > 0) { + if (qos_pd_mgr->dev_type == PS3_DEV_TYPE_SAS_HDD || + qos_pd_mgr->dev_type == PS3_DEV_TYPE_SATA_HDD) { + timeout_jiffies = + waitq->last_sched_jiffies + + msecs_to_jiffies( + PS3_QOS_VD_HDD_DELAY_THD_MS); + } else { + timeout_jiffies = + waitq->last_sched_jiffies + + msecs_to_jiffies( + PS3_QOS_VD_SDD_DELAY_THD_MS); + } + + vd_expired = time_after(jiffies, timeout_jiffies); + if (vd_expired) { + LOG_DEBUG( + "go to next vd waitq for delay:host_no:%u qid:%u pid:%u", + PS3_HOST(instance), waitq->id, + qos_pd_mgr->disk_id); + goto _next_waitq; + } + } + + waitq->last_sched_jiffies = jiffies; + waitq->has_resend += ps3_qos_pd_resend_cmd( + qos_pd_mgr, waitq, + waitq->can_resend - waitq->has_resend); + if (waitq->has_resend >= waitq->can_resend) + goto _next_waitq; + continue; +_next_waitq: + waitq->has_resend = 0; + if (++qos_pd_mgr->poll_que_id == + instance->qos_context.max_vd_count) { + qos_pd_mgr->poll_que_id = 1; + } + } + + if (qos_pd_mgr->total_wait_cmd_cnt == 0) + waitq_cleared = PS3_TRUE; + ps3_spin_unlock_irqrestore(&qos_pd_mgr->rc_lock, flag); + + if (waitq_cleared) { + LOG_DEBUG( + "qos pd quota waitq is cleard. host_no:%u diskid:%u\n", + PS3_HOST(instance), qos_pd_mgr->disk_id); + } +} + +static void ps3_qos_pd_resend_work(struct work_struct *work) +{ + struct ps3_qos_pd_mgr *qos_pd_mgr = + ps3_container_of(work, struct ps3_qos_pd_mgr, resend_work); + + ps3_qos_pd_jbod_resend(qos_pd_mgr); + ps3_qos_pd_vd_member_resend(qos_pd_mgr); + qos_pd_mgr->last_sched_jiffies = jiffies; +} + +static void ps3_poll_vd_cmd_waitq(struct ps3_qos_tg_context *qos_tg_ctx) +{ + struct qos_wait_queue *waitq = NULL; + unsigned int free_cnt = 0; + unsigned char vd_id = 0; + int ret = PS3_SUCCESS; + struct ps3_cmd *cmd = NULL; + unsigned int sent_count = 0; + struct ps3_qos_vd_mgr *qos_vd_mgr = NULL; + struct ps3_instance *instance = qos_tg_ctx->instance; + unsigned int mgrq_cnt = qos_tg_ctx->mgr_cmd_wait_q.count; + + free_cnt = ps3_atomic_read(&qos_tg_ctx->share_free_cnt); + vd_id = qos_tg_ctx->poll_vd_id; + while (free_cnt > sent_count && + qos_tg_ctx->total_wait_cmd_cnt > mgrq_cnt) { + waitq = &qos_tg_ctx->vd_cmd_waitqs[vd_id]; + if (waitq->count > 0) { + qos_vd_mgr = + &instance->qos_context.vd_ctx.qos_vd_mgrs[vd_id]; + cmd = list_first_entry(&waitq->wait_list, + struct ps3_cmd, qos_list); + list_del(&cmd->qos_list); + cmd->qos_waitq_flag = 0; + waitq->count--; + qos_tg_ctx->total_wait_cmd_cnt--; + PS3_QOS_STAT_END(instance, cmd, PS3_QOS_TAG_QUEUE); + if (ps3_qos_vd_seq_check(cmd)) + continue; + ret = ps3_scsi_cmd_send(instance, cmd, PS3_FALSE); + if (unlikely(ret != PS3_SUCCESS)) { + ps3_qos_update_vd_quota(cmd); + ps3_qos_update_pd_quota(cmd); + ps3_qos_cmd_resend_fail(cmd, ret); + } else { + ps3_atomic_inc(&qos_vd_mgr->share_cmd_used); + ++sent_count; + LOG_DEBUG( + "qos cmd waitq resend:host_no:%u t_id:0x%llx CFID:%u type:%u\n", + PS3_HOST(instance), cmd->trace_id, + cmd->index, cmd->cmd_word.type); + } + } + + if (++vd_id > instance->qos_context.max_vd_count) + vd_id = 1; + } + ps3_atomic_sub(sent_count, &qos_tg_ctx->share_free_cnt); + LOG_DEBUG("resend vd cmd waitq. host_no:%u poll_vd_id:%u sent:%u\n", + PS3_HOST(instance), vd_id, sent_count); + + qos_tg_ctx->poll_vd_id = vd_id; +} + +static void ps3_mgr_cmd_waitq_resend(struct ps3_qos_tg_context *qos_tg_ctx) +{ + struct ps3_cmd *cmd = NULL; + struct ps3_cmd *cmd_next = NULL; + struct qos_wait_queue *wait_q = NULL; + struct ps3_instance *instance = qos_tg_ctx->instance; + int ret = PS3_SUCCESS; + unsigned int free_cnt = ps3_atomic_read(&qos_tg_ctx->share_free_cnt); + unsigned int sent_count = 0; + + wait_q = &qos_tg_ctx->mgr_cmd_wait_q; + if (wait_q->count > 0 && free_cnt > 0) { + list_for_each_entry_safe(cmd, cmd_next, &wait_q->wait_list, + qos_list) { + list_del(&cmd->qos_list); + cmd->qos_waitq_flag = 0; + wait_q->count--; + qos_tg_ctx->total_wait_cmd_cnt--; + PS3_QOS_STAT_END(instance, cmd, PS3_QOS_TAG_QUEUE); + ret = ps3_scsi_cmd_send(instance, cmd, PS3_FALSE); + if (unlikely(ret != PS3_SUCCESS)) { + ps3_qos_cmd_resend_fail(cmd, ret); + } else { + LOG_DEBUG( + "qos cmd waitq resend:host_no:%u t_id:0x%llx CFID:%u type:%u\n", + PS3_HOST(instance), cmd->trace_id, + cmd->index, cmd->cmd_word.type); + ps3_atomic_inc(&qos_tg_ctx->mgr_share_used); + if (++sent_count == free_cnt) + break; + } + } + + ps3_atomic_sub(sent_count, &qos_tg_ctx->share_free_cnt); + } +} + +static void ps3_qos_tg_share_resend(struct ps3_qos_tg_context *qos_tg_ctx) +{ + ps3_mgr_cmd_waitq_resend(qos_tg_ctx); + ps3_poll_vd_cmd_waitq(qos_tg_ctx); +} + +static void ps3_qos_tg_exclusive_resend(struct ps3_qos_tg_context *qos_tg_ctx) +{ + struct ps3_qos_vd_mgr *qos_vd_mgr = NULL; + struct qos_wait_queue *waitq = NULL; + struct ps3_cmd *cmd = NULL; + struct ps3_cmd *cmd_next = NULL; + int ret = PS3_SUCCESS; + unsigned char id = 0; + struct ps3_instance *instance = qos_tg_ctx->instance; + + waitq = &qos_tg_ctx->mgr_cmd_wait_q; + if (waitq->count > 0 && + ps3_atomic_read(&qos_tg_ctx->mgr_free_cnt) > 0) { + list_for_each_entry_safe(cmd, cmd_next, &waitq->wait_list, + qos_list) { + if (ps3_atomic_dec_return(&qos_tg_ctx->mgr_free_cnt) < + 0) { + ps3_atomic_inc(&qos_tg_ctx->mgr_free_cnt); + break; + } + list_del(&cmd->qos_list); + cmd->qos_waitq_flag = 0; + waitq->count--; + qos_tg_ctx->total_wait_cmd_cnt--; + PS3_QOS_STAT_END(instance, cmd, PS3_QOS_TAG_QUEUE); + ret = ps3_scsi_cmd_send(instance, cmd, PS3_FALSE); + if (unlikely(ret != PS3_SUCCESS)) { + ps3_qos_cmd_resend_fail(cmd, ret); + ps3_atomic_inc(&qos_tg_ctx->mgr_free_cnt); + } else { + LOG_DEBUG( + "qos cmd waitq resend:host_no:%u t_id:0x%llx CFID:%u type:%u\n", + PS3_HOST(instance), cmd->trace_id, + cmd->index, cmd->cmd_word.type); + } + } + } + + for (id = 1; id <= instance->qos_context.max_vd_count; id++) { + waitq = &qos_tg_ctx->vd_cmd_waitqs[id]; + qos_vd_mgr = &instance->qos_context.vd_ctx.qos_vd_mgrs[id]; + if (waitq->count > 0 && + ps3_atomic_read(&qos_vd_mgr->exclusive_cmd_cnt) > 0) { + list_for_each_entry_safe(cmd, cmd_next, + &waitq->wait_list, qos_list) { + if (ps3_atomic_dec_return( + &qos_vd_mgr->exclusive_cmd_cnt) < + 0) { + ps3_atomic_inc( + &qos_vd_mgr->exclusive_cmd_cnt); + break; + } + list_del(&cmd->qos_list); + cmd->qos_waitq_flag = 0; + waitq->count--; + qos_tg_ctx->total_wait_cmd_cnt--; + PS3_QOS_STAT_END(instance, cmd, + PS3_QOS_TAG_QUEUE); + if (ps3_qos_vd_seq_check(cmd)) { + ps3_atomic_inc( + &qos_vd_mgr->exclusive_cmd_cnt); + continue; + } + ret = ps3_scsi_cmd_send(instance, cmd, + PS3_FALSE); + if (unlikely(ret != PS3_SUCCESS)) { + ps3_qos_update_vd_quota(cmd); + ps3_qos_update_pd_quota(cmd); + ps3_qos_cmd_resend_fail(cmd, ret); + ps3_atomic_inc( + &qos_vd_mgr->exclusive_cmd_cnt); + } else { + LOG_DEBUG( + "qos cmd waitq resend:host_no:%u t_id:0x%llx CFID:%u type:%u\n", + PS3_HOST(instance), + cmd->trace_id, cmd->index, + cmd->cmd_word.type); + } + } + } + } +} + +static void ps3_qos_tg_waitq_resend(struct ps3_qos_tg_context *qos_tg_ctx) +{ + unsigned long flag = 0; + unsigned char waitq_cleard = PS3_FALSE; + struct ps3_instance *instance = qos_tg_ctx->instance; + + ps3_spin_lock_irqsave(&qos_tg_ctx->lock, &flag); + ps3_qos_tg_share_resend(qos_tg_ctx); + ps3_qos_tg_exclusive_resend(qos_tg_ctx); + + if (qos_tg_ctx->total_wait_cmd_cnt == 0) + waitq_cleard = PS3_TRUE; + ps3_spin_unlock_irqrestore(&qos_tg_ctx->lock, flag); + if (waitq_cleard) { + LOG_DEBUG("hno:%u resend all tag waited cmds\n", + PS3_HOST(instance)); + } + + if (!ps3_qos_vd_notify(instance)) + ps3_qos_pd_notify(instance); + qos_tg_ctx->last_sched_jiffies = jiffies; +} + +static void ps3_qos_tg_resend_work(struct work_struct *work) +{ + struct ps3_qos_tg_context *qos_tg_ctx = + ps3_container_of(work, struct ps3_qos_tg_context, resend_work); + ps3_qos_tg_waitq_resend(qos_tg_ctx); +} + +unsigned int g_ps3_qos_cmdq_depth = PS3_QOS_CMDQ_DEPTH; +unsigned int g_ps3_qos_mgrq_depth = PS3_QOS_MGRQ_DEPTH; +unsigned int g_ps3_qos_hdd_pd_quota = PS3_QOS_DEFAULT_PD_QUOTA; +unsigned int g_ps3_qos_ssd_pd_quota = PS3_QOS_DEFAULT_PD_QUOTA; +unsigned int g_ps3_qos_sas_pd_quota = PS3_QOS_SAS_PD_QUOTA; +unsigned int g_ps3_qos_nvme_pd_quota = PS3_QOS_NVME_DIRECT_QUOTA; +unsigned int g_ps3_qos_nvme_member_quota = PS3_QOS_NVME_MEMBER_QUOTA; +unsigned int g_ps3_qos_hba_nvme_normal_quota = PS3_QOS_HBA_NVME_NORMAL_QUOTA; +unsigned int g_ps3_qos_raid_nvme_normal_quota = PS3_QOS_RAID_NVME_NORMAL_QUOTA; +unsigned int g_ps3_qos_vd_quota = PS3_QOS_FUNC1_JBOD_VD_QUOTA; +unsigned int g_ps3_qos_max_cmds = PS3_QOS_HBA_MAX_CMD; +unsigned int g_ps3_qos_jbod_exclusive = PS3_QOS_JBOD_EXCLUSIVE_CMD_COUNT; +unsigned int g_ps3_qos_vd_exclusive = PS3_QOS_VD_EXCLUSIVE_CMD_COUNT; +unsigned int g_ps3_qos_mgr_exclusive = QOS_MGR_EXCLUSIVE_CMD_COUNT; + +#define PS3_QOS_MAX_WORKQ_NAME_LENGTH 32 +static int ps3_qos_pd_context_init(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + unsigned short i = 0; + unsigned short j = 0; + unsigned short k = 0; + struct ps3_qos_pd_context *qos_pd_ctx = NULL; + struct ps3_qos_pd_mgr *qos_pd_mgr = NULL; + char workq_name[PS3_QOS_MAX_WORKQ_NAME_LENGTH] = { 0 }; + unsigned short max_pd_count = 0; + struct qos_wait_queue *waitq = NULL; + unsigned int cpu_num = 0; + + qos_pd_ctx = &instance->qos_context.pd_ctx; + if (ps3_is_last_func(instance)) { + cpu_num = num_online_cpus(); + if (instance->irq_context.is_balance_current_perf_mode) { + if (cpu_num > + instance->irq_context.high_iops_msix_vectors + + PS3_QOS_FUNC1_PD_WORKQ_COUNT) { + qos_pd_ctx->workq_count = + cpu_num - + instance->irq_context + .high_iops_msix_vectors; + } else { + qos_pd_ctx->workq_count = + PS3_QOS_FUNC1_PD_WORKQ_COUNT; + } + } else { + qos_pd_ctx->workq_count = PS3_QOS_FUNC1_PD_WORKQ_COUNT; + } + } else { + qos_pd_ctx->workq_count = PS3_QOS_FUNC0_PD_WORKQ_COUNT; + } + + max_pd_count = instance->qos_context.max_pd_count; + qos_pd_ctx->qos_pd_mgrs = (struct ps3_qos_pd_mgr *)ps3_vzalloc( + instance, (max_pd_count + 1) * sizeof(struct ps3_qos_pd_mgr)); + if (qos_pd_ctx->qos_pd_mgrs == NULL) { + LOG_ERROR("hno:%u:Failed to kcalloc memory for qos_pd_mgrs\n", + PS3_HOST(instance)); + ret = -PS3_FAILED; + goto _out; + } + memset(qos_pd_ctx->qos_pd_mgrs, 0, + sizeof(struct ps3_qos_pd_mgr) * (max_pd_count + 1)); + for (i = 1; i <= max_pd_count; i++) { + qos_pd_mgr = &qos_pd_ctx->qos_pd_mgrs[i]; + INIT_WORK(&qos_pd_mgr->resend_work, ps3_qos_pd_resend_work); + ps3_spin_lock_init(&qos_pd_mgr->rc_lock); + ps3_spin_lock_init(&qos_pd_mgr->direct_rsc_lock); + ps3_spin_lock_init(&qos_pd_mgr->adjust_quota_lock); + + qos_pd_mgr->workq_id = 0; + qos_pd_mgr->clearing = PS3_FALSE; + qos_pd_mgr->poll_cmd_cnt = 1; + qos_pd_mgr->poll_que_id = 1; + qos_pd_mgr->poll_start_que_id = 1; + ps3_atomic_set(&qos_pd_mgr->processing_cnt, 0); + qos_pd_mgr->pd_init_quota = 0; + qos_pd_mgr->instance = instance; + qos_pd_mgr->total_wait_cmd_cnt = 0; + qos_pd_mgr->total_waited_direct_cmd = 0; + qos_pd_mgr->waitq_cnt = instance->qos_context.max_vd_count + 1; + qos_pd_mgr->waitqs = (struct qos_wait_queue *)ps3_vzalloc( + instance, + qos_pd_mgr->waitq_cnt * sizeof(struct qos_wait_queue)); + if (qos_pd_mgr->waitqs == NULL) { + LOG_ERROR( + "hno:%u:Failed to kcalloc memory for pd waitqs\n", + PS3_HOST(instance)); + ret = -PS3_FAILED; + goto _release_pd_waitq; + } + + for (j = 0; j < qos_pd_mgr->waitq_cnt; j++) { + waitq = &qos_pd_mgr->waitqs[j]; + waitq->count = 0; + waitq->id = j; + waitq->can_resend = 0; + waitq->has_resend = 0; + INIT_LIST_HEAD(&waitq->wait_list); + if (j == 0) { + waitq->free_rsc = &qos_pd_mgr->direct_quota; + waitq->used_rsc = + &qos_pd_mgr->direct_used_quota; + waitq->rsc_lock = &qos_pd_mgr->direct_rsc_lock; + waitq->total_waited_cnt = + &qos_pd_mgr->total_waited_direct_cmd; + } else { + waitq->free_rsc = &qos_pd_mgr->pd_quota; + waitq->used_rsc = &qos_pd_mgr->pd_used_quota; + waitq->rsc_lock = &qos_pd_mgr->rc_lock; + waitq->total_waited_cnt = + &qos_pd_mgr->total_wait_cmd_cnt; + } + } + } + + qos_pd_ctx->work_queues = (struct workqueue_struct **)ps3_vzalloc( + instance, + (qos_pd_ctx->workq_count) * sizeof(struct workqueue_struct *)); + if (qos_pd_ctx->work_queues == NULL) { + LOG_ERROR("hno:%u:Failed to kcalloc memory for work_queues\n", + PS3_HOST(instance)); + ret = -PS3_FAILED; + goto _release_pd_mgr; + } + memset(qos_pd_ctx->work_queues, 0, + (qos_pd_ctx->workq_count) * sizeof(struct workqueue_struct *)); + for (j = 0; j < qos_pd_ctx->workq_count; j++) { + snprintf(workq_name, PS3_QOS_MAX_WORKQ_NAME_LENGTH, + "pd_wq_f%u_%u", ps3_get_pci_function(instance->pdev), + j); + qos_pd_ctx->work_queues[j] = + create_singlethread_workqueue(workq_name); + if (qos_pd_ctx->work_queues[j] == NULL) { + LOG_ERROR("qos pd workq:%u create failed\n", j); + ret = -PS3_FAILED; + goto _destroy_created_workqueue; + } + } + + LOG_INFO("hno:%u: qos pd context init success\n", PS3_HOST(instance)); + goto _out; +_destroy_created_workqueue: + for (k = 0; k < j; k++) { + destroy_workqueue(qos_pd_ctx->work_queues[k]); + qos_pd_ctx->work_queues[k] = NULL; + } + ps3_vfree(instance, qos_pd_ctx->work_queues); + qos_pd_ctx->work_queues = NULL; +_release_pd_waitq: + for (k = 1; k < i; k++) { + qos_pd_mgr = &qos_pd_ctx->qos_pd_mgrs[k]; + if (qos_pd_mgr->waitqs) { + ps3_vfree(instance, qos_pd_mgr->waitqs); + qos_pd_mgr->waitqs = NULL; + } + } +_release_pd_mgr: + ps3_vfree(instance, qos_pd_ctx->qos_pd_mgrs); + qos_pd_ctx->qos_pd_mgrs = NULL; +_out: + return ret; +} + +static void ps3_qos_pd_context_exit(struct ps3_instance *instance) +{ + unsigned short i = 0; + struct ps3_qos_pd_mgr *qos_pd_mgr = NULL; + struct ps3_qos_pd_context *qos_pd_ctx = NULL; + struct workqueue_struct *wq = NULL; + + qos_pd_ctx = &instance->qos_context.pd_ctx; + if (qos_pd_ctx->qos_pd_mgrs != NULL) { + for (i = 1; i <= instance->qos_context.max_pd_count; i++) { + qos_pd_mgr = &qos_pd_ctx->qos_pd_mgrs[i]; + cancel_work_sync(&qos_pd_mgr->resend_work); + ps3_vfree(instance, qos_pd_mgr->waitqs); + qos_pd_mgr->waitqs = NULL; + } + ps3_vfree(instance, qos_pd_ctx->qos_pd_mgrs); + qos_pd_ctx->qos_pd_mgrs = NULL; + } + + if (qos_pd_ctx->work_queues != NULL) { + for (i = 0; i < qos_pd_ctx->workq_count; i++) { + wq = qos_pd_ctx->work_queues[i]; + flush_workqueue(wq); + destroy_workqueue(wq); + qos_pd_ctx->work_queues[i] = NULL; + } + ps3_vfree(instance, qos_pd_ctx->work_queues); + qos_pd_ctx->work_queues = NULL; + } +} + +static int ps3_qos_vd_context_init(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + unsigned short i = 0; + unsigned short j = 0; + struct ps3_qos_vd_context *qos_vd_ctx = NULL; + struct ps3_qos_vd_mgr *qos_vd_mgr = NULL; + char workq_name[PS3_QOS_MAX_WORKQ_NAME_LENGTH] = { 0 }; + unsigned short max_vd_count = 0; + unsigned short jbod_vd_quota = 0; + + qos_vd_ctx = &instance->qos_context.vd_ctx; + if (ps3_is_last_func(instance)) { + qos_vd_ctx->vd_exclusive_cnt = g_ps3_qos_vd_exclusive; + qos_vd_ctx->jbod_exclusive_cnt = g_ps3_qos_jbod_exclusive; + } else { + qos_vd_ctx->vd_exclusive_cnt = 0; + qos_vd_ctx->jbod_exclusive_cnt = 0; + } + + jbod_vd_quota = instance->qos_context.tg_ctx.share + + qos_vd_ctx->jbod_exclusive_cnt; + + max_vd_count = instance->qos_context.max_vd_count; + qos_vd_ctx->qos_vd_mgrs = (struct ps3_qos_vd_mgr *)ps3_vzalloc( + instance, (max_vd_count + 1) * sizeof(struct ps3_qos_vd_mgr)); + if (qos_vd_ctx->qos_vd_mgrs == NULL) { + LOG_ERROR("hno:%u:Failed to kcalloc memory for qos_vd_mgrs\n", + PS3_HOST(instance)); + ret = -PS3_FAILED; + goto _out; + } + memset(qos_vd_ctx->qos_vd_mgrs, 0, + sizeof(struct ps3_qos_vd_mgr) * (max_vd_count + 1)); + for (i = 1; i <= max_vd_count; i++) { + qos_vd_mgr = &qos_vd_ctx->qos_vd_mgrs[i]; + qos_vd_mgr->instance = instance; + ps3_spin_lock_init(&qos_vd_mgr->rsc_lock); + INIT_LIST_HEAD(&qos_vd_mgr->vd_quota_wait_q.wait_list); + qos_vd_mgr->vd_quota_wait_q.rsc_lock = &qos_vd_mgr->rsc_lock; + qos_vd_mgr->vd_quota_wait_q.count = 0; + INIT_WORK(&qos_vd_mgr->resend_work, ps3_qos_vd_resend_work); + qos_vd_mgr->workq_id = 0; + qos_vd_mgr->id = i; + if (i == max_vd_count) { + ps3_atomic_set(&qos_vd_mgr->vd_quota, jbod_vd_quota); + ps3_atomic_set(&qos_vd_mgr->exclusive_cmd_cnt, + qos_vd_ctx->jbod_exclusive_cnt); + qos_vd_mgr->valid = PS3_TRUE; + } else { + ps3_atomic_set(&qos_vd_mgr->exclusive_cmd_cnt, + qos_vd_ctx->vd_exclusive_cnt); + } + ps3_atomic_set(&qos_vd_mgr->share_cmd_used, 0); + } + + qos_vd_ctx->workq_count = 1; + qos_vd_ctx->work_queues = (struct workqueue_struct **)ps3_vzalloc( + instance, + (qos_vd_ctx->workq_count) * sizeof(struct workqueue_struct *)); + if (qos_vd_ctx->work_queues == NULL) { + LOG_ERROR("hno:%u:Failed to kcalloc memory for work_queues\n", + PS3_HOST(instance)); + ret = -PS3_FAILED; + goto _release_vd_mgr; + } + memset(qos_vd_ctx->work_queues, 0, + (qos_vd_ctx->workq_count) * sizeof(struct workqueue_struct *)); + for (i = 0; i < qos_vd_ctx->workq_count; i++) { + snprintf(workq_name, PS3_QOS_MAX_WORKQ_NAME_LENGTH, + "qos_vd_wq_f%u_%u", + ps3_get_pci_function(instance->pdev), i); + qos_vd_ctx->work_queues[i] = + create_singlethread_workqueue(workq_name); + if (qos_vd_ctx->work_queues[i] == NULL) { + LOG_ERROR("qos vd workq:%u create failed\n", i); + ret = -PS3_FAILED; + goto _destroy_created_workqueue; + } + } + + LOG_INFO("hno:%u: qos vd context init success\n", PS3_HOST(instance)); + + goto _out; +_destroy_created_workqueue: + for (j = 0; j < i; j++) { + destroy_workqueue(qos_vd_ctx->work_queues[j]); + qos_vd_ctx->work_queues[j] = NULL; + } + ps3_vfree(instance, qos_vd_ctx->work_queues); + qos_vd_ctx->work_queues = NULL; +_release_vd_mgr: + ps3_vfree(instance, qos_vd_ctx->qos_vd_mgrs); + qos_vd_ctx->qos_vd_mgrs = NULL; +_out: + return ret; +} + +static void ps3_qos_vd_context_exit(struct ps3_instance *instance) +{ + unsigned short i = 0; + struct ps3_qos_vd_mgr *qos_vd_mgr = NULL; + struct ps3_qos_vd_context *qos_vd_ctx = NULL; + struct workqueue_struct *wq = NULL; + + qos_vd_ctx = &instance->qos_context.vd_ctx; + if (qos_vd_ctx->qos_vd_mgrs != NULL) { + for (i = 1; i <= instance->qos_context.max_vd_count; i++) { + qos_vd_mgr = &qos_vd_ctx->qos_vd_mgrs[i]; + cancel_work_sync(&qos_vd_mgr->resend_work); + } + ps3_vfree(instance, qos_vd_ctx->qos_vd_mgrs); + qos_vd_ctx->qos_vd_mgrs = NULL; + } + + if (qos_vd_ctx->work_queues != NULL) { + for (i = 0; i < qos_vd_ctx->workq_count; i++) { + wq = qos_vd_ctx->work_queues[i]; + flush_workqueue(wq); + destroy_workqueue(wq); + } + ps3_vfree(instance, qos_vd_ctx->work_queues); + qos_vd_ctx->work_queues = NULL; + } +} + +static int ps3_qos_tg_context_init(struct ps3_instance *instance) +{ + struct ps3_qos_tg_context *qos_tg_ctx = NULL; + int ret = PS3_SUCCESS; + unsigned long long tfifo_depth = 0; + unsigned short i = 0; + struct qos_wait_queue *wait_q = NULL; + char workq_name[PS3_QOS_MAX_WORKQ_NAME_LENGTH] = { 0 }; + unsigned int tag_exclusive = 0; + + ps3_tfifo_depth_get(instance, &tfifo_depth); + if (tfifo_depth == 0) { + ret = -PS3_FAILED; + LOG_ERROR("hno:%u:tfifo_depth is invalid func:%u\n", + PS3_HOST(instance), + ps3_get_pci_function(instance->pdev)); + goto out; + } + qos_tg_ctx = &instance->qos_context.tg_ctx; + qos_tg_ctx->instance = instance; + + qos_tg_ctx->high_pri_exclusive_cnt = QOS_HIGH_PRI_EXCLUSIVE_CMD_COUNT; + qos_tg_ctx->mgr_exclusive_cnt = g_ps3_qos_mgr_exclusive; + if (!ps3_ioc_multi_func_support(instance)) { + tag_exclusive = + qos_tg_ctx->mgr_exclusive_cnt + + qos_tg_ctx->high_pri_exclusive_cnt + + g_ps3_qos_jbod_exclusive + + instance->ctrl_info.maxVdCount * g_ps3_qos_vd_exclusive; + } else { + qos_tg_ctx->high_pri_exclusive_cnt = + qos_tg_ctx->high_pri_exclusive_cnt >> 1; + qos_tg_ctx->mgr_exclusive_cnt = + qos_tg_ctx->mgr_exclusive_cnt >> 1; + tag_exclusive = qos_tg_ctx->high_pri_exclusive_cnt + + qos_tg_ctx->mgr_exclusive_cnt; + if (ps3_get_pci_function(instance->pdev) == PS3_FUNC_ID_1) { + tag_exclusive += (g_ps3_qos_jbod_exclusive + + instance->ctrl_info.maxVdCount * + g_ps3_qos_vd_exclusive); + } + } + + if (tfifo_depth > tag_exclusive) + qos_tg_ctx->share = tfifo_depth - tag_exclusive; + else + qos_tg_ctx->share = tag_exclusive; + + ps3_atomic_set(&qos_tg_ctx->mgr_free_cnt, + qos_tg_ctx->mgr_exclusive_cnt); + ps3_atomic_set(&qos_tg_ctx->share_free_cnt, qos_tg_ctx->share); + ps3_atomic_set(&qos_tg_ctx->mgr_share_used, 0); + ps3_spin_lock_init(&qos_tg_ctx->lock); + qos_tg_ctx->poll_vd_id = 1; + + wait_q = &qos_tg_ctx->mgr_cmd_wait_q; + INIT_LIST_HEAD(&wait_q->wait_list); + qos_tg_ctx->vd_cmd_waitqs = (struct qos_wait_queue *)ps3_vzalloc( + instance, (instance->qos_context.max_vd_count + 1) * + sizeof(struct qos_wait_queue)); + if (qos_tg_ctx->vd_cmd_waitqs == NULL) { + LOG_ERROR("hno:%u:Failed to kcalloc memory for qos_context\n", + PS3_HOST(instance)); + ret = -PS3_FAILED; + goto out; + } + memset(qos_tg_ctx->vd_cmd_waitqs, 0, + (instance->qos_context.max_vd_count + 1) * + sizeof(struct qos_wait_queue)); + for (i = 1; i < instance->qos_context.max_vd_count + 1; i++) { + wait_q = &qos_tg_ctx->vd_cmd_waitqs[i]; + INIT_LIST_HEAD(&wait_q->wait_list); + } + + INIT_WORK(&qos_tg_ctx->resend_work, ps3_qos_tg_resend_work); + snprintf(workq_name, PS3_QOS_MAX_WORKQ_NAME_LENGTH, "qos_tag_wq_f%u", + ps3_get_pci_function(instance->pdev)); + qos_tg_ctx->work_queue = create_singlethread_workqueue(workq_name); + if (qos_tg_ctx->work_queue == NULL) { + LOG_ERROR("qos tag workq:%u create failed\n", i); + ret = -PS3_FAILED; + goto release_vd_cmd_waitqs; + } + + LOG_INFO("hno:%u func:%u: qos tg context init success tfifo:%llu\n", + PS3_HOST(instance), ps3_get_pci_function(instance->pdev), + tfifo_depth); + goto out; +release_vd_cmd_waitqs: + ps3_vfree(instance, qos_tg_ctx->vd_cmd_waitqs); + qos_tg_ctx->vd_cmd_waitqs = NULL; +out: + return ret; +} + +static void ps3_qos_tg_context_exit(struct ps3_instance *instance) +{ + struct ps3_qos_tg_context *qos_tg_ctx = NULL; + + qos_tg_ctx = &instance->qos_context.tg_ctx; + if (qos_tg_ctx->work_queue != NULL) { + cancel_work_sync(&qos_tg_ctx->resend_work); + flush_workqueue(qos_tg_ctx->work_queue); + destroy_workqueue(qos_tg_ctx->work_queue); + qos_tg_ctx->work_queue = NULL; + } + + if (qos_tg_ctx->vd_cmd_waitqs != NULL) { + ps3_vfree(instance, qos_tg_ctx->vd_cmd_waitqs); + qos_tg_ctx->vd_cmd_waitqs = NULL; + } +} + +int ps3_hba_qos_init(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + + ret = ps3_qos_tg_context_init(instance); + if (ret != PS3_SUCCESS) + goto _out; + + ret = ps3_qos_vd_context_init(instance); + if (ret != PS3_SUCCESS) + goto _tg_ctx_exit; + + ret = ps3_qos_pd_context_init(instance); + if (ret != PS3_SUCCESS) + goto _vd_ctx_exit; + + goto _out; +_vd_ctx_exit: + ps3_qos_vd_context_exit(instance); +_tg_ctx_exit: + ps3_qos_tg_context_exit(instance); +_out: + return ret; +} + +void ps3_hba_qos_exit(struct ps3_instance *instance) +{ + ps3_qos_tg_context_exit(instance); + ps3_qos_vd_context_exit(instance); + ps3_qos_pd_context_exit(instance); + + LOG_INFO("hba qos exit. host_no:%u\n", PS3_HOST(instance)); +} + +static unsigned char ps3_qos_vd_member_insert(struct ps3_cmd *cmd) +{ + unsigned char i = 0; + unsigned short disk_id = 0; + struct ps3_qos_pd_mgr *qos_pd_mgr = NULL; + struct qos_wait_queue *waitq = NULL; + unsigned char in_queue = PS3_FALSE; + + for (i = 0; i < cmd->target_pd_count; i++) { + disk_id = cmd->target_pd[i].flat_disk_id; + qos_pd_mgr = ps3_qos_pd_mgr_get(cmd->instance, disk_id); + ps3_atomic_inc(&qos_pd_mgr->processing_cnt); + if (unlikely(qos_pd_mgr->clearing)) { + LOG_DEBUG( + "qos vd member is clearing .host_no:%u:t_id:0x%llx CFID:%u did[%u %u]\n", + PS3_HOST(qos_pd_mgr->instance), cmd->trace_id, + cmd->index, qos_pd_mgr->disk_id, + qos_pd_mgr->vd_id); + ps3_atomic_dec(&qos_pd_mgr->processing_cnt); + } else { + in_queue = PS3_TRUE; + break; + } + } + + if (in_queue) { + waitq = ps3_qos_pd_waitq_get(qos_pd_mgr, cmd); + ps3_qos_pd_in_q(cmd, waitq, i); + ps3_atomic_dec(&qos_pd_mgr->processing_cnt); + LOG_DEBUG("insert qos pd quota waitq.host_no:%u:t_id:0x%llx\n" + "\tCFID:%u waitq[%u,%u] did[%u %u]\n", + PS3_HOST(cmd->instance), cmd->trace_id, cmd->index, + waitq->id, waitq->count, qos_pd_mgr->disk_id, i); + } + + return in_queue; +} + +static unsigned char ps3_qos_pd_quota_decision(struct ps3_cmd *cmd) +{ + unsigned char can_get = PS3_FALSE; + struct ps3_qos_pd_mgr *qos_pd_mgr = NULL; + unsigned short disk_id = 0; + unsigned short i = 0; + struct scsi_device *sdev = NULL; + int pd_quota = 0; + + PS3_QOS_STAT_START(cmd->instance, cmd, PS3_QOS_PD_PRO); + if (cmd->io_attr.dev_type == PS3_DEV_TYPE_VD) { + ps3_qos_cmd_member_pd_calc(cmd); + for (i = 0; i < cmd->target_pd_count; i++) { + disk_id = cmd->target_pd[i].flat_disk_id; + qos_pd_mgr = ps3_qos_pd_mgr_get(cmd->instance, disk_id); + if (ps3_qos_pd_quota_check(qos_pd_mgr, cmd)) { + cmd->target_pd[i].get_quota = PS3_TRUE; + can_get = PS3_TRUE; + break; + } + } + + if (cmd->target_pd_count == 0) + can_get = PS3_TRUE; + + if (!can_get) { + if (!ps3_qos_vd_member_insert(cmd)) + can_get = PS3_TRUE; + } + } else { + disk_id = cmd->io_attr.disk_id; + qos_pd_mgr = ps3_qos_pd_mgr_get(cmd->instance, disk_id); + if (ps3_is_nvme_direct_cmd(qos_pd_mgr, cmd)) + pd_quota = qos_pd_mgr->direct_quota; + else + pd_quota = qos_pd_mgr->pd_quota; + sdev = cmd->scmd->device; + if (pd_quota >= sdev->queue_depth) { + can_get = PS3_TRUE; + } else { + ps3_qos_cmd_member_pd_calc(cmd); + can_get = ps3_qos_pd_quota_req(qos_pd_mgr, cmd, 0); + } + } + PS3_QOS_STAT_END(cmd->instance, cmd, PS3_QOS_PD_PRO); + return can_get; +} + +static unsigned char ps3_qos_vd_quota_decision(struct ps3_cmd *cmd) +{ + unsigned char can_get = PS3_TRUE; + + PS3_QOS_STAT_START(cmd->instance, cmd, PS3_QOS_VD_PRO); + if (cmd->io_attr.dev_type != PS3_DEV_TYPE_VD) + can_get = ps3_qos_vd_quota_check(cmd); + PS3_QOS_STAT_END(cmd->instance, cmd, PS3_QOS_VD_PRO); + + return can_get; +} + +static inline unsigned char ps3_qos_dev_valid(struct ps3_cmd *cmd) +{ + unsigned char valid = PS3_TRUE; + struct ps3_scsi_priv_data *priv_data = NULL; + + priv_data = scsi_device_private_data(cmd->scmd); + if (priv_data->dev_deling) { + LOG_INFO_LIM("qos dev is deling. host_no:%u chanel:%u id:%u\n", + PS3_HOST(cmd->instance), + PS3_SDEV_CHANNEL(cmd->scmd->device), + PS3_SDEV_TARGET(cmd->scmd->device)); + valid = PS3_FALSE; + } + + return valid; +} + +static int ps3_qos_pre_check(struct ps3_instance *instance, struct ps3_cmd *cmd) +{ + int ret = PS3_SUCCESS; + + if (instance->task_manager_host_busy) { + LOG_INFO_LIM("hno:%u task_manager_host_busy\n", + PS3_HOST(instance)); + ret = -PS3_RETRY; + goto l_out; + } + + if (instance->is_probe_finish && !instance->state_machine.is_load) { + LOG_INFO_LIM("hno:%u instance state not is_load\n", + PS3_HOST(instance)); + ret = -PS3_FAILED; + goto l_out; + } + + if (!ps3_qos_dev_valid(cmd)) + ret = -PS3_FAILED; + + if (!ps3_is_instance_state_normal(instance, PS3_TRUE)) { + LOG_INFO_LIM("hno:%u instance state is not normal\n", + PS3_HOST(instance)); + ret = -PS3_RECOVERED; + goto l_out; + } +l_out: + return ret; +} + +unsigned char ps3_hba_qos_decision(struct ps3_cmd *cmd) +{ + unsigned char can_get = PS3_FALSE; + + if (PS3_CMD_TYPE_IS_RW(cmd->cmd_word.type)) { + can_get = ps3_qos_pd_quota_decision(cmd); + if (!can_get || + cmd->io_attr.direct_flag == PS3_CMDWORD_DIRECT_OK) { + goto _out; + } + + can_get = ps3_qos_vd_quota_decision(cmd); + if (!can_get) + goto _out; + } + + can_get = ps3_qos_tg_decision(cmd); +_out: + return can_get; +} + +void ps3_hba_qos_waitq_notify(struct ps3_instance *instance) +{ + if (!ps3_qos_tg_notify(instance)) { + if (!ps3_qos_vd_notify(instance)) + ps3_qos_pd_notify(instance); + } +} + +static unsigned char ps3_pd_quota_waiq_abort(struct ps3_cmd *cmd) +{ + unsigned long lock_flag_pd = 0; + unsigned short disk_id = 0; + unsigned char found = PS3_FALSE; + struct ps3_qos_pd_mgr *qos_pd_mgr = NULL; + struct qos_wait_queue *waitq = NULL; + unsigned short index = 0; + + for (index = cmd->first_over_quota_pd_idx; index < cmd->target_pd_count; + index++) { + disk_id = cmd->target_pd[index].flat_disk_id; + qos_pd_mgr = ps3_qos_pd_mgr_get(cmd->instance, disk_id); + waitq = ps3_qos_pd_waitq_get(qos_pd_mgr, cmd); + ps3_spin_lock_irqsave(waitq->rsc_lock, &lock_flag_pd); + if (cmd->qos_waitq_flag == PS3_QOS_CMD_IN_PD && + index == cmd->first_over_quota_pd_idx) { + list_del(&cmd->qos_list); + waitq->count--; + (*waitq->total_waited_cnt)--; + ps3_spin_unlock_irqrestore(waitq->rsc_lock, + lock_flag_pd); + found = PS3_TRUE; + break; + } + ps3_spin_unlock_irqrestore(waitq->rsc_lock, lock_flag_pd); + LOG_INFO( + "abort pd waitq. t_id:0x%llx did[%u,%u] qid:%u found:%u\n", + cmd->trace_id, index, disk_id, waitq->id, found); + } + + return found; +} + +static bool ps3_vd_quota_waiq_abort(struct ps3_cmd *aborted_cmd) +{ + unsigned long flag = 0; + unsigned char found = PS3_FALSE; + struct ps3_qos_vd_mgr *qos_vd_mgr = NULL; + + qos_vd_mgr = ps3_qos_vd_mgr_get(aborted_cmd); + ps3_spin_lock_irqsave(qos_vd_mgr->vd_quota_wait_q.rsc_lock, &flag); + if (aborted_cmd->qos_waitq_flag == PS3_QOS_CMD_IN_VD) { + list_del(&aborted_cmd->qos_list); + qos_vd_mgr->vd_quota_wait_q.count--; + found = PS3_TRUE; + } + ps3_spin_unlock_irqrestore(qos_vd_mgr->vd_quota_wait_q.rsc_lock, flag); + + return found; +} + +bool ps3_cmd_waitq_abort(struct ps3_cmd *aborted_cmd) +{ + unsigned long lock_flag_mgr = 0; + struct ps3_instance *instance = NULL; + unsigned char found = PS3_FALSE; + struct qos_wait_queue *cmd_wait_q = NULL; + struct ps3_qos_tg_context *qos_tg_ctx = NULL; + + instance = aborted_cmd->instance; + qos_tg_ctx = &instance->qos_context.tg_ctx; + ps3_spin_lock_irqsave(&qos_tg_ctx->lock, &lock_flag_mgr); + if (aborted_cmd->qos_waitq_flag == PS3_QOS_CMD_IN_FRAME) { + cmd_wait_q = ps3_qos_cmd_waitq_get(qos_tg_ctx, aborted_cmd); + list_del(&aborted_cmd->qos_list); + cmd_wait_q->count--; + qos_tg_ctx->total_wait_cmd_cnt--; + found = PS3_TRUE; + } + ps3_spin_unlock_irqrestore(&qos_tg_ctx->lock, lock_flag_mgr); + + return found; +} + +static unsigned char ps3_hba_qos_waitq_abort(struct ps3_cmd *cmd) +{ + unsigned char found = PS3_FALSE; + + if (cmd->qos_waitq_flag == 0) + goto out; + + if (PS3_CMD_TYPE_IS_RW(cmd->cmd_word.type)) { + if (cmd->qos_waitq_flag == PS3_QOS_CMD_IN_PD) { + found = ps3_pd_quota_waiq_abort(cmd); + if (found) + goto out; + } + + if (cmd->qos_waitq_flag == PS3_QOS_CMD_IN_VD) { + found = ps3_vd_quota_waiq_abort(cmd); + if (found) { + ps3_qos_update_pd_quota(cmd); + goto out; + } + } + } + + if (cmd->qos_waitq_flag == PS3_QOS_CMD_IN_FRAME) { + found = ps3_cmd_waitq_abort(cmd); + if (found) { + ps3_qos_update_vd_quota(cmd); + ps3_qos_update_pd_quota(cmd); + } + } +out: + return found; +} + +static void ps3_vd_cmd_waitq_clean(struct ps3_instance *instance, + unsigned short disk_id, int result) +{ + unsigned long flag = 0; + struct ps3_cmd *cmd = NULL; + struct ps3_cmd *cmd_next = NULL; + struct ps3_qos_tg_context *qos_tg_ctx = &instance->qos_context.tg_ctx; + struct qos_wait_queue *wait_q = &qos_tg_ctx->vd_cmd_waitqs[disk_id]; + + ps3_spin_lock_irqsave(&qos_tg_ctx->lock, &flag); + if (wait_q->count > 0) { + list_for_each_entry_safe(cmd, cmd_next, &wait_q->wait_list, + qos_list) { + LOG_DEBUG( + "qos clean vd cmd waitq. hno:%u t_id:0x%llx cmd:%d vd_id:%u\n", + PS3_HOST(instance), cmd->trace_id, cmd->index, + disk_id); + list_del(&cmd->qos_list); + wait_q->count--; + qos_tg_ctx->total_wait_cmd_cnt--; + ps3_qos_update_vd_quota(cmd); + ps3_qos_update_pd_quota(cmd); + ps3_scsih_drv_io_reply_scsi(cmd->scmd, cmd, result, + PS3_FALSE); + } + } + ps3_spin_unlock_irqrestore(&qos_tg_ctx->lock, flag); +} + +static void ps3_jbod_cmd_waitq_clean(struct ps3_instance *instance, + struct ps3_scsi_priv_data *priv_data, + int result) +{ + unsigned long flag = 0; + struct ps3_cmd *cmd = NULL; + struct ps3_cmd *cmd_next = NULL; + struct ps3_qos_tg_context *qos_tg_ctx = &instance->qos_context.tg_ctx; + struct qos_wait_queue *wait_q = + &qos_tg_ctx->vd_cmd_waitqs[instance->qos_context.max_vd_count]; + unsigned short disk_id = priv_data->disk_pos.diskDev.ps3Dev.virtDiskID; + + ps3_spin_lock_irqsave(&qos_tg_ctx->lock, &flag); + if (wait_q->count > 0) { + list_for_each_entry_safe(cmd, cmd_next, &wait_q->wait_list, + qos_list) { + if (scsi_device_private_data(cmd->scmd) == priv_data) { + LOG_DEBUG( + "qos clean jbod cmd waitq. hno:%u t_id:0x%llx cmd:%d dev_t:%u diskid:%u\n", + PS3_HOST(instance), cmd->trace_id, + cmd->index, priv_data->dev_type, + disk_id); + list_del(&cmd->qos_list); + wait_q->count--; + qos_tg_ctx->total_wait_cmd_cnt--; + ps3_qos_update_vd_quota(cmd); + ps3_qos_update_pd_quota(cmd); + ps3_scsih_drv_io_reply_scsi(cmd->scmd, cmd, + result, PS3_FALSE); + } + } + } + ps3_spin_unlock_irqrestore(&qos_tg_ctx->lock, flag); +} + +static void ps3_mgr_cmd_waitq_clean(struct ps3_instance *instance, + struct ps3_scsi_priv_data *priv_data, + int result) +{ + unsigned long flag = 0; + struct ps3_cmd *cmd = NULL; + struct ps3_cmd *cmd_next = NULL; + struct ps3_qos_tg_context *qos_tg_ctx = &instance->qos_context.tg_ctx; + struct qos_wait_queue *wait_q = &qos_tg_ctx->mgr_cmd_wait_q; + unsigned short disk_id = priv_data->disk_pos.diskDev.ps3Dev.virtDiskID; + + ps3_spin_lock_irqsave(&qos_tg_ctx->lock, &flag); + if (wait_q->count > 0) { + list_for_each_entry_safe(cmd, cmd_next, &wait_q->wait_list, + qos_list) { + if (cmd->scmd && + scsi_device_private_data(cmd->scmd) == priv_data) { + LOG_DEBUG( + "qos clean mgr cmd waitq. hno:%u t_id:0x%llx cmd:%d dev_t:%u diskid:%u\n", + PS3_HOST(instance), cmd->trace_id, + cmd->index, priv_data->dev_type, + disk_id); + list_del(&cmd->qos_list); + wait_q->count--; + qos_tg_ctx->total_wait_cmd_cnt--; + ps3_scsih_drv_io_reply_scsi(cmd->scmd, cmd, + result, PS3_FALSE); + } + } + } + ps3_spin_unlock_irqrestore(&qos_tg_ctx->lock, flag); +} + +static void ps3_hba_qos_vd_clean(struct ps3_instance *instance, + struct ps3_scsi_priv_data *priv_data, + int resp_status) +{ + unsigned short vd_id = 0; + unsigned short pd_id = 0; + struct ps3_qos_vd_mgr *qos_vd_mgr = NULL; + struct ps3_qos_pd_mgr *qos_pd_mgr = NULL; + + vd_id = get_offset_of_vdid(PS3_VDID_OFFSET(instance), + PS3_VDID(&priv_data->disk_pos)); + qos_vd_mgr = ps3_qos_vd_mgr_get_by_id(instance, + PS3_VDID(&priv_data->disk_pos)); + for (pd_id = 1; pd_id <= instance->qos_context.max_pd_count; pd_id++) { + qos_pd_mgr = ps3_qos_pd_mgr_get(instance, pd_id); + if (ps3_atomic_read(&qos_pd_mgr->valid) == 1 && + qos_pd_mgr->vd_id == vd_id) { + ps3_pd_quota_waitq_clean(qos_pd_mgr, vd_id, + resp_status); + cancel_work_sync(&qos_pd_mgr->resend_work); + } + } + + ps3_vd_quota_waitq_clean(qos_vd_mgr, NULL, resp_status); + cancel_work_sync(&qos_vd_mgr->resend_work); + + ps3_vd_cmd_waitq_clean(instance, vd_id, resp_status); + ps3_mgr_cmd_waitq_clean(instance, priv_data, resp_status); + + LOG_FILE_INFO("qos clean vd. host_no:%u type:%u disk_id:%u\n", + PS3_HOST(instance), priv_data->dev_type, vd_id); +} + +#define PS3_QOS_JBOD_VD_MGR(instance) \ + (&instance->qos_context.vd_ctx \ + .qos_vd_mgrs[instance->qos_context.max_vd_count]) + +#if defined(PS3_SUPPORT_LINX80) +void ps3_linx80_vd_member_change(struct ps3_instance *instance, + struct ps3_pd_entry *pd_entry) +{ + unsigned short pd_id = 0; + struct ps3_qos_pd_mgr *qos_pd_mgr = NULL; + + if (!PS3_QOS_INITED(instance)) + goto _out; + + pd_id = PS3_PDID(&pd_entry->disk_pos); + qos_pd_mgr = ps3_qos_pd_mgr_get(instance, pd_id); + if (ps3_atomic_read(&qos_pd_mgr->valid) != PS3_TRUE) + goto _out; + ps3_pd_quota_waitq_clean(qos_pd_mgr, 0, PS3_STATUS_VD_MEMBER_OFFLINE); + cancel_work_sync(&qos_pd_mgr->resend_work); + LOG_INFO("linx80 update pd qos rsc. host_no:%u pd_id:%u dev_type:%u\n", + PS3_HOST(instance), pd_id, pd_entry->dev_type); + +_out: + return; +} +#endif +static void ps3_hba_qos_pd_clean(struct ps3_instance *instance, + struct ps3_scsi_priv_data *priv_data, + int resp_status) +{ + unsigned short disk_id = 0; + struct ps3_qos_vd_mgr *qos_vd_mgr = NULL; + struct ps3_qos_pd_mgr *qos_pd_mgr = NULL; + + disk_id = PS3_PDID(&priv_data->disk_pos); + qos_pd_mgr = ps3_qos_pd_mgr_get(instance, disk_id); + ps3_pd_quota_waitq_clean(qos_pd_mgr, 0, resp_status); + cancel_work_sync(&qos_pd_mgr->resend_work); + + qos_vd_mgr = PS3_QOS_JBOD_VD_MGR(instance); + ps3_vd_quota_waitq_clean(qos_vd_mgr, priv_data, resp_status); + ps3_jbod_cmd_waitq_clean(instance, priv_data, resp_status); + ps3_mgr_cmd_waitq_clean(instance, priv_data, resp_status); + + LOG_FILE_INFO("qos clean pd. host_no:%u diskid:%u\n", + PS3_HOST(instance), disk_id); +} + +static void ps3_qos_wait_io_end(struct ps3_instance *instance) +{ + unsigned int i = 0; + struct ps3_cmd *cmd = NULL; + + LOG_DEBUG("host_no:%u wait cmd in qos flighting start\n", + PS3_HOST(instance)); + for (i = 0; i < instance->cmd_context.max_scsi_cmd_count; i++) { + cmd = instance->cmd_context.cmd_buf[i]; + while (cmd->qos_processing) + ps3_msleep(PS3_LOOP_TIME_INTERVAL_20MS); + } + LOG_DEBUG("host_no:%u wait cmd in qos flighting end\n", + PS3_HOST(instance)); +} + +void ps3_qos_device_clean(struct ps3_instance *instance, + struct ps3_scsi_priv_data *priv_data, int resp_status) +{ + if (!PS3_QOS_INITED(instance)) + return; + + ps3_qos_wait_io_end(instance); + if (priv_data->dev_type == PS3_DEV_TYPE_VD) { + instance->qos_context.opts.qos_vd_clean(instance, priv_data, + resp_status); + } else { + instance->qos_context.opts.qos_pd_clean(instance, priv_data, + resp_status); + } +} + +static inline void ps3_qos_dev_end(struct ps3_instance *instance, + struct ps3_scsi_priv_data *priv_data) +{ + unsigned int i = 0; + struct ps3_cmd *cmd = NULL; + struct scsi_cmnd *scmd = NULL; + struct scsi_device *sdev = NULL; + unsigned int softChan = PS3_CHANNEL(&priv_data->disk_pos); + unsigned int devID = PS3_TARGET(&priv_data->disk_pos); + + LOG_DEBUG("host_no:%u dev wait cmd in qos flighting start\n", + PS3_HOST(instance)); + for (i = 0; i < instance->cmd_context.max_scsi_cmd_count; i++) { + cmd = instance->cmd_context.cmd_buf[i]; + if (likely(!cmd->qos_processing)) + continue; + + scmd = cmd->scmd; + if (scmd == NULL) + continue; + + sdev = scmd->device; + if (sdev == NULL) + continue; + + if (PS3_SDEV_CHANNEL(sdev) == softChan && + PS3_SDEV_TARGET(sdev) == devID) { + LOG_DEBUG("cmd is flighting. host_no:%u CFID:%u\n", + PS3_HOST(instance), cmd->index); + while (cmd->qos_processing) + ps3_msleep(PS3_LOOP_TIME_INTERVAL_20MS); + } + } + LOG_DEBUG("host_no:%u dev wait cmd in qos flighting end\n", + PS3_HOST(instance)); +} + +void ps3_qos_disk_del(struct ps3_instance *instance, + struct ps3_scsi_priv_data *priv_data) +{ + struct ps3_qos_pd_mgr *qos_pd_mgr = NULL; + unsigned short disk_id = 0; + int resp_status = PS3_STATUS_DEVICE_NOT_FOUND; + + if (!PS3_QOS_INITED(instance)) + return; + + priv_data->dev_deling = PS3_TRUE; + wmb(); /* in order to force CPU ordering */ + disk_id = PS3_PDID(&priv_data->disk_pos); + LOG_FILE_INFO("qos disk del. host_no:%u dev_t:%u diskid:%u\n", + PS3_HOST(instance), priv_data->dev_type, disk_id); + if (priv_data->dev_type == PS3_DEV_TYPE_VD) { + ps3_qos_dev_end(instance, priv_data); + instance->qos_context.opts.qos_vd_clean(instance, priv_data, + resp_status); + } else { + qos_pd_mgr = ps3_qos_pd_mgr_get(instance, disk_id); + ps3_qos_dev_end(instance, priv_data); + if (PS3_QOS_PD_IS_VD_MEMBER(qos_pd_mgr)) { + resp_status = PS3_STATUS_VD_MEMBER_OFFLINE; + qos_pd_mgr->clearing = PS3_TRUE; + wmb(); /* in order to force CPU ordering */ + LOG_INFO( + "host_no:%u wait cmd in qos vd member flighting count:%d did:%u\n", + PS3_HOST(instance), + ps3_atomic_read(&qos_pd_mgr->processing_cnt), + disk_id); + while (ps3_atomic_read(&qos_pd_mgr->processing_cnt) > + 0) { + ps3_msleep(PS3_LOOP_TIME_INTERVAL_100MS); + } + } + instance->qos_context.opts.qos_pd_clean(instance, priv_data, + resp_status); + } +} + +void ps3_qos_vd_member_del(struct ps3_instance *instance, + struct PS3DiskDevPos *dev_pos) +{ + struct ps3_qos_pd_mgr *qos_pd_mgr = NULL; + unsigned short disk_id = 0; + + disk_id = PS3_PDID(dev_pos); + qos_pd_mgr = ps3_qos_pd_mgr_get(instance, disk_id); + LOG_INFO("qos disk del. host_no:%u did:%u vid:%u\n", PS3_HOST(instance), + disk_id, qos_pd_mgr->vd_id); + + if (PS3_QOS_PD_IS_VD_MEMBER(qos_pd_mgr)) { + ps3_pd_quota_waitq_clean(qos_pd_mgr, 0, + PS3_STATUS_VD_MEMBER_OFFLINE); + } +} + +void ps3_hba_qos_waitq_clear_all(struct ps3_instance *instance, int resp_status) +{ + unsigned long flag = 0; + unsigned short i = 0; + struct ps3_qos_tg_context *qos_tg_ctx = &instance->qos_context.tg_ctx; + struct ps3_qos_pd_mgr *qos_pd_mgr = NULL; + struct ps3_qos_vd_mgr *qos_vd_mgr = NULL; + struct qos_wait_queue *wait_q = NULL; + struct ps3_cmd *cmd = NULL; + + for (i = 1; i <= instance->qos_context.max_pd_count; i++) { + qos_pd_mgr = ps3_qos_pd_mgr_get(instance, i); + if (ps3_atomic_read(&qos_pd_mgr->valid) == 1) { + ps3_pd_quota_waitq_clear_all(qos_pd_mgr, resp_status); + cancel_work_sync(&qos_pd_mgr->resend_work); + } + } + + for (i = 1; i <= instance->qos_context.max_vd_count; i++) { + qos_vd_mgr = &instance->qos_context.vd_ctx.qos_vd_mgrs[i]; + if (qos_vd_mgr->valid) { + ps3_vd_quota_waitq_clean(qos_vd_mgr, NULL, resp_status); + cancel_work_sync(&qos_vd_mgr->resend_work); + } + } + + ps3_spin_lock_irqsave(&qos_tg_ctx->lock, &flag); + for (i = 1; i <= instance->qos_context.max_vd_count; i++) { + wait_q = &qos_tg_ctx->vd_cmd_waitqs[i]; + while (wait_q->count > 0) { + cmd = list_first_entry(&wait_q->wait_list, + struct ps3_cmd, qos_list); + LOG_DEBUG( + "qos clear tg vd waitq. hno:%u qid:%u t_id:0x%llx cmd:%d\n", + PS3_HOST(instance), i, cmd->trace_id, + cmd->index); + list_del(&cmd->qos_list); + wait_q->count--; + qos_tg_ctx->total_wait_cmd_cnt--; + ps3_qos_update_vd_quota(cmd); + ps3_qos_update_pd_quota(cmd); + ps3_scsih_drv_io_reply_scsi(cmd->scmd, cmd, resp_status, + PS3_FALSE); + } + } + + wait_q = &qos_tg_ctx->mgr_cmd_wait_q; + while (wait_q->count > 0) { + cmd = list_first_entry(&wait_q->wait_list, struct ps3_cmd, + qos_list); + LOG_DEBUG("qos clear tg mgr waitq. hno:%u t_id:0x%llx cmd:%d\n", + PS3_HOST(instance), cmd->trace_id, cmd->index); + list_del(&cmd->qos_list); + wait_q->count--; + qos_tg_ctx->total_wait_cmd_cnt--; + ps3_scsih_drv_io_reply_scsi(cmd->scmd, cmd, resp_status, + PS3_FALSE); + } + ps3_spin_unlock_irqrestore(&qos_tg_ctx->lock, flag); + cancel_work_sync(&qos_tg_ctx->resend_work); + + LOG_INFO("host_no:%u:clear all qos waitq\n", PS3_HOST(instance)); +} + +static inline unsigned short +ps3_qos_pd_workq_id_get(struct ps3_qos_pd_context *qos_pd_ctx) +{ + return ps3_atomic_inc_return(&qos_pd_ctx->workq_id_cnt) % + qos_pd_ctx->workq_count; +} + +void ps3_qos_pd_rsc_init(struct ps3_qos_pd_mgr *qos_pd_mgr, + struct ps3_pd_entry *pd_entry) +{ + struct ps3_qos_pd_context *pd_ctx = NULL; + struct PS3QosInfo *qos_cfg_info = NULL; + + qos_cfg_info = &qos_pd_mgr->instance->ctrl_info.qosInfo; + qos_pd_mgr->pd_quota = pd_entry->normal_quota; + qos_pd_mgr->direct_quota = pd_entry->direct_quota; + qos_pd_mgr->dev_type = pd_entry->dev_type; + switch (pd_entry->dev_type) { + case PS3_DEV_TYPE_SAS_HDD: + if (qos_pd_mgr->pd_quota == 0) { + qos_pd_mgr->pd_quota = + qos_cfg_info->sasHddQuota > 0 ? + qos_cfg_info->sasHddQuota : + g_ps3_qos_sas_pd_quota; + } + qos_pd_mgr->pd_init_quota = qos_pd_mgr->pd_quota; + qos_pd_mgr->adjust_max_quota = qos_pd_mgr->pd_quota; + qos_pd_mgr->adjust_min_quota = + qos_pd_mgr->pd_quota - (qos_pd_mgr->pd_quota / 10); + break; + case PS3_DEV_TYPE_SAS_SSD: + if (qos_pd_mgr->pd_quota == 0) { + qos_pd_mgr->pd_quota = + qos_cfg_info->sasSsdQuota > 0 ? + qos_cfg_info->sasSsdQuota : + g_ps3_qos_sas_pd_quota; + } + qos_pd_mgr->pd_init_quota = qos_pd_mgr->pd_quota; + qos_pd_mgr->adjust_max_quota = qos_pd_mgr->pd_quota; + qos_pd_mgr->adjust_min_quota = + qos_pd_mgr->pd_quota - (qos_pd_mgr->pd_quota / 10); + break; + case PS3_DEV_TYPE_SATA_HDD: + if (qos_pd_mgr->pd_quota == 0) { + qos_pd_mgr->pd_quota = + qos_cfg_info->sataHddQuota > 0 ? + qos_cfg_info->sataHddQuota : + g_ps3_qos_hdd_pd_quota; + } + qos_pd_mgr->pd_init_quota = qos_pd_mgr->pd_quota; + qos_pd_mgr->adjust_max_quota = qos_pd_mgr->pd_quota; + qos_pd_mgr->adjust_min_quota = + qos_pd_mgr->pd_quota - (qos_pd_mgr->pd_quota / 10); + break; + case PS3_DEV_TYPE_SATA_SSD: + if (qos_pd_mgr->pd_quota == 0) { + qos_pd_mgr->pd_quota = + qos_cfg_info->sataSsdQuota > 0 ? + qos_cfg_info->sataSsdQuota : + g_ps3_qos_ssd_pd_quota; + } + qos_pd_mgr->pd_init_quota = qos_pd_mgr->pd_quota; + qos_pd_mgr->adjust_max_quota = qos_pd_mgr->pd_quota; + qos_pd_mgr->adjust_min_quota = + qos_pd_mgr->pd_quota - (qos_pd_mgr->pd_quota / 10); + break; + case PS3_DEV_TYPE_NVME_SSD: + pd_ctx = &qos_pd_mgr->instance->qos_context.pd_ctx; + if (qos_pd_mgr->pd_quota == 0) { + qos_pd_mgr->pd_quota = + qos_cfg_info->nvmeNormalQuota > 0 ? + qos_cfg_info->nvmeNormalQuota : + pd_ctx->nvme_normal_quota; + } + if (qos_pd_mgr->direct_quota == 0) { + qos_pd_mgr->direct_quota = + qos_cfg_info->nvmeDirectQuota > 0 ? + qos_cfg_info->nvmeDirectQuota : + g_ps3_qos_nvme_pd_quota; + } + qos_pd_mgr->pd_init_quota = qos_pd_mgr->direct_quota; + qos_pd_mgr->adjust_max_quota = qos_pd_mgr->direct_quota; + qos_pd_mgr->adjust_min_quota = qos_pd_mgr->direct_quota - + (qos_pd_mgr->direct_quota / 10); + break; + } + + LOG_DEBUG( + "qos pd rsc init. hno:%u type:%u normal_quota:%u direct_quota:%u\n", + PS3_HOST(qos_pd_mgr->instance), pd_entry->dev_type, + pd_entry->normal_quota, pd_entry->direct_quota); +} + +void ps3_qos_adjust_pd_rsc(struct scsi_device *sdev, + struct ps3_instance *instance, int reason) +{ + int *quota = NULL; + unsigned long flag = 0; + struct ps3_qos_pd_mgr *qos_pd_mgr = NULL; + + if (!PS3_QOS_INITED(instance) || (PS3_SDEV_PRI_DATA(sdev) == NULL) || + (PS3_SDEV_PRI_DATA(sdev)->dev_type == PS3_DEV_TYPE_VD)) { + goto l_out; + } + + qos_pd_mgr = ps3_qos_pd_mgr_get( + instance, PS3_PDID(&PS3_SDEV_PRI_DATA(sdev)->disk_pos)); + + switch (PS3_SDEV_PRI_DATA(sdev)->dev_type) { + case PS3_DEV_TYPE_SAS_HDD: + case PS3_DEV_TYPE_SAS_SSD: + case PS3_DEV_TYPE_SATA_HDD: + case PS3_DEV_TYPE_SATA_SSD: + quota = &qos_pd_mgr->pd_quota; + break; + case PS3_DEV_TYPE_NVME_SSD: + quota = &qos_pd_mgr->direct_quota; + break; + default: + goto l_out; + } + if ((*quota > sdev->queue_depth && + reason == PS3_QOS_QUOTA_ADJUST_QFULL) || + ((*quota == qos_pd_mgr->adjust_max_quota || + time_after(sdev->last_queue_full_time + sdev->queue_ramp_up_period, + jiffies) || + time_after(sdev->last_queue_ramp_up + sdev->queue_ramp_up_period, + jiffies)) && + reason == PS3_QOS_QUOTA_ADJUST_UP)) { + goto l_out; + } + if (reason == PS3_QOS_QUOTA_ADJUST_UP) { + ps3_spin_lock_irqsave(&qos_pd_mgr->adjust_quota_lock, &flag); + if (*quota < qos_pd_mgr->adjust_max_quota) + (*quota)++; + } else if (reason == PS3_QOS_QUOTA_ADJUST_QFULL) { + ps3_spin_lock_irqsave(&qos_pd_mgr->adjust_quota_lock, &flag); + if (*quota > qos_pd_mgr->adjust_min_quota) + (*quota)--; + } else if (reason == PS3_QOS_QUOTA_ADJUST_DEFULAT) { + ps3_spin_lock_irqsave(&qos_pd_mgr->adjust_quota_lock, &flag); + *quota = qos_pd_mgr->adjust_max_quota; + } else { + goto l_out; + } + ps3_spin_unlock_irqrestore(&qos_pd_mgr->adjust_quota_lock, flag); + LOG_INFO_IN_IRQ( + instance, + "hno:%u dev[%u:%u] qos quota change to [%d] reason [%d]\n", + PS3_HOST(instance), sdev->channel, sdev->id, *quota, reason); +l_out: + return; +} + +struct ps3_qos_pd_mgr *ps3_qos_pd_mgr_init(struct ps3_instance *instance, + struct ps3_pd_entry *pd_entry) +{ + unsigned short pd_id = 0; + struct ps3_qos_pd_mgr *qos_pd_mgr = NULL; + + if (!PS3_QOS_INITED(instance)) + goto _out; + + pd_id = PS3_PDID(&pd_entry->disk_pos); + qos_pd_mgr = ps3_qos_pd_mgr_get(instance, pd_id); + + if ((ps3_atomic_add_unless(&qos_pd_mgr->valid, 1, 1) != 0) || +#if defined(PS3_SUPPORT_LINX80) + ((ps3_sas_is_support_smp(instance)) && + (!ps3_check_pd_is_vd_member(pd_entry->config_flag))) || + (pd_entry->config_flag == MIC_PD_STATE_JBOD)) { +#else + (pd_entry->config_flag == MIC_PD_STATE_JBOD)) { +#endif + if (pd_entry->config_flag == MIC_PD_STATE_JBOD) + ps3_qos_vd_member_del(instance, &pd_entry->disk_pos); + + ps3_qos_pd_rsc_init(qos_pd_mgr, pd_entry); + qos_pd_mgr->disk_id = pd_id; + qos_pd_mgr->vd_id = 0; + qos_pd_mgr->workq_id = + ps3_qos_pd_workq_id_get(&instance->qos_context.pd_ctx); + qos_pd_mgr->clearing = PS3_FALSE; + LOG_INFO_IN_IRQ( + instance, + "host_no:%u pd_id:%u dev_type:%u device qos init\n", + PS3_HOST(instance), pd_id, qos_pd_mgr->dev_type); + } +_out: + return qos_pd_mgr; +} + +void ps3_qos_pd_mgr_reset(struct ps3_instance *instance, unsigned short pd_id) +{ + struct ps3_qos_pd_mgr *qos_pd_mgr = NULL; + + if (!PS3_QOS_INITED(instance)) + return; + + qos_pd_mgr = ps3_qos_pd_mgr_get(instance, pd_id); + qos_pd_mgr->clearing = PS3_FALSE; +} + +void ps3_qos_vd_attr_change(struct ps3_instance *instance, + struct PS3VDEntry *vd_entry_old, + struct PS3VDEntry *vd_entry) +{ + struct ps3_qos_pd_mgr *qos_pd_mgr = NULL; + unsigned short change_type = PS3_QOS_VD_TYPE_CHANGE_INVALID; + unsigned short i = 0; + unsigned short j = 0; + unsigned short pd_id = 0; + + if (!PS3_QOS_INITED(instance) || vd_entry->isNvme) + goto l_out; + + if (!vd_entry->isSsd) + change_type = PS3_QOS_VD_TYPE_CHANGE_TO_HDD; + else + change_type = PS3_QOS_VD_TYPE_CHANGE_TO_SSD; + + for (i = 0; i < vd_entry->spanCount; i++) { + for (j = 0; j < vd_entry->span[i].spanPdNum; j++) { + pd_id = vd_entry->span[i] + .extent[j] + .phyDiskID.ps3Dev.phyDiskID; + if (pd_id <= PS3_MAX_PD_COUNT(instance)) { + qos_pd_mgr = + ps3_qos_pd_mgr_get(instance, pd_id); + switch (change_type) { + case PS3_QOS_VD_TYPE_CHANGE_TO_SSD: + qos_pd_mgr->pd_quota = + qos_pd_mgr->adjust_max_quota; + break; + case PS3_QOS_VD_TYPE_CHANGE_TO_HDD: + if (vd_entry_old != NULL) { + qos_pd_mgr->pd_quota = + instance->cmd_context + .max_scsi_cmd_count; + } else { + qos_pd_mgr->pd_quota = + qos_pd_mgr + ->adjust_max_quota; + } + break; + default: + break; + } + } + } + } + + LOG_INFO_IN_IRQ(instance, + "host_no:%u qos vd[%u:%u:%u] attr change:%u complete\n", + PS3_HOST(instance), PS3_CHANNEL(&vd_entry->diskPos), + PS3_TARGET(&vd_entry->diskPos), + PS3_VDID(&vd_entry->diskPos), change_type); +l_out: + return; +} + +void ps3_qos_vd_member_change(struct ps3_instance *instance, + struct ps3_pd_entry *pd_entry, + struct scsi_device *sdev, + unsigned char is_vd_member) +{ + unsigned short pd_id = 0; + struct ps3_qos_pd_mgr *qos_pd_mgr = NULL; + struct ps3_scsi_priv_data *priv_data = NULL; + + if (!PS3_QOS_INITED(instance)) + goto _out; + + pd_id = PS3_PDID(&pd_entry->disk_pos); + ps3_mutex_lock(&instance->dev_context.dev_priv_lock); + priv_data = PS3_SDEV_PRI_DATA(sdev); + if (priv_data != NULL) { + if (is_vd_member) { + ps3_hba_qos_pd_clean(instance, priv_data, + PS3_STATUS_DEVICE_NOT_FOUND); + ps3_mutex_unlock(&instance->dev_context.dev_priv_lock); + } else { + ps3_hba_qos_pd_clean(instance, priv_data, + PS3_STATUS_VD_MEMBER_OFFLINE); + ps3_mutex_unlock(&instance->dev_context.dev_priv_lock); + qos_pd_mgr = ps3_qos_pd_mgr_get(instance, pd_id); + ps3_qos_pd_rsc_init(qos_pd_mgr, pd_entry); + qos_pd_mgr->vd_id = 0; + } + } else { + ps3_mutex_unlock(&instance->dev_context.dev_priv_lock); + } + LOG_INFO( + "update pd qos rsc. host_no:%u pd_id:%u dev_type:%u is_vd_member:%u\n", + PS3_HOST(instance), pd_id, pd_entry->dev_type, is_vd_member); + +_out: + return; +} + +void ps3_hba_qos_vd_init(struct ps3_instance *instance, + struct PS3VDEntry *vd_entry) +{ + struct ps3_qos_vd_mgr *qos_vd_mgr = NULL; + unsigned short vd_id = 0; + + vd_id = PS3_VDID(&vd_entry->diskPos); + qos_vd_mgr = ps3_qos_vd_mgr_get_by_id(instance, vd_id); + ps3_atomic_set(&qos_vd_mgr->vd_quota, + g_ps3_qos_hdd_pd_quota * vd_entry->physDrvCnt); + qos_vd_mgr->vd_entry = vd_entry; + qos_vd_mgr->valid = PS3_TRUE; +} + +static void ps3_qos_r1x_member_init(struct ps3_qos_pd_mgr *qos_pd_mgr, + struct ps3_pd_entry *pd_entry, + unsigned short judge_type) +{ + switch (judge_type) { + case PS3_IS_SSD_ODD_R1X_VD: + if (qos_pd_mgr->dev_type == PS3_DEV_TYPE_NVME_SSD) { + qos_pd_mgr->direct_quota = + qos_pd_mgr->pd_init_quota >> 1; + qos_pd_mgr->adjust_max_quota = qos_pd_mgr->direct_quota; + qos_pd_mgr->adjust_min_quota = + qos_pd_mgr->direct_quota - + (qos_pd_mgr->direct_quota / 10); + } else { + qos_pd_mgr->pd_quota = qos_pd_mgr->pd_init_quota >> 1; + qos_pd_mgr->adjust_max_quota = qos_pd_mgr->pd_quota; + qos_pd_mgr->adjust_min_quota = + qos_pd_mgr->pd_quota - + (qos_pd_mgr->pd_quota / 10); + } + break; + case PS3_IS_SSD_EVEN_R1X_VD: + case PS3_IS_VALID_R1X_VD: + if (qos_pd_mgr->vd_id > 0) + ps3_qos_pd_rsc_init(qos_pd_mgr, pd_entry); + break; + case PS3_IS_HDD_R1X_VD: + default: + break; + } +} + +void ps3_qos_vd_init(struct ps3_instance *instance, struct PS3VDEntry *vd_entry) +{ + struct ps3_qos_pd_mgr *qos_pd_mgr = NULL; + unsigned short vd_id = 0; + unsigned short pd_id = 0; + struct ps3_pd_entry *pd_entry = NULL; + unsigned short i, j = 0; + unsigned short judge_type = PS3_IS_VALID_R1X_VD; + + if (!PS3_QOS_INITED(instance)) + return; + + vd_id = get_offset_of_vdid(PS3_VDID_OFFSET(instance), + PS3_VDID(&vd_entry->diskPos)); + judge_type = ps3_odd_r1x_judge(vd_entry); + for (i = 0; i < vd_entry->spanCount; i++) { + for (j = 0; j < vd_entry->span[i].spanPdNum; j++) { + pd_id = vd_entry->span[i] + .extent[j] + .phyDiskID.ps3Dev.phyDiskID; + if (pd_id > PS3_MAX_PD_COUNT(instance)) + continue; + pd_entry = ps3_dev_mgr_lookup_pd_info_by_id( + instance, pd_id); + if (pd_entry) { + qos_pd_mgr = ps3_qos_pd_mgr_init( + instance, pd_entry); + if (unlikely(qos_pd_mgr->dev_type != + pd_entry->dev_type)) { + ps3_qos_pd_rsc_init(qos_pd_mgr, + pd_entry); + if (!vd_entry->isSsd) { + qos_pd_mgr->pd_quota = + instance->cmd_context + .max_scsi_cmd_count; + } + } + + ps3_qos_r1x_member_init(qos_pd_mgr, + pd_entry, + judge_type); + if (qos_pd_mgr->dev_type == PS3_DEV_TYPE_NVME_SSD) { + if (qos_pd_mgr->pd_quota > + qos_pd_mgr->direct_quota) { + qos_pd_mgr->pd_quota = + qos_pd_mgr + ->direct_quota; + } + } + qos_pd_mgr->vd_id = vd_id; + } + } + } + + if (instance->qos_context.opts.qos_vd_init) + instance->qos_context.opts.qos_vd_init(instance, vd_entry); + + LOG_INFO_IN_IRQ(instance, + "host_no:%u disk_pos[%u:%u:%u] device qos init\n", + PS3_HOST(instance), PS3_CHANNEL(&vd_entry->diskPos), + PS3_TARGET(&vd_entry->diskPos), vd_id); +} + +void ps3_hba_qos_vd_reset(struct ps3_instance *instance, unsigned short disk_id) +{ + struct ps3_qos_vd_mgr *qos_vd_mgr = NULL; + + qos_vd_mgr = ps3_qos_vd_mgr_get_by_id(instance, disk_id); + qos_vd_mgr->valid = PS3_FALSE; +} + +void ps3_qos_vd_reset(struct ps3_instance *instance, unsigned short disk_id) +{ + if (!PS3_QOS_INITED(instance)) + return; + + if (instance->qos_context.opts.qos_vd_reset) + instance->qos_context.opts.qos_vd_reset(instance, disk_id); +} + +static void ps3_qos_pd_notify_timeout(struct ps3_instance *instance) +{ + struct ps3_qos_pd_context *qos_pd_ctx = NULL; + unsigned short i = 0; + struct ps3_qos_pd_mgr *qos_pd_mgr = NULL; + unsigned long timeout_jiffies = 0; + + qos_pd_ctx = &instance->qos_context.pd_ctx; + for (i = 1; i <= instance->qos_context.max_pd_count; i++) { + qos_pd_mgr = ps3_qos_pd_mgr_get(instance, i); + timeout_jiffies = qos_pd_mgr->last_sched_jiffies + + PS3_QOS_WAITQ_TIMEOUT * HZ; + if (time_after(jiffies, timeout_jiffies)) { + if (ps3_qos_single_pd_notify(qos_pd_ctx, qos_pd_mgr)) { + LOG_INFO( + "awake qos pd quota waitq by poll. host_no:%u vid:%u pid:%u\n", + PS3_HOST(instance), qos_pd_mgr->vd_id, + qos_pd_mgr->disk_id); + } + } + } +} + +static unsigned char ps3_qos_vd_notify_timeout(struct ps3_instance *instance) +{ + struct ps3_qos_vd_context *qos_vd_ctx = NULL; + unsigned short i = 0; + struct ps3_qos_vd_mgr *qos_vd_mgr = NULL; + unsigned long timeout_jiffies = 0; + unsigned char notified = PS3_FALSE; + + for (i = 1; i <= instance->qos_context.max_vd_count; i++) { + qos_vd_ctx = &instance->qos_context.vd_ctx; + qos_vd_mgr = &qos_vd_ctx->qos_vd_mgrs[i]; + timeout_jiffies = qos_vd_mgr->last_sched_jiffies + + PS3_QOS_WAITQ_TIMEOUT * HZ; + if (time_after(jiffies, timeout_jiffies)) { + if (ps3_qos_single_vd_notify(qos_vd_ctx, qos_vd_mgr)) { + notified = PS3_TRUE; + LOG_INFO( + "awake qos vd quota waitq by poll. host_no:%u vid:%u\n", + PS3_HOST(instance), qos_vd_mgr->id); + } + } + } + + return notified; +} + +static unsigned char ps3_qos_tg_notify_timeout(struct ps3_instance *instance) +{ + struct ps3_qos_tg_context *qos_tg_ctx = NULL; + unsigned char notified = PS3_FALSE; + unsigned long timeout_jiffies = 0; + + qos_tg_ctx = &instance->qos_context.tg_ctx; + timeout_jiffies = + qos_tg_ctx->last_sched_jiffies + PS3_QOS_WAITQ_TIMEOUT * HZ; + if (qos_tg_ctx->total_wait_cmd_cnt && + ps3_qos_tag_rsc_available(instance) && + time_after(jiffies, timeout_jiffies)) { + queue_work(qos_tg_ctx->work_queue, &qos_tg_ctx->resend_work); + notified = PS3_TRUE; + LOG_INFO("awake qos cmd waitq by poll. host_no:%u\n", + PS3_HOST(instance)); + } + + return notified; +} + +void ps3_hba_qos_waitq_poll(struct ps3_instance *instance) +{ + if (!ps3_qos_tg_notify_timeout(instance)) { + if (!ps3_qos_vd_notify_timeout(instance)) + ps3_qos_pd_notify_timeout(instance); + } +} + +void ps3_qos_waitq_poll(struct ps3_instance *instance) +{ + if (!PS3_QOS_INITED(instance)) + return; + + if (++instance->qos_context.poll_count == PS3_QOS_POLL_INTERVAL) { + instance->qos_context.poll_count = 0; + instance->qos_context.opts.qos_waitq_poll(instance); + } +} + +static void ps3_hba_qos_reset(struct ps3_instance *instance) +{ + struct ps3_qos_context *qos_ctx = &instance->qos_context; + struct ps3_qos_vd_mgr *qos_vd_mgr = NULL; + struct ps3_qos_pd_mgr *qos_pd_mgr = NULL; + struct ps3_qos_tg_context *qos_tg_ctx = NULL; + unsigned short i = 0; + struct ps3_pd_entry *pd_entry = NULL; + + qos_tg_ctx = &instance->qos_context.tg_ctx; + ps3_atomic_set(&qos_tg_ctx->mgr_free_cnt, + qos_tg_ctx->mgr_exclusive_cnt); + ps3_atomic_set(&qos_tg_ctx->mgr_share_used, 0); + ps3_atomic_set(&qos_tg_ctx->share_free_cnt, qos_tg_ctx->share); + + for (i = 1; i <= qos_ctx->max_vd_count; i++) { + qos_vd_mgr = &instance->qos_context.vd_ctx.qos_vd_mgrs[i]; + if (i == qos_ctx->max_vd_count) { + ps3_atomic_set(&qos_vd_mgr->vd_quota, + g_ps3_qos_vd_quota); + ps3_atomic_set(&qos_vd_mgr->exclusive_cmd_cnt, + g_ps3_qos_jbod_exclusive); + } else { + ps3_atomic_set(&qos_vd_mgr->exclusive_cmd_cnt, + g_ps3_qos_vd_exclusive); + } + ps3_atomic_set(&qos_vd_mgr->share_cmd_used, 0); + } + + qos_ctx->pd_ctx.nvme_normal_quota = g_ps3_qos_hba_nvme_normal_quota; + for (i = 1; i <= qos_ctx->max_pd_count; i++) { + qos_pd_mgr = ps3_qos_pd_mgr_get(instance, i); + if (ps3_atomic_read(&qos_pd_mgr->valid) == 1) { + pd_entry = + ps3_dev_mgr_lookup_pd_info_by_id(instance, i); + if (pd_entry != NULL) + ps3_qos_pd_rsc_init(qos_pd_mgr, pd_entry); + } + } +} + +void ps3_qos_close(struct ps3_instance *instance) +{ + if (!PS3_QOS_INITED(instance)) + return; + + ps3_qos_wait_io_end(instance); + instance->qos_context.opts.qos_waitq_clear( + instance, PS3_SCSI_RESULT_HOST_STATUS(DID_SOFT_ERROR)); + LOG_INFO("qos close. host_no:%u\n", PS3_HOST(instance)); +} + +void ps3_qos_open(struct ps3_instance *instance) +{ + if (!PS3_QOS_INITED(instance)) + return; + + instance->qos_context.opts.qos_reset(instance); +} + +static unsigned char ps3_qos_waitq_flag_get(struct ps3_qos_softq_mgr *softq_mgr) +{ + unsigned char waitq_flag = 0; + + switch (softq_mgr->id) { + case PS3_QOS_MGRQ: + waitq_flag = PS3_QOS_CMD_IN_MGR; + break; + case PS3_QOS_CMDQ_0: + waitq_flag = PS3_QOS_CMD_IN_CMDQ0; + break; + case PS3_QOS_CMDQ_1: + waitq_flag = PS3_QOS_CMD_IN_CMDQ1; + break; + case PS3_QOS_CMDQ_2: + waitq_flag = PS3_QOS_CMD_IN_CMDQ2; + break; + case PS3_QOS_CMDQ_3: + waitq_flag = PS3_QOS_CMD_IN_CMDQ3; + break; + default: + LOG_ERROR_IN_IRQ(softq_mgr->instance, + "invalid softq id. host_no:%u id:%u\n", + PS3_HOST(softq_mgr->instance), softq_mgr->id); + } + + return waitq_flag; +} + +static unsigned char ps3_qos_cq_rc_get(struct ps3_cmd *cmd, + struct ps3_qos_softq_mgr *softq_mgr, + struct qos_wait_queue *wait_q) +{ + unsigned char can_get = PS3_FALSE; + unsigned long flag = 0; + + if (ps3_atomic_dec_return(&softq_mgr->free_cnt) >= 0) + can_get = PS3_TRUE; + else + ps3_atomic_inc(&softq_mgr->free_cnt); + + if (!can_get) { + ps3_spin_lock_irqsave(&softq_mgr->rc_lock, &flag); + list_add_tail(&cmd->qos_list, &wait_q->wait_list); + wait_q->count++; + softq_mgr->total_wait_cmd_cnt++; + cmd->qos_waitq_flag = ps3_qos_waitq_flag_get(softq_mgr); + ps3_spin_unlock_irqrestore(&softq_mgr->rc_lock, flag); + LOG_DEBUG("insert qos cq waitq.host_no:%u:t_id:0x%llx\n" + "\tCFID:%u que_id:%u diskid:%u waitq:%u\n", + PS3_HOST(cmd->instance), cmd->trace_id, cmd->index, + softq_mgr->id, cmd->io_attr.disk_id, wait_q->count); + } + + return can_get; +} + +static struct qos_wait_queue * +ps3_qos_cq_waitq_get(struct ps3_cmd *cmd, struct ps3_qos_softq_mgr *softq_mgr) +{ + unsigned short waitq_id = 0; + struct qos_wait_queue *waitq = NULL; + + if (PS3_CMD_TYPE_IS_RW(cmd->cmd_word.type)) { + if (cmd->io_attr.dev_type == PS3_DEV_TYPE_VD) { + waitq_id = get_offset_of_vdid( + PS3_VDID_OFFSET(cmd->instance), + PS3_VDID(&cmd->io_attr.vd_entry->diskPos)); + } else { + waitq_id = cmd->instance->qos_context.max_vd_count; + } + waitq = &softq_mgr->waitqs[waitq_id]; + } else { + waitq = &softq_mgr->waitqs[0]; + } + + return waitq; +} + +static unsigned char ps3_qos_cq_rc_check(struct ps3_cmd *cmd, + struct ps3_qos_softq_mgr *softq_mgr) +{ + unsigned char can_get = PS3_FALSE; + unsigned long flag = 0; + struct qos_wait_queue *waitq = NULL; + + waitq = ps3_qos_cq_waitq_get(cmd, softq_mgr); + if (likely(softq_mgr->total_wait_cmd_cnt == 0)) { + can_get = ps3_qos_cq_rc_get(cmd, softq_mgr, waitq); + } else { + ps3_spin_lock_irqsave(&softq_mgr->rc_lock, &flag); + if (softq_mgr->total_wait_cmd_cnt > 0) { + list_add_tail(&cmd->qos_list, &waitq->wait_list); + waitq->count++; + softq_mgr->total_wait_cmd_cnt++; + cmd->qos_waitq_flag = ps3_qos_waitq_flag_get(softq_mgr); + ps3_spin_unlock_irqrestore(&softq_mgr->rc_lock, flag); + can_get = PS3_FALSE; + LOG_DEBUG( + "insert qos cq waitq.host_no:%u:t_id:0x%llx CFID:%u\n" + "\tque_id:%u diskid:%u waitq:%u\n", + PS3_HOST(cmd->instance), cmd->trace_id, + cmd->index, softq_mgr->id, cmd->io_attr.disk_id, + waitq->count); + } else { + ps3_spin_unlock_irqrestore(&softq_mgr->rc_lock, flag); + can_get = ps3_qos_cq_rc_get(cmd, softq_mgr, waitq); + } + } + + return can_get; +} + +static inline struct ps3_qos_softq_mgr * +ps3_qos_cmdq_mgr_get(struct ps3_instance *instance, unsigned short index) +{ + return &instance->qos_context.cq_ctx.cmdqs[index]; +} + +static void ps3_qos_update_target_cmdq(struct ps3_cmd *cmd) +{ + unsigned char que_id = 0; + unsigned char qmask = 0; + + cmd->cmdq_count = 0; + qmask = cmd->cmd_word.qMask; + while (que_id < cmd->instance->ctrl_info.vdQueueNum) { + if (qmask & (1 << que_id)) { + cmd->cmdq_info[cmd->cmdq_count].que_id = que_id; + if (++cmd->cmdq_count == PS3_QOS_MAX_CMDQ_ONE_CMD) + break; + } + que_id++; + } +} + +static unsigned char ps3_qos_cq_decision(struct ps3_cmd *cmd) +{ + unsigned char can_get = PS3_TRUE; + struct ps3_instance *instance = cmd->instance; + struct ps3_qos_softq_mgr *softq_mgr = NULL; + unsigned short cmdq_index = 0; + unsigned char i = 0; + + if (cmd->cmd_word.type == PS3_CMDWORD_TYPE_MGR) { + PS3_QOS_STAT_START(cmd->instance, cmd, PS3_QOS_MGR_PRO); + softq_mgr = &instance->qos_context.cq_ctx.mgrq; + can_get = ps3_qos_cq_rc_check(cmd, softq_mgr); + PS3_QOS_STAT_END(cmd->instance, cmd, PS3_QOS_MGR_PRO); + if (!can_get) { + PS3_QOS_STAT_START(cmd->instance, cmd, + PS3_QOS_MGR_QUEUE); + } + } else { + PS3_QOS_STAT_START(cmd->instance, cmd, PS3_QOS_CMD_PRO); + ps3_qos_update_target_cmdq(cmd); + for (i = 0; i < cmd->cmdq_count; i++) { + cmdq_index = cmd->cmdq_info[i].que_id; + softq_mgr = ps3_qos_cmdq_mgr_get(instance, cmdq_index); + can_get = ps3_qos_cq_rc_check(cmd, softq_mgr); + if (can_get) { + cmd->cmdq_info[i].get_rc = PS3_TRUE; + } else { + can_get = PS3_FALSE; + break; + } + } + PS3_QOS_STAT_END(cmd->instance, cmd, PS3_QOS_CMD_PRO); + if (!can_get) { + PS3_QOS_STAT_START(cmd->instance, cmd, + PS3_QOS_CMD_QUEUE); + } + } + + return can_get; +} + +unsigned char ps3_raid_qos_decision(struct ps3_cmd *cmd) +{ + unsigned char can_get = PS3_FALSE; + + if (PS3_CMD_TYPE_IS_RW(cmd->cmd_word.type)) { + can_get = ps3_qos_pd_quota_decision(cmd); + if (!can_get || + cmd->io_attr.direct_flag == PS3_CMDWORD_DIRECT_OK) { + goto _out; + } + } + can_get = ps3_qos_cq_decision(cmd); +_out: + return can_get; +} + +static void ps3_qos_update_cmdq_rc(struct ps3_cmd *cmd) +{ + unsigned char i = 0; + unsigned char index = 0; + struct ps3_qos_softq_mgr *softq_mgr = NULL; + int cmdq_free = 0; + + for (i = 0; i < cmd->cmdq_count; i++) { + index = cmd->cmdq_info[i].que_id; + softq_mgr = ps3_qos_cmdq_mgr_get(cmd->instance, index); + cmdq_free = ps3_atomic_inc_return(&softq_mgr->free_cnt); + LOG_DEBUG( + "update cmdq rc. host_no:%u t_id:0x%llx CFID:%u rc[%u,%d] dev_t:%u\n", + PS3_HOST(cmd->instance), cmd->trace_id, cmd->index, + softq_mgr->id, cmdq_free, cmd->io_attr.dev_type); + } +} + +static void ps3_qos_update_mgrq_rc(struct ps3_cmd *cmd) +{ + struct ps3_qos_cq_context *qos_cq_ctx = + &cmd->instance->qos_context.cq_ctx; + int free_cnt = 0; + + free_cnt = ps3_atomic_inc_return(&qos_cq_ctx->mgrq.free_cnt); + LOG_DEBUG("update mgrq rc. host_no:%u t_id:0x%llx CFID:%u rc:%d\n", + PS3_HOST(cmd->instance), cmd->trace_id, cmd->index, free_cnt); +} + +static void ps3_raid_qos_cmd_update(struct ps3_cmd *cmd) +{ + if (cmd->cmd_word.type == PS3_CMDWORD_TYPE_MGR) { + ps3_qos_update_mgrq_rc(cmd); + } else { + ps3_qos_update_pd_quota(cmd); + ps3_qos_update_cmdq_rc(cmd); + } +} + +void ps3_qos_mgrq_resend(struct ps3_qos_softq_mgr *softq_mgr) +{ + int ret = PS3_SUCCESS; + unsigned long flag = 0; + struct ps3_cmd *cmd = NULL; + struct ps3_instance *instance = softq_mgr->instance; + struct qos_wait_queue *waitq = &softq_mgr->waitqs[0]; + unsigned char waitq_cleared = PS3_FALSE; + + ps3_spin_lock_irqsave(&softq_mgr->rc_lock, &flag); + while (waitq->count > 0) { + cmd = list_first_entry(&waitq->wait_list, struct ps3_cmd, + qos_list); + if (ps3_atomic_dec_return(&softq_mgr->free_cnt) < 0) { + ps3_atomic_inc(&softq_mgr->free_cnt); + break; + } + list_del(&cmd->qos_list); + waitq->count--; + softq_mgr->total_wait_cmd_cnt--; + PS3_QOS_STAT_END(instance, cmd, PS3_QOS_MGR_QUEUE); + ret = ps3_scsi_cmd_send(instance, cmd, PS3_FALSE); + if (unlikely(ret != PS3_SUCCESS)) { + ps3_raid_qos_cmd_update(cmd); + ps3_qos_cmd_resend_fail(cmd, ret); + } else { + cmd->qos_waitq_flag = 0; + LOG_DEBUG( + "raid qos mgr waitq resend:host_no:%u t_id:0x%llx CFID:%u type:%u\n", + PS3_HOST(instance), cmd->trace_id, cmd->index, + cmd->cmd_word.type); + } + } + + if (softq_mgr->total_wait_cmd_cnt == 0) + waitq_cleared = PS3_TRUE; + ps3_spin_unlock_irqrestore(&softq_mgr->rc_lock, flag); + + if (waitq_cleared) { + LOG_DEBUG("hno:%u resend all mgrq waited cmds\n", + PS3_HOST(instance)); + } + softq_mgr->last_sched_jiffies = jiffies; +} + +static void ps3_qos_mgrq_resend_work(struct work_struct *work) +{ + struct ps3_qos_softq_mgr *softq_mgr = + ps3_container_of(work, struct ps3_qos_softq_mgr, resend_work); + ps3_qos_mgrq_resend(softq_mgr); +} + +static int ps3_qos_mgrq_init(struct ps3_instance *instance, + struct ps3_qos_softq_mgr *softq_mgr) +{ + int ret = PS3_SUCCESS; + unsigned int mgrq_depth = 0; + unsigned int high_mgr_cmd = 0; + char workq_name[PS3_QOS_MAX_WORKQ_NAME_LENGTH] = { 0 }; + + if (!ps3_ioc_multi_func_support(instance)) + high_mgr_cmd = PS3_QOS_HIGH_PRI_MGR_CMD_COUNT; + else + high_mgr_cmd = PS3_QOS_HIGH_PRI_MGR_CMD_COUNT >> 1; + + mgrq_depth = instance->qos_context.cq_ctx.mgrq_depth; + if (mgrq_depth <= high_mgr_cmd) { + ret = -PS3_FAILED; + LOG_ERROR("hno:%u:mgrq_depth is too small func:%u depth:%u\n", + PS3_HOST(softq_mgr->instance), + ps3_get_pci_function(instance->pdev), mgrq_depth); + goto _out; + } + + softq_mgr->id = PS3_QOS_MGRQ; + softq_mgr->instance = instance; + ps3_spin_lock_init(&softq_mgr->rc_lock); + ps3_atomic_set(&softq_mgr->free_cnt, mgrq_depth - high_mgr_cmd); + softq_mgr->waitq_cnt = 1; + softq_mgr->waitqs = (struct qos_wait_queue *)ps3_vzalloc( + instance, sizeof(struct qos_wait_queue)); + if (softq_mgr->waitqs == NULL) { + LOG_ERROR("hno:%u:Failed to kcalloc memory for waitqs\n", + PS3_HOST(softq_mgr->instance)); + ret = -PS3_FAILED; + goto _out; + } + INIT_LIST_HEAD(&softq_mgr->waitqs[0].wait_list); + softq_mgr->waitqs[0].count = 0; + INIT_WORK(&softq_mgr->resend_work, ps3_qos_mgrq_resend_work); + softq_mgr->total_wait_cmd_cnt = 0; + softq_mgr->last_sched_jiffies = 0; + snprintf(workq_name, PS3_QOS_MAX_WORKQ_NAME_LENGTH, "mgrq_wq_f%u", + ps3_get_pci_function(instance->pdev)); + softq_mgr->work_queue = create_singlethread_workqueue(workq_name); + if (softq_mgr->work_queue == NULL) { + LOG_ERROR("qos mgr workq create failed\n"); + ret = -PS3_FAILED; + goto _release_waitq; + } + + LOG_INFO("hno:%u:mgrq init success func:%u depth:%u\n", + PS3_HOST(softq_mgr->instance), + ps3_get_pci_function(instance->pdev), mgrq_depth); + goto _out; +_release_waitq: + ps3_vfree(instance, softq_mgr->waitqs); + softq_mgr->waitqs = NULL; +_out: + return ret; +} + +static unsigned char ps3_qos_softq_notify(struct ps3_qos_softq_mgr *softq_mgr) +{ + unsigned char notified = PS3_FALSE; + + if (softq_mgr->total_wait_cmd_cnt > 0 && + ps3_atomic_read(&softq_mgr->free_cnt) > 0) { + queue_work(softq_mgr->work_queue, &softq_mgr->resend_work); + notified = PS3_TRUE; + } + + return notified; +} + +static unsigned char ps3_qos_cq_notify(struct ps3_instance *instance) +{ + unsigned char i = 0; + unsigned char notified = PS3_FALSE; + struct ps3_qos_cq_context *qos_cq_ctx = &instance->qos_context.cq_ctx; + struct ps3_qos_softq_mgr *softq_mgr = NULL; + + softq_mgr = &qos_cq_ctx->mgrq; + ps3_qos_softq_notify(softq_mgr); + + for (i = 0; i < qos_cq_ctx->cmdq_cnt; i++) { + softq_mgr = ps3_qos_cmdq_mgr_get(instance, i); + if (ps3_qos_softq_notify(softq_mgr)) + notified = PS3_TRUE; + } + + return notified; +} + +void ps3_raid_qos_waitq_notify(struct ps3_instance *instance) +{ + if (!ps3_qos_cq_notify(instance)) + ps3_qos_pd_notify(instance); +} + +static unsigned char +ps3_qos_cmdq_resend_judge(struct ps3_qos_softq_mgr *softq_mgr, + struct ps3_cmd *cmd) +{ + unsigned char can_get = PS3_TRUE; + struct ps3_instance *instance = NULL; + struct ps3_qos_softq_mgr *next_softq_mgr = NULL; + + instance = cmd->instance; + if (cmd->cmdq_count == 1) { + cmd->cmdq_info[0].get_rc = PS3_TRUE; + } else { + if (cmd->cmdq_info[0].que_id == softq_mgr->id) { + cmd->cmdq_info[0].get_rc = PS3_TRUE; + next_softq_mgr = ps3_qos_cmdq_mgr_get( + instance, cmd->cmdq_info[1].que_id); + can_get = ps3_qos_cq_rc_check(cmd, next_softq_mgr); + if (can_get) + cmd->cmdq_info[1].get_rc = PS3_TRUE; + } else { + cmd->cmdq_info[1].get_rc = PS3_TRUE; + } + } + + if (can_get) + PS3_QOS_STAT_END(instance, cmd, PS3_QOS_CMD_QUEUE); + + LOG_DEBUG( + "raid resend cmdq judge:host_no:%u t_id:0x%llx CFID:%u cmdq_id:%u ret:%u\n", + PS3_HOST(cmd->instance), cmd->trace_id, cmd->index, + softq_mgr->id, can_get); + return can_get; +} + +static void ps3_qos_resend_multi_cmd(struct ps3_qos_softq_mgr *softq_mgr, + struct qos_wait_queue *waitq, int count) +{ + int ret = PS3_SUCCESS; + struct ps3_cmd *cmd = NULL; + struct ps3_instance *instance = softq_mgr->instance; + unsigned int can_send = 0; + + can_send = PS3_MIN(ps3_atomic_read(&softq_mgr->free_cnt), count); + while (waitq->count > 0 && can_send > 0) { + cmd = list_first_entry(&waitq->wait_list, struct ps3_cmd, + qos_list); + list_del(&cmd->qos_list); + softq_mgr->total_wait_cmd_cnt--; + waitq->count--; + + if (ps3_qos_vd_seq_check(cmd)) + continue; + can_send--; + ps3_atomic_dec(&softq_mgr->free_cnt); + if (ps3_qos_cmdq_resend_judge(softq_mgr, cmd)) { + ret = ps3_scsi_cmd_send(instance, cmd, PS3_FALSE); + if (unlikely(ret != PS3_SUCCESS)) { + ps3_raid_qos_cmd_update(cmd); + ps3_qos_cmd_resend_fail(cmd, ret); + } else { + cmd->qos_waitq_flag = 0; + LOG_DEBUG( + "raid qos cmdq waitq resend:host_no:%u t_id:0x%llx CFID:%u qid:%u\n", + PS3_HOST(instance), cmd->trace_id, + cmd->index, waitq->id); + } + } + } +} + +static void ps3_qos_cmdq_resend(struct ps3_qos_softq_mgr *softq_mgr) +{ + unsigned long flag = 0; + struct ps3_instance *instance = softq_mgr->instance; + struct qos_wait_queue *waitq = NULL; + unsigned short cur_que_id = softq_mgr->poll_que_id; + + ps3_spin_lock_irqsave(&softq_mgr->rc_lock, &flag); + while (ps3_atomic_read(&softq_mgr->free_cnt) > 0 && + softq_mgr->total_wait_cmd_cnt > 0) { + waitq = &softq_mgr->waitqs[cur_que_id]; + if (waitq->count > 0) { + ps3_qos_resend_multi_cmd(softq_mgr, waitq, + softq_mgr->poll_cmd_cnt); + } + if (++cur_que_id > instance->qos_context.max_vd_count) + cur_que_id = 1; + } + ps3_spin_unlock_irqrestore(&softq_mgr->rc_lock, flag); + softq_mgr->poll_que_id = cur_que_id; + softq_mgr->last_sched_jiffies = jiffies; + ps3_qos_pd_notify(instance); +} + +static void ps3_qos_cmdq_resend_work(struct work_struct *work) +{ + struct ps3_qos_softq_mgr *softq_mgr = + ps3_container_of(work, struct ps3_qos_softq_mgr, resend_work); + ps3_qos_cmdq_resend(softq_mgr); +} + +static int ps3_qos_cmdq_init(struct ps3_instance *instance, + struct ps3_qos_softq_mgr *softq_mgr) +{ + int ret = PS3_SUCCESS; + unsigned short i = 0; + struct qos_wait_queue *waitq = NULL; + char workq_name[PS3_QOS_MAX_WORKQ_NAME_LENGTH] = { 0 }; + unsigned int cmdq_depth = 0; + + cmdq_depth = instance->qos_context.cq_ctx.cmdq_depth; + softq_mgr->instance = instance; + ps3_spin_lock_init(&softq_mgr->rc_lock); + ps3_atomic_set(&softq_mgr->free_cnt, cmdq_depth); + softq_mgr->waitq_cnt = instance->qos_context.max_vd_count + 1; + softq_mgr->waitqs = (struct qos_wait_queue *)ps3_vzalloc( + instance, softq_mgr->waitq_cnt * sizeof(struct qos_wait_queue)); + if (softq_mgr->waitqs == NULL) { + LOG_ERROR("hno:%u:Failed to kcalloc memory for waitqs\n", + PS3_HOST(instance)); + ret = -PS3_FAILED; + goto _out; + } + memset(softq_mgr->waitqs, 0, + (instance->qos_context.max_vd_count + 1) * + sizeof(struct qos_wait_queue)); + for (i = 1; i < softq_mgr->waitq_cnt; i++) { + waitq = &softq_mgr->waitqs[i]; + waitq->id = i; + INIT_LIST_HEAD(&waitq->wait_list); + waitq->count = 0; + } + + softq_mgr->poll_cmd_cnt = PS3_QOS_POLL_CMD_COUNT; + softq_mgr->poll_que_id = 1; + INIT_WORK(&softq_mgr->resend_work, ps3_qos_cmdq_resend_work); + softq_mgr->total_wait_cmd_cnt = 0; + softq_mgr->last_sched_jiffies = 0; + snprintf(workq_name, PS3_QOS_MAX_WORKQ_NAME_LENGTH, "cmdq_wq_f%u_%u", + ps3_get_pci_function(instance->pdev), softq_mgr->id); + softq_mgr->work_queue = create_singlethread_workqueue(workq_name); + if (softq_mgr->work_queue == NULL) { + LOG_ERROR("qos mgr workq create failed\n"); + ret = -PS3_FAILED; + goto _release_waitq; + } + + LOG_INFO("hno:%u:cmdq init success func:%u depth:%u id:%u\n", + PS3_HOST(softq_mgr->instance), + ps3_get_pci_function(instance->pdev), cmdq_depth, + softq_mgr->id); + goto _out; +_release_waitq: + ps3_vfree(instance, softq_mgr->waitqs); + softq_mgr->waitqs = NULL; +_out: + return ret; +} + +static void ps3_qos_softq_exit(struct ps3_instance *instance, + struct ps3_qos_softq_mgr *softq_mgr) +{ + cancel_work_sync(&softq_mgr->resend_work); + flush_workqueue(softq_mgr->work_queue); + destroy_workqueue(softq_mgr->work_queue); + ps3_vfree(instance, softq_mgr->waitqs); + softq_mgr->waitqs = NULL; +} + +static int ps3_qos_cq_context_init(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + unsigned short i = 0; + unsigned short j = 0; + struct ps3_qos_cq_context *qos_cq_ctx = NULL; + struct ps3_qos_softq_mgr *softq_mgr = NULL; + unsigned long long mgrq_depth = 0; + unsigned long long cmdq_depth = 0; + + qos_cq_ctx = &instance->qos_context.cq_ctx; + ps3_mgrq_depth_get(instance, &mgrq_depth); + if (mgrq_depth == 0) { + ret = -PS3_FAILED; + LOG_ERROR("hno:%u:mgrq_depth is invalid func:%u\n", + PS3_HOST(instance), + ps3_get_pci_function(instance->pdev)); + goto _out; + } + qos_cq_ctx->mgrq_depth = mgrq_depth; + + ps3_cmdq_depth_get(instance, &cmdq_depth); + if (cmdq_depth == 0) { + ret = -PS3_FAILED; + LOG_ERROR("hno:%u:cmdq_depth is invalid func:%u\n", + PS3_HOST(instance), + ps3_get_pci_function(instance->pdev)); + goto _out; + } + qos_cq_ctx->cmdq_depth = cmdq_depth; + + if (ps3_qos_mgrq_init(instance, &qos_cq_ctx->mgrq) != PS3_SUCCESS) { + ret = -PS3_FAILED; + goto _out; + } + + qos_cq_ctx->cmdq_cnt = instance->ctrl_info.vdQueueNum; + qos_cq_ctx->cmdqs = (struct ps3_qos_softq_mgr *)ps3_vzalloc( + instance, + qos_cq_ctx->cmdq_cnt * sizeof(struct ps3_qos_softq_mgr)); + if (qos_cq_ctx->cmdqs == NULL) { + LOG_ERROR("hno:%u:Failed to kcalloc memory for cmdqs\n", + PS3_HOST(instance)); + ret = -PS3_FAILED; + goto _mgrq_exit; + } + + for (i = 0; i < qos_cq_ctx->cmdq_cnt; i++) { + softq_mgr = &qos_cq_ctx->cmdqs[i]; + softq_mgr->id = i; + if (ps3_qos_cmdq_init(instance, softq_mgr) != PS3_SUCCESS) { + ret = -PS3_FAILED; + goto _cmdq_exit; + } + } + + LOG_INFO("hno:%u: qos cq context init success cmdq_cnt:%u\n", + PS3_HOST(instance), qos_cq_ctx->cmdq_cnt); + goto _out; +_cmdq_exit: + for (j = 0; j < i; j++) { + softq_mgr = &qos_cq_ctx->cmdqs[j]; + ps3_qos_softq_exit(instance, softq_mgr); + } + ps3_vfree(instance, qos_cq_ctx->cmdqs); + qos_cq_ctx->cmdqs = NULL; +_mgrq_exit: + ps3_qos_softq_exit(instance, &qos_cq_ctx->mgrq); +_out: + return ret; +} + +static void ps3_qos_cq_context_exit(struct ps3_instance *instance) +{ + struct ps3_qos_cq_context *qos_cq_ctx = NULL; + unsigned short i = 0; + struct ps3_qos_softq_mgr *softq_mgr = NULL; + + qos_cq_ctx = &instance->qos_context.cq_ctx; + if (qos_cq_ctx->cmdqs != NULL) { + ps3_qos_softq_exit(instance, &qos_cq_ctx->mgrq); + for (i = 0; i < qos_cq_ctx->cmdq_cnt; i++) { + softq_mgr = &qos_cq_ctx->cmdqs[i]; + ps3_qos_softq_exit(instance, softq_mgr); + } + ps3_vfree(instance, qos_cq_ctx->cmdqs); + qos_cq_ctx->cmdqs = NULL; + } + + LOG_INFO("hno:%u: qos cq context exit success\n", PS3_HOST(instance)); +} + +int ps3_raid_qos_init(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + + ret = ps3_qos_cq_context_init(instance); + if (ret != PS3_SUCCESS) + goto _out; + + ret = ps3_qos_pd_context_init(instance); + if (ret != PS3_SUCCESS) + goto _cq_ctx_exit; + + goto _out; +_cq_ctx_exit: + ps3_qos_cq_context_exit(instance); +_out: + return ret; +} + +static unsigned char ps3_qos_softq_abort(struct ps3_qos_softq_mgr *softq_mgr, + struct ps3_cmd *cmd, + unsigned char waitq_flag) +{ + unsigned long lock_flag = 0; + unsigned char found = PS3_FALSE; + struct qos_wait_queue *waitq = NULL; + + waitq = ps3_qos_cq_waitq_get(cmd, softq_mgr); + ps3_spin_lock_irqsave(&softq_mgr->rc_lock, &lock_flag); + if (cmd->qos_waitq_flag == waitq_flag) { + list_del(&cmd->qos_list); + waitq->count--; + softq_mgr->total_wait_cmd_cnt--; + found = PS3_TRUE; + } + ps3_spin_unlock_irqrestore(&softq_mgr->rc_lock, lock_flag); + + return found; +} + +static void ps3_qos_peer_cmdq_rc_update(struct ps3_cmd *cmd) +{ + unsigned char i = 0; + struct ps3_qos_softq_mgr *softq_mgr = NULL; + + for (i = 0; i < cmd->cmdq_count; i++) { + if (cmd->cmdq_info[i].get_rc) { + softq_mgr = ps3_qos_cmdq_mgr_get( + cmd->instance, cmd->cmdq_info[i].que_id); + ps3_atomic_inc(&softq_mgr->free_cnt); + } + } +} + +static unsigned char ps3_qos_cmdq_abort(struct ps3_cmd *cmd) +{ + unsigned char i = 0; + unsigned char waitq_flag = 0; + unsigned char found = PS3_FALSE; + struct ps3_qos_softq_mgr *qos_softq_mgr = NULL; + + for (i = 0; i < cmd->cmdq_count; i++) { + qos_softq_mgr = ps3_qos_cmdq_mgr_get(cmd->instance, + cmd->cmdq_info[i].que_id); + waitq_flag = ps3_qos_waitq_flag_get(qos_softq_mgr); + if (cmd->qos_waitq_flag == waitq_flag) { + found = ps3_qos_softq_abort(qos_softq_mgr, cmd, + waitq_flag); + if (found) + break; + } + } + + return found; +} + +unsigned char ps3_raid_qos_waitq_abort(struct ps3_cmd *cmd) +{ + unsigned char found = PS3_FALSE; + struct ps3_qos_softq_mgr *softq_mgr = NULL; + + if (cmd->qos_waitq_flag == 0) + goto _out; + + if (PS3_CMD_TYPE_IS_RW(cmd->cmd_word.type)) { + if (cmd->qos_waitq_flag == PS3_QOS_CMD_IN_PD) { + found = ps3_pd_quota_waiq_abort(cmd); + if (found) + goto _out; + } + + found = ps3_qos_cmdq_abort(cmd); + if (found) { + ps3_qos_update_pd_quota(cmd); + ps3_qos_peer_cmdq_rc_update(cmd); + } + } else { + if (cmd->qos_waitq_flag == PS3_QOS_CMD_IN_MGR) { + softq_mgr = &cmd->instance->qos_context.cq_ctx.mgrq; + found = ps3_qos_softq_abort(softq_mgr, cmd, + PS3_QOS_CMD_IN_MGR); + } + } + +_out: + return found; +} + +static void ps3_qos_mgrq_clean(struct ps3_instance *instance, + struct ps3_scsi_priv_data *priv_data, + int resp_status) +{ + struct ps3_qos_softq_mgr *softq_mgr = NULL; + struct ps3_cmd *cmd = NULL; + struct ps3_cmd *cmd_next = NULL; + struct qos_wait_queue *waitq = NULL; + unsigned long flag = 0; + + softq_mgr = &instance->qos_context.cq_ctx.mgrq; + waitq = &softq_mgr->waitqs[0]; + ps3_spin_lock_irqsave(&softq_mgr->rc_lock, &flag); + if (waitq->count > 0) { + list_for_each_entry_safe(cmd, cmd_next, &waitq->wait_list, + qos_list) { + if (priv_data == NULL || + priv_data == scsi_device_private_data(cmd->scmd)) { + LOG_DEBUG( + "qos clean mgrq. hno:%u t_id:0x%llx CFID:%d\n", + PS3_HOST(instance), cmd->trace_id, + cmd->index); + list_del(&cmd->qos_list); + waitq->count--; + softq_mgr->total_wait_cmd_cnt--; + ps3_scsih_drv_io_reply_scsi( + cmd->scmd, cmd, resp_status, PS3_FALSE); + } + } + } + ps3_spin_unlock_irqrestore(&softq_mgr->rc_lock, flag); + if (priv_data == NULL) + cancel_work_sync(&softq_mgr->resend_work); +} + +static void ps3_qos_cmdq_clean(struct ps3_instance *instance, + unsigned short disk_id, + struct ps3_scsi_priv_data *priv_data, + int resp_status) +{ + unsigned char i = 0; + struct ps3_qos_softq_mgr *softq_mgr = NULL; + struct ps3_cmd *cmd = NULL; + struct ps3_cmd *cmd_next = NULL; + struct qos_wait_queue *waitq = NULL; + unsigned long flag = 0; + struct ps3_qos_cq_context *qos_cq_ctx = &instance->qos_context.cq_ctx; + + for (i = 0; i < qos_cq_ctx->cmdq_cnt; i++) { + softq_mgr = ps3_qos_cmdq_mgr_get(instance, i); + waitq = &softq_mgr->waitqs[disk_id]; + if (waitq->count > 0) { + ps3_spin_lock_irqsave(&softq_mgr->rc_lock, &flag); + list_for_each_entry_safe(cmd, cmd_next, + &waitq->wait_list, qos_list) { + if (priv_data == NULL || + priv_data == scsi_device_private_data( + cmd->scmd)) { + LOG_DEBUG( + "qos clean cmdq. hno:%u t_id:0x%llx CFID:%d cmdq:%u\n", + PS3_HOST(instance), + cmd->trace_id, cmd->index, + softq_mgr->id); + list_del(&cmd->qos_list); + waitq->count--; + softq_mgr->total_wait_cmd_cnt--; + ps3_qos_update_pd_quota(cmd); + ps3_scsih_drv_io_reply_scsi(cmd->scmd, + cmd, + resp_status, + PS3_FALSE); + } + } + ps3_spin_unlock_irqrestore(&softq_mgr->rc_lock, flag); + } + } +} + +static void ps3_qos_cmdq_clear(struct ps3_instance *instance, int resp_status) +{ + unsigned short i = 0; + unsigned short j = 0; + struct ps3_qos_softq_mgr *softq_mgr = NULL; + struct ps3_cmd *cmd = NULL; + struct ps3_cmd *cmd_next = NULL; + struct qos_wait_queue *waitq = NULL; + unsigned long flag = 0; + struct ps3_qos_cq_context *qos_cq_ctx = &instance->qos_context.cq_ctx; + + for (i = 0; i < qos_cq_ctx->cmdq_cnt; i++) { + softq_mgr = ps3_qos_cmdq_mgr_get(instance, i); + for (j = 1; j <= instance->qos_context.max_vd_count; j++) { + waitq = &softq_mgr->waitqs[j]; + if (waitq->count > 0) { + ps3_spin_lock_irqsave(&softq_mgr->rc_lock, + &flag); + list_for_each_entry_safe(cmd, cmd_next, + &waitq->wait_list, + qos_list) { + LOG_DEBUG( + "qos clean cmdq. hno:%u t_id:0x%llx CFID:%d cmdq:%u\n", + PS3_HOST(instance), + cmd->trace_id, cmd->index, + softq_mgr->id); + list_del(&cmd->qos_list); + waitq->count--; + softq_mgr->total_wait_cmd_cnt--; + ps3_qos_update_pd_quota(cmd); + ps3_scsih_drv_io_reply_scsi(cmd->scmd, + cmd, + resp_status, + PS3_FALSE); + } + ps3_spin_unlock_irqrestore(&softq_mgr->rc_lock, + flag); + } + } + cancel_work_sync(&softq_mgr->resend_work); + } +} + +static void ps3_raid_qos_pd_clean(struct ps3_instance *instance, + struct ps3_scsi_priv_data *priv_data, + int resp_status) +{ + unsigned short pd_id = 0; + unsigned short vd_id = 0; + struct ps3_qos_pd_mgr *qos_pd_mgr = NULL; + + pd_id = PS3_PDID(&priv_data->disk_pos); + qos_pd_mgr = ps3_qos_pd_mgr_get(instance, pd_id); + ps3_pd_quota_waitq_clean(qos_pd_mgr, 0, resp_status); + + if (!PS3_QOS_PD_IS_VD_MEMBER(qos_pd_mgr)) { + vd_id = instance->qos_context.max_vd_count; + ps3_qos_cmdq_clean(instance, vd_id, priv_data, resp_status); + ps3_qos_mgrq_clean(instance, priv_data, resp_status); + } + + LOG_INFO("qos clean vd. host_no:%u did:%u vid:%u\n", PS3_HOST(instance), + pd_id, vd_id); +} + +static void ps3_raid_qos_vd_clean(struct ps3_instance *instance, + struct ps3_scsi_priv_data *priv_data, + int resp_status) +{ + unsigned short vd_id = 0; + unsigned short pd_id = 0; + struct ps3_qos_pd_mgr *qos_pd_mgr = NULL; + + vd_id = get_offset_of_vdid(PS3_VDID_OFFSET(instance), + PS3_VDID(&priv_data->disk_pos)); + for (pd_id = 1; pd_id <= instance->qos_context.max_pd_count; pd_id++) { + qos_pd_mgr = ps3_qos_pd_mgr_get(instance, pd_id); + if (ps3_atomic_read(&qos_pd_mgr->valid) == 1 && + PS3_QOS_PD_IS_VD_MEMBER(qos_pd_mgr)) { + ps3_pd_quota_waitq_clean(qos_pd_mgr, vd_id, + resp_status); + } + } + + ps3_qos_cmdq_clean(instance, vd_id, NULL, resp_status); + ps3_qos_mgrq_clean(instance, priv_data, resp_status); + + LOG_FILE_INFO("qos clean vd. host_no:%u type:%u disk_id:%u\n", + PS3_HOST(instance), priv_data->dev_type, vd_id); +} + +void ps3_raid_qos_waitq_clear_all(struct ps3_instance *instance, + int resp_status) +{ + unsigned short i = 0; + struct ps3_qos_pd_mgr *qos_pd_mgr = NULL; + + for (i = 1; i <= instance->qos_context.max_pd_count; i++) { + qos_pd_mgr = ps3_qos_pd_mgr_get(instance, i); + if (ps3_atomic_read(&qos_pd_mgr->valid) == 1) { + ps3_pd_quota_waitq_clear_all(qos_pd_mgr, resp_status); + cancel_work_sync(&qos_pd_mgr->resend_work); + } + } + + ps3_qos_cmdq_clear(instance, resp_status); + + ps3_qos_mgrq_clean(instance, NULL, resp_status); + + LOG_INFO("host_no:%u:clear all qos waitq\n", PS3_HOST(instance)); +} + +static unsigned char +ps3_qos_softq_notify_timeout(struct ps3_qos_softq_mgr *qos_softq_mgr) +{ + unsigned char notified = PS3_FALSE; + unsigned long timeout_jiffies = 0; + + timeout_jiffies = + qos_softq_mgr->last_sched_jiffies + PS3_QOS_WAITQ_TIMEOUT * HZ; + if (qos_softq_mgr->total_wait_cmd_cnt && + ps3_atomic_read(&qos_softq_mgr->free_cnt) > 0 && + time_after(jiffies, timeout_jiffies)) { + queue_work(qos_softq_mgr->work_queue, + &qos_softq_mgr->resend_work); + notified = PS3_TRUE; + LOG_INFO("awake qos softq by poll. host_no:%u q_id:%u\n", + PS3_HOST(qos_softq_mgr->instance), qos_softq_mgr->id); + } + + return notified; +} + +static unsigned char ps3_qos_cq_notify_timeout(struct ps3_instance *instance) +{ + struct ps3_qos_cq_context *qos_cq_ctx = NULL; + struct ps3_qos_softq_mgr *qos_softq_mgr = NULL; + unsigned char notified = PS3_FALSE; + unsigned char i = 0; + + qos_cq_ctx = &instance->qos_context.cq_ctx; + ps3_qos_softq_notify_timeout(&qos_cq_ctx->mgrq); + + for (i = 0; i < qos_cq_ctx->cmdq_cnt; i++) { + qos_softq_mgr = &qos_cq_ctx->cmdqs[i]; + if (ps3_qos_softq_notify_timeout(qos_softq_mgr)) + notified = PS3_TRUE; + } + + return notified; +} + +void ps3_raid_qos_waitq_poll(struct ps3_instance *instance) +{ + if (!ps3_qos_cq_notify_timeout(instance)) + ps3_qos_pd_notify_timeout(instance); +} + +static void ps3_raid_qos_reset(struct ps3_instance *instance) +{ + struct ps3_qos_context *qos_ctx = &instance->qos_context; + struct ps3_qos_pd_mgr *qos_pd_mgr = NULL; + struct ps3_qos_cq_context *qos_cq_ctx = NULL; + struct ps3_qos_softq_mgr *qos_softq_mgr = NULL; + unsigned short i = 0; + struct ps3_pd_entry *pd_entry = NULL; + + qos_cq_ctx = &instance->qos_context.cq_ctx; + qos_softq_mgr = &qos_cq_ctx->mgrq; + ps3_atomic_set(&qos_softq_mgr->free_cnt, + qos_ctx->cq_ctx.mgrq_depth - + (PS3_QOS_HIGH_PRI_MGR_CMD_COUNT >> 1)); + for (i = 0; i < qos_cq_ctx->cmdq_cnt; i++) { + qos_softq_mgr = &qos_cq_ctx->cmdqs[i]; + ps3_atomic_set(&qos_softq_mgr->free_cnt, + qos_ctx->cq_ctx.cmdq_depth); + } + + qos_ctx->pd_ctx.nvme_normal_quota = g_ps3_qos_raid_nvme_normal_quota; + for (i = 1; i <= qos_ctx->max_pd_count; i++) { + qos_pd_mgr = ps3_qos_pd_mgr_get(instance, i); + if (ps3_atomic_read(&qos_pd_mgr->valid) == 1) { + pd_entry = + ps3_dev_mgr_lookup_pd_info_by_id(instance, i); + if (pd_entry != NULL) + ps3_qos_pd_rsc_init(qos_pd_mgr, pd_entry); + } + } +} + +static unsigned char ps3_raid_qos_pd_resend_check(struct ps3_cmd *cmd) +{ + return ps3_qos_cq_decision(cmd); +} + +void ps3_raid_qos_exit(struct ps3_instance *instance) +{ + ps3_qos_cq_context_exit(instance); + ps3_qos_pd_context_exit(instance); + + LOG_INFO("raid qos exit. hno:%u\n", PS3_HOST(instance)); +} + +void ps3_hba_qos_prepare(struct ps3_instance *instance) +{ + instance->qos_context.opts.qos_init = ps3_hba_qos_init; + instance->qos_context.opts.qos_exit = ps3_hba_qos_exit; + instance->qos_context.opts.qos_decision = ps3_hba_qos_decision; + instance->qos_context.opts.qos_cmd_update = ps3_hba_qos_cmd_update; + instance->qos_context.opts.qos_waitq_notify = ps3_hba_qos_waitq_notify; + instance->qos_context.opts.qos_pd_resend_check = + ps3_hba_qos_pd_resend_check; + instance->qos_context.opts.qos_waitq_abort = ps3_hba_qos_waitq_abort; + instance->qos_context.opts.qos_vd_clean = ps3_hba_qos_vd_clean; + instance->qos_context.opts.qos_pd_clean = ps3_hba_qos_pd_clean; + instance->qos_context.opts.qos_waitq_poll = ps3_hba_qos_waitq_poll; + instance->qos_context.opts.qos_waitq_clear = + ps3_hba_qos_waitq_clear_all; + instance->qos_context.opts.qos_reset = ps3_hba_qos_reset; + instance->qos_context.opts.qos_vd_init = ps3_hba_qos_vd_init; + instance->qos_context.opts.qos_vd_reset = ps3_hba_qos_vd_reset; + instance->qos_context.qos_switch = 1; + instance->qos_context.pd_ctx.nvme_normal_quota = + g_ps3_qos_hba_nvme_normal_quota; +} + +void ps3_raid_qos_prepare(struct ps3_instance *instance) +{ + instance->qos_context.opts.qos_init = ps3_raid_qos_init; + instance->qos_context.opts.qos_exit = ps3_raid_qos_exit; + instance->qos_context.opts.qos_decision = ps3_raid_qos_decision; + instance->qos_context.opts.qos_cmd_update = ps3_raid_qos_cmd_update; + instance->qos_context.opts.qos_waitq_notify = ps3_raid_qos_waitq_notify; + instance->qos_context.opts.qos_pd_resend_check = + ps3_raid_qos_pd_resend_check; + instance->qos_context.opts.qos_waitq_abort = ps3_raid_qos_waitq_abort; + instance->qos_context.opts.qos_vd_clean = ps3_raid_qos_vd_clean; + instance->qos_context.opts.qos_pd_clean = ps3_raid_qos_pd_clean; + instance->qos_context.opts.qos_waitq_poll = ps3_raid_qos_waitq_poll; + instance->qos_context.opts.qos_waitq_clear = + ps3_raid_qos_waitq_clear_all; + instance->qos_context.opts.qos_reset = ps3_raid_qos_reset; + instance->qos_context.opts.qos_vd_init = NULL; + instance->qos_context.opts.qos_vd_reset = NULL; + instance->qos_context.qos_switch = 1; + instance->qos_context.pd_ctx.nvme_normal_quota = + g_ps3_qos_raid_nvme_normal_quota; +} + +int ps3_qos_decision(struct ps3_cmd *cmd) +{ + int ret = PS3_SUCCESS; + unsigned char can_get = PS3_TRUE; + struct ps3_instance *instance = NULL; + + cmd->qos_processing = PS3_TRUE; + wmb(); /* in order to force CPU ordering */ + instance = cmd->instance; + if (!ps3_qos_enable(cmd->instance) || !PS3_QOS_INITED(instance)) + goto _exit; + + ret = ps3_qos_pre_check(instance, cmd); + if (ret != PS3_SUCCESS) + goto _exit; + + can_get = instance->qos_context.opts.qos_decision(cmd); + if (!can_get) { + ret = -PS3_IN_QOS_Q; + PS3_QOS_CMD_INC(instance); + } +_exit: + cmd->qos_processing = PS3_FALSE; + return ret; +} + +void ps3_qos_cmd_update(struct ps3_instance *instance, struct ps3_cmd *cmd) +{ + if (!ps3_qos_enable(instance) || !PS3_QOS_INITED(instance)) + return; + + instance->qos_context.opts.qos_cmd_update(cmd); +} + +void ps3_qos_waitq_notify(struct ps3_instance *instance) +{ + if (!ps3_qos_enable(instance) || !PS3_QOS_INITED(instance)) + return; + instance->qos_context.opts.qos_waitq_notify(instance); +} + +unsigned char ps3_qos_waitq_abort(struct ps3_cmd *cmd) +{ + unsigned char found = PS3_FALSE; + + if (!PS3_QOS_INITED(cmd->instance)) + goto out; + + found = cmd->instance->qos_context.opts.qos_waitq_abort(cmd); + LOG_INFO( + "task abort cmd in qos waitq t_id:0x%llx CFID:%u flag:%u ret:%u\n", + cmd->trace_id, cmd->index, cmd->qos_waitq_flag, found); + if (found) { + ps3_scsih_drv_io_reply_scsi( + cmd->scmd, cmd, SCSI_STATUS_TASK_ABORTED, PS3_FALSE); + } +out: + return found; +} + +void ps3_qos_hard_reset(struct ps3_instance *instance) +{ + int resp_status = 0; + + if (ps3_pci_err_recovery_get(instance)) + resp_status = PS3_STATUS_PCI_RECOVERY; + else + resp_status = PS3_STATUS_HOST_RESET; + + ps3_qos_waitq_clear_all(instance, resp_status); +} + +void ps3_qos_waitq_clear_all(struct ps3_instance *instance, int resp_status) +{ + if (!PS3_QOS_INITED(instance)) + return; + + ps3_qos_wait_io_end(instance); + instance->qos_context.opts.qos_waitq_clear(instance, resp_status); +} + +int ps3_qos_init(struct ps3_instance *instance) +{ + struct ps3_qos_context *qos_ctx = &instance->qos_context; + int ret = PS3_SUCCESS; + + qos_ctx->max_vd_count = instance->ctrl_info.maxVdCount + 1; + qos_ctx->max_pd_count = instance->ctrl_info.maxPdCount; + qos_ctx->inited = 0; + + LOG_INFO( + "hno:%u ps3 qos info:tfifo:%u sas_pd_quota[%u %u] sata_pd_quota[%u %u] nvme_quota[%u %u %u]\n", + PS3_HOST(instance), instance->ctrl_info.qosInfo.tfifoDepth, + instance->ctrl_info.qosInfo.sasHddQuota, + instance->ctrl_info.qosInfo.sasSsdQuota, + instance->ctrl_info.qosInfo.sataHddQuota, + instance->ctrl_info.qosInfo.sataSsdQuota, + instance->ctrl_info.qosInfo.nvmeVdQuota, + instance->ctrl_info.qosInfo.nvmeDirectQuota, + instance->ctrl_info.qosInfo.nvmeNormalQuota); + + if (qos_ctx->opts.qos_init) { + if (qos_ctx->opts.qos_init(instance) == PS3_SUCCESS) { + qos_ctx->inited = 1; + LOG_INFO( + "hno:%u:ps3 qos init success, vd_count:%u pd_count:%u\n", + PS3_HOST(instance), qos_ctx->max_vd_count, + qos_ctx->max_pd_count); + } else { + ret = -PS3_FAILED; + } + } + + return ret; +} + +void ps3_qos_exit(struct ps3_instance *instance) +{ + if (instance->qos_context.opts.qos_exit) { + instance->qos_context.opts.qos_exit(instance); + instance->qos_context.inited = 0; + } +} diff --git a/drivers/scsi/linkdata/ps3stor/ps3_qos.h b/drivers/scsi/linkdata/ps3stor/ps3_qos.h new file mode 100644 index 0000000000000000000000000000000000000000..b1d9cdebccbe78a5d5f207ad9348660b2b4baba5 --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/ps3_qos.h @@ -0,0 +1,303 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _PS3_QOS_H_ +#define _PS3_QOS_H_ + +#include "ps3_platform_utils.h" +#include "ps3_htp.h" +#include "ps3_cmd_channel.h" +#include "ps3_kernel_version.h" + +#define QOS_HIGH_PRI_EXCLUSIVE_CMD_COUNT 32 +#define QOS_MGR_EXCLUSIVE_CMD_COUNT 64 +#define PS3_QOS_DEFAULT_PD_QUOTA 40 +#define PS3_QOS_SAS_PD_QUOTA 125 +#define PS3_QOS_NVME_MEMBER_QUOTA 127 +#define PS3_QOS_NVME_DIRECT_QUOTA 127 +#define PS3_QOS_HBA_NVME_NORMAL_QUOTA 126 +#define PS3_QOS_RAID_NVME_NORMAL_QUOTA 252 +#define PS3_QOS_FUNC1_JBOD_VD_QUOTA 768 +#define PS3_QOS_VD_EXCLUSIVE_CMD_COUNT 40 +#define PS3_QOS_JBOD_EXCLUSIVE_CMD_COUNT 128 +#define PS3_QOS_POLL_INTERVAL 2 +#define PS3_QOS_WAITQ_TIMEOUT 5 +#define PS3_QOS_HBA_MAX_CMD 944 +#define PS3_QOS_CS_FUNC0_SHARE_CMD 80 +#define PS3_QOS_CS_FUNC0_JBOD_VD_QUOTA 80 +#define PS3_QOS_NOTIFY_CMD_COUNT 32 +#define PS3_QOS_FUNC0_PD_WORKQ_COUNT 1 +#define PS3_QOS_FUNC1_PD_WORKQ_COUNT 4 +#define PS3_QOS_POLL_CMD_COUNT 16 + +#define PS3_CMD_NEED_QOS(cmd) \ + ((cmd)->cmd_word.direct == PS3_CMDWORD_DIRECT_NORMAL || \ + (cmd)->cmd_word.direct == PS3_CMDWORD_DIRECT_ADVICE) + +#define PS3_QOS_INITED(instance) ((instance)->qos_context.inited) + +enum ps3_qos_fifo_type { + PS3_QOS_CMDQ_0, + PS3_QOS_CMDQ_1, + PS3_QOS_CMDQ_2, + PS3_QOS_CMDQ_3, + PS3_QOS_MGRQ, + PS3_QOS_TFIFO, + PS3_QOS_FIFO_TYPE_MAX +}; + +enum ps3_qos_cmd_flag { + PS3_QOS_CMD_INIT = 0, + PS3_QOS_CMD_IN_PD, + PS3_QOS_CMD_IN_VD, + PS3_QOS_CMD_IN_FRAME, + PS3_QOS_CMD_IN_MGR, + PS3_QOS_CMD_IN_CMDQ0, + PS3_QOS_CMD_IN_CMDQ1, + PS3_QOS_CMD_IN_CMDQ2, + PS3_QOS_CMD_IN_CMDQ3 +}; + +enum ps3_qos_quota_adjust_flag { + PS3_QOS_QUOTA_ADJUST_DEFULAT = 0, + PS3_QOS_QUOTA_ADJUST_QFULL, + PS3_QOS_QUOTA_ADJUST_UP, + PS3_QOS_QUOTA_ADJUST_INVALID +}; + +enum ps3_qos_vd_change_type { + PS3_QOS_VD_TYPE_CHANGE_TO_HDD, + PS3_QOS_VD_TYPE_CHANGE_TO_SSD, + PS3_QOS_VD_TYPE_CHANGE_INVALID +}; + +struct qos_wait_queue { + struct list_head wait_list; + unsigned int count; + unsigned short id; + spinlock_t *rsc_lock; + int *free_rsc; + atomic_t *used_rsc; + unsigned int *total_waited_cnt; + unsigned short can_resend; + unsigned short has_resend; + unsigned long last_sched_jiffies; +}; + +struct ps3_qos_vd_mgr { + unsigned char valid; + unsigned short id; + unsigned char workq_id; + atomic_t vd_quota; + spinlock_t rsc_lock; + struct qos_wait_queue vd_quota_wait_q; + atomic_t exclusive_cmd_cnt; + atomic_t share_cmd_used; + struct PS3VDEntry *vd_entry; + struct ps3_instance *instance; + struct work_struct resend_work; + unsigned long long last_sched_jiffies; +}; + +struct ps3_qos_pd_mgr { + atomic_t valid; + unsigned char workq_id; + unsigned char dev_type; + unsigned char clearing; + unsigned short disk_id; + unsigned short vd_id; + int pd_quota; + atomic_t pd_used_quota; + struct ps3_instance *instance; + struct work_struct resend_work; + unsigned long long last_sched_jiffies; + struct qos_wait_queue *waitqs; + unsigned short waitq_cnt; + unsigned int total_wait_cmd_cnt; + spinlock_t rc_lock; + unsigned short poll_que_id; + unsigned short poll_start_que_id; + unsigned short poll_cmd_cnt; + spinlock_t direct_rsc_lock; + int direct_quota; + atomic_t direct_used_quota; + unsigned int total_waited_direct_cmd; + atomic_t processing_cnt; + spinlock_t adjust_quota_lock; + int adjust_max_quota; + int adjust_min_quota; + int pd_init_quota; +}; + +struct ps3_qos_pd_context { + struct ps3_qos_pd_mgr *qos_pd_mgrs; + struct workqueue_struct **work_queues; + unsigned short sas_sata_hdd_quota; + unsigned short sas_sata_ssd_quota; + unsigned short nvme_normal_quota; + unsigned short nvme_direct_quota; + atomic_t workq_id_cnt; + unsigned char workq_count; +}; + +struct ps3_qos_vd_context { + struct ps3_qos_vd_mgr *qos_vd_mgrs; + struct workqueue_struct **work_queues; + unsigned short jbod_exclusive_cnt; + unsigned short vd_exclusive_cnt; + unsigned char workq_count; + unsigned char inited; +}; + +struct ps3_qos_tg_context { + unsigned int share; + unsigned int mgr_exclusive_cnt; + unsigned short high_pri_exclusive_cnt; + atomic_t mgr_free_cnt; + atomic_t mgr_share_used; + atomic_t share_free_cnt; + struct qos_wait_queue mgr_cmd_wait_q; + struct qos_wait_queue *vd_cmd_waitqs; + unsigned int total_wait_cmd_cnt; + unsigned char poll_vd_id; + spinlock_t lock; + struct ps3_instance *instance; + struct workqueue_struct *work_queue; + struct work_struct resend_work; + unsigned long long last_sched_jiffies; +}; + +struct ps3_qos_ops { + int (*qos_init)(struct ps3_instance *instance); + void (*qos_exit)(struct ps3_instance *instance); + void (*qos_vd_init)(struct ps3_instance *instance, + struct PS3VDEntry *vd_entry); + void (*qos_vd_reset)(struct ps3_instance *instance, + unsigned short disk_id); + unsigned char (*qos_decision)(struct ps3_cmd *cmd); + void (*qos_cmd_update)(struct ps3_cmd *cmd); + void (*qos_waitq_notify)(struct ps3_instance *instance); + unsigned char (*qos_pd_resend_check)(struct ps3_cmd *cmd); + unsigned char (*qos_waitq_abort)(struct ps3_cmd *cmd); + void (*qos_vd_clean)(struct ps3_instance *instance, + struct ps3_scsi_priv_data *pri_data, int ret_code); + void (*qos_pd_clean)(struct ps3_instance *instance, + struct ps3_scsi_priv_data *priv_data, + int ret_code); + void (*qos_waitq_clear)(struct ps3_instance *instance, int ret_code); + void (*qos_waitq_poll)(struct ps3_instance *instance); + void (*qos_reset)(struct ps3_instance *instance); +}; + +int ps3_hba_qos_init(struct ps3_instance *instance); + +void ps3_hba_qos_exit(struct ps3_instance *instance); + +void ps3_qos_vd_init(struct ps3_instance *instance, + struct PS3VDEntry *vd_entry); + +void ps3_qos_vd_reset(struct ps3_instance *instance, unsigned short disk_id); + +struct ps3_qos_pd_mgr *ps3_qos_pd_mgr_init(struct ps3_instance *instance, + struct ps3_pd_entry *pd_entry); + +void ps3_qos_pd_mgr_reset(struct ps3_instance *instance, unsigned short pd_id); + +void ps3_qos_vd_member_change(struct ps3_instance *instance, + struct ps3_pd_entry *pd_entry, + struct scsi_device *sdev, + unsigned char is_vd_member); + +int ps3_qos_decision(struct ps3_cmd *cmd); + +void ps3_qos_cmd_update(struct ps3_instance *instance, struct ps3_cmd *cmd); + +void ps3_qos_waitq_notify(struct ps3_instance *instance); + +unsigned char ps3_qos_waitq_abort(struct ps3_cmd *aborted_cmd); + +void ps3_qos_device_clean(struct ps3_instance *instance, + struct ps3_scsi_priv_data *pri_data, int ret_code); + +void ps3_qos_disk_del(struct ps3_instance *instance, + struct ps3_scsi_priv_data *priv_data); + +void ps3_qos_vd_member_del(struct ps3_instance *instance, + struct PS3DiskDevPos *dev_pos); + +void ps3_qos_hard_reset(struct ps3_instance *instance); + +void ps3_qos_waitq_clear_all(struct ps3_instance *instance, int resp_status); + +void ps3_qos_waitq_poll(struct ps3_instance *instance); + +void ps3_qos_close(struct ps3_instance *instance); + +void ps3_qos_open(struct ps3_instance *instance); + +void ps3_hba_qos_prepare(struct ps3_instance *instance); + +void ps3_raid_qos_prepare(struct ps3_instance *instance); + +#define PS3_QOS_CMDQ_COUNT 4 +#define PS3_QOS_CMDQ_DEPTH 4096 +#define PS3_QOS_MGRQ_DEPTH 1024 +#define PS3_QOS_HIGH_PRI_MGR_CMD_COUNT 32 +struct ps3_qos_softq_mgr { + unsigned short id; + struct ps3_instance *instance; + spinlock_t rc_lock; + atomic_t free_cnt; + struct qos_wait_queue *waitqs; + unsigned short waitq_cnt; + unsigned int total_wait_cmd_cnt; + struct workqueue_struct *work_queue; + struct work_struct resend_work; + unsigned long long last_sched_jiffies; + unsigned short poll_cmd_cnt; + unsigned short poll_que_id; +}; + +struct ps3_qos_cq_context { + struct ps3_qos_softq_mgr mgrq; + struct ps3_qos_softq_mgr *cmdqs; + unsigned char cmdq_cnt; + unsigned int mgrq_depth; + unsigned int cmdq_depth; +}; + +struct ps3_qos_context { + struct ps3_qos_ops opts; + struct ps3_qos_pd_context pd_ctx; + struct ps3_qos_vd_context vd_ctx; + struct ps3_qos_tg_context tg_ctx; + struct ps3_qos_cq_context cq_ctx; + unsigned short max_vd_count; + unsigned short max_pd_count; + unsigned char poll_count; + unsigned char qos_switch; + unsigned char inited; +}; + +int ps3_raid_qos_init(struct ps3_instance *instance); + +void ps3_raid_qos_exit(struct ps3_instance *instance); + +int ps3_qos_init(struct ps3_instance *instance); + +void ps3_qos_exit(struct ps3_instance *instance); + +void ps3_qos_adjust_pd_rsc(struct scsi_device *sdev, + struct ps3_instance *instance, int reason); + +void ps3_qos_vd_attr_change(struct ps3_instance *instance, + struct PS3VDEntry *vd_entry_old, + struct PS3VDEntry *vd_entry); + +void ps3_qos_pd_rsc_init(struct ps3_qos_pd_mgr *qos_pd_mgr, + struct ps3_pd_entry *pd_entry); + +#if defined(PS3_SUPPORT_LINX80) +void ps3_linx80_vd_member_change(struct ps3_instance *instance, + struct ps3_pd_entry *pd_entry); +#endif + +#endif diff --git a/drivers/scsi/linkdata/ps3stor/ps3_r1x_write_lock.c b/drivers/scsi/linkdata/ps3stor/ps3_r1x_write_lock.c new file mode 100644 index 0000000000000000000000000000000000000000..314d3646f85a424610376d46a208839113b8ab8d --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/ps3_r1x_write_lock.c @@ -0,0 +1,1492 @@ +// SPDX-License-Identifier: GPL-2.0 +#ifndef _WINDOWS +#include +#include +#include +#include +#include +#include +#include + +#endif +#include "ps3_err_def.h" +#include "ps3_driver_log.h" +#include "ps3_rb_tree.h" +#include "ps3_inner_data.h" +#include "ps3_htp_meta.h" +#include "ps3_cmd_channel.h" +#include "ps3_scsih_cmd_parse.h" +#include "ps3_r1x_write_lock.h" +#include "ps3_instance_manager.h" +#include "ps3_platform_utils.h" +#include "ps3_driver_log.h" +#include "ps3_cmd_statistics.h" +#include "ps3_scsih.h" +#include "ps3_scsi_cmd_err.h" +#include "ps3_module_para.h" + +enum { + PS3_CONFLICT_QUEUE_EMPTY = 0, + PS3_CONFLICT_QUEUE_CONFLICT = 1, + PS3_CONFLICT_QUEUE_NO_CONFLICT = 2, +}; + +#define PS3_R1X_RESEND_COUNT (32) + +#define LOCK_BLOCK_SIZE_SHIFT 3 +#define HASH_TABLE_SIZE_SHIFT 12 +#define HASH_TABLE_SIZE (1 << HASH_TABLE_SIZE_SHIFT) +#define HASH_TABLE_SIZE_MASK (HASH_TABLE_SIZE - 1) +#define SZBLOCK_SIZE_SHIFT 10 +#define SZBLOCK_SIZE ((unsigned int)(1 << SZBLOCK_SIZE_SHIFT)) +#define SZBLOCK_SIZE_MASK (SZBLOCK_SIZE - 1) +#define VD_QUE_DEPTH 512 +#define CMD_SIZE_MAX 4096 + +#define PS3_R1X_BIT_CNT \ + ((SZBLOCK_SIZE >> LOCK_BLOCK_SIZE_SHIFT) * HASH_TABLE_SIZE) +#define PS3_R1X_BIT_MAP_SIZE (PS3_R1X_BIT_CNT >> 3) + +#define SECTOR_SIZE_4K 4096 +#define SECTOR_512_4K_SHIFT 3 +#define SZ_BLOCK_SPLIT_MAX ((4096 >> SZBLOCK_SIZE_SHIFT) + 2) + +#define RW_R1X_HASHIDX(lba) \ + (((lba) >> SZBLOCK_SIZE_SHIFT) & HASH_TABLE_SIZE_MASK) +#define RW_R1X_HASHIDX_HASH(mgr, lba) \ + ps3_private_r1x_hash((mgr), ((lba) >> SZBLOCK_SIZE_SHIFT)) + +#define PS3_LBA(lba_hi, lba_lo) \ + ((((unsigned long long)(lba_hi)) << PS3_SHIFT_DWORD) | (lba_lo)) + +#define PS3_SZ_BLOCK_CNT (2560) + +enum { + PS3_R1X_IO_CONFLICT = 1, + PS3_R1X_HASH_CONFILICT = 2, + PS3_R1X_BIT_NOT_ENOUGH = 3, +}; + +#define PS3_R1X_LOCK_TRACE(fmt, ...) LOG_DEBUG(fmt "\n", ##__VA_ARGS__) +#define PS3_R1X_LOCK_INFO(fmt, ...) LOG_INFO(fmt "\n", ##__VA_ARGS__) +#define PS3_R1X_LOCK_WARN(fmt, ...) LOG_WARN(fmt "\n", ##__VA_ARGS__) +#define PS3_R1X_LOCK_ERROR(fmt, ...) LOG_ERROR(fmt "\n", ##__VA_ARGS__) + +#define PS3_R1X_TIMEOUT(cmd, tmo) \ + (time_after(jiffies, (cmd)->scmd->jiffies_at_alloc + (tmo))) + +unsigned int g_ps3_r1x_lock_flag = PS3_R1X_HASHRANGE_LOCK; +unsigned int g_ps3_r1x_lock_enable = 1; + +static int ps3_r1x_hash_bit_lock_of_conflict_queue(struct ps3_r1x_lock_mgr *mgr, + struct ps3_cmd *cmd); + +#if PS3_DESC("-----------------------hash bit互斥方案------------------------") +#define LOCK_BLOCK_SIZE (1 << LOCK_BLOCK_SIZE_SHIFT) +#define LOCK_BLOCK_SIZE_MASK (LOCK_BLOCK_SIZE - 1) +#define RW_BIT_TO_BYTE(bit) ((bit) >> 3) +#define RW_BYTE_TO_BIT(byte) ((bit) << 3) +#define RW_BIT_BYTE_MASK 7 +#define RW_R1X_SZBLOCKIDX(lba) (lba >> SZBLOCK_SIZE_SHIFT) +#define RW_SZBLOCK_IDX_INVALID 0xFFFFFFFFFFFF +#define RW_BITMAP_IDX_INVALID 0xFFFF +#define RW_R1X_GET_BITMAP(bit_buff, idx) (bit_buff[idx]) +#define RW_BIT_64_MASK 63 +#define RW_BYTE_TO_U64(n) (n >> 3) + +struct ps3_r1x_hash_bit_item { + unsigned long long sz_block_idx : 48; + unsigned long long bitmap_idx : 16; +}; + +struct ps3_r1x_bit_block_mgr { + unsigned char *bit_buff; + unsigned char bit_block_size; + unsigned char resv[7]; +}; + +struct ps3_r1x_hash_bit_mgr { + struct ps3_r1x_hash_bit_item hash_item[HASH_TABLE_SIZE]; + struct ps3_r1x_bit_block_mgr bitmap_mgr; +}; + +static unsigned short ps3_private_r1x_hash(struct ps3_r1x_lock_mgr *mgr, + unsigned long hashval) +{ + unsigned long tmp; + + tmp = (hashval * (unsigned long)mgr) ^ + (GOLDEN_RATIO_PRIME + hashval) / L1_CACHE_BYTES; + tmp = tmp ^ ((tmp ^ GOLDEN_RATIO_PRIME) >> HASH_TABLE_SIZE_SHIFT); + return tmp & HASH_TABLE_SIZE_MASK; +} + +static inline void ps3_r1x_conflict_queue_del(struct ps3_r1x_lock_mgr *mgr, + struct ps3_cmd *cmd) +{ + unsigned long flag = 0; + + if (cmd->is_inserted_c_q == 1) { + ps3_spin_lock_irqsave(&mgr->mgr_lock, &flag); + list_del(&cmd->cmd_list); + cmd->is_inserted_c_q = 0; + mgr->cmd_count_in_q--; + ps3_spin_unlock_irqrestore(&mgr->mgr_lock, flag); + } +} + +void ps3_r1x_conflict_queue_clean(struct ps3_scsi_priv_data *pri_data, + int ret_code) +{ + unsigned long flag = 0; + + if (unlikely(pri_data == NULL) || pri_data->lock_mgr.hash_mgr == NULL) + return; + + ps3_spin_lock_irqsave(&pri_data->lock_mgr.mgr_lock, &flag); + if (pri_data->lock_mgr.force_ret_code == SCSI_STATUS_GOOD) { + pri_data->lock_mgr.force_ret_code = ret_code; + complete(&pri_data->lock_mgr.thread_sync); + } + ps3_spin_unlock_irqrestore(&pri_data->lock_mgr.mgr_lock, flag); + + LOG_FILE_INFO("dev[%u:%u], wait r1x conflict queue clean begin\n", + PS3_CHANNEL(&pri_data->disk_pos), + PS3_TARGET(&pri_data->disk_pos)); + wmb(); /* in order to force CPU ordering */ + while (pri_data->lock_mgr.force_ret_code != SCSI_STATUS_GOOD) { + ps3_msleep(100); + rmb(); /* in order to force CPU ordering */ + } + LOG_FILE_INFO("dev[%u:%u], wait r1x conflict queue clean end\n", + PS3_CHANNEL(&pri_data->disk_pos), + PS3_TARGET(&pri_data->disk_pos)); +} + +void ps3_r1x_conflict_queue_target_reset(struct ps3_instance *instance, + unsigned short target_id) +{ + struct PS3ChannelInfo *channel_info = &instance->ctrl_info.channelInfo; + unsigned char i = 0; + struct ps3_scsi_priv_data *pri_data = NULL; + + for (i = 0; i < channel_info->channelNum; i++) { + if (channel_info->channels[i].channelType != PS3_DISK_TYPE_VD || + channel_info->channels[i].maxDevNum <= target_id) { + continue; + } + ps3_mutex_lock(&instance->dev_context.dev_priv_lock); + + pri_data = + ps3_dev_mgr_lookup_vd_pri_data(instance, i, target_id); + ps3_r1x_conflict_queue_clean(pri_data, + SCSI_STATUS_TASK_ABORTED); + + ps3_mutex_unlock(&instance->dev_context.dev_priv_lock); + } +} + +void ps3_r1x_conflict_queue_clean_all(struct ps3_instance *instance, + int ret_code, unsigned char is_remove) +{ + struct PS3ChannelInfo *channel_info = &instance->ctrl_info.channelInfo; + unsigned short maxDevNum = 0; + unsigned char i = 0; + unsigned short j = 0; + struct ps3_scsi_priv_data *pri_data = NULL; + + for (i = 0; i < channel_info->channelNum; i++) { + if (channel_info->channels[i].channelType != PS3_DISK_TYPE_VD) + continue; + maxDevNum = channel_info->channels[i].maxDevNum; + + for (j = 0; j < maxDevNum; j++) { + ps3_mutex_lock(&instance->dev_context.dev_priv_lock); + + pri_data = + ps3_dev_mgr_lookup_vd_pri_data(instance, i, j); + if (unlikely(pri_data == NULL) || + pri_data->lock_mgr.hash_mgr == NULL) { + ps3_mutex_unlock( + &instance->dev_context.dev_priv_lock); + continue; + } + pri_data->lock_mgr.dev_deling = is_remove; + ps3_r1x_conflict_queue_clean(pri_data, ret_code); + + ps3_mutex_unlock(&instance->dev_context.dev_priv_lock); + } + } +} + +unsigned char ps3_r1x_conflict_queue_abort(struct ps3_cmd *cmd, + struct scsi_cmnd *scmd) +{ + unsigned char ret = PS3_TRUE; + struct ps3_scsi_priv_data *data = NULL; + + if (cmd->is_inserted_c_q == 0) { + ret = PS3_FALSE; + goto l_out; + } + + LOG_INFO("task abort cmd:%d is in conflict queue !\n", cmd->index); + + data = (struct ps3_scsi_priv_data *)scmd->device->hostdata; + if (unlikely(data == NULL)) { + ret = PS3_FALSE; + goto l_out; + } + + cmd->is_r1x_aborting = 1; + complete(&data->lock_mgr.thread_sync); + + while (cmd->is_inserted_c_q == 1) { + ps3_msleep(100); + rmb(); /* in order to force CPU ordering */ + } + + if (cmd->is_r1x_aborting == 0) { + ret = PS3_TRUE; + while (cmd->cmd_state.state != PS3_CMD_STATE_INIT) { + ps3_msleep(10); + rmb(); /* in order to force CPU ordering */ + } + } else { + ret = PS3_FALSE; + } + LOG_INFO("task abort cmd:%d in conflict queue aborting ret:%d\n", + cmd->index, ret); + +l_out: + return ret; +} + +static void ps3_conflict_queue_clean_bitmap(struct ps3_r1x_hash_bit_mgr *mgr) +{ + unsigned int i = 0; + + LOG_FILE_INFO("ready clean conflict queue bitmap\n"); + + for (i = 0; i < HASH_TABLE_SIZE; i++) + mgr->hash_item[i].sz_block_idx = RW_SZBLOCK_IDX_INVALID; + + memset(mgr->bitmap_mgr.bit_buff, 0, PS3_R1X_BIT_MAP_SIZE); +} + +static void ps3_conflict_queue_return_all(struct ps3_r1x_lock_mgr *mgr) +{ + unsigned long flag = 0; + struct list_head conflict_cmd_list_tmp; + struct ps3_cmd *cmd = NULL; + struct ps3_cmd *cmd_next = NULL; + struct scsi_cmnd *s_cmd = NULL; + int ret_result = SCSI_STATUS_GOOD; + + INIT_LIST_HEAD(&conflict_cmd_list_tmp); + ps3_spin_lock_irqsave(&mgr->mgr_lock, &flag); + + ret_result = mgr->force_ret_code; + + if (!list_empty(&mgr->conflict_cmd_list)) { + list_for_each_entry_safe(cmd, cmd_next, + &mgr->conflict_cmd_list, cmd_list) { + list_move_tail(&cmd->cmd_list, &conflict_cmd_list_tmp); + } + } + + ps3_conflict_queue_clean_bitmap( + (struct ps3_r1x_hash_bit_mgr *)mgr->hash_mgr_conflict); + + mgr->cmd_count_in_q = 0; + + ps3_spin_unlock_irqrestore(&mgr->mgr_lock, flag); + + list_for_each_entry_safe(cmd, cmd_next, &conflict_cmd_list_tmp, + cmd_list) { + LOG_DEBUG("conflict queue cmd:%d t_id:0x%llx return:0x%x\n", + cmd->index, cmd->trace_id, ret_result); + + ps3_scsi_dma_unmap(cmd); + s_cmd = cmd->scmd; + PS3_IO_OUTSTAND_DEC(cmd->instance, s_cmd); + PS3_IO_BACK_ERR_INC(cmd->instance, s_cmd); + PS3_DEV_BUSY_DEC(s_cmd); + ps3_scsi_cmd_free(cmd); + s_cmd->result = ret_result; + SCMD_IO_DONE(s_cmd); + } + + if (ret_result != SCSI_STATUS_GOOD) + mgr->force_ret_code = SCSI_STATUS_GOOD; +} + +static int ps3_conflict_queue_resend(struct ps3_instance *instance, + struct ps3_r1x_lock_mgr *mgr, + struct ps3_cmd *cmd) +{ + int ret = PS3_SUCCESS; + struct ps3_scsi_priv_data *pri_data = NULL; + struct scsi_cmnd *s_cmd = cmd->scmd; + static unsigned long j; + + ret = instance->ioc_adpter->io_cmd_build(cmd); + if (ret == -PS3_IO_CONFLICT_IN_Q) { + LOG_DEBUG("t_id:0x%llx hno:%u tag:%d cmd build err ret:%d\n", + cmd->trace_id, PS3_HOST(instance), cmd->index, ret); + goto l_out; + } + + ps3_r1x_conflict_queue_del(mgr, cmd); + + if (ret == -PS3_IN_QOS_Q) { + ret = PS3_SUCCESS; + LOG_DEBUG("insert qos waitq. hno:%u t_id:0x%llx tag:%u\n", + PS3_HOST(instance), cmd->trace_id, cmd->index); + goto l_out; + } + + if (ret != PS3_SUCCESS) { + s_cmd->result = PS3_SCSI_RESULT_HOST_STATUS(DID_NO_CONNECT); + LOG_ERROR_TIME_LIM( + &j, PS3_LOG_LIMIT_INTERVAL_MSEC, + "t_id:0x%llx hno:%u tag:%d cmd build NOK ret:%d\n", + cmd->trace_id, PS3_HOST(instance), cmd->index, ret); + goto l_cmd_release; + } + + ret = ps3_scsi_cmd_send(instance, cmd, PS3_TRUE); + if (unlikely(ret != PS3_SUCCESS)) { + if (ret == -PS3_RECOVERED || ret == -PS3_RETRY) { + ps3_errcode_to_scsi_status(instance, s_cmd, + SCSI_STATUS_BUSY, NULL, 0, + cmd); + } else { + s_cmd->result = + PS3_SCSI_RESULT_HOST_STATUS(DID_NO_CONNECT); + } + LOG_ERROR_TIME_LIM( + &j, PS3_LOG_LIMIT_INTERVAL_MSEC, + "t_id:0x%llx hno:%u tag:%d cmd send NOK ret:%d\n", + cmd->trace_id, PS3_HOST(instance), cmd->index, ret); + PS3_DEV_IO_START_ERR_INC(instance, cmd); + goto l_cmd_release; + } + + goto l_out; + +l_cmd_release: + if (cmd->is_got_r1x == 1) { + pri_data = (struct ps3_scsi_priv_data *)s_cmd->device->hostdata; + ps3_r1x_write_unlock(&pri_data->lock_mgr, cmd); + } +l_out: + return ret; +} + +static int ps3_conflict_queue_send_wk(void *data) +{ + struct scsi_device *sdev = (struct scsi_device *)data; + struct ps3_r1x_lock_mgr *mgr = &PS3_SDEV_PRI_DATA(sdev)->lock_mgr; + struct ps3_instance *instance = + (struct ps3_instance *)sdev->host->hostdata; + struct ps3_cmd *cmd = NULL; + struct ps3_cmd *cmd_next = NULL; + struct scsi_cmnd *s_cmd = NULL; + unsigned long flag = 0; + int ret = PS3_SUCCESS; + unsigned int cur_total_count = 0; + unsigned int cmd_count = 0; + unsigned char try_count = 0; + + LOG_INFO("hno:%u vd[%u:%u] conflict queue thread enter\n", + PS3_HOST(instance), PS3_SDEV_CHANNEL(sdev), + PS3_SDEV_TARGET(sdev)); + + while (true) { + wait_for_completion_interruptible(&mgr->thread_sync); + if (mgr->thread_stop || kthread_should_stop()) { + LOG_INFO("hno:%u r1x conflict thread, ready stop\n", + PS3_HOST(instance)); + goto l_out; + } + + if (mgr->force_ret_code != SCSI_STATUS_GOOD) { + LOG_INFO( + "hno:%u r1x conflict thread, return all cmd with:0x%x\n", + PS3_HOST(instance), mgr->force_ret_code); + ps3_conflict_queue_return_all(mgr); + LOG_INFO( + "hno:%u r1x conflict thread, return all cmd with:0x%x end\n", + PS3_HOST(instance), mgr->force_ret_code); + continue; + } + + ps3_spin_lock_irqsave(&mgr->mgr_lock, &flag); + cur_total_count = mgr->cmd_count_in_q; + ps3_spin_unlock_irqrestore(&mgr->mgr_lock, flag); + cmd_count = 0; + try_count = 0; + + list_for_each_entry_safe(cmd, cmd_next, + &mgr->conflict_cmd_list, cmd_list) { + if (cmd->is_r1x_aborting == 1) { + ps3_scsi_dma_unmap(cmd); + s_cmd = cmd->scmd; + cmd->is_r1x_aborting = 0; + wmb(); /* in order to force CPU ordering */ + ps3_r1x_conflict_queue_del(mgr, cmd); + PS3_IO_OUTSTAND_DEC(instance, s_cmd); + PS3_IO_BACK_ERR_INC(instance, s_cmd); + PS3_DEV_BUSY_DEC(s_cmd); + s_cmd->result = SCSI_STATUS_TASK_ABORTED; + SCMD_IO_DONE(s_cmd); + ps3_scsi_cmd_free(cmd); + LOG_INFO( + "hno:%u cmd:%d out r1x conflict queue by abort\n", + PS3_HOST(instance), cmd->index); + continue; + } + + LOG_DEBUG("hno:%u cmd:%d t_id:0x%llx ready resend\n", + PS3_HOST(instance), cmd->index, + cmd->trace_id); + ret = ps3_conflict_queue_resend(instance, mgr, cmd); + if (ret == PS3_SUCCESS) { + LOG_DEBUG( + "hno:%u cmd:%d t_id[%llx] out r1x conflict queue by send success\n", + PS3_HOST(instance), cmd->index, + cmd->trace_id); + } else if (ret == -PS3_IO_CONFLICT_IN_Q) { + } else { + LOG_INFO( + "hno:%u cmd:%d tid:0x%llx out r1x conflict queue by send failed\n", + PS3_HOST(instance), cmd->index, + cmd->trace_id); + ps3_scsi_dma_unmap(cmd); + s_cmd = cmd->scmd; + PS3_IO_OUTSTAND_DEC(cmd->instance, s_cmd); + PS3_IO_BACK_ERR_INC(cmd->instance, s_cmd); + PS3_DEV_BUSY_DEC(s_cmd); + ps3_scsi_cmd_free(cmd); + SCMD_IO_DONE(s_cmd); + } + LOG_DEBUG("hno:%u cmd:%d t_id:0x%llx resend ret:%d\n", + PS3_HOST(instance), cmd->index, cmd->trace_id, + ret); + if (++cmd_count == cur_total_count) + break; + + if (++try_count == PS3_R1X_RESEND_COUNT) { + cond_resched(); + try_count = 0; + } + } + + ps3_spin_lock_irqsave(&mgr->mgr_lock, &flag); + if (list_empty(&mgr->conflict_cmd_list)) { + if (mgr->cmd_count_in_q != 0) { + LOG_INFO_LIM("cmd count in q not zero\n"); + PS3_BUG(); + } + ps3_conflict_queue_clean_bitmap( + (struct ps3_r1x_hash_bit_mgr *) + mgr->hash_mgr_conflict); + } + ps3_spin_unlock_irqrestore(&mgr->mgr_lock, flag); + } + +l_out: + LOG_FILE_INFO("hno:%u vd[%u:%u] conflict queue thread exit\n", + PS3_HOST(instance), PS3_SDEV_CHANNEL(sdev), + PS3_SDEV_TARGET(sdev)); + + return 0; +} + +static inline void ps3_r1x_bitmap_set(struct ps3_r1x_hash_bit_mgr *pHashbitMgr, + unsigned short bitmap_idx, + unsigned int bit_start, + unsigned int bit_cnt) +{ + unsigned int i = 0; + unsigned long *addr = (unsigned long *)(void *)&RW_R1X_GET_BITMAP( + pHashbitMgr->bitmap_mgr.bit_buff, bitmap_idx); + for (; i < bit_cnt; ++i) + setBitNonAtomic(bit_start + i, addr); +} + +static inline void +ps3_r1x_bitmap_clean(struct ps3_r1x_hash_bit_mgr *pHashbitMgr, + unsigned short bitmap_idx, unsigned int bit_start, + unsigned int bit_cnt) +{ + unsigned int i = 0; + unsigned long *addr = (unsigned long *)(void *)&RW_R1X_GET_BITMAP( + pHashbitMgr->bitmap_mgr.bit_buff, bitmap_idx); + for (; i < bit_cnt; ++i) + clearBitNonAtomic(bit_start + i, addr); +} + +static inline int ps3_r1x_bitmap_check(struct ps3_r1x_hash_bit_mgr *pHashbitMgr, + unsigned short bitmap_idx, + unsigned int bit_start, + unsigned int bit_cnt) +{ + unsigned int i = 0; + unsigned long *addr = (unsigned long *)(void *)&RW_R1X_GET_BITMAP( + pHashbitMgr->bitmap_mgr.bit_buff, bitmap_idx); + for (; i < bit_cnt; ++i) { + if (unlikely(testBitNonAtomic(bit_start + i, addr))) + return -PS3_R1X_IO_CONFLICT; + } + return PS3_SUCCESS; +} + +static inline unsigned int calc_block_num(unsigned long long lba, + unsigned int len, + unsigned int block_shift, + unsigned int block_mask) +{ + unsigned long long start_align = lba; + unsigned int block_num = 0; + unsigned int remain_len = 0; + + if (lba & block_mask) { + block_num += 1; + start_align = ((lba >> block_shift) + 1) << block_shift; + } + + if (start_align < lba + len) { + remain_len = len - ((unsigned int)(start_align - lba)); + block_num += remain_len >> block_shift; + if (remain_len & block_mask) + block_num += 1; + } + + return block_num; +} + +struct ps3_write_r1x_hash_tmp { + struct ps3_r1x_hash_bit_item *hash_item; + unsigned int bit_start; + unsigned int bit_cnt; + unsigned char is_new; + unsigned char resv[7]; +}; + +int ps3_r1x_hash_bit_check(struct ps3_r1x_hash_bit_mgr *hash_mgr, + unsigned long long lba, unsigned int len, + struct ps3_write_r1x_hash_tmp *hash_tmp, + unsigned short hash_idx) +{ + unsigned long long sz_block_idx = 0; + unsigned int offset = 0; + struct ps3_r1x_hash_bit_item *hash_item = NULL; + int ret = PS3_SUCCESS; + unsigned int bit_start = 0; + unsigned int bit_cnt = 0; + + hash_item = &hash_mgr->hash_item[hash_idx]; + sz_block_idx = RW_R1X_SZBLOCKIDX(lba); + offset = (unsigned int)(lba & SZBLOCK_SIZE_MASK); + bit_start = offset >> LOCK_BLOCK_SIZE_SHIFT; + bit_cnt = calc_block_num(lba, len, LOCK_BLOCK_SIZE_SHIFT, + LOCK_BLOCK_SIZE_MASK); + hash_tmp->hash_item = hash_item; + hash_tmp->is_new = PS3_FALSE; + hash_tmp->bit_start = bit_start; + hash_tmp->bit_cnt = bit_cnt; + + PS3_R1X_LOCK_TRACE( + "lba=%llu, len=%u, hash_idx=%u, hash item=%p, bit check.", lba, + len, hash_idx, hash_item); + if (hash_item->sz_block_idx == RW_SZBLOCK_IDX_INVALID) { + hash_item->sz_block_idx = sz_block_idx; + hash_tmp->is_new = PS3_TRUE; + PS3_R1X_LOCK_TRACE( + "lba=%llu, len=%u, hash_idx=%u, bit_idx=%u, hash item=%p, new hash item.", + lba, len, hash_idx, + (unsigned short)hash_item->bitmap_idx, hash_item); + } else { + if (hash_item->sz_block_idx == sz_block_idx) { + ret = ps3_r1x_bitmap_check( + hash_mgr, (unsigned short)hash_item->bitmap_idx, + bit_start, bit_cnt); + } else { + PS3_R1X_LOCK_TRACE( + "hash item=%p, oldSzBlock=%llu, newSzBlock=%llu, hash conflict.", + hash_item, + (unsigned long long)hash_item->sz_block_idx, + sz_block_idx); + ret = -PS3_R1X_HASH_CONFILICT; + } + } + + return ret; +} + +static unsigned char +ps3_r1x_hash_bit_check_conflict_queue(struct ps3_r1x_lock_mgr *mgr, + unsigned long long lba, unsigned int len, + unsigned int left) +{ + unsigned char is_conflict = PS3_TRUE; + int ret = PS3_SUCCESS; + unsigned int i = 0; + unsigned char item_cnt = 0; + + struct ps3_write_r1x_hash_tmp *hash_tmp = NULL; + struct ps3_write_r1x_hash_tmp tmp_array[SZ_BLOCK_SPLIT_MAX]; + struct ps3_r1x_hash_bit_mgr *hash_mgr = + (struct ps3_r1x_hash_bit_mgr *)mgr->hash_mgr_conflict; + + LOG_DEBUG("lba=%llu, len=%u, left=%u,\n" + "\tcheck if conflict with conflict queue.\n", + lba, len, left); + + do { + len = PS3_MIN(len, left); + ret = ps3_r1x_hash_bit_check(hash_mgr, lba, len, + &tmp_array[item_cnt], + RW_R1X_HASHIDX(lba)); + item_cnt += 1; + if (ret != PS3_SUCCESS) { + is_conflict = PS3_TRUE; + goto exit_fail; + } + left -= len; + lba += len; + len = SZBLOCK_SIZE; + } while (left > 0); + + is_conflict = PS3_FALSE; + +exit_fail: + + for (i = 0; i < item_cnt; ++i) { + hash_tmp = &tmp_array[i]; + if (hash_tmp->is_new) { + hash_tmp->hash_item->sz_block_idx = + RW_SZBLOCK_IDX_INVALID; + } + } + + return is_conflict; +} + +void ps3_r1x_conflict_queue_hash_bit_lock(struct ps3_r1x_lock_mgr *mgr, + struct ps3_cmd *cmd) +{ + unsigned int i = 0; + unsigned char item_cnt = 0; + unsigned int shift = 0; + unsigned long long lba = 0; + unsigned int len = 0; + unsigned int left = 0; + + struct ps3_r1x_hash_bit_mgr *hash_mgr_conflict = + (struct ps3_r1x_hash_bit_mgr *)mgr->hash_mgr_conflict; + struct ps3_write_r1x_hash_tmp *hash_tmp = NULL; + struct ps3_write_r1x_hash_tmp tmp_array[SZ_BLOCK_SPLIT_MAX]; + + shift = cmd->io_attr.vd_entry->sectorSize != SECTOR_SIZE_4K ? + 0 : + SECTOR_512_4K_SHIFT; + lba = PS3_LBA(cmd->io_attr.lba_hi, cmd->io_attr.lba_lo) << shift; + left = cmd->io_attr.num_blocks << shift; + len = SZBLOCK_SIZE - (lba & SZBLOCK_SIZE_MASK); + + do { + len = PS3_MIN(len, left); + + hash_tmp = &tmp_array[item_cnt]; + hash_tmp->hash_item = + &hash_mgr_conflict->hash_item[RW_R1X_HASHIDX(lba)]; + hash_tmp->hash_item->sz_block_idx = RW_R1X_SZBLOCKIDX(lba); + hash_tmp->is_new = PS3_TRUE; + hash_tmp->bit_start = + ((unsigned int)(lba & SZBLOCK_SIZE_MASK)) >> + LOCK_BLOCK_SIZE_SHIFT; + hash_tmp->bit_cnt = calc_block_num( + lba, len, LOCK_BLOCK_SIZE_SHIFT, LOCK_BLOCK_SIZE_MASK); + + item_cnt += 1; + left -= len; + lba += len; + len = SZBLOCK_SIZE; + } while (left > 0); + + for (i = 0; i < item_cnt; ++i) { + hash_tmp = &tmp_array[i]; + ps3_r1x_bitmap_set( + hash_mgr_conflict, + (unsigned short)hash_tmp->hash_item->bitmap_idx, + hash_tmp->bit_start, hash_tmp->bit_cnt); + } + +} + +int ps3_r1x_hash_bit_lock(struct ps3_r1x_lock_mgr *mgr, struct ps3_cmd *cmd) +{ + int ret = PS3_SUCCESS; + unsigned int i = 0; + unsigned char item_cnt = 0; + unsigned char item_cnt_conflict = 0; + unsigned int shift = 0; + unsigned long long lba = 0; + unsigned int len = 0; + unsigned int left = 0; + struct ps3_r1x_hash_bit_mgr *hash_mgr = + (struct ps3_r1x_hash_bit_mgr *)mgr->hash_mgr; + struct ps3_r1x_hash_bit_mgr *hash_mgr_conflict = + (struct ps3_r1x_hash_bit_mgr *)mgr->hash_mgr_conflict; + struct ps3_write_r1x_hash_tmp *hash_tmp = NULL; + struct ps3_write_r1x_hash_tmp tmp_array[SZ_BLOCK_SPLIT_MAX]; + struct ps3_write_r1x_hash_tmp tmp_array_conflict[SZ_BLOCK_SPLIT_MAX]; + unsigned long flag = 0; + unsigned char is_conflict_q_empty = PS3_FALSE; + + LOG_DEBUG( + "t_id:0x%llx lba_high=%u, lba_low=%u, len=%u, cmd=%p, try lock.\n", + cmd->trace_id, cmd->io_attr.lba_hi, cmd->io_attr.lba_lo, + cmd->io_attr.num_blocks, cmd); + + shift = cmd->io_attr.vd_entry->sectorSize != SECTOR_SIZE_4K ? + 0 : + SECTOR_512_4K_SHIFT; + lba = PS3_LBA(cmd->io_attr.lba_hi, cmd->io_attr.lba_lo) << shift; + len = cmd->io_attr.num_blocks << shift; + left = len; + + ps3_spin_lock_irqsave(&mgr->mgr_lock, &flag); + + if (mgr->dev_deling) { + ps3_spin_unlock_irqrestore(&mgr->mgr_lock, flag); + ret = PS3_IO_CONFLICT; + goto l_out; + } + + is_conflict_q_empty = list_empty(&mgr->conflict_cmd_list); + + + len = SZBLOCK_SIZE - (lba & SZBLOCK_SIZE_MASK); + if (is_conflict_q_empty) { + do { + PS3_BUG_ON(item_cnt >= SZ_BLOCK_SPLIT_MAX); + len = PS3_MIN(len, left); + ret = ps3_r1x_hash_bit_check(hash_mgr, lba, len, + &tmp_array[item_cnt], + RW_R1X_HASHIDX(lba)); + item_cnt += 1; + if (ret != PS3_SUCCESS) + goto exit_fail; + left -= len; + lba += len; + len = SZBLOCK_SIZE; + } while (left > 0); + } else { + do { + PS3_BUG_ON(item_cnt >= SZ_BLOCK_SPLIT_MAX); + len = PS3_MIN(len, left); + ret = ps3_r1x_hash_bit_check(hash_mgr, lba, len, + &tmp_array[item_cnt], + RW_R1X_HASHIDX(lba)); + item_cnt += 1; + if (ret != PS3_SUCCESS) + goto exit_fail; + ret = ps3_r1x_hash_bit_check( + hash_mgr_conflict, lba, len, + &tmp_array_conflict[item_cnt_conflict], + RW_R1X_HASHIDX(lba)); + item_cnt_conflict += 1; + if (ret != PS3_SUCCESS) + goto exit_fail; + left -= len; + lba += len; + len = SZBLOCK_SIZE; + } while (left > 0); + + for (i = 0; i < item_cnt_conflict; ++i) { + hash_tmp = &tmp_array_conflict[i]; + if (hash_tmp->is_new == PS3_TRUE) { + hash_tmp->hash_item->sz_block_idx = + RW_SZBLOCK_IDX_INVALID; + } + } + } + + for (i = 0; i < item_cnt; ++i) { + hash_tmp = &tmp_array[i]; + ps3_r1x_bitmap_set( + hash_mgr, + (unsigned short)hash_tmp->hash_item->bitmap_idx, + hash_tmp->bit_start, hash_tmp->bit_cnt); + } + + ps3_spin_unlock_irqrestore(&mgr->mgr_lock, flag); + + cmd->szblock_cnt = item_cnt; + cmd->is_got_r1x = 1; + + LOG_DEBUG( + "t_id:0x%llx lba_high=%u, lba_low=%u, len=%u, lock success.\n", + cmd->trace_id, cmd->io_attr.lba_hi, cmd->io_attr.lba_lo, + cmd->io_attr.num_blocks); + + ret = PS3_SUCCESS; + goto l_out; +exit_fail: + + LOG_DEBUG( + "t_id:0x%llx lba_high=%u, lba_low=%u, len=%u, lock failed, ret=%d\n" + "\t(%d: io conflict, %d: hash conflict, %d: bitmap not enough).\n", + cmd->trace_id, cmd->io_attr.lba_hi, cmd->io_attr.lba_lo, + cmd->io_attr.num_blocks, ret, -PS3_R1X_IO_CONFLICT, + -PS3_R1X_HASH_CONFILICT, -PS3_R1X_BIT_NOT_ENOUGH); + + cmd->szblock_cnt = 0; + for (i = 0; i < item_cnt; ++i) { + hash_tmp = &tmp_array[i]; + if (hash_tmp->is_new == PS3_TRUE) { + hash_tmp->hash_item->sz_block_idx = + RW_SZBLOCK_IDX_INVALID; + } + } + + for (i = 0; i < item_cnt_conflict; ++i) { + hash_tmp = &tmp_array_conflict[i]; + if (hash_tmp->is_new == PS3_TRUE) { + hash_tmp->hash_item->sz_block_idx = + RW_SZBLOCK_IDX_INVALID; + } + } + + if (ps3_r1x_conflict_queue_support_query()) { + if (PS3_R1X_TIMEOUT(cmd, ps3_r1x_tmo_query())) { + list_add_tail(&cmd->cmd_list, &mgr->conflict_cmd_list); + + ps3_r1x_conflict_queue_hash_bit_lock(mgr, cmd); + cmd->is_inserted_c_q = 1; + mgr->cmd_count_in_q++; + + ret = -PS3_IO_CONFLICT_IN_Q; + } else { + if (is_conflict_q_empty) + ret = -PS3_IO_REQUEUE; + else + ret = -PS3_IO_CONFLICT; + } + } else { + ret = -PS3_IO_CONFLICT; + } + + ps3_spin_unlock_irqrestore(&mgr->mgr_lock, flag); + + LOG_DEBUG( + "t_id:0x%llx lba_high=%u, lba_low=%u, len=%u, lock failed, fanal ret=%d\n" + "\t(%d: conflict requeue, %d: conflict in queue, %d: conflict busy).\n", + cmd->trace_id, cmd->io_attr.lba_hi, cmd->io_attr.lba_lo, + cmd->io_attr.num_blocks, ret, -PS3_IO_REQUEUE, + -PS3_IO_CONFLICT_IN_Q, -PS3_IO_CONFLICT); +l_out: + + return ret; +} + +static int ps3_r1x_hash_bit_lock_of_conflict_queue(struct ps3_r1x_lock_mgr *mgr, + struct ps3_cmd *cmd) +{ + int ret = PS3_SUCCESS; + unsigned int i = 0; + unsigned char item_cnt = 0; + unsigned int shift = 0; + unsigned long long lba = 0; + unsigned int len = 0; + unsigned int left = 0; + struct ps3_r1x_hash_bit_mgr *hash_mgr = + (struct ps3_r1x_hash_bit_mgr *)mgr->hash_mgr; + struct ps3_write_r1x_hash_tmp *hash_tmp = NULL; + struct ps3_write_r1x_hash_tmp tmp_array[SZ_BLOCK_SPLIT_MAX]; + unsigned long flag = 0; + + LOG_DEBUG( + "t_id:0x%llx lba_high=%u, lba_low=%u, len=%u, cmd=%p, try lock.\n", + cmd->trace_id, cmd->io_attr.lba_hi, cmd->io_attr.lba_lo, + cmd->io_attr.num_blocks, cmd); + + shift = cmd->io_attr.vd_entry->sectorSize != SECTOR_SIZE_4K ? + 0 : + SECTOR_512_4K_SHIFT; + ; + lba = PS3_LBA(cmd->io_attr.lba_hi, cmd->io_attr.lba_lo) << shift; + ; + len = cmd->io_attr.num_blocks << shift; + left = len; + + ps3_spin_lock_irqsave(&mgr->mgr_lock, &flag); + + len = SZBLOCK_SIZE - (lba & SZBLOCK_SIZE_MASK); + do { + PS3_BUG_ON(item_cnt >= SZ_BLOCK_SPLIT_MAX); + len = PS3_MIN(len, left); + ret = ps3_r1x_hash_bit_check(hash_mgr, lba, len, + &tmp_array[item_cnt], + RW_R1X_HASHIDX(lba)); + item_cnt += 1; + if (ret != PS3_SUCCESS) + goto exit_fail; + left -= len; + lba += len; + len = SZBLOCK_SIZE; + } while (left > 0); + + for (i = 0; i < item_cnt; ++i) { + hash_tmp = &tmp_array[i]; + ps3_r1x_bitmap_set( + hash_mgr, + (unsigned short)hash_tmp->hash_item->bitmap_idx, + hash_tmp->bit_start, hash_tmp->bit_cnt); + } + + ps3_spin_unlock_irqrestore(&mgr->mgr_lock, flag); + + cmd->szblock_cnt = item_cnt; + cmd->is_got_r1x = 1; + + LOG_DEBUG( + "t_id:0x%llx lba_high=%u, lba_low=%u, len=%u, lock success.\n", + cmd->trace_id, cmd->io_attr.lba_hi, cmd->io_attr.lba_lo, + cmd->io_attr.num_blocks); + + return PS3_SUCCESS; + +exit_fail: + + for (i = 0; i < item_cnt; ++i) { + hash_tmp = &tmp_array[i]; + if (hash_tmp->is_new == PS3_TRUE) { + hash_tmp->hash_item->sz_block_idx = + RW_SZBLOCK_IDX_INVALID; + } + } + + ps3_spin_unlock_irqrestore(&mgr->mgr_lock, flag); + + cmd->szblock_cnt = 0; + ret = -PS3_IO_CONFLICT_IN_Q; + + LOG_DEBUG( + "t_id:0x%llx lba_high=%u, lba_low=%u, len=%u, relock failed, ret=%d\n" + "\t(%d: io conflict, %d: hash conflict, %d: bitmap not enough).\n", + cmd->trace_id, cmd->io_attr.lba_hi, cmd->io_attr.lba_lo, + cmd->io_attr.num_blocks, ret, -PS3_R1X_IO_CONFLICT, + -PS3_R1X_HASH_CONFILICT, -PS3_R1X_BIT_NOT_ENOUGH); + return ret; +} + +void ps3_r1x_hash_bit_unlock(struct ps3_r1x_lock_mgr *mgr, struct ps3_cmd *cmd) +{ + struct ps3_r1x_hash_bit_item *hash_item = NULL; + unsigned int bit_start = 0; + unsigned int bit_cnt = 0; + unsigned long long *bit_map = NULL; + unsigned int i = 0; + int is_clean = PS3_TRUE; + unsigned int shift = 0; + unsigned long long lba = 0; + unsigned int len = 0; + unsigned int left = 0; + unsigned long long conflict_check_lba = 0; + unsigned int conflict_check_len = 0; + unsigned int conflict_check_left = 0; + struct ps3_r1x_hash_bit_mgr *hash_mgr = + (struct ps3_r1x_hash_bit_mgr *)mgr->hash_mgr; + struct ps3_r1x_bit_block_mgr *bit_map_mgr = &hash_mgr->bitmap_mgr; + unsigned long flag = 0; + unsigned char ret_check = PS3_CONFLICT_QUEUE_EMPTY; + + PS3_R1X_LOCK_TRACE( + "t_id:0x%llx lba_high=%u, lba_low=%u, len=%u, cmd=%p, unlock.", + cmd->trace_id, cmd->io_attr.lba_hi, cmd->io_attr.lba_lo, + cmd->io_attr.num_blocks, cmd); + + shift = cmd->io_attr.vd_entry->sectorSize != SECTOR_SIZE_4K ? + 0 : + SECTOR_512_4K_SHIFT; + lba = PS3_LBA(cmd->io_attr.lba_hi, cmd->io_attr.lba_lo) << shift; + left = cmd->io_attr.num_blocks << shift; + len = SZBLOCK_SIZE - (lba & SZBLOCK_SIZE_MASK); + + conflict_check_lba = lba; + conflict_check_len = len; + conflict_check_left = left; + + ps3_spin_lock_irqsave(&mgr->mgr_lock, &flag); + do { + hash_item = &hash_mgr->hash_item[RW_R1X_HASHIDX(lba)]; + len = PS3_MIN(len, left); + bit_start = (lba & SZBLOCK_SIZE_MASK) >> LOCK_BLOCK_SIZE_SHIFT; + bit_cnt = calc_block_num(lba, len, LOCK_BLOCK_SIZE_SHIFT, + LOCK_BLOCK_SIZE_MASK); + ps3_r1x_bitmap_clean(hash_mgr, + (unsigned short)hash_item->bitmap_idx, + bit_start, bit_cnt); + + is_clean = PS3_TRUE; + bit_map = (unsigned long long *)(void *)&RW_R1X_GET_BITMAP( + bit_map_mgr->bit_buff, hash_item->bitmap_idx); + for (i = 0; i < bit_map_mgr->bit_block_size; ++i) { + if (bit_map[i] != 0) + is_clean = PS3_FALSE; + } + + if (is_clean == PS3_TRUE) { + PS3_R1X_LOCK_TRACE( + "t_id:0x%llx lba=%llu, len=%u, szBlockIdx=%llu, bitIdx=%lu,\n" + "\tszblock_cnt=%u, cmd=%p, release hash item.", + cmd->trace_id, lba, len, + (unsigned long long)hash_item->sz_block_idx, + (unsigned long)hash_item->bitmap_idx, + cmd->szblock_cnt, cmd); + hash_item->sz_block_idx = RW_SZBLOCK_IDX_INVALID; + } + + left -= len; + lba += len; + len = SZBLOCK_SIZE; + } while (left > 0); + + if (!list_empty(&mgr->conflict_cmd_list)) { + if (ps3_r1x_hash_bit_check_conflict_queue( + mgr, conflict_check_lba, conflict_check_len, + conflict_check_left)) { + ret_check = PS3_CONFLICT_QUEUE_CONFLICT; + } else { + ret_check = PS3_CONFLICT_QUEUE_NO_CONFLICT; + } + } + + ps3_spin_unlock_irqrestore(&mgr->mgr_lock, flag); + + if (ret_check != PS3_CONFLICT_QUEUE_EMPTY) { + LOG_FILE_INFO("t_id:0x%llx lba_high=%u, lba_low=%u, len=%u,\n" + "\tconflict queue check ret:%d.\n", + cmd->trace_id, cmd->io_attr.lba_hi, + cmd->io_attr.lba_lo, cmd->io_attr.num_blocks, + ret_check); + + if (ret_check == PS3_CONFLICT_QUEUE_CONFLICT) + complete(&mgr->thread_sync); + } + +} + +static void *ps3_r1x_lock_hash_bit_init(struct ps3_instance *instance) +{ + unsigned int bit_map_size = 0; + unsigned short sz_block_bit_cnt = 0; + unsigned short bitmap_block_size = 0; + unsigned short bitmap_block_cnt = 0; + unsigned int total_size = 0; + struct ps3_r1x_hash_bit_mgr *mgr = NULL; + unsigned char *buff = NULL; + unsigned int i = 0; + (void)instance; + (void)bitmap_block_cnt; + + if (SZBLOCK_SIZE & LOCK_BLOCK_SIZE_MASK) { + LOG_FILE_ERROR("hno: %u, SZBLOCK_SIZE check NOK\n", + PS3_HOST(instance)); + goto exit; + } + bit_map_size = PS3_R1X_BIT_MAP_SIZE; + sz_block_bit_cnt = SZBLOCK_SIZE >> LOCK_BLOCK_SIZE_SHIFT; + if (sz_block_bit_cnt & RW_BIT_64_MASK) { + LOG_FILE_ERROR("hno: %u, sz_block_bit_cnt check NOK\n", + PS3_HOST(instance)); + goto exit; + } + total_size = sizeof(struct ps3_r1x_hash_bit_mgr) + bit_map_size; + buff = (unsigned char *)(void *)ps3_vzalloc(instance, total_size); + if (buff == NULL) { + PS3_R1X_LOCK_ERROR( + "r1x vd alloc write lock mgr failed. totoalSize=%u", + total_size); + goto exit; + } + + bitmap_block_size = RW_BIT_TO_BYTE(sz_block_bit_cnt); + mgr = (struct ps3_r1x_hash_bit_mgr *)(void *)buff; + for (i = 0; i < HASH_TABLE_SIZE; ++i) { + mgr->hash_item[i].sz_block_idx = RW_SZBLOCK_IDX_INVALID; + if (RW_BITMAP_IDX_INVALID < (i * bitmap_block_size)) { + LOG_ERROR("hno: %u, bitmap_block_size check NOK\n", + PS3_HOST(instance)); + PS3_BUG(); + ps3_vfree(instance, buff); + mgr = NULL; + goto exit; + } + mgr->hash_item[i].bitmap_idx = i * bitmap_block_size; + } + mgr->bitmap_mgr.bit_block_size = + (unsigned char)RW_BYTE_TO_U64(bitmap_block_size); + buff += sizeof(struct ps3_r1x_hash_bit_mgr); + mgr->bitmap_mgr.bit_buff = (unsigned char *)(void *)buff; + buff += bit_map_size; + + LOG_FILE_INFO( + "r1x vd init write lock mgr success,totoalSize=%u, hashTableSize=%u,\n" + "\tbitmapSize=%u(bytes), bitmap_block_cnt=%u, bitmap_block_size=%u.\n", + total_size, HASH_TABLE_SIZE, bit_map_size, bitmap_block_cnt, + bitmap_block_size); + +exit: + return mgr; +} +#endif + +#if PS3_DESC("-----------------------hash range互斥方案------------------------") +struct ps3_renge_extent { + unsigned long long start; + unsigned long long end; +}; + +struct ps3_range_tree_node { + struct Ps3RbNode rbNode; + struct ps3_renge_extent extent; +}; + +struct ps3_range_tree_root { + struct Ps3RbRoot rbRoot; +}; + +struct ps3_r1x_hash_range_mgr { + struct ps3_range_tree_root hash_item[HASH_TABLE_SIZE]; +}; + +#define RANGETEE_EXTENT(rbNodePtr) \ + ((ps3_container_of(rbNodePtr, struct ps3_range_tree_node, rbNode)) \ + ->extent) + +int ps3_range_check_and_insert(struct ps3_range_tree_root *range_root, + struct ps3_range_tree_node *range_node) +{ + int ret = PS3_SUCCESS; + struct Ps3RbNode *node = &range_node->rbNode; + struct Ps3RbNode *parent = NULL; + struct Ps3RbNode **pp_linker = NULL; + struct ps3_renge_extent *ext = &range_node->extent; + + PS3_R1X_LOCK_TRACE("root=%p, rbNode=%p, lba=%llu, endLba=%llu, check.", + range_root->rbRoot.pRoot, node, ext->start, + ext->end); + + pp_linker = &range_root->rbRoot.pRoot; + while (*pp_linker != NULL) { + parent = *pp_linker; + if (ext->start > RANGETEE_EXTENT(parent).end) { + pp_linker = &parent->pRight; + } else if (ext->end < RANGETEE_EXTENT(parent).start) { + pp_linker = &parent->pLeft; + } else { + ret = -PS3_R1X_IO_CONFLICT; + PS3_R1X_LOCK_TRACE( + "root=%p, rbNode=%p, lba=%llu, endLba=%llu, confilct.", + range_root->rbRoot.pRoot, node, ext->start, + ext->end); + goto exit; + } + } + + node->pParentColor = ((uintptr_t)(void *)parent); + node->pLeft = NULL; + node->pRight = NULL; + (*pp_linker) = node; + ps3RbtColorAfterAdd(&range_root->rbRoot, node); + PS3_R1X_LOCK_TRACE("root=%p, rbNode=%p, lba=%llu, endLba=%llu, add.", + range_root->rbRoot.pRoot, node, ext->start, + ext->end); + +exit: + return ret; +} + +static inline void ps3_range_del_node(struct ps3_range_tree_root *range_root, + struct ps3_range_tree_node *del_node) +{ + PS3_R1X_LOCK_TRACE("root=%p, rbNode=%p, lba=%llu, endLba=%llu, del.", + range_root->rbRoot.pRoot, &del_node->rbNode, + del_node->extent.start, del_node->extent.end); + if (unlikely(PS3_SUCCESS != + ps3RbtDelNode(&range_root->rbRoot, &del_node->rbNode))) { + LOG_FILE_ERROR( + "startLba=%llu, endLba=%llu, del range node failed.\n", + del_node->extent.start, del_node->extent.end); + PS3_BUG(); + } +} + +int ps3_r1x_hash_range_lock(struct ps3_r1x_lock_mgr *mgr, struct ps3_cmd *cmd) +{ + int ret = PS3_SUCCESS; + unsigned long long hash_idx = 0; + unsigned char node_num = 0; + unsigned int shift = 0; + unsigned long long lba = 0; + unsigned int len = 0; + unsigned int left = 0; + struct ps3_r1x_hash_range_mgr *hash_range_mgr = + (struct ps3_r1x_hash_range_mgr *)mgr->hash_mgr; + struct ps3_range_tree_node *range_node = + (struct ps3_range_tree_node *)cmd->node_buff; + struct ps3_range_tree_node *del_node = NULL; + unsigned long flag = 0; + + PS3_R1X_LOCK_TRACE( + "t_id:0x%llx lba_high=%u, lba_low=%u, len=%u, try lock.", + cmd->trace_id, cmd->io_attr.lba_hi, cmd->io_attr.lba_lo, + cmd->io_attr.num_blocks); + + shift = cmd->io_attr.vd_entry->sectorSize != SECTOR_SIZE_4K ? + 0 : + SECTOR_512_4K_SHIFT; + ; + lba = PS3_LBA(cmd->io_attr.lba_hi, cmd->io_attr.lba_lo) << shift; + ; + len = cmd->io_attr.num_blocks << shift; + left = len; + + ps3_spin_lock_irqsave(&mgr->mgr_lock, &flag); + + len = SZBLOCK_SIZE - (lba & SZBLOCK_SIZE_MASK); + do { + range_node = &( + (struct ps3_range_tree_node *)cmd->node_buff)[node_num]; + len = PS3_MIN(len, left); + range_node->extent.start = lba; + range_node->extent.end = lba + len - 1; + ps3RbNodeInit(&range_node->rbNode); + hash_idx = RW_R1X_HASHIDX_HASH(mgr, lba); + + ret = ps3_range_check_and_insert( + &hash_range_mgr->hash_item[hash_idx], range_node); + if (unlikely(ret != PS3_SUCCESS)) + goto exit_fail; + + left -= len; + lba += len; + len = SZBLOCK_SIZE; + node_num++; + } while (left > 0); + + ps3_spin_unlock_irqrestore(&mgr->mgr_lock, flag); + cmd->szblock_cnt = node_num; + cmd->is_got_r1x = 1; + + PS3_R1X_LOCK_TRACE( + "t_id:0x%llx lba_high=%u, lba_low=%u, len=%u, lock success.", + cmd->trace_id, cmd->io_attr.lba_hi, cmd->io_attr.lba_lo, + cmd->io_attr.num_blocks); + return ret; + +exit_fail: + cmd->szblock_cnt = 0; + del_node = (struct ps3_range_tree_node *)cmd->node_buff; + for (; del_node != range_node; del_node++) { + hash_idx = RW_R1X_HASHIDX_HASH(mgr, del_node->extent.start); + ps3_range_del_node(&hash_range_mgr->hash_item[hash_idx], + del_node); + } + ps3_spin_unlock_irqrestore(&mgr->mgr_lock, flag); + + LOG_DEBUG( + "t_id:0x%llx lba_high=%u, lba_low=%u, len=%u, io conflict, lock failed.\n", + cmd->trace_id, cmd->io_attr.lba_hi, cmd->io_attr.lba_lo, + cmd->io_attr.num_blocks); + ret = -PS3_IO_REQUEUE; + return ret; +} + +void ps3_r1x_hash_range_unlock(struct ps3_r1x_lock_mgr *mgr, + struct ps3_cmd *cmd) +{ + unsigned int i = 0; + unsigned long long hash_idx = 0; + struct ps3_range_tree_node *range_node = NULL; + struct ps3_r1x_hash_range_mgr *hash_range_mgr = + (struct ps3_r1x_hash_range_mgr *)mgr->hash_mgr; + unsigned long flag = 0; + + PS3_R1X_LOCK_TRACE( + "t_id:0x%llx lba_high=%u, lba_low=%u, len=%u, unlock.", + cmd->trace_id, cmd->io_attr.lba_hi, cmd->io_attr.lba_lo, + cmd->io_attr.num_blocks); + + ps3_spin_lock_irqsave(&mgr->mgr_lock, &flag); + + for (i = 0; i < cmd->szblock_cnt; ++i) { + range_node = &((struct ps3_range_tree_node *)cmd->node_buff)[i]; + hash_idx = RW_R1X_HASHIDX_HASH(mgr, range_node->extent.start); + ps3_range_del_node(&hash_range_mgr->hash_item[hash_idx], + range_node); + } + ps3_spin_unlock_irqrestore(&mgr->mgr_lock, flag); + +} + +static void *ps3_r1x_hash_range_init(struct ps3_instance *instance) +{ + unsigned int total_size = 0; + unsigned int i = 0; + struct ps3_r1x_hash_range_mgr *mgr = NULL; + + total_size = sizeof(struct ps3_r1x_hash_range_mgr); + mgr = (struct ps3_r1x_hash_range_mgr *)(void *)ps3_vzalloc(instance, + total_size); + if (mgr == NULL) { + PS3_R1X_LOCK_ERROR( + "r1x vd write lock alloc hash range mgr failed. totoalSize=%u", + total_size); + goto exit; + } + + for (i = 0; i < HASH_TABLE_SIZE; ++i) + mgr->hash_item[i].rbRoot.pRoot = NULL; + + LOG_FILE_INFO("r1x vd write lock init hash range mgr success.\n" + "\ttotoalSize=%u, hashTableSize=%u\n", + total_size, HASH_TABLE_SIZE); +exit: + return mgr; +} + +#endif + +#if PS3_DESC("-----------------------对外接口------------------------") + +unsigned int ps3_r1x_get_node_Buff_size(void) +{ + return sizeof(struct ps3_range_tree_node) * SZ_BLOCK_SPLIT_MAX; +} + +int ps3_r1x_lock_prepare_for_vd(struct ps3_instance *instance, + struct scsi_device *sdev, + unsigned char raid_level) +{ + int ret = PS3_SUCCESS; + struct ps3_r1x_lock_mgr *mgr = &PS3_SDEV_PRI_DATA(sdev)->lock_mgr; + + mgr->hash_mgr = NULL; + mgr->try_lock = NULL; + mgr->unlock = NULL; + + if (raid_level != RAID1 && raid_level != RAID1E && + raid_level != RAID10) { + goto exit; + } + + ps3_spin_lock_init(&mgr->mgr_lock); + + if (g_ps3_r1x_lock_flag == PS3_R1X_HASHBIT_LOCK) { + mgr->hash_mgr = ps3_r1x_lock_hash_bit_init(instance); + mgr->try_lock = (int (*)(struct ps3_r1x_lock_mgr *, void *))( + void *)ps3_r1x_hash_bit_lock; + mgr->resend_try_lock = + (int (*)(struct ps3_r1x_lock_mgr *, void *))( + void *)ps3_r1x_hash_bit_lock_of_conflict_queue; + mgr->unlock = (void (*)(struct ps3_r1x_lock_mgr *, void *))( + void *)ps3_r1x_hash_bit_unlock; + } else if (g_ps3_r1x_lock_flag == PS3_R1X_HASHRANGE_LOCK) { + LOG_DEBUG("hno:%u vd[%u:%u] use rb tree r1x\n", + PS3_HOST(instance), PS3_SDEV_CHANNEL(sdev), + PS3_SDEV_TARGET(sdev)); + mgr->hash_mgr = ps3_r1x_hash_range_init(instance); + mgr->try_lock = (int (*)(struct ps3_r1x_lock_mgr *, void *))( + void *)ps3_r1x_hash_range_lock; + mgr->resend_try_lock = + (int (*)(struct ps3_r1x_lock_mgr *, void *))( + void *)ps3_r1x_hash_range_lock; + mgr->unlock = (void (*)(struct ps3_r1x_lock_mgr *, void *))( + void *)ps3_r1x_hash_range_unlock; + } + if (unlikely(mgr->hash_mgr == NULL)) { + LOG_ERROR("hno:%u vd[%u:%u] r1x hash mgr init failed\n", + PS3_HOST(instance), PS3_SDEV_CHANNEL(sdev), + PS3_SDEV_TARGET(sdev)); + goto l_err; + } + + mgr->hash_mgr_conflict = ps3_r1x_lock_hash_bit_init(instance); + if (unlikely(mgr->hash_mgr_conflict == NULL)) { + LOG_ERROR( + "hno:%u vd[%u:%u] r1x hash mgr conflict init failed\n", + PS3_HOST(instance), PS3_SDEV_CHANNEL(sdev), + PS3_SDEV_TARGET(sdev)); + goto l_err; + } + + mgr->thread_stop = PS3_FALSE; + mgr->dev_deling = PS3_FALSE; + mgr->force_ret_code = SCSI_STATUS_GOOD; + + INIT_LIST_HEAD(&mgr->conflict_cmd_list); + mgr->cmd_count_in_q = 0; + init_completion(&mgr->thread_sync); + + mgr->conflict_send_th = + kthread_run(ps3_conflict_queue_send_wk, sdev, "r1x_send"); + if (IS_ERR(mgr->conflict_send_th)) { + LOG_ERROR( + "hno:%u vd[%u:%u] r1x conflict send thread creat failed\n", + PS3_HOST(instance), PS3_SDEV_CHANNEL(sdev), + PS3_SDEV_TARGET(sdev)); + goto l_err; + } + + goto exit; +l_err: + ps3_r1x_lock_destroy_for_vd(instance, mgr); + ret = -PS3_FAILED; +exit: + return ret; +} + +static int ps3_kthread_stop(struct ps3_instance *instance, + struct ps3_r1x_lock_mgr *mgr) +{ + int ret = PS3_SUCCESS; + + (void)instance; + mgr->thread_stop = PS3_TRUE; + complete(&mgr->thread_sync); + + LOG_FILE_INFO("hno:%u r1x conflict destroy, begin stop\n", + PS3_HOST(instance)); + ret = kthread_stop(mgr->conflict_send_th); + LOG_FILE_INFO("hno:%u r1x conflict destroy, stopped\n", + PS3_HOST(instance)); + + return ret; +} + +void ps3_r1x_lock_destroy_for_vd(struct ps3_instance *instance, + struct ps3_r1x_lock_mgr *mgr) +{ + if (mgr->conflict_send_th != NULL) + ps3_kthread_stop(instance, mgr); + + if (mgr->hash_mgr_conflict != NULL) + ps3_vfree(instance, mgr->hash_mgr_conflict); + + if (mgr->hash_mgr != NULL) + ps3_vfree(instance, mgr->hash_mgr); + mgr->hash_mgr = NULL; + mgr->try_lock = NULL; + mgr->unlock = NULL; + LOG_FILE_INFO("r1x vd deinit write lock mgr success."); + +} + +#endif diff --git a/drivers/scsi/linkdata/ps3stor/ps3_r1x_write_lock.h b/drivers/scsi/linkdata/ps3stor/ps3_r1x_write_lock.h new file mode 100644 index 0000000000000000000000000000000000000000..100d5718752682b4313eb24dd943520f43821e24 --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/ps3_r1x_write_lock.h @@ -0,0 +1,62 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __PS3_R1X_WRITE_LOCK_H__ +#define __PS3_R1X_WRITE_LOCK_H__ + +#include "ps3_cmd_channel.h" + +enum { + PS3_R1X_HASHBIT_LOCK = 0, + PS3_R1X_HASHRANGE_LOCK = 1, +}; + +extern unsigned int g_ps3_r1x_lock_flag; +extern unsigned int g_ps3_r1x_lock_enable; + +unsigned int ps3_r1x_get_node_Buff_size(void); + +static inline int ps3_r1x_write_lock(struct ps3_r1x_lock_mgr *mgr, + struct ps3_cmd *cmd) +{ + if (mgr->hash_mgr == NULL) + return PS3_SUCCESS; + + if (g_ps3_r1x_lock_enable == 0) + return PS3_SUCCESS; + + if (!cmd->io_attr.is_confilct_check) + return PS3_SUCCESS; + + if (cmd->is_inserted_c_q == 0) + return mgr->try_lock(mgr, cmd); + else + return mgr->resend_try_lock(mgr, cmd); +} + +static inline void ps3_r1x_write_unlock(struct ps3_r1x_lock_mgr *mgr, + struct ps3_cmd *cmd) +{ + if (cmd->szblock_cnt == 0) + return; + mgr->unlock(mgr, cmd); + cmd->szblock_cnt = 0; +} + +int ps3_r1x_lock_prepare_for_vd(struct ps3_instance *instance, + struct scsi_device *sdev, + unsigned char raid_level); +void ps3_r1x_lock_destroy_for_vd(struct ps3_instance *instance, + struct ps3_r1x_lock_mgr *mgr); + +void ps3_r1x_conflict_queue_clean(struct ps3_scsi_priv_data *pri_data, + int ret_code); + +unsigned char ps3_r1x_conflict_queue_abort(struct ps3_cmd *cmd, + struct scsi_cmnd *scmd); + +void ps3_r1x_conflict_queue_target_reset(struct ps3_instance *instance, + unsigned short target_id); + +void ps3_r1x_conflict_queue_clean_all(struct ps3_instance *instance, + int ret_code, unsigned char is_remove); + +#endif diff --git a/drivers/scsi/linkdata/ps3stor/ps3_rb_tree.c b/drivers/scsi/linkdata/ps3stor/ps3_rb_tree.c new file mode 100644 index 0000000000000000000000000000000000000000..888152b162cb234134c717b96d649b3d553a2375 --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/ps3_rb_tree.c @@ -0,0 +1,588 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "ps3_rb_tree.h" +static void rbtNodeSetParent(struct Ps3RbNode *pNode, struct Ps3RbNode *pParent) +{ + pNode->pParentColor = + ((pNode->pParentColor & 3ULL) | ((uintptr_t)(void *)pParent)); +} + +static void rbtNodeSetColor(struct Ps3RbNode *pNode, unsigned int color) +{ + pNode->pParentColor = ((pNode->pParentColor & ~1ULL) | color); +} + +static void rbtRotateLeft(struct Ps3RbRoot *pRoot, struct Ps3RbNode *pNode) +{ + struct Ps3RbNode *pRight = pNode->pRight; + struct Ps3RbNode *pParent = RBT_PARENT(pNode); + + pNode->pRight = pRight->pLeft; + if (pNode->pRight != NULL) + rbtNodeSetParent(pRight->pLeft, pNode); + + pRight->pLeft = pNode; + rbtNodeSetParent(pRight, pParent); + + if (pParent != NULL) { + if (pNode == pParent->pLeft) + pParent->pLeft = pRight; + else + pParent->pRight = pRight; + } else { + pRoot->pRoot = pRight; + } + + rbtNodeSetParent(pNode, pRight); +} + +static void rbtRotateRight(struct Ps3RbRoot *pRoot, struct Ps3RbNode *pNode) +{ + struct Ps3RbNode *pLeft = pNode->pLeft; + struct Ps3RbNode *pParent = RBT_PARENT(pNode); + + pNode->pLeft = pLeft->pRight; + if (pNode->pLeft != NULL) + rbtNodeSetParent(pLeft->pRight, pNode); + + pLeft->pRight = pNode; + rbtNodeSetParent(pLeft, pParent); + + if (pParent != NULL) { + if (pNode == pParent->pRight) + pParent->pRight = pLeft; + else + pParent->pLeft = pLeft; + } else { + pRoot->pRoot = pLeft; + } + + rbtNodeSetParent(pNode, pLeft); +} + +static void rbtColorAfterDel(struct Ps3RbRoot *pRoot, struct Ps3RbNode *pNode, + struct Ps3RbNode *pParent) +{ + struct Ps3RbNode *pOther = NULL; + struct Ps3RbNode *pOLeft = NULL; + struct Ps3RbNode *pORight = NULL; + + while (((pNode == NULL) || RBT_IS_BLACK(pNode)) && + (pNode != pRoot->pRoot)) { + if (pParent->pLeft == pNode) { + pOther = pParent->pRight; + if (RBT_IS_RED(pOther)) { + RBT_SET_BLACK(pOther); + RBT_SET_RED(pParent); + rbtRotateLeft(pRoot, pParent); + pOther = pParent->pRight; + } + if (((pOther->pLeft == NULL) || + RBT_IS_BLACK(pOther->pLeft)) && + ((pOther->pRight == NULL) || + RBT_IS_BLACK(pOther->pRight))) { + RBT_SET_RED(pOther); + pNode = pParent; + pParent = RBT_PARENT(pNode); + + continue; + } + if ((pOther->pRight == NULL) || + RBT_IS_BLACK(pOther->pRight)) { + pOLeft = pOther->pLeft; + if (pOLeft != NULL) + RBT_SET_BLACK(pOLeft); + + RBT_SET_RED(pOther); + rbtRotateRight(pRoot, pOther); + pOther = pParent->pRight; + } + + rbtNodeSetColor(pOther, RBT_COLOR(pParent)); + RBT_SET_BLACK(pParent); + + if (pOther->pRight != NULL) + RBT_SET_BLACK(pOther->pRight); + + rbtRotateLeft(pRoot, pParent); + pNode = pRoot->pRoot; + + break; + } + pOther = pParent->pLeft; + if (RBT_IS_RED(pOther)) { + RBT_SET_BLACK(pOther); + RBT_SET_RED(pParent); + + rbtRotateRight(pRoot, pParent); + pOther = pParent->pLeft; + } + if (((pOther->pLeft == NULL) || RBT_IS_BLACK(pOther->pLeft)) && + ((pOther->pRight == NULL) || + RBT_IS_BLACK(pOther->pRight))) { + RBT_SET_RED(pOther); + pNode = pParent; + pParent = RBT_PARENT(pNode); + + continue; + } + if ((pOther->pLeft == NULL) || RBT_IS_BLACK(pOther->pLeft)) { + pORight = pOther->pRight; + if (pORight != NULL) + RBT_SET_BLACK(pORight); + + RBT_SET_RED(pOther); + rbtRotateLeft(pRoot, pOther); + pOther = pParent->pLeft; + } + + rbtNodeSetColor(pOther, RBT_COLOR(pParent)); + RBT_SET_BLACK(pParent); + + if (pOther->pLeft != NULL) + RBT_SET_BLACK(pOther->pLeft); + + rbtRotateRight(pRoot, pParent); + pNode = pRoot->pRoot; + + break; + } + + if (pNode != NULL) + RBT_SET_BLACK(pNode); +} + +void rbtDelNodeDo(struct Ps3RbRoot *pRoot, struct Ps3RbNode *pNode) +{ + struct Ps3RbNode *pParent = NULL; + struct Ps3RbNode *pChild = NULL; + struct Ps3RbNode *pOld = NULL; + unsigned int color = 0; + + if (pNode->pLeft == NULL) { + pChild = pNode->pRight; + } else if (pNode->pRight == NULL) { + pChild = pNode->pLeft; + } else { + pOld = pNode; + + pNode = pNode->pRight; + while (pNode->pLeft != NULL) + pNode = pNode->pLeft; + + pChild = pNode->pRight; + pParent = RBT_PARENT(pNode); + color = RBT_COLOR(pNode); + + if (pChild != NULL) + rbtNodeSetParent(pChild, pParent); + + if (pParent == pOld) { + pParent->pRight = pChild; + pParent = pNode; + } else { + pParent->pLeft = pChild; + } + + pNode->pParentColor = pOld->pParentColor; + pNode->pRight = pOld->pRight; + pNode->pLeft = pOld->pLeft; + + if (RBT_PARENT(pOld) != NULL) { + if (RBT_PARENT(pOld)->pLeft == pOld) + RBT_PARENT(pOld)->pLeft = pNode; + else + RBT_PARENT(pOld)->pRight = pNode; + } else { + pRoot->pRoot = pNode; + } + + rbtNodeSetParent(pOld->pLeft, pNode); + if (pOld->pRight != NULL) + rbtNodeSetParent(pOld->pRight, pNode); + + goto l_color; + } + + pParent = RBT_PARENT(pNode); + color = RBT_COLOR(pNode); + + if (pChild != NULL) + rbtNodeSetParent(pChild, pParent); + + if (pParent != NULL) { + if (pParent->pLeft == pNode) + pParent->pLeft = pChild; + else + pParent->pRight = pChild; + } else { + pRoot->pRoot = pChild; + } + +l_color: + if (color == RBT_BLACK) + rbtColorAfterDel(pRoot, pChild, pParent); +} + +static struct Ps3RbNode *rbtFindNodeDo(struct Ps3RbRoot *pRoot, void *pKey, + struct Ps3RbTreeOps *pOps, + unsigned char intent_addnode, + struct Ps3RbNode **ppParent, + struct Ps3RbNode ***pppLinker) +{ + struct Ps3RbNode *pNode = NULL; + struct Ps3RbNode *pParent = NULL; + struct Ps3RbNode **ppLinker = NULL; + void *pKeyCur = NULL; + enum Ps3Cmp cmprc = PS3_EQ; + + BUG_ON(pOps->cmpkey == NULL); + BUG_ON((pOps->getkey == NULL) && + (!testBitNonAtomic(RBTBF_KEYOFFSET_ENABLE, + (unsigned long *)&pOps->flags))); + + ppLinker = &pRoot->pRoot; + while (NULL != (*ppLinker)) { + pParent = (*ppLinker); + + pKeyCur = ps3RbNodeGetKey(pParent, pOps); + cmprc = pOps->cmpkey(pKey, pKeyCur); + if (cmprc == PS3_LT) { + ppLinker = &pParent->pLeft; + } else if (cmprc == PS3_GT) { + ppLinker = &pParent->pRight; + } else if ((intent_addnode == PS3_TRUE) && + testBitNonAtomic( + RBTBF_CONFLICT_ENABLE, + (unsigned long *)&pOps->flags)) { + ppLinker = &pParent->pLeft; + } else { + pNode = pParent; + break; + } + } + + if (pppLinker != NULL) + (*pppLinker) = ppLinker; + + if (ppParent != NULL) { + if (pNode != NULL) + (*ppParent) = RBT_PARENT(pNode); + else + (*ppParent) = pParent; + } + + return pNode; +} + +void ps3RbtColorAfterAdd(struct Ps3RbRoot *pRoot, struct Ps3RbNode *pNode) +{ + struct Ps3RbNode *pGparent = NULL; + struct Ps3RbNode *pParent = NULL; + struct Ps3RbNode *pUncle = NULL; + struct Ps3RbNode *pTmp = NULL; + + while (1) { + pParent = RBT_PARENT(pNode); + if ((pParent == NULL) || RBT_IS_BLACK(pParent)) + break; + + pGparent = RBT_PARENT(pParent); + if (pParent == pGparent->pLeft) { + pUncle = pGparent->pRight; + if ((pUncle != NULL) && RBT_IS_RED(pUncle)) { + RBT_SET_BLACK(pUncle); + RBT_SET_BLACK(pParent); + RBT_SET_RED(pGparent); + + pNode = pGparent; + continue; + } + + if (pParent->pRight == pNode) { + rbtRotateLeft(pRoot, pParent); + + pTmp = pParent; + pParent = pNode; + pNode = pTmp; + } + + RBT_SET_BLACK(pParent); + RBT_SET_RED(pGparent); + rbtRotateRight(pRoot, pGparent); + } else { + pUncle = pGparent->pLeft; + if ((pUncle != NULL) && RBT_IS_RED(pUncle)) { + RBT_SET_BLACK(pUncle); + RBT_SET_BLACK(pParent); + RBT_SET_RED(pGparent); + + pNode = pGparent; + continue; + } + + if (pParent->pLeft == pNode) { + rbtRotateRight(pRoot, pParent); + + pTmp = pParent; + pParent = pNode; + pNode = pTmp; + } + + RBT_SET_BLACK(pParent); + RBT_SET_RED(pGparent); + rbtRotateLeft(pRoot, pGparent); + } + } + + RBT_SET_BLACK(pRoot->pRoot); +} + +struct Ps3RbNode *ps3RbtHeadNode(struct Ps3RbRoot *pRoot) +{ + struct Ps3RbNode *pNode = NULL; + + pNode = pRoot->pRoot; + if (pNode == NULL) + goto end; + + while (pNode->pLeft != NULL) + pNode = pNode->pLeft; + +end: + return pNode; +} + +struct Ps3RbNode *ps3RbtTailNode(struct Ps3RbRoot *pRoot) +{ + struct Ps3RbNode *pNode = NULL; + + pNode = pRoot->pRoot; + if (pNode == NULL) + goto end; + + while (pNode->pRight != NULL) + pNode = pNode->pRight; + +end: + return pNode; +} + +struct Ps3RbNode *ps3RbtPrevNode(struct Ps3RbNode *pNode) +{ + struct Ps3RbNode *pParent = NULL; + + if (pNode == NULL) + goto end; + + if (pNode->pLeft != NULL) { + pNode = pNode->pLeft; + while (pNode->pRight != NULL) + pNode = pNode->pRight; + + return pNode; + } + while (1) { + pParent = RBT_PARENT(pNode); + if ((pParent == NULL) || (pNode != pParent->pLeft)) + goto end; + + pNode = pParent; + } + +end: + return pParent; +} + +struct Ps3RbNode *ps3RbtNextNode(struct Ps3RbNode *pNode) +{ + struct Ps3RbNode *pParent = NULL; + + if (pNode == NULL) + goto end; + + if (pNode->pRight != NULL) { + pNode = pNode->pRight; + while (pNode->pLeft != NULL) + pNode = pNode->pLeft; + + return pNode; + } + + while (1) { + pParent = RBT_PARENT(pNode); + if ((pParent == NULL) || (pNode != pParent->pRight)) + goto end; + + pNode = pParent; + } + +end: + return pParent; +} + +void ps3RbtReplaceNode(struct Ps3RbRoot *pRoot, struct Ps3RbNode *pNew, + struct Ps3RbNode *pVictim) +{ + struct Ps3RbNode *pParent = RBT_PARENT(pVictim); + + if (pParent != NULL) { + if (pVictim == pParent->pLeft) + pParent->pLeft = pNew; + else + pParent->pRight = pNew; + } else { + pRoot->pRoot = pNew; + } + + if (pVictim->pLeft != NULL) + rbtNodeSetParent(pVictim->pLeft, pNew); + + if (pVictim->pRight != NULL) + rbtNodeSetParent(pVictim->pRight, pNew); + + (*pNew) = (*pVictim); +} + +int ps3RbtDelNode(struct Ps3RbRoot *pRoot, struct Ps3RbNode *pNode) +{ + int rc = 0; + + if (RBT_NODE_IS_EMPTY(pNode)) { + rc = -PS3_FAILED; + goto end; + } + + rbtDelNodeDo(pRoot, pNode); + ps3RbNodeInit(pNode); + +end: + return rc; +} + +int ps3RbtAddNode(struct Ps3RbRoot *pRoot, struct Ps3RbNode *pNode, + struct Ps3RbTreeOps *pOps) +{ + struct Ps3RbNode *pParent = NULL; + struct Ps3RbNode **ppLinker = NULL; + void *pKey = NULL; + int rc = 0; + + BUG_ON((pOps->getkey == NULL) && + (!testBitNonAtomic(RBTBF_KEYOFFSET_ENABLE, + (unsigned long *)&pOps->flags))); + + if (!RBT_NODE_IS_EMPTY(pNode)) { + rc = -PS3_FAILED; + goto end; + } + + pKey = ps3RbNodeGetKey(pNode, pOps); + if (NULL != + rbtFindNodeDo(pRoot, pKey, pOps, PS3_TRUE, &pParent, &ppLinker)) { + rc = -PS3_FAILED; + goto end; + } + + ps3RbNodeLink(pNode, pParent, ppLinker); + ps3RbtColorAfterAdd(pRoot, pNode); + +end: + return rc; +} + +struct Ps3RbNode *ps3RbtFindNode(struct Ps3RbRoot *pRoot, void *pKey, + struct Ps3RbTreeOps *pOps) +{ + struct Ps3RbNode *pNode = NULL; + + if (pKey == NULL) { + pNode = ps3RbtHeadNode(pRoot); + goto end; + } + + pNode = rbtFindNodeDo(pRoot, pKey, pOps, PS3_FALSE, NULL, NULL); + +end: + return pNode; +} + +struct Ps3RbNode *ps3RbtFindNextNode(struct Ps3RbRoot *pRoot, void *pKey, + struct Ps3RbTreeOps *pOps) +{ + struct Ps3RbNode *pNode = NULL; + struct Ps3RbNode *pParent = NULL; + struct Ps3RbNode **ppLinker = NULL; + void *pKeyCur = NULL; + + if (pKey == NULL) { + pNode = ps3RbtHeadNode(pRoot); + goto end; + } + + pNode = rbtFindNodeDo(pRoot, pKey, pOps, PS3_FALSE, &pParent, + &ppLinker); + if (pNode != NULL) { + pNode = ps3RbtNextNode(pNode); + + if (!testBitNonAtomic(RBTBF_CONFLICT_ENABLE, + (unsigned long *)&pOps->flags)) { + goto end; + } + + while (pNode != NULL) { + pKeyCur = ps3RbNodeGetKey(pNode, pOps); + if (pOps->cmpkey(pKey, pKeyCur) != PS3_EQ) + break; + + pNode = ps3RbtNextNode(pNode); + } + + goto end; + } + + if (pParent == NULL) + goto end; + + if (ppLinker == &pParent->pLeft) { + pNode = pParent; + goto end; + } + + pNode = ps3RbtNextNode(pParent); + +end: + return pNode; +} + +void ps3RbtClean(struct Ps3RbRoot *pRoot) +{ + struct Ps3RbNode *pNode = NULL; + + pNode = ps3RbtHeadNode(pRoot); + while (pNode != NULL) { + (void)ps3RbtDelNode(pRoot, pNode); + + pNode = ps3RbtHeadNode(pRoot); + } +} + +int ps3RbtTraverse(struct Ps3RbRoot *pRoot, + int (*visit)(struct Ps3RbNode *pNode, void *pCtxt), + void *p_ctxt) +{ + struct Ps3RbNode *pNode = NULL; + struct Ps3RbNode *pNext = NULL; + int rc = 0; + + BUG_ON(visit == NULL); + + RBT_FOR_EACH_SAFE(pNode, pNext, pRoot) + { + rc = visit(pNode, p_ctxt); + if (rc < 0) + goto end; + } + +end: + return rc; +} diff --git a/drivers/scsi/linkdata/ps3stor/ps3_rb_tree.h b/drivers/scsi/linkdata/ps3stor/ps3_rb_tree.h new file mode 100644 index 0000000000000000000000000000000000000000..8642441cdefae6eaa74aacd791c2db384c658763 --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/ps3_rb_tree.h @@ -0,0 +1,325 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __PS3_RBTREE_H__ +#define __PS3_RBTREE_H__ + +#ifndef _WINDOWS +#include +#include +#include +#endif + +#include "ps3_driver_log.h" +#include "ps3_err_def.h" + +static inline void setBitNonAtomic(unsigned int nr, + unsigned long *addr) +{ + set_bit(nr, addr); +} + +static inline void clearBitNonAtomic(unsigned int nr, + unsigned long *addr) +{ + clear_bit(nr, addr); +} + +static inline int testBitNonAtomic(unsigned int nr, + const unsigned long *addr) +{ + return test_bit(nr, addr); +} + + +struct Ps3RbNode { + __aligned(8) unsigned long long pParentColor; + + struct { + struct Ps3RbNode *pLeft; + struct Ps3RbNode *pRight; + }; +}; + +struct Ps3RbRoot { + struct Ps3RbNode *pRoot; +}; + +#define PS3_RBROOT_INITNIL \ + { \ + NULL \ + } + +enum Ps3Cmp { + PS3_EQ = 0, + PS3_GT = 1, + PS3_LT = 2, + PS3_CMPNR = 3, +}; + +enum Ps3RbtreebFlag { + RBTBF_KEYOFFSET_ENABLE = 0, + RBTBF_CONFLICT_ENABLE, +}; + +struct Ps3RbTreeOps { + enum Ps3Cmp (*cmpkey)(void *pKey1, void *pKey2); + + union { + void *(*getkey)(struct Ps3RbNode *pNode, void *pCtxt); + unsigned long long keyoffset; + }; + + unsigned int flags; + void *pCtxt; +}; + +struct Ps3RbTree { + struct Ps3RbRoot root; + unsigned int nodenr; + struct Ps3RbTreeOps ops; +}; + +static inline void ps3RbNodeInit(struct Ps3RbNode *pNode) +{ + pNode->pParentColor = ((uintptr_t)(void *)pNode); +} + +static inline void ps3RbNodeLink(struct Ps3RbNode *pNode, + struct Ps3RbNode *pParent, + struct Ps3RbNode **ppLinker) +{ + pNode->pParentColor = ((uintptr_t)(void *)pParent); + pNode->pLeft = NULL; + pNode->pRight = NULL; + + (*ppLinker) = pNode; +} + +static inline void *ps3RbNodeGetKey(struct Ps3RbNode *pNode, + struct Ps3RbTreeOps *pOps) +{ + if (testBitNonAtomic(RBTBF_KEYOFFSET_ENABLE, + (unsigned long *)&pOps->flags)) { + return (void *)((unsigned char *)pNode + pOps->keyoffset); + } + + return pOps->getkey(pNode, pOps->pCtxt); +} + +static inline void ps3RbRootInit(struct Ps3RbRoot *pRoot) +{ + pRoot->pRoot = NULL; +} + +static inline void ps3RbRootFini(struct Ps3RbRoot *pRoot) +{ + BUG_ON(pRoot->pRoot != NULL); +} + +void ps3RbtColorAfterAdd(struct Ps3RbRoot *pRoot, struct Ps3RbNode *pNode); + +struct Ps3RbNode *ps3RbtHeadNode(struct Ps3RbRoot *pRoot); + +struct Ps3RbNode *ps3RbtTailNode(struct Ps3RbRoot *pRoot); + +struct Ps3RbNode *ps3RbtPrevNode(struct Ps3RbNode *pNode); + +struct Ps3RbNode *ps3RbtNextNode(struct Ps3RbNode *pNode); + +void ps3RbtReplaceNode(struct Ps3RbRoot *pRoot, struct Ps3RbNode *pNew, + struct Ps3RbNode *pVictim); + +int ps3RbtDelNode(struct Ps3RbRoot *pRoot, struct Ps3RbNode *pNode); + +int ps3RbtAddNode(struct Ps3RbRoot *pRoot, struct Ps3RbNode *pNode, + struct Ps3RbTreeOps *pOps); + +struct Ps3RbNode *ps3RbtFindNode(struct Ps3RbRoot *pRoot, void *pKey, + struct Ps3RbTreeOps *pOps); + +struct Ps3RbNode *ps3RbtFindNextNode(struct Ps3RbRoot *pRoot, void *pKey, + struct Ps3RbTreeOps *pOps); + +void ps3RbtClean(struct Ps3RbRoot *pRoot); + +int ps3RbtTraverse(struct Ps3RbRoot *pRoot, + int (*visit)(struct Ps3RbNode *pNode, void *pCtxt), + void *pCtxt); + +static inline unsigned char ps3RbtIsEmpty(struct Ps3RbRoot *pRoot) +{ + return (unsigned char)(pRoot->pRoot == NULL); +} + +static inline int ps3RbTreeInit(struct Ps3RbTree *pTree, + struct Ps3RbTreeOps *pOps) +{ + int rc = 0; + + ps3RbRootInit(&pTree->root); + pTree->nodenr = 0; + + memset(&pTree->ops, 0, sizeof(struct Ps3RbTreeOps)); + if (pOps != NULL) + pTree->ops = (*pOps); + + return rc; +} + +static inline int +ps3RbTreeInitGetKey(struct Ps3RbTree *pTree, + enum Ps3Cmp (*cmpkey)(void *pKey1, void *pKey2), + void *(*getkey)(struct Ps3RbNode *pNode, void *pCtxt), + void *pCtxt) +{ + struct Ps3RbTreeOps ops; + + memset(&ops, 0, sizeof(struct Ps3RbTreeOps)); + ops.cmpkey = cmpkey; + ops.getkey = getkey; + ops.pCtxt = pCtxt; + + return ps3RbTreeInit(pTree, &ops); +} + +static inline int +ps3RbTreeInitKeyOffset(struct Ps3RbTree *pTree, + enum Ps3Cmp (*cmpkey)(void *pKey1, void *pKey2), + unsigned long long keyoffset, void *pCtxt) +{ + struct Ps3RbTreeOps ops; + + memset(&ops, 0, sizeof(struct Ps3RbTreeOps)); + ops.cmpkey = cmpkey; + ops.keyoffset = keyoffset; + ops.pCtxt = pCtxt; + setBitNonAtomic(RBTBF_KEYOFFSET_ENABLE, + (unsigned long *)&ops.flags); + + return ps3RbTreeInit(pTree, &ops); +} + +static inline void ps3RbTreeFini(struct Ps3RbTree *pTree) +{ + BUG_ON(pTree->nodenr != 0); + ps3RbRootFini(&pTree->root); +} + +static inline struct Ps3RbNode *ps3RbTreeHeadNode(struct Ps3RbTree *pTree) +{ + return ps3RbtHeadNode(&pTree->root); +} + +static inline struct Ps3RbNode *ps3RbTreeTailNode(struct Ps3RbTree *pTree) +{ + return ps3RbtTailNode(&pTree->root); +} + +static inline struct Ps3RbNode *ps3RbTreePrevNode(struct Ps3RbNode *pNode) +{ + return ps3RbtPrevNode(pNode); +} + +static inline struct Ps3RbNode *ps3RbTreeNextNode(struct Ps3RbNode *pNode) +{ + return ps3RbtNextNode(pNode); +} + +static inline void ps3RbTreeReplaceNode(struct Ps3RbTree *pTree, + struct Ps3RbNode *pNew, + struct Ps3RbNode *pVictim) +{ + ps3RbtReplaceNode(&pTree->root, pNew, pVictim); +} + +static inline int ps3RbTreeDelNode(struct Ps3RbTree *pTree, + struct Ps3RbNode *pNode) +{ + int rc = 0; + + rc = ps3RbtDelNode(&pTree->root, pNode); + if (rc >= 0) + pTree->nodenr--; + + return rc; +} + +static inline int ps3RbTreeAddNode(struct Ps3RbTree *pTree, + struct Ps3RbNode *pNode) +{ + int rc = 0; + + rc = ps3RbtAddNode(&pTree->root, pNode, &pTree->ops); + if (rc >= 0) + pTree->nodenr++; + + return rc; +} + +static inline struct Ps3RbNode *ps3RbTreeFindNode(struct Ps3RbTree *pTree, + void *pKey) +{ + return ps3RbtFindNode(&pTree->root, pKey, &pTree->ops); +} + +static inline struct Ps3RbNode *ps3RbTreeFindNextNode(struct Ps3RbTree *pTree, + void *pKey) +{ + return ps3RbtFindNextNode(&pTree->root, pKey, &pTree->ops); +} + +static inline void ps3RbTreeClean(struct Ps3RbTree *pTree) +{ + ps3RbtClean(&pTree->root); + pTree->nodenr = 0; +} + +static inline int ps3RbTreeTraverse(struct Ps3RbTree *pTree, + int (*visit)(struct Ps3RbNode *pNode, + void *pCtxt), + void *pCtxt) +{ + return ps3RbtTraverse(&pTree->root, visit, pCtxt); +} + +static inline unsigned char ps3RbTreeIsEmpty(struct Ps3RbTree *pTree) +{ + return (unsigned char)(pTree->root.pRoot == NULL); +} + +static inline unsigned int ps3RbTreeNodeNr(struct Ps3RbTree *pTree) +{ + return pTree->nodenr; +} + +#define RBT_RED (0) +#define RBT_BLACK (1) + +#define RBT_PARENT(_n) \ + ((struct Ps3RbNode *)(uintptr_t)((_n)->pParentColor & ~3ULL)) +#define RBT_COLOR(_n) ((_n)->pParentColor & 1ULL) + +#define RBT_IS_RED(_n) (!RBT_COLOR(_n)) +#define RBT_IS_BLACK(_n) RBT_COLOR(_n) +#define RBT_SET_RED(_n) ((_n)->pParentColor &= ~1ULL) +#define RBT_SET_BLACK(_n) ((_n)->pParentColor |= 1ULL) +#define RBT_ROOT_IS_EMPTY(_r) ((_r)->pRoot == NULL) +#define RBT_TREE_IS_EMPTY(_t) RBT_ROOT_IS_EMPTY(&(_t)->root) +#define RBT_NODE_IS_EMPTY(_n) (RBT_PARENT(_n) == (_n)) +#define RBT_NODE_CLEAR(_n) (ps3RbNodeInit(_n)) +#define RBT_FOR_EACH(_p_node, _p_root) \ + for (_p_node = ps3RbtHeadNode(_p_root); _p_node != NULL; \ + _p_node = ps3RbtNextNode(_p_node)) + +#define RBT_FOR_EACH_SAFE(_p_node, _p_next, _p_root) \ + for (_p_node = ps3RbtHeadNode(_p_root), \ + _p_next = ps3RbtNextNode(_p_node); \ + _p_node != NULL; \ + _p_node = _p_next, _p_next = ps3RbtNextNode(_p_node)) + +#define RBTREE_FOR_EACH(_p_node, _p_tree) \ + RBT_FOR_EACH((_p_node), &(_p_tree)->root) + +#define RBTREE_FOR_EACH_SAFE(_p_node, _p_next, _p_tree) \ + RBT_FOR_EACH_SAFE((_p_node), (_p_next), &(_p_tree)->root) + +#endif diff --git a/drivers/scsi/linkdata/ps3stor/ps3_recovery.c b/drivers/scsi/linkdata/ps3stor/ps3_recovery.c new file mode 100644 index 0000000000000000000000000000000000000000..c63616bda5fef596bf52e56fa530d2404725689c --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/ps3_recovery.c @@ -0,0 +1,3899 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "ps3_recovery.h" + +#ifndef _WINDOWS + +#include +#endif +#include "ps3_recovery.h" +#include "ps3_ioc_state.h" +#include "ps3_event.h" +#include "ps3_mgr_channel.h" +#include "ps3_cmd_complete.h" +#include "ps3_device_update.h" +#include "ps3_ioc_manager.h" +#include "ps3_mgr_cmd.h" +#include "ps3_scsi_cmd_err.h" +#include "ps3_scsih.h" +#include "ps3_cmd_statistics.h" +#include "ps3_r1x_write_lock.h" +#include "ps3_ioc_state.h" +#include "ps3_module_para.h" +#include "ps3_ioctl.h" +#include "ps3_dump.h" +#include "ps3_cli_debug.h" +#include "ps3_kernel_version.h" + +int ps3_recovery_cancel_work_sync(struct ps3_instance *instance); +#ifndef _WINDOWS +static void ps3_recovery_work(struct work_struct *work); +#else +static void ps3_recovery_work(void *work); +#endif +static int ps3_hard_recovery_handle(struct ps3_instance *instance); +static int ps3_soft_recovery_handle(struct ps3_instance *instance); +static int ps3_recovery_ready_to_force_cmd_stop(struct ps3_instance *instance); +static int ps3_recovery_ready_to_running(struct ps3_instance *instance); +static void ps3_can_queue_reset(struct ps3_instance *instance, + unsigned int cur_max_fw_cmds); +static void ps3_cmd_reset_flag_set(struct ps3_instance *instance, + unsigned char reset_flag); +static int +ps3_soft_recovery_fail_to_hard_recovery(struct ps3_instance *instance); + +enum { + PS3_RECOVERY_INTER_ERR_SUCCESS = 0, + PS3_RECOVERY_INTER_ERR_INTERRUPT = 1, + PS3_RECOVERY_INTER_ERR_FAILED = 2, + PS3_RECOVERY_INTER_ERR_NEED_RECOVERY = 3, + PS3_RECOVERY_INTER_ERR_PCIE_ERR = 4, + PS3_RECOVERY_INTER_ERR_SUSPEND_RESUME = 5, +}; + +static inline void +ps3_wait_watchdog_dect_recovery(struct ps3_instance *instance) +{ + unsigned short wait_step = 100; + unsigned short cur_cnt = 0; + unsigned char printed = PS3_TRUE; + + while (!instance->watchdog_context.is_halt && + instance->watchdog_context.watchdog_queue != NULL) { + ps3_msleep(wait_step); + if (printed && ((++cur_cnt) * wait_step > + PS3_RECOVERY_WHILE_PRINT_REACH_TIME)) { + printed = PS3_FALSE; + LOG_WARN("host:%u wait watchdog recovery.\n", + PS3_HOST(instance)); + } + } +} + +struct ps3_recovery_context *ps3_recovery_context_alloc(void) +{ + struct ps3_recovery_context *context; + + context = kzalloc(sizeof(struct ps3_recovery_context), GFP_KERNEL); + if (!context) + return NULL; + + return context; +} +void ps3_recovery_context_free(struct ps3_recovery_context *context) +{ + if (context != NULL) + kfree(context); +} + +void ps3_recovery_context_delete(struct ps3_recovery_context *context) +{ + if (context != NULL) { + if (context->recovery_wq) { + destroy_workqueue(context->recovery_wq); + context->recovery_wq = NULL; + } + kfree(context); + context = NULL; + } +} + +#ifndef _WINDOWS +static void ps3_recovery_irq_service(struct work_struct *work) +{ + struct ps3_instance *instance = + ps3_container_of(work, struct ps3_instance, recovery_irq_work); + int ret = PS3_SUCCESS; + unsigned int count = 0; + + LOG_INFO("hno:%u recovery irq triggered\n", PS3_HOST(instance)); + + if ((instance->recovery_context->heartbeat_recovery == + PS3_HEARTBEAT_HARDRESET_DECIDE || + instance->recovery_context->heartbeat_recovery == + PS3_HEARTBEAT_HARDRESET_RECOVERY) && + (instance->recovery_context->recovery_state == + PS3_HARD_RECOVERY_SHALLOW || + instance->recovery_context->recovery_state == + PS3_HARD_RECOVERY_DECIDE)) { + goto l_out; + } + + ps3_mutex_lock( + &instance->recovery_context->ps3_watchdog_recovery_mutex); + if (instance->recovery_context->heartbeat_recovery == + PS3_HEARTBEAT_NULL) { + instance->recovery_context->heartbeat_recovery = + PS3_HEARTBEAT_HARDRESET_DECIDE; + } + ps3_mutex_unlock( + &instance->recovery_context->ps3_watchdog_recovery_mutex); + while ((ret = ps3_hard_recovery_request(instance)) == -PS3_RETRY) { + if (count++ == ps3_use_hard_reset_max_retry()) { + ret = -PS3_FAILED; + break; + } + ps3_mdelay(10); + } + if (ret == PS3_SUCCESS) { + LOG_INFO("hno[%u], recovery success!\n", PS3_HOST(instance)); + } else { + LOG_ERROR("hno[%u], recovery request NOK, %s!\n", + PS3_HOST(instance), + namePS3InstanceState(ps3_atomic_read( + &instance->state_machine.state))); + } +l_out: + return; +} + +int ps3_recovery_irq_start(struct ps3_instance *instance) +{ + char request_irq_queue_name[PS3_RECOVERY_IRQ_NAME_MAX_LENGTH]; + struct work_struct *recovery_irq_work = &instance->recovery_irq_work; + + if (instance->recovery_irq_queue != NULL) { + LOG_DEBUG("hno:%u watchdog for is already started!\n", + PS3_HOST(instance)); + goto l_out; + } + + memset(request_irq_queue_name, 0, PS3_RECOVERY_IRQ_NAME_MAX_LENGTH); + INIT_WORK(recovery_irq_work, ps3_recovery_irq_service); + + snprintf(request_irq_queue_name, PS3_RECOVERY_IRQ_NAME_MAX_LENGTH, + "ps3_irq_recovery_start_service_host%d", + instance->host->host_no); + + instance->recovery_irq_queue = + create_singlethread_workqueue(request_irq_queue_name); + if (instance->recovery_irq_queue == NULL) { + LOG_ERROR("hno:%u watchdog work queue create failed\n", + PS3_HOST(instance)); + return -PS3_FAILED; + } + instance->recovery_irq_enable = PS3_TRUE; +l_out: + return PS3_SUCCESS; +} + +irqreturn_t ps3_recovery_irq_handler(int virq, void *dev_id) +{ + struct ps3_irq_recovery *irq = (struct ps3_irq_recovery *)dev_id; + struct ps3_instance *instance = irq->instance; + + if (instance->ioc_adpter->ioc_heartbeat_detect == NULL) + goto l_out; + + if (instance->ioc_adpter->ioc_heartbeat_detect(instance) == PS3_TRUE && + !ps3_pci_err_recovery_get(instance) && + instance->recovery_irq_enable) { + + LOG_DEBUG( + "hno:%u recovery irq received, virq: %d, dev_id: 0x%llx\n", + PS3_HOST(instance), virq, + (unsigned long long)(uintptr_t)dev_id); + if (!work_busy(&instance->recovery_irq_work)) { + queue_work(instance->recovery_irq_queue, + &instance->recovery_irq_work); + } + } +l_out: + return IRQ_HANDLED; +} + +void ps3_recovery_irq_queue_destroy(struct ps3_instance *instance) +{ + if (instance->recovery_irq_queue) { + flush_workqueue(instance->recovery_irq_queue); + destroy_workqueue(instance->recovery_irq_queue); + instance->recovery_irq_queue = NULL; + } +} + +void ps3_recovery_work_queue_destroy(struct ps3_instance *instance) +{ + struct ps3_recovery_context *context = instance->recovery_context; + struct workqueue_struct *wq = context->recovery_wq; + + if (!ps3_is_latest_func(instance)) { + LOG_DEBUG("hno:%u not latest func\n", PS3_HOST(instance)); + goto l_out; + } + + context->recovery_wq = NULL; + if (wq == NULL) + return; + + flush_workqueue(wq); + destroy_workqueue(wq); +l_out: + return; +} +#endif +void ps3_recovery_function_init(struct ps3_instance *instance) +{ + instance->recovery_function.halt_handle_cb = NULL; + instance->recovery_function.hardreset_handle_init_running_cb = + ps3_hardreset_handle_init_running; + instance->recovery_function.hardreset_handle_post_cb = + ps3_hardreset_handle_post; + instance->recovery_function.hardreset_handle_pre_cb = + ps3_hardreset_handle_pre; + instance->recovery_function.hardreset_handle_wait_ready_cb = + ps3_hardreset_handle_wait_ready; + instance->recovery_function.hardreset_handle_finish_cb = + ps3_hardreset_handle_finish; + instance->recovery_function.hardreset_handle_offline_cb = + ps3_hardreset_handle_offline; + instance->recovery_function.recovery_handle_cb = NULL; + instance->recovery_function.softreset_handle_post_cb = + ps3_softreset_handle_post; + instance->recovery_function.softreset_handle_pre_cb = + ps3_softreset_handle_pre; + +} + +int ps3_recovery_context_init(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + struct ps3_recovery_context *context = NULL; + + ps3_recovery_function_init(instance); + + if (instance->peer_instance != NULL) { + instance->recovery_context = + instance->peer_instance->recovery_context; + goto l_recovery_irq_init; + } + + context = ps3_recovery_context_alloc(); + if (context == NULL) { + LOG_ERROR("hno:%u, alloc recovery context failed !\n", + PS3_HOST(instance)); + ret = -PS3_FAILED; + goto l_out; + } + + LOG_DEBUG("recovery context add!\n"); + snprintf(context->recovery_wq_name, 20, "ps3_rec_th_%d", + instance->host->host_no); + context->recovery_wq = + create_singlethread_workqueue(context->recovery_wq_name); + if (context->recovery_wq == NULL) { + LOG_ERROR("hno:%u create recovery_wq NOK !\n", + PS3_HOST(instance)); + ret = -PS3_FAILED; + goto l_out; + } + INIT_WORK(&context->recovery_work, ps3_recovery_work); + + ps3_mutex_init(&context->ps3_watchdog_recovery_mutex); + ps3_mutex_init(&context->free_cmd_lock); + ps3_spin_lock_init(&context->recovery_lock); + ps3_spin_lock_init(&context->ps3_hardreset_lock); + context->parall_hardreset_state = PS3_PARALLEL_HARDRESET_STATE_INIT; + ps3_atomic_set(&context->hardreset_ref, 0); + context->recovery_state = PS3_SOFT_RECOVERY_PROBE_PROCESS; + instance->recovery_context = context; + instance->recovery_context->hardreset_count = 0; + context->host_reset_state = PS3_HOST_RESET_INIT; + context->instance_change = 0; + +l_recovery_irq_init: + if (ps3_recovery_irq_start(instance) != PS3_SUCCESS) { + LOG_ERROR("hno:%u recovery irq NOK\n", PS3_HOST(instance)); + ret = -PS3_FAILED; + goto l_recovery_start_failed; + } + + return ret; +l_recovery_start_failed: + ps3_recovery_irq_queue_destroy(instance); + ps3_mutex_destroy(&context->ps3_watchdog_recovery_mutex); + ps3_mutex_destroy(&context->free_cmd_lock); + instance->recovery_context = NULL; +l_out: + ps3_recovery_context_delete(context); + return ret; +} + +void ps3_recovery_clean(struct ps3_instance *instance) +{ + struct ps3_recovery_context *context = instance->recovery_context; + + if (context == NULL) + goto l_out; + + if (!ps3_is_latest_func(instance)) { + instance->recovery_context = NULL; + goto l_out; + } + + ps3_mutex_destroy(&context->ps3_watchdog_recovery_mutex); + ps3_mutex_destroy(&context->free_cmd_lock); + ps3_recovery_context_free(instance->recovery_context); + instance->recovery_context = NULL; +l_out: + return; +} + +void ps3_recovery_destroy(struct ps3_instance *instance) +{ +#ifndef _WINDOWS + ps3_recovery_cancel_work_sync(instance); + ps3_recovery_work_queue_destroy(instance); +#else + struct ps3_recovery_context *context = instance->recovery_context; + + ps3_worker_exit(&context->recovery_work); +#endif +} + +void ps3_recovery_context_exit(struct ps3_instance *instance) +{ + ps3_recovery_irq_queue_destroy(instance); + + if (instance->peer_instance == NULL && + instance->recovery_context != NULL) { + ps3_recovery_destroy(instance); + ps3_recovery_clean(instance); + } + +} +int ps3_recovery_state_transfer(struct ps3_instance *instance, + unsigned int dest_state) +{ + unsigned int recovery_origin_state; + + ps3_mutex_lock(&instance->state_machine.lock); + recovery_origin_state = instance->recovery_context->recovery_state; + if (recovery_origin_state == dest_state) + goto l_fail; + + switch (recovery_origin_state) { + case PS3_SOFT_RECOVERY_PROBE_PROCESS: + if (dest_state == PS3_SOFT_RECOVERY_SHALLOW || + dest_state == PS3_SOFT_RECOVERY_DEEP || + dest_state == PS3_SOFT_RECOVERY_IOC_RECOVERY || + dest_state == PS3_HARD_RECOVERY_DECIDE) { + goto l_success; + } else { + goto l_fail; + } + break; + case PS3_SOFT_RECOVERY_SHALLOW: + case PS3_SOFT_RECOVERY_DEEP: + case PS3_SOFT_RECOVERY_IOC_RECOVERY: + if ((dest_state == PS3_HARD_RECOVERY_DECIDE) || + (dest_state == PS3_HARD_RECOVERY_SHALLOW) || + (dest_state == PS3_SOFT_RECOVERY_FINISH)) { + goto l_success; + } else { + goto l_fail; + } + break; + case PS3_SOFT_RECOVERY_FINISH: + if ((dest_state == PS3_HARD_RECOVERY_DECIDE) || + (dest_state == PS3_SOFT_RECOVERY_SHALLOW) || + (dest_state == PS3_SOFT_RECOVERY_DEEP) || + (dest_state == PS3_SOFT_RECOVERY_IOC_RECOVERY) || + (dest_state == PS3_HARD_RECOVERY_SHALLOW)) { + goto l_success; + } else { + goto l_fail; + } + break; + case PS3_HARD_RECOVERY_DECIDE: + if (dest_state == PS3_HARD_RECOVERY_SHALLOW || + dest_state == PS3_HARD_RECOVERY_FINISH) { + goto l_success; + } else { + goto l_fail; + } + break; + case PS3_HARD_RECOVERY_FINISH: + if ((dest_state == PS3_HARD_RECOVERY_DECIDE) || + (dest_state == PS3_SOFT_RECOVERY_SHALLOW) || + (dest_state == PS3_SOFT_RECOVERY_DEEP) || + (dest_state == PS3_SOFT_RECOVERY_IOC_RECOVERY) || + (dest_state == PS3_HARD_RECOVERY_SHALLOW)) { + goto l_success; + } else { + goto l_fail; + } + break; + case PS3_HARD_RECOVERY_SHALLOW: + if (dest_state == PS3_HARD_RECOVERY_FINISH) + goto l_success; + else + goto l_fail; + break; + default: + goto l_fail; + } + +l_fail: + ps3_mutex_unlock(&instance->state_machine.lock); + LOG_ERROR("hno:%u recovery state transfer NOK! [cur_state:%d][dest_state:%d]\n", + PS3_HOST(instance), recovery_origin_state, dest_state); + return -PS3_FAILED; +l_success: + instance->recovery_context->recovery_state = dest_state; + ps3_mutex_unlock(&instance->state_machine.lock); + LOG_FILE_INFO("hno:%u recovery state transfer from %d to %d!\n", + PS3_HOST(instance), recovery_origin_state, dest_state); + + return PS3_SUCCESS; +} + +#ifndef _WINDOWS + +static int ps3_recovery_work_start(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + struct ps3_recovery_context *context = instance->recovery_context; + + + if (unlikely(context->recovery_wq == NULL)) { + ret = -PS3_FAILED; + goto l_out; + } + context->work_instance = instance; + + queue_work(context->recovery_wq, &context->recovery_work); + +l_out: + return ret; +} +static inline void ps3_wait_hard_reset_finish(struct ps3_instance *instance) +{ + while (ps3_atomic_read(&instance->recovery_context->hardreset_ref) != + 0) { + ps3_msleep(PS3_LOOP_TIME_INTERVAL_100MS); + } + + if (instance->recovery_context->recovery_state == + PS3_HARD_RECOVERY_DECIDE) { + do { + ps3_msleep(100); + + if (likely(instance->recovery_context->recovery_state == + PS3_HARD_RECOVERY_DECIDE)) { + continue; + } else { + cancel_work_sync(&instance->recovery_context + ->recovery_work); + break; + } + } while (1); + } else { + cancel_work_sync(&instance->recovery_context->recovery_work); + } +} +int ps3_recovery_cancel_work_sync(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + struct ps3_recovery_context *context = instance->recovery_context; + + if (unlikely(context->recovery_wq == NULL)) { + ret = -PS3_FAILED; + goto l_out; + } + + ps3_wait_hard_reset_finish(instance); + +l_out: + return ret; +} + +static int ps3_recovery_start(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + + ps3_mutex_lock(&instance->state_machine.lock); + if (instance->recovery_context->recovery_state == + PS3_HARD_RECOVERY_SHALLOW) { + LOG_ERROR("hno:%u hard recovery work proc,exit!\n", + PS3_HOST(instance)); + ps3_mutex_unlock(&instance->state_machine.lock); + goto l_out; + } + ps3_mutex_unlock(&instance->state_machine.lock); + if (unlikely(instance->recovery_context->recovery_wq == NULL)) { + ret = -PS3_FAILED; + LOG_ERROR("hno:%u recovery work sync NOK!\n", + PS3_HOST(instance)); + goto l_out; + } + + cancel_work_sync(&instance->recovery_context->recovery_work); + + ps3_mutex_lock(&instance->state_machine.lock); + if (instance->recovery_context->recovery_state == + PS3_HARD_RECOVERY_DECIDE) { + if (ps3_instance_no_lock_state_transfer( + instance, PS3_INSTANCE_STATE_RECOVERY) != + PS3_SUCCESS) { + LOG_ERROR( + "hno:%u transfer to PS3_INSTANCE_STATE_RECOVERY NOK!\n", + PS3_HOST(instance)); + ret = -PS3_FAILED; + ps3_mutex_unlock(&instance->state_machine.lock); + goto l_out; + } + } + ps3_mutex_unlock(&instance->state_machine.lock); + + if (instance->peer_instance != NULL) { + ps3_mutex_lock(&instance->peer_instance->state_machine.lock); + if (instance->peer_instance->recovery_context->recovery_state == + PS3_HARD_RECOVERY_DECIDE) { + if (ps3_instance_no_lock_state_transfer( + instance->peer_instance, + PS3_INSTANCE_STATE_RECOVERY) != + PS3_SUCCESS) { + LOG_ERROR( + "hno:%u transfer to PS3_INSTANCE_STATE_RECOVERY NOK!\n", + PS3_HOST(instance->peer_instance)); + ret = -PS3_FAILED; + ps3_mutex_unlock(&instance->peer_instance + ->state_machine.lock); + goto l_out; + } + } + ps3_mutex_unlock(&instance->peer_instance->state_machine.lock); + } + + ret = ps3_recovery_work_start(instance); + if (ret != PS3_SUCCESS) { + LOG_ERROR("hno:%u recovery work start NOK!\n", + PS3_HOST(instance)); + goto l_out; + } +l_out: + return ret; +} +#else +static int ps3_recovery_start(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + struct ps3_recovery_context *context = instance->recovery_context; + + if (ps3_worker_start(&context->recovery_work) != PS3_SUCCESS) { + LOG_ERROR( + "trace_id[0x%llx], hno:%u recovery start worker failed\n", + PS3_HOST(instance)); + ret = -PS3_FAILED; + goto l_out; + } +l_out: + return ret; +} +#endif + +static unsigned char ps3_recovery_is_state_halt(struct ps3_instance *instance) +{ + unsigned char ret = PS3_FALSE; + struct ps3_instance_state_machine *state_machine = + &instance->state_machine; + unsigned int cur_state = 0; + unsigned int ioc_state = instance->ioc_adpter->ioc_state_get(instance); + + cur_state = ps3_atomic_read(&state_machine->state); + if (cur_state == PS3_INSTANCE_STATE_DEAD) { + LOG2_INFO_LIM( + "hno:%u ioc_state:%s, drv_state:PS3_INSTANCE_STATE_DEAD, stop recovery!\n", + PS3_HOST(instance), ps3_ioc_state_print(ioc_state)); + ret = PS3_TRUE; + goto l_out; + } + + if (ioc_state == PS3_FW_STATE_HALT && + instance->recovery_context->heartbeat_recovery != + PS3_HEARTBEAT_HARDRESET_DECIDE) { + ps3_atomic_set(&state_machine->state, PS3_INSTANCE_STATE_DEAD); + LOG2_INFO_LIM( + "hno:%u ioc_state:PS3_FW_STATE_HALT, stop recovery!\n", + PS3_HOST(instance)); + while (1) + ps3_msleep(10); + ret = PS3_TRUE; + goto l_out; + } + + if (PS3_IOC_STATE_HALT_SUPPORT(instance) && + instance->recovery_context->heartbeat_recovery != + PS3_HEARTBEAT_HARDRESET_DECIDE) { + instance->ioc_adpter->ioc_force_to_halt(instance); + ps3_atomic_set(&state_machine->state, PS3_INSTANCE_STATE_DEAD); + LOG_WARN("hno:%u IOC support HALT, enter HALT!\n", + PS3_HOST(instance)); + while (1) + ps3_msleep(10); + ret = PS3_TRUE; + } +l_out: + + return ret; +} + +static inline int +ps3_hard_recovery_request_decide(struct ps3_instance *instance, + unsigned int cur_state, + unsigned int peer_cur_state) +{ + int ret = PS3_RECOVERY_INTER_ERR_SUCCESS; + + if (instance->recovery_context->recovery_state == + PS3_HARD_RECOVERY_SHALLOW || + instance->recovery_context->recovery_state == + PS3_HARD_RECOVERY_DECIDE) { + LOG_INFO("hno:%u instance state during hard recovery!\n", + PS3_HOST(instance)); + ret = PS3_RECOVERY_INTER_ERR_SUCCESS; + goto l_out; + } + + if ((cur_state == PS3_INSTANCE_STATE_OPERATIONAL) || + (cur_state == PS3_INSTANCE_STATE_SOFT_RECOVERY) || + (cur_state == PS3_INSTANCE_STATE_PRE_OPERATIONAL) || + (cur_state == PS3_INSTANCE_STATE_DEAD) || + (cur_state == PS3_INSTANCE_STATE_PCIE_RECOVERY) || + (cur_state == PS3_INSTANCE_STATE_INIT)) { + if (instance->is_need_event) { + if (instance->event_context.event_abort_cmd != NULL) { + complete(&instance->event_context + .event_abort_cmd->sync_done); + } else if (instance->dev_context.vdpending_abort_cmd != + NULL) { + complete(&instance->dev_context + .vdpending_abort_cmd + ->sync_done); + } else if (instance->webSubscribe_context + .web_abort_cmd != NULL) { + complete(&instance->webSubscribe_context + .web_abort_cmd->sync_done); + } + } + + if (instance->peer_instance == NULL) { + instance->recovery_context->recovery_state = + PS3_HARD_RECOVERY_DECIDE; + LOG_INFO("hno:%u instance state to recovery!\n", + PS3_HOST(instance)); + ret = PS3_RECOVERY_INTER_ERR_NEED_RECOVERY; + } + } else { + LOG_INFO("hno:%u instance state during hard recovery!\n", + PS3_HOST(instance)); + ret = PS3_RECOVERY_INTER_ERR_SUCCESS; + goto l_out; + } + + if (instance->peer_instance != NULL) { + if ((peer_cur_state == PS3_INSTANCE_STATE_OPERATIONAL) || + (peer_cur_state == PS3_INSTANCE_STATE_SOFT_RECOVERY) || + (peer_cur_state == PS3_INSTANCE_STATE_PRE_OPERATIONAL) || + (peer_cur_state == PS3_INSTANCE_STATE_DEAD) || + (peer_cur_state == PS3_INSTANCE_STATE_PCIE_RECOVERY) || + (peer_cur_state == PS3_INSTANCE_STATE_INIT)) { + if (instance->peer_instance->is_need_event) { + if (instance->peer_instance->event_context + .event_abort_cmd != NULL) { + complete(&instance->peer_instance + ->event_context + .event_abort_cmd + ->sync_done); + } else if (instance->peer_instance->dev_context + .vdpending_abort_cmd != + NULL) { + complete(&instance->peer_instance + ->dev_context + .vdpending_abort_cmd + ->sync_done); + } else if (instance->webSubscribe_context + .web_abort_cmd != NULL) { + complete(&instance->webSubscribe_context + .web_abort_cmd + ->sync_done); + } + } + + instance->peer_instance->recovery_context + ->recovery_state = PS3_HARD_RECOVERY_DECIDE; + + LOG_INFO("hno:%u instance state to recovery!\n", + PS3_HOST(instance->peer_instance)); + ret = PS3_RECOVERY_INTER_ERR_NEED_RECOVERY; + } else { + LOG_INFO( + "hno:%u instance state during hard recovery!\n", + PS3_HOST(instance->peer_instance)); + ret = PS3_RECOVERY_INTER_ERR_SUCCESS; + } + } +l_out: + return ret; +} + +static int ps3_hard_recovery_request_prepare(struct ps3_instance *instance) +{ + int ret = PS3_RECOVERY_INTER_ERR_SUCCESS; + unsigned int ioc_state = 0; + struct ps3_instance_state_machine *state_machine = + &instance->state_machine; + unsigned int cur_state = 0; + struct ps3_instance_state_machine *state_machine_peer = NULL; + unsigned int peer_cur_state = PS3_INSTANCE_STATE_OPERATIONAL; + + if (instance->reg_set == NULL) + return ret; + ioc_state = instance->ioc_adpter->ioc_state_get(instance); + ps3_mutex_lock(&state_machine->lock); + cur_state = ps3_atomic_read(&state_machine->state); + + LOG_INFO("hno:%u hard recovery request:%s, %s\n", PS3_HOST(instance), + ps3_ioc_state_print(ioc_state), + namePS3InstanceState(cur_state)); + + if (instance->peer_instance != NULL) { + state_machine_peer = &instance->peer_instance->state_machine; + if (instance->peer_instance->reg_set == NULL) + goto l_clean; + ioc_state = instance->peer_instance->ioc_adpter->ioc_state_get( + instance->peer_instance); + ps3_mutex_lock(&state_machine_peer->lock); + peer_cur_state = ps3_atomic_read(&state_machine_peer->state); + + LOG_INFO("hno:%u hard recovery request:%s, %s\n", + PS3_HOST(instance->peer_instance), + ps3_ioc_state_print(ioc_state), + namePS3InstanceState(peer_cur_state)); + } + + ps3_mutex_lock( + &instance->recovery_context->ps3_watchdog_recovery_mutex); + if (instance->recovery_context->heartbeat_recovery == + PS3_HEARTBEAT_HARDRESET_DECIDE) { + ret = ps3_hard_recovery_request_decide(instance, cur_state, + peer_cur_state); + if (ret == PS3_RECOVERY_INTER_ERR_NEED_RECOVERY) { + LOG_INFO("hno:%u heartbeat hard recovery start\n", + PS3_HOST(instance)); + instance->recovery_context->heartbeat_recovery = + PS3_HEARTBEAT_HARDRESET_RECOVERY; + } + ps3_mutex_unlock(&instance->recovery_context + ->ps3_watchdog_recovery_mutex); + goto l_out; + } + ps3_mutex_unlock( + &instance->recovery_context->ps3_watchdog_recovery_mutex); + + ret = ps3_hard_recovery_request_decide(instance, cur_state, + peer_cur_state); + +l_out: + if (instance->peer_instance != NULL) + ps3_mutex_unlock(&state_machine_peer->lock); +l_clean: + ps3_mutex_unlock(&state_machine->lock); + return ret; +} + +int ps3_hard_recovery_request(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + struct ps3_instance *instance_recovery = NULL; + + LOG_WARN( + "hno:%u ps3 hard recovery request start, IOC outstanding: %d!!\n", + PS3_HOST(instance), + ps3_atomic_read(&instance->cmd_statistics.cmd_outstanding)); + ps3_atomic_inc(&instance->recovery_context->hardreset_ref); + mb(); /* in order to force CPU ordering */ + + if (instance->recovery_context->instance_change) { + LOG_INFO("hno:%u peer instance is change\n", + PS3_HOST(instance)); + ret = -PS3_RETRY; + goto l_out; + } + + while (instance->peer_instance != NULL && + PS3_IS_INSTANCE_NOT_LOAD_NORMAL(instance->peer_instance)) { + if (instance->recovery_context->recovery_state == + PS3_HARD_RECOVERY_SHALLOW || + instance->recovery_context->recovery_state == + PS3_HARD_RECOVERY_DECIDE) { + instance->recovery_context->parall_hardreset_state = + PS3_PARALLEL_HARDRESET_STATE_INIT; + goto l_out; + } + + if (instance->recovery_context->instance_change) { + LOG_INFO("hno:%u peer instance is change\n", + PS3_HOST(instance)); + ret = -PS3_RETRY; + goto l_out; + } + + if (instance->recovery_context->parall_hardreset_state == + PS3_PARALLEL_HARDRESET_STATE_INIT) { + instance->recovery_context->parall_hardreset_state = + PS3_PARALLEL_HARDRESET_STATE_PENDING; + } + ps3_atomic_dec(&instance->recovery_context->hardreset_ref); + ps3_msleep(10); + if (instance->recovery_context->parall_hardreset_state == + PS3_PARALLEL_HARDRESET_STATE_CONTINUE) { + ps3_atomic_inc( + &instance->recovery_context->hardreset_ref); + mb(); /* in order to force CPU ordering */ + instance->recovery_context->parall_hardreset_state = + PS3_PARALLEL_HARDRESET_STATE_INIT; + break; + } + ps3_atomic_inc(&instance->recovery_context->hardreset_ref); + mb(); /* in order to force CPU ordering */ + } + + if (instance->recovery_context->parall_hardreset_state == + PS3_PARALLEL_HARDRESET_STATE_PENDING) { + instance->recovery_context->parall_hardreset_state = + PS3_PARALLEL_HARDRESET_STATE_INIT; + } + LOG_INFO( + "hno:%u peer_instance[%p], hard recovery request Function[%d]\n", + PS3_HOST(instance), instance->peer_instance, + ps3_get_pci_function(instance->pdev)); + if (ps3_get_pci_function(instance->pdev) == PS3_FUNC_ID_0 || + (ps3_get_pci_function(instance->pdev) == PS3_FUNC_ID_1 && + instance->peer_instance == NULL)) { + instance_recovery = instance; + } else { + instance_recovery = instance->peer_instance; + } + + if (ps3_recovery_is_state_halt(instance_recovery)) { + LOG_WARN("hno:%u driver_state:DEAD or HALT now !!!\n", + PS3_HOST(instance_recovery)); + ret = -PS3_FAILED; + goto l_out; + } + + if ((!PS3_IOC_HARD_RECOVERY_SUPPORT(instance_recovery)) || + (!ps3_hard_reset_enable_query())) { + LOG_ERROR( + "hno:%u soc feature unsupport Hard reset[%d] or unable[%d]!!\n", + PS3_HOST(instance_recovery), + PS3_IOC_HARD_RECOVERY_SUPPORT(instance_recovery), + ps3_hard_reset_enable_query()); + ret = -PS3_FAILED; + goto l_out; + } + + ret = ps3_hard_recovery_request_prepare(instance_recovery); + if (ret == PS3_RECOVERY_INTER_ERR_SUCCESS) + ret = PS3_SUCCESS; + else if (ret == PS3_RECOVERY_INTER_ERR_NEED_RECOVERY) + ret = ps3_recovery_start(instance_recovery); + else + ret = -PS3_FAILED; + +l_out: + LOG_INFO("hno:%u hard recovery request end, ret:%d!\n", + PS3_HOST(instance), ret); + + ps3_atomic_dec(&instance->recovery_context->hardreset_ref); + return ret; +} + +static inline int +ps3_ioc_soft_recovery_request_decide(struct ps3_instance *instance, + unsigned int ioc_state) +{ + unsigned int ioc_recovery_count = 0; + int ret = PS3_RECOVERY_INTER_ERR_SUCCESS; + struct ps3_instance_state_machine *state_machine = + &instance->state_machine; + int recovery_state = 0; + + recovery_state = instance->recovery_context->recovery_state; + if (recovery_state == PS3_HARD_RECOVERY_SHALLOW || + recovery_state == PS3_HARD_RECOVERY_DECIDE) { + LOG_INFO( + "hno:%u instance state %s,recovery_state %d,hard reset doing, IOC soft return!\n", + PS3_HOST(instance), + namePS3InstanceState( + ps3_atomic_read(&state_machine->state)), + recovery_state); + ret = PS3_RECOVERY_INTER_ERR_INTERRUPT; + goto l_out; + } else if (recovery_state == PS3_SOFT_RECOVERY_SHALLOW || + recovery_state == PS3_SOFT_RECOVERY_DEEP || + recovery_state == PS3_SOFT_RECOVERY_IOC_RECOVERY) { + LOG_INFO( + "hno:%u instance state %s,recovery_state %d,repeat request!\n", + PS3_HOST(instance), + namePS3InstanceState( + ps3_atomic_read(&state_machine->state)), + recovery_state); + goto l_out; + } + if (!ps3_ioc_recovery_count_get(instance, &ioc_recovery_count)) { + LOG_INFO( + "hno:%u entry IOC soft recovery request decide!,ioc_state:%s,\n" + "\tsave_recovery_count:%d, ioc_recovery_count:%d\n", + PS3_HOST(instance), ps3_ioc_state_print(ioc_state), + instance->recovery_context->ioc_recovery_count, + ioc_recovery_count); + ret = PS3_RECOVERY_INTER_ERR_FAILED; + goto l_out; + } + + LOG_INFO("hno:%u entry IOC soft recovery request decide!,ioc_state:%s,\n" + "\tsave_recovery_count:%d, ioc_recovery_count:%d\n", + PS3_HOST(instance), ps3_ioc_state_print(ioc_state), + instance->recovery_context->ioc_recovery_count, + ioc_recovery_count); + + if (instance->recovery_context->ioc_recovery_count == + ioc_recovery_count) { + goto l_out; + } + + if (ps3_instance_no_lock_state_transfer( + instance, PS3_INSTANCE_STATE_SOFT_RECOVERY) != + PS3_SUCCESS) { + LOG_ERROR("hno:%u transfer to ready failed!\n", + PS3_HOST(instance)); + ret = PS3_RECOVERY_INTER_ERR_FAILED; + goto l_out; + } + + if (instance->peer_instance != NULL) { + if (ps3_instance_no_lock_state_transfer( + instance->peer_instance, + PS3_INSTANCE_STATE_SOFT_RECOVERY) != PS3_SUCCESS) { + LOG_ERROR("hno:%u transfer to ready failed!\n", + PS3_HOST(instance->peer_instance)); + ret = PS3_RECOVERY_INTER_ERR_FAILED; + goto l_out; + } + } + instance->recovery_context->recovery_state = + PS3_SOFT_RECOVERY_IOC_RECOVERY; + + ret = PS3_RECOVERY_INTER_ERR_NEED_RECOVERY; +l_out: + return ret; +} + +static inline int +ps3_soft_recovery_request_decide(struct ps3_instance *instance, + unsigned int ioc_state) +{ + int ret = PS3_RECOVERY_INTER_ERR_SUCCESS; + struct ps3_instance_state_machine *state_machine = + &instance->state_machine; + int recovery_state = instance->recovery_context->recovery_state; + + if (recovery_state == PS3_HARD_RECOVERY_SHALLOW || + recovery_state == PS3_HARD_RECOVERY_DECIDE) { + LOG_INFO( + "hno:%u instance state %s,recovery_state %d,repeat request!\n", + PS3_HOST(instance), + namePS3InstanceState( + ps3_atomic_read(&state_machine->state)), + recovery_state); + ret = PS3_RECOVERY_INTER_ERR_INTERRUPT; + goto l_out; + } else if (recovery_state == PS3_SOFT_RECOVERY_SHALLOW || + recovery_state == PS3_SOFT_RECOVERY_DEEP || + recovery_state == PS3_SOFT_RECOVERY_IOC_RECOVERY) { + LOG_INFO( + "hno:%u instance state %s,recovery_state %d,repeat request!\n", + PS3_HOST(instance), + namePS3InstanceState( + ps3_atomic_read(&state_machine->state)), + recovery_state); + goto l_out; + } + + if (ioc_state == PS3_FW_STATE_RUNNING) { + if (ps3_instance_no_lock_state_transfer( + instance, PS3_INSTANCE_STATE_SOFT_RECOVERY) != + PS3_SUCCESS) { + LOG_ERROR("hno:%u transfer to ready failed!\n", + PS3_HOST(instance)); + ret = PS3_RECOVERY_INTER_ERR_FAILED; + goto l_out; + } + + if (instance->peer_instance != NULL) { + if (ps3_instance_no_lock_state_transfer( + instance->peer_instance, + PS3_INSTANCE_STATE_SOFT_RECOVERY) != + PS3_SUCCESS) { + LOG_ERROR("hno:%u transfer to ready failed!\n", + PS3_HOST(instance->peer_instance)); + ret = PS3_RECOVERY_INTER_ERR_FAILED; + goto l_out; + } + } + instance->recovery_context->recovery_state = + PS3_SOFT_RECOVERY_SHALLOW; + + LOG_INFO("hno:%u instance state to soft recovery shallow!\n", + PS3_HOST(instance)); + + ret = PS3_RECOVERY_INTER_ERR_NEED_RECOVERY; + } +l_out: + return ret; +} + +static int ps3_recovery_request_prepare(struct ps3_instance *instance) +{ + int ret = PS3_RECOVERY_INTER_ERR_SUCCESS; + unsigned int ioc_state = 0; + struct ps3_instance_state_machine *state_machine = + &instance->state_machine; + unsigned int cur_state = 0; + struct ps3_instance_state_machine *state_machine_peer = NULL; + unsigned int peer_cur_state = PS3_INSTANCE_STATE_OPERATIONAL; + + ps3_mutex_lock(&state_machine->lock); + ioc_state = instance->ioc_adpter->ioc_state_get(instance); + cur_state = ps3_atomic_read(&state_machine->state); + + LOG_INFO("hno:%u recovery request:%s, %s\n", PS3_HOST(instance), + ps3_ioc_state_print(ioc_state), + namePS3InstanceState(cur_state)); + if (ps3_pci_err_recovery_get(instance) || + (instance->peer_instance != NULL && + ps3_pci_err_recovery_get(instance->peer_instance))) { + LOG_WARN("hno:%u pci recovery resetting\n", + PS3_HOST(instance->peer_instance)); + ret = PS3_RECOVERY_INTER_ERR_SUCCESS; + goto l_out; + } + + ps3_mutex_lock( + &instance->recovery_context->ps3_watchdog_recovery_mutex); + if (instance->recovery_context->heartbeat_recovery == + PS3_HEARTBEAT_HARDRESET_DECIDE) { + ret = ps3_hard_recovery_request_decide(instance, cur_state, + peer_cur_state); + if (ret == PS3_RECOVERY_INTER_ERR_NEED_RECOVERY) { + LOG_INFO("hno:%u heartbeat recovery start\n", + PS3_HOST(instance)); + instance->recovery_context->heartbeat_recovery = + PS3_HEARTBEAT_HARDRESET_RECOVERY; + } + ps3_mutex_unlock(&instance->recovery_context + ->ps3_watchdog_recovery_mutex); + goto l_out; + } + ps3_mutex_unlock( + &instance->recovery_context->ps3_watchdog_recovery_mutex); + + if (ioc_state == PS3_FW_STATE_HALT) { + ps3_instance_state_transfer_to_dead_nolock(instance); + if (instance->peer_instance != NULL) { + ps3_instance_state_transfer_to_dead_nolock( + instance->peer_instance); + } + LOG_ERROR( + "hno:%u IOC state has halt, instance state to dead!\n", + PS3_HOST(instance)); + ret = PS3_RECOVERY_INTER_ERR_FAILED; + goto l_out; + } + + if (cur_state == PS3_INSTANCE_STATE_DEAD || + cur_state == PS3_INSTANCE_STATE_QUIT || + cur_state == PS3_INSTANCE_STATE_SUSPEND) { + LOG_ERROR( + "hno:%u instance state is %s, and ioc_state is %s!\n", + PS3_HOST(instance), namePS3InstanceState(cur_state), + ps3_ioc_state_print(ioc_state)); + ret = PS3_RECOVERY_INTER_ERR_FAILED; + goto l_out; + } + + if (instance->peer_instance != NULL) { + state_machine_peer = &instance->peer_instance->state_machine; + if (ps3_pci_err_recovery_get(instance->peer_instance)) { + LOG_WARN("hno:%u pci recovery resetting\n", + PS3_HOST(instance->peer_instance)); + ret = PS3_RECOVERY_INTER_ERR_SUCCESS; + goto l_out; + } + ps3_mutex_lock(&state_machine_peer->lock); + ioc_state = instance->peer_instance->ioc_adpter->ioc_state_get( + instance); + peer_cur_state = ps3_atomic_read(&state_machine->state); + LOG_INFO("hno:%u recovery request:%s, %s\n", + PS3_HOST(instance->peer_instance), + ps3_ioc_state_print(ioc_state), + namePS3InstanceState(peer_cur_state)); + + if (ioc_state == PS3_FW_STATE_HALT) { + ps3_instance_state_transfer_to_dead_nolock( + instance->peer_instance); + ps3_instance_state_transfer_to_dead_nolock(instance); + LOG_ERROR( + "hno:%u ioc state has halt, instance state to dead!\n", + PS3_HOST(instance->peer_instance)); + ret = PS3_RECOVERY_INTER_ERR_FAILED; + goto l_out_peer; + } + if (peer_cur_state == PS3_INSTANCE_STATE_DEAD || + peer_cur_state == PS3_INSTANCE_STATE_QUIT || + peer_cur_state == PS3_INSTANCE_STATE_SUSPEND) { + LOG_ERROR( + "hno:%u instance state is %s, and ioc_state is %s!\n", + PS3_HOST(instance->peer_instance), + namePS3InstanceState(peer_cur_state), + ps3_ioc_state_print(ioc_state)); + ret = PS3_RECOVERY_INTER_ERR_FAILED; + goto l_out_peer; + } + } + + if (ioc_state == PS3_FW_STATE_FAULT || + ioc_state == PS3_FW_STATE_CRITICAL) { + LOG_ERROR( + "hno:%u instance state is %s, and ioc_state is %s!\n", + PS3_HOST(instance), + namePS3InstanceState(peer_cur_state), + ps3_ioc_state_print(ioc_state)); + ret = ps3_hard_recovery_request_decide(instance, cur_state, + peer_cur_state); + goto l_out_peer; + } + + if (cur_state != PS3_INSTANCE_STATE_OPERATIONAL || + peer_cur_state != PS3_INSTANCE_STATE_OPERATIONAL) { + LOG_INFO( + "hno:%u instance state is %s:%s! no need repeat recovery requeset\n", + PS3_HOST(instance), namePS3InstanceState(cur_state), + namePS3InstanceState(cur_state)); + ret = PS3_RECOVERY_INTER_ERR_SUCCESS; + goto l_out_peer; + } + + ret = ps3_ioc_soft_recovery_request_decide(instance, ioc_state); + if (ret == PS3_RECOVERY_INTER_ERR_NEED_RECOVERY || + ret == PS3_RECOVERY_INTER_ERR_INTERRUPT) { + goto l_out_peer; + } else if (ret == PS3_RECOVERY_INTER_ERR_FAILED) { + goto l_hardreset; + } + + ret = ps3_soft_recovery_request_decide(instance, ioc_state); + if (ret == PS3_RECOVERY_INTER_ERR_NEED_RECOVERY || + ret == PS3_RECOVERY_INTER_ERR_SUCCESS || + ret == PS3_RECOVERY_INTER_ERR_INTERRUPT) { + goto l_out_peer; + } else if (ret == PS3_RECOVERY_INTER_ERR_FAILED) { + goto l_hardreset; + } + + LOG_ERROR("hno:%u UNEXPECT!!! hard recovery!,ioc_state:%s, %s:%s\n", + PS3_HOST(instance), ps3_ioc_state_print(ioc_state), + namePS3InstanceState(cur_state), + namePS3InstanceState(peer_cur_state)); + +l_hardreset: + ret = ps3_hard_recovery_request_decide(instance, cur_state, + peer_cur_state); +l_out_peer: + if (instance->peer_instance != NULL) + ps3_mutex_unlock(&state_machine_peer->lock); +l_out: + ps3_mutex_unlock(&state_machine->lock); + return ret; +} + +int ps3_recovery_request(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + struct ps3_instance *instance_recovery = NULL; + + LOG_WARN("hno:%u ps3 recovery request start, IOC outstanding: %d!!\n", + PS3_HOST(instance), + ps3_atomic_read(&instance->cmd_statistics.cmd_outstanding)); + ps3_atomic_inc(&instance->recovery_context->hardreset_ref); + mb(); /* in order to force CPU ordering */ + if (instance->recovery_context->instance_change) { + LOG_INFO("hno:%u peer instance is change\n", + PS3_HOST(instance)); + ret = -PS3_RETRY; + goto l_out; + } + while (instance->peer_instance != NULL && + PS3_IS_INSTANCE_NOT_LOAD_NORMAL(instance->peer_instance)) { + if (instance->recovery_context->recovery_state == + PS3_HARD_RECOVERY_SHALLOW || + instance->recovery_context->recovery_state == + PS3_HARD_RECOVERY_DECIDE) { + instance->recovery_context->parall_hardreset_state = + PS3_PARALLEL_HARDRESET_STATE_INIT; + goto l_out; + } + + if (instance->recovery_context->parall_hardreset_state == + PS3_PARALLEL_HARDRESET_STATE_INIT) { + instance->recovery_context->parall_hardreset_state = + PS3_PARALLEL_HARDRESET_STATE_PENDING; + } + if (instance->recovery_context->instance_change) { + LOG_INFO("hno:%u peer instance is change\n", + PS3_HOST(instance)); + ret = -PS3_RETRY; + goto l_out; + } + + ps3_atomic_dec(&instance->recovery_context->hardreset_ref); + ps3_msleep(10); + if (instance->recovery_context->parall_hardreset_state == + PS3_PARALLEL_HARDRESET_STATE_CONTINUE) { + ps3_atomic_inc( + &instance->recovery_context->hardreset_ref); + mb(); /* in order to force CPU ordering */ + instance->recovery_context->parall_hardreset_state = + PS3_PARALLEL_HARDRESET_STATE_INIT; + break; + } + ps3_atomic_inc(&instance->recovery_context->hardreset_ref); + mb(); /* in order to force CPU ordering */ + } + if (instance->recovery_context->parall_hardreset_state == + PS3_PARALLEL_HARDRESET_STATE_PENDING) { + instance->recovery_context->parall_hardreset_state = + PS3_PARALLEL_HARDRESET_STATE_INIT; + } + LOG_INFO( + "hno:%u peer_instance[%p], hard recovery request Function[%d]\n", + PS3_HOST(instance), instance->peer_instance, + ps3_get_pci_function(instance->pdev)); + if (ps3_get_pci_function(instance->pdev) == PS3_FUNC_ID_0 || + (ps3_get_pci_function(instance->pdev) == PS3_FUNC_ID_1 && + instance->peer_instance == NULL)) { + instance_recovery = instance; + } else { + instance_recovery = instance->peer_instance; + } + + if (ps3_pci_err_recovery_get(instance_recovery)) { + LOG_WARN("hno:%u pci recovery resetting\n", + PS3_HOST(instance_recovery)); + ret = PS3_SUCCESS; + goto l_out; + } + + if (ps3_recovery_is_state_halt(instance_recovery)) { + LOG_INFO_LIM("hno:%u driver_state:DEAD or HALT now !!!\n", + PS3_HOST(instance_recovery)); + ret = -PS3_FAILED; + goto l_out; + } + + if (instance_recovery->peer_instance != NULL) { + if (ps3_pci_err_recovery_get( + instance_recovery->peer_instance)) { + LOG_WARN("hno:%u pci recovery resetting\n", + PS3_HOST(instance_recovery->peer_instance)); + ret = PS3_SUCCESS; + goto l_out; + } + + if (instance_recovery->peer_instance->reg_set == NULL || + ps3_recovery_is_state_halt( + instance_recovery->peer_instance)) { + LOG_WARN("hno:%u driver_state:DEAD or HALT now !!!\n", + PS3_HOST(instance_recovery->peer_instance)); + ret = -PS3_FAILED; + goto l_out; + } + } + + ret = ps3_recovery_request_prepare(instance_recovery); + if (ret == PS3_RECOVERY_INTER_ERR_SUCCESS || + ret == PS3_RECOVERY_INTER_ERR_INTERRUPT) { + ret = PS3_SUCCESS; + } else if (ret == PS3_RECOVERY_INTER_ERR_NEED_RECOVERY) { + ret = ps3_recovery_start(instance_recovery); + } else { + ret = -PS3_FAILED; + } + +l_out: + LOG_INFO("hno:%u recovery request end, ret:%d!\n", PS3_HOST(instance), + ret); + ps3_atomic_dec(&instance->recovery_context->hardreset_ref); + return ret; +} + +static unsigned char ps3_recovery_reg(struct ps3_instance *instance) +{ + unsigned char ret = PS3_FALSE; + union HilReg0Ps3RegisterFPs3FeatureSupport *ps3_feature_support = NULL; + union HilReg0Ps3RegisterFPs3FirmwareVersion *pver = NULL; + unsigned int cur_max_fw_cmds = 0; + unsigned int fw_cur_state = PS3_FW_STATE_UNDEFINED; + unsigned long long value = 0; + unsigned long long ver = 0; + + PS3_IOC_REG_READ_WITH_CHECK(instance, reg_f.Excl_reg, ps3FeatureSupport, + value); + if (value == U64_MAX) + goto l_out; + ps3_feature_support = + (union HilReg0Ps3RegisterFPs3FeatureSupport *)&value; + PS3_IOC_REG_READ_WITH_CHECK(instance, reg_f.Excl_reg, + ps3FirmwareVersion, ver); + if (ver == U64_MAX) + goto l_out; + pver = (union HilReg0Ps3RegisterFPs3FirmwareVersion *)&ver; + if (!ps3_ioc_mgr_max_fw_cmd_get(instance, &cur_max_fw_cmds)) + goto l_out; + fw_cur_state = instance->ioc_adpter->ioc_state_get(instance); + if (fw_cur_state != PS3_FW_STATE_RUNNING) + goto l_out; + instance->is_ioc_halt_support = + (ps3_feature_support->reg.fwHaltSupport == 1); + instance->dump_context.is_dump_support = + (ps3_feature_support->reg.dumpCrashSupport == 1); + instance->is_shallow_soft_recovery_support = + (ps3_feature_support->reg.shallowSoftRecoverySupport == 1); + instance->is_deep_soft_recovery_support = + (ps3_feature_support->reg.deepSoftRecoverySupport == 1); + instance->is_hard_recovery_support = + (ps3_feature_support->reg.hardRecoverySupport == 1); + instance->ioc_fw_version = (unsigned long long)pver->reg.ps3FmVer; + ps3_can_queue_reset(instance, cur_max_fw_cmds); + ret = PS3_TRUE; +l_out: + return ret; +} + +static int ps3_recovery_finish(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + + if (!ps3_recovery_reg(instance)) + LOG_WARN("hno:%u recovery reg NOK\n", PS3_HOST(instance)); + return ret; +} + +static int ps3_recovery_complete(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + + if (ps3_pci_err_recovery_get(instance)) { + LOG_WARN("hno:%u pci recovery resetting\n", + PS3_HOST(instance)); + ret = -PS3_IN_PCIE_ERR; + goto l_out; + } + + if (PS3_IS_INSTANCE_NOT_LOAD_NORMAL(instance)) + goto l_out; + + if (ps3_recovery_finish(instance) != PS3_SUCCESS) { + ps3_instance_state_transfer_to_dead(instance); + LOG_ERROR("hno:%u recovery finish process failed, to DEAD\n", + PS3_HOST(instance)); + ret = -PS3_FAILED; + } + +l_out: + return ret; +} +static void ps3_hard_recovery(struct ps3_instance *instance) +{ + struct ps3_recovery_context *context = instance->recovery_context; + + if (ps3_hard_recovery_handle(instance) != PS3_SUCCESS) { + context->recovery_result = -PS3_FAILED; + LOG_ERROR("hno:%u hard recovery failed!\n", + PS3_HOST(instance)); + } else { + context->recovery_result = PS3_SUCCESS; + LOG_WARN("hno:%u hard recovery success!\n", + PS3_HOST(instance)); + } + ps3_mutex_lock( + &instance->recovery_context->ps3_watchdog_recovery_mutex); + instance->recovery_context->heartbeat_recovery = PS3_HEARTBEAT_NULL; + ps3_mutex_unlock( + &instance->recovery_context->ps3_watchdog_recovery_mutex); + +} + +#ifndef _WINDOWS +static void ps3_recovery_work(struct work_struct *work) +{ + struct ps3_recovery_context *context = ps3_container_of( + work, struct ps3_recovery_context, recovery_work); + struct ps3_instance *instance = context->work_instance; +#else +static void ps3_recovery_work(void *ins) +{ + struct ps3_instance *instance = (struct ps3_instance *)ins; + struct ps3_recovery_context *context = instance->recovery_context; +#endif + unsigned int cur_state = + ps3_atomic_read(&instance->state_machine.state); + int ret = PS3_RECOVERY_INTER_ERR_SUCCESS; + + LOG_INFO("hno:%u recovery work start, %s recovery state[%d]\n", + PS3_HOST(instance), namePS3InstanceState(cur_state), + context->recovery_state); + + if (ps3_pci_err_recovery_get(instance)) { + LOG_WARN("hno:%u pci recovery resetting\n", + PS3_HOST(instance)); + + + if (instance->recovery_context->recovery_state == + PS3_HARD_RECOVERY_DECIDE) { + ps3_recovery_state_transfer(instance, + PS3_HARD_RECOVERY_FINISH); + } else { + ps3_recovery_state_transfer(instance, + PS3_SOFT_RECOVERY_FINISH); + } + + ps3_mutex_lock(&instance->recovery_context + ->ps3_watchdog_recovery_mutex); + instance->recovery_context->heartbeat_recovery = + PS3_HEARTBEAT_NULL; + ps3_mutex_unlock(&instance->recovery_context + ->ps3_watchdog_recovery_mutex); + goto l_out; + } + + context->recovery_result = PS3_SUCCESS; + if (cur_state == PS3_INSTANCE_STATE_SOFT_RECOVERY) { + ps3_mutex_lock(&instance->state_machine.lock); + if (context->recovery_state == PS3_SOFT_RECOVERY_IOC_RECOVERY) { + ps3_mutex_unlock(&instance->state_machine.lock); + goto l_soft_reset; + } + + if (context->recovery_state == PS3_SOFT_RECOVERY_SHALLOW) { + ps3_mutex_unlock(&instance->state_machine.lock); + goto l_soft_reset; + } + + if (context->recovery_state == PS3_SOFT_RECOVERY_DEEP) { + ps3_mutex_unlock(&instance->state_machine.lock); + goto l_soft_reset; + } + + LOG_ERROR("hno:%u nothing to do %s,recovery_state %d!\n", + PS3_HOST(instance), namePS3InstanceState(cur_state), + context->recovery_state); + ps3_mutex_unlock(&instance->state_machine.lock); + if (ps3_instance_state_transfer( + instance, PS3_INSTANCE_STATE_SOFT_RECOVERY, + PS3_INSTANCE_STATE_RECOVERY) != PS3_SUCCESS) { + if (instance->peer_instance == NULL || + instance->peer_instance->recovery_context == NULL) { + context->recovery_result = -PS3_FAILED; + LOG_ERROR( + "hno:%u hard recovery NOK,recovery_state %d!\n", + PS3_HOST(instance), + context->recovery_state); + ps3_recovery_state_transfer( + instance, PS3_HARD_RECOVERY_FINISH); + goto l_out; + } + } + + if (instance->peer_instance != NULL && + ps3_instance_state_transfer( + instance->peer_instance, + PS3_INSTANCE_STATE_SOFT_RECOVERY, + PS3_INSTANCE_STATE_RECOVERY) != PS3_SUCCESS) { + context->recovery_result = -PS3_FAILED; + LOG_ERROR( + "hno:%u hard recovery NOK,recovery_state %d!\n", + PS3_HOST(instance->peer_instance), + context->recovery_state); + ps3_recovery_state_transfer(instance->peer_instance, + PS3_HARD_RECOVERY_FINISH); + goto l_out; + } + } else if (cur_state == PS3_INSTANCE_STATE_RECOVERY) { + goto l_hard_reset; + } else { + LOG_ERROR( + "hno:%u nothing to do %s,recovery_state %d, cur_state:%s!\n", + PS3_HOST(instance), namePS3InstanceState(cur_state), + context->recovery_state, + namePS3InstanceState(ps3_atomic_read( + &instance->state_machine.state))); + if (instance->recovery_context->recovery_state == + PS3_HARD_RECOVERY_DECIDE) { + ps3_recovery_state_transfer(instance, + PS3_HARD_RECOVERY_FINISH); + } else { + ps3_recovery_state_transfer(instance, + PS3_SOFT_RECOVERY_FINISH); + } + goto l_out; + } + +l_soft_reset: + ret = ps3_soft_recovery_handle(instance); + if (ret == PS3_RECOVERY_INTER_ERR_INTERRUPT) { + context->recovery_result = PS3_SUCCESS; + LOG_INFO("hno:%u soft recovery interrupt!\n", + PS3_HOST(instance)); + ps3_recovery_state_transfer(instance, PS3_SOFT_RECOVERY_FINISH); + } else if (ret == PS3_RECOVERY_INTER_ERR_SUCCESS) { + context->recovery_result = PS3_SUCCESS; + ps3_recovery_state_transfer(instance, PS3_SOFT_RECOVERY_FINISH); + LOG_WARN("hno:%u soft recovery success!\n", + PS3_HOST(instance)); + } else { + LOG_INFO("hno:%u soft recovery to hard recovery!\n", + PS3_HOST(instance)); + if (ps3_soft_recovery_fail_to_hard_recovery(instance) != + PS3_SUCCESS) { + context->recovery_result = -PS3_FAILED; + LOG_ERROR("hno:%u soft to hard recovery failed!\n", + PS3_HOST(instance)); + } else { + context->recovery_result = PS3_SUCCESS; + LOG_INFO("hno:%u soft to hard recovery success!\n", + PS3_HOST(instance)); + } + } + goto l_out; + +l_hard_reset: + ps3_hard_recovery(instance); + +l_out: + if (ps3_pci_err_recovery_get(instance)) { + LOG_INFO( + "hno:%u recovery is interrupted by pci err recovery.\n", + PS3_HOST(instance)); + ps3_instance_state_transfer_to_pcie_recovery(instance); + if (instance->peer_instance != NULL) { + ps3_instance_state_transfer_to_pcie_recovery( + instance->peer_instance); + } + } +} + +static int ps3_soft_recovery_cmd_reply_check(struct ps3_instance *instance) +{ + int ret = PS3_RECOVERY_INTER_ERR_SUCCESS; + unsigned int i = 0; + struct ps3_cmd *cmd = NULL; + struct ps3_cmd_context *context = &instance->cmd_context; + unsigned int no_reply_count = 0; + + for (i = context->max_scsi_cmd_count; i < context->max_cmd_count; i++) { + cmd = context->cmd_buf[i]; + + ps3_mutex_lock(&instance->state_machine.lock); + if (PS3_IS_INTERRUPT_SOFT_RECOVERY(instance)) { + LOG_WARN("hno:%u soft reset proc is interrupt!\n", + PS3_HOST(instance)); + ret = PS3_RECOVERY_INTER_ERR_INTERRUPT; + ps3_mutex_unlock(&instance->state_machine.lock); + goto l_out; + } + ps3_mutex_unlock(&instance->state_machine.lock); + + if (cmd->cmd_state.reset_flag != PS3_CMD_FLAG_SOFTRESET) + continue; + + if (cmd->cmd_state.state == PS3_CMD_STATE_INIT) + continue; + + if (cmd->req_frame->mgrReq.reqHead.noReplyWord == + PS3_CMD_WORD_NO_REPLY_WORD && + ps3_cmd_resp_status(cmd) != U32_MAX) { + continue; + } + if (cmd == instance->event_context.event_cmd || + cmd == instance->dev_context.vd_pending_cmd || + cmd == instance->webSubscribe_context.webSubscribe_cmd) { + continue; + } + + no_reply_count++; + LOG_DEBUG( + "hno:%u no reply cmd,CFID[%d], %s,max_scsi_cmd_count %u, max_cmd_count %u\n", + PS3_HOST(instance), i, + namePS3CmdState(cmd->cmd_state.state), + context->max_scsi_cmd_count, context->max_cmd_count); + } + + if (no_reply_count > 0) + ret = PS3_RECOVERY_INTER_ERR_FAILED; +l_out: + return ret; +} + +static int +ps3_soft_recovery_cmd_reply_polling_check(struct ps3_instance *instance, + unsigned int wait_seconds) +{ + int ret = PS3_RECOVERY_INTER_ERR_FAILED; + const unsigned int interval_check_ms = 20; + unsigned int msecs = U32_MAX; + unsigned int i = 0; + + if (wait_seconds != 0) { +#ifndef _WINDOWS + msecs = wait_seconds * HZ; +#else + msecs = wait_seconds * 1000; +#endif + } + + for (i = 0; (i < msecs); i += interval_check_ms) { + if (!ps3_is_instance_state_normal(instance, PS3_TRUE)) { + ret = PS3_RECOVERY_INTER_ERR_INTERRUPT; + break; + } + ps3_mutex_lock(&instance->state_machine.lock); + + if (PS3_IS_INTERRUPT_SOFT_RECOVERY(instance)) { + LOG_WARN("hno:%u soft reset proc is interrupt!\n", + PS3_HOST(instance)); + ret = PS3_RECOVERY_INTER_ERR_INTERRUPT; + ps3_mutex_unlock(&instance->state_machine.lock); + break; + } + ps3_mutex_unlock(&instance->state_machine.lock); + + if (ps3_pci_err_recovery_get(instance)) { + LOG_WARN("hno:%u host in pci err recovery\n", + PS3_HOST(instance)); + ret = PS3_RECOVERY_INTER_ERR_INTERRUPT; + break; + } + + ret = ps3_soft_recovery_cmd_reply_check(instance); + if (ret == PS3_RECOVERY_INTER_ERR_SUCCESS || + ret == PS3_RECOVERY_INTER_ERR_INTERRUPT) { + LOG_WARN("hno:%u no mgr cmd pending\n", + PS3_HOST(instance)); + break; + } + + ps3_msleep(interval_check_ms); + } + + return ret; +} + +static int ps3_soft_recovery_to_pre_operational(struct ps3_instance *instance) +{ + int ret = PS3_RECOVERY_INTER_ERR_SUCCESS; + + ps3_mutex_lock(&instance->state_machine.lock); + if (PS3_IS_INTERRUPT_SOFT_RECOVERY(instance)) { + ret = PS3_RECOVERY_INTER_ERR_INTERRUPT; + LOG_WARN("hno:%u soft reset proc is interrupt!\n", + PS3_HOST(instance)); + ps3_mutex_unlock(&instance->state_machine.lock); + goto l_out; + } + ps3_mutex_unlock(&instance->state_machine.lock); + + ps3_cmd_reset_flag_set(instance, PS3_CMD_FLAG_SOFTRESET); + + ret = ps3_ioc_state_transfer_wait_to_running(instance); + if (ret != PS3_SUCCESS) { + LOG_ERROR("hno:%u soft recovery to running failed!\n", + PS3_HOST(instance)); + ret = PS3_RECOVERY_INTER_ERR_FAILED; + goto l_out; + } + ret = ps3_soft_recovery_cmd_reply_polling_check( + instance, PS3_SOFT_RESET_WAIT_TIMEOUT); + if (ret != PS3_RECOVERY_INTER_ERR_SUCCESS) { + LOG_ERROR("hno:%u pending mgr cmd no reply all!\n", + PS3_HOST(instance)); + goto l_out; + } + + ret = PS3_RECOVERY_INTER_ERR_SUCCESS; + LOG_INFO("hno:%u soft recovery to pre-operational success!\n", + PS3_HOST(instance)); +l_out: + return ret; +} + +static int +ps3_hard_recovery_pre_operational_to_operational(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + + if (!ps3_ioc_recovery_count_get( + instance, + &instance->recovery_context->ioc_recovery_count)) { + LOG_ERROR("hno:%u get recovery count NOK\n", + PS3_HOST(instance)); + ret = -PS3_FAILED; + goto l_out; + } + + LOG_INFO("hno:%u pre-operational to operational success!\n", + PS3_HOST(instance)); +l_out: + return ret; +} + +static int +ps3_soft_recovery_pre_operational_to_operational(struct ps3_instance *instance) +{ + int ret = PS3_RECOVERY_INTER_ERR_SUCCESS; + + ps3_mutex_lock(&instance->state_machine.lock); + if (PS3_IS_INTERRUPT_SOFT_RECOVERY(instance)) { + ret = PS3_RECOVERY_INTER_ERR_INTERRUPT; + LOG_WARN("hno:%u soft reset proc is interrupt!\n", + PS3_HOST(instance)); + ps3_mutex_unlock(&instance->state_machine.lock); + goto l_out; + } + ps3_mutex_unlock(&instance->state_machine.lock); + if (!ps3_ioc_recovery_count_get( + instance, + &instance->recovery_context->ioc_recovery_count)) { + ret = PS3_RECOVERY_INTER_ERR_FAILED; + goto l_out; + } + + LOG_INFO( + "hno:%u pre-operational to operational success!,reset count:%d\n", + PS3_HOST(instance), + instance->recovery_context->ioc_recovery_count); +l_out: + return ret; +} +static int ps3_cmd_resubscribe(struct ps3_instance *instance) +{ + int ret = PS3_RECOVERY_INTER_ERR_SUCCESS; + + ps3_mutex_lock(&instance->state_machine.lock); + if (PS3_IS_INTERRUPT_SOFT_RECOVERY(instance)) { + LOG_WARN("hno:%u soft reset proc is interrupt!\n", + PS3_HOST(instance)); + ret = PS3_RECOVERY_INTER_ERR_INTERRUPT; + ps3_mutex_unlock(&instance->state_machine.lock); + goto l_out; + } + ps3_mutex_unlock(&instance->state_machine.lock); + ret = ps3_soft_reset_event_resubscribe(instance); + if (ret != PS3_SUCCESS) { + LOG_ERROR("hno:%u event unsubscribe failed!\n", + PS3_HOST(instance)); + ret = PS3_RECOVERY_INTER_ERR_FAILED; + goto l_out; + } + + ps3_mutex_lock(&instance->state_machine.lock); + if (PS3_IS_INTERRUPT_SOFT_RECOVERY(instance)) { + LOG_WARN("hno:%u soft reset proc is interrupt!\n", + PS3_HOST(instance)); + ret = PS3_RECOVERY_INTER_ERR_INTERRUPT; + ps3_mutex_unlock(&instance->state_machine.lock); + goto l_out; + } + ps3_mutex_unlock(&instance->state_machine.lock); + + ret = ps3_dev_mgr_vd_info_resubscribe(instance); + if (ret != PS3_SUCCESS) { + LOG_ERROR("hno:%u vd pending unsubscribe failed!\n", + PS3_HOST(instance)); + ret = PS3_RECOVERY_INTER_ERR_FAILED; + goto l_out; + } + + ps3_mutex_lock(&instance->state_machine.lock); + if (PS3_IS_INTERRUPT_SOFT_RECOVERY(instance)) { + LOG_WARN("hno:%u soft reset proc is interrupt!\n", + PS3_HOST(instance)); + ret = PS3_RECOVERY_INTER_ERR_INTERRUPT; + ps3_mutex_unlock(&instance->state_machine.lock); + goto l_out; + } + ps3_mutex_unlock(&instance->state_machine.lock); + + if (ps3_atomic_read(&instance->webSubscribe_context.is_subscribe) == + 1) { + ret = ps3_soft_reset_web_resubscribe(instance); + if (ret != PS3_SUCCESS) { + LOG_ERROR("hno:%u web unsubscribe failed!\n", + PS3_HOST(instance)); + ret = PS3_RECOVERY_INTER_ERR_FAILED; + goto l_out; + } + } + + ret = PS3_RECOVERY_INTER_ERR_SUCCESS; +l_out: + return ret; +} +static int ps3_soft_recovery_complete(struct ps3_instance *instance) +{ + int ret = ps3_soft_recovery_to_pre_operational(instance); + + if (ret != PS3_RECOVERY_INTER_ERR_SUCCESS) { + LOG_WARN("hno:%u ioc soft reset failed!\n", + PS3_HOST(instance)); + goto l_out; + } + + if (ps3_instance_state_transfer( + instance, PS3_INSTANCE_STATE_SOFT_RECOVERY, + PS3_INSTANCE_STATE_PRE_OPERATIONAL) != PS3_SUCCESS) { + LOG_ERROR("hno:%u transfer to pre operational failed!\n", + PS3_HOST(instance)); + ret = PS3_RECOVERY_INTER_ERR_INTERRUPT; + goto l_out; + } + + if (instance->peer_instance != NULL && + instance->peer_instance->recovery_function.softreset_handle_pre_cb != + NULL) { + ret = instance->peer_instance->recovery_function + .softreset_handle_pre_cb(instance->peer_instance); + if (ret != PS3_RECOVERY_INTER_ERR_SUCCESS) { + LOG_WARN("hno:%u softreset handle pre cb failed\n", + PS3_HOST(instance->peer_instance)); + goto l_out; + } + + if (ps3_instance_state_transfer( + instance->peer_instance, + PS3_INSTANCE_STATE_SOFT_RECOVERY, + PS3_INSTANCE_STATE_PRE_OPERATIONAL) != + PS3_SUCCESS) { + LOG_ERROR( + "hno:%u transfer to pre operational failed!\n", + PS3_HOST(instance->peer_instance)); + ret = PS3_RECOVERY_INTER_ERR_INTERRUPT; + goto l_out; + } + } + + ret = ps3_soft_recovery_pre_operational_to_operational(instance); + if (ret != PS3_RECOVERY_INTER_ERR_SUCCESS) { + LOG_WARN("hno:%u ioc soft reset failed!\n", + PS3_HOST(instance)); + goto l_out; + } + + if (instance->peer_instance != NULL && + instance->peer_instance->recovery_function + .softreset_handle_post_cb != NULL) { + ret = instance->peer_instance->recovery_function + .softreset_handle_post_cb( + instance->peer_instance); + if (ret != PS3_RECOVERY_INTER_ERR_SUCCESS) { + LOG_WARN("hno:%u softreset handle post cb failed\n", + PS3_HOST(instance->peer_instance)); + goto l_out; + } + } + +l_out: + return ret; +} + +static int +ps3_soft_recovery_fail_to_hard_recovery(struct ps3_instance *instance) +{ + int ret = -PS3_FAILED; + unsigned int cur_state = + ps3_atomic_read(&instance->state_machine.state); + int ret_peer = PS3_SUCCESS; + unsigned int peer_cur_state = PS3_INSTANCE_STATE_OPERATIONAL; + + ps3_mutex_lock(&instance->state_machine.lock); + + if (instance->peer_instance != NULL) { + ps3_mutex_lock(&instance->peer_instance->state_machine.lock); + peer_cur_state = ps3_atomic_read( + &instance->peer_instance->state_machine.state); + } + + ret = ps3_hard_recovery_request_decide(instance, cur_state, + peer_cur_state); + if (instance->peer_instance != NULL) + ps3_mutex_unlock(&instance->peer_instance->state_machine.lock); + ps3_mutex_unlock(&instance->state_machine.lock); + if (ret != PS3_RECOVERY_INTER_ERR_NEED_RECOVERY) { + LOG_WARN("hno:%u decide no need to hard recovery\n", + PS3_HOST(instance)); + ret = PS3_SUCCESS; + goto l_out; + } + + ret = ps3_instance_state_transfer(instance, + PS3_INSTANCE_STATE_SOFT_RECOVERY, + PS3_INSTANCE_STATE_RECOVERY); + if (ret != PS3_SUCCESS) { + ret = ps3_instance_state_transfer( + instance, PS3_INSTANCE_STATE_PRE_OPERATIONAL, + PS3_INSTANCE_STATE_RECOVERY); + } + if (ret != PS3_SUCCESS) { + ret = ps3_instance_state_transfer( + instance, PS3_INSTANCE_STATE_OPERATIONAL, + PS3_INSTANCE_STATE_RECOVERY); + } + + if (instance->peer_instance != NULL) { + ret_peer = ps3_instance_state_transfer( + instance->peer_instance, + PS3_INSTANCE_STATE_SOFT_RECOVERY, + PS3_INSTANCE_STATE_RECOVERY); + if (ret_peer != PS3_SUCCESS) { + ret_peer = ps3_instance_state_transfer( + instance->peer_instance, + PS3_INSTANCE_STATE_PRE_OPERATIONAL, + PS3_INSTANCE_STATE_RECOVERY); + } + if (ret_peer != PS3_SUCCESS) { + ret_peer = ps3_instance_state_transfer( + instance->peer_instance, + PS3_INSTANCE_STATE_OPERATIONAL, + PS3_INSTANCE_STATE_RECOVERY); + } + } + + if (ret == PS3_SUCCESS && ret_peer == PS3_SUCCESS) { + ps3_need_wait_hard_reset_request(instance); + ret = ps3_hard_recovery_handle(instance); + } else { + LOG_ERROR( + "hno:%u transfer to recovery failed!, cur_state:%s\n", + PS3_HOST(instance), + namePS3InstanceState(ps3_atomic_read( + &instance->state_machine.state))); + ps3_recovery_state_transfer(instance, PS3_SOFT_RECOVERY_FINISH); + } + + if (ret != PS3_SUCCESS) { + LOG_ERROR("hno:%u hard reset failed!\n", PS3_HOST(instance)); + } else { + LOG_INFO("hno:%u hard reset success and finished!\n", + PS3_HOST(instance)); + } +l_out: + return ret; +} + +static int ps3_ioc_soft_recovery(struct ps3_instance *instance) +{ + int ret = PS3_RECOVERY_INTER_ERR_SUCCESS; + + ret = ps3_soft_recovery_complete(instance); + if (ret == PS3_RECOVERY_INTER_ERR_INTERRUPT) { + LOG_WARN("hno:%u IOC soft reset proc is interrupt!\n", + PS3_HOST(instance)); + goto l_out; + } else if (ret == PS3_RECOVERY_INTER_ERR_SUCCESS) { + LOG_INFO("hno:%u IOC soft reset success!\n", + PS3_HOST(instance)); + goto l_reset_success; + } else { + LOG_ERROR( + "hno:%u IOC self soft reset failed! need to hard reset\n", + PS3_HOST(instance)); + goto l_out; + } + +l_reset_success: + ret = ps3_instance_state_transfer(instance, + PS3_INSTANCE_STATE_PRE_OPERATIONAL, + PS3_INSTANCE_STATE_OPERATIONAL); + if (ret != PS3_SUCCESS) { + LOG_ERROR("hno:%u transfer to operational failed!\n", + PS3_HOST(instance)); + ret = PS3_RECOVERY_INTER_ERR_INTERRUPT; + goto l_out; + } + ret = ps3_cmd_resubscribe(instance); + if (ret != PS3_RECOVERY_INTER_ERR_SUCCESS) { + LOG_ERROR("hno:%u cmd unsubscribe failed!\n", + PS3_HOST(instance)); + goto l_out; + } + ret = ps3_recovery_complete(instance); + if (ret != PS3_SUCCESS) { + LOG_INFO( + "hno:%u IOC soft reset opeational but complete failed! cur_state:%s,ret %d\n", + PS3_HOST(instance), + namePS3InstanceState(ps3_atomic_read( + &instance->state_machine.state)), + ret); + ret = PS3_RECOVERY_INTER_ERR_FAILED; + goto l_out; + } + if (instance->peer_instance != NULL) { + ret = ps3_instance_state_transfer( + instance->peer_instance, + PS3_INSTANCE_STATE_PRE_OPERATIONAL, + PS3_INSTANCE_STATE_OPERATIONAL); + if (ret != PS3_SUCCESS) { + LOG_ERROR("hno:%u transfer to operational failed!\n", + PS3_HOST(instance->peer_instance)); + ret = PS3_RECOVERY_INTER_ERR_FAILED; + goto l_out; + } + LOG_INFO("hno:%u soft reset success and finished!\n", + PS3_HOST(instance->peer_instance)); + ret = ps3_cmd_resubscribe(instance->peer_instance); + if (ret != PS3_RECOVERY_INTER_ERR_SUCCESS) { + LOG_ERROR("hno:%u cmd unsubscribe failed!\n", + PS3_HOST(instance->peer_instance)); + goto l_out; + } + ret = ps3_recovery_complete(instance->peer_instance); + if (ret != PS3_SUCCESS) { + LOG_INFO( + "hno:%u soft reset opeational but complete failed! cur_state:%s,ret %d\n", + PS3_HOST(instance->peer_instance), + namePS3InstanceState(ps3_atomic_read( + &instance->peer_instance->state_machine + .state)), + ret); + ret = PS3_RECOVERY_INTER_ERR_FAILED; + goto l_out; + } + } + ps3_recovery_state_transfer(instance, PS3_SOFT_RECOVERY_FINISH); + + ret = PS3_RECOVERY_INTER_ERR_SUCCESS; + LOG_INFO("hno:%u soft reset success and finished!\n", + PS3_HOST(instance)); +l_out: + return ret; +} + +static int ps3_host_shallow_soft_recovery(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + int err_code = PS3_RECOVERY_INTER_ERR_SUCCESS; + + if (PS3_IOC_SHALLOW_SOFT_RECOVERY_SUPPORT(instance) == PS3_FALSE) { + LOG_ERROR("hno:%u soc feature unsupport soft reset Shallow!\n", + PS3_HOST(instance)); + err_code = PS3_RECOVERY_INTER_ERR_NEED_RECOVERY; + goto l_out; + } + + if (instance->peer_instance != NULL) { + if (PS3_FALSE == PS3_IOC_SHALLOW_SOFT_RECOVERY_SUPPORT( + instance->peer_instance)) { + LOG_ERROR( + "hno:%u soc feature unsupport soft reset Shallow!\n", + PS3_HOST(instance->peer_instance)); + err_code = PS3_RECOVERY_INTER_ERR_NEED_RECOVERY; + goto l_out; + } + } + ps3_mutex_lock(&instance->state_machine.lock); + if (PS3_IS_INTERRUPT_SOFT_RECOVERY(instance)) { + LOG_WARN("hno:%u shallow soft reset proc is interrupt!\n", + PS3_HOST(instance)); + err_code = PS3_RECOVERY_INTER_ERR_INTERRUPT; + ps3_mutex_unlock(&instance->state_machine.lock); + goto l_out; + } + ps3_mutex_unlock(&instance->state_machine.lock); + if (instance->ioc_adpter->ioc_shallow_soft_reset != NULL) { + ps3_recovery_state_transfer(instance, + PS3_SOFT_RECOVERY_SHALLOW); + ret = instance->ioc_adpter->ioc_shallow_soft_reset(instance); + } else { + LOG_ERROR("hno:%u driver unsupport soft reset shallow!\n", + PS3_HOST(instance)); + err_code = PS3_RECOVERY_INTER_ERR_NEED_RECOVERY; + goto l_out; + } + + if (ret == PS3_SUCCESS) { + LOG_INFO("hno:%u shallow soft reset entry complete prcoess!\n", + PS3_HOST(instance)); + err_code = ps3_soft_recovery_complete(instance); + } else { + LOG_ERROR("hno:%u shallow soft reset fail!\n", + PS3_HOST(instance)); + err_code = PS3_RECOVERY_INTER_ERR_NEED_RECOVERY; + } + + LOG_INFO("hno:%u shallow soft reset finished!:ret:%d, err_code:%d\n", + PS3_HOST(instance), ret, err_code); + +l_out: + return err_code; +} + +static int ps3_host_deep_soft_recovery(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + int err_code = PS3_RECOVERY_INTER_ERR_SUCCESS; + + if (PS3_IOC_DEEP_SOFT_RECOVERY_SUPPORT(instance) == PS3_FALSE) { + LOG_ERROR("hno:%u soc feature unsupport soft reset Deep!\n", + PS3_HOST(instance)); + err_code = PS3_RECOVERY_INTER_ERR_NEED_RECOVERY; + goto l_out; + } + + if (instance->peer_instance != NULL) { + if (PS3_FALSE == PS3_IOC_DEEP_SOFT_RECOVERY_SUPPORT( + instance->peer_instance)) { + LOG_ERROR( + "hno:%u soc feature unsupport soft reset Deep!\n", + PS3_HOST(instance->peer_instance)); + err_code = PS3_RECOVERY_INTER_ERR_NEED_RECOVERY; + goto l_out; + } + } + + ps3_mutex_lock(&instance->state_machine.lock); + if (PS3_IS_INTERRUPT_SOFT_RECOVERY(instance)) { + LOG_WARN("hno:%u deep soft reset proc is interrupt!\n", + PS3_HOST(instance)); + err_code = PS3_RECOVERY_INTER_ERR_INTERRUPT; + ps3_mutex_unlock(&instance->state_machine.lock); + goto l_out; + } + ps3_mutex_unlock(&instance->state_machine.lock); + ps3_instance_state_transfer(instance, + PS3_INSTANCE_STATE_PRE_OPERATIONAL, + PS3_INSTANCE_STATE_SOFT_RECOVERY); + + if (ps3_atomic_read(&instance->state_machine.state) != + PS3_INSTANCE_STATE_SOFT_RECOVERY) { + LOG_ERROR("hno:%u soft reovery has been interrupt!\n", + PS3_HOST(instance)); + err_code = PS3_RECOVERY_INTER_ERR_INTERRUPT; + goto l_out; + } + if (instance->peer_instance != NULL) { + ps3_instance_state_transfer(instance->peer_instance, + PS3_INSTANCE_STATE_PRE_OPERATIONAL, + PS3_INSTANCE_STATE_SOFT_RECOVERY); + + if (ps3_atomic_read( + &instance->peer_instance->state_machine.state) != + PS3_INSTANCE_STATE_SOFT_RECOVERY) { + LOG_ERROR("hno:%u soft reovery has been interrupt!\n", + PS3_HOST(instance->peer_instance)); + err_code = PS3_RECOVERY_INTER_ERR_INTERRUPT; + goto l_out; + } + } + + if (instance->ioc_adpter->ioc_deep_soft_reset != NULL) { + ps3_recovery_state_transfer(instance, PS3_SOFT_RECOVERY_DEEP); + ret = instance->ioc_adpter->ioc_deep_soft_reset(instance); + } else { + LOG_ERROR("hno:%u driver unsupport soft reset deep!\n", + PS3_HOST(instance)); + err_code = PS3_RECOVERY_INTER_ERR_NEED_RECOVERY; + goto l_out; + } + + if (ret == PS3_SUCCESS) { + LOG_INFO("hno:%u deep soft reset entry complete prcoess!\n", + PS3_HOST(instance)); + err_code = ps3_soft_recovery_complete(instance); + } else { + LOG_ERROR("hno:%u deep soft reset fail!\n", + PS3_HOST(instance)); + err_code = PS3_RECOVERY_INTER_ERR_NEED_RECOVERY; + } + + LOG_INFO("hno:%u deep soft reset finished!:ret:%d, err_code:%d\n", + PS3_HOST(instance), ret, err_code); + +l_out: + return err_code; +} +static inline int +ps3_wait_event_vdpending_cmd_complete(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + unsigned int count = 0; + + + const unsigned int retry_max = PS3_WAIT_EVENT_CMD_LOOP_COUNT; + + while (ps3_atomic_read(&instance->event_context.abort_eventcmd) != 0 || + ps3_atomic_read(&instance->dev_context.abort_vdpending_cmd) != 0 || + ps3_atomic_read(&instance->webSubscribe_context.abort_webcmd) != 0) { + ps3_msleep(PS3_LOOP_TIME_INTERVAL_100MS); + + if (count++ > retry_max) { + LOG_INFO("hno:%u wait event proc over:%d ms,failed\n", + PS3_HOST(instance), + retry_max * PS3_LOOP_TIME_INTERVAL_100MS); + ret = -PS3_FAILED; + break; + } + } + return ret; +} +static int ps3_soft_recovery_handle(struct ps3_instance *instance) +{ + int ret = PS3_RECOVERY_INTER_ERR_SUCCESS; + struct ps3_recovery_context *context = instance->recovery_context; + + if (ps3_pci_err_recovery_get(instance)) { + LOG_WARN("hno:%u pci error recovery resetting\n", + PS3_HOST(instance)); + goto l_out; + } + + if (ps3_wait_event_vdpending_cmd_complete(instance) != PS3_SUCCESS) { + ret = PS3_RECOVERY_INTER_ERR_FAILED; + goto l_out; + } + if (instance->peer_instance != NULL) { + if (ps3_pci_err_recovery_get(instance->peer_instance)) { + LOG_WARN("hno:%u pci error recovery resetting\n", + PS3_HOST(instance->peer_instance)); + goto l_out; + } + + if (ps3_wait_event_vdpending_cmd_complete( + instance->peer_instance) != PS3_SUCCESS) { + ret = PS3_RECOVERY_INTER_ERR_FAILED; + goto l_out; + } + } + ps3_mutex_lock(&instance->state_machine.lock); + if (context->recovery_state == PS3_SOFT_RECOVERY_IOC_RECOVERY) { + ps3_mutex_unlock(&instance->state_machine.lock); + ret = ps3_ioc_soft_recovery(instance); + goto l_out; + } + ps3_mutex_unlock(&instance->state_machine.lock); + + ret = ps3_host_shallow_soft_recovery(instance); + if (ret == PS3_RECOVERY_INTER_ERR_INTERRUPT) { + LOG_WARN("hno:%u shallow soft reset proc is interrupt!\n", + PS3_HOST(instance)); + goto l_out; + } else if (ret == PS3_RECOVERY_INTER_ERR_SUCCESS) { + LOG_WARN("hno:%u shallow soft reset success!\n", + PS3_HOST(instance)); + goto l_reset_success; + } else { + LOG_WARN( + "hno:%u shallow soft reset fail, need to deep recovery!\n", + PS3_HOST(instance)); + } + + ret = ps3_host_deep_soft_recovery(instance); + if (ret == PS3_RECOVERY_INTER_ERR_INTERRUPT) { + LOG_WARN("hno:%u deep soft reset proc is interrupt!\n", + PS3_HOST(instance)); + goto l_out; + } else if (ret == PS3_RECOVERY_INTER_ERR_SUCCESS) { + LOG_INFO("hno:%u deep soft reset success!\n", + PS3_HOST(instance)); + goto l_reset_success; + } else { + LOG_INFO( + "hno:%u deep soft reset fail, need to hard recovery!\n", + PS3_HOST(instance)); + goto l_out; + } + +l_reset_success: + ret = ps3_instance_state_transfer(instance, + PS3_INSTANCE_STATE_PRE_OPERATIONAL, + PS3_INSTANCE_STATE_OPERATIONAL); + if (ret != PS3_SUCCESS) { + LOG_ERROR("hno:%u transfer to operational failed!\n", + PS3_HOST(instance)); + ret = PS3_RECOVERY_INTER_ERR_FAILED; + goto l_out; + } + LOG_INFO("hno:%u soft reset success and finished!\n", + PS3_HOST(instance)); + ret = ps3_cmd_resubscribe(instance); + if (ret != PS3_RECOVERY_INTER_ERR_SUCCESS) { + LOG_ERROR("hno:%u cmd unsubscribe failed!\n", + PS3_HOST(instance)); + goto l_out; + } + ret = ps3_recovery_complete(instance); + if (ret != PS3_SUCCESS) { + LOG_INFO( + "hno:%u soft reset opeational but complete failed! cur_state:%s,ret %d\n", + PS3_HOST(instance), + namePS3InstanceState(ps3_atomic_read( + &instance->state_machine.state)), + ret); + ret = PS3_RECOVERY_INTER_ERR_FAILED; + goto l_out; + } + + if (instance->peer_instance != NULL) { + ret = ps3_instance_state_transfer( + instance->peer_instance, + PS3_INSTANCE_STATE_PRE_OPERATIONAL, + PS3_INSTANCE_STATE_OPERATIONAL); + if (ret != PS3_SUCCESS) { + LOG_ERROR("hno:%u transfer to operational failed!\n", + PS3_HOST(instance->peer_instance)); + ret = PS3_RECOVERY_INTER_ERR_FAILED; + goto l_out; + } + LOG_INFO("hno:%u soft reset success and finished!\n", + PS3_HOST(instance->peer_instance)); + ret = ps3_cmd_resubscribe(instance->peer_instance); + if (ret != PS3_RECOVERY_INTER_ERR_SUCCESS) { + LOG_ERROR("hno:%u cmd unsubscribe failed!\n", + PS3_HOST(instance->peer_instance)); + goto l_out; + } + ret = ps3_recovery_complete(instance->peer_instance); + if (ret != PS3_SUCCESS) { + LOG_INFO( + "hno:%u soft reset opeational but complete failed! cur_state:%s,ret %d\n", + PS3_HOST(instance->peer_instance), + namePS3InstanceState(ps3_atomic_read( + &instance->peer_instance->state_machine + .state)), + ret); + ret = PS3_RECOVERY_INTER_ERR_FAILED; + goto l_out; + } + } + + LOG_INFO("hno:%u soft reset success and finished!\n", + PS3_HOST(instance)); + ret = PS3_RECOVERY_INTER_ERR_SUCCESS; + return ret; + +l_out: + return ret; +} + +static int ps3_hard_recovery_to_ready(struct ps3_instance *instance) +{ + int err_code = PS3_RECOVERY_INTER_ERR_SUCCESS; + unsigned int ioc_state = 0; + int ret = PS3_SUCCESS; + + if (ps3_pci_err_recovery_get(instance)) { + LOG_WARN("hno:%u pci recovery resetting\n", + PS3_HOST(instance)); + err_code = PS3_RECOVERY_INTER_ERR_PCIE_ERR; + goto l_out; + } + + ret = ps3_ioc_hard_reset_to_ready(instance); + if (ret != PS3_SUCCESS) { + ioc_state = instance->ioc_adpter->ioc_state_get(instance); + LOG_ERROR("hno:%u hard reset to ready NOK,%s!\n", + PS3_HOST(instance), ps3_ioc_state_print(ioc_state)); + if (ret == -PS3_IN_PCIE_ERR) + err_code = PS3_RECOVERY_INTER_ERR_PCIE_ERR; + else + err_code = PS3_RECOVERY_INTER_ERR_NEED_RECOVERY; + goto l_out; + } + + if (ps3_instance_state_transfer(instance, PS3_INSTANCE_STATE_RECOVERY, + PS3_INSTANCE_STATE_READY) != + PS3_SUCCESS) { + LOG_ERROR("hno:%u transfer to ready NOK!\n", + PS3_HOST(instance)); + err_code = PS3_RECOVERY_INTER_ERR_INTERRUPT; + } +l_out: + return err_code; +} + +static int ps3_hard_recovery_to_pre_operational(struct ps3_instance *instance) +{ + int err_code = PS3_RECOVERY_INTER_ERR_SUCCESS; + int ret = PS3_SUCCESS; + + if (ps3_pci_err_recovery_get(instance)) { + LOG_WARN("hno:%u pci recovery resetting\n", + PS3_HOST(instance)); + err_code = PS3_RECOVERY_INTER_ERR_PCIE_ERR; + goto l_out; + } + + if (PS3_IS_INSTANCE_NOT_LOAD_NORMAL(instance)) + goto l_out; + + ret = ps3_recovery_ready_to_running(instance); + if (ret != PS3_SUCCESS) { + LOG_ERROR("hno:%u hard reset to running NOK!\n", + PS3_HOST(instance)); + + if (ret == -PS3_IN_PCIE_ERR) { + err_code = PS3_RECOVERY_INTER_ERR_PCIE_ERR; + goto l_out; + } + if (ps3_instance_state_transfer( + instance, PS3_INSTANCE_STATE_READY, + PS3_INSTANCE_STATE_RECOVERY) != PS3_SUCCESS) { + LOG_ERROR("hno:%u transfer to recovery NOK!\n", + PS3_HOST(instance)); + err_code = PS3_RECOVERY_INTER_ERR_INTERRUPT; + goto l_out; + } + + err_code = PS3_RECOVERY_INTER_ERR_NEED_RECOVERY; + goto l_out; + } + + instance->ioc_adpter->irq_enable(instance); +#ifndef _WINDOWS + ps3_irqpolls_enable(instance); +#endif + if (instance->is_need_event && + ps3_atomic_read(&instance->webSubscribe_context.is_subscribe) == + 1) { + ps3_web_cmd_clear(instance); + } + if (instance->is_need_event) + ps3_dev_mgr_vd_info_clear(instance); + if (ps3_instance_state_transfer(instance, PS3_INSTANCE_STATE_READY, + PS3_INSTANCE_STATE_PRE_OPERATIONAL) != + PS3_SUCCESS) { + LOG_ERROR("hno:%u transfer to pre-operational NOK!\n", + PS3_HOST(instance)); + err_code = PS3_RECOVERY_INTER_ERR_INTERRUPT; + goto l_out; + } + ps3_dump_ctrl_set_int_ready(instance); + +l_out: + return err_code; +} + +static int ps3_hard_recovery_to_operational(struct ps3_instance *instance) +{ + int err_code = PS3_RECOVERY_INTER_ERR_SUCCESS; + int ret; + unsigned long flags = 0; + struct ps3_cmd *cmd = NULL; + unsigned char need_subscribe = PS3_FALSE; + + if (ps3_pci_err_recovery_get(instance)) { + LOG_WARN("hno:%u pci recovery resetting\n", + PS3_HOST(instance)); + err_code = PS3_RECOVERY_INTER_ERR_PCIE_ERR; + goto l_out; + } + + if (PS3_IS_INSTANCE_NOT_LOAD_NORMAL(instance)) + goto l_out; + + if (ps3_hard_recovery_pre_operational_to_operational(instance) != + PS3_SUCCESS) { + LOG_ERROR("hno:%u pre operational NOK!\n", PS3_HOST(instance)); + if (ps3_instance_state_transfer( + instance, PS3_INSTANCE_STATE_PRE_OPERATIONAL, + PS3_INSTANCE_STATE_RECOVERY) != PS3_SUCCESS) { + LOG_ERROR("hno:%u transfer to recovery NOK!\n", + PS3_HOST(instance)); + err_code = PS3_RECOVERY_INTER_ERR_INTERRUPT; + goto l_out; + } + + err_code = PS3_RECOVERY_INTER_ERR_NEED_RECOVERY; + goto l_out; + } + + if (ps3_instance_state_transfer( + instance, PS3_INSTANCE_STATE_PRE_OPERATIONAL, + PS3_INSTANCE_STATE_OPERATIONAL) != PS3_SUCCESS) { + LOG_ERROR("hno:%u transfer to operational NOK!\n", + PS3_HOST(instance)); + err_code = PS3_RECOVERY_INTER_ERR_INTERRUPT; + goto l_out; + } + + if (!instance->is_need_event) { + LOG_INFO("hno:%u not need event\n", PS3_HOST(instance)); + goto l_out; + } + + ps3_spin_lock_irqsave(&instance->recovery_context->recovery_lock, + &flags); + cmd = instance->event_context.event_cmd; + if (cmd == NULL) { + LOG_WARN_IN_IRQ(instance, "hno:%u Event is not register yet\n", + PS3_HOST(instance)); + need_subscribe = PS3_TRUE; + goto l_subscribe; + } + if (ps3_atomic_read(&instance->event_context.subwork) == 0) { + if (ps3_atomic_read(&instance->hardreset_event) != 0) { + ps3_atomic_set(&instance->hardreset_event, 0); + LOG_INFO_IN_IRQ(instance, + "hno:%u event cmd free, CFID:%d\n", + PS3_HOST(instance), cmd->index); + instance->event_context.event_cmd = NULL; + ps3_mgr_cmd_free(instance, cmd); + need_subscribe = PS3_TRUE; + } + } + +l_subscribe: + if (need_subscribe == PS3_TRUE) { + instance->event_req_info.eventTypeMapProcResult = + instance->event_req_info.eventTypeMap; + ret = ps3_event_subscribe(instance); + if (ret != PS3_SUCCESS && ret != -PS3_IN_UNLOAD) { + err_code = PS3_RECOVERY_INTER_ERR_SUCCESS; + ps3_spin_unlock_irqrestore( + &instance->recovery_context->recovery_lock, + flags); + LOG_WARN("hno:%u IOC event subscribe NOK ret[%d]\n", + PS3_HOST(instance), ret); + goto l_out; + } + } + ps3_spin_unlock_irqrestore(&instance->recovery_context->recovery_lock, + flags); + + ret = ps3_dev_mgr_vd_info_subscribe(instance); + if (ret != PS3_SUCCESS && ret != -PS3_IN_UNLOAD) { + err_code = PS3_RECOVERY_INTER_ERR_SUCCESS; + LOG_WARN("hno:%u vd info subscribe NOK ret[%d]\n", + PS3_HOST(instance), ret); + goto l_out; + } + + if (ps3_atomic_read(&instance->webSubscribe_context.is_subscribe) == + 1) { + ret = ps3_web_subscribe(instance); + if (ret != PS3_SUCCESS && ret != -PS3_IN_UNLOAD) { + err_code = PS3_RECOVERY_INTER_ERR_SUCCESS; + LOG_WARN("hno:%u web subscribe NOK ret[%d]\n", + PS3_HOST(instance), ret); + goto l_out; + } + } +l_out: + return err_code; +} + +static inline void ps3_hard_reset_unnormal_handle(struct ps3_instance *instance) +{ + ps3_recovery_ready_to_force_cmd_stop(instance); + + ps3_instance_state_transfer(instance, PS3_INSTANCE_STATE_READY, + PS3_INSTANCE_STATE_RECOVERY); + instance->is_half_hard_reset = PS3_TRUE; + if (!instance->state_machine.is_load || + instance->state_machine.is_suspend) { + ps3_ioc_notify_unload(instance); + } +} + +static inline int +ps3_hard_reset_error_code_decide(struct ps3_instance *instance) +{ + int err_code = PS3_RECOVERY_INTER_ERR_SUCCESS; + + if (PS3_IS_INSTANCE_SUSPEND_OR_RESUME(instance)) { + err_code = PS3_RECOVERY_INTER_ERR_SUSPEND_RESUME; + goto l_out; + } + + if (PS3_IS_INSTANCE_NOT_LOAD_NORMAL(instance) && + instance->peer_instance == NULL) { + err_code = PS3_RECOVERY_INTER_ERR_FAILED; + } + +l_out: + return err_code; +} + +static inline int +ps3_hard_reset_multi_unnormal_handle(struct ps3_instance *instance) +{ + if (PS3_IS_INSTANCE_NOT_LOAD_NORMAL(instance)) { + LOG_WARN("host_no[%u], reset while probe or in shutdown/remove,\n" + "\tfinish[%d], flag[%d], is_load[%d]\n", + PS3_HOST(instance), instance->is_probe_finish, + instance->is_probe_failed, + instance->state_machine.is_load); + if (((PS3_IS_INSTANCE_PROBE(instance) || + PS3_IS_INSTANCE_RESUME(instance)) && + instance->state_machine.is_load) || + PS3_IS_INSTANCE_REMOVE(instance)) { + ps3_hard_reset_unnormal_handle(instance); + ps3_instance_state_transfer_to_dead(instance); + } else if (PS3_IS_INSTANCE_SUSPEND(instance)) { + ps3_hard_reset_unnormal_handle(instance); + } + } + if (instance->peer_instance != NULL && + PS3_IS_INSTANCE_NOT_LOAD_NORMAL(instance->peer_instance)) { + LOG_WARN("host_no[%u], reset while probe or in shutdown/remove,\n" + "\tfinish[%d], flag[%d], is_load[%d]\n", + PS3_HOST(instance->peer_instance), + instance->peer_instance->is_probe_finish, + instance->peer_instance->is_probe_failed, + instance->peer_instance->state_machine.is_load); + if (((PS3_IS_INSTANCE_PROBE(instance->peer_instance) || + PS3_IS_INSTANCE_RESUME(instance->peer_instance)) && + instance->peer_instance->state_machine.is_load) || + PS3_IS_INSTANCE_REMOVE(instance->peer_instance)) { + ps3_hard_reset_unnormal_handle(instance->peer_instance); + ps3_instance_state_transfer_to_dead( + instance->peer_instance); + } else if (PS3_IS_INSTANCE_SUSPEND(instance->peer_instance)) { + ps3_hard_reset_unnormal_handle(instance->peer_instance); + } + } + return ps3_hard_reset_error_code_decide(instance); +} + +static void ps3_recovery_wait_reg_access_done(struct ps3_instance *instance) +{ + unsigned short cur_cnt = 0; + unsigned char printed = PS3_TRUE; + + LOG_DEBUG("hno:%u recovery wait reg access done start\n", + PS3_HOST(instance)); + ps3_wait_scsi_cmd_done(instance, PS3_TRUE); + ps3_wait_mgr_cmd_done(instance, PS3_TRUE); + while (ps3_atomic_read(&instance->reg_op_count) != 0) { + ps3_msleep(PS3_LOOP_TIME_INTERVAL_20MS); + if (printed && ((++cur_cnt) * PS3_LOOP_TIME_INTERVAL_20MS > + PS3_RECOVERY_WHILE_PRINT_REACH_TIME)) { + printed = PS3_FALSE; + LOG_WARN("host:%u wait reg access done.\n", + PS3_HOST(instance)); + } + } + LOG_DEBUG("hno:%u recovery wait reg access done end\n", + PS3_HOST(instance)); +} + +void ps3_hard_recovery_state_finish(struct ps3_instance *instance) +{ + ps3_mutex_lock(&instance->state_machine.lock); + if (instance->recovery_context->host_reset_state == + PS3_HOST_RESET_START) { + instance->recovery_context->host_reset_state = + PS3_HOST_RESET_HARD_RESET_DONE; + } + ps3_mutex_unlock(&instance->state_machine.lock); + ps3_recovery_state_transfer(instance, PS3_HARD_RECOVERY_FINISH); +} + +static int ps3_hard_recovery_handle(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + unsigned int retries = 0; + unsigned int cur_state = 0; + unsigned int ioc_state = 0; + int err_code = PS3_RECOVERY_INTER_ERR_SUCCESS; + unsigned int wait_count = 0; + unsigned long flags = 0; + unsigned short cur_cnt = 0; + unsigned char printed = PS3_TRUE; + + if (ps3_pci_err_recovery_get(instance)) { + LOG_WARN("hno:%u pci recovery resetting\n", + PS3_HOST(instance)); + goto l_out; + } + + if (((!PS3_IOC_HARD_RECOVERY_SUPPORT(instance)) || + (!ps3_hard_reset_enable_query())) && + (instance->recovery_context->heartbeat_recovery != + PS3_HEARTBEAT_HARDRESET_RECOVERY)) { + LOG_ERROR( + "hno:%u soc feature unsupport Hard reset! need to be offline!\n", + PS3_HOST(instance)); + goto l_offline; + } + + ps3_spin_lock_irqsave(&instance->recovery_context->recovery_lock, + &flags); + if (ps3_atomic_read(&instance->event_context.abort_eventcmd) != 0) + ps3_atomic_set(&instance->event_context.abort_eventcmd, 0); + + if (ps3_atomic_read(&instance->hardreset_event) == 0) + ps3_atomic_set(&instance->hardreset_event, 1); + ps3_spin_unlock_irqrestore(&instance->recovery_context->recovery_lock, + flags); + ps3_recovery_state_transfer(instance, PS3_HARD_RECOVERY_SHALLOW); + if (instance->is_scan_host_finish && !instance->is_probe_finish) { + LOG_INFO("hno:%u recovery wait until probe finish/failed\n", + PS3_HOST(instance)); + while (wait_count < PS3_RECOVERY_WAIT_PROBE_FINISH_LOOP_COUNT) { + if (instance->is_probe_finish || + instance->is_probe_failed) { + break; + } + + wait_count++; + ps3_msleep(PS3_RECOVERY_WAIT_LOOP_TIME_INTERVAL_20MS); + } + + LOG_INFO( + "hno:%u probe finish(%d)/failed(%d), continue recovery\n", + PS3_HOST(instance), instance->is_probe_finish, + instance->is_probe_failed); + } + + ps3_wait_watchdog_dect_recovery(instance); + + if (instance->peer_instance != NULL && + instance->peer_instance->recovery_function.hardreset_handle_pre_cb != + NULL) { + ret = instance->peer_instance->recovery_function + .hardreset_handle_pre_cb(instance->peer_instance); + if (ret != PS3_SUCCESS) { + LOG_WARN("hno:%u hardreset handle pre cb NOK\n", + PS3_HOST(instance)); + if (ret == -PS3_IN_PCIE_ERR) + goto l_out; + else + goto l_offline; + } + } + + while (ps3_use_hard_reset_max_retry() > retries) { + LOG_DEBUG("hno:%u request IOC state to fault! retries:%d\n", + PS3_HOST(instance), retries); + ps3_mutex_lock(&instance->recovery_context + ->ps3_watchdog_recovery_mutex); + if (instance->recovery_context->heartbeat_recovery == + PS3_HEARTBEAT_HARDRESET_RECOVERY && + retries != 0) { + instance->recovery_context->heartbeat_recovery = + PS3_HEARTBEAT_HARDRESET_RETRY; + } + ps3_mutex_unlock(&instance->recovery_context + ->ps3_watchdog_recovery_mutex); + + instance->dump_context.is_hard_recovered = PS3_TRUE; + if (instance->recovery_context->heartbeat_recovery != + PS3_HEARTBEAT_HARDRESET_RECOVERY) { + ret = instance->ioc_adpter->ioc_force_to_fault( + instance); + if (ret != PS3_SUCCESS) { + LOG_WARN( + "hno:%u hardreset handle force to fault NOK\n", + PS3_HOST(instance)); + if (ret == -PS3_IN_PCIE_ERR) + goto l_out; + } + } else { + ps3_recovery_wait_reg_access_done(instance); + if (instance->peer_instance != NULL) { + ps3_recovery_wait_reg_access_done( + instance->peer_instance); + } + } + + ioc_state = instance->ioc_adpter->ioc_state_get(instance); + LOG_DEBUG("hno:%u IOC state is %s! retries:%d\n", + PS3_HOST(instance), ps3_ioc_state_print(ioc_state), + retries); + + while (ps3_atomic_read(&instance->is_err_scsi_processing) > 0) { + ps3_msleep(10); + if (printed && ((++cur_cnt) * 10 > + PS3_RECOVERY_WHILE_PRINT_REACH_TIME)) { + printed = PS3_FALSE; + LOG_WARN("host:%u wait err scsi process\n", + PS3_HOST(instance)); + } + } + instance->ioc_adpter->irq_disable(instance); +#ifndef _WINDOWS + ps3_irqs_sync(instance); +#endif + + err_code = ps3_hard_recovery_to_ready(instance); + if (err_code != PS3_RECOVERY_INTER_ERR_SUCCESS) { + LOG_INFO( + "hno:%u IOC state to ready failed! retries:%d\n", + PS3_HOST(instance), retries); + if (err_code == PS3_RECOVERY_INTER_ERR_NEED_RECOVERY) { + retries++; + continue; + } else if (err_code == + PS3_RECOVERY_INTER_ERR_PCIE_ERR) { + goto l_out; + } else { + goto l_offline; + } + } + LOG_INFO("hno:%u IOC state to ready success! retries:%d\n", + PS3_HOST(instance), retries); + + if (instance->peer_instance != NULL && + instance->peer_instance->recovery_function + .hardreset_handle_wait_ready_cb != NULL) { + err_code = instance->peer_instance->recovery_function + .hardreset_handle_wait_ready_cb( + instance->peer_instance); + if (err_code != PS3_RECOVERY_INTER_ERR_SUCCESS) { + LOG_INFO( + "hno:%u ioc state to ready failed! retries:%d\n", + PS3_HOST(instance), retries); + if (err_code == + PS3_RECOVERY_INTER_ERR_NEED_RECOVERY) { + retries++; + continue; + } else if (err_code == + PS3_RECOVERY_INTER_ERR_PCIE_ERR) { + goto l_out; + } else { + goto l_offline; + } + } + LOG_INFO( + "hno:%u ioc state to ready success! retries:%d\n", + PS3_HOST(instance), retries); + } + + err_code = ps3_hard_reset_multi_unnormal_handle(instance); + if (err_code != PS3_RECOVERY_INTER_ERR_SUCCESS) { + LOG_INFO("driver loading or unloading err_code:%d\n", + err_code); + if (err_code == PS3_RECOVERY_INTER_ERR_SUSPEND_RESUME) + goto l_out; + else + goto l_offline; + } + err_code = ps3_hard_recovery_to_pre_operational(instance); + if (err_code != PS3_RECOVERY_INTER_ERR_SUCCESS) { + LOG_INFO( + "hno:%u IOC state to pre-operatioal failed! retries:%d\n", + PS3_HOST(instance), retries); + if (err_code == PS3_RECOVERY_INTER_ERR_NEED_RECOVERY) { + if (instance->peer_instance != NULL) { + if (ps3_instance_state_transfer( + instance->peer_instance, + PS3_INSTANCE_STATE_READY, + PS3_INSTANCE_STATE_RECOVERY) != + PS3_SUCCESS) { + LOG_WARN( + "hno:%u transfer to recovery NOK!\n", + PS3_HOST( + instance->peer_instance)); + goto l_offline; + } + } + retries++; + continue; + } else if (err_code == + PS3_RECOVERY_INTER_ERR_PCIE_ERR) { + goto l_out; + } else { + goto l_offline; + } + } + LOG_INFO( + "hno:%u IOC state to pre-operatioal success! retries:%d\n", + PS3_HOST(instance), retries); + if (instance->peer_instance != NULL && + !PS3_IS_INSTANCE_NOT_LOAD_NORMAL(instance->peer_instance) && + instance->peer_instance->recovery_function + .hardreset_handle_init_running_cb != NULL) { + if (ps3_atomic_read(&instance->peer_instance + ->state_machine.state) != + PS3_INSTANCE_STATE_DEAD) { + err_code = + instance->peer_instance + ->recovery_function + .hardreset_handle_init_running_cb( + instance->peer_instance); + if (err_code == + PS3_RECOVERY_INTER_ERR_SUCCESS) { + LOG_INFO( + "hno:%u IOC state to pre-operatioal success! retries:%d\n", + PS3_HOST(instance), retries); + goto l_operational; + } + LOG_INFO( + "hno:%u IOC state to pre-operatioal failed! retries:%d\n", + PS3_HOST(instance), retries); + if (err_code == + PS3_RECOVERY_INTER_ERR_NEED_RECOVERY) { + if (ps3_instance_state_transfer( + instance, + PS3_INSTANCE_STATE_PRE_OPERATIONAL, + PS3_INSTANCE_STATE_RECOVERY) != + PS3_SUCCESS) { + LOG_WARN( + "hno:%u transfer to recovery NOK!\n", + PS3_HOST( + instance->peer_instance)); + goto l_offline; + } + retries++; + continue; + } else if (err_code == + PS3_RECOVERY_INTER_ERR_PCIE_ERR) { + goto l_out; + } else { + goto l_offline; + } + } + } + +l_operational: + + err_code = ps3_hard_recovery_to_operational(instance); + if (err_code != PS3_RECOVERY_INTER_ERR_SUCCESS) { + LOG_INFO( + "hno:%u IOC state to operatioal failed! retries:%d\n", + PS3_HOST(instance), retries); + if (err_code == PS3_RECOVERY_INTER_ERR_NEED_RECOVERY) { + retries++; + continue; + } else if (err_code == + PS3_RECOVERY_INTER_ERR_PCIE_ERR) { + goto l_out; + } else { + goto l_offline; + } + } + LOG_INFO( + "hno:%u IOC state to operatioal success! retries:%d\n", + PS3_HOST(instance), retries); + + if (instance->peer_instance != NULL && + instance->peer_instance->recovery_function + .hardreset_handle_post_cb != NULL) { + if (!PS3_IS_INSTANCE_NOT_LOAD_NORMAL( + instance->peer_instance) && + ps3_atomic_read(&instance->peer_instance + ->state_machine.state) != + PS3_INSTANCE_STATE_DEAD) { + err_code = + instance->peer_instance + ->recovery_function + .hardreset_handle_post_cb( + instance->peer_instance); + if (err_code != PS3_RECOVERY_INTER_ERR_SUCCESS) { + LOG_INFO( + "hno:%u IOC state to operatioal failed! retries:%d\n", + PS3_HOST(instance), + retries); + if (err_code == PS3_RECOVERY_INTER_ERR_NEED_RECOVERY) { + retries++; + continue; + } else if (err_code == PS3_RECOVERY_INTER_ERR_PCIE_ERR) { + goto l_out; + } else { + goto l_offline; + } + } + LOG_INFO( + "hno:%u IOC state to operatioal success! retries:%d\n", + PS3_HOST(instance), retries); + } + } + break; + } + + cur_state = ps3_atomic_read(&instance->state_machine.state); + if ((cur_state != PS3_INSTANCE_STATE_OPERATIONAL || + ps3_use_hard_reset_max_retry() == retries) && + !PS3_IS_INSTANCE_NOT_LOAD_NORMAL(instance)) { + LOG_ERROR("hno:%u hard reset failed! cur_state:%s\n", + PS3_HOST(instance), namePS3InstanceState(cur_state)); + goto l_offline; + } + + ret = ps3_recovery_complete(instance); + if (ret != PS3_SUCCESS) { + LOG_WARN( + "hno:%u hard reset opeational but complete failed! cur_state:%s,ret %d\n", + PS3_HOST(instance), namePS3InstanceState(cur_state), + ret); + if (ret == -PS3_IN_PCIE_ERR) + goto l_out; + else + goto l_offline; + } + + if (instance->peer_instance == NULL || + instance->peer_instance->recovery_function + .hardreset_handle_finish_cb == NULL) { + ps3_hard_recovery_state_finish(instance); + LOG_INFO("hno:%u hard reset finish! cur_state:%s\n", + PS3_HOST(instance), namePS3InstanceState(cur_state)); + } else { + if (!PS3_IS_INSTANCE_NOT_LOAD_NORMAL(instance->peer_instance)) { + if (ps3_atomic_read(&instance->peer_instance + ->state_machine.state) != + PS3_INSTANCE_STATE_DEAD) { + ret = instance->peer_instance->recovery_function + .hardreset_handle_finish_cb( + instance->peer_instance); + } + if (ret != PS3_SUCCESS) { + LOG_WARN( + "hno:%u hardreset handle running cb NOK\n", + PS3_HOST(instance->peer_instance)); + if (ret == -PS3_IN_PCIE_ERR) + goto l_out; + else + goto l_offline; + } + } else { + ps3_hard_recovery_state_finish(instance); + } + } + ps3_ioc_can_hardreset_set(instance, PS3_IOC_CAN_HARDRESET); + if (instance->peer_instance != NULL) { + ps3_ioc_can_hardreset_set(instance->peer_instance, + PS3_IOC_CAN_HARDRESET); + } + return ret; +l_offline: + ps3_instance_state_transfer_to_dead(instance); + if (instance->peer_instance != NULL && + instance->peer_instance->recovery_function + .hardreset_handle_offline_cb != NULL) { + instance->peer_instance->recovery_function + .hardreset_handle_offline_cb(instance->peer_instance); + } +l_out: + ps3_hard_recovery_state_finish(instance); + if (ps3_pci_err_recovery_get(instance)) { + ps3_mutex_lock(&instance->recovery_context + ->ps3_watchdog_recovery_mutex); + instance->recovery_context->heartbeat_recovery = + PS3_HEARTBEAT_NULL; + ps3_mutex_unlock(&instance->recovery_context + ->ps3_watchdog_recovery_mutex); + } + LOG_WARN("hno:%u hard reset finish! cur_state:%s\n", + PS3_HOST(instance), namePS3InstanceState(cur_state)); + ret = -PS3_FAILED; + return ret; +} + +static int ps3_recovery_ready_to_force_cmd_stop(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + unsigned short cur_cnt = 0; + unsigned char printed = PS3_TRUE; + + ps3_cmd_reset_flag_set(instance, PS3_CMD_FLAG_HARDRESET); + + while (ps3_atomic_read(&instance->is_err_scsi_processing) > 0) { + ps3_msleep(10); + if (printed && + ((++cur_cnt) * 10 > PS3_RECOVERY_WHILE_PRINT_REACH_TIME)) { + printed = PS3_FALSE; + LOG_WARN("host:%u cmd stop wait err scsi process\n", + PS3_HOST(instance)); + } + } + + ps3_all_reply_fifo_complete(instance); + + ps3_cmd_force_stop(instance); + + LOG2_DEBUG("hno:%u force cmd stop end\n", PS3_HOST(instance)); + return ret; +} + +static int ps3_recovery_ready_to_running(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + unsigned short cur_cnt = 0; + unsigned char printed = PS3_TRUE; + + ps3_cmd_reset_flag_set(instance, PS3_CMD_FLAG_HARDRESET); + + while (ps3_atomic_read(&instance->is_err_scsi_processing) > 0) { + ps3_msleep(10); + if (printed && + ((++cur_cnt) * 10 > PS3_RECOVERY_WHILE_PRINT_REACH_TIME)) { + printed = PS3_FALSE; + LOG_WARN("host:%u to running wait err scsi process\n", + PS3_HOST(instance)); + } + } + + ps3_all_reply_fifo_complete(instance); + + + ps3_cmd_force_stop(instance); + if (!ps3_bit_pos_update(instance)) { + LOG_ERROR("hno:%u update bit pos NOK\n", PS3_HOST(instance)); + ret = -PS3_FAILED; + goto l_out; + } + ret = instance->ioc_adpter->ioc_init_proc(instance); + if (ret != PS3_SUCCESS) { + LOG_ERROR("hno:%u init IOC frame NOK\n", PS3_HOST(instance)); + goto l_out; + } + +l_out: + LOG_DEBUG("hno:%u ready to running :%d\n", PS3_HOST(instance), ret); + return ret; +} + +static void ps3_can_queue_reset(struct ps3_instance *instance, + unsigned int cur_max_fw_cmds) +{ + unsigned long flag = 0; + + LOG_INFO("hno:%u IOC cur max fw cmd is %d!\n", PS3_HOST(instance), + cur_max_fw_cmds); + + if (cur_max_fw_cmds <= instance->cmd_context.max_cmd_count) { + instance->cmd_attr.cur_can_que = + cur_max_fw_cmds - + instance->cmd_context.max_mgr_cmd_count - + instance->cmd_context.max_r1x_cmd_count; + spin_lock_irqsave(instance->host->host_lock, flag); + instance->host->can_queue = instance->cmd_attr.cur_can_que; + LOG_INFO_IN_IRQ( + instance, + "hno:%u IOC cur max fw cmd %d is less than %d, can queue is updated to %d!\n", + PS3_HOST(instance), cur_max_fw_cmds, + instance->cmd_context.max_cmd_count, + instance->host->can_queue); + spin_unlock_irqrestore(instance->host->host_lock, flag); + } +} + +static void ps3_cmd_reset_flag_set(struct ps3_instance *instance, + unsigned char reset_flag) +{ + unsigned int i = 0; + struct ps3_cmd *cmd = NULL; + struct ps3_cmd_context *context = &instance->cmd_context; + unsigned long flags = 0; + + if (context->cmd_buf == NULL) + return; + + for (i = 0; i < context->max_cmd_count; i++) { + cmd = context->cmd_buf[i]; + ps3_spin_lock_irqsave(&cmd->cmd_state.lock, &flags); + if (cmd->cmd_state.state != PS3_CMD_STATE_INIT) + cmd->cmd_state.reset_flag = reset_flag; + ps3_spin_unlock_irqrestore(&cmd->cmd_state.lock, flags); + } +} + +static unsigned char ps3_is_scsi_task_cmd(struct ps3_cmd *cmd) +{ + unsigned char cmd_type = cmd->req_frame->word[0]; + + if (cmd_type == PS3_CMD_SCSI_TASK_MANAGEMENT) + return PS3_TRUE; + else + return PS3_FALSE; +} + +void ps3_scsi_cmd_force_stop(struct ps3_instance *instance) +{ + int i = 0; + struct ps3_cmd *cmd = NULL; + struct ps3_cmd *peer_cmd = NULL; + struct ps3_cmd_context *context = &instance->cmd_context; + struct scsi_cmnd *s_cmd = NULL; + struct ps3_scsi_priv_data *data = NULL; + unsigned short count = 0; +#ifndef _WINDOWS + int ret_code = 0; + + if (ps3_pci_err_recovery_get(instance)) + ret_code = PS3_SCSI_RESULT_HOST_STATUS(DID_NO_CONNECT); + else + ret_code = ps3_get_requeue_or_reset(); +#endif + ps3_mutex_lock(&instance->recovery_context->free_cmd_lock); + for (i = 0; i < instance->cmd_attr.cur_can_que; i++) { + cmd = context->cmd_buf[i]; + if ((cmd->cmd_state.state == PS3_CMD_STATE_INIT) || + (cmd->cmd_state.state == PS3_CMD_STATE_COMPLETE)) { + continue; + } + + LOG_INFO("hno:%u force stop scsi CFID[%d] [%u]\n", + PS3_HOST(instance), i, cmd->cmd_state.state); + + s_cmd = cmd->scmd; + count++; + + PS3_IO_OUTSTAND_DEC(instance, s_cmd); + PS3_VD_OUTSTAND_DEC(instance, s_cmd); + PS3_DEV_IO_OUTSTAND_DEC(instance, cmd); + PS3_IOC_DRV2IOC_BACK_INC(cmd->instance, cmd, + PS3_REPLY_WORD_FLAG_FAIL); + ps3_qos_cmd_update(instance, cmd); + +#ifndef _WINDOWS + PS3_IO_BACK_ERR_INC(cmd->instance, s_cmd); + PS3_DEV_BUSY_DEC(s_cmd); + + s_cmd->result = ret_code; +#if defined(PS3_SUPPORT_CMD_SCP) + s_cmd->SCp.ptr = NULL; +#endif + ps3_scsi_dma_unmap(cmd); + if (likely(cmd && cmd->scmd && cmd->scmd->device && + cmd->scmd->device->hostdata)) { + data = (struct ps3_scsi_priv_data *) + cmd->scmd->device->hostdata; + if (likely(data != NULL) && + data->lock_mgr.hash_mgr != NULL) { + ps3_r1x_write_unlock(&data->lock_mgr, cmd); + } + } else { + LOG_WARN( + "hno:%u force stop scsi has Null pointer CFID[%u]\n", + PS3_HOST(instance), i); + } + peer_cmd = cmd->r1x_peer_cmd; + ps3_scsi_cmd_free(cmd); + if (peer_cmd != NULL) { + LOG_DEBUG( + "hno:%u force stop r1x scsi CFID[%d] and CFID[%d]\n", + PS3_HOST(instance), i, peer_cmd->index); + PS3_IOC_DRV2IOC_BACK_INC(peer_cmd->instance, peer_cmd, + PS3_REPLY_WORD_FLAG_FAIL); + ps3_r1x_peer_cmd_free_nolock(peer_cmd); + } + SCMD_IO_DONE(s_cmd); +#else + PS3_IO_BACK_ERR_INC(cmd->instance, s_cmd, cmd->index); + scsi_cmnd_hoststatus_set(s_cmd, DID_RESET); + data = scsi_device_private_data(s_cmd); + if (likely(data != NULL) && data->lock_mgr.hash_mgr != NULL) + ps3_r1x_write_unlock(&data->lock_mgr, cmd); + ps3_scsi_cmd_done(cmd); + +#endif + } + ps3_mutex_unlock(&instance->recovery_context->free_cmd_lock); + LOG_WARN("hno:%u force stop scsi cmd count[%u] during hard recovery\n", + PS3_HOST(instance), count); + +} + +void ps3_mgr_cmd_force_stop(struct ps3_instance *instance) +{ + unsigned int i = 0; + struct ps3_cmd *cmd = NULL; + struct ps3_cmd *abort_cmd = NULL; + struct ps3_cmd_context *context = &instance->cmd_context; + unsigned long cmd_lock_flags = 0; + + for (i = context->max_scsi_cmd_count; i < context->max_cmd_count; i++) { + cmd = context->cmd_buf[i]; + ps3_spin_lock_irqsave(&cmd->cmd_state.lock, &cmd_lock_flags); + if ((cmd->cmd_state.state == PS3_CMD_STATE_INIT) || + (cmd->cmd_state.state == PS3_CMD_STATE_COMPLETE)) { + LOG_FILE_WARN("hno:%u init or complete CFID:%d\n", + PS3_HOST(instance), i); + ps3_spin_unlock_irqrestore(&cmd->cmd_state.lock, + cmd_lock_flags); + continue; + } + + if (cmd->cmd_state.state == PS3_CMD_STATE_DEAD) { + LOG_FILE_WARN("hno:%u force free CFID:%d\n", + PS3_HOST(instance), i); + PS3_MGR_CMD_BACK_INC(instance, cmd, + PS3_REPLY_WORD_FLAG_FAIL); + if (PS3_MGR_CMD_TYPE(cmd) == PS3_CMD_IOCTL) { + LOG_FILE_WARN( + "hno:%u force free CFID:%d ioctl buff\n", + PS3_HOST(instance), i); + ps3_ioctl_buff_release(cmd); + } + if (cmd == instance->event_context.event_cmd) + instance->event_context.event_cmd = NULL; + ps3_mgr_cmd_free_nolock(instance, cmd); + + ps3_spin_unlock_irqrestore(&cmd->cmd_state.lock, + cmd_lock_flags); + continue; + } + + if (cmd->req_frame->mgrReq.reqHead.noReplyWord == + PS3_CMD_WORD_NO_REPLY_WORD) { + LOG_FILE_WARN( + "hno:%u force complete no reply word CFID:%d\n", + PS3_HOST(instance), i); + if (ps3_is_scsi_task_cmd(cmd)) { + cmd->resp_frame->normalRespFrame.respStatus = + SCSI_STATUS_GOOD; + } else { + cmd->resp_frame->normalRespFrame.respStatus = + PS3_DRV_MGR_BUSY; + } + PS3_MGR_CMD_BACK_INC(instance, cmd, + PS3_REPLY_WORD_FLAG_FAIL); + ps3_spin_unlock_irqrestore(&cmd->cmd_state.lock, + cmd_lock_flags); + continue; + } + + if (ps3_is_scsi_task_cmd(cmd)) { + cmd->resp_frame->normalRespFrame.respStatus = + SCSI_STATUS_GOOD; + PS3_MGR_CMD_BACK_INC(instance, cmd, + PS3_REPLY_WORD_FLAG_FAIL); + cmd->cmd_state.state = PS3_CMD_STATE_COMPLETE; + complete(&cmd->sync_done); + LOG_FILE_WARN( + "hno:%u force complete task mgr CFID:%d\n", + PS3_HOST(instance), i); + ps3_spin_unlock_irqrestore(&cmd->cmd_state.lock, + cmd_lock_flags); + continue; + } + + if (cmd->req_frame->mgrReq.syncFlag == 1) { + PS3_MGR_CMD_BACK_INC(instance, cmd, + PS3_REPLY_WORD_FLAG_FAIL); + cmd->resp_frame->normalRespFrame.respStatus = + PS3_DRV_MGR_BUSY; + cmd->cmd_state.state = PS3_CMD_STATE_COMPLETE; + LOG_FILE_WARN( + "hno:%u force complete sync mgr CFID:%d\n", + PS3_HOST(instance), i); + complete(&cmd->sync_done); + } else { + if (instance->is_need_event) { + if (cmd == instance->event_context.event_cmd || + cmd == instance->dev_context.vd_pending_cmd || + cmd == instance->webSubscribe_context + .webSubscribe_cmd) { + ps3_spin_unlock_irqrestore( + &cmd->cmd_state.lock, + cmd_lock_flags); + LOG_INFO( + "trace_id[0x%llx], hno:%u Event cmd:%d ignore!\n", + cmd->trace_id, + PS3_HOST(instance), cmd->index); + continue; + } + } + } + ps3_spin_unlock_irqrestore(&cmd->cmd_state.lock, + cmd_lock_flags); + } + if (instance->is_need_event) { + if (instance->event_context.event_abort_cmd != NULL) { + abort_cmd = instance->event_context.event_abort_cmd; + instance->event_context.event_abort_cmd = NULL; + } + if (abort_cmd != NULL) + ps3_task_cmd_free(instance, abort_cmd); + + if (instance->dev_context.vdpending_abort_cmd != NULL) { + abort_cmd = instance->dev_context.vdpending_abort_cmd; + instance->dev_context.vdpending_abort_cmd = NULL; + } + if (abort_cmd != NULL) + ps3_task_cmd_free(instance, abort_cmd); + + if (instance->webSubscribe_context.web_abort_cmd != NULL) { + abort_cmd = + instance->webSubscribe_context.web_abort_cmd; + instance->webSubscribe_context.web_abort_cmd = NULL; + } + if (abort_cmd != NULL) + ps3_task_cmd_free(instance, abort_cmd); + } +} + +void ps3_cmd_force_stop(struct ps3_instance *instance) +{ + int ret_code = 0; + struct ps3_cmd_context *context = &instance->cmd_context; + + if (context->cmd_buf == NULL) + return; + + if (ps3_pci_err_recovery_get(instance)) + ret_code = PS3_SCSI_RESULT_HOST_STATUS(DID_NO_CONNECT); + else + ret_code = ps3_get_requeue_or_reset(); + + ps3_r1x_conflict_queue_clean_all(instance, ret_code, PS3_FALSE); + + ps3_qos_hard_reset(instance); + ps3_scsi_cmd_force_stop(instance); + ps3_mgr_cmd_force_stop(instance); +} + +unsigned char ps3_is_need_hard_reset(struct ps3_instance *instance) +{ + unsigned char need_hardreset = PS3_FALSE; + unsigned char is_support_halt = PS3_FALSE; + unsigned int hard_reset_enable = 0; + int cur_state = PS3_INSTANCE_STATE_INIT; + + if (!ps3_feature_support_reg_get(instance)) + goto l_out; + + is_support_halt = PS3_IOC_STATE_HALT_SUPPORT(instance); + + LOG_INFO("hno:%u instance state not support hardreset!\n", + PS3_HOST(instance)); + + if (is_support_halt && (cur_state != PS3_INSTANCE_STATE_DEAD)) { + ps3_atomic_set(&instance->state_machine.state, + PS3_INSTANCE_STATE_DEAD); + instance->ioc_adpter->ioc_force_to_halt(instance); + LOG_INFO("hno:%u instance state while support halt!\n", + PS3_HOST(instance)); + while (1) + ps3_msleep(10); + } + + if (PS3_INSTANCE_ABNORMAL_FORCE_HARD_RECOVERY(instance)) { + need_hardreset = PS3_TRUE; + goto l_out; + } + + hard_reset_enable = ps3_hard_reset_enable_query(); + cur_state = ps3_atomic_read(&instance->state_machine.state); + + LOG_WARN("hno[%u], ready to hard reset,instance state: %s\n", + PS3_HOST(instance), namePS3InstanceState(cur_state)); + if ((!PS3_IOC_HARD_RECOVERY_SUPPORT(instance)) || + (!hard_reset_enable)) { + LOG_INFO("hno:%u instance state not support hardreset!\n", + PS3_HOST(instance)); + ps3_instance_state_transfer_to_dead(instance); + } else { + need_hardreset = PS3_TRUE; + } + +l_out: + return need_hardreset; +} + +int ps3_hard_reset_to_ready_with_doorbell(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + + if (instance->peer_instance != NULL && + instance->recovery_context->parall_hardreset_state == + PS3_PARALLEL_HARDRESET_STATE_PENDING) { + instance->recovery_context->parall_hardreset_state = + PS3_PARALLEL_HARDRESET_STATE_CONTINUE; + while (instance->recovery_context->parall_hardreset_state != + PS3_PARALLEL_HARDRESET_STATE_INIT) { + ps3_msleep( + PS3_PARALLEL_HARDRESET_STATE_WAIT_INIT_INTERVAL); + } + } else { + ret = ps3_hard_recovery_request(instance); + if (ret != PS3_SUCCESS) { + LOG_WARN("hno:%u hard recovery request NOK\n", + PS3_HOST(instance)); + goto l_out; + } + } + ps3_recovery_cancel_work_sync(instance); + +l_out: + return ret; +} +int ps3_init_fail_hard_reset_with_doorbell(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + + LOG_WARN("hno[%u], ready to hard reset\n", PS3_HOST(instance)); + if (instance->recovery_context->recovery_state == + PS3_HARD_RECOVERY_SHALLOW) { + goto l_out; + } + + if (instance->peer_instance != NULL && + instance->recovery_context->parall_hardreset_state == + PS3_PARALLEL_HARDRESET_STATE_PENDING) { + instance->recovery_context->parall_hardreset_state = + PS3_PARALLEL_HARDRESET_STATE_CONTINUE; + while (instance->recovery_context->parall_hardreset_state != + PS3_PARALLEL_HARDRESET_STATE_INIT) { + ps3_msleep( + PS3_PARALLEL_HARDRESET_STATE_WAIT_INIT_INTERVAL); + } + } else { + ret = ps3_hard_recovery_request_with_retry(instance); + if (ret != PS3_SUCCESS) { + LOG_WARN("hno:%u hard recovery request NOK\n", + PS3_HOST(instance)); + goto l_out; + } + } + ps3_recovery_cancel_work_sync(instance); + + if (instance->ioc_adpter->ioc_state_get(instance) != + PS3_FW_STATE_READY) { + ret = -PS3_FAILED; + } + + LOG_WARN("device[%d] hard reset success,exit init proc %d.\n", + instance->pdev->dev.id, ret); + return ret; +l_out: + if (ps3_recovery_is_state_halt(instance)) { + LOG_WARN("hno:%u driver_state:DEAD or HALT now !!!\n", + PS3_HOST(instance)); + ret = -PS3_FAILED; + goto l_end; + } + + if ((!PS3_IOC_HARD_RECOVERY_SUPPORT(instance)) || + (!ps3_hard_reset_enable_query())) { + LOG_ERROR( + "hno:%u soc feature unsupport Hard reset! need to be offline!\n", + PS3_HOST(instance)); + + ret = -PS3_FAILED; + ps3_msleep(PS3_DEFAULT_MGR_CMD_TIMEOUT * 1000); + goto l_end; + } + + ret = instance->ioc_adpter->ioc_force_to_fault(instance); + if (ret != PS3_SUCCESS) { + LOG_ERROR("device[%d] doorbell fault NOK.\n", + instance->pdev->dev.id); + } + + if (!instance->ioc_adpter->ioc_hard_reset) { + ret = -PS3_FAILED; + LOG_ERROR("device[%d] ioc_hard_reset is null.\n", + instance->pdev->dev.id); + goto l_end; + } + ret = instance->ioc_adpter->ioc_hard_reset(instance); + if (ret != PS3_SUCCESS) { + LOG_ERROR("device[%d] hard reset NOK,exit init proc.\n", + instance->pdev->dev.id); + goto l_end; + } + LOG_WARN("device[%d] hard reset success,exit init proc.\n", + instance->pdev->dev.id); + ret = -PS3_FAILED; +l_end: + return ret; +} + +int ps3_hardreset_handle_pre(struct ps3_instance *instance) +{ + unsigned int wait_count = 0; + unsigned long flags = 0; + + LOG_DEBUG("hno:%u functoin[%d], hardreset handle pre start\n", + PS3_HOST(instance), ps3_get_pci_function(instance->pdev)); + + if (ps3_pci_err_recovery_get(instance)) { + LOG_WARN("hno:%u pci recovery resetting\n", + PS3_HOST(instance)); + return -PS3_IN_PCIE_ERR; + } + if (!PS3_IS_INSTANCE_PROBE_INIT(instance)) { + if ((!PS3_IOC_HARD_RECOVERY_SUPPORT(instance)) || + (!ps3_hard_reset_enable_query())) { + LOG_ERROR( + "hno:%u soc feature unsupport Hard reset! need to be offline!\n", + PS3_HOST(instance)); + return -PS3_FAILED; + } + } + ps3_spin_lock_irqsave(&instance->recovery_context->recovery_lock, + &flags); + if (ps3_atomic_read(&instance->event_context.abort_eventcmd) != 0) + ps3_atomic_set(&instance->event_context.abort_eventcmd, 0); + + if (ps3_atomic_read(&instance->hardreset_event) == 0) + ps3_atomic_set(&instance->hardreset_event, 1); + ps3_spin_unlock_irqrestore(&instance->recovery_context->recovery_lock, + flags); + + if (PS3_IS_INSTANCE_PROBE(instance) && instance->is_scan_host_finish && + !instance->is_probe_finish) { + LOG_INFO("hno:%u recovery wait until probe finish/failed\n", + PS3_HOST(instance)); + while (wait_count < PS3_RECOVERY_WAIT_PROBE_FINISH_LOOP_COUNT) { + if (instance->is_probe_finish || + instance->is_probe_failed) { + break; + } + + wait_count++; + ps3_msleep(PS3_RECOVERY_WAIT_LOOP_TIME_INTERVAL_20MS); + } + + LOG_INFO( + "hno:%u probe finish(%d)/failed(%d), continue recovery\n", + PS3_HOST(instance), instance->is_probe_finish, + instance->is_probe_failed); + } + + ps3_wait_watchdog_dect_recovery(instance); + + LOG_DEBUG("hno:%u functoin[%d], hardreset handle pre end\n", + PS3_HOST(instance), ps3_get_pci_function(instance->pdev)); + + return PS3_SUCCESS; +} + +int ps3_hardreset_handle_wait_ready(struct ps3_instance *instance) +{ + int err_code = PS3_RECOVERY_INTER_ERR_SUCCESS; + unsigned int ioc_state = 0; + int ret = PS3_SUCCESS; + unsigned short cur_cnt = 0; + unsigned char printed = PS3_TRUE; + + LOG_DEBUG("hno:%u function[%d], hardreset handle wait ready start.\n", + PS3_HOST(instance), ps3_get_pci_function(instance->pdev)); + + instance->dump_context.is_hard_recovered = PS3_TRUE; + ioc_state = instance->ioc_adpter->ioc_state_get(instance); + LOG_DEBUG("hno:%u ioc state is %s! function:%d\n", PS3_HOST(instance), + ps3_ioc_state_print(ioc_state), + ps3_get_pci_function(instance->pdev)); + + while (ps3_atomic_read(&instance->is_err_scsi_processing) > 0) { + ps3_msleep(10); + if (printed && + ((++cur_cnt) * 10 > PS3_RECOVERY_WHILE_PRINT_REACH_TIME)) { + printed = PS3_FALSE; + LOG_WARN("host:%u wait ready wait err scsi process\n", + PS3_HOST(instance)); + } + } + instance->ioc_adpter->irq_disable(instance); +#ifndef _WINDOWS + ps3_irqs_sync(instance); +#endif + + if (ps3_pci_err_recovery_get(instance)) { + LOG_WARN("hno:%u pci recovery resetting\n", + PS3_HOST(instance)); + err_code = PS3_RECOVERY_INTER_ERR_PCIE_ERR; + goto l_out; + } + + ret = ps3_ioc_state_transfer_to_ready(instance); + if (ret != PS3_SUCCESS) { + LOG_ERROR("hno:%u hard reset to ready NOK,%s!\n", + PS3_HOST(instance), ps3_ioc_state_print(ioc_state)); + if (ret == -PS3_IN_PCIE_ERR) { + err_code = PS3_RECOVERY_INTER_ERR_PCIE_ERR; + } else { + if (ps3_instance_state_transfer( + instance->peer_instance, + PS3_INSTANCE_STATE_READY, + PS3_INSTANCE_STATE_RECOVERY) != + PS3_SUCCESS) { + LOG_WARN("hno:%u transfer to recovery NOK!\n", + PS3_HOST(instance->peer_instance)); + err_code = PS3_RECOVERY_INTER_ERR_INTERRUPT; + goto l_out; + } + err_code = PS3_RECOVERY_INTER_ERR_NEED_RECOVERY; + } + goto l_out; + } + if (instance->state_machine.is_load || + instance->state_machine.is_suspend) { + if (ps3_instance_state_transfer( + instance, PS3_INSTANCE_STATE_RECOVERY, + PS3_INSTANCE_STATE_READY) != PS3_SUCCESS) { + LOG_ERROR("hno:%u transfer to ready NOK!\n", + PS3_HOST(instance)); + err_code = PS3_RECOVERY_INTER_ERR_INTERRUPT; + } + } + +l_out: + LOG_DEBUG("hno:%u function[%d], hardreset handle wait ready end.\n", + PS3_HOST(instance), ps3_get_pci_function(instance->pdev)); + + return err_code; +} + +int ps3_hardreset_handle_init_running(struct ps3_instance *instance) +{ + unsigned int err_code = PS3_RECOVERY_INTER_ERR_SUCCESS; + + if (!instance->is_probe_finish || instance->is_probe_failed || + !instance->state_machine.is_load) { + ps3_recovery_ready_to_force_cmd_stop(instance); + LOG_ERROR("hno:%u reset while probe or in shutdown/remove,\n" + "\tfinish[%d], failed[%d], is_load[%d]\n", + PS3_HOST(instance), instance->is_probe_finish, + instance->is_probe_failed, + instance->state_machine.is_load); + ps3_instance_state_transfer(instance, PS3_INSTANCE_STATE_READY, + PS3_INSTANCE_STATE_RECOVERY); + instance->is_half_hard_reset = PS3_TRUE; + if (!instance->state_machine.is_load) + ps3_ioc_notify_unload(instance); + goto l_out; + } + + err_code = ps3_hard_recovery_to_pre_operational(instance); +l_out: + return err_code; +} + +int ps3_hardreset_handle_post(struct ps3_instance *instance) +{ + return ps3_hard_recovery_to_operational(instance); +} + +int ps3_hardreset_handle_finish(struct ps3_instance *instance) +{ + unsigned int cur_state = 0; + int ret = PS3_SUCCESS; + + cur_state = ps3_atomic_read(&instance->state_machine.state); + if (cur_state != PS3_INSTANCE_STATE_OPERATIONAL) { + ps3_instance_state_transfer_to_dead(instance); + LOG_ERROR("hno:%u hard reset NOK! cur_state:%s\n", + PS3_HOST(instance), namePS3InstanceState(cur_state)); + return -PS3_FAILED; + } + + ret = ps3_recovery_complete(instance); + if (ret != PS3_SUCCESS) { + LOG_WARN( + "hno:%u hard reset opeational but complete failed! cur_state:%s,ret %d\n", + PS3_HOST(instance), namePS3InstanceState(cur_state), + ret); + return ret; + } + + ps3_hard_recovery_state_finish(instance); + LOG_INFO("hno:%u hard reset finish! cur_state:%s\n", + PS3_HOST(instance), namePS3InstanceState(cur_state)); + + return PS3_SUCCESS; +} + +int ps3_hardreset_handle_offline(struct ps3_instance *instance) +{ + unsigned int cur_state = 0; + + ps3_instance_state_transfer_to_dead(instance); + cur_state = ps3_atomic_read(&instance->state_machine.state); + LOG_ERROR("hno:%u hard reset finish! cur_state:%s\n", + PS3_HOST(instance), namePS3InstanceState(cur_state)); + return PS3_SUCCESS; +} + +int ps3_softreset_handle_pre(struct ps3_instance *instance) +{ + int ret = PS3_RECOVERY_INTER_ERR_SUCCESS; + + ps3_cmd_reset_flag_set(instance, PS3_CMD_FLAG_SOFTRESET); + + ret = ps3_ioc_state_transfer_wait_to_running(instance); + if (ret != PS3_SUCCESS) { + LOG_ERROR("hno:%u soft recovery to running failed!\n", + PS3_HOST(instance)); + ret = PS3_RECOVERY_INTER_ERR_FAILED; + goto l_out; + } + ret = ps3_soft_recovery_cmd_reply_polling_check( + instance, PS3_SOFT_RESET_WAIT_TIMEOUT); + if (ret != PS3_RECOVERY_INTER_ERR_SUCCESS) { + LOG_ERROR("hno:%u pending mgr cmd no reply all!\n", + PS3_HOST(instance)); + goto l_out; + } + + ret = PS3_RECOVERY_INTER_ERR_SUCCESS; + LOG_INFO("hno:%u soft recovery to pre-operational success!\n", + PS3_HOST(instance)); +l_out: + return ret; +} + +int ps3_softreset_handle_post(struct ps3_instance *instance) +{ + int ret = PS3_RECOVERY_INTER_ERR_SUCCESS; + + if (!ps3_ioc_recovery_count_get( + instance, + &instance->recovery_context->ioc_recovery_count)) { + ret = PS3_RECOVERY_INTER_ERR_FAILED; + goto l_out; + } + + LOG_INFO( + "hno:%u pre-operational to operational success!,reset count:%d\n", + PS3_HOST(instance), + instance->recovery_context->ioc_recovery_count); +l_out: + return ret; +} + +int ps3_hard_recovery_request_with_retry(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + unsigned int count = 0; + + while ((ret = ps3_hard_recovery_request(instance)) == -PS3_RETRY) { + if (count++ == ps3_use_hard_reset_max_retry()) { + ret = -PS3_FAILED; + break; + } + ps3_mdelay(10); + } + + return ret; +} +int ps3_recovery_request_with_retry(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + unsigned int count = 0; + + while ((ret = ps3_recovery_request(instance)) == -PS3_RETRY) { + if (count++ == ps3_use_hard_reset_max_retry()) { + ret = -PS3_FAILED; + break; + } + ps3_mdelay(10); + } + + return ret; +} diff --git a/drivers/scsi/linkdata/ps3stor/ps3_recovery.h b/drivers/scsi/linkdata/ps3stor/ps3_recovery.h new file mode 100644 index 0000000000000000000000000000000000000000..f79ba430fb72370fb76a6338f34915b91ce88d62 --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/ps3_recovery.h @@ -0,0 +1,165 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _PS3_RECOVERY_H_ +#define _PS3_RECOVERY_H_ + +#ifndef _WINDOWS +#include +#include +#include +#include +#else +#include "ps3_worker.h" +#endif + +#include "ps3_platform_utils.h" +#include "ps3_inner_data.h" +#include "ps3_driver_log.h" +enum { + PS3_SOFT_RECOVERY_PROBE_PROCESS = 0, + PS3_SOFT_RECOVERY_SHALLOW = 1, + PS3_SOFT_RECOVERY_DEEP = 2, + PS3_SOFT_RECOVERY_IOC_RECOVERY = 3, + PS3_SOFT_RECOVERY_FINISH = 4, + PS3_HARD_RECOVERY_DECIDE = 5, + PS3_HARD_RECOVERY_SHALLOW = 6, + PS3_HARD_RECOVERY_FINISH = 7, + PS3_RESET_LOG_INTERVAL = 8, +}; + +enum { + PS3_HOST_RESET_INIT = 0, + PS3_HOST_RESET_START = 1, + PS3_HOST_RESET_HARD_RESET_DONE = 2, +}; +enum { + PS3_PARALLEL_HARDRESET_STATE_INIT = 0, + PS3_PARALLEL_HARDRESET_STATE_PENDING, + PS3_PARALLEL_HARDRESET_STATE_CONTINUE, +}; +#define PS3_PARALLEL_HARDRESET_STATE_WAIT_INIT_INTERVAL (5) +#define PS3_RECOVERY_CONTEXT_MAX_NUM (8) +#define PS3_RECOVERY_IRQ_NAME_MAX_LENGTH (48) +#define PS3_RECOVERY_WHILE_PRINT_REACH_TIME (10 * 1000) + +enum { + PS3_HEARTBEAT_NULL = 0, + PS3_HEARTBEAT_HARDRESET_DECIDE = 1, + PS3_HEARTBEAT_HARDRESET_RECOVERY = 2, + PS3_HEARTBEAT_HARDRESET_RETRY = 3, +}; + +enum { + PS3_IOC_CANNOT_HARDRESET = 0, + PS3_IOC_CAN_HARDRESET = 1, +}; + +struct ps3_recovery_context { + struct mutex free_cmd_lock; + spinlock_t recovery_lock; + spinlock_t ps3_hardreset_lock; + unsigned int ioc_recovery_count; + unsigned char recovery_state; + unsigned char reserved1; + unsigned char host_reset_state; + unsigned char heartbeat_recovery; +#ifndef _WINDOWS + struct workqueue_struct *recovery_wq; + char recovery_wq_name[20]; + struct work_struct recovery_work; +#else + struct ps3_worker recovery_work; +#endif + struct ps3_instance *instance[2]; + struct ps3_instance *work_instance; + int recovery_result; + unsigned int hardreset_count; + unsigned char parall_hardreset_state; + unsigned char instance_change; + unsigned char reserved[6]; + atomic_t hardreset_ref; + struct mutex ps3_watchdog_recovery_mutex; +}; +#define PS3_IS_INTERRUPT_SOFT_RECOVERY(instance) \ + (instance->recovery_context->recovery_state == PS3_HARD_RECOVERY_DECIDE) + +#define PS3_IS_INSTANCE_NOT_LOAD_NORMAL(instance) \ + (PS3_IS_INSTANCE_PROBE(instance) || \ + PS3_IS_INSTANCE_REMOVE(instance) || \ + PS3_IS_INSTANCE_SUSPEND_OR_RESUME(instance)) + +#define PS3_IS_INSTANCE_PROBE(instance) \ + (!(instance)->state_machine.is_suspend && \ + !((instance)->is_probe_finish || (instance)->is_probe_failed)) + +#define PS3_IS_INSTANCE_REMOVE(instance) \ + (!(instance)->state_machine.is_suspend && \ + ((instance)->is_probe_finish || (instance)->is_probe_failed) && \ + !(instance)->state_machine.is_load) + +#define PS3_INSTANCE_ABNORMAL_FORCE_HARD_RECOVERY(instance) \ + (PS3_IS_INSTANCE_NOT_LOAD_NORMAL(instance) || \ + ((instance)->peer_instance != NULL && \ + PS3_IS_INSTANCE_NOT_LOAD_NORMAL((instance)->peer_instance))) + +#define PS3_IS_INSTANCE_PROBE_INIT(instance) \ + (!((instance)->is_probe_finish || (instance)->is_probe_failed) && \ + !(instance)->state_machine.is_load) + +#define PS3_IS_INSTANCE_SUSPEND_OR_RESUME(instance) \ + (PS3_IS_INSTANCE_SUSPEND(instance) || PS3_IS_INSTANCE_RESUME(instance)) + +#define PS3_IS_INSTANCE_SUSPEND(instance) \ + ((instance)->state_machine.is_suspend && (instance)->is_suspend) + +#define PS3_IS_INSTANCE_RESUME(instance) \ + ((instance)->state_machine.is_suspend && (instance)->is_resume) + +int ps3_recovery_context_init(struct ps3_instance *instance); + +void ps3_recovery_function_init(struct ps3_instance *instance); + +void ps3_recovery_context_exit(struct ps3_instance *instance); + +void ps3_recovery_clean(struct ps3_instance *instance); + +void ps3_recovery_destroy(struct ps3_instance *instance); + +int ps3_recovery_request(struct ps3_instance *instance); + +int ps3_hard_recovery_request(struct ps3_instance *instance); + +void ps3_scsi_cmd_force_stop(struct ps3_instance *instance); +void ps3_mgr_cmd_force_stop(struct ps3_instance *instance); + +int ps3_recovery_cancel_work_sync(struct ps3_instance *instance); + +#ifndef _WINDOWS +int ps3_hard_reset_to_ready_with_doorbell(struct ps3_instance *instance); +int ps3_init_fail_hard_reset_with_doorbell(struct ps3_instance *instance); +unsigned char ps3_is_need_hard_reset(struct ps3_instance *instance); + +void ps3_recovery_work_queue_destroy(struct ps3_instance *instance); +#endif + +int ps3_hardreset_handle_pre(struct ps3_instance *instance); + +int ps3_hardreset_handle_wait_ready(struct ps3_instance *instance); + +int ps3_hardreset_handle_init_running(struct ps3_instance *instance); + +int ps3_hardreset_handle_post(struct ps3_instance *instance); + +int ps3_hardreset_handle_finish(struct ps3_instance *instance); + +int ps3_hardreset_handle_offline(struct ps3_instance *instance); + +int ps3_softreset_handle_pre(struct ps3_instance *instance); + +int ps3_softreset_handle_post(struct ps3_instance *instance); +int ps3_hard_recovery_request_with_retry(struct ps3_instance *instance); +int ps3_recovery_request_with_retry(struct ps3_instance *instance); +irqreturn_t ps3_recovery_irq_handler(int virq, void *dev_id); +int ps3_recovery_irq_start(struct ps3_instance *instance); + +void ps3_cmd_force_stop(struct ps3_instance *instance); +#endif diff --git a/drivers/scsi/linkdata/ps3stor/ps3_sas_transport.c b/drivers/scsi/linkdata/ps3stor/ps3_sas_transport.c new file mode 100644 index 0000000000000000000000000000000000000000..ecd86f57b5983315b2f5e293ec01d997126d821e --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/ps3_sas_transport.c @@ -0,0 +1,935 @@ +// SPDX-License-Identifier: GPL-2.0 +#ifndef _WINDOWS +#include + +#include "ps3_sas_transport.h" +#include "ps3_mgr_channel.h" +#include "ps3_irq.h" +#include "ps3_mgr_cmd_err.h" +#include "ps3_mgr_cmd.h" +#include "ps3_driver_log.h" +#include "ps3_device_manager_sas.h" + +static struct scsi_transport_template *ps3_sas_transport_template; +extern struct ps3_sas_node * +ps3_sas_find_node_by_sas_addr(struct ps3_instance *instance, + unsigned long long sas_addr); +struct scsi_transport_template *ps3_sas_transport_get(void) +{ + return ps3_sas_transport_template; +} + +static inline struct ps3_instance *phy_to_ps3_instance(struct sas_phy *phy) +{ + struct Scsi_Host *s_host = dev_to_shost(phy->dev.parent); + + return (struct ps3_instance *)s_host->hostdata; +} + +static inline struct ps3_instance *rphy_to_ps3_instance(struct sas_rphy *rphy) +{ + struct Scsi_Host *s_host = dev_to_shost(rphy->dev.parent); + + return (struct ps3_instance *)s_host->hostdata; +} + +static inline int ps3_sas_request_pre_check(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + + if (!instance->state_machine.is_load) { + LOG_WARN("hno:%u instance state not is_load\n", + PS3_HOST(instance)); + ret = -EFAULT; + goto l_out; + } + + if (!ps3_is_instance_state_normal(instance, PS3_TRUE)) { + ret = -EFAULT; + goto l_out; + } + + if (ps3_pci_err_recovery_get(instance)) { + LOG_WARN("hno[%u] host in pci recovery\n", PS3_HOST(instance)); + ret = -EFAULT; + goto l_out; + } + +l_out: + return ret; +} + +static inline int ps3_sas_smp_pre_check(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + + ret = ps3_sas_request_pre_check(instance); + if (ret != PS3_SUCCESS) + goto l_out; + + LOG_INFO("hno:%u ready get semaphore\n", PS3_HOST(instance)); + if (down_interruptible( + &instance->sas_dev_context.ps3_sas_smp_semaphore)) { + LOG_WARN("hno:%u smp concurrency full\n", PS3_HOST(instance)); + ret = -EFAULT; + } + LOG_INFO("hno:%u got semaphore\n", PS3_HOST(instance)); + +l_out: + return ret; +} + +static inline void ps3_sas_linkerror_reqframe_build(struct ps3_cmd *cmd, + struct sas_phy *phy, + unsigned char encl_id) +{ + struct PS3MgrReqFrame *mgrReq = &cmd->req_frame->mgrReq; + + mgrReq->reqHead.timeout = cmd->time_out; + mgrReq->reqHead.traceID = cmd->trace_id; + mgrReq->reqHead.cmdType = PS3_CMD_SAS_MANAGEMENT; + mgrReq->reqHead.cmdSubType = PS3_SAS_GET_LINK_ERR; + mgrReq->reqHead.cmdFrameID = cmd->index; + mgrReq->reqHead.control = 0; + mgrReq->syncFlag = 1; + mgrReq->timeout = 0; + mgrReq->sgeOffset = offsetof(struct PS3MgrReqFrame, sgl) >> + PS3_MGR_CMD_SGL_OFFSET_DWORD_SHIFT; + mgrReq->sgeCount = 1; + mgrReq->sgl[0].length = cpu_to_le32(sizeof(struct PS3LinkErrInfo)); + mgrReq->sgl[0].addr = cpu_to_le64(cmd->ext_buf_phys); + mgrReq->sgl[0].lastSge = 1; + mgrReq->sgl[0].ext = 0; + + mgrReq->value.sasMgr.sasAddr = cpu_to_le64(phy->identify.sas_address); + mgrReq->value.sasMgr.enclID = encl_id; + mgrReq->value.sasMgr.phyCount = 1; + mgrReq->value.sasMgr.startPhyID = phy->identify.phy_identifier; +} + +int ps3_sas_linkerrors_get(struct sas_phy *phy) +{ + int ret = 0; + int send_result = PS3_SUCCESS; + unsigned char encl_id = PS3_SAS_INVALID_ID; + struct ps3_cmd *cmd = NULL; + struct PS3LinkErrInfo *erroInfo = NULL; + struct ps3_instance *instance = phy_to_ps3_instance(phy); + + ret = ps3_sas_smp_pre_check(instance); + if (ret != PS3_SUCCESS) { + ret = -ENXIO; + goto l_out; + } + + encl_id = ps3_sas_encl_id_get(instance, phy->identify.sas_address); + if (encl_id == PS3_SAS_INVALID_ID) { + LOG_ERROR("hno:%u cannot foud PS3 node by sas_addr[%016llx]\n", + PS3_HOST(instance), phy->identify.sas_address); + ret = -EINVAL; + goto l_no_free_cmd; + } + ps3_atomic_inc(&instance->cmd_statistics.cmd_delivering); + if (ps3_sas_request_pre_check(instance) != PS3_SUCCESS) { + LOG_WARN_LIM( + "sas_addr[%016llx], hno:%u smp linkerror pre check NOK\n", + phy->identify.sas_address, PS3_HOST(instance)); + ps3_atomic_dec(&instance->cmd_statistics.cmd_delivering); + ret = -ENXIO; + goto l_no_free_cmd; + } + cmd = ps3_mgr_cmd_alloc(instance); + if (cmd == NULL) { + LOG_WARN("hno:%u not get a cmd packet\n", PS3_HOST(instance)); + ps3_atomic_dec(&instance->cmd_statistics.cmd_delivering); + ret = -ENOMEM; + goto l_no_free_cmd; + } + + cmd->time_out = PS3_SAS_TIMEOUT_SEC; + cmd->is_interrupt = PS3_DRV_FALSE; + + LOG_DEBUG( + "hno:%u trace_id[0x%llx] CFID [%u], sas_addr[%016llx] get mgr cmd succeed\n", + PS3_HOST(instance), cmd->trace_id, cmd->index, + phy->identify.sas_address); + + ps3_sas_linkerror_reqframe_build(cmd, phy, encl_id); + ps3_mgr_cmd_word_build(cmd); + send_result = ps3_cmd_send_sync(instance, cmd); + ps3_atomic_dec(&instance->cmd_statistics.cmd_delivering); + if (send_result == PS3_SUCCESS) + send_result = ps3_cmd_wait_sync(instance, cmd); + ret = ps3_mgr_complete_proc(instance, cmd, send_result); + if (ret == -PS3_CMD_NO_RESP) { + LOG_ERROR("hno:%u %d respStatus NOK CFID[%d] respStatus[%d]\n", + PS3_HOST(cmd->instance), ret, + cmd->cmd_word.cmdFrameID, ps3_cmd_resp_status(cmd)); + ret = -ETIMEDOUT; + goto l_no_free_cmd; + } + + if (ret != PS3_SUCCESS) { + LOG_ERROR("hno:%u %d respStatus NOK CFID[%d] respStatus[%d]\n", + PS3_HOST(cmd->instance), ret, + cmd->cmd_word.cmdFrameID, ps3_cmd_resp_status(cmd)); + ret = -EFAULT; + } else { + erroInfo = (struct PS3LinkErrInfo *)cmd->ext_buf; + phy->invalid_dword_count = + le32_to_cpu(erroInfo->invalidDwordCount); + phy->running_disparity_error_count = + le32_to_cpu(erroInfo->runningDisparityErrCount); + phy->loss_of_dword_sync_count = + le32_to_cpu(erroInfo->lossOfDwordSyncCount); + phy->phy_reset_problem_count = + le32_to_cpu(erroInfo->phyResetProblemCount); + ret = 0; + } + + LOG_DEBUG("hno:%u trace_id[0x%llx] CFID [%u], end, ret[%d]\n", + PS3_HOST(instance), cmd->trace_id, cmd->index, ret); + + ps3_mgr_cmd_free(instance, cmd); +l_no_free_cmd: + up(&instance->sas_dev_context.ps3_sas_smp_semaphore); +l_out: + return ret; +} + +int ps3_sas_enclosure_identifier_get(struct sas_rphy *rphy, u64 *identifier) +{ + int ret = 0; + struct ps3_instance *instance = rphy_to_ps3_instance(rphy); + *identifier = 0; + + LOG_DEBUG( + "hno:%u ----1---ready get encl identifier sas_addr[%016llx]\n", + PS3_HOST(instance), rphy->identify.sas_address); + + ret = ps3_sas_smp_pre_check(instance); + if (ret != PS3_SUCCESS) { + ret = -ENXIO; + goto l_out; + } + + LOG_DEBUG("hno:%u ready get encl identifier sas_addr[%016llx]\n", + PS3_HOST(instance), rphy->identify.sas_address); + + *identifier = ps3_sas_rphy_parent_sas_addr_get( + instance, rphy->identify.sas_address); + if (*identifier == PS3_SAS_INVALID_SAS_ADDR) { + ret = -ENXIO; + *identifier = 0; + } + + up(&instance->sas_dev_context.ps3_sas_smp_semaphore); +l_out: + LOG_DEBUG( + "hno:%u end get encl identifier sas_addr[%016llx], identifier[%llu]\n", + PS3_HOST(instance), rphy->identify.sas_address, *identifier); + return ret; +} + +int ps3_sas_bay_identifier_get(struct sas_rphy *rphy) +{ + unsigned int solt_id = 0; + struct ps3_instance *instance = rphy_to_ps3_instance(rphy); + + LOG_DEBUG("hno:%u ----1---ready get bay identifier sas_addr[%016llx]\n", + PS3_HOST(instance), rphy->identify.sas_address); + + if (ps3_sas_smp_pre_check(instance) != PS3_SUCCESS) { + solt_id = -ENXIO; + goto l_out; + } + + LOG_DEBUG("hno:%u ready get bay identifier sas_addr[%016llx]\n", + PS3_HOST(instance), rphy->identify.sas_address); + + if (ps3_sas_rphy_slot_get(instance, rphy->identify.sas_address, + &solt_id) != PS3_SUCCESS) { + solt_id = -ENXIO; + } + + up(&instance->sas_dev_context.ps3_sas_smp_semaphore); +l_out: + LOG_DEBUG( + "hno:%u end get bay identifier sas_addr[%016llx], slot_id[%d]\n", + PS3_HOST(instance), rphy->identify.sas_address, solt_id); + return solt_id; +} + +static inline void ps3_sas_ctrl_reqframe_build(struct ps3_cmd *cmd, + struct sas_phy *phy, + enum PhyCtrl ctrl_code) +{ + struct PS3MgrReqFrame *mgrReq = &cmd->req_frame->mgrReq; + + mgrReq->reqHead.timeout = cmd->time_out; + mgrReq->reqHead.traceID = cmd->trace_id; + mgrReq->reqHead.cmdType = PS3_CMD_SAS_MANAGEMENT; + mgrReq->reqHead.cmdSubType = PS3_SAS_PHY_CTRL; + mgrReq->reqHead.cmdFrameID = cmd->index; + mgrReq->reqHead.control = 0; + mgrReq->syncFlag = 1; + mgrReq->timeout = 0; + mgrReq->sgeCount = 0; + + mgrReq->value.phySet.sasAddr = cpu_to_le64(phy->identify.sas_address); + mgrReq->value.phySet.phyCtrl = ctrl_code; + mgrReq->value.phySet.phyID = phy->identify.phy_identifier; + mgrReq->value.phySet.maxLinkRate = phy->maximum_linkrate; + mgrReq->value.phySet.minLinkRate = phy->minimum_linkrate; +} + +static inline int __ps3_sas_phy_ctrl(struct sas_phy *phy, + enum PhyCtrl ctrl_code) +{ + int ret = PS3_SUCCESS; + int send_result = PS3_SUCCESS; + struct ps3_cmd *cmd = NULL; + struct ps3_instance *instance = phy_to_ps3_instance(phy); + + ret = ps3_sas_smp_pre_check(instance); + if (ret != PS3_SUCCESS) { + ret = -ENXIO; + goto l_out; + } + ps3_atomic_inc(&instance->cmd_statistics.cmd_delivering); + if (ps3_sas_request_pre_check(instance) != PS3_SUCCESS) { + LOG_WARN_LIM( + "sas_addr[%016llx], hno:%u smp phyctrl pre check NOK\n", + phy->identify.sas_address, PS3_HOST(instance)); + ps3_atomic_dec(&instance->cmd_statistics.cmd_delivering); + ret = -ENXIO; + goto l_no_free_cmd; + } + cmd = ps3_mgr_cmd_alloc(instance); + if (cmd == NULL) { + LOG_WARN("hno:%u not get a cmd packet\n", PS3_HOST(instance)); + ps3_atomic_dec(&instance->cmd_statistics.cmd_delivering); + ret = -ENOMEM; + goto l_no_free_cmd; + } + cmd->time_out = PS3_SAS_TIMEOUT_SEC; + cmd->is_interrupt = PS3_DRV_FALSE; + + LOG_DEBUG( + "hno:%u trace_id[0x%llx] CFID [%u], sas_addr[%016llx] ctrl_code[%s] get mgr cmd succeed\n", + PS3_HOST(instance), cmd->trace_id, cmd->index, + phy->identify.sas_address, + namePhyCtrl((enum PhyCtrl)ctrl_code)); + + ps3_sas_ctrl_reqframe_build(cmd, phy, ctrl_code); + ps3_mgr_cmd_word_build(cmd); + send_result = ps3_cmd_send_sync(instance, cmd); + ps3_atomic_dec(&instance->cmd_statistics.cmd_delivering); + if (send_result == PS3_SUCCESS) + send_result = ps3_cmd_wait_sync(instance, cmd); + ret = ps3_mgr_complete_proc(instance, cmd, send_result); + if (ret == -PS3_CMD_NO_RESP) { + LOG_ERROR("hno:%u %d respStatus NOK CFID[%d] respStatus[%d]\n", + PS3_HOST(cmd->instance), ret, + cmd->cmd_word.cmdFrameID, ps3_cmd_resp_status(cmd)); + ret = -ETIMEDOUT; + goto l_no_free_cmd; + } + + if (ret != PS3_SUCCESS) { + LOG_ERROR("hno:%u %d respStatus NOK CFID[%d] respStatus[%d]\n", + PS3_HOST(cmd->instance), ret, + cmd->cmd_word.cmdFrameID, ps3_cmd_resp_status(cmd)); + ret = -EFAULT; + } + + LOG_DEBUG("hno:%u trace_id[0x%llx] CFID [%u], end, ret[%d]\n", + PS3_HOST(instance), cmd->trace_id, cmd->index, ret); + ps3_mgr_cmd_free(instance, cmd); +l_no_free_cmd: + up(&instance->sas_dev_context.ps3_sas_smp_semaphore); +l_out: + return ret; +} + +int ps3_sas_phy_reset(struct sas_phy *phy, int hard_reset) +{ + int ret = PS3_SUCCESS; + enum PhyCtrl ctrl_code = + hard_reset ? PS3_SAS_CTRL_RESET_HARD : PS3_SAS_CTRL_RESET; + struct sas_phy *tmp_phy = kcalloc(1, sizeof(struct sas_phy), GFP_KERNEL); + + if (tmp_phy == NULL) { + LOG_ERROR("kcalloc sas_phy fail!\n"); + ret = -ENOMEM; + goto l_out; + } + memcpy(tmp_phy, phy, sizeof(struct sas_phy)); + tmp_phy->maximum_linkrate = 0; + tmp_phy->minimum_linkrate = 0; + + LOG_INFO("enter phy reset, phy sas_addr[%016llx], is_hard[%d]\n", + phy->identify.sas_address, hard_reset); + ret = __ps3_sas_phy_ctrl(tmp_phy, ctrl_code); + kfree(tmp_phy); + tmp_phy = NULL; + +l_out: + return ret; +} + +int ps3_sas_phy_enable(struct sas_phy *phy, int enable) +{ + int ret = PS3_SUCCESS; + enum PhyCtrl ctrl_code = + enable ? PS3_SAS_CTRL_RESET : PS3_SAS_CTRL_DISABLE; + struct sas_phy *tmp_phy = kcalloc(1, sizeof(struct sas_phy), GFP_KERNEL); + + if (tmp_phy == NULL) { + LOG_ERROR("kcalloc sas_phy fail!\n"); + ret = -ENOMEM; + goto l_out; + } + memcpy(tmp_phy, phy, sizeof(struct sas_phy)); + tmp_phy->maximum_linkrate = 0; + tmp_phy->minimum_linkrate = 0; + + LOG_INFO("enter phy enable, phy sas_addr[%016llx], is_enable[%d]\n", + phy->identify.sas_address, enable); + ret = __ps3_sas_phy_ctrl(tmp_phy, ctrl_code); + kfree(tmp_phy); + tmp_phy = NULL; + +l_out: + return ret; +} + +int ps3_sas_update_phy_info(struct sas_phy *phy) +{ + int ret = -PS3_FAILED; + struct PS3SasMgr sas_req_param; + + struct ps3_instance *instance = phy_to_ps3_instance(phy); + struct PS3PhyInfo *phy_info = + instance->sas_dev_context.ps3_sas_phy_buff; + unsigned long flags = 0; + + struct ps3_sas_node *sas_node = ps3_sas_find_node_by_sas_addr( + instance, phy->identify.sas_address); + + memset(&sas_req_param, 0, sizeof(sas_req_param)); + sas_req_param.enclID = sas_node->encl_id; + sas_req_param.sasAddr = cpu_to_le64(sas_node->sas_address); + sas_req_param.startPhyID = phy->identify.phy_identifier; + sas_req_param.phyCount = 1; + + LOG_DEBUG("hno:%u ready get phys[%d] of encl_id[%d] !\n", + PS3_HOST(instance), phy->identify.phy_identifier, + sas_req_param.enclID); + + memset(phy_info, 0, PS3_SAS_REQ_BUFF_LEN); + ret = ps3_sas_phy_get(instance, &sas_req_param); + if (ret != PS3_SUCCESS) { + LOG_ERROR("hno:%u get phy info NOK\n", PS3_HOST(instance)); + goto l_out; + } + + spin_lock_irqsave(&instance->sas_dev_context.ps3_sas_node_lock, flags); + LOG_INFO_IN_IRQ(instance, + "hno:%u ready update phy %d of encl_id[%d]!\n", + PS3_HOST(instance), phy->identify.phy_identifier, + sas_req_param.enclID); + + ps3_sas_node_phy_update(instance, + &sas_node->phys[phy->identify.phy_identifier], + &phy_info[0]); + spin_unlock_irqrestore(&instance->sas_dev_context.ps3_sas_node_lock, + flags); +l_out: + return ret; +} + +int ps3_sas_linkrates_set(struct sas_phy *phy, struct sas_phy_linkrates *rates) +{ + int ret = PS3_SUCCESS; + unsigned char tmp_min = 0; + unsigned char tmp_max = 0; + + LOG_INFO("enter link rate set, phy sas_addr[%016llx],\n" + "\tminimum_linkrate[%d], maximum_linkrate[%d]\n", + phy->identify.sas_address, rates->minimum_linkrate, + rates->maximum_linkrate); + + if (!rates->minimum_linkrate) + rates->minimum_linkrate = phy->minimum_linkrate; + else if (rates->minimum_linkrate < phy->minimum_linkrate_hw) + rates->minimum_linkrate = phy->minimum_linkrate_hw; + + if (!rates->maximum_linkrate) + rates->maximum_linkrate = phy->maximum_linkrate; + else if (rates->maximum_linkrate > phy->maximum_linkrate_hw) + rates->maximum_linkrate = phy->maximum_linkrate_hw; + + if (rates->maximum_linkrate < phy->minimum_linkrate || + rates->minimum_linkrate > phy->maximum_linkrate) { + LOG_ERROR( + "linkrate set param NOK, %d phy sas_addr[%016llx],\n" + "\trate minimum_linkrate[%d] > cur maximum_linkrate[%d] or\n" + "\trate maximum_linkrate[%d] < cur minimum_linkrate[%d]\n", + phy->identify.phy_identifier, phy->identify.sas_address, + rates->minimum_linkrate, phy->maximum_linkrate, + rates->maximum_linkrate, phy->minimum_linkrate); + ret = -EINVAL; + goto l_out; + } + + tmp_min = phy->minimum_linkrate; + tmp_max = phy->maximum_linkrate; + + phy->minimum_linkrate = rates->minimum_linkrate; + phy->maximum_linkrate = rates->maximum_linkrate; + + ret = __ps3_sas_phy_ctrl(phy, PS3_SAS_CTRL_RESET); + if (ret != PS3_SUCCESS) { + LOG_ERROR("linkrate NOK, phy sas_addr[%016llx],\n" + "\tminimum_linkrate[%d], maximum_linkrate[%d]\n", + phy->identify.sas_address, rates->minimum_linkrate, + rates->maximum_linkrate); + + phy->minimum_linkrate = tmp_min; + phy->maximum_linkrate = tmp_max; + goto l_out; + } + + ps3_sas_update_phy_info(phy); +l_out: + return ret; +} + +static inline void ps3_sas_smp_reqframe_build(struct ps3_cmd *cmd, + unsigned long long sas_addr, + unsigned int req_data_len) +{ + struct PS3MgrReqFrame *mgrReq = &cmd->req_frame->mgrReq; + + mgrReq->reqHead.timeout = cmd->time_out; + mgrReq->reqHead.traceID = cmd->trace_id; + mgrReq->reqHead.cmdType = PS3_CMD_SAS_MANAGEMENT; + mgrReq->reqHead.cmdSubType = PS3_SAS_SMP_REQUEST; + mgrReq->reqHead.cmdFrameID = cmd->index; + mgrReq->reqHead.control = 0; + mgrReq->syncFlag = 1; + mgrReq->timeout = 0; + + mgrReq->sgeOffset = offsetof(struct PS3MgrReqFrame, sgl) >> + PS3_MGR_CMD_SGL_OFFSET_DWORD_SHIFT; + mgrReq->sgeCount = 1; + mgrReq->sgl[0].length = + cpu_to_le32(cmd->instance->cmd_context.ext_buf_size); + mgrReq->sgl[0].addr = cpu_to_le64(cmd->ext_buf_phys); + mgrReq->sgl[0].lastSge = 1; + mgrReq->sgl[0].ext = 0; + + mgrReq->value.sasMgr.sasAddr = cpu_to_le64(sas_addr); + mgrReq->value.sasMgr.reqLen = + cpu_to_le16(req_data_len - PS3_SMP_CRC_LEN); +} + +static inline void show_smp(unsigned char *data, unsigned short len) +{ + unsigned short i = 0; + char tmp_buf[256] = { 0 }; + char *p_tmp = tmp_buf; + + LOG_DEBUG("smp frame data start\n"); + while (len != 0) { + memset(tmp_buf, 0, sizeof(char) * 256); + for (i = 0; i < 32 && len != 0; i++, len--) { + snprintf(p_tmp, 4, " %02x", *data++); + p_tmp += 3; + } + LOG_DEBUG("smp frame data is ==[%s]==\n", tmp_buf); + p_tmp = tmp_buf; + } + + LOG_DEBUG("smp frame data end\n"); +} + +#if defined(PS3_SAS_SMP_RETURN) + +static inline unsigned int ps3_sas_req_to_ext_buf(struct ps3_instance *instance, + struct request *req, + void *ext_buf) +{ +#if defined(PS3_SYPPORT_BIO_ITER) + struct bio_vec bvec; + struct bvec_iter iter; +#else + struct bio_vec *bvec = NULL; + unsigned int i = 0; +#endif + + unsigned int req_len = 0; + + if (unlikely(blk_rq_bytes(req) > instance->cmd_context.ext_buf_size)) { + LOG_ERROR( + "hno:%u request is too big!(req_len:%d > ext_buf_len:%d\n", + PS3_HOST(instance), blk_rq_bytes(req), + instance->cmd_context.ext_buf_size); + goto l_out; + } + +#if defined(PS3_SYPPORT_BIO_ITER) + bio_for_each_segment(bvec, req->bio, iter) { + memcpy((unsigned char *)ext_buf + req_len, + page_address(bvec.bv_page) + bvec.bv_offset, + bvec.bv_len); + req_len += bvec.bv_len; + } +#else + bio_for_each_segment(bvec, req->bio, i) { + memcpy((unsigned char *)ext_buf + req_len, + page_address(bvec->bv_page) + bvec->bv_offset, + bvec->bv_len); + req_len += bvec->bv_len; + } +#endif + +l_out: + return req_len; +} + +static inline int ps3_sas_ext_buf_to_rsp(struct ps3_instance *instance, + struct request *req, void *ext_buf) +{ + int ret = PS3_SUCCESS; + struct request *rsp = req->next_rq; +#if defined(PS3_SYPPORT_BIO_ITER) + struct bio_vec bvec; + struct bvec_iter iter; +#else + struct bio_vec *bvec = NULL; + unsigned int i = 0; +#endif + unsigned int offset = 0; + unsigned short rsq_data_len = 0; + unsigned short smp_len = 0; + + if (rsp == NULL) { + ret = -PS3_FAILED; + LOG_ERROR("hno:%u rsp == NULL\n", PS3_HOST(instance)); + goto l_out; + } + + rsq_data_len = + min(blk_rq_bytes(rsp), instance->cmd_context.ext_buf_size); + + smp_len = ((unsigned char *)ext_buf)[3] * 4 + 4; + rsp->resid_len -= smp_len; + LOG_DEBUG("hno:%u smp frame len[%d], rsq_data_len[%d]\n", + PS3_HOST(instance), smp_len, rsq_data_len); + + rsq_data_len = min(smp_len, rsq_data_len); + + show_smp((unsigned char *)ext_buf, rsq_data_len); +#if defined(PS3_SYPPORT_BIO_ITER) + bio_for_each_segment(bvec, rsp->bio, iter) { + if (rsq_data_len <= bvec.bv_len) { + memcpy(page_address(bvec.bv_page) + bvec.bv_offset, + (unsigned char *)ext_buf + offset, rsq_data_len); + break; + } + memcpy(page_address(bvec.bv_page) + bvec.bv_offset, + (unsigned char *)ext_buf + offset, bvec.bv_len); + rsq_data_len -= bvec.bv_len; + offset += bvec.bv_len; + } +#else + bio_for_each_segment(bvec, rsp->bio, i) { + if (rsq_data_len <= bvec->bv_len) { + memcpy(page_address(bvec->bv_page) + bvec->bv_offset, + (unsigned char *)ext_buf + offset, rsq_data_len); + break; + } + memcpy(page_address(bvec->bv_page) + bvec->bv_offset, + (unsigned char *)ext_buf + offset, bvec->bv_len); + rsq_data_len -= bvec->bv_len; + offset += bvec->bv_len; + } +#endif +l_out: + return ret; +} +int ps3_sas_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy, + struct request *req) +{ + int ret = -PS3_FAILED; + int send_result = PS3_SUCCESS; + struct ps3_instance *instance = (struct ps3_instance *)shost->hostdata; + struct ps3_cmd *cmd = NULL; + unsigned int req_data_len = 0; + unsigned long long sas_addr = 0; + struct ps3_sas_node *ps3_sas_node = NULL; + + ret = ps3_sas_smp_pre_check(instance); + if (ret != PS3_SUCCESS) { + ret = -EFAULT; + goto l_out; + } + + sas_addr = (rphy) ? (rphy->identify.sas_address) : + (instance->sas_dev_context.ps3_hba_sas.sas_address); + ps3_sas_node = ps3_sas_find_node_by_sas_addr(instance, sas_addr); + if (ps3_sas_node == NULL) { + LOG_ERROR("hno:%u cannot find node[%llx] !\n", + PS3_HOST(instance), sas_addr); + up(&instance->sas_dev_context.ps3_sas_smp_semaphore); + ret = -EFAULT; + goto l_out; + } + + ps3_atomic_inc(&instance->cmd_statistics.cmd_delivering); + if (ps3_sas_request_pre_check(instance) != PS3_SUCCESS) { + LOG_WARN_LIM("sas_addr[%016llx], hno:%u smp pre check NOK\n", + sas_addr, PS3_HOST(instance)); + ps3_atomic_dec(&instance->cmd_statistics.cmd_delivering); + ret = -EFAULT; + goto l_no_free_cmd; + } + cmd = ps3_mgr_cmd_alloc(instance); + if (cmd == NULL) { + LOG_WARN("hno:%u not get a cmd packet\n", PS3_HOST(instance)); + ps3_atomic_dec(&instance->cmd_statistics.cmd_delivering); + ret = -ENOMEM; + goto l_no_free_cmd; + } + cmd->time_out = PS3_SAS_TIMEOUT_SEC; + cmd->is_interrupt = PS3_DRV_FALSE; + + req_data_len = ps3_sas_req_to_ext_buf(instance, req, cmd->ext_buf); + if (req_data_len == 0) { + ret = -ENOMEM; + ps3_atomic_dec(&instance->cmd_statistics.cmd_delivering); + goto l_out_failed; + } + + LOG_DEBUG( + "hno:%u trace_id[0x%llx] CFID [%u], sas_addr[%016llx], len[%u] send smp req\n", + PS3_HOST(instance), cmd->trace_id, cmd->index, sas_addr, + req_data_len); + + ps3_sas_smp_reqframe_build(cmd, sas_addr, req_data_len); + ps3_mgr_cmd_word_build(cmd); + send_result = ps3_cmd_send_sync(instance, cmd); + ps3_atomic_dec(&instance->cmd_statistics.cmd_delivering); + if (send_result == PS3_SUCCESS) + send_result = ps3_cmd_wait_sync(instance, cmd); + ret = ps3_mgr_complete_proc(instance, cmd, send_result); + if (ret == -PS3_CMD_NO_RESP) { + LOG_ERROR("hno:%u %d respStatus NOK CFID[%d] respStatus[%d]\n", + PS3_HOST(cmd->instance), ret, + cmd->cmd_word.cmdFrameID, ps3_cmd_resp_status(cmd)); + ret = -ETIMEDOUT; + goto l_no_free_cmd; + } + + if (ret != PS3_SUCCESS) { + LOG_ERROR("hno:%u %d respStatus NOK CFID[%d] respStatus[%d]\n", + PS3_HOST(cmd->instance), ret, + cmd->cmd_word.cmdFrameID, ps3_cmd_resp_status(cmd)); + ret = -ENXIO; + if (ret == -PS3_TIMEOUT) + ret = -ETIMEDOUT; + goto l_out_failed; + } + + ret = ps3_sas_ext_buf_to_rsp(instance, req, cmd->ext_buf); + if (ret != PS3_SUCCESS) { + LOG_ERROR("hno:%u %d smp response NOK CFID[%d]\n", + PS3_HOST(cmd->instance), ret, + cmd->cmd_word.cmdFrameID); + ret = -EINVAL; + goto l_out_failed; + } + + LOG_DEBUG("hno:%u trace_id[0x%llx] CFID [%u], end, ret[%d]\n", + PS3_HOST(instance), cmd->trace_id, cmd->index, ret); +l_out_failed: + ps3_mgr_cmd_free(instance, cmd); +l_no_free_cmd: + up(&instance->sas_dev_context.ps3_sas_smp_semaphore); +l_out: + return ret; +} +#else + +static inline unsigned int ps3_sas_req_to_ext_buf(struct ps3_instance *instance, + struct bsg_buffer *req_buf, + void *ext_buf) +{ + unsigned int req_len = 0; + + if (unlikely(req_buf->payload_len > + instance->cmd_context.ext_buf_size)) { + LOG_ERROR( + "hno:%u request is too big!(req_len:%d > ext_buf_len:%d\n", + PS3_HOST(instance), req_buf->payload_len, + instance->cmd_context.ext_buf_size); + goto l_out; + } + + req_len = sg_copy_to_buffer(req_buf->sg_list, req_buf->sg_cnt, ext_buf, + req_buf->payload_len); +l_out: + return req_len; +} + +static inline unsigned int ps3_sas_ext_buf_to_rsp(struct ps3_instance *instance, + struct bsg_buffer *rsp_buf, + void *ext_buf) +{ + unsigned short rsq_data_len = 0; + unsigned short smp_len = 0; + + rsq_data_len = + min(rsp_buf->payload_len, instance->cmd_context.ext_buf_size); + + smp_len = ((unsigned char *)ext_buf)[3] * 4 + 4; + LOG_DEBUG("hno:%u smp frame len[%d], rsq_data_len[%d]\n", + PS3_HOST(instance), smp_len, rsq_data_len); + + rsq_data_len = min(smp_len, rsq_data_len); + + show_smp((unsigned char *)ext_buf, rsq_data_len); + + sg_copy_from_buffer(rsp_buf->sg_list, rsp_buf->sg_cnt, ext_buf, + rsp_buf->payload_len); + return rsq_data_len; +} + +void ps3_sas_smp_handler(struct bsg_job *job, struct Scsi_Host *shost, + struct sas_rphy *rphy) +{ + int ret = PS3_SUCCESS; + int send_result = PS3_SUCCESS; + struct ps3_instance *instance = (struct ps3_instance *)shost->hostdata; + struct ps3_cmd *cmd = NULL; + unsigned int req_data_len = 0; + unsigned int resp_len = 0; + unsigned long long sas_addr = 0; + struct ps3_sas_node *ps3_sas_node = NULL; + + ret = ps3_sas_smp_pre_check(instance); + if (ret != PS3_SUCCESS) { + ret = -EFAULT; + goto l_out; + } + + sas_addr = (rphy) ? (rphy->identify.sas_address) : + (instance->sas_dev_context.ps3_hba_sas.sas_address); + ps3_sas_node = ps3_sas_find_node_by_sas_addr(instance, sas_addr); + if (ps3_sas_node == NULL) { + LOG_ERROR("hno:%u cannot find node[%llx] !\n", + PS3_HOST(instance), sas_addr); + up(&instance->sas_dev_context.ps3_sas_smp_semaphore); + ret = -EFAULT; + goto l_out; + } + ps3_atomic_inc(&instance->cmd_statistics.cmd_delivering); + if (ps3_sas_request_pre_check(instance) != PS3_SUCCESS) { + LOG_WARN_LIM("sas_addr[%016llx], hno:%u smp pre check NOK\n", + sas_addr, PS3_HOST(instance)); + ps3_atomic_dec(&instance->cmd_statistics.cmd_delivering); + ret = -EFAULT; + goto l_no_free_cmd; + } + cmd = ps3_mgr_cmd_alloc(instance); + if (cmd == NULL) { + LOG_WARN("hno:%u Failed to get a cmd packet\n", + PS3_HOST(instance)); + ps3_atomic_dec(&instance->cmd_statistics.cmd_delivering); + ret = -ENOMEM; + goto l_no_free_cmd; + } + cmd->time_out = PS3_SAS_TIMEOUT_SEC; + cmd->is_interrupt = PS3_DRV_FALSE; + + req_data_len = ps3_sas_req_to_ext_buf(instance, &job->request_payload, + cmd->ext_buf); + if (req_data_len == 0) { + ret = -ENOMEM; + ps3_atomic_dec(&instance->cmd_statistics.cmd_delivering); + goto l_out_failed; + } + + LOG_DEBUG( + "hno:%u trace_id[0x%llx] CFID [%u], sas_addr[%016llx], len[%u] send smp req\n", + PS3_HOST(instance), cmd->trace_id, cmd->index, sas_addr, + req_data_len); + + ps3_sas_smp_reqframe_build(cmd, sas_addr, req_data_len); + ps3_mgr_cmd_word_build(cmd); + send_result = ps3_cmd_send_sync(instance, cmd); + ps3_atomic_dec(&instance->cmd_statistics.cmd_delivering); + if (send_result == PS3_SUCCESS) + send_result = ps3_cmd_wait_sync(instance, cmd); + ret = ps3_mgr_complete_proc(instance, cmd, send_result); + if (ret == -PS3_CMD_NO_RESP) { + LOG_ERROR("hno:%u %d respStatus NOK CFID[%d] respStatus[%d]\n", + PS3_HOST(cmd->instance), ret, + cmd->cmd_word.cmdFrameID, ps3_cmd_resp_status(cmd)); + ret = -ETIMEDOUT; + goto l_no_free_cmd; + } + + if (ret != PS3_SUCCESS) { + LOG_ERROR("hno:%u %d respStatus NOK CFID[%d] respStatus[%d]\n", + PS3_HOST(cmd->instance), ret, + cmd->cmd_word.cmdFrameID, ps3_cmd_resp_status(cmd)); + ret = -ENXIO; + goto l_out_failed; + } + + resp_len = ps3_sas_ext_buf_to_rsp(instance, &job->reply_payload, + cmd->ext_buf); + + LOG_DEBUG("hno:%u trace_id[0x%llx] CFID [%u], end, ret[%d]\n", + PS3_HOST(instance), cmd->trace_id, cmd->index, ret); +l_out_failed: + ps3_mgr_cmd_free(instance, cmd); +l_no_free_cmd: + up(&instance->sas_dev_context.ps3_sas_smp_semaphore); +l_out: + bsg_job_done(job, ret, resp_len); +} +#endif + +int ps3_sas_attach_transport(void) +{ + int ret = PS3_SUCCESS; + + static struct sas_function_template ps3_sas_transport_functions = { + .get_linkerrors = ps3_sas_linkerrors_get, + .get_enclosure_identifier = ps3_sas_enclosure_identifier_get, + .get_bay_identifier = ps3_sas_bay_identifier_get, + .phy_reset = ps3_sas_phy_reset, + .phy_enable = ps3_sas_phy_enable, + .set_phy_speed = ps3_sas_linkrates_set, + .smp_handler = ps3_sas_smp_handler, + }; + + ps3_sas_transport_template = + sas_attach_transport(&ps3_sas_transport_functions); + if (!ps3_sas_transport_template) + ret = -PS3_FAILED; + return ret; +} + +void ps3_sas_release_transport(void) +{ + if (ps3_sas_transport_template != NULL) { + sas_release_transport(ps3_sas_transport_template); + ps3_sas_transport_template = NULL; + } +} +#endif diff --git a/drivers/scsi/linkdata/ps3stor/ps3_sas_transport.h b/drivers/scsi/linkdata/ps3stor/ps3_sas_transport.h new file mode 100644 index 0000000000000000000000000000000000000000..b49cf40cbaccdba7b53a3010bcc92f96c0774147 --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/ps3_sas_transport.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _WINDOWS + +#ifndef _PS3_SAS_TRANSPORT_H_ +#define _PS3_SAS_TRANSPORT_H_ + +#include "ps3_instance_manager.h" +#include +#include + +#include "ps3_htp.h" +#include "ps3_kernel_version.h" + +#define PS3_SAS_TIMEOUT_SEC (40) +#define PS3_SMP_CRC_LEN (4) + +struct scsi_transport_template *ps3_sas_transport_get(void); + +int ps3_sas_attach_transport(void); + +void ps3_sas_release_transport(void); + +int ps3_sas_linkerrors_get(struct sas_phy *phy); + +int ps3_sas_enclosure_identifier_get(struct sas_rphy *rphy, u64 *identifier); + +int ps3_sas_bay_identifier_get(struct sas_rphy *rphy); + +int ps3_sas_phy_reset(struct sas_phy *phy, int hard_reset); + +int ps3_sas_phy_enable(struct sas_phy *phy, int enable); + +int ps3_sas_linkrates_set(struct sas_phy *phy, struct sas_phy_linkrates *rates); + +#if defined(PS3_SAS_SMP_RETURN) +int ps3_sas_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy, + struct request *req); +#else +void ps3_sas_smp_handler(struct bsg_job *job, struct Scsi_Host *shost, + struct sas_rphy *rphy); +#endif + +#endif +#endif diff --git a/drivers/scsi/linkdata/ps3stor/ps3_scsi_cmd_err.c b/drivers/scsi/linkdata/ps3stor/ps3_scsi_cmd_err.c new file mode 100644 index 0000000000000000000000000000000000000000..474f4e4b2081ca814490c81d66af2767623942c5 --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/ps3_scsi_cmd_err.c @@ -0,0 +1,1461 @@ +// SPDX-License-Identifier: GPL-2.0 +#ifndef _WINDOWS +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#endif + +#include "ps3_htp_def.h" +#include "ps3_util.h" +#include "ps3_instance_manager.h" +#include "ps3_ioc_state.h" +#include "ps3_mgr_cmd_err.h" +#include "ps3_mgr_cmd.h" +#include "ps3_scsih.h" +#include "ps3_cmd_complete.h" +#include "ps3_driver_log.h" +#include "ps3_cmd_statistics.h" +#include "ps3_scsih_cmd_parse.h" +#include "ps3_module_para.h" +#include "ps3_r1x_write_lock.h" +#include "ps3_htp_nvme_spec.h" +#include "ps3_nvme_resp_to_scsi.h" +#include "ps3_scsi_cmd_err.h" +#include "ps3_ioc_manager.h" +#include "ps3_mgr_channel.h" + +static void ps3_scsi_sense_print(struct ps3_instance *instance, + const unsigned char *sense_buffer, + unsigned char len, int CFID); + +static void ps3_internal_errcode_to_scsi(struct scsi_cmnd *s_cmd, int err_code, + unsigned int xfer_cnt, + unsigned char is_sata_jbod_mgr_cmd) +{ + int host_status = DID_OK; + int status = SCSI_STATUS_GOOD; + + switch (err_code) { + case PS3_STATUS_VD_OFFLINE: + case PS3_STATUS_DEVICE_NOT_FOUND: + host_status = DID_BAD_TARGET; + break; + case PS3_STATUS_INTERNAL_ERR: + host_status = DID_ERROR; + break; + + case PS3_STATUS_IO_ABORTED: + case PS3_STATUS_INTERNAL_SOFT_ERR: +#ifndef _WINDOWS + host_status = DID_SOFT_ERROR; +#else + host_status = DID_ERROR; +#endif + break; + case PS3_STATUS_ACCESS_BLOCK: + host_status = DID_OK; +#ifndef _WINDOWS +#if defined(PS3_SUPPORT_DRIVER_SENSE) + s_cmd->result |= PS3_SCSI_RESULT_DRIVER_STATUS(DRIVER_SENSE); +#endif + status = SAM_STAT_CHECK_CONDITION; + scsi_build_sense_buffer(0, s_cmd->sense_buffer, ILLEGAL_REQUEST, + 0x20, 0x02); + scsi_set_resid(s_cmd, scsi_bufflen(s_cmd)); + break; + case PS3_STATUS_REQ_ILLEGAL: + host_status = DID_OK; +#if defined(PS3_SUPPORT_DRIVER_SENSE) + s_cmd->result |= PS3_SCSI_RESULT_DRIVER_STATUS(DRIVER_SENSE); +#endif + status = SAM_STAT_CHECK_CONDITION; + scsi_build_sense_buffer(0, s_cmd->sense_buffer, ILLEGAL_REQUEST, + 0x20, 0x00); + scsi_set_resid(s_cmd, scsi_bufflen(s_cmd)); +#else + scsi_build_sense_buffer(s_cmd, 0, ILLEGAL_REQUEST, 0x20, 0x02); +#endif + break; + case PS3_STATUS_HOST_NOT_FOUND: + case PS3_STATUS_PCI_RECOVERY: + host_status = DID_NO_CONNECT; + break; + case PS3_STATUS_VD_MEMBER_OFFLINE: + host_status = DID_IMM_RETRY; + break; + case PS3_STATUS_HOST_RESET: +#if defined(PS3_DID_REQUEUE) + host_status = DID_REQUEUE; +#else + host_status = DID_RESET; +#endif + break; + case PS3_STATUS_UNDERRUN: + if ((scsi_bufflen(s_cmd) < xfer_cnt) && + (!is_sata_jbod_mgr_cmd)) { + host_status = DID_SOFT_ERROR; + scsi_set_resid(s_cmd, 0); + } else if (scsi_bufflen(s_cmd) == xfer_cnt) { + host_status = DID_OK; + } else { + if (scsi_bufflen(s_cmd) > xfer_cnt) { + scsi_set_resid(s_cmd, + scsi_bufflen(s_cmd) - xfer_cnt); + } else { + scsi_set_resid(s_cmd, 0); + } + if (xfer_cnt < s_cmd->underflow) { + host_status = DID_SOFT_ERROR; + } else if (!xfer_cnt && s_cmd->cmnd[0] == REPORT_LUNS) { + host_status = DID_OK; +#if defined(PS3_SUPPORT_DRIVER_SENSE) + s_cmd->result |= PS3_SCSI_RESULT_DRIVER_STATUS( + DRIVER_SENSE); +#endif + status = SAM_STAT_CHECK_CONDITION; + scsi_build_sense_buffer(0, s_cmd->sense_buffer, + ILLEGAL_REQUEST, 0x20, + 0x0); + } else { + host_status = DID_OK; + } + } + break; + case PS3_STATUS_OVERRUN: + scsi_set_resid(s_cmd, 0); + host_status = DID_SOFT_ERROR; + break; + + default: + host_status = DID_ERROR; + break; + } +#ifndef _WINDOWS + s_cmd->result |= PS3_SCSI_RESULT_HOST_STATUS(host_status) | status; +#else + scsi_cmnd_scsistatus_set(s_cmd, status); + scsi_cmnd_hoststatus_set(s_cmd, host_status); +#endif +} + +static void ps3_standard_errcode_to_scsi(struct ps3_instance *instance, + struct scsi_cmnd *s_cmd, int err_code, + union PS3RespFrame *resp_frame) +{ + int host_status = DID_OK; + int status = SCSI_STATUS_GOOD; + + switch (err_code) { + case SCSI_STATUS_GOOD: + case SCSI_STATUS_CONDITION_MET: + case SCSI_STATUS_BUSY: + case SCSI_STATUS_TASK_SET_FULL: + case SCSI_STATUS_ACA_ACTIVE: + case SCSI_STATUS_TASK_ABORTED: + status = err_code; + break; + case SCSI_STATUS_RESERVATION_CONFLICT: +#if defined(PS3_DID_NEXUS_FAILURE) + host_status = DID_NEXUS_FAILURE; +#endif + status = err_code; + break; + case SCSI_STATUS_CHECK_CONDITION: + host_status = DID_OK; + status = err_code; + if (resp_frame != NULL) { + memcpy(s_cmd->sense_buffer, + resp_frame->normalRespFrame.sense, + SCSI_SENSE_BUFFERSIZE); + ps3_scsi_sense_print(instance, s_cmd->sense_buffer, + SCSI_SENSE_BUFFERSIZE, + SCMD_GET_REQUEST(s_cmd)->tag); + } +#if defined(PS3_SUPPORT_DRIVER_SENSE) + s_cmd->result |= PS3_SCSI_RESULT_DRIVER_STATUS(DRIVER_SENSE); +#endif + break; + default: + host_status = DID_ERROR; + break; + } + + s_cmd->result |= PS3_SCSI_RESULT_HOST_STATUS(host_status) | status; +} + +void ps3_errcode_to_scsi_status(struct ps3_instance *instance, + struct scsi_cmnd *s_cmd, unsigned int err_code, + union PS3RespFrame *resp_frame, + unsigned int xfer_cnt, struct ps3_cmd *cmd) +{ + unsigned char is_sata_jbod_mgr_cmd = 0; + + if (cmd != NULL) + is_sata_jbod_mgr_cmd = ps3_scsih_is_sata_jbod_mgr_cmd(cmd); + + s_cmd->result = SCSI_STATUS_GOOD; + + if (err_code < PS3_STATUS_DEVICE_NOT_FOUND) { + ps3_standard_errcode_to_scsi(instance, s_cmd, err_code, + resp_frame); + } else { + ps3_internal_errcode_to_scsi(s_cmd, err_code, xfer_cnt, + is_sata_jbod_mgr_cmd); + } +} + +static inline void ps3_err_scsi_errcode_mapping(struct ps3_cmd *cmd, + unsigned short mode, + unsigned char is_sas_direct) +{ + unsigned char opcode = 0; + unsigned short sub_opcode = 0; + unsigned int resp_status = 0; + union PS3RespFrame *resp_frame = cmd->resp_frame; + unsigned int xfer_cnt = 0; + struct Ps3SasDirectRespFrameIU *frame = NULL; + + ps3_scsih_cdb_opcode_get(cmd->scmd->cmnd, &opcode, &sub_opcode); + LOG_FILE_INFO("tid:0x%llx hno:%u op:0x%x sub_op:0x%x tag:%u\n", + cmd->trace_id, PS3_HOST(cmd->instance), opcode, + sub_opcode, cmd->index); + + if (ps3_err_is_resp_from_direct_cmd(mode) && + (cmd->io_attr.dev_type != PS3_DEV_TYPE_VD)) { + resp_status = + resp_frame->sasRespFrame.status & PS3_SCSI_STATUS_MASK; + } else { + resp_status = resp_frame->normalRespFrame.respStatus & + PS3_SCSI_STATUS_MASK; + } + xfer_cnt = ps3_scsih_xfer_cnt_get(cmd); + ps3_errcode_to_scsi_status(cmd->instance, cmd->scmd, resp_status, + resp_frame, xfer_cnt, cmd); + + if (is_sas_direct) { + frame = &resp_frame->sasRespFrame; + LOG_INFO_IN_IRQ( + cmd->instance, + "tid:0x%llx hno:%u [%u:%u:%u], op:0x%x sub_op:0x%x\n" + "\ttag:%u data_pres:%u resp_status:0x%x scmd status:0x%x\n" + "\tscmd cmd_flags:0x%llx, retries:%d, allowd:%d,\n" + "\txfer_cnt:%u scsi_buflen:%u\n", + cmd->trace_id, PS3_HOST(cmd->instance), + cmd->scmd->device->channel, cmd->scmd->device->id, + cmd->io_attr.disk_id, opcode, sub_opcode, cmd->index, + frame->dataPres, frame->status, cmd->scmd->result, + (unsigned long long)SCMD_GET_REQUEST(cmd->scmd) + ->cmd_flags, + cmd->scmd->retries, cmd->scmd->allowed, xfer_cnt, + scsi_bufflen(cmd->scmd)); + } else { + LOG_INFO_IN_IRQ( + cmd->instance, + "tid:0x%llx hno:%u [%u:%u:%u], op:0x%x sub_op:0x%x\n" + "\ttag:%u resp_status:0x%x scmd status:0x%x\n" + "\tscmd cmd_flags:0x%llx, retries:%d, allowd:%d,\n" + "\txfer_cnt:%u scsi_buflen:%u\n", + cmd->trace_id, PS3_HOST(cmd->instance), + cmd->scmd->device->channel, cmd->scmd->device->id, + cmd->io_attr.disk_id, opcode, sub_opcode, cmd->index, + resp_frame->normalRespFrame.respStatus, + cmd->scmd->result, + (unsigned long long)SCMD_GET_REQUEST(cmd->scmd) + ->cmd_flags, + cmd->scmd->retries, cmd->scmd->allowed, xfer_cnt, + scsi_bufflen(cmd->scmd)); + } +} + +static unsigned char +ps3_err_retry_sys_state_check(struct ps3_instance *instance) +{ + unsigned char ret = PS3_DRV_TRUE; + int cur_state = PS3_INSTANCE_STATE_INIT; + + if (!instance->state_machine.is_load) { + LOG_WARN_IN_IRQ(instance, "hno:%u instance state not is_load\n", + PS3_HOST(instance)); + ret = PS3_DRV_FALSE; + goto l_out; + } + + cur_state = ps3_atomic_read(&instance->state_machine.state); + if (cur_state == PS3_INSTANCE_STATE_DEAD || + cur_state == PS3_INSTANCE_STATE_QUIT) { + LOG_WARN_IN_IRQ(instance, + "hno:%u instance state is dead/quit\n", + PS3_HOST(instance)); + ret = PS3_DRV_FALSE; + goto l_out; + } + +l_out: + return ret; +} + +static int ps3_is_state_abnormal(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + + static unsigned long j; + + if (!ps3_is_instance_state_normal(instance, PS3_TRUE)) { + ret = -PS3_FAILED; + goto l_out; + } + + if (ps3_pci_err_recovery_get(instance)) { + LOG_WARN_TIME_LIM(&j, PS3_LOG_LIMIT_INTERVAL_MSEC, + "hno:%u host in pci recovery\n", + PS3_HOST(instance)); + ret = -PS3_FAILED; + goto l_out; + } +l_out: + return ret; +} + +static unsigned char ps3_err_code_is_need_change(const struct ps3_cmd *cmd) +{ + unsigned int err_code = 0; + union PS3RespFrame *resp_frame = cmd->resp_frame; + unsigned short mode = cmd->reply_word.mode; + unsigned char ret = PS3_TRUE; + struct PS3NvmeCmdStatus status; + struct ps3_nvme_scsi_status cpl; + + if (ps3_err_is_resp_from_direct_cmd(mode)) { + if (cmd->reply_word.diskType == PS3_DEV_TYPE_SAS_SATA) { + err_code = resp_frame->sasRespFrame.status & + PS3_SCSI_STATUS_MASK; + } else { + status.cmdStatus = cmd->reply_word.retStatus; + memset(&cpl, 0, sizeof(struct ps3_nvme_scsi_status)); + ps3_nvme_error_to_scsi_status(status, &cpl); + err_code = cpl.status; + } + } + + switch (err_code) { + case SCSI_STATUS_BUSY: + case SCSI_STATUS_TASK_SET_FULL: + case SCSI_STATUS_CONDITION_MET: + case SCSI_STATUS_ACA_ACTIVE: + case PS3_STATUS_OVERRUN: + case PS3_STATUS_UNDERRUN: + ret = PS3_FALSE; + break; + default: + ret = PS3_TRUE; + } + + LOG_DEBUG("tid:0x%llx hno:%u tag:%u status:0x%x %s change\n", + cmd->trace_id, PS3_HOST(cmd->instance), cmd->index, err_code, + ret == PS3_TRUE ? "need" : "no need"); + + return ret; +} + +static inline unsigned char +ps3_is_need_direct_to_normal(const struct ps3_instance *instance, + const struct ps3_cmd *cmd) +{ + return ((instance->ioc_adpter->is_need_direct_to_normal != NULL) && + (instance->ioc_adpter->is_need_direct_to_normal(cmd)) && + (cmd->scmd->allowed != 0)); +} + +void ps3_scsih_drv_io_reply_scsi(struct scsi_cmnd *s_cmd, struct ps3_cmd *cmd, + unsigned char resp_status, + unsigned char cmd_lock); + +int ps3_err_scsi_io_processing(struct ps3_instance *instance, unsigned int id, + unsigned int channel) +{ + int ret = PS3_SUCCESS; + struct ps3_cmd_context *cmd_context = &instance->cmd_context; + struct ps3_cmd *cmd = NULL; + unsigned short idx = 0; + struct scsi_cmnd *s_cmd = NULL; + static unsigned long j; + + ps3_msleep(ps3_task_reset_delay_time_query()); + + ps3_atomic_inc(&instance->is_err_scsi_processing); + rmb(); /* in order to force CPU ordering */ + + ret = ps3_is_state_abnormal(instance); + if (ret != PS3_SUCCESS) { + LOG_INFO( + "hno:%u %s [%d:%d] fail, abnormal\n", + PS3_HOST(instance), __func__, channel, id); + ret = -PS3_FAILED; + ps3_atomic_dec(&instance->is_err_scsi_processing); + goto l_out; + } + + instance->ioc_adpter->irq_disable(instance); +#ifndef _WINDOWS + ps3_irqs_sync(instance); +#endif + + ps3_all_reply_fifo_complete(instance); + + for (idx = 0; idx < cmd_context->max_scsi_cmd_count; idx++) { + cmd = ps3_cmd_find(instance, idx); + if (cmd == NULL) { + ret = -PS3_FAILED; + break; + } + if (cmd->cmd_state.state == PS3_CMD_STATE_INIT) + continue; + s_cmd = cmd->scmd; + if (s_cmd == NULL) + continue; + if (s_cmd->device == NULL) + continue; + if (PS3_SDEV_TARGET(s_cmd->device) == id && + PS3_SDEV_CHANNEL(s_cmd->device) == channel) { +#ifndef _WINDOWS + scsi_print_command(s_cmd); +#endif + LOG_WARN_TIME_LIM( + &j, PS3_LOG_LIMIT_INTERVAL_MSEC, + "hno:%u SCSI commands pending to target\n" + "\ttid:0x%llx [%u:%u]: frame_id:%d\n", + PS3_HOST(instance), cmd->trace_id, channel, id, + cmd->index); + ret = -PS3_FAILED; + break; + } + } + + instance->ioc_adpter->irq_enable(instance); +#ifndef _WINDOWS + ps3_irqpolls_enable(instance); +#endif + + ps3_atomic_dec(&instance->is_err_scsi_processing); +l_out: + return ret; +} + +static void ps3_err_sas_errcode_mapping_with_sense(struct ps3_cmd *cmd, + union PS3RespFrame *frame) +{ + unsigned int sense_data_len = 0; + struct scsi_cmnd *scmd = cmd->scmd; + struct ps3_instance *instance = cmd->instance; +#ifndef _WINDOWS + int host_status = DID_OK; + struct Ps3SasDirectRespFrameIU frame_sas = frame->sasRespFrame; + + if (scmd == NULL || scmd->sense_buffer == NULL) { + LOG_WARN_IN_IRQ(instance, "scmd is null\n"); + goto l_out; + } + scmd->result = 0; + + if (frame == NULL) { + LOG_ERROR_IN_IRQ(instance, "direct frame is null, retry\n"); + scmd->result = PS3_SCSI_RESULT_HOST_STATUS(DID_SOFT_ERROR); + goto l_out; + } + +#if defined(PS3_SUPPORT_DRIVER_SENSE) + scmd->result |= PS3_SCSI_RESULT_DRIVER_STATUS(DRIVER_SENSE); +#endif + scmd->result |= + PS3_SCSI_RESULT_HOST_STATUS(host_status) | frame_sas.status; + + memset(scmd->sense_buffer, 0, SCSI_SENSE_BUFFERSIZE); + sense_data_len = min_t(unsigned int, SCSI_SENSE_BUFFERSIZE, + sizeof(frame_sas.data)); + if (sense_data_len == 0) { + LOG_ERROR_IN_IRQ(instance, "sense_data_len len is zero\n"); + goto l_out; + } + memcpy(scmd->sense_buffer, frame_sas.data, sense_data_len); + ps3_scsi_sense_print(instance, scmd->sense_buffer, + SCSI_SENSE_BUFFERSIZE, + SCMD_GET_REQUEST(scmd)->tag); +#else + if (frame == NULL) { + LOG_ERROR_IN_IRQ(instance, "direct frame is null, retry\n"); + scsi_cmnd_scsistatus_set(scmd, SCSI_STATUS_BUSY); + goto l_out; + } + sense_data_len = min(SCSI_SENSE_BUFFERSIZE, sizeof(frame_sas.data)); + if (sense_data_len == 0) { + LOG_ERROR_IN_IRQ(instance, "sense_data_len len is zero\n"); + goto l_out; + } + + ps3_sense_info_set(scmd, frame_sas.data, + (unsigned short)sense_data_len); +#endif +l_out: + return; +} + +static void ps3_err_sas_errcode_mapping(struct ps3_instance *instance, + struct ps3_cmd *cmd, + unsigned short mode) +{ + struct scsi_cmnd *scmd = cmd->scmd; + unsigned int channel = 0; + unsigned int id = 0; + unsigned short disk_id = 0; + union PS3RespFrame *frame = cmd->resp_frame; + static unsigned long j; +#ifndef _WINDOWS + channel = scmd->device->channel; + id = scmd->device->id; + disk_id = cmd->io_attr.disk_id; +#endif + if (ps3_err_is_resp_from_direct_cmd(mode)) { + struct Ps3SasDirectRespFrameIU frame_sas = frame->sasRespFrame; + + switch (frame_sas.dataPres) { + case PS3_SAS_PRES_NO_DATA: + case PS3_SAS_PRES_REPSNSE_DATA: + ps3_err_scsi_errcode_mapping(cmd, mode, PS3_TRUE); + break; + case PS3_SAS_PRES_SENSE_DATA: + LOG_INFO_IN_IRQ( + instance, + "tid:0x%llx hno:%u [%u:%u:%u], CFID:%u data_pres:%u status:%u\n", + cmd->trace_id, PS3_HOST(instance), channel, id, + disk_id, cmd->index, + frame->sasRespFrame.dataPres, + frame->sasRespFrame.status); + ps3_err_sas_errcode_mapping_with_sense(cmd, frame); + break; + default: +#ifndef _WINDOWS + scmd->result |= PS3_SCSI_RESULT_HOST_STATUS(DID_ERROR); + LOG_INFO_IN_IRQ( + instance, + "tid:0x%llx hno:%d tag:%u scmd result:%x\n", + cmd->trace_id, PS3_HOST(cmd->instance), + cmd->index, cmd->scmd->result); +#else + scsi_cmnd_hoststatus_set(scmd, DID_ERROR); + LOG_INFO_IN_IRQ(instance, "tid:0x%llx hno:%d tag:%u\n", + cmd->trace_id, PS3_HOST(cmd->instance), + cmd->index); +#endif + break; + } + + } else { + LOG_WARN_TIME_LIM( + &j, PS3_LOG_LIMIT_INTERVAL_MSEC, + "tid:0x%llx hno:%u [chl:%u, id:%u, devid:%u], CFID:%u respStatus:%u\n", + cmd->trace_id, PS3_HOST(instance), channel, id, disk_id, + cmd->index, frame->normalRespFrame.respStatus); + ps3_err_scsi_errcode_mapping(cmd, mode, PS3_FALSE); + } +} + +static void ps3_err_nvme_errcode_mapping(struct ps3_instance *instance, + struct ps3_cmd *cmd, + unsigned short mode) +{ + ps3_nvme_resp_to_scsi_status(cmd); + ps3_err_scsi_errcode_mapping(cmd, mode, PS3_FALSE); + (void)instance; + + LOG_INFO_IN_IRQ(instance, "tid:0x%llx hno:%d tag:%d scmd result:%x\n", + cmd->trace_id, PS3_HOST(instance), cmd->index, + cmd->scmd->result); +} + +static void ps3_err_protocol_errcode_mapping(struct ps3_instance *instance, + struct ps3_cmd *cmd, + struct PS3ReplyWord *reply_word) +{ + if (reply_word->diskType == PS3_DEV_TYPE_SAS_SATA) + ps3_err_sas_errcode_mapping(instance, cmd, reply_word->mode); + else + ps3_err_nvme_errcode_mapping(instance, cmd, reply_word->mode); + +} + +unsigned char ps3_err_is_resp_from_direct_cmd(unsigned short mode) +{ + return ((mode == PS3_REPLY_WORD_MODE_DIRECT_OK) || + (mode == PS3_REPLY_WORD_MODE_DIRECT_ADVICE_TO_DIRECT)); +} + +int ps3_err_scsi_cmd_fault_proc(struct ps3_instance *instance, + struct ps3_cmd *cmd) +{ + unsigned short mode = cmd->reply_word.mode; + int ret = PS3_SUCCESS; + + LOG_FILE_INFO( + "tid:0x%llx hno:%u scsi cmd fault proc CFID:%u\n" + "\treply_word info: mode:%d retStatus[%02x], rettype:%d disktype:%d\n", + cmd->trace_id, PS3_HOST(cmd->instance), cmd->index, mode, + cmd->reply_word.retStatus, cmd->reply_word.retType, + cmd->reply_word.diskType); + + if (cmd->reply_word.retType == 0) { + if ((cmd->io_attr.dev_type != PS3_DEV_TYPE_VD) && + ps3_err_is_resp_from_direct_cmd(mode)) { + if (ps3_err_retry_sys_state_check(instance)) { + LOG_FILE_INFO( + "hno:%u Direct cmd needs retry!\n" + "\t[scmd cmd_flags:0x%llx, retries:%d, allowd:%d]\n", + PS3_HOST(instance), + (unsigned long long)SCMD_GET_REQUEST( + cmd->scmd) + ->cmd_flags, + cmd->scmd->retries, cmd->scmd->allowed); + + if ((ps3_is_need_direct_to_normal(instance, + cmd)) && + (ps3_err_code_is_need_change(cmd))) { + cmd->scmd->result = + PS3_SCSI_RESULT_HOST_STATUS( + DID_SOFT_ERROR); + } else { + ps3_err_protocol_errcode_mapping( + instance, cmd, + &(cmd->reply_word)); + } + } else { + cmd->scmd->result = PS3_SCSI_RESULT_HOST_STATUS( + DID_NO_CONNECT); + } + } else { + ps3_err_scsi_errcode_mapping(cmd, mode, PS3_FALSE); + } + } else { + if (ps3_err_retry_sys_state_check(instance)) { + LOG_FILE_INFO( + "hno:%u Hil hardware err,need retry!\n" + "\t[scmd cmd_flags:0x%llx, retries:%d, allowd:%d]\n", + PS3_HOST(instance), + SCMD_GET_REQUEST(cmd->scmd)->cmd_flags, + cmd->scmd->retries, cmd->scmd->allowed); + if (cmd->reply_word.retStatus == + PS3_NVME_RESP_STATUS_SQ_FULL && + cmd->io_attr.dev_type == PS3_DEV_TYPE_VD) { + cmd->scmd->result = + PS3_SCSI_RESULT_HOST_STATUS(DID_OK) | + SCSI_STATUS_TASK_SET_FULL; + cmd->scmd->retries = + ((cmd->scmd->retries == 0) ? + 1 : + cmd->scmd->retries); + } else { + cmd->scmd->result = PS3_SCSI_RESULT_HOST_STATUS( + DID_SOFT_ERROR); + } + } else { + cmd->scmd->result = + PS3_SCSI_RESULT_HOST_STATUS(DID_NO_CONNECT); + } + } + if (cmd->scmd->result == SCSI_STATUS_TASK_SET_FULL) { + ps3_qos_adjust_pd_rsc(cmd->scmd->device, instance, + PS3_QOS_QUOTA_ADJUST_QFULL); + } + return ret; +} + +void ps3_scsih_drv_io_reply_scsi(struct scsi_cmnd *s_cmd, struct ps3_cmd *cmd, + unsigned char resp_status, + unsigned char cmd_lock) +{ + struct ps3_instance *instance = cmd->instance; + struct ps3_scsi_priv_data *data = NULL; + + if (cmd_lock) { + ps3_mutex_lock(&instance->recovery_context->free_cmd_lock); + LOG_INFO( + "hno:%u tid:0x%llx CFID:%u drv reply scsi independently!\n", + PS3_HOST(instance), cmd->trace_id, cmd->index); + } + if (likely(cmd->scmd == NULL)) { + LOG_DEBUG("scmd is has reply scsi\n"); + goto l_out; + } + + PS3_IO_OUTSTAND_DEC(instance, s_cmd); + PS3_VD_OUTSTAND_DEC(instance, s_cmd); + + PS3_IO_BACK_ERR_INC(instance, s_cmd); + PS3_DEV_BUSY_DEC(s_cmd); + + ps3_errcode_to_scsi_status(cmd->instance, s_cmd, resp_status, NULL, 0, + cmd); + +#if defined(PS3_SUPPORT_CMD_SCP) + s_cmd->SCp.ptr = NULL; +#endif + ps3_scsi_dma_unmap(cmd); + data = (struct ps3_scsi_priv_data *)s_cmd->device->hostdata; + if (likely(data != NULL)) + ps3_r1x_write_unlock(&data->lock_mgr, cmd); + + if (cmd->r1x_peer_cmd != NULL) { + LOG_DEBUG( + "host_no:%u task repaly r1x scsi write CFID:%d and CFID:%d\n", + PS3_HOST(instance), cmd->index, + cmd->r1x_peer_cmd->index); + if (!ps3_r1x_peer_cmd_free_nolock(cmd->r1x_peer_cmd)) + ps3_scsi_cmd_free(cmd->r1x_peer_cmd); + + if (!ps3_r1x_peer_cmd_free_nolock(cmd)) + ps3_scsi_cmd_free(cmd); + } else { + ps3_scsi_cmd_free(cmd); + } + SCMD_IO_DONE(s_cmd); + +l_out: + if (cmd_lock) + ps3_mutex_unlock(&instance->recovery_context->free_cmd_lock); +} + +int ps3_err_scsi_task_mgr_abort(struct scsi_cmnd *scmd) +{ +#ifdef _WINDOWS + (void)scmd; +#else + int ret = SUCCESS; + + struct ps3_cmd *aborted_cmd = NULL; + struct ps3_cmd *aborted_peer_cmd = NULL; + unsigned short cmd_frame_id = 0; + struct ps3_instance *instance = scsi_host_data(scmd); + struct ps3_scsi_priv_data *scsi_priv_data = + scsi_device_private_data(scmd); + unsigned int cmd_flighting_wait_cnt = 0; + + LOG_WARN("hno:%u enter, ready aborted CFID:%u op:0x%x chl:%u id:%u\n", + PS3_HOST(instance), SCMD_GET_REQUEST(scmd)->tag, scmd->cmnd[0], + ps3_scsi_channel_query(scmd), ps3_scsi_target_query(scmd)); + + if (ps3_mgr_cmd_send_pre_check(instance, PS3_FALSE) != PS3_SUCCESS) { + LOG_WARN("hno:%u pre check NOK!\n", PS3_HOST(instance)); + ret = FAILED; + goto l_out; + } + + if (!scsi_priv_data->is_taskmgmt_enable) { + LOG_ERROR("hno:%u task mgmt is disable!\n", PS3_HOST(instance)); + ret = FAILED; + goto l_out; + } + cmd_frame_id = SCMD_GET_REQUEST(scmd)->tag; + aborted_cmd = ps3_cmd_find(instance, cmd_frame_id); + if (aborted_cmd == NULL || aborted_cmd->scmd == NULL) { + LOG_INFO("host_no:%u there is no aborted cmd CFID:%u\n", + PS3_HOST(instance), cmd_frame_id); + + ret = SUCCESS; + goto l_out; + } + + LOG_FILE_WARN("tid:0x%llx hno:%u task aborted CFID:%u ready\n", + aborted_cmd->trace_id, PS3_HOST(instance), cmd_frame_id); + + scsi_priv_data->task_manager_busy = 1; + + while (aborted_cmd->flighting && + cmd_flighting_wait_cnt < PS3_ABORT_WAIT_CMD_FLIGHT_END) { + ps3_msleep(PS3_LOOP_TIME_INTERVAL_100MS); + cmd_flighting_wait_cnt++; + } + + if (aborted_cmd->flighting) { + ret = FAILED; + LOG_WARN( + "task abort wait cmd filghting end NOK. hno:%u tid:0x%llx CFID:%u\n", + PS3_HOST(instance), aborted_cmd->trace_id, + aborted_cmd->index); + goto l_busy_clean_out; + } + + LOG_DEBUG("hno:%u task abort wait flighting cmd end\n", + PS3_HOST(instance)); + + if (ps3_r1x_conflict_queue_abort(aborted_cmd, scmd)) { + ret = SUCCESS; + goto l_busy_clean_out; + } + + if (ps3_qos_waitq_abort(aborted_cmd)) { + ret = SUCCESS; + goto l_busy_clean_out; + } + + aborted_peer_cmd = aborted_cmd->r1x_peer_cmd; + if (aborted_peer_cmd != NULL) { + ps3_mutex_lock(&instance->task_abort_lock); + aborted_peer_cmd->is_aborting = 1; + wmb(); /* in order to force CPU ordering */ + if (aborted_cmd->r1x_peer_cmd == NULL) { + aborted_peer_cmd->is_aborting = 0; + ps3_mutex_unlock(&instance->task_abort_lock); + goto l_busy_clean_out; + } + ps3_mutex_unlock(&instance->task_abort_lock); + if (!aborted_peer_cmd->is_r1x_scsi_complete) { + ret = ps3_scsi_task_mgr_abort(instance, scsi_priv_data, + aborted_peer_cmd->index, + scmd); + ret = (ret == PS3_SUCCESS) ? SUCCESS : FAILED; + aborted_peer_cmd->is_aborting = 0; + if (ret == SUCCESS) { + LOG_INFO( + "hno:%u task abort peer cmd:%d success !\n", + PS3_HOST(instance), + aborted_peer_cmd->index); + aborted_peer_cmd = ps3_cmd_find( + instance, aborted_peer_cmd->index); + if (unlikely(aborted_peer_cmd == NULL)) { + ret = FAILED; + goto l_busy_clean_out; + } + } else { + goto l_busy_clean_out; + } + } else { + aborted_peer_cmd->is_aborting = 0; + } + } + + ret = ps3_scsi_task_mgr_abort(instance, scsi_priv_data, cmd_frame_id, + scmd); + ret = (ret == PS3_SUCCESS) ? SUCCESS : FAILED; + + if (ret == SUCCESS) { + LOG_INFO("hno:%u task abort cmd success !\n", + PS3_HOST(instance)); + aborted_cmd = ps3_cmd_find(instance, cmd_frame_id); + if (aborted_cmd == NULL) { + ret = FAILED; + } else { + if (unlikely(aborted_cmd->scmd)) { + LOG_WARN( + "task abort success but scmd is not freed. hno:%u tid:0x%llx CFID:%u\n", + PS3_HOST(instance), + aborted_cmd->trace_id, cmd_frame_id); + ret = FAILED; + } + ps3_can_queue_depth_update(instance); + } + } +l_busy_clean_out: + scsi_priv_data->task_manager_busy = 0; + wmb(); /* in order to force CPU ordering */ +l_out: + LOG_WARN("hno:%u quit ret:%#x\n", PS3_HOST(instance), ret); + return ret; +#endif + return -PS3_FAILED; +} + +void ps3_set_task_manager_busy(struct ps3_instance *instance, + unsigned int channel, unsigned int id, + unsigned int state) +{ + struct ps3_scsi_priv_data *scsi_priv_data = NULL; + + ps3_mutex_lock(&instance->dev_context.dev_priv_lock); + + scsi_priv_data = ps3_dev_mgr_lookup_vd_pri_data(instance, channel, id); + if (scsi_priv_data == NULL) { + ps3_mutex_unlock(&instance->dev_context.dev_priv_lock); + LOG_ERROR("hno:%u device has been deleted.channel:%u, id:%u!\n", + PS3_HOST(instance), channel, id); + goto l_out; + } + scsi_priv_data->task_manager_busy = state; + + ps3_mutex_unlock(&instance->dev_context.dev_priv_lock); + +l_out: + return; +} + +int ps3_err_scsi_task_mgr_reset(struct scsi_cmnd *scmd) +{ + int ret = PS3_SUCCESS; + int send_result = PS3_SUCCESS; + struct ps3_instance *instance = scsi_host_data(scmd); + struct ps3_scsi_priv_data *scsi_priv_data = NULL; + struct ps3_scsi_priv_data pending_priv_data; + struct ps3_cmd *cmd = NULL; + + LOG_INFO("hno:%u enter device reset channel:%u id:%u\n", + PS3_HOST(instance), PS3_SDEV_CHANNEL(scmd->device), + PS3_SDEV_TARGET(scmd->device)); + + ps3_mutex_lock(&instance->dev_context.dev_priv_lock); + scsi_priv_data = scsi_device_private_data(scmd); + if (scsi_priv_data == NULL) { + ps3_mutex_unlock(&instance->dev_context.dev_priv_lock); + LOG_ERROR("hno:%u device has been deleted!\n", + PS3_HOST(instance)); +#ifndef _WINDOWS + scmd->result = PS3_SCSI_RESULT_HOST_STATUS(DID_NO_CONNECT); + ret = SUCCESS; +#else + scsi_cmnd_hoststatus_set(scmd, DID_ERROR); +#endif + goto l_out; + } + + LOG_WARN("hno:%u enter ready reset[%u:%u:%u:%#x], CFID:%d\n", + PS3_HOST(instance), + scsi_priv_data->disk_pos.diskDev.ps3Dev.softChan, + scsi_priv_data->disk_pos.diskDev.ps3Dev.devID, + scsi_priv_data->disk_pos.diskDev.ps3Dev.phyDiskID, + scsi_priv_data->disk_pos.diskMagicNum, + SCMD_GET_REQUEST(scmd)->tag); + + if (ps3_mgr_cmd_send_pre_check(instance, PS3_FALSE) != PS3_SUCCESS) { + LOG_WARN("hno:%u pre check NOK!\n", PS3_HOST(instance)); +#ifndef _WINDOWS + ret = FAILED; +#else + ret = -PS3_FAILED; +#endif + ps3_mutex_unlock(&instance->dev_context.dev_priv_lock); + goto l_out; + } + + if (!scsi_priv_data->is_taskmgmt_enable) { + LOG_ERROR("hno:%u task mgmt is disable!\n", PS3_HOST(instance)); +#ifndef _WINDOWS + ret = FAILED; +#else + ret = -PS3_FAILED; +#endif + ps3_mutex_unlock(&instance->dev_context.dev_priv_lock); + goto l_out; + } + + LOG_INFO("hno:%u task ready reset[%u:%u:%u]\n", PS3_HOST(instance), + scsi_priv_data->disk_pos.diskDev.ps3Dev.softChan, + scsi_priv_data->disk_pos.diskDev.ps3Dev.devID, + scsi_priv_data->disk_pos.diskDev.ps3Dev.phyDiskID); + scsi_priv_data->task_manager_busy = 1; + + ps3_wait_scsi_cmd_done(instance, PS3_TRUE); + LOG_WARN("hno[%u], task reset wait scsi delivering cmd count[%d] !\n", + PS3_HOST(instance), + ps3_atomic_read(&instance->cmd_statistics.cmd_delivering)); + ps3_wait_mgr_cmd_done(instance, PS3_TRUE); + LOG_WARN("hno:%u task reset wait delivering cmd end !\n", + PS3_HOST(instance)); + + ps3_r1x_conflict_queue_clean(scsi_priv_data, SCSI_STATUS_TASK_ABORTED); + memcpy(&pending_priv_data, scsi_priv_data, + sizeof(struct ps3_scsi_priv_data)); + + ps3_qos_device_clean(instance, scsi_priv_data, + SCSI_STATUS_TASK_ABORTED); + ps3_mutex_unlock(&instance->dev_context.dev_priv_lock); + + ps3_atomic_inc(&instance->cmd_statistics.cmd_delivering); + if (ps3_mgr_cmd_send_pre_check(instance, PS3_FALSE) != PS3_SUCCESS) { + LOG_WARN_LIM("hno:%u mgr reset cmd pre check NOK CFID:%d\n", + PS3_HOST(instance), SCMD_GET_REQUEST(scmd)->tag); + ps3_atomic_dec(&instance->cmd_statistics.cmd_delivering); + ret = FAILED; + goto l_err; + } + cmd = ps3_scsi_task_mgr_reset_build(instance, &pending_priv_data); + if (cmd == NULL) { + LOG_ERROR("hno:%u task reset cmd NOK!\n", PS3_HOST(instance)); + ps3_atomic_dec(&instance->cmd_statistics.cmd_delivering); + ret = FAILED; + goto l_err; + } + + send_result = ps3_cmd_send_sync(instance, cmd); + ps3_atomic_dec(&instance->cmd_statistics.cmd_delivering); + if (send_result == PS3_SUCCESS) + send_result = ps3_cmd_wait_sync(instance, cmd); + ret = ps3_mgr_complete_proc(instance, cmd, send_result); + LOG_INFO("host_no:%u reset finish CFID:%u t_id:0x%llx!\n", + PS3_HOST(instance), ps3_cmd_frame_id(cmd), cmd->trace_id); + + if ((ret != -PS3_CMD_NO_RESP) && (cmd != NULL)) + ps3_task_cmd_free(instance, cmd); + + ret = (ret == PS3_SUCCESS) ? SUCCESS : FAILED; + if (ret == SUCCESS) { + LOG_INFO("hno:%u target reset success !\n", PS3_HOST(instance)); + ret = ps3_err_scsi_io_processing(instance, scmd->device->id, + scmd->device->channel); + ret = (ret == PS3_SUCCESS) ? SUCCESS : FAILED; + ps3_can_queue_depth_update(instance); + } +l_err: + ps3_mutex_lock(&instance->dev_context.dev_priv_lock); + scsi_priv_data = scsi_device_private_data(scmd); + if (scsi_priv_data == NULL) { + ps3_mutex_unlock(&instance->dev_context.dev_priv_lock); + LOG_ERROR("hno:%u device has been deleted!\n", + PS3_HOST(instance)); + goto l_out; + } + scsi_priv_data->task_manager_busy = 0; + ps3_mutex_unlock(&instance->dev_context.dev_priv_lock); +l_out: + LOG_WARN("hno:%u quit ret:%#x\n", PS3_HOST(instance), ret); + return ret; +} + +int ps3_device_reset_handler(struct scsi_cmnd *scmd) +{ + int ret = PS3_SUCCESS; + struct ps3_instance *instance = scsi_host_data(scmd); + + LOG_INFO("hno:%u ready device[%u:%u] reset!\n", PS3_HOST(instance), + PS3_SDEV_CHANNEL(scmd->device), PS3_SDEV_TARGET(scmd->device)); + + ps3_mutex_lock(&instance->task_mgr_reset_lock); + instance->task_manager_host_busy = PS3_TRUE; + ret = ps3_err_scsi_task_mgr_reset(scmd); + instance->task_manager_host_busy = PS3_FALSE; + ps3_mutex_unlock(&instance->task_mgr_reset_lock); + + return ret; +} + +static int ps3_hard_reset_pre_check(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + int cur_state = PS3_INSTANCE_STATE_INIT; + unsigned char is_support_halt = PS3_IOC_STATE_HALT_SUPPORT(instance); + unsigned int hard_reset_enable = ps3_hard_reset_enable_query(); + + cur_state = ps3_atomic_read(&instance->state_machine.state); + + LOG_WARN("hno:%u ready to host reset,instance state: %s\n", + PS3_HOST(instance), namePS3InstanceState(cur_state)); + if (is_support_halt && (cur_state != PS3_INSTANCE_STATE_DEAD)) { + ps3_atomic_set(&instance->state_machine.state, + PS3_INSTANCE_STATE_DEAD); + instance->ioc_adpter->ioc_force_to_halt(instance); + LOG_INFO("host_no:%u instance state while support halt!\n", + PS3_HOST(instance)); + ret = -PS3_FAILED; + } + + cur_state = ps3_atomic_read(&instance->state_machine.state); + if (cur_state == PS3_INSTANCE_STATE_DEAD && + (!hard_reset_enable || is_support_halt || + (!PS3_IOC_HARD_RECOVERY_SUPPORT(instance)))) { + LOG_INFO("hno:%u host reset, instance state: %s\n", + PS3_HOST(instance), namePS3InstanceState(cur_state)); + ret = -PS3_FAILED; + } + + LOG_INFO("hno:%u host reset, instance state: %s\n", PS3_HOST(instance), + namePS3InstanceState(cur_state)); + + return ret; +} +static int ps3_host_reset_pre_check(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + int cur_state = ps3_atomic_read(&instance->state_machine.state); + + LOG_WARN("hno:%u ready to host reset,instance state: %s\n", + PS3_HOST(instance), namePS3InstanceState(cur_state)); + if (PS3_DEVICE_IS_SWITCH(instance->dev_id) && + !instance->is_scan_host_finish) { + LOG_INFO( + "host_no:%u instance deviceId:%u is probe, exit failed!\n", + PS3_HOST(instance), instance->dev_id); + ret = -PS3_FAILED; + goto l_out; + } + + if (cur_state == PS3_INSTANCE_STATE_QUIT || + cur_state == PS3_INSTANCE_STATE_DEAD || + !instance->state_machine.can_hostreset) { + LOG_ERROR("hno:%u cannot perform host reset due to %s\n", + PS3_HOST(instance), namePS3InstanceState(cur_state)); + ret = -PS3_FAILED; + } else { + LOG_INFO("hno:%u host reset, instance state: %s\n", + PS3_HOST(instance), namePS3InstanceState(cur_state)); + } +l_out: + return ret; +} +int ps3_wait_for_outstanding_complete(struct ps3_instance *instance) +{ + unsigned int i = 0; + unsigned int ioc_state = PS3_FW_STATE_UNDEFINED; + int ret = -PS3_FAILED; + unsigned int waittime_for_io_completion = + PS3_WAIT_FOR_OUTSTANDING_IO_COMPLETE; + unsigned int interval = 1000; + unsigned int count = 0; + + for (i = 0; i < waittime_for_io_completion * 10 / interval; i++) { + if (!ps3_ioc_state_get_with_check(instance, &ioc_state)) { + LOG_ERROR_LIM("host_no:%u get ioc state NOK\n", + PS3_HOST(instance)); + if (count++ < 3) + continue; + goto l_out; + } + if (ioc_state == PS3_FW_STATE_FAULT || + ioc_state == PS3_FW_STATE_CRITICAL) { + LOG_WARN("host_no:%u ioc_state:0x%x\n", + PS3_HOST(instance), ioc_state); + goto l_out; + } + + if (atomic_read(&instance->cmd_statistics.io_outstanding) == + 0) { + LOG_WARN("host_no:%u io_outstanding is zero\n", + PS3_HOST(instance)); + ret = PS3_SUCCESS; + goto l_out; + } + + ps3_msleep(interval); + } + +l_out: + return ret; +} + +int ps3_reset_host(struct ps3_instance *instance) +{ + int ret = SUCCESS; + int cur_state = PS3_INSTANCE_STATE_INIT; + + LOG_INFO("hno:%u Host reset is requested, IOC outstanding: %d!!\n", + PS3_HOST(instance), + ps3_atomic_read(&instance->cmd_statistics.io_outstanding)); + ps3_atomic_inc(&instance->host_reset_processing); + if (ps3_host_reset_pre_check(instance) != PS3_SUCCESS) { + ret = FAILED; + goto l_out; + } + do { + if (unlikely(ps3_pci_err_recovery_get(instance))) { + LOG_WARN_LIM( + "hno:%u host in pci recovery during reset request\n", + PS3_HOST(instance)); + } else { + ps3_mutex_lock(&instance->state_machine.lock); + if (instance->recovery_context->host_reset_state == + PS3_HOST_RESET_INIT) { + instance->recovery_context->host_reset_state = + PS3_HOST_RESET_START; + ps3_mutex_unlock(&instance->state_machine.lock); + break; + } + ps3_mutex_unlock(&instance->state_machine.lock); + } + ps3_msleep(100); + } while (1); + + + ps3_mutex_lock(&instance->state_machine.lock); + if (instance->recovery_context->recovery_state == + PS3_HARD_RECOVERY_DECIDE) { + do { + ps3_mutex_unlock(&instance->state_machine.lock); + ps3_msleep(100); + + ps3_mutex_lock(&instance->state_machine.lock); + if (likely(instance->recovery_context->recovery_state == + PS3_HARD_RECOVERY_DECIDE)) { + continue; + } else { + ps3_mutex_unlock(&instance->state_machine.lock); + cancel_work_sync(&instance->recovery_context + ->recovery_work); + goto wait_hard_reset; + } + } while (1); + } else if (instance->recovery_context->recovery_state == + PS3_HARD_RECOVERY_SHALLOW) { + ps3_mutex_unlock(&instance->state_machine.lock); + cancel_work_sync(&instance->recovery_context->recovery_work); + } else { + ps3_mutex_unlock(&instance->state_machine.lock); + } + ps3_mutex_lock(&instance->state_machine.lock); + if (unlikely(instance->recovery_context->recovery_state == + PS3_HARD_RECOVERY_DECIDE)) { + instance->recovery_context->recovery_state = + PS3_HARD_RECOVERY_FINISH; + } + ps3_mutex_unlock(&instance->state_machine.lock); + + if (ps3_wait_for_outstanding_complete(instance) == PS3_SUCCESS) { + ret = SUCCESS; + goto l_out; + } + + if (ps3_host_reset_pre_check(instance) != PS3_SUCCESS) { + ret = FAILED; + goto l_out; + } + + if (ps3_hard_reset_pre_check(instance) != PS3_SUCCESS) { + LOG_ERROR("hno:%u hard reset pre check NOK, %s!\n", + PS3_HOST(instance), + namePS3InstanceState(ps3_atomic_read( + &instance->state_machine.state))); + ret = FAILED; + goto l_wait; + } + + ps3_mutex_lock(&instance->state_machine.lock); + if (instance->recovery_context->host_reset_state == + PS3_HOST_RESET_HARD_RESET_DONE) { + instance->recovery_context->host_reset_state = + PS3_HOST_RESET_START; + } + ps3_mutex_unlock(&instance->state_machine.lock); + + ret = ps3_hard_recovery_request_with_retry(instance); + if (ret != PS3_SUCCESS) { + cur_state = ps3_atomic_read(&instance->state_machine.state); + LOG_ERROR("hno:%u recovery request NOK, %s!\n", + PS3_HOST(instance), namePS3InstanceState(cur_state)); + ret = FAILED; + goto l_wait; + } + +wait_hard_reset: + ret = ps3_instance_wait_for_hard_reset_flag_done(instance); + if (ret == PS3_SUCCESS) { + cur_state = ps3_atomic_read(&instance->state_machine.state); + LOG_INFO("hno:%u recovery success, %s!\n", PS3_HOST(instance), + namePS3InstanceState(cur_state)); + ret = SUCCESS; + goto l_wait; + } else if (ret == -PS3_RETRY) { + ps3_mutex_lock(&instance->state_machine.lock); + if (unlikely(instance->recovery_context->recovery_state == + PS3_HARD_RECOVERY_DECIDE)) { + instance->recovery_context->recovery_state = + PS3_HARD_RECOVERY_FINISH; + } + ps3_mutex_unlock(&instance->state_machine.lock); + ret = ps3_hard_recovery_request_with_retry(instance); + if (ret != PS3_SUCCESS) { + cur_state = + ps3_atomic_read(&instance->state_machine.state); + LOG_ERROR("hno:%u recovery request NOK, %s!\n", + PS3_HOST(instance), + namePS3InstanceState(cur_state)); + ret = FAILED; + goto l_wait; + } + ret = ps3_instance_wait_for_hard_reset_flag_done(instance); + if (ret != PS3_SUCCESS) { + cur_state = + ps3_atomic_read(&instance->state_machine.state); + LOG_ERROR( + "hno:%u recovery NOK, %s,host_reset_state:%d!\n", + PS3_HOST(instance), + namePS3InstanceState(cur_state), + instance->recovery_context->host_reset_state); + ret = FAILED; + } else { + ret = SUCCESS; + } + } else { + ret = FAILED; + goto l_out; + } + +l_wait: + while (atomic_read(&instance->cmd_statistics.io_outstanding) != 0) + ps3_msleep(3000); +l_out: + if (!instance->state_machine.is_load && !instance->is_suspend) + instance->state_machine.can_hostreset = PS3_FALSE; + cur_state = ps3_atomic_read(&instance->state_machine.state); + if (cur_state == PS3_INSTANCE_STATE_DEAD && + instance->state_machine.can_hostreset) { + ps3_r1x_conflict_queue_clean_all( + instance, PS3_SCSI_RESULT_HOST_STATUS(DID_NO_CONNECT), + PS3_TRUE); + ps3_qos_waitq_clear_all(instance, PS3_STATUS_HOST_NOT_FOUND); + } + ps3_mutex_lock(&instance->state_machine.lock); + instance->recovery_context->host_reset_state = PS3_HOST_RESET_INIT; + ps3_mutex_unlock(&instance->state_machine.lock); + ps3_atomic_dec(&instance->host_reset_processing); + LOG_WARN("hno:%u host reset hard recovery %d!\n", PS3_HOST(instance), + ret); + return ret; +} + +int ps3_err_reset_target(struct scsi_cmnd *scmd) +{ +#ifndef _WINDOWS + LOG_WARN("hno:%u enter target reset channel:%u id:%u\n", + PS3_HOST(scsi_host_data(scmd)), PS3_SDEV_CHANNEL(scmd->device), + PS3_SDEV_TARGET(scmd->device)); + + return ps3_device_reset_handler(scmd); +#else + (void)scmd; + return PS3_SUCCESS; +#endif +} + +int ps3_err_reset_host(struct scsi_cmnd *scmd) +{ + int ret = 0; +#ifndef _WINDOWS + struct ps3_instance *instance = + (struct ps3_instance *)scmd->device->host->hostdata; +#else + struct ps3_instance *instance = scsi_host_data(scmd); +#endif + + ret = ps3_reset_host(instance); + return ret; +} + +#if defined(PS3_RESET_TIMER) +enum scsi_timeout_action ps3_err_reset_timer(struct scsi_cmnd *scmd) +#else +enum blk_eh_timer_return ps3_err_reset_timer(struct scsi_cmnd *scmd) +#endif +{ + struct ps3_instance *instance = NULL; + unsigned long flags = 0; + + instance = (struct ps3_instance *)scmd->device->host->hostdata; + if (time_after(jiffies, + scmd->jiffies_at_alloc + + (SCMD_GET_REQUEST(scmd)->timeout * 2))) { + LOG_FILE_WARN( + "reset timer not handled [%u:%u:%u:%llu], tag:%d op:0x%x\n" + "\ttimeout:%u alloc_time:%lu cur_time:%lu\n", + PS3_HOST(instance), scmd->device->channel, + scmd->device->id, (unsigned long long)scmd->device->lun, + SCMD_GET_REQUEST(scmd)->tag, + scmd->cmd_len > 0 ? scmd->cmnd[0] : 0xff, + SCMD_GET_REQUEST(scmd)->timeout, scmd->jiffies_at_alloc, + jiffies); +#if defined(PS3_BLK_EH_NOT_HANDLED) + return BLK_EH_NOT_HANDLED; +#elif defined(PS3_RESET_TIMER) + return SCSI_EH_NOT_HANDLED; +#else + return BLK_EH_DONE; +#endif + } + + if (instance->is_support_io_limit && + !instance->fault_context.ioc_busy) { + spin_lock_irqsave(instance->host->host_lock, flags); + instance->host->can_queue = + instance->cmd_attr.throttle_que_depth; + instance->fault_context.last_time = jiffies; + instance->fault_context.ioc_busy = PS3_DRV_TRUE; + + spin_unlock_irqrestore(instance->host->host_lock, flags); + } + + LOG_FILE_WARN("reset timer [%u:%u:%u:%llu], tag:%d op:0x%x\n" + "\ttimeout:%u alloc_time:%lu cur_time:%lu\n", + PS3_HOST(instance), scmd->device->channel, + scmd->device->id, (unsigned long long)scmd->device->lun, + SCMD_GET_REQUEST(scmd)->tag, + scmd->cmd_len > 0 ? scmd->cmnd[0] : 0xff, + SCMD_GET_REQUEST(scmd)->timeout, scmd->jiffies_at_alloc, + jiffies); + +#if defined(PS3_RESET_TIMER) + return SCSI_EH_RESET_TIMER; +#else + return BLK_EH_RESET_TIMER; +#endif +} +static void ps3_scsi_sense_print(struct ps3_instance *instance, + const unsigned char *sense_buffer, + unsigned char len, int CFID) +{ + struct ps3_scsi_sense_hdr sshdr; + + memset(&sshdr, 0, sizeof(sshdr)); + + (void)instance; + if (sense_buffer == NULL || len == 0) + goto l_out; + + sshdr.resp_code = (sense_buffer[0] & PS3_SENSE_RESP_CODE_MASK); + if ((sshdr.resp_code & PS3_SENSE_RESP_CODE_VALID_MASK) != + PS3_SENSE_RESP_CODE_VALID_MASK) { + LOG_WARN_IN_IRQ(instance, "CFID:%d sense rcode invalid:0x%x\n", + CFID, sshdr.resp_code); + goto l_out; + } + + if (sshdr.resp_code >= PS3_SENSE_RESP_CODE_DESC_FORMAT) { + if (len > 1) + sshdr.sense_key = + (sense_buffer[1] & PS3_SENSE_KEY_MASK); + if (len > 2) + sshdr.asc = sense_buffer[2]; + if (len > 3) + sshdr.ascq = sense_buffer[3]; + if (len > 7) + sshdr.additional_len = sense_buffer[7]; + } else { + if (len > 2) + sshdr.sense_key = + (sense_buffer[2] & PS3_SENSE_KEY_MASK); + if (len > 7) { + len = (len < (sense_buffer[7] + 8)) ? + len : + (sense_buffer[7] + 8); + if (len > 12) + sshdr.asc = sense_buffer[12]; + if (len > 13) + sshdr.ascq = sense_buffer[13]; + } + } + + LOG_WARN_IN_IRQ( + instance, + "sense: CFID:%d, rcode:0x%x, key:0x%x, asc:0x%x, ascq:0x%x\n", + CFID, sshdr.resp_code, sshdr.sense_key, sshdr.asc, sshdr.ascq); +l_out: + return; +} +void ps3_check_and_wait_host_reset(struct ps3_instance *instance) +{ + while (ps3_atomic_read(&instance->host_reset_processing) != 0) + ps3_msleep(PS3_LOOP_TIME_INTERVAL_100MS); + +} diff --git a/drivers/scsi/linkdata/ps3stor/ps3_scsi_cmd_err.h b/drivers/scsi/linkdata/ps3stor/ps3_scsi_cmd_err.h new file mode 100644 index 0000000000000000000000000000000000000000..38af5da47656e5122e669e47607daff78297323f --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/ps3_scsi_cmd_err.h @@ -0,0 +1,95 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _PS3_SCSI_CMD_ERR_H_ +#define _PS3_SCSI_CMD_ERR_H_ + +#ifndef _WINDOWS +#include +#else +#include "ps3_cmd_adp.h" +#endif + +#include "ps3_htp_def.h" +#include "ps3_instance_manager.h" +#include "ps3_kernel_version.h" + +#define PS3_SCSI_HOST_SHIFT (16) +#define PS3_SCSI_DRIVER_SHIFT (24) + +#define PS3_SAS_DATA_PRES_SHIFT (8) +#define PS3_SAS_DATA_PRES_MASK (0x3) +#define PS3_SAS_SENSE_LEN_OFFSET (16) +#define PS3_SAS_RESPDATA_LEN_OFFSET (20) +#define PS3_SAS_SENSE_RESPDATA_LEN_BYTE (4) +#define PS3_SAS_SCSI_STATUS_OFFSET (11) +#define PS3_SAS_RESP_CODE_BYTE (3) + +#define PS3_SCSI_STATUS_MASK (0xFF) + +#define PS3_SCSI_RESULT_HOST_STATUS(STATUS) ((STATUS) << PS3_SCSI_HOST_SHIFT) +#define PS3_SCSI_RESULT_DRIVER_STATUS(STATUS) \ + ((STATUS) << PS3_SCSI_DRIVER_SHIFT) + +#define PS3_SENSE_RESP_CODE_VALID_MASK (0x70) +#define PS3_SENSE_RESP_CODE_DESC_FORMAT (0x72) +#define PS3_SENSE_RESP_CODE_MASK (0x7F) +#define PS3_SENSE_KEY_MASK (0xF) +#define PS3_ABORT_WAIT_CMD_FLIGHT_END 50 +#define PS3_NVME_RESP_STATUS_SQ_FULL (0x102) + +enum ps3_sas_resp_code { + PS3_SAS_RESP_CODE_TASK_MGR_COMPLETE = 0x0, + PS3_SAS_RESP_CODE_INVALID_FRAME = 0x2, + PS3_SAS_RESP_CODE_TASK_MGR_NOT_SUPPORT = 0x4, + PS3_SAS_RESP_CODE_TASK_MGR_FAILED = 0x5, + PS3_SAS_RESP_CODE_TASK_MGR_SUCCESS = 0x8, + PS3_SAS_RESP_CODE_INCORRECT_LUN = 0x9, + PS3_SAS_RESP_CODE_OVERLAPPED_INIT_PORT = 0xa, +}; + +enum ps3_sas_data_pres { + PS3_SAS_PRES_NO_DATA = 0x0, + PS3_SAS_PRES_REPSNSE_DATA = 0x1, + PS3_SAS_PRES_SENSE_DATA = 0x2, + PS3_SAS_PRES_RESERVED = 0x3, +}; + +struct ps3_scsi_sense_hdr { + unsigned char resp_code; + unsigned char sense_key; + unsigned char asc; + unsigned char ascq; + unsigned char byte4; + unsigned char byte5; + unsigned char byte6; + unsigned char additional_len; +}; + +int ps3_err_scsi_cmd_fault_proc(struct ps3_instance *instance, + struct ps3_cmd *cmd); +int ps3_err_scsi_task_mgr_reset(struct scsi_cmnd *scmd); +int ps3_err_scsi_task_mgr_abort(struct scsi_cmnd *scmd); +int ps3_device_reset_handler(struct scsi_cmnd *scmd); +int ps3_err_reset_target(struct scsi_cmnd *scmd); +int ps3_err_reset_host(struct scsi_cmnd *scmd); +#if defined(PS3_RESET_TIMER) +enum scsi_timeout_action ps3_err_reset_timer(struct scsi_cmnd *scmd); +#else +enum blk_eh_timer_return ps3_err_reset_timer(struct scsi_cmnd *scmd); +#endif +unsigned char ps3_err_is_resp_from_direct_cmd(unsigned short mode); + +void ps3_errcode_to_scsi_status(struct ps3_instance *instance, + struct scsi_cmnd *s_cmd, unsigned int err_code, + union PS3RespFrame *resp_frame, + unsigned int xfer_cnt, struct ps3_cmd *cmd); + +int ps3_err_scsi_io_processing(struct ps3_instance *instance, unsigned int id, + unsigned int channel); +int ps3_reset_host(struct ps3_instance *instance); + +void ps3_scsih_drv_io_reply_scsi(struct scsi_cmnd *s_cmd, struct ps3_cmd *cmd, + unsigned char resp_status, + unsigned char cmd_lock); +void ps3_check_and_wait_host_reset(struct ps3_instance *instance); + +#endif diff --git a/drivers/scsi/linkdata/ps3stor/ps3_scsih.c b/drivers/scsi/linkdata/ps3stor/ps3_scsih.c new file mode 100644 index 0000000000000000000000000000000000000000..85759caa537d11be701cec07bea7235067c75e15 --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/ps3_scsih.c @@ -0,0 +1,3692 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "ps3_scsih.h" + +#ifndef _WINDOWS +#include +#include +#include +#include +#include "ps3_module_para.h" +#include +#include + +#endif + +#include "ps3_htp.h" +#include "ps3_err_def.h" +#include "ps3_instance_manager.h" +#include "ps3_cmd_channel.h" +#include "ps3_scsi_cmd_err.h" +#include "ps3_scsih_cmd_parse.h" +#include "ps3_util.h" +#include "ps3_cmd_statistics.h" +#include "ps3_scsih_raid_engine.h" +#include "ps3_module_para.h" +#include "ps3_device_manager.h" +#include "ps3_r1x_write_lock.h" +#include "ps3_io_trace.h" +#include "ps3_htp_sas.h" + +static int ps3_scsih_vd_req_frame_build(struct ps3_cmd *cmd); +static int ps3_scsih_sas_hw_req_frame_build(struct ps3_cmd *cmd, + unsigned short disk_id, + unsigned long long data_addr, + unsigned int sge_count); +static int ps3_scsih_sas_req_frame_build(struct ps3_cmd *cmd); +static int ps3_scsih_sata_req_frame_build(struct ps3_cmd *cmd); +static int ps3_scsih_nvme_req_frame_build(struct ps3_cmd *cmd); +static inline unsigned short +ps3_scsih_is_use_hard_cmd(const struct ps3_cmd *cmd); +static int ps3_vd_access_policy_check(struct ps3_instance *instance, + unsigned char channel, unsigned short id, + struct scsi_cmnd *s_cmd); +static unsigned short +ps3_scsih_frontend_data_buf_build(struct ps3_cmd *cmd, + struct PS3FrontEndReqFrame *req); +static void ps3_scsih_req_frame_head_build(struct ps3_cmd *cmd, + unsigned char req_frame_format); +static inline void ps3_scsih_print_req_head(struct ps3_cmd *cmd, + unsigned char log_level); +static unsigned int ps3_scsih_datelen_calc(const struct ps3_cmd *cmd); + +#ifdef _WINDOWS +static void ps3_scsi_need_remap_check(struct ps3_cmd *cmd); +static int ps3_scsi_remap_sgl(struct ps3_cmd *cmd); +#endif + +#define PS3_CMD_WORD_QUE_CALC(vd_id, lba, map_block, que_num) \ + (((vd_id) + (PS3_DIV64_32((lba), (map_block)))) & que_num) + +#define PS3_SCSIH_NOT_SAME_MAPBLOCK(lba, map_block, num_blocks) \ + (((map_block) - (PS3_MOD64((lba), (map_block)))) < (num_blocks)) +#define PS3_LBA(lba_hi, lba_lo) \ + ((((unsigned long long)(lba_hi)) << PS3_SHIFT_DWORD) | (lba_lo)) + +#ifndef _WINDOWS +#define sg_dma_address_u64(sg) sg_dma_address(sg) +#endif + +#define UNMAP_PARAM_LEN (8) +#define UNMAP_DESCRIPTOR_LEN (16) +#define UNMAP_CDB_VALID_LEN (10) + +static struct disk_type_to_proc_func_table g_req_frame_func_table[] = { + { PS3_DEV_TYPE_VD, ps3_scsih_vd_req_frame_build }, + { PS3_DEV_TYPE_SAS_HDD, ps3_scsih_sas_req_frame_build }, + { PS3_DEV_TYPE_SATA_HDD, ps3_scsih_sata_req_frame_build }, + { PS3_DEV_TYPE_SATA_SSD, ps3_scsih_sata_req_frame_build }, + { PS3_DEV_TYPE_SAS_SSD, ps3_scsih_sas_req_frame_build }, + { PS3_DEV_TYPE_NVME_SSD, ps3_scsih_nvme_req_frame_build }, + { PS3_DEV_TYPE_SES, ps3_scsih_sas_req_frame_build }, + { PS3_DEV_TYPE_VEP, ps3_scsih_sas_req_frame_build } + +}; +#ifndef _WINDOWS + +unsigned char ps3_scsih_stream_is_detect(struct ps3_cmd *cmd) +{ + struct ps3_scsi_priv_data *data = + (struct ps3_scsi_priv_data *)cmd->scmd->device->hostdata; + + struct ps3_vd_stream_detect *current_ld_sd; + u64 *track_stream; + u8 stream_num; + u64 shifted_values, unshifted_values; + u64 index_value_mask, shifted_values_mask; + u32 index; + struct ps3_stream_detect *current_sd; + u64 lba = 0; + u32 len = 0; + unsigned char is_stream = PS3_FALSE; + unsigned int typeIndex; + u32 type = + PS3_SCSI_CMD_TYPE(ps3_scsih_cdb_rw_type_get(cmd->scmd->cmnd)); + unsigned long flag = 0; + long long value = 0; + + if (PS3_IF_QUIT_STREAM_DIRECT_DETECT()) + goto l_out; + + + if ((type != PS3_SCSI_CMD_TYPE_READ) && + (type != PS3_SCSI_CMD_TYPE_WRITE)) { + goto l_out; + } + + typeIndex = type - PS3_SCSI_CMD_TYPE_READ; + current_ld_sd = &(data->vd_sd[typeIndex]); + ps3_spin_lock_irqsave(¤t_ld_sd->ps3_sequence_stream_lock, &flag); + track_stream = ¤t_ld_sd->mru_bit_map; + + ps3_scsih_lba_parse(cmd->scmd->cmnd, &lba); + ps3_scsih_len_parse(cmd->scmd->cmnd, &len); + for (index = 0; index < PS3_IO_MAX_STREAMS_TRACKED; ++index) { + stream_num = (*track_stream >> + (index << BITS_PER_INDEX_STREAM_SHIFT)) & + STREAM_MASK; + current_sd = ¤t_ld_sd->stream_track[stream_num]; + value = current_sd->next_seq_lba - IO_STREAM_DETECT_RANGE; + + if ((((value >= 0) && + (lba >= + current_sd->next_seq_lba - IO_STREAM_DETECT_RANGE) && + (lba <= (current_sd->next_seq_lba + + IO_STREAM_DETECT_RANGE * 2))) || + ((current_sd->next_seq_lba) && + (lba >= current_sd->next_seq_lba) && + (lba <= (current_sd->next_seq_lba + 32)))) && + (type == current_sd->rw_type)) { + is_stream = PS3_TRUE; + cmd->io_attr.seq_flag = SCSI_RW_SEQ_CMD; + cmd->req_frame->frontendReq.reqHead.isStream1 = + PS3_TRUE; + current_sd->next_seq_lba = lba + len; + + shifted_values_mask = + (1ULL + << (index << BITS_PER_INDEX_STREAM_SHIFT)) - + 1; + shifted_values = ((*track_stream & shifted_values_mask) + << BITS_PER_INDEX_STREAM); + index_value_mask = + STREAM_MASK + << (index << BITS_PER_INDEX_STREAM_SHIFT); + unshifted_values = + *track_stream & + ~(shifted_values_mask | index_value_mask); + *track_stream = + unshifted_values | shifted_values | stream_num; + goto end; + } + } + stream_num = (*track_stream >> ((PS3_IO_MAX_STREAMS_TRACKED - 1) + << BITS_PER_INDEX_STREAM_SHIFT)) & + STREAM_MASK; + current_sd = ¤t_ld_sd->stream_track[stream_num]; + current_sd->rw_type = type; + current_sd->next_seq_lba = lba + len; + *track_stream = + (((*track_stream & ZERO_LAST_STREAM) << 4) | stream_num); + cmd->io_attr.seq_flag = SCSI_RW_RANDOM_CMD; + +end: + ps3_spin_unlock_irqrestore(¤t_ld_sd->ps3_sequence_stream_lock, + flag); +l_out: + + return is_stream; +} +unsigned char ps3_raid_scsih_stream_is_direct(const struct ps3_cmd *cmd) +{ + struct ps3_scsi_priv_data *p_priv_data = + scsi_device_private_data(cmd->scmd); + unsigned char is_direct = PS3_TRUE; + + if (p_priv_data->dev_type == PS3_DEV_TYPE_VD) { + if (ps3_scsih_is_read_cmd(cmd->io_attr.rw_flag)) { + if (cmd->io_attr.vd_entry->isNvme || + !(cmd->io_attr.vd_entry->isSsd && + (ps3_atomic_read(&p_priv_data->rd_io_outstand) > + PS3_VD_IO_16_OUTSTANDING) && + (cmd->io_attr.num_blocks > + PS3_BLOCK_NUM_OF_32K))) { + is_direct = PS3_FALSE; + } + } + } + + return is_direct; +} +unsigned char ps3_hba_scsih_stream_is_direct(const struct ps3_cmd *cmd) +{ + unsigned char is_direct = PS3_TRUE; + struct ps3_scsi_priv_data *p_priv_data = + scsi_device_private_data(cmd->scmd); + if (p_priv_data->dev_type == PS3_DEV_TYPE_VD && + cmd->io_attr.vd_entry->isNvme && + ps3_scsih_is_read_cmd(cmd->io_attr.rw_flag)) { + if (ps3_atomic_read(&p_priv_data->rd_io_outstand) > + PS3_VD_IO_16_OUTSTANDING && + cmd->io_attr.num_blocks > PS3_BLOCK_NUM_OF_32K) { + is_direct = PS3_FALSE; + } + } + + return is_direct; +} + +static unsigned char ps3_scsih_sys_state_check(struct ps3_instance *instance, + struct scsi_cmnd *s_cmd, + int *scsi_result) +{ + unsigned char ret = PS3_TRUE; + int cur_state = PS3_INSTANCE_STATE_INIT; + + *scsi_result = PS3_SUCCESS; + if (!instance->state_machine.is_load) { + LOG_INFO_LIM("hno:%u instance state not is_load\n", + PS3_HOST(instance)); + goto l_scsi_done; + } + + PS3_IO_START_INC(instance, s_cmd); + cur_state = atomic_read(&instance->state_machine.state); + if (cur_state == PS3_INSTANCE_STATE_DEAD || + cur_state == PS3_INSTANCE_STATE_QUIT) { + PS3_IO_BACK_ERR_INC(instance, s_cmd); + LOG_INFO_LIM("hno:%u instance state is dead/quit\n", + PS3_HOST(instance)); + goto l_scsi_done; + } + + if (instance->recovery_context->host_reset_state != + PS3_HOST_RESET_INIT) { + *scsi_result = SCSI_MLQUEUE_HOST_BUSY; + ret = PS3_FALSE; + PS3_IO_BACK_ERR_INC(instance, s_cmd); + PS3_DEV_BUSY_DEC(s_cmd); + LOG_WARN_LIM("hno:%u host_Reset_state:%d\n", PS3_HOST(instance), + instance->recovery_context->host_reset_state); + goto l_out; + } + + if (cur_state != PS3_INSTANCE_STATE_OPERATIONAL && + cur_state != PS3_INSTANCE_STATE_PRE_OPERATIONAL && + cur_state != PS3_INSTANCE_STATE_SOFT_RECOVERY) { + *scsi_result = SCSI_MLQUEUE_HOST_BUSY; + ret = PS3_FALSE; + PS3_IO_BACK_ERR_INC(instance, s_cmd); + PS3_DEV_BUSY_DEC(s_cmd); + LOG_INFO_LIM( + "hno:%u cannot request scsi cmd tag:%d due to %s\n", + PS3_HOST(instance), SCMD_GET_REQUEST(s_cmd)->tag, + namePS3InstanceState(cur_state)); + goto l_out; + } + + goto l_out; + +l_scsi_done: + PS3_DEV_BUSY_DEC(s_cmd); + s_cmd->result = PS3_SCSI_RESULT_HOST_STATUS(DID_NO_CONNECT); + SCMD_IO_DONE(s_cmd); + *scsi_result = PS3_SUCCESS; + ret = PS3_FALSE; + +l_out: + return ret; +} + +static inline unsigned char +ps3_scsih_is_illegel_vd_io(const struct scsi_cmnd *s_cmd, + struct ps3_instance *instance) +{ + return (!ps3_dev_id_valid_check(instance, s_cmd->device->channel, + s_cmd->device->id, PS3_DISK_TYPE_VD) || + s_cmd->device->lun); +} + +static unsigned char ps3_scsih_device_attr_check(struct ps3_instance *instance, + struct scsi_cmnd *s_cmd, + int *scsi_result) +{ + unsigned char ret = PS3_TRUE; + struct ps3_scsi_priv_data *data = NULL; + static unsigned long j; + *scsi_result = PS3_SUCCESS; + if (unlikely(instance->task_manager_host_busy)) { + PS3_IO_BACK_ERR_INC(instance, s_cmd); + PS3_DEV_BUSY_DEC(s_cmd); + *scsi_result = SCSI_MLQUEUE_DEVICE_BUSY; + ret = PS3_FALSE; + LOG_INFO_LIM("hno:%u task_manager_host_busy\n", + PS3_HOST(instance)); + goto l_out; + } + + data = (struct ps3_scsi_priv_data *)s_cmd->device->hostdata; + if (unlikely(data == NULL)) { + s_cmd->result = PS3_SCSI_RESULT_HOST_STATUS(DID_NO_CONNECT); + LOG_ERROR_TIME_LIM( + &j, PS3_LOG_LIMIT_INTERVAL_MSEC, + "hno:%u device_priv_data is null s_cmd result:%d\n", + PS3_HOST(instance), s_cmd->result); + goto l_scsi_done; + } + + if (unlikely(data->task_manager_busy)) { + PS3_IO_BACK_ERR_INC(instance, s_cmd); + PS3_DEV_BUSY_DEC(s_cmd); + *scsi_result = SCSI_MLQUEUE_DEVICE_BUSY; + ret = PS3_FALSE; + LOG_INFO_LIM("hno:%u task_manager_busy\n", PS3_HOST(instance)); + goto l_out; + } + + if (data->dev_type == PS3_DEV_TYPE_VD) { + if (unlikely(ps3_scsih_is_illegel_vd_io(s_cmd, instance))) { + s_cmd->result = + PS3_SCSI_RESULT_HOST_STATUS(DID_BAD_TARGET); + LOG_ERROR_TIME_LIM( + &j, PS3_LOG_LIMIT_INTERVAL_MSEC, + "hno:%u dev_type:%s id:%u max_vd_count:%u vd id NOK\n", + PS3_HOST(instance), + namePS3DevType((enum PS3DevType)data->dev_type), + s_cmd->device->id, + instance->ctrl_info.maxVdCount); + goto l_scsi_done; + } + + if (unlikely(ps3_vd_access_policy_check( + instance, s_cmd->device->channel, + s_cmd->device->id, + s_cmd) != PS3_SUCCESS)) { + goto l_scsi_done; + } + } + + goto l_out; + +l_scsi_done: + PS3_IO_BACK_ERR_INC(instance, s_cmd); + PS3_DEV_BUSY_DEC(s_cmd); + *scsi_result = PS3_SUCCESS; + ret = PS3_FALSE; + SCMD_IO_DONE(s_cmd); +l_out: + return ret; +} + +static inline unsigned char +ps3_scsih_dev_type_get(const struct scsi_cmnd *s_cmd) +{ + struct ps3_scsi_priv_data *data = + (struct ps3_scsi_priv_data *)s_cmd->device->hostdata; + return data->dev_type; +} + +static inline unsigned char +ps3_scsih_is_vd_scsi_rw_cmd(const struct scsi_cmnd *s_cmd) +{ + return (ps3_scsih_dev_type_get(s_cmd) == PS3_DEV_TYPE_VD) ? + ps3_scsih_cdb_is_rw_cmd(s_cmd->cmnd) : + PS3_DRV_FALSE; +} + +static unsigned char ps3_scsih_qos_check(struct ps3_instance *instance, + struct scsi_cmnd *s_cmd, + int *scsi_result) +{ + unsigned char ret = PS3_DRV_TRUE; + int vd_io_threshold = instance->cmd_attr.vd_io_threshold; + int io_outstand = 0; + int vd_io_outstand = 0; + + if (unlikely(!ps3_stat_outstand_switch_is_open(instance))) + goto l_out; + + io_outstand = + atomic_inc_return(&instance->cmd_statistics.io_outstanding); + if (unlikely(io_outstand > instance->host->can_queue)) { + PS3_IO_BACK_ERR_INC(instance, s_cmd); + PS3_DEV_BUSY_DEC(s_cmd); + *scsi_result = SCSI_MLQUEUE_HOST_BUSY; + ret = PS3_FALSE; + + LOG_INFO_LIM( + "hno:%u ioc io exceed can_queue:%d io_outstanding:%d\n", + PS3_HOST(instance), instance->host->can_queue, + io_outstand); + goto l_dec_io_outstand; + } + + if (unlikely(vd_io_threshold && ps3_scsih_is_vd_scsi_rw_cmd(s_cmd))) { + vd_io_outstand = atomic_inc_return( + &instance->cmd_statistics.vd_io_outstanding); + if (unlikely(vd_io_outstand > vd_io_threshold)) { + PS3_IO_BACK_ERR_INC(instance, s_cmd); + PS3_DEV_BUSY_DEC(s_cmd); + *scsi_result = SCSI_MLQUEUE_DEVICE_BUSY; + ret = PS3_FALSE; + LOG_INFO_LIM( + "hno:%u vd io exceed vd_io_threshold:%d vd_outstanding:%d\n", + PS3_HOST(instance), vd_io_threshold, + vd_io_outstand); + goto l_dec_vd_io_outstand; + } + } + + PS3_VD_OUTSTAND_INC(instance, s_cmd); + + goto l_out; + +l_dec_vd_io_outstand: + atomic_dec(&instance->cmd_statistics.vd_io_outstanding); +l_dec_io_outstand: + atomic_dec(&instance->cmd_statistics.io_outstanding); +l_out: + return ret; +} + +static unsigned char ps3_scsih_que_cmd_check(struct ps3_instance *instance, + struct scsi_cmnd *s_cmd, + int *scsi_result) +{ + unsigned char ret = PS3_TRUE; + + s_cmd->result = DID_OK; + ret = ps3_scsih_sys_state_check(instance, s_cmd, scsi_result); + if (unlikely(!ret)) + goto l_out; + + ret = ps3_scsih_device_attr_check(instance, s_cmd, scsi_result); + if (unlikely(!ret)) + goto l_out; + + ret = ps3_scsih_qos_check(instance, s_cmd, scsi_result); + +l_out: + return ret; +} + +#else +unsigned char ps3_scsih_sys_state_check(struct ps3_instance *instance, + int *host_status) +{ + unsigned char ret = PS3_DRV_TRUE; + int cur_state = PS3_INSTANCE_STATE_INIT; + static unsigned long j; + + if (!instance->state_machine.is_load) { + LOG_WARN_TIME_LIM(&j, PS3_LOG_LIMIT_INTERVAL_MSEC, + "hno:%u instance state not is_load\n", + PS3_HOST(instance)); + *host_status = DID_ERROR; + goto l_out; + } + + cur_state = ps3_atomic_read(&instance->state_machine.state); + if (cur_state == PS3_INSTANCE_STATE_DEAD || + cur_state == PS3_INSTANCE_STATE_QUIT) { + LOG_WARN_TIME_LIM(&j, PS3_LOG_LIMIT_INTERVAL_MSEC, + "hno:%u instance state is dead/quit\n", + PS3_HOST(instance)); + *host_status = DID_BAD_TARGET; + goto l_out; + } + + if (cur_state != PS3_INSTANCE_STATE_OPERATIONAL && + cur_state != PS3_INSTANCE_STATE_PRE_OPERATIONAL && + cur_state != PS3_INSTANCE_STATE_SOFT_RECOVERY) { + ret = PS3_DRV_FALSE; + *host_status = DID_BUS_BUSY; + LOG_ERROR_TIME_LIM(&j, PS3_LOG_LIMIT_INTERVAL_MSEC, + "hno:%u cannot request scsi cmd due to %s\n", + PS3_HOST(instance), + namePS3InstanceState(cur_state)); + goto l_out; + } + +l_out: + return ret; +} + +unsigned char ps3_scsih_stream_is_detect(struct ps3_cmd *cmd) +{ + (void)cmd; + return PS3_FALSE; +} +#endif + +#ifndef _WINDOWS +int ps3_scsih_queue_command(struct Scsi_Host *s_host, struct scsi_cmnd *s_cmd) +{ + int ret = PS3_SUCCESS; + struct ps3_cmd *cmd = NULL; + struct ps3_instance *instance = NULL; + struct ps3_scsi_priv_data *data = + (struct ps3_scsi_priv_data *)s_cmd->device->hostdata; + static unsigned long j; + + instance = (struct ps3_instance *)s_host->hostdata; + if (unlikely(instance == NULL)) { + s_cmd->result = PS3_SCSI_RESULT_HOST_STATUS(DID_NO_CONNECT); + PS3_BUG_ON(instance == NULL); + goto l_scsi_done; + } + + LOG_DEBUG("hno:%u tag:%d op:0x%x cmd_len:%d chl:%u id:%u timeout:%u\n", + s_host->host_no, SCMD_GET_REQUEST(s_cmd)->tag, s_cmd->cmnd[0], + s_cmd->cmd_len, s_cmd->device->channel, s_cmd->device->id, + SCMD_GET_REQUEST(s_cmd)->timeout); + + PS3_DEV_BUSY_INC(s_cmd); + if (unlikely(!ps3_scsih_que_cmd_check(instance, s_cmd, &ret))) + goto l_out; + + if (unlikely(s_cmd->cmd_len > PS3_FRAME_CDB_BUFLEN)) { + LOG_ERROR("hno:%u cmd len %u check NOK\n", PS3_HOST(instance), + s_cmd->cmd_len); + PS3_BUG(); + s_cmd->result = PS3_SCSI_RESULT_HOST_STATUS(DID_ERROR); + goto l_scsi_done; + } + + ps3_scsi_cmd_deliver_get(instance); + if (unlikely(!ps3_is_instance_state_normal(instance, PS3_TRUE))) { + ret = SCSI_MLQUEUE_HOST_BUSY; + goto l_cmd_stat_dec; + } + + cmd = ps3_scsi_cmd_alloc(instance, SCMD_GET_REQUEST(s_cmd)->tag); + if (unlikely(cmd == NULL)) { + LOG_ERROR_TIME_LIM(&j, PS3_LOG_LIMIT_INTERVAL_MSEC, + "hno:%u tag:%d ps3_cmd_get NOK ret:%d\n", + PS3_HOST(instance), + SCMD_GET_REQUEST(s_cmd)->tag, ret); + PS3_IO_BACK_ERR_INC(instance, s_cmd); + PS3_IO_OUTSTAND_DEC(instance, s_cmd); + PS3_VD_OUTSTAND_DEC(instance, s_cmd); + PS3_DEV_BUSY_DEC(s_cmd); + ret = SCSI_MLQUEUE_HOST_BUSY; + ps3_scsi_cmd_deliver_put(instance); + goto l_scsi_done; + } + + if (cmd->scmd != NULL) { + LOG_ERROR_TIME_LIM(&j, PS3_LOG_LIMIT_INTERVAL_MSEC, + "hno:%u CFID:%u cmd is exist\n", + PS3_HOST(instance), + SCMD_GET_REQUEST(s_cmd)->tag); + PS3_BUG(); + s_cmd->result = PS3_SCSI_RESULT_HOST_STATUS(DID_NO_CONNECT); + goto l_cmd_stat_dec; + } + + cmd->scmd = s_cmd; + + ret = instance->ioc_adpter->io_cmd_build(cmd); + if (unlikely(ret != PS3_SUCCESS)) { + switch (ret) { + case -PS3_IO_CONFLICT: + ps3_errcode_to_scsi_status(instance, s_cmd, + SCSI_STATUS_BUSY, NULL, 0, + cmd); + ret = SCSI_MLQUEUE_DEVICE_BUSY; + goto l_cmd_release; + case -PS3_IO_REQUEUE: + s_cmd->result = + PS3_SCSI_RESULT_HOST_STATUS(DID_REQUEUE); + break; + case -PS3_IO_CONFLICT_IN_Q: + LOG_DEBUG("tid:0x%llx hno:%u tag:%d conflict in queue,\n" + "\ttimeout:%u alloc_time:%lu cur_time:%lu\n", + cmd->trace_id, PS3_HOST(instance), + SCMD_GET_REQUEST(s_cmd)->tag, + SCMD_GET_REQUEST(s_cmd)->timeout, + s_cmd->jiffies_at_alloc, jiffies); + ret = PS3_SUCCESS; + cmd->flighting = PS3_FALSE; + wmb(); /* in order to force CPU ordering */ + ps3_scsi_cmd_deliver_put(instance); + goto l_out; + case -PS3_IN_QOS_Q: + ret = PS3_SUCCESS; + cmd->flighting = PS3_FALSE; + wmb(); /* in order to force CPU ordering */ + ps3_scsi_cmd_deliver_put(instance); + goto l_out; + case -PS3_RETRY: + ret = SCSI_MLQUEUE_DEVICE_BUSY; + goto l_cmd_release; + case -PS3_RECOVERED: + case SCSI_MLQUEUE_HOST_BUSY: + ret = SCSI_MLQUEUE_HOST_BUSY; + goto l_cmd_release; + default: + s_cmd->result = + PS3_SCSI_RESULT_HOST_STATUS(DID_NO_CONNECT); + break; + } + LOG_DEBUG("tid:0x%llx hno:%u tag:%d cmd build err ret:%d\n", + cmd->trace_id, PS3_HOST(instance), + SCMD_GET_REQUEST(s_cmd)->tag, ret); + ret = PS3_SUCCESS; + goto l_cmd_release; + } + + ret = ps3_scsi_cmd_send(instance, cmd, PS3_TRUE); + if (unlikely(ret != PS3_SUCCESS)) { + ps3_qos_cmd_update(instance, cmd); + if (ret == -PS3_RECOVERED) { + ret = SCSI_MLQUEUE_HOST_BUSY; + } else if (ret == -PS3_RETRY) { + ret = SCSI_MLQUEUE_DEVICE_BUSY; + } else { + s_cmd->result = + PS3_SCSI_RESULT_HOST_STATUS(DID_NO_CONNECT); + ret = PS3_SUCCESS; + } + LOG_ERROR_LIM("tid:0x%llx hno:%u tag:%d cmd send NOK ret:%d\n", + cmd->trace_id, PS3_HOST(instance), + SCMD_GET_REQUEST(s_cmd)->tag, ret); + PS3_DEV_IO_START_ERR_INC(instance, cmd); + goto l_cmd_release; + } + + ps3_scsi_cmd_deliver_put(instance); + goto l_out; + +l_cmd_release: + ps3_scsi_dma_unmap(cmd); + if (cmd->is_got_r1x == 1) { + data = (struct ps3_scsi_priv_data *)s_cmd->device->hostdata; + ps3_r1x_write_unlock(&data->lock_mgr, cmd); + } + + ps3_scsi_cmd_free(cmd); +l_cmd_stat_dec: + PS3_IO_BACK_ERR_INC(instance, s_cmd); + PS3_IO_OUTSTAND_DEC(instance, s_cmd); + PS3_VD_OUTSTAND_DEC(instance, s_cmd); + PS3_DEV_BUSY_DEC(s_cmd); + ps3_scsi_cmd_deliver_put(instance); +l_scsi_done: + if (ret != SCSI_MLQUEUE_HOST_BUSY && ret != SCSI_MLQUEUE_DEVICE_BUSY) + SCMD_IO_DONE(s_cmd); +l_out: + return ret; +} + +#endif +static inline unsigned char +ps3_is_need_build_hw_req_frame(const struct ps3_cmd *cmd) +{ + return ps3_scsih_is_use_hard_cmd(cmd) == PS3_CMDWORD_FORMAT_HARDWARE; +} + +unsigned char ps3_is_r1x_write_cmd(const struct ps3_cmd *cmd) +{ + unsigned char ret = PS3_FALSE; + + switch (cmd->io_attr.vd_entry->raidLevel) { + case RAID1: + case RAID10: + case RAID1E: + if (cmd->io_attr.rw_flag == PS3_SCSI_CMD_TYPE_WRITE) + ret = PS3_TRUE; + break; + default: + ret = PS3_FALSE; + break; + } + + return ret; +} + +static unsigned char direct_cmd_is_raidlevel_support(struct ps3_cmd *cmd) +{ + unsigned char ret = PS3_DRV_TRUE; + + switch (cmd->io_attr.vd_entry->raidLevel) { + case RAID0: + case RAID00: + break; + case RAID1: + case RAID10: + case RAID1E: + if (cmd->instance->cmd_context.max_r1x_cmd_count > 0) { + if (cmd->io_attr.rw_flag == PS3_SCSI_CMD_TYPE_READ || + cmd->io_attr.rw_flag == PS3_SCSI_CMD_TYPE_WRITE) { + ret = PS3_TRUE; + } else { + ret = PS3_FALSE; + } + } else if (cmd->io_attr.rw_flag != PS3_SCSI_CMD_TYPE_READ) { + ret = PS3_FALSE; + } + break; + case RAID5: + case RAID6: + case RAID50: + case RAID60: + if (cmd->io_attr.rw_flag != PS3_SCSI_CMD_TYPE_READ) + ret = PS3_FALSE; + break; + default: + ret = PS3_FALSE; + break; + } + + return ret; +} + +static unsigned char +direct_cmd_is_plba_sector_aligned(struct ps3_cmd *cmd, + const struct ps3_pd_entry *pd) +{ + const struct PS3VDEntry *vd = cmd->io_attr.vd_entry; + unsigned char ret = PS3_DRV_FALSE; + + if (unlikely(pd->sector_size == 0)) { + LOG_FILE_ERROR("pd chl:%u id:%u sector_size is 0\n", + PS3_CHANNEL(&pd->disk_pos), + PS3_TARGET(&pd->disk_pos)); + goto l_out; + } +#ifndef _WINDOWS + if (PS3_MOD64((cmd->io_attr.plba << ilog2(vd->sectorSize)), + pd->sector_size)) { +#else + if ((cmd->io_attr.plba * vd->sectorSize) % pd->sector_size) { +#endif + LOG_DEBUG("pd chl:%u id:%u plba:0x%llx pd sector_size:%u\n", + PS3_CHANNEL(&pd->disk_pos), PS3_TARGET(&pd->disk_pos), + cmd->io_attr.plba, pd->sector_size); + goto l_out; + } +#ifndef _WINDOWS + if ((cmd->io_attr.num_blocks << ilog2(vd->sectorSize)) % + pd->sector_size) { +#else + if ((cmd->io_attr.num_blocks * vd->sectorSize) % pd->sector_size) { +#endif + LOG_DEBUG("num_blocks:%u pd sector_size:%u vd sector_size:%u\n", + cmd->io_attr.num_blocks, pd->sector_size, + vd->sectorSize); + goto l_out; + } + ret = PS3_DRV_TRUE; +l_out: + return ret; +} +unsigned char +ps3_scsih_sata_direct_is_support(struct ps3_cmd *cmd, + const struct ps3_pd_entry *pd_entry) +{ + unsigned char ret = PS3_FALSE; + + if (cmd == NULL || cmd->instance == NULL || pd_entry == NULL) { + LOG_ERROR_LIM("sata direct support judge parameter NOK\n"); + goto l_out; + } + + if (cmd->instance->ctrl_info.capabilities.supportSataNcq == 0) { + LOG_DEBUG("IOC SATA NCQ not support\n"); + goto l_out; + } + + if (pd_entry->support_ncq == 0) { + LOG_DEBUG("SATA ncq not support\n"); + goto l_out; + } + ret = PS3_TRUE; + +l_out: + return ret; +} + +static inline unsigned char +ps3_scsih_datelen_buflen_is_match(const struct ps3_cmd *cmd) +{ + unsigned char ret = PS3_FALSE; + unsigned int data_len = 0; + + data_len = ps3_scsih_datelen_calc(cmd); + + if (data_len != scsi_bufflen(cmd->scmd)) { + LOG_INFO("data_len:%u not equal to bufflen:%u\n", data_len, + scsi_bufflen(cmd->scmd)); + goto l_out; + } + ret = PS3_TRUE; + +l_out: + return ret; +} + +static unsigned int ps3_scsih_datelen_calc(const struct ps3_cmd *cmd) +{ + unsigned short sector_size = 0; + unsigned int num_blocks = 0; + unsigned int date_len = 0; + static unsigned long j; + + if (cmd->io_attr.dev_type == PS3_DEV_TYPE_VD) { + if (cmd->io_attr.vd_entry == NULL) { + LOG_ERROR_TIME_LIM(&j, PS3_LOG_LIMIT_INTERVAL_MSEC, + "host parameter NOK\n"); + goto l_out; + } + sector_size = cmd->io_attr.vd_entry->sectorSize; + + } else { + sector_size = cmd->io_attr.pd_entry->sector_size; + } + if (sector_size == 0) { + LOG_ERROR_TIME_LIM(&j, PS3_LOG_LIMIT_INTERVAL_MSEC, + "host parameter NOK\n"); + goto l_out; + } + num_blocks = cmd->io_attr.num_blocks; + date_len = num_blocks << ilog2(sector_size); + +l_out: + return date_len; +} + +unsigned char ps3_scsih_sata_direct_is_need(struct ps3_cmd *cmd) +{ + unsigned char ret = PS3_FALSE; + + if (cmd->scmd->cmd_len == 32) { + LOG_DEBUG("read32 or write32 no need direct\n"); + goto l_out; + } + + if (ps3_scsih_datelen_buflen_is_match(cmd) != PS3_TRUE) + goto l_out; + + if (ps3_scsih_cdb_opts_parse(cmd) == PS3_SUCCESS) { + if (likely(cmd->io_attr.cdb_opts.protect == 0 && + cmd->io_attr.cdb_opts.fua == 0)) { + ret = PS3_TRUE; + } else { + LOG_DEBUG("SATA cdb_opts is 0x%x\n", + cmd->io_attr.cdb_opts.option); + } + } else { + LOG_DEBUG("SATA options parse failure\n"); + } +l_out: + return ret; +} + +static inline unsigned char +ps3_scsih_sas_direct_is_need(const struct ps3_cmd *cmd) +{ + (void)cmd; + return PS3_TRUE; +} + +static inline unsigned char ps3_scsih_nvme_direct_is_need(struct ps3_cmd *cmd) +{ + if (ps3_scsih_cdb_opts_parse(cmd) == PS3_SUCCESS) { + if (likely(cmd->io_attr.cdb_opts.protect != 0)) { + LOG_DEBUG("NVMe protect is 0x%x\n", + cmd->io_attr.cdb_opts.protect); + return PS3_FALSE; + } + } else { + LOG_DEBUG("NVMe options parse failure\n"); + return PS3_FALSE; + } + + return (ps3_scsih_datelen_buflen_is_match(cmd) == PS3_TRUE); +} + +static unsigned char +ps3_scsih_direct_cmd_is_supported_logic(struct ps3_cmd *cmd, + const struct ps3_pd_entry *pd_entry) +{ + unsigned char ret = PS3_FALSE; + + if (pd_entry == NULL) { + PS3_BUG(); + goto l_out; + } + + switch (pd_entry->dev_type) { + case PS3_DEV_TYPE_SATA_SSD: + case PS3_DEV_TYPE_SATA_HDD: + ret = ps3_scsih_sata_direct_is_need(cmd); + break; + case PS3_DEV_TYPE_SAS_SSD: + case PS3_DEV_TYPE_SAS_HDD: + ret = ps3_scsih_sas_direct_is_need(cmd); + break; + case PS3_DEV_TYPE_NVME_SSD: + ret = ps3_scsih_nvme_direct_is_need(cmd); + break; + default: + ret = PS3_FALSE; + break; + } +l_out: + return ret; +} + +static unsigned char +direct_cmd_is_supported_device(struct ps3_cmd *cmd, + const struct ps3_pd_entry *pd_entry) +{ + unsigned char ret = PS3_FALSE; + + if (pd_entry == NULL) { + LOG_ERROR("tid:0x%llx hno:%u CFID:%d pd entry is NULL\n", + cmd->trace_id, PS3_HOST(cmd->instance), cmd->index); + PS3_BUG(); + return ret; + } + if (pd_entry->is_direct_disable) { + LOG_DEBUG("Direct is disable\n"); + return ret; + } + switch (pd_entry->dev_type) { + case PS3_DEV_TYPE_SATA_SSD: + case PS3_DEV_TYPE_SATA_HDD: + ret = ps3_scsih_sata_direct_is_support(cmd, pd_entry); + break; + case PS3_DEV_TYPE_SAS_SSD: + case PS3_DEV_TYPE_SAS_HDD: + ret = PS3_TRUE; + break; + case PS3_DEV_TYPE_NVME_SSD: + ret = ps3_scsih_is_protocal_rw(cmd->scmd->cmnd); + break; + default: + ret = PS3_FALSE; + break; + } + + return ret; +} + +static inline int ps3_scsih_is_valid_vlba(struct ps3_cmd *cmd) +{ + return ((U32_HIGH_LOW_TO_U64(cmd->io_attr.lba_hi, cmd->io_attr.lba_lo) + + cmd->io_attr.num_blocks) > cmd->io_attr.vd_entry->capacity) ? + -PS3_FAILED : + PS3_SUCCESS; +} + +static inline unsigned char +ps3_scsih_is_direct_to_normal(const struct ps3_instance *instance, + const struct ps3_cmd *cmd) +{ + return (instance->ioc_adpter->is_need_direct_to_normal != NULL && + instance->ioc_adpter->is_need_direct_to_normal(cmd)); +} + +static inline unsigned char ps3_scsih_is_sas_jbod_cmd(const struct ps3_cmd *cmd) +{ + return cmd->io_attr.dev_type == PS3_DEV_TYPE_SAS_HDD || + cmd->io_attr.dev_type == PS3_DEV_TYPE_SAS_SSD; +} + +static inline unsigned char +ps3_scsih_is_sata_jbod_cmd(const struct ps3_cmd *cmd) +{ + return cmd->io_attr.dev_type == PS3_DEV_TYPE_SATA_HDD || + cmd->io_attr.dev_type == PS3_DEV_TYPE_SATA_SSD; +} + +unsigned char ps3_scsih_is_sata_jbod_mgr_cmd(const struct ps3_cmd *cmd) +{ + return (ps3_scsih_is_sata_jbod_cmd(cmd)) && + (!ps3_scsih_cdb_is_rw_cmd(cmd->scmd->cmnd)); +} + +unsigned int ps3_scsih_xfer_cnt_get(const struct ps3_cmd *cmd) +{ + unsigned int xfer_cnt = 0; + + union PS3RespFrame *resp_frame = cmd->resp_frame; + unsigned short mode = cmd->reply_word.mode; + + if (ps3_err_is_resp_from_direct_cmd(mode) && + (cmd->io_attr.dev_type != PS3_DEV_TYPE_VD)) { + xfer_cnt = resp_frame->sasRespFrame.xfer_cnt; + } else { + xfer_cnt = resp_frame->normalRespFrame.respDetail.xfer_cnt; + } + return xfer_cnt; +} +static inline unsigned char +ps3_scsih_hdd_vd_direct_check(const struct ps3_cmd *cmd) +{ + unsigned char is_direct = PS3_TRUE; + struct ps3_scsi_priv_data *p_priv_data = + scsi_device_private_data(cmd->scmd); + + if (p_priv_data->dev_type == PS3_DEV_TYPE_VD) { + if (!cmd->io_attr.vd_entry->isNvme && + !cmd->io_attr.vd_entry->isSsd) { + if (ps3_scsih_is_write_cmd(cmd->io_attr.rw_flag) && + (ps3_atomic_read(&p_priv_data->wr_io_outstand) != + PS3_VD_IO_1_OUTSTANDING)) { + is_direct = PS3_FALSE; + } else if (ps3_scsih_is_read_cmd( + cmd->io_attr.rw_flag)) { + is_direct = PS3_FALSE; + } + } + } + return is_direct; +} + +static void ps3_scsih_build_direct_cmd(struct ps3_cmd *cmd) +{ + int ret = PS3_SUCCESS; + unsigned long long lba = 0; + + struct ps3_cmd_attr_context *cmd_attr = &cmd->instance->cmd_attr; + + if (!cmd_attr->is_support_direct_cmd) + goto l_out; + + if (cmd->io_attr.is_force_normal) + goto l_out; + + if (!ps3_scsih_is_rw_type(cmd->io_attr.rw_flag)) + goto l_out; + + if ((!ps3_scsih_is_protocal_rw(cmd->scmd->cmnd)) && + ps3_scsih_is_sas_jbod_cmd(cmd)) { + goto l_out; + } + + if (unlikely(cmd->io_attr.num_blocks == 0)) { + LOG_DEBUG("tid:0x%llx hno:%u num blocks is zero\n", + cmd->trace_id, PS3_HOST(cmd->instance)); + goto l_out; + } + + if (unlikely(cmd->io_attr.is_retry_cmd && + ps3_scsih_is_direct_to_normal(cmd->instance, cmd))) { + LOG_FILE_WARN("tid:0x%llx hno:%u is_retry_cmd\n", + cmd->trace_id, PS3_HOST(cmd->instance)); + goto l_out; + } + + if (cmd->io_attr.dev_type == PS3_DEV_TYPE_VD) { + if (cmd->instance->ioc_adpter->scsih_stream_is_detect != NULL && + cmd->instance->ioc_adpter->scsih_stream_is_detect(cmd)) { + LOG_DEBUG( + "hno[%u], cmd index[%u], tid[0x%llx], detected it is sequential io\n", + PS3_HOST(cmd->instance), cmd->index, + cmd->trace_id); + if (cmd->instance->ioc_adpter->scsih_stream_is_direct != + NULL && + !cmd->instance->ioc_adpter->scsih_stream_is_direct( + cmd)) { + goto l_out; + } + } + if (unlikely(!cmd->io_attr.vd_entry->isDirectEnable)) { + LOG_DEBUG("tid:0x%llx hno:%u direct disable\n", + cmd->trace_id, PS3_HOST(cmd->instance)); + goto l_out; + } + + if (cmd->instance->ioc_adpter->write_direct_enable != NULL && + !cmd->instance->ioc_adpter->write_direct_enable(cmd)) { + LOG_DEBUG("tid:0x%llx hno:%u write direct disable\n", + cmd->trace_id, PS3_HOST(cmd->instance)); + goto l_out; + } + + if (unlikely(ps3_scsih_is_valid_vlba(cmd) != PS3_SUCCESS)) { + LOG_ERROR_LIM( + "hno:%u tid:%#llx lba:0x%llx\n" + "\tnum_blks:0x%x vd capacity:%llu cmd out of range\n", + PS3_HOST(cmd->instance), cmd->trace_id, + U32_HIGH_LOW_TO_U64(cmd->io_attr.lba_hi, + cmd->io_attr.lba_lo), + cmd->io_attr.num_blocks, + cmd->io_attr.vd_entry->capacity); + goto l_out; + } + + if (!direct_cmd_is_raidlevel_support(cmd)) + goto l_out; + + if (!ps3_scsih_is_protocal_rw(cmd->scmd->cmnd)) + goto l_out; + + if (cmd->io_attr.vd_entry->raidLevel == RAID1) { + if (cmd->io_attr.vd_entry->mapBlock == 0) { + PS3_BUG(); + goto l_out; + } + if (cmd->instance->is_raid1_direct_skip_mapblock_check) { + LOG_DEBUG("hba raid1, skip MapBlock check\n"); + } else { + lba = PS3_LBA(cmd->io_attr.lba_hi, + cmd->io_attr.lba_lo); + if (PS3_SCSIH_NOT_SAME_MAPBLOCK( + lba, + cmd->io_attr.vd_entry->mapBlock, + cmd->io_attr.num_blocks)) { + LOG_DEBUG( + "hno:%u map_block:%llu lba:0x%llx num_blks:0x%x not same map block\n", + PS3_HOST(cmd->instance), + cmd->io_attr.vd_entry->mapBlock, + lba, cmd->io_attr.num_blocks); + goto l_out; + } + } + + } else { + if ((cmd->instance + ->is_single_disk_raid0_direct_skip_strip_check) && + (cmd->io_attr.vd_entry->raidLevel == RAID0) && + (cmd->io_attr.vd_entry->physDrvCnt == 1)) { + LOG_DEBUG( + "hba raid0 with single disk, skip strip check\n"); + } else { + if (!ps3_scsih_is_same_strip( + cmd->io_attr.vd_entry, + cmd->io_attr.lba_lo, + cmd->io_attr.num_blocks)) { + LOG_DEBUG( + "not same strip lba:0x%x num_blks:0x%x\n", + cmd->io_attr.lba_lo, + cmd->io_attr.num_blocks); + goto l_out; + } + } + } + + ret = ps3_scsih_vd_rw_io_to_pd_calc(cmd); + if (ret != PS3_SUCCESS) { + LOG_DEBUG("cmd index:%u vlba to plba fail\n", + cmd->index); + goto l_out; + } + + if (!direct_cmd_is_supported_device(cmd, + cmd->io_attr.pd_entry)) { + LOG_DEBUG( + "tid:0x%llx hno:%u not support to direct for this cmd\n", + cmd->trace_id, PS3_HOST(cmd->instance)); + goto l_out; + } + + if (ps3_scsih_direct_cmd_is_supported_logic( + cmd, cmd->io_attr.pd_entry) != PS3_TRUE) { + LOG_DEBUG( + "tid:0x%llx hno:%u not need to direct for this cmd\n", + cmd->trace_id, PS3_HOST(cmd->instance)); + goto l_out; + } + + if (!direct_cmd_is_plba_sector_aligned(cmd, + cmd->io_attr.pd_entry)) { + goto l_out; + } + + if (ps3_is_r1x_write_cmd(cmd)) { + if (!direct_cmd_is_supported_device( + cmd, cmd->io_attr.peer_pd_entry)) { + LOG_DEBUG( + "tid:0x%llx hno:%u peer_pd[%u:%u:%u] not\n" + "\tsupport to direct for this cmd\n", + cmd->trace_id, PS3_HOST(cmd->instance), + PS3_CHANNEL(&cmd->io_attr.peer_pd_entry + ->disk_pos), + PS3_TARGET(&cmd->io_attr.peer_pd_entry + ->disk_pos), + PS3_PDID(&cmd->io_attr.peer_pd_entry + ->disk_pos)); + goto l_out; + } + + if (ps3_scsih_direct_cmd_is_supported_logic( + cmd, cmd->io_attr.peer_pd_entry) != + PS3_TRUE) { + goto l_out; + } + + if (!direct_cmd_is_plba_sector_aligned( + cmd, cmd->io_attr.peer_pd_entry)) { + goto l_out; + } + } + if (!ps3_scsih_hdd_vd_direct_check(cmd)) + goto l_out; + cmd->io_attr.direct_flag = + (unsigned char)PS3_CMDWORD_DIRECT_ADVICE; + } else { + if (!direct_cmd_is_supported_device(cmd, + cmd->io_attr.pd_entry)) { + goto l_out; + } + + if (ps3_scsih_direct_cmd_is_supported_logic( + cmd, cmd->io_attr.pd_entry) != PS3_TRUE) { + LOG_DEBUG( + "tid:0x%llx hno:%u not need to direct for this cmd\n", + cmd->trace_id, PS3_HOST(cmd->instance)); + goto l_out; + } + + cmd->io_attr.direct_flag = (unsigned char)PS3_CMDWORD_DIRECT_OK; + cmd->io_attr.plba = + PS3_LBA(cmd->io_attr.lba_hi, cmd->io_attr.lba_lo); + } + +l_out: + return; +} + +static inline unsigned char ps3_scsih_is_invalid_dev(unsigned char type) +{ + return (type <= PS3_DEV_TYPE_UNKNOWN || type >= PS3_DEV_TYPE_COUNT); +} + +static inline void ps3_scsih_use_frontend_prp_check(struct ps3_cmd *cmd) +{ + if (!cmd->instance->is_use_frontend_prp) + goto l_out; + + if (cmd->io_attr.dev_type == PS3_DEV_TYPE_VD) { + if (!cmd->io_attr.vd_entry->isNvme) + goto l_out; + } else { + if (cmd->io_attr.pd_entry->dev_type != PS3_DEV_TYPE_NVME_SSD) + goto l_out; + } + + cmd->io_attr.is_use_frontend_prp = + ps3_scsih_is_protocal_rw(cmd->scmd->cmnd); + +l_out: + return; +} + +static unsigned char ps3_unmap_check_block_valid(struct ps3_cmd *cmd) +{ + unsigned char ret = PS3_FALSE; + unsigned char umap_xbuf[32] = { 0 }; + unsigned int *p_umap_unmblk = &cmd->io_attr.num_blocks; + unsigned short payload_len = 0; + unsigned short desc_len = 0; + + if (cmd->scmd->cmnd[0] != UNMAP) { + ret = PS3_TRUE; + goto l_out; + } + + payload_len = ps3_get_unaligned_be16(&cmd->scmd->cmnd[7]); + if (unlikely(payload_len != scsi_bufflen(cmd->scmd))) { + LOG_DEBUG("hno:%u. unmap: buf len:%u != cdb:%u\n", + PS3_HOST(cmd->instance), scsi_bufflen(cmd->scmd), + payload_len); + goto l_out; + } + + if (unlikely(scsi_bufflen(cmd->scmd) > + (UNMAP_PARAM_LEN + UNMAP_DESCRIPTOR_LEN))) { + LOG_DEBUG("hno:%u. unmap: buf len:%u > %u\n", + PS3_HOST(cmd->instance), scsi_bufflen(cmd->scmd), + (UNMAP_PARAM_LEN + UNMAP_DESCRIPTOR_LEN)); + goto l_out; + } + + scsi_sg_copy_to_buffer(cmd->scmd, umap_xbuf, scsi_bufflen(cmd->scmd)); + + if (ps3_get_unaligned_be16(&umap_xbuf[0]) != (payload_len - 2)) { + LOG_DEBUG("hno:%u. unmap data len: :%u > payload:%u\n", + PS3_HOST(cmd->instance), + ps3_get_unaligned_be16(&umap_xbuf[0]), + payload_len - 2); + goto l_out; + } + + desc_len = ps3_get_unaligned_be16(&umap_xbuf[2]); + if ((desc_len >> 4) > cmd->io_attr.vd_entry->umapBlkDescCnt) { + LOG_DEBUG("hno:%u. unmap: desc len:%u > desc cnt:%u*16\n", + PS3_HOST(cmd->instance), desc_len, + cmd->io_attr.vd_entry->umapBlkDescCnt); + goto l_out; + } + + ps3_scsih_unmap_desc_parse(&umap_xbuf[UNMAP_PARAM_LEN], p_umap_unmblk, + &cmd->io_attr.lba_lo, &cmd->io_attr.lba_hi); + if (*p_umap_unmblk > cmd->io_attr.vd_entry->umapNumblk) { + LOG_DEBUG("hno:%u. unmap: numblk:%u > limit %u\n", + PS3_HOST(cmd->instance), *p_umap_unmblk, + cmd->io_attr.vd_entry->umapNumblk); + goto l_out; + } + + ret = PS3_TRUE; +l_out: + return ret; +} + +static inline void ps3_req_frame_head_init(struct PS3ReqFrameHead *req_head) +{ + req_head->cmdType = 0; + req_head->cmdSubType = 0; + req_head->cmdFrameID = 0; + req_head->control = 0; + req_head->devID.diskID = 0; + req_head->timeout = 0; + req_head->virtDiskSeq = 0; + req_head->reserved1[0] = 0; + req_head->reserved1[1] = 0; + req_head->reserved1[2] = 0; + req_head->reserved1[3] = 0; + req_head->traceID = 0; +} + +static inline unsigned int ps3_hw_vd_max_io_size_get(unsigned char enum_size) +{ + unsigned int max_size = PS3_HW_VD_MAX_IO_SIZE_1M; + + switch (enum_size) { + case PS3_ENUM_HW_VD_MAX_IO_SIZE_1M: + default: + break; + } + return max_size; +} + +static int ps3_scsih_cmd_build_prepare(struct ps3_cmd *cmd) +{ + int ret = PS3_SUCCESS; + int dma_map_ret = 0; + unsigned char is_need_split = PS3_FALSE; + struct ps3_scsi_priv_data *data = scsi_device_private_data(cmd->scmd); + unsigned int hw_vd_max_num_blk = 0; + unsigned int hw_vd_max_io_size = 0; + + cmd->io_attr.dev_type = data->dev_type; + if (cmd->is_inserted_c_q == 0) { + dma_map_ret = ps3_scsi_dma_map(cmd); + if (unlikely(dma_map_ret < 0)) { + LOG_WARN_LIM("hno:%u. cfid[%u] dma_map NOK:%d\n", + PS3_HOST(cmd->instance), cmd->index, + dma_map_ret); + ret = -PS3_ENOMEM; + goto l_out; + } + } + + if (unlikely(ps3_scsih_is_invalid_dev(cmd->io_attr.dev_type))) { + LOG_ERROR_LIM( + "hno:%u get dev type NOK :%d dev_type:%s\n", + PS3_HOST(cmd->instance), cmd->index, + namePS3DevType((enum PS3DevType)cmd->io_attr.dev_type)); + ret = -PS3_DEV_UNKNOWN; + goto l_out; + } + + ps3_req_frame_head_init(&cmd->req_frame->hwReq.reqHead); + + cmd->io_attr.disk_id = data->disk_pos.diskDev.ps3Dev.phyDiskID; +#ifndef _WINDOWS + cmd->io_attr.is_retry_cmd = (cmd->scmd->retries != 0); +#endif + cmd->io_attr.direct_flag = (unsigned char)PS3_CMDWORD_DIRECT_NORMAL; + cmd->io_attr.rw_type = + ps3_scsih_cdb_rw_type_get(scsi_cmnd_cdb(cmd->scmd)); + + if (ps3_scsih_is_rw_type(cmd->io_attr.rw_flag)) { + ps3_scsih_cdb_parse(scsi_cmnd_cdb(cmd->scmd), + &cmd->io_attr.num_blocks, + &cmd->io_attr.lba_lo, &cmd->io_attr.lba_hi, + &is_need_split); + } + + if (cmd->io_attr.dev_type == PS3_DEV_TYPE_VD) { + cmd->io_attr.vd_entry = ps3_dev_mgr_lookup_vd_info_by_id( + cmd->instance, cmd->io_attr.disk_id); + if (unlikely(cmd->io_attr.vd_entry == NULL)) { + ret = -PS3_ENODEV; + goto l_out; + } + + if (unlikely(cmd->io_attr.num_blocks > + cmd->io_attr.vd_entry->maxIOSize)) { + LOG_DEBUG("hno:%u. numblk:%u > maxio:%u\n", + PS3_HOST(cmd->instance), + cmd->io_attr.num_blocks, + cmd->io_attr.vd_entry->maxIOSize); + cmd->io_attr.is_force_normal = PS3_TRUE; + } + + hw_vd_max_io_size = ps3_hw_vd_max_io_size_get( + cmd->instance->ctrl_info.hwVdMaxIOSize); + hw_vd_max_num_blk = hw_vd_max_io_size >> + ps3_blocksize_to_shift( + cmd->io_attr.vd_entry->sectorSize); + if (unlikely(cmd->io_attr.num_blocks > hw_vd_max_num_blk && + cmd->io_attr.num_blocks > + cmd->io_attr.vd_entry->maxIOSize)) { + LOG_DEBUG( + "hno:%u. numblk:%u > max size, hw max numblk:%u maxIOSize:%u\n", + PS3_HOST(cmd->instance), + cmd->io_attr.num_blocks, hw_vd_max_num_blk, + cmd->io_attr.vd_entry->maxIOSize); + goto l_skip_r1x_write_lock; + } + + if (!ps3_unmap_check_block_valid(cmd)) { + cmd->io_attr.is_force_normal = PS3_TRUE; + goto l_skip_r1x_write_lock; + } + + ret = ps3_r1x_write_lock(&data->lock_mgr, cmd); + if (unlikely(ret != PS3_SUCCESS)) + goto l_out; + } else { + cmd->io_attr.pd_entry = ps3_dev_mgr_lookup_pd_info_by_id( + cmd->instance, cmd->io_attr.disk_id); + if (unlikely(cmd->io_attr.pd_entry == NULL)) { + ret = -PS3_ENODEV; + goto l_out; + } + } + +l_skip_r1x_write_lock: + + ps3_scsih_use_frontend_prp_check(cmd); + + ps3_scsih_build_direct_cmd(cmd); + + cmd->cmd_receive_cb = ps3_scsih_io_done; + +l_out: + return ret; +} + +static int ps3_scsih_req_frame_build(struct ps3_cmd *cmd) +{ + int ret = -PS3_FAILED; + unsigned int table_size = ARRAY_SIZE(g_req_frame_func_table); + unsigned int i = 0; +#ifdef _WINDOWS + ps3_scsi_need_remap_check(cmd); + if (cmd->scmd->is_remap_databuff) { + if (ps3_scsi_remap_sgl(cmd) != PS3_SUCCESS) { + LOG_ERROR("remap sgl error\n"); + return ret; + } + } +#endif + + for (; i < table_size; ++i) { + if (cmd->io_attr.dev_type == g_req_frame_func_table[i].type) { + ret = g_req_frame_func_table[i].func(cmd); + break; + } + } + + return ret; +} + +static void ps3_scsih_vd_cmd_word_devid_build(struct ps3_cmd *cmd, + const struct PS3VDEntry *vd_info) +{ + cmd->cmd_word.virtDiskID = (unsigned char)PS3_VDID(&vd_info->diskPos); + + switch (cmd->io_attr.direct_flag) { + case PS3_CMDWORD_DIRECT_OK: + case PS3_CMDWORD_DIRECT_ADVICE: + cmd->cmd_word.phyDiskID = + PS3_PDID(&cmd->io_attr.pd_entry->disk_pos); + break; + case PS3_CMDWORD_DIRECT_RESERVE: + case PS3_CMDWORD_DIRECT_NORMAL: + default: + cmd->cmd_word.phyDiskID = 0; + break; + } + +} + +static unsigned char ps3_is_no_calc_mapblocks(const struct PS3VDEntry *vd_info, + const struct ps3_cmd *cmd) +{ + unsigned char ret = PS3_FALSE; + (void)vd_info; + + if (ps3_scsih_is_sync_cache(scsi_cmnd_cdb(cmd->scmd))) { + LOG_DEBUG("hno:%u is SYNCHRONIZE_CACHE\n", + PS3_HOST(cmd->instance)); + ret = PS3_FALSE; + goto l_out; + } + + if (ps3_scsih_is_rw_type(cmd->io_attr.rw_flag) == PS3_FALSE) { + LOG_DEBUG("hno:%u not rw cmd\n", PS3_HOST(cmd->instance)); + ret = PS3_TRUE; + goto l_out; + } + +l_out: + return ret; +} + +static int ps3_scsih_vd_cmd_word_cpu_set(struct ps3_cmd *cmd, + const struct PS3VDEntry *vd_info) +{ + int ret = PS3_SUCCESS; + unsigned int num_blocks = cmd->io_attr.num_blocks; + unsigned int lba_lo = cmd->io_attr.lba_lo; + unsigned int lba_hi = cmd->io_attr.lba_hi; + unsigned long long lba = 0; + unsigned char que_offset = 0; + struct PS3ReqFrameHead *req_head = &cmd->req_frame->frontendReq.reqHead; + + cmd->cmd_word.qMask = 0; + req_head->mapBlockVer = PS3_CMDWORD_VER_INVALID; + + lba = ((unsigned long long)lba_hi << PS3_SHIFT_DWORD) | lba_lo; + + LOG_DEBUG( + "tid:0x%llx hno:%u CFID:%d lba:0x%llx map_block[%lld], num_blks:0x%x\n", + cmd->trace_id, PS3_HOST(cmd->instance), cmd->index, lba, + vd_info->mapBlock, num_blocks); + + if (ps3_is_no_calc_mapblocks(vd_info, cmd)) { + LOG_DEBUG( + "tid:0x%llx hno:%u CFID:%u lba:0x%llx map_block[%lld], num_blks:0x%x\n", + cmd->trace_id, PS3_HOST(cmd->instance), cmd->index, lba, + vd_info->mapBlock, num_blocks); + goto l_out; + } + + req_head->mapBlockVer = cmd->io_attr.vd_entry->mapBlockVer; + + if (vd_info->mapBlock == 0) { + PS3_BUG(); + ret = -PS3_FAILED; + goto l_out; + } + + if (vd_info->isSsd && + cmd->instance->ioc_adpter->ssd_vd_qmask_calculate != NULL) { + if (cmd->instance->ioc_adpter->ssd_vd_qmask_calculate(cmd)) + goto l_hba_ssd_vd; + } + + que_offset = PS3_CMD_WORD_QUE_CALC( + vd_info->diskPos.diskDev.ps3Dev.virtDiskID, lba, + vd_info->mapBlock, (cmd->instance->ctrl_info.vdQueueNum - 1)); + cmd->cmd_word.qMask = 1 << que_offset; + + if ((PS3_SCSIH_NOT_SAME_MAPBLOCK(lba, vd_info->mapBlock, num_blocks)) && + (cmd->instance->ioc_adpter->rw_cmd_is_need_split != NULL) && + (cmd->instance->ioc_adpter->rw_cmd_is_need_split(cmd))) { + if (que_offset == (cmd->instance->ctrl_info.vdQueueNum - 1)) + cmd->cmd_word.qMask |= 0x1; + else + cmd->cmd_word.qMask |= 1 << (que_offset + 1); + req_head->mapBlockVer = PS3_CMDWORD_VER_INVALID; + LOG_DEBUG("hno:%u map_block:%llu not same map block qMask 0x:%x\n", + PS3_HOST(cmd->instance), vd_info->mapBlock, + cmd->cmd_word.qMask); + } +l_hba_ssd_vd: + + LOG_DEBUG("tid:0x%llx hno:%u CFID:%d que:%d ver:%u\n", cmd->trace_id, + PS3_HOST(cmd->instance), cmd->index, cmd->cmd_word.qMask, + req_head->mapBlockVer); + +l_out: + return ret; +} + +static int ps3_scsih_cmd_word_type_set(struct ps3_cmd *cmd) +{ + int ret = PS3_SUCCESS; + + + switch (cmd->io_attr.rw_flag) { + case PS3_SCSI_CMD_TYPE_READ: + cmd->cmd_word.type = PS3_CMDWORD_TYPE_READ; + break; + case PS3_SCSI_CMD_TYPE_WRITE: + case PS3_SCSI_CMD_TYPE_UNMAP: + case PS3_SCSI_CMD_TYPE_RW: + cmd->cmd_word.type = PS3_CMDWORD_TYPE_WRITE; + break; + case PS3_SCSI_CMD_TYPE_NORW: + cmd->cmd_word.type = PS3_CMDWORD_TYPE_MGR; + break; + default: + cmd->cmd_word.type = PS3_CMDWORD_TYPE_MGR; + ret = -PS3_FAILED; + LOG_ERROR_LIM( + "hno:%u cmd word set type NOK CFID:%d ret:%d rw_flag:%d\n", + PS3_HOST(cmd->instance), cmd->index, ret, + cmd->io_attr.rw_flag); + break; + } + + return ret; +} + +static inline unsigned short +ps3_scsih_is_use_hard_cmd(const struct ps3_cmd *cmd) +{ + return ((cmd->cmd_word.direct == PS3_CMDWORD_DIRECT_OK) || + (cmd->cmd_word.direct == PS3_CMDWORD_DIRECT_ADVICE)) ? + PS3_CMDWORD_FORMAT_HARDWARE : + PS3_CMDWORD_FORMAT_FRONTEND; +} + +static int ps3_scsih_vd_cmd_word_build(struct ps3_cmd *cmd) +{ + int ret = PS3_SUCCESS; + const struct PS3VDEntry *vd_info = cmd->io_attr.vd_entry; + + memset(&cmd->cmd_word, 0, sizeof(cmd->cmd_word)); + ret = ps3_scsih_cmd_word_type_set(cmd); + if (unlikely(ret != PS3_SUCCESS)) { + LOG_ERROR_LIM("tid:0x%llx hno:%u set cmd word fail\n" + "\tcmd:%d chl:%u id:%u\n", + cmd->trace_id, PS3_HOST(cmd->instance), + cmd->index, PS3_SDEV_CHANNEL(cmd->scmd->device), + PS3_SDEV_TARGET(cmd->scmd->device)); + ret = -PS3_FAILED; + goto l_out; + } + + ret = ps3_scsih_vd_cmd_word_cpu_set(cmd, vd_info); + + cmd->cmd_word.direct = cmd->io_attr.direct_flag; +#ifndef _WINDOWS + cmd->cmd_word.isrSN = ps3_msix_index_get(cmd, vd_info->dev_busy_scale); +#else + cmd->cmd_word.isrSN = 0; +#endif + + ps3_scsih_vd_cmd_word_devid_build(cmd, vd_info); + cmd->cmd_word.cmdFrameID = cmd->index; + +l_out: + return ret; +} + +static int ps3_scsih_pd_cmd_word_build(struct ps3_cmd *cmd) +{ + int ret = PS3_SUCCESS; + struct ps3_scsi_priv_data *priv_data = NULL; + + cmd->cmd_word.direct = cmd->io_attr.direct_flag; + ret = ps3_scsih_cmd_word_type_set(cmd); + if (ret != PS3_SUCCESS) + goto l_out; + +#ifndef _WINDOWS + cmd->cmd_word.isrSN = ps3_msix_index_get(cmd, 1); +#else + cmd->cmd_word.isrSN = 0; +#endif + + cmd->cmd_word.cmdFrameID = cmd->index; + priv_data = scsi_device_private_data(cmd->scmd); + cmd->cmd_word.phyDiskID = priv_data->disk_pos.diskDev.ps3Dev.phyDiskID; + cmd->cmd_word.virtDiskID = PS3_INVALID_DEV_ID; + + if ((cmd->cmd_word.type == PS3_CMDWORD_TYPE_READ) || + (cmd->cmd_word.type == PS3_CMDWORD_TYPE_WRITE)) { + cmd->cmd_word.qMask = 0x1; + } + +l_out: + return ret; +} + +static int ps3_scsih_cmd_word_build(struct ps3_cmd *cmd) +{ + int ret = PS3_SUCCESS; + + if (cmd->io_attr.dev_type == PS3_DEV_TYPE_VD) + ret = ps3_scsih_vd_cmd_word_build(cmd); + else + ret = ps3_scsih_pd_cmd_word_build(cmd); + + if (unlikely(ret != PS3_SUCCESS)) { + LOG_ERROR_LIM("tid:0x%llx hno:%u CFID:%d cmd word build NOK!\n", + cmd->trace_id, PS3_HOST(cmd->instance), + cmd->index); + } + + return ret; +} + +static inline const char *ps3_scsih_print_rw_type(unsigned char rw_flag) +{ + static const char *const rw_type_table[] = { + [PS3_SCSI_CMD_TYPE_UNKNOWN] = "SCMD_T_UNKNOWN", + [PS3_SCSI_CMD_TYPE_READ] = "SCMD_T_R", + [PS3_SCSI_CMD_TYPE_WRITE] = "SCMD_T_W", + [PS3_SCSI_CMD_TYPE_RW] = "SCMD_T_RW", + [PS3_SCSI_CMD_TYPE_UNMAP] = "SCMD_T_UNMAP", + [PS3_SCSI_CMD_TYPE_NORW] = "SCMD_T_NORW", + [PS3_SCSI_CMD_TYPE_COUNT] = "SCMD_T_CNT" + }; + + if (rw_flag >= PS3_SCSI_CMD_TYPE_COUNT) + return rw_type_table[PS3_SCSI_CMD_TYPE_UNKNOWN]; + + return rw_type_table[rw_flag]; +} + +static void ps3_scsih_print_cdb(const unsigned char *cdb) +{ + LOG_DEBUG("CDB: %02x %02x %02x %02x %02x %02x %02x %02x\n" + "\t%02x %02x %02x %02x %02x %02x %02x %02x\n" + "\t%02x %02x %02x %02x %02x %02x %02x %02x\n" + "\t%02x %02x %02x %02x %02x %02x %02x %02x\n", + cdb[0], cdb[1], cdb[2], cdb[3], cdb[4], cdb[5], cdb[6], + cdb[7], cdb[8], cdb[9], cdb[10], cdb[11], cdb[12], cdb[13], + cdb[14], cdb[15], cdb[16], cdb[17], cdb[18], cdb[19], cdb[20], + cdb[21], cdb[22], cdb[23], cdb[24], cdb[25], cdb[26], cdb[27], + cdb[28], cdb[29], cdb[30], cdb[31]); +} + +static inline void ps3_scsih_print_req_head(struct ps3_cmd *cmd, + unsigned char log_level) +{ + if (cmd->req_frame->frontendReq.reqHead.reqFrameFormat == + PS3_REQFRAME_FORMAT_FRONTEND) { + struct PS3ReqFrameHead reqHead = + cmd->req_frame->frontendReq.reqHead; + + LOG_LEVEL( + log_level, + "tid:0x%llx hno:%u fe head cmd_t:%d\n" + "\tstype:%d CFID:%d ctrl:0x%x dev id:0x%x noReplyWord:%d\n" + "\tdataFormat:%d reqFrameFormat:%d mapBlockVer:%d isWrite:%d\n" + "\tvirtDiskSeq:%d\n", + reqHead.traceID, PS3_HOST(cmd->instance), + reqHead.cmdType, reqHead.cmdSubType, reqHead.cmdFrameID, + reqHead.control, reqHead.devID.diskID, + reqHead.noReplyWord, reqHead.dataFormat, + reqHead.reqFrameFormat, reqHead.mapBlockVer, + reqHead.isWrite, reqHead.virtDiskSeq); + } else { + struct PS3ReqFrameHead reqHead = cmd->req_frame->hwReq.reqHead; + + LOG_LEVEL( + log_level, + "tid:0x%llx hno:%u fe head cmd_t:%d\n" + "\tstype:%d CFID:%d ctrl:0x%x dev id:0x%x noReplyWord:%d\n" + "\tdataFormat:%d reqFrameFormat:%d mapBlockVer:%d isWrite:%d\n" + "\tvirtDiskSeq:%d\n", + reqHead.traceID, PS3_HOST(cmd->instance), + reqHead.cmdType, reqHead.cmdSubType, reqHead.cmdFrameID, + reqHead.control, reqHead.devID.diskID, + reqHead.noReplyWord, reqHead.dataFormat, + reqHead.reqFrameFormat, reqHead.mapBlockVer, + reqHead.isWrite, reqHead.virtDiskSeq); + } +} + +static void ps3_scsih_print_hw_req(struct ps3_cmd *cmd, unsigned char log_level) +{ + struct IODT_V1 *req = NULL; + struct ps3_pd_entry *pd_entry = + (struct ps3_pd_entry *)cmd->io_attr.pd_entry; + if (pd_entry == NULL) + return; + ps3_scsih_print_req_head(cmd, log_level); + switch (pd_entry->dev_type) { + case PS3_DEV_TYPE_SAS_HDD: + case PS3_DEV_TYPE_SAS_SSD: + req = &cmd->req_frame->hwReq.sasReqFrame; + LOG_LEVEL(log_level, + "hno:%u cmdProto:%d cmdType:%d\n" + "\tcmdLen:%d dataAddr:0x%llx sgeMode:%d\n" + "\tdirect:%d function:%d phyDiskID:%d\n" + "\treqFrameID:%d CmdWordType:%d cmdDir:%d\n" + "\tdataBufLenDWAlign:0x%x iuSrc:%d sataCtl:%d\n", + PS3_HOST(cmd->instance), req->protocolType, + req->frameType, le32_to_cpu(req->cmdLen), + le64_to_cpu(req->dataBaseAddr), req->dmaCfg.sgMode, + req->commonWord.direct, req->commonWord.function, + le16_to_cpu(req->commonWord.phyDiskID), + le16_to_cpu(req->commonWord.reqFrameID), + req->commonWord.type, req->cmdDir, + le32_to_cpu(req->dataBufLenDWAlign), req->iuSrc, + req->sasCtl); + ps3_scsih_print_cdb(req->B.cdb); + break; + case PS3_DEV_TYPE_SATA_HDD: + case PS3_DEV_TYPE_SATA_SSD: + req = &cmd->req_frame->hwReq.sasReqFrame; + LOG_LEVEL(log_level, + "hno:%u cmdProto:%d cmdType:%d\n" + "\tdataAddr:0x%llx sgeMode:%d\n" + "\tdirect:%d function:%d phyDiskID:%d\n" + "\treqFrameID:%d CmdWordType:%d cmdDir:%d\n" + "\tdataBufLenDWAlign:0x%x lba:0x%llx\n" + "\topCode:0x%llx iuSrc:%d sataCtl:%d\n", + PS3_HOST(cmd->instance), req->protocolType, + req->frameType, le64_to_cpu(req->dataBaseAddr), + req->dmaCfg.sgMode, req->commonWord.direct, + req->commonWord.function, + le16_to_cpu(req->commonWord.phyDiskID), + le16_to_cpu(req->commonWord.reqFrameID), + req->commonWord.type, req->cmdDir, + le32_to_cpu(req->dataBufLenDWAlign), + le64_to_cpu(req->C.lba), + (unsigned long long)req->C.opCode, req->iuSrc, + req->sataCtl); + break; + case PS3_DEV_TYPE_NVME_SSD: + break; + default: + LOG_LEVEL(log_level, "hno:%u dev_type:%d\n", + PS3_HOST(cmd->instance), pd_entry->dev_type); + break; + } +} + +static void ps3_scsih_print_frontend_req(struct ps3_cmd *cmd, + unsigned char log_level) +{ + ps3_scsih_print_req_head(cmd, log_level); + LOG_LEVEL(log_level, + "cmd sge_cnt:%d sge_of:%d data_len:%d isStream:%d\n" + "\tnum_blks:0x%x\n", + cmd->req_frame->frontendReq.sgeCount, + cmd->req_frame->frontendReq.sgeOffset, + cmd->req_frame->frontendReq.dataXferLen, + cmd->req_frame->frontendReq.vdAccAttr.isStream, + cmd->io_attr.num_blocks); + ps3_scsih_print_cdb(cmd->req_frame->frontendReq.cdb); +} + +void ps3_scsih_print_req(struct ps3_cmd *cmd, unsigned char log_level) +{ + if (cmd->req_frame->frontendReq.reqHead.reqFrameFormat == + PS3_REQFRAME_FORMAT_FRONTEND) { + ps3_scsih_print_frontend_req(cmd, log_level); + } else { + ps3_scsih_print_hw_req(cmd, log_level); + } +} + +static void ps3_scsih_print_io_cmd(struct ps3_cmd *cmd) +{ + if (cmd->io_attr.dev_type == PS3_DEV_TYPE_VD) { + if (cmd->io_attr.pd_entry != NULL) { + LOG_DEBUG( + "tid:0x%llx hno:%u cmd CFID:%d, dev_t:%s [%d:%d:%d]\n", + cmd->trace_id, PS3_HOST(cmd->instance), + cmd->index, + namePS3DevType((enum PS3DevType)cmd->io_attr + .pd_entry->dev_type), + cmd->io_attr.pd_entry->disk_pos.diskDev.ps3Dev + .softChan, + cmd->io_attr.pd_entry->disk_pos.diskDev.ps3Dev + .devID, + cmd->io_attr.pd_entry->disk_pos.diskDev.ps3Dev + .phyDiskID); + } + } + + LOG_DEBUG( + "tid:0x%llx hno:%u print CMD: CFID:%u\n" + "\tdev_t:%s outstand:%u is_retry_cmd:%d\n" + "\tdirect_f:%d rw_f:%s CMD_WORD: type:%d direct:%d\n" + "\tqmask:0x%x CFID:%d isr_sn:%d vid:%d pid:%d\n" + "\tlba:0x%llx opcode:0x%x\n", + cmd->trace_id, PS3_HOST(cmd->instance), cmd->index, + namePS3DevType((enum PS3DevType)cmd->io_attr.dev_type), + ps3_atomic_read(&cmd->instance->cmd_statistics.io_outstanding), + cmd->io_attr.is_retry_cmd, cmd->io_attr.direct_flag, + ps3_scsih_print_rw_type(cmd->io_attr.rw_flag), + + cmd->cmd_word.type, cmd->cmd_word.direct, cmd->cmd_word.qMask, + cmd->cmd_word.cmdFrameID, cmd->cmd_word.isrSN, + cmd->cmd_word.virtDiskID, cmd->cmd_word.phyDiskID, + ((((unsigned long long)(cmd->io_attr.lba_hi)) << 32) + + (cmd->io_attr.lba_lo)), + cmd->scmd->cmnd[0]); + + ps3_scsih_print_req(cmd, LEVEL_DEBUG); +} + +int ps3_scsih_cmd_build(struct ps3_cmd *cmd) +{ + int ret = PS3_SUCCESS; + static unsigned long j; + + ret = ps3_scsih_cmd_build_prepare(cmd); + if (unlikely(ret != PS3_SUCCESS)) { + LOG_DEBUG( + "tid:0x%llx hno:%u cmd_word build prepare fail CFID:%d ret:%d\n", + cmd->trace_id, PS3_HOST(cmd->instance), cmd->index, + ret); + ret = ((-PS3_IO_CONFLICT == ret || -PS3_IO_REQUEUE == ret || + -PS3_IO_CONFLICT_IN_Q == ret) ? + ret : + SCSI_MLQUEUE_HOST_BUSY); + goto l_out; + } + + ret = ps3_scsih_cmd_word_build(cmd); + if (unlikely(ret != PS3_SUCCESS)) { + LOG_ERROR_TIME_LIM( + &j, PS3_LOG_LIMIT_INTERVAL_MSEC, + "tid:0x%llx hno:%u cmd_word build NOK CFID:%d ret:%d\n", + cmd->trace_id, PS3_HOST(cmd->instance), cmd->index, + ret); + ret = SCSI_MLQUEUE_HOST_BUSY; + goto l_out; + } + + ret = ps3_scsih_req_frame_build(cmd); + if (unlikely(ret != PS3_SUCCESS)) { + LOG_ERROR_TIME_LIM( + &j, PS3_LOG_LIMIT_INTERVAL_MSEC, + "tid:0x%llx hno:%u req_frame NOK CFID:%d ret:%d\n", + cmd->trace_id, PS3_HOST(cmd->instance), cmd->index, + ret); + ret = SCSI_MLQUEUE_HOST_BUSY; + goto l_out; + } + + ps3_scsih_print_io_cmd(cmd); + PS3_IO_TRACE(cmd, PS3_IO_TRACE_DIRECT_SEND); + + ret = ps3_qos_decision(cmd); +l_out: + return ret; +} + +static inline union PS3DiskDev +ps3_scsih_dev_id_get(const struct scsi_cmnd *s_cmd) +{ + struct ps3_scsi_priv_data *data = scsi_device_private_data(s_cmd); + + return data->disk_pos.diskDev; +} + +static inline unsigned char ps3_scsih_is_vd_accelerate(struct ps3_cmd *cmd) +{ + return ps3_scsih_vd_acc_att_build(cmd); +} + +static inline void ps3_sas_cdb_build(struct ps3_cmd *cmd) +{ + unsigned int blocks = 0; + + memset(cmd->io_attr.cdb, 0, PS3_FRAME_CDB_BUFLEN); + memcpy(cmd->io_attr.cdb, cmd->scmd->cmnd, cmd->scmd->cmd_len); +#ifndef _WINDOWS + blocks = cmd->io_attr.num_blocks + << ilog2(cmd->io_attr.vd_entry->sectorSize); +#else + blocks = cmd->io_attr.num_blocks * cmd->io_attr.vd_entry->sectorSize; +#endif + blocks = blocks >> + ps3_blocksize_to_shift(cmd->io_attr.pd_entry->sector_size); + + ps3_scsih_cdb_rebuild(cmd->io_attr.cdb, cmd->scmd->cmd_len, blocks, + (unsigned int)cmd->io_attr.plba, + (unsigned int)(cmd->io_attr.plba >> + PS3_SHIFT_DWORD)); +} +int ps3_vd_direct_req_frame_build(struct ps3_cmd *cmd) +{ + int ret = PS3_SUCCESS; + + switch (cmd->io_attr.pd_entry->dev_type) { + case PS3_DEV_TYPE_SAS_SSD: + case PS3_DEV_TYPE_SAS_HDD: + ps3_sas_cdb_build(cmd); + ret = ps3_scsih_sas_req_frame_build(cmd); + break; + case PS3_DEV_TYPE_SATA_SSD: + case PS3_DEV_TYPE_SATA_HDD: + ret = ps3_scsih_sata_req_frame_build(cmd); + break; + case PS3_DEV_TYPE_NVME_SSD: + ret = ps3_scsih_nvme_req_frame_build(cmd); + break; + default: + PS3_BUG(); + ret = -PS3_FAILED; + break; + } + return ret; +} + +static inline unsigned int +ps3_scsih_data_direction_build(const struct ps3_cmd *cmd) +{ + unsigned int ret; + static unsigned long j; + + if ((cmd->scmd->sc_data_direction == DMA_BIDIRECTIONAL) || + (cmd->scmd->sc_data_direction == DMA_TO_DEVICE)) { + ret = PS3_DATA_DIRECTION_WRITE; + } else if ((cmd->scmd->sc_data_direction == DMA_FROM_DEVICE) || + (cmd->scmd->sc_data_direction == DMA_NONE)) { + ret = PS3_DATA_DIRECTION_READ; + } else { + LOG_ERROR_TIME_LIM( + &j, PS3_LOG_LIMIT_INTERVAL_MSEC, + "hno:%u tid:0x%llx date direction:%d check NOK", + PS3_HOST(cmd->instance), cmd->trace_id, + cmd->scmd->sc_data_direction); + ret = PS3_DATA_DIRECTION_WRITE; + } + return ret; +} + +static int ps3_scsih_vd_frontend_req_build(struct ps3_cmd *cmd, + struct PS3FrontEndReqFrame *req, + unsigned short sge_count) +{ + int ret = PS3_SUCCESS; + struct ps3_scsi_priv_data *p_priv_data = + scsi_device_private_data(cmd->scmd); + ps3_scsih_req_frame_head_build(cmd, PS3_REQFRAME_FORMAT_FRONTEND); + + memcpy(req->cdb, cmd->scmd->cmnd, cmd->scmd->cmd_len); + req->dataXferLen = cmd->io_attr.sgl_buf_len; + req->sgeCount = sge_count; + req->sgeOffset = offsetof(struct PS3FrontEndReqFrame, sgl) >> 2; + if (cmd->io_attr.seq_flag == SCSI_RW_UNUSED_CMD && + cmd->instance->ioc_adpter->scsih_stream_is_detect != NULL) { + cmd->instance->ioc_adpter->scsih_stream_is_detect(cmd); + } + req->vdAccAttr.isStream = + ((cmd->io_attr.seq_flag == SCSI_RW_SEQ_CMD) ? (1) : (0)); + if (ps3_scsih_is_read_cmd(cmd->io_attr.rw_flag)) { + req->vdAccAttr.ioOutStandingCnt = + (unsigned short)ps3_atomic_read( + &p_priv_data->rd_io_outstand); + } + if (ps3_scsih_is_vd_accelerate(cmd)) { + LOG_DEBUG("tid:0x%llx hno:%u cmd_type:%d CFID:%d\n" + "\tret:%d vlba->plba:0x%llx\n", + cmd->trace_id, PS3_HOST(cmd->instance), + req->reqHead.cmdType, cmd->index, ret, + cmd->io_attr.plba); + } + + return ret; +} + +static inline void ps3_align_16bytes_check(unsigned long long addr) +{ + if (unlikely(addr & PS3_SCSI_ALINNMENT_MASK)) + LOG_ERROR_LIM("addr:0x%llx not align\n", addr); +} + +static inline void ps3_align_4bytes_check(unsigned long long addr) +{ + if (unlikely(addr & PS3_SCSI_ALINNMENT_MASK)) + LOG_ERROR_LIM("addr:0x%llx not align\n", addr); +} + +static inline unsigned char +ps3_is_invalid_ossgl_count(unsigned short valid_os_sge_count, + unsigned short max_drv_sge_count) +{ + return (max_drv_sge_count < valid_os_sge_count + 1); +} + +static inline void ps3_last_sge_build(struct PS3Sge *sgl_ptr, + const struct scatterlist *os_sgl) +{ + sgl_ptr->length = cpu_to_le32(sg_dma_len(os_sgl)); + sgl_ptr->addr = cpu_to_le64(sg_dma_address_u64(os_sgl)); + sgl_ptr->lastSge = 1; + sgl_ptr->ext = 0; + +} + +static inline unsigned char ps3_is_last_sge(int sgl_idx, + unsigned short os_sge_count) +{ + return ((sgl_idx + 1) == os_sge_count); +} + +static inline unsigned char ps3_is_list_sge(int sgl_idx, + unsigned short frame_sge_count) +{ + return ((sgl_idx + 1) == frame_sge_count); +} + +static inline void ps3_list_sge_build(struct PS3Sge *sgl_ptr, + const struct ps3_cmd *cmd, + unsigned short ext_sge_size) +{ + sgl_ptr->length = ext_sge_size; + sgl_ptr->addr = cpu_to_le64(cmd->ext_buf_phys); + sgl_ptr->lastSge = 0; + sgl_ptr->ext = 1; + +} + +static unsigned int ps3_scsih_data_buf_len(struct ps3_cmd *cmd, + unsigned int os_sge_count) +{ + struct scsi_cmnd *scp = cmd->scmd; + struct scatterlist *os_sgl = NULL; + unsigned int index = 0; + + cmd->io_attr.sgl_buf_len = 0; + scsi_for_each_sg(scp, os_sgl, os_sge_count, index) { + cmd->io_attr.sgl_buf_len += sg_dma_len(os_sgl); + mb(); /* in order to force CPU ordering */ + } + + if (scsi_bufflen(scp) != cmd->io_attr.sgl_buf_len) { + LOG_INFO("data_buf:%u buf_len:%u mismatch\n", scsi_bufflen(scp), + cmd->io_attr.sgl_buf_len); + } + + return cmd->io_attr.sgl_buf_len; +} + +static inline unsigned short +ps3_drv_max_sge_count(const struct ps3_cmd *cmd, unsigned short frame_sge_count) +{ + unsigned short count = 0; + + if (cmd->instance->cmd_context.sgl_mode_support) { + count = ((unsigned short)cmd->instance->cmd_context + .ext_sge_frame_count + + frame_sge_count); + } else { + count = PS3_MAX((unsigned short)cmd->instance->cmd_context + .ext_sge_frame_count + + 1, + frame_sge_count); + } + + return count; +} + +static inline unsigned short +ps3_scsih_frame_ext_sge_pos(const struct ps3_cmd *cmd, + unsigned short frame_sge_count, + unsigned short os_sge_count) +{ + unsigned short pos = 0; + + if (cmd->instance->cmd_context.sgl_mode_support) + pos = frame_sge_count; + else + pos = (os_sge_count <= frame_sge_count) ? 0 : 1; + + return pos; +} + +static inline unsigned short +ps3_scsih_ext_sge_size_calc(unsigned short ext_sge_pos, + unsigned short frame_sge_count, + unsigned short os_sge_count) +{ + unsigned short cnt = 0; + + if (ext_sge_pos == 0) + cnt = 0; + else if (ext_sge_pos == 1) + cnt = os_sge_count; + else + cnt = os_sge_count - frame_sge_count + 1; + return cnt * sizeof(struct PS3Sge); +} + +static unsigned short ps3_scsih_sgl_build(struct ps3_cmd *cmd, + struct PS3Sge *sge, + unsigned char frame_sge_count) +{ + int index = 0; + unsigned short max_drv_sge_count = 0; + unsigned short os_sge_count = cmd->os_sge_map_count; + struct scsi_cmnd *scp = cmd->scmd; + struct scatterlist *os_sgl = NULL; + struct PS3Sge *sge_ptr = sge; + unsigned short ret_sge_count = 0; + unsigned short ext_sge_pos = 0; + unsigned short ext_sge_size = 0; + static unsigned long j; + + cmd->io_attr.sgl_buf_len = 0; + max_drv_sge_count = ps3_drv_max_sge_count(cmd, frame_sge_count); + + if (unlikely(ps3_is_invalid_ossgl_count(os_sge_count, + max_drv_sge_count))) { + LOG_ERROR_TIME_LIM( + &j, PS3_LOG_LIMIT_INTERVAL_MSEC, + "tid:0x%llx hno:%u CFID:%u io is\n" + "\ttoo huge max_drv_sge_count:%d os_sge_count:%u\n", + cmd->trace_id, PS3_HOST(cmd->instance), cmd->index, + max_drv_sge_count, os_sge_count); + goto l_out; + } + + memset(&cmd->req_frame->hwReq.sgl, 0, + sizeof(cmd->req_frame->hwReq.sgl)); + ext_sge_pos = + ps3_scsih_frame_ext_sge_pos(cmd, frame_sge_count, os_sge_count); + ext_sge_size = ps3_scsih_ext_sge_size_calc(ext_sge_pos, frame_sge_count, + os_sge_count); + + ret_sge_count = os_sge_count; + scsi_for_each_sg(scp, os_sgl, os_sge_count, index) { + if (ps3_is_last_sge(index, os_sge_count)) { + ps3_last_sge_build(sge_ptr, os_sgl); + cmd->io_attr.sgl_buf_len += sge_ptr->length; + ps3_align_4bytes_check(sge_ptr->addr); + LOG_DEBUG("sgl addr:0x%llx len:%d\n", sge_ptr->addr, + sge_ptr->length); + break; + } + + if (ps3_is_list_sge(index, ext_sge_pos)) { + ps3_list_sge_build(sge_ptr, cmd, ext_sge_size); + sge_ptr = (struct PS3Sge *)cmd->ext_buf; + ret_sge_count += 1; + } + + sge_ptr->length = cpu_to_le32(sg_dma_len(os_sgl)); + sge_ptr->addr = cpu_to_le64(sg_dma_address_u64(os_sgl)); + sge_ptr->lastSge = 0; + sge_ptr->ext = 0; + cmd->io_attr.sgl_buf_len += sge_ptr->length; + ps3_align_4bytes_check(sge_ptr->addr); + + sge_ptr++; + } +l_out: + return ret_sge_count; +} + +static int ps3_vd_normal_req_frame_build(struct ps3_cmd *cmd) +{ + int ret = PS3_SUCCESS; + struct PS3FrontEndReqFrame *req = &cmd->req_frame->frontendReq; + unsigned short sge_count = 0; + static unsigned long j; + + memset(&req->reserved, 0, sizeof(req->reserved)); + req->vdAccAttr.isAccActive = 0; + req->vdAccAttr.reserved1 = 0; + memset(&req->vdAccAttr.reserved2, 0, sizeof(req->vdAccAttr.reserved2)); + + sge_count = ps3_scsih_frontend_data_buf_build(cmd, req); + if (sge_count == 0 && cmd->os_sge_map_count != 0) { + cmd->io_attr.is_use_frontend_prp = PS3_FALSE; + sge_count = ps3_scsih_frontend_data_buf_build(cmd, req); + if (sge_count == 0 && cmd->os_sge_map_count != 0) { + ret = -PS3_FAILED; + LOG_ERROR_TIME_LIM(&j, PS3_LOG_LIMIT_INTERVAL_MSEC, + "hno:%u tid:0x%llx drv_sge_count:%u os_sge_count:%u\n", + PS3_HOST(cmd->instance), + cmd->trace_id, sge_count, + cmd->os_sge_map_count); + goto l_out; + } + } + + ret = ps3_scsih_vd_frontend_req_build(cmd, req, sge_count); + if (unlikely(ret != PS3_SUCCESS)) { + LOG_ERROR_TIME_LIM( + &j, PS3_LOG_LIMIT_INTERVAL_MSEC, + "tid:0x%llx hno:%u CFID:%d ret:%d frondend build NOK\n", + cmd->trace_id, PS3_HOST(cmd->instance), cmd->index, + ret); + } + +l_out: + return ret; +} + +static inline int ps3_vd_adv_to_normal_req_frame_rebuild(struct ps3_cmd *cmd) +{ + int ret = PS3_SUCCESS; + static unsigned long j; + + cmd->io_attr.direct_flag = PS3_CMDWORD_DIRECT_NORMAL; + cmd->io_attr.is_use_frontend_prp = PS3_FALSE; + memset((void *)&cmd->cmd_word, 0, sizeof(struct PS3CmdWord)); + memset((void *)cmd->req_frame, 0, sizeof(union PS3ReqFrame)); + memset(cmd->ext_buf, 0, cmd->instance->cmd_context.ext_buf_size); + ret = ps3_scsih_vd_cmd_word_build(cmd); + if (unlikely(ret != PS3_SUCCESS)) { + LOG_ERROR_TIME_LIM( + &j, PS3_LOG_LIMIT_INTERVAL_MSEC, + "tid:0x%llx hno:%u cmd_word build NOK CFID:%d ret:%d\n", + cmd->trace_id, PS3_HOST(cmd->instance), cmd->index, + ret); + ret = -PS3_FAILED; + goto l_out; + } + ret = ps3_vd_normal_req_frame_build(cmd); + + ps3_scsih_print_io_cmd(cmd); +l_out: + return ret; +} + +static int ps3_scsih_vd_req_frame_build(struct ps3_cmd *cmd) +{ + int ret = PS3_SUCCESS; + static unsigned long j; + + if ((cmd->io_attr.direct_flag == PS3_CMDWORD_DIRECT_OK) || + (cmd->io_attr.direct_flag == PS3_CMDWORD_DIRECT_ADVICE)) { + ret = ps3_vd_direct_req_frame_build(cmd); + + if ((ret != PS3_SUCCESS) && (cmd->io_attr.pd_entry->dev_type == + PS3_DEV_TYPE_NVME_SSD)) { + LOG_WARN_TIME_LIM( + &j, PS3_LOG_LIMIT_INTERVAL_MSEC, + "tid:0x%llx hno:%u advice direct NVMe vd NOK, to normal\n" + "\tis_use_frontend_prp:%d\n", + cmd->trace_id, PS3_HOST(cmd->instance), + cmd->io_attr.is_use_frontend_prp); + ret = ps3_vd_adv_to_normal_req_frame_rebuild(cmd); + } + } else { + ret = ps3_vd_normal_req_frame_build(cmd); + } + return ret; +} + +static int ps3_software_zone_build(struct ps3_cmd *cmd, + unsigned long long virtDiskLba, + unsigned char type, unsigned short sge_count) +{ + struct PS3SoftwareZone *zone = NULL; + unsigned int num_blocks = cmd->io_attr.num_blocks; + int ret = PS3_SUCCESS; + static unsigned long j; + + if (unlikely((type < PS3_DEV_TYPE_SAS_HDD) || + (type >= PS3_DEV_TYPE_COUNT))) { + LOG_ERROR_TIME_LIM(&j, PS3_LOG_LIMIT_INTERVAL_MSEC, + "tid:0x%llx hno:%u type:%d\n", cmd->trace_id, + PS3_HOST(cmd->instance), type); + ret = -PS3_FAILED; + goto l_ret; + } + + zone = &cmd->req_frame->hwReq.softwareZone; + ps3_scsih_cdb_opcode_get(cmd->scmd->cmnd, &zone->opcode, + &zone->subOpcode); + + zone->virtDiskLba = cpu_to_le64(virtDiskLba); + zone->numBlocks = cpu_to_le32(num_blocks); + zone->sgeCount = sge_count; + + if (type == PS3_DEV_TYPE_NVME_SSD) { + zone->sglOffset = offsetof(struct PS3NvmeCmdDw0_9, dPtr) >> 2; + zone->sglFormat = 1; + } else { + zone->sglOffset = offsetof(struct PS3HwReqFrame, sgl) >> 2; + zone->sglFormat = 0; + } + zone->isResendCmd = 0; + +l_ret: + return ret; +} + +static unsigned char ps3_prp_build_check(struct ps3_cmd *cmd, + unsigned int data_len) +{ + struct scsi_cmnd *scmd = cmd->scmd; + unsigned char prp_convert_support = PS3_TRUE; + int index = 0; + unsigned short total_sge_count = cmd->os_sge_map_count; + unsigned int nvme_page_size = cmd->instance->cmd_attr.nvme_page_size; + struct scatterlist *sge_ptr = scsi_sglist(scmd); + unsigned long long sge_addr = 0; + unsigned int first_prp_len = 0; + unsigned int nvme_page_size_mask = nvme_page_size - 1; + static unsigned long j; + (void)data_len; + LOG_DEBUG("hno:%u CFID:%u sge_count:%u\n", PS3_HOST(cmd->instance), + cmd->index, total_sge_count); + + scsi_for_each_sg(scmd, sge_ptr, total_sge_count, index) { + sge_addr = sg_dma_address(sge_ptr); + if (index == 0) { + first_prp_len = nvme_page_size - + (sge_addr & nvme_page_size_mask); + if (first_prp_len >= sg_dma_len(sge_ptr)) { + prp_convert_support = PS3_TRUE; + break; + } + + if (total_sge_count <= 1) { + prp_convert_support = PS3_TRUE; + break; + } else if (ps3_utility_mod64( + sge_addr + sg_dma_len(sge_ptr), + nvme_page_size)) { + prp_convert_support = PS3_FALSE; + LOG_WARN_TIME_LIM(&j, + PS3_LOG_LIMIT_INTERVAL_MSEC, + "hno:%u CFID:%u index:%u\n" + "\tsge_addr:0x%llx len:%u\n", + PS3_HOST(cmd->instance), + cmd->index, index, sge_addr, + sg_dma_len(sge_ptr)); + break; + } + } else { + if ((total_sge_count > 1) && + (index == (total_sge_count - 1))) { + if (ps3_utility_mod64(sge_addr, + nvme_page_size)) { + LOG_WARN_TIME_LIM( + &j, PS3_LOG_LIMIT_INTERVAL_MSEC, + "hno:%u CFID:%u index:%u\n" + "\tsge_addr:0x%llx len:%u\n", + PS3_HOST(cmd->instance), + cmd->index, index, sge_addr, + sg_dma_len(sge_ptr)); + prp_convert_support = PS3_FALSE; + break; + } + } + + if ((total_sge_count > 1) && + (index != (total_sge_count - 1))) { + if (ps3_utility_mod64(sg_dma_len(sge_ptr), + nvme_page_size) || + ps3_utility_mod64(sge_addr, + nvme_page_size)) { + LOG_WARN_TIME_LIM( + &j, PS3_LOG_LIMIT_INTERVAL_MSEC, + "middle hno:%u CFID:%u index:%u\n" + "\tsge_addr:0x%llx len:%u\n", + PS3_HOST(cmd->instance), + cmd->index, index, sge_addr, + sg_dma_len(sge_ptr)); + prp_convert_support = PS3_FALSE; + break; + } + } + } + } + + return prp_convert_support; +} + +static unsigned short ps3_scsih_prp_build(struct ps3_cmd *cmd, + unsigned long long *prp_ptr, + unsigned char frame_prp_count, + unsigned int data_len, + unsigned char is_need_clean_sgl) +{ + struct scsi_cmnd *scp = cmd->scmd; + struct scatterlist *sge_scmd = scsi_sglist(scp); + unsigned int page_size = cmd->instance->cmd_attr.nvme_page_size; + unsigned int nvme_page_size_mask = page_size - 1; + unsigned int sge_len = sg_dma_len(sge_scmd); + unsigned long long sge_addr = sg_dma_address_u64(sge_scmd); + unsigned int first_prp_len = 0; + unsigned short prp_entry_count = 0; + static unsigned long j; + + if (is_need_clean_sgl) { + memset(&cmd->req_frame->hwReq.sgl, 0, + sizeof(cmd->req_frame->hwReq.sgl)); + } + *prp_ptr = sge_addr; + prp_entry_count++; + first_prp_len = page_size - (sge_addr & nvme_page_size_mask); + + LOG_DEBUG("hno:%u CFID:%u sge0_addr:0x%llx data_len:%u sge_len:%u\n" + "\tfirst_max_prp_len:%u\n", + PS3_HOST(cmd->instance), cmd->index, sge_addr, data_len, + sge_len, first_prp_len); + + data_len = (data_len > first_prp_len) ? (data_len - first_prp_len) : 0; + if (data_len <= 0) + goto l_out; + + if (sge_len > first_prp_len) { + sge_addr += first_prp_len; + sge_len = (sge_len > first_prp_len) ? + (sge_len - first_prp_len) : + 0; + } else { + sge_scmd = sg_next(sge_scmd); + sge_len = sg_dma_len(sge_scmd); + sge_addr = sg_dma_address_u64(sge_scmd); + } + + prp_ptr++; + frame_prp_count--; + + for (;;) { + if (prp_entry_count > + cmd->instance->cmd_context.max_prp_count) { + LOG_ERROR_TIME_LIM( + &j, PS3_LOG_LIMIT_INTERVAL_MSEC, + "tid:0x%llx hno:%u CFID:%u prp_entry_count:%u\n" + "\tmax_prp_count:%u\n", + cmd->trace_id, PS3_HOST(cmd->instance), + cmd->index, prp_entry_count, + cmd->instance->cmd_context.max_prp_count); + prp_entry_count = 0; + goto l_out; + } + + if (frame_prp_count == 1) { + if (data_len > page_size) { + *prp_ptr = cmd->ext_buf_phys; + prp_ptr = (unsigned long long *)cmd->ext_buf; + prp_entry_count++; + } + frame_prp_count--; + } + + *prp_ptr = sge_addr; + prp_entry_count++; + + sge_len = (sge_len > page_size) ? (sge_len - page_size) : 0; + data_len = (data_len > page_size) ? (data_len - page_size) : 0; + if (frame_prp_count > 0) + frame_prp_count--; + + if (data_len <= 0) { + LOG_DEBUG( + "end hno:%u CFID:%u data_len:%u prp_entry:0x%llx\n" + "\tsge_addr:0x%llx sge_len:%u prp_entry_count:%u\n", + PS3_HOST(cmd->instance), cmd->index, data_len, + *prp_ptr, sge_addr, sge_len, prp_entry_count); + break; + } + + if (unlikely(cmd->instance->is_print_special_log)) { + LOG_DEBUG( + "end hno:%u CFID:%u data_len:%u prp_entry:0x%llx\n" + "\tsge_addr:0x%llx sge_len:%u\n", + PS3_HOST(cmd->instance), cmd->index, data_len, + *prp_ptr, sge_addr, sge_len); + } + + prp_ptr++; + + if (sge_len > 0) { + sge_addr += page_size; + continue; + } + sge_scmd = sg_next(sge_scmd); + sge_addr = sg_dma_address_u64(sge_scmd); + sge_len = sg_dma_len(sge_scmd); + } + +l_out: + return prp_entry_count; +} + +static void ps3_scsih_frontend_prp_build(struct ps3_cmd *cmd, + unsigned long long *prp_ptr, + unsigned int data_len, + unsigned short prp_count) +{ + struct scsi_cmnd *scp = cmd->scmd; + struct scatterlist *sge_scmd = scsi_sglist(scp); + struct PS3Sge *sge_ptr = (struct PS3Sge *)prp_ptr; + unsigned int page_size = cmd->instance->cmd_attr.nvme_page_size; + unsigned int nvme_page_size_mask = page_size - 1; + unsigned int first_prp_len = 0; + unsigned int sge_len = sg_dma_len(sge_scmd); + unsigned long long sge_addr = sg_dma_address_u64(sge_scmd); + + first_prp_len = page_size - + (sg_dma_address_u64(sge_scmd) & nvme_page_size_mask); + data_len = (data_len > first_prp_len) ? (data_len - first_prp_len) : 0; + + sge_ptr->length = first_prp_len; + sge_ptr->addr = sge_addr; + sge_ptr->lastSge = 0; + sge_ptr->ext = 0; + ps3_align_16bytes_check(sge_ptr->addr); + + LOG_DEBUG("hno:%u CFID:%u sge0_addr:0x%llx sge_len:%u\n", + PS3_HOST(cmd->instance), cmd->index, sge_ptr->addr, + sge_ptr->length); + + if (data_len <= 0) + goto l_out; + + if (sge_len > first_prp_len) { + sge_addr += first_prp_len; + sge_len = (sge_len > first_prp_len) ? + (sge_len - first_prp_len) : + 0; + } else { + sge_scmd = sg_next(sge_scmd); + sge_len = sg_dma_len(sge_scmd); + sge_addr = sg_dma_address_u64(sge_scmd); + } + + sge_ptr++; + + if (data_len <= page_size) { + sge_ptr->length = data_len; + sge_ptr->addr = sge_addr; + sge_ptr->lastSge = 1; + sge_ptr->ext = 0; + ps3_align_16bytes_check(sge_ptr->addr); + goto l_out; + } else { + sge_ptr->length = (prp_count - PS3_FRAME_REQ_PRP_NUM_FE) * + sizeof(unsigned long long); + sge_ptr->addr = cmd->ext_buf_phys; + sge_ptr->lastSge = 0; + sge_ptr->ext = 1; + sge_ptr = (struct PS3Sge *)prp_ptr; + sge_ptr = sge_ptr + PS3_FRAME_REQ_SGE_NUM_FE - 1; + sge_ptr->length = (prp_count - PS3_FRAME_REQ_PRP_NUM_FE) * + sizeof(unsigned long long); + sge_ptr->addr = cmd->ext_buf_phys; + sge_ptr->lastSge = 0; + sge_ptr->ext = 1; + } + LOG_DEBUG("hno:%u CFID:%u sge1_addr:0x%llx sge_len:%u sge_ext:%d\n", + PS3_HOST(cmd->instance), cmd->index, sge_ptr->addr, + sge_ptr->length, sge_ptr->ext); +l_out: + return; +} + +static inline int ps3_scsih_prp_len_check(struct ps3_cmd *cmd, + unsigned int data_len) +{ + int ret = PS3_SUCCESS; + unsigned int max_prp_count = cmd->instance->cmd_context.max_prp_count; + unsigned long long max_nvme_data_size = + (unsigned long long)(max_prp_count - + (unsigned int)PS3_FRAME_REQ_PRP_NUM_FE - + 1) * + cmd->instance->cmd_attr.nvme_page_size; + static unsigned long j; + + if (unlikely(max_nvme_data_size < data_len)) { + LOG_ERROR_TIME_LIM(&j, PS3_LOG_LIMIT_INTERVAL_MSEC, + "tid:0x%llx hno:%u io is too huge\n" + "\t[max_nvme_data_size:%llu][data_len:%d]\n", + cmd->trace_id, PS3_HOST(cmd->instance), + max_nvme_data_size, data_len); + ret = -PS3_FAILED; + } + + return ret; +} +static unsigned short +ps3_scsih_frontend_data_buf_build(struct ps3_cmd *cmd, + struct PS3FrontEndReqFrame *req) +{ + unsigned short sge_count = 0; + unsigned int data_len = 0; + static unsigned long j; + + if (cmd->io_attr.is_use_frontend_prp) { + if (cmd->os_sge_map_count == 0) { + LOG_WARN_TIME_LIM( + &j, PS3_LOG_LIMIT_INTERVAL_MSEC, + "tid:0x%llx hno:%u CFID:%u os_sge_map_count==0\n", + cmd->trace_id, PS3_HOST(cmd->instance), + cmd->index); + sge_count = 0; + goto l_out; + } + data_len = ps3_scsih_data_buf_len(cmd, cmd->os_sge_map_count); + if (ps3_scsih_prp_len_check(cmd, data_len) != PS3_SUCCESS) { + sge_count = 0; + goto l_out; + } + + if (ps3_prp_build_check(cmd, data_len)) { + sge_count = + ps3_scsih_prp_build(cmd, &req->prp.prp1, + PS3_FRAME_REQ_PRP_NUM_FE, + data_len, PS3_TRUE); + + ps3_scsih_frontend_prp_build(cmd, &req->prp.prp1, + data_len, sge_count); + } else { + LOG_WARN_TIME_LIM( + &j, PS3_LOG_LIMIT_INTERVAL_MSEC, + "tid:0x%llx hno:%u CFID:%u nvme prp change to sgl\n", + cmd->trace_id, PS3_HOST(cmd->instance), + cmd->index); + sge_count = 0; + } + } else { + sge_count = ps3_scsih_sgl_build(cmd, req->sgl, + PS3_FRAME_REQ_SGE_NUM_FE); + } + +l_out: + return sge_count; +} + +static int ps3_scsih_pd_frontend_req_build(struct ps3_cmd *cmd) +{ + int ret = PS3_SUCCESS; + struct PS3FrontEndReqFrame *req = &cmd->req_frame->frontendReq; + static unsigned long j; + + ps3_scsih_req_frame_head_build(cmd, PS3_REQFRAME_FORMAT_FRONTEND); + + memset(&req->reserved, 0, sizeof(req->reserved)); + req->vdAccAttr.isAccActive = 0; + req->vdAccAttr.reserved1 = 0; + memset(&req->vdAccAttr.reserved2, 0, sizeof(req->vdAccAttr.reserved2)); + + req->sgeCount = ps3_scsih_frontend_data_buf_build(cmd, req); + if (req->sgeCount == 0 && cmd->os_sge_map_count != 0) { + cmd->io_attr.is_use_frontend_prp = PS3_FALSE; + cmd->req_frame->hwReq.reqHead.dataFormat = PS3_SGL; + req->sgeCount = ps3_scsih_frontend_data_buf_build(cmd, req); + if (req->sgeCount == 0 && cmd->os_sge_map_count != 0) { + ret = -PS3_FAILED; + LOG_ERROR_TIME_LIM(&j, PS3_LOG_LIMIT_INTERVAL_MSEC, + "hno:%u tid:0x%llx drv_sge_count:%u\n" + "\tos_sge_count:%u\n", + PS3_HOST(cmd->instance), + cmd->trace_id, req->sgeCount, + cmd->os_sge_map_count); + goto l_out; + } + } + + memcpy(req->cdb, cmd->scmd->cmnd, cmd->scmd->cmd_len); + + req->sgeOffset = offsetof(struct PS3FrontEndReqFrame, sgl) >> 2; + req->dataXferLen = cmd->io_attr.sgl_buf_len; + +l_out: + return ret; +} + +static unsigned short ps3_scsi_cmd_timeout_get(struct ps3_cmd *cmd) +{ + unsigned int time_s = SCMD_GET_REQUEST(cmd->scmd)->timeout / HZ; + unsigned short timeout = 0; + + if (time_s > 0xfffe) { + timeout = 0; + return timeout; + } + timeout = time_s; + return timeout; +} + +static void ps3_scsih_req_frame_head_build(struct ps3_cmd *cmd, + unsigned char req_frame_format) +{ + struct PS3ReqFrameHead *req_head = &cmd->req_frame->hwReq.reqHead; + static unsigned long j; + + req_head->reqFrameFormat = req_frame_format; + req_head->cmdSubType = 0; + req_head->cmdFrameID = cmd->index; + req_head->noReplyWord = PS3_CMD_WORD_NEED_REPLY_WORD; + req_head->dataFormat = + cmd->io_attr.is_use_frontend_prp || + req_frame_format == PS3_REQFRAME_FORMAT_NVME ? + PS3_PRP : + PS3_SGL; + req_head->isWrite = ps3_scsih_data_direction_build(cmd); + req_head->timeout = ps3_scsi_cmd_timeout_get(cmd); + req_head->traceID = cmd->trace_id; + if (cmd->io_attr.dev_type == PS3_DEV_TYPE_VD) { + req_head->devID = cmd->io_attr.vd_entry->diskPos.diskDev; + if (unlikely(req_head->devID.diskID == 0)) { + LOG_ERROR_TIME_LIM( + &j, PS3_LOG_LIMIT_INTERVAL_MSEC, + "tid:0x%llx hno:%u chl:%u id:%u dev id:0x%x NOK\n", + cmd->trace_id, PS3_HOST(cmd->instance), + PS3_SDEV_CHANNEL(cmd->scmd->device), + PS3_SDEV_TARGET(cmd->scmd->device), + req_head->devID.diskID); + } + + req_head->cmdType = ps3_scsih_is_rw_type(cmd->io_attr.rw_flag) ? + PS3_CMD_VD_SCSI_IO_RW : + PS3_CMD_VD_SCSI_IO_NORW; + req_head->virtDiskSeq = cmd->io_attr.vd_entry->virtDiskSeq; + + } else { + req_head->devID.diskID = + PS3_DISKID(&cmd->io_attr.pd_entry->disk_pos); + req_head->mapBlockVer = PS3_CMDWORD_VER_INVALID; + req_head->cmdType = ps3_scsih_is_rw_type(cmd->io_attr.rw_flag) ? + PS3_CMD_PD_SCSI_IO_RW : + PS3_CMD_PD_SCSI_IO_NORW; + } +} + +static inline unsigned long long +ps3_scsih_direct_sgl_base_addr(const struct ps3_cmd *cmd, + unsigned short sge_count) +{ + unsigned long long data_base = 0; + + if (sge_count == PS3_SGL_MODE_SGE_COUNT_DIRECT) { + data_base = cmd->req_frame->hwReq.sgl[0].addr; + } else if ((sge_count > PS3_FRAME_REQ_SGE_NUM_HW) && + (!cmd->instance->cmd_context.sgl_mode_support)) { + data_base = cmd->ext_buf_phys; + ps3_align_16bytes_check(data_base); + } else { + data_base = cmd->req_frame_phys + + offsetof(struct PS3HwReqFrame, sgl); + ps3_align_16bytes_check(data_base); + } + + return data_base; +} + +static inline void ps3_scsih_sata_iodt_data_build(struct ps3_cmd *cmd, + unsigned int sge_count, + unsigned long long data_addr) +{ + struct IODT_V1 *iodt = &cmd->req_frame->hwReq.sasReqFrame; + struct ps3_scsi_io_attr *cmd_io_attr = &cmd->io_attr; + unsigned short sector_size = cmd_io_attr->pd_entry->sector_size; +#ifndef _WINDOWS + unsigned int data_buf_len = cmd_io_attr->num_blocks + << ilog2(sector_size); +#else + unsigned int data_buf_len = cmd_io_attr->num_blocks * sector_size; +#endif + switch (sge_count) { + case PS3_SGL_MODE_SGE_COUNT_NO_DATA: + iodt->dataBufLenDWAlign = 0; + iodt->dataBaseAddr = 0; + iodt->dmaCfg.sgMode = IODT_SGEMODE_DIRECT; + break; + case PS3_SGL_MODE_SGE_COUNT_DIRECT: + iodt->dataBaseAddr = cpu_to_le64(data_addr); + iodt->dataBufLenDWAlign = + cpu_to_le32(ENCODE_CCS_XFERLEN(data_buf_len)); + iodt->dmaCfg.sgMode = IODT_SGEMODE_DIRECT; + break; + default: + iodt->dataBaseAddr = cpu_to_le64(data_addr); + iodt->dataBufLenDWAlign = + cpu_to_le32(ENCODE_CCS_XFERLEN(data_buf_len)); + iodt->dmaCfg.sgMode = IODT_SGEMODE_SGL; + break; + } +} + +static int ps3_scsih_sata_hw_req_frame_build(struct ps3_cmd *cmd, + unsigned short sge_count, + unsigned long long data_addr) +{ + int ret = PS3_SUCCESS; + struct ps3_scsi_io_attr *cmd_io_attr = &cmd->io_attr; + struct IODT_V1 *iodt = &cmd->req_frame->hwReq.sasReqFrame; + struct PS3ReqFrameHead *req_head = &cmd->req_frame->hwReq.reqHead; + unsigned char req_frame_format = PS3_REQFRAME_FORMAT_SATA; + + memset(iodt, 0, sizeof(struct IODT_V1)); + + ps3_scsih_req_frame_head_build(cmd, req_frame_format); + + iodt->protocolType = PROTOCOL_DIRT; + iodt->frameType = FRAMETYPE_DIRECT; + iodt->iuSrc = IU_SRC_TUPLE; + + if (cmd->cmd_word.type == PS3_CMDWORD_TYPE_READ) { + iodt->commonWord.type = PS3_CMDWORD_TYPE_READ; + iodt->C.opCode = PS3_ATA_OPC_READ_FPDMA; + } else { + iodt->commonWord.type = PS3_CMDWORD_TYPE_WRITE; + iodt->C.opCode = PS3_ATA_OPC_WRITE_FPDMA; + } + + iodt->cmdDir = ps3_scsih_data_direction_build(cmd); + iodt->sataCtl = 0; + iodt->sasCtl = 1; + iodt->commonWord.direct = DIRECT_FLAG_DIRECT; + iodt->commonWord.reqFrameID = req_head->cmdFrameID; + iodt->commonWord.function = ps3_get_pci_function(cmd->instance->pdev); + iodt->commonWord.phyDiskID = cmd->cmd_word.phyDiskID; + + iodt->C.lba = cmd_io_attr->plba; + + ps3_scsih_sata_iodt_data_build(cmd, sge_count, data_addr); + + return ret; +} + +static int ps3_scsih_sata_req_frame_build(struct ps3_cmd *cmd) +{ + int ret = PS3_SUCCESS; + unsigned long long lba = 0; + unsigned long long data_addr = 0; + unsigned short sge_count = 0; + + if (ps3_is_need_build_hw_req_frame(cmd)) { + sge_count = ps3_scsih_sgl_build(cmd, cmd->req_frame->hwReq.sgl, + PS3_FRAME_REQ_SGE_NUM_HW); + data_addr = ps3_scsih_direct_sgl_base_addr(cmd, sge_count); + ret = ps3_scsih_sata_hw_req_frame_build(cmd, sge_count, + data_addr); + if (likely(ret == PS3_SUCCESS)) { + cmd->req_frame->hwReq.reqHead.traceID = cmd->trace_id; + lba = PS3_LBA(cmd->io_attr.lba_hi, cmd->io_attr.lba_lo); + ret = ps3_software_zone_build( + cmd, lba, cmd->io_attr.pd_entry->dev_type, + sge_count); + } + } else { + ret = ps3_scsih_pd_frontend_req_build(cmd); + } + + return ret; +} + +static inline void ps3_scsih_sas_iodt_data_build(struct ps3_cmd *cmd, + unsigned long long data_addr, + unsigned int sge_count) +{ + struct IODT_V1 *iodt = &cmd->req_frame->hwReq.sasReqFrame; + + switch (sge_count) { + case PS3_SGL_MODE_SGE_COUNT_NO_DATA: + iodt->dataBufLenDWAlign = 0; + iodt->dataBaseAddr = 0; + iodt->dmaCfg.sgMode = IODT_SGEMODE_DIRECT; + break; + + case PS3_SGL_MODE_SGE_COUNT_DIRECT: + iodt->dataBufLenDWAlign = cpu_to_le32( + ENCODE_CCS_XFERLEN(cmd->io_attr.sgl_buf_len)); + iodt->dataBaseAddr = cpu_to_le64(data_addr); + iodt->dmaCfg.sgMode = IODT_SGEMODE_DIRECT; + break; + + default: + iodt->dataBufLenDWAlign = cpu_to_le32( + ENCODE_CCS_XFERLEN(cmd->io_attr.sgl_buf_len)); + iodt->dataBaseAddr = cpu_to_le64(data_addr); + iodt->dmaCfg.sgMode = IODT_SGEMODE_SGL; + break; + } +} + +static int ps3_scsih_sas_hw_req_frame_build(struct ps3_cmd *cmd, + unsigned short disk_id, + unsigned long long data_addr, + unsigned int sge_count) +{ + struct IODT_V1 *iodt = &cmd->req_frame->hwReq.sasReqFrame; + unsigned char *cdb = cmd->scmd->cmnd; + int ret = PS3_SUCCESS; + struct PS3ReqFrameHead *req_head = &cmd->req_frame->hwReq.reqHead; + unsigned char req_frame_format = PS3_REQFRAME_FORMAT_SAS; + + if (cmd->io_attr.dev_type == PS3_DEV_TYPE_VD) + cdb = cmd->io_attr.cdb; + memset(iodt, 0, sizeof(struct IODT_V1)); + + ps3_scsih_req_frame_head_build(cmd, req_frame_format); + + iodt->protocolType = PROTOCOL_DIRT; + iodt->frameType = FRAMETYPE_DIRECT; + iodt->iuSrc = IU_SRC_IODT; + + if (cmd->scmd->cmd_len != CMD_LEN_THR) + iodt->cmdLen = CMD_LEN_S; + else + iodt->cmdLen = CMD_LEN_L; + + memcpy(iodt->B.cdb, cdb, CMD_LEN_THR); + + iodt->cmdDir = ps3_scsih_data_direction_build(cmd); + + iodt->sasCtl = 0; + + if (cmd->cmd_word.type == PS3_CMDWORD_TYPE_READ) + iodt->commonWord.type = PS3_CMDWORD_TYPE_READ; + else + iodt->commonWord.type = PS3_CMDWORD_TYPE_WRITE; + + iodt->commonWord.direct = DIRECT_FLAG_DIRECT; + iodt->commonWord.function = ps3_get_pci_function(cmd->instance->pdev); + iodt->commonWord.phyDiskID = disk_id; + iodt->commonWord.reqFrameID = req_head->cmdFrameID; + ps3_scsih_sas_iodt_data_build(cmd, data_addr, sge_count); + + return ret; +} + +static int ps3_scsih_sas_req_frame_build(struct ps3_cmd *cmd) +{ + int ret = PS3_SUCCESS; + unsigned short sge_count = 0; + unsigned long long data_addr = 0; + unsigned long long lba = 0; + + if (ps3_is_need_build_hw_req_frame(cmd)) { + sge_count = ps3_scsih_sgl_build(cmd, cmd->req_frame->hwReq.sgl, + PS3_FRAME_REQ_SGE_NUM_HW); + data_addr = ps3_scsih_direct_sgl_base_addr(cmd, sge_count); + ret = ps3_scsih_sas_hw_req_frame_build( + cmd, PS3_PDID(&cmd->io_attr.pd_entry->disk_pos), + data_addr, sge_count); + if (likely(ret == PS3_SUCCESS)) { + lba = (unsigned long long)cmd->io_attr.lba_hi + << PS3_SHIFT_DWORD | + cmd->io_attr.lba_lo; + ret = ps3_software_zone_build( + cmd, lba, cmd->io_attr.pd_entry->dev_type, + sge_count); + } + } else { + ret = ps3_scsih_pd_frontend_req_build(cmd); + } + return ret; +} + +static void ps3_hw_nvme_req_frame_build(struct ps3_cmd *cmd, + struct PS3HwReqFrame *hw_reqframe) +{ + struct PS3NvmeRWCmd *rwReqFrame = &hw_reqframe->nvmeReqFrame.rwReqFrame; + + rwReqFrame->numLba = cpu_to_le32(cmd->io_attr.num_blocks - 1); + rwReqFrame->numLba |= + (unsigned int)((unsigned int)cmd->io_attr.cdb_opts.fua << 30); + + rwReqFrame->sLbaHi = cpu_to_le32( + (unsigned int)(cmd->io_attr.plba >> PS3_SHIFT_DWORD)); + rwReqFrame->sLbaLo = cpu_to_le32((unsigned int)(cmd->io_attr.plba)); + + rwReqFrame->cDW0_9.cID = cpu_to_le16(cmd->index); + rwReqFrame->cDW0_9.psdt = 0; + rwReqFrame->cDW0_9.nsID = 1; + if (cmd->io_attr.rw_flag == PS3_SCSI_CMD_TYPE_READ) + rwReqFrame->cDW0_9.opcode = 0x02; + else if (cmd->io_attr.rw_flag == PS3_SCSI_CMD_TYPE_WRITE) + rwReqFrame->cDW0_9.opcode = 0x01; + + LOG_DEBUG("hno:%u cid:%d, op:%d, nsid:%d, prp1:0x%llx,\n" + "\tprp2:0x%llx, numLb:%d, LbaHi:0x%x, LbaLo:0x%x, dw13:0x%x,\n" + "\tdw13:0x%x, dw13:0x%x\n", + PS3_HOST(cmd->instance), rwReqFrame->cDW0_9.cID, + rwReqFrame->cDW0_9.opcode, rwReqFrame->cDW0_9.nsID, + rwReqFrame->cDW0_9.dPtr.prp.prp1, + rwReqFrame->cDW0_9.dPtr.prp.prp2, rwReqFrame->numLba, + rwReqFrame->sLbaHi, rwReqFrame->sLbaLo, rwReqFrame->cDW13, + rwReqFrame->cDW14, rwReqFrame->cDW15); + +} + +static void ps3_hw_nvme_ext_fill(struct ps3_cmd *cmd, unsigned short prp_count) +{ + if (prp_count > PS3_FRAME_REQ_PRP_NUM_HW) { + cmd->req_frame->hwReq.sgl[PS3_FRAME_REQ_SGE_NUM_HW - 1].length = + (prp_count - PS3_FRAME_REQ_PRP_NUM_HW) * + sizeof(unsigned long long); + cmd->req_frame->hwReq.sgl[PS3_FRAME_REQ_SGE_NUM_HW - 1].addr = + cmd->ext_buf_phys; + cmd->req_frame->hwReq.sgl[PS3_FRAME_REQ_SGE_NUM_HW - 1].lastSge = + 0; + cmd->req_frame->hwReq.sgl[PS3_FRAME_REQ_SGE_NUM_HW - 1].ext = 1; + } else { + if (cmd->req_frame->hwReq.sgl[PS3_FRAME_REQ_SGE_NUM_HW - 1] + .ext != 0) { + cmd->req_frame->hwReq.sgl[PS3_FRAME_REQ_SGE_NUM_HW - 1] + .ext = 0; + } + } +} + +static int ps3_scsih_nvme_hw_req_frame_build(struct ps3_cmd *cmd) +{ + int ret = PS3_SUCCESS; + unsigned long long *prp_ptr = NULL; + struct PS3HwReqFrame *hw_reqframe = NULL; + unsigned char pd_dev_type = PS3_DEV_TYPE_COUNT; + unsigned long long lba = 0; + unsigned short prp_count = 0; + unsigned int data_len = + ps3_scsih_data_buf_len(cmd, cmd->os_sge_map_count); + static unsigned long j; + + hw_reqframe = &cmd->req_frame->hwReq; + memset(&hw_reqframe->nvmeReqFrame, 0, sizeof(union PS3NvmeReqFrame)); + prp_ptr = &hw_reqframe->nvmeReqFrame.rwReqFrame.cDW0_9.dPtr.prp.prp1; + + ps3_scsih_req_frame_head_build(cmd, PS3_REQFRAME_FORMAT_NVME); + if (cmd->io_attr.pd_entry != NULL) + pd_dev_type = cmd->io_attr.pd_entry->dev_type; + + if (ps3_prp_build_check(cmd, data_len)) { + prp_count = ps3_scsih_prp_build(cmd, prp_ptr, + PS3_FRAME_REQ_PRP_NUM_HW, + data_len, PS3_FALSE); + ps3_hw_nvme_ext_fill(cmd, prp_count); + ps3_hw_nvme_req_frame_build(cmd, hw_reqframe); + cmd->req_frame->hwReq.reqHead.traceID = cmd->trace_id; + lba = (unsigned long long)cmd->io_attr.lba_hi + << PS3_SHIFT_DWORD | + cmd->io_attr.lba_lo; + ret = ps3_software_zone_build(cmd, lba, pd_dev_type, prp_count); + } else { + LOG_ERROR_TIME_LIM( + &j, PS3_LOG_LIMIT_INTERVAL_MSEC, + "tid:0x%llx hno:%u this io don't support prp!\n", + cmd->trace_id, PS3_HOST(cmd->instance)); + ret = -PS3_FAILED; + } + return ret; +} + +static int ps3_scsih_nvme_req_frame_build(struct ps3_cmd *cmd) +{ + int ret = PS3_SUCCESS; + static unsigned long j; + + if (ps3_is_need_build_hw_req_frame(cmd)) { + ret = ps3_scsih_nvme_hw_req_frame_build(cmd); + if (ret != PS3_SUCCESS) { + LOG_WARN_TIME_LIM( + &j, PS3_LOG_LIMIT_INTERVAL_MSEC, + "tid:0x%llx hno:%u direct NVMe NOK, to normal\n" + "\tis_use_frontend_prp:%d\n", + cmd->trace_id, PS3_HOST(cmd->instance), + cmd->io_attr.is_use_frontend_prp); + cmd->io_attr.direct_flag = PS3_CMDWORD_DIRECT_NORMAL; + cmd->io_attr.is_use_frontend_prp = PS3_FALSE; + memset((void *)&cmd->cmd_word, 0, + sizeof(struct PS3CmdWord)); + memset((void *)cmd->req_frame, 0, + sizeof(union PS3ReqFrame)); + memset(cmd->ext_buf, 0, + cmd->instance->cmd_context.ext_buf_size); + ret = ps3_scsih_pd_cmd_word_build(cmd); + if (unlikely(ret != PS3_SUCCESS)) { + LOG_ERROR_TIME_LIM( + &j, PS3_LOG_LIMIT_INTERVAL_MSEC, + "tid:0x%llx hno:%u cmd_word build NOK CFID:%d ret:%d\n", + cmd->trace_id, PS3_HOST(cmd->instance), + cmd->index, ret); + ret = -PS3_FAILED; + goto l_out; + } + ret = ps3_scsih_pd_frontend_req_build(cmd); + } + } else { + ret = ps3_scsih_pd_frontend_req_build(cmd); + } + +l_out: + return ret; +} + +static inline void ps3_scsih_cmd_back_stat(struct ps3_cmd *cmd, + unsigned char reply_flags) +{ + PS3_IOC_DRV2IOC_BACK_INC(cmd->instance, cmd, reply_flags); + PS3_DEV_IO_OUTSTAND_DEC(cmd->instance, cmd); + PS3_DEV_IO_BACK_INC(cmd->instance, cmd, reply_flags); +#ifdef _WINDOWS + PS3_IO_BACK_INC(cmd->instance, cmd->scmd, cmd->index, reply_flags); +#else + PS3_IO_BACK_INC(cmd->instance, cmd->scmd, reply_flags); + PS3_DEV_BUSY_DEC(cmd->scmd); +#endif +} + +static inline void ps3_scsih_overrun_underrun_verify(struct ps3_cmd *cmd) +{ + if ((ps3_scsih_is_sas_jbod_cmd(cmd)) || + (ps3_scsih_is_sata_jbod_cmd(cmd) && + ps3_scsih_cdb_is_rw_cmd(cmd->scmd->cmnd))) { + if (cmd->io_attr.sgl_buf_len > scsi_bufflen(cmd->scmd)) { + scsi_set_resid(cmd->scmd, 0); + cmd->scmd->result = + PS3_SCSI_RESULT_HOST_STATUS(DID_SOFT_ERROR); + LOG_DEBUG( + "cmd overrun, CFID:%u op:0x%x data_len:%u scsi_buf_len:%u\n", + cmd->index, cmd->scmd->cmnd[0], + cmd->io_attr.sgl_buf_len, + scsi_bufflen(cmd->scmd)); + } + } + +} + +static inline void ps3_r1x_read_dec(struct ps3_cmd *cmd, + struct ps3_r1x_read_balance_info *rb_info) +{ + if (rb_info != NULL && cmd->r1x_read_pd > 0) { + if (cmd->r1x_read_pd > PS3_MAX_PD_NUM_ONE_VD) { + PS3_BUG(); + return; + } + ps3_atomic_dec( + &rb_info->scsi_outstanding_cmds[cmd->r1x_read_pd]); + } +} + +int ps3_scsih_io_done(struct ps3_cmd *cmd, unsigned short reply_flags) +{ + int ret = PS3_SUCCESS; + struct scsi_cmnd *s_cmd = cmd->scmd; + struct ps3_scsi_priv_data *data = NULL; + struct ps3_cmd *err_cmd = cmd; + struct ps3_cmd *peer_cmd = NULL; + + if (unlikely(s_cmd == NULL)) { + LOG_ERROR_IN_IRQ(cmd->instance, "hno:%u CFID:%d scmd is null\n", + PS3_HOST(cmd->instance), cmd->index); + goto l_out; + } + + if (cmd->r1x_peer_cmd != NULL) { + LOG_DEBUG( + "hno:%u CFID:%d one of the r1x write scmd is return, rflag:%d\n", + PS3_HOST(cmd->instance), cmd->index, reply_flags); + cmd->r1x_reply_flag = reply_flags; + cmd->is_r1x_scsi_complete = PS3_TRUE; + if (!cmd->r1x_peer_cmd->is_r1x_scsi_complete) { + PS3_IOC_DRV2IOC_BACK_INC( + cmd->instance, cmd->r1x_peer_cmd, reply_flags); + LOG_DEBUG( + "hno:%u CFID:%d r1x write peer cmd:%d is not return\n", + PS3_HOST(cmd->instance), cmd->index, + cmd->r1x_peer_cmd->index); + goto l_out; + } + + if (cmd->r1x_peer_cmd->r1x_reply_flag != + PS3_REPLY_WORD_FLAG_SUCCESS) { + err_cmd = cmd->r1x_peer_cmd; + reply_flags = cmd->r1x_peer_cmd->r1x_reply_flag; + } + + if (cmd->index >= + (unsigned int)cmd->instance->cmd_attr.cur_can_que) + cmd = cmd->r1x_peer_cmd; + } + + data = scsi_device_private_data(s_cmd); + LOG_DEBUG( + "tid:0x%llx hno:%u scsi cmd reply cb CFID:%u rep_f:%u\n" + "\top:0x%x chl:%u id:%u data_len:%u scsi_buflen:%u dev_type:%s\n", + cmd->trace_id, PS3_HOST(cmd->instance), cmd->index, reply_flags, + s_cmd->cmnd[0], PS3_SDEV_CHANNEL(cmd->scmd->device), + PS3_SDEV_TARGET(cmd->scmd->device), cmd->io_attr.sgl_buf_len, + scsi_bufflen(cmd->scmd), + namePS3DevType((enum PS3DevType)cmd->io_attr.dev_type)); + + ps3_scsih_cmd_back_stat(cmd, reply_flags); + PS3_IO_TRACE(cmd, PS3_IO_TRACE_DIRECT_RECV); + + if (unlikely(reply_flags != PS3_REPLY_WORD_FLAG_SUCCESS)) { + PS3_DEV_IO_ERR_STAT_INC(cmd->instance, cmd); + ret = ps3_err_scsi_cmd_fault_proc(cmd->instance, err_cmd); + } else { + ps3_scsih_overrun_underrun_verify(cmd); + if (cmd->scmd->result == DID_OK) { + ps3_qos_adjust_pd_rsc(s_cmd->device, cmd->instance, + PS3_QOS_QUOTA_ADJUST_UP); + } + } + + ps3_r1x_read_dec(cmd, data->r1x_rb_info); + +#if defined(PS3_SUPPORT_CMD_SCP) + s_cmd->SCp.ptr = NULL; +#endif + ps3_scsi_dma_unmap(cmd); + + ps3_qos_cmd_update(cmd->instance, cmd); + + ps3_r1x_write_unlock(&data->lock_mgr, cmd); + peer_cmd = cmd->r1x_peer_cmd; + PS3_IO_OUTSTAND_DEC(cmd->instance, s_cmd); + PS3_VD_OUTSTAND_DEC(cmd->instance, s_cmd); + ps3_scsi_cmd_free(cmd); + if (peer_cmd != NULL) { + LOG_DEBUG( + "host_no:%u r1x scsi write done CFID:%d and CFID:%d\n", + PS3_HOST(peer_cmd->instance), + peer_cmd->r1x_peer_cmd->index, peer_cmd->index); + ps3_r1x_peer_cmd_free_nolock(peer_cmd); + } + SCMD_IO_DONE(s_cmd); + +l_out: + return ret; +} + +void ps3_scsih_direct_to_normal_req_frame_rebuild(struct ps3_cmd *cmd) +{ + ps3_vd_adv_to_normal_req_frame_rebuild(cmd); +} + +#ifdef _WINDOWS +static unsigned char ps3_scsi_sge_remap_check(struct ps3_cmd *cmd, + struct scatterlist *os_sgl, + int index) +{ + unsigned char ret = PS3_FALSE; + struct scsi_cmnd *scp = cmd->scmd; + unsigned short os_sge_count = + (unsigned short)scp->scatterlist->NumberOfElements; + + if (index != 0 && index != os_sge_count - 1) + goto l_check; + + if (cmd->io_attr.dev_type == PS3_DEV_TYPE_VD) { + if (cmd->io_attr.vd_entry->isNvme) + goto l_nvme_check; + } else if (cmd->io_attr.dev_type == PS3_DEV_TYPE_NVME_SSD) { + goto l_nvme_check; + } else { + } + + goto l_check; +l_nvme_check: + if (index == 0) { + if (os_sgl->PhysicalAddress.QuadPart & + PS3_SCSI_ALINNMENT_MASK) { + ret = PS3_TRUE; + } + } else { + if (os_sgl->PhysicalAddress.QuadPart & + scp->dma_addr_alignment_mask) { + ret = PS3_TRUE; + } + } + + goto l_out; +l_check: + if ((os_sgl->PhysicalAddress.QuadPart & scp->dma_addr_alignment_mask) || + (os_sgl->Length & scp->dma_len_alignment_mask)) { + ret = PS3_TRUE; + } +l_out: + return ret; +} + +static void ps3_scsi_dma_algin_calc(struct ps3_cmd *cmd) +{ + unsigned int dma_addr_alignment = 0; + unsigned int dma_len_alignment = 0; + struct scsi_cmnd *scp = cmd->scmd; + + if ((cmd->io_attr.dev_type == PS3_DEV_TYPE_VD) && + (ps3_scsih_is_use_hard_cmd(cmd) != PS3_CMDWORD_FORMAT_HARDWARE)) { + dma_addr_alignment = cmd->io_attr.vd_entry->dmaAddrAlignShift; + dma_len_alignment = cmd->io_attr.vd_entry->dmaLenAlignShift; + dma_addr_alignment = + dma_addr_alignment ? 1 << dma_addr_alignment : 0; + dma_len_alignment = + dma_len_alignment ? 1 << dma_len_alignment : 0; + } else { + dma_addr_alignment = cmd->io_attr.pd_entry->dma_addr_alignment; + dma_len_alignment = cmd->io_attr.pd_entry->dma_len_alignment; + } + + scp->dma_addr_alignment_mask = PS3_SCSI_ALINNMENT_MASK; + scp->dma_len_alignment_mask = 0; + if (dma_addr_alignment) + scp->dma_addr_alignment_mask = dma_addr_alignment - 1; + + if (dma_len_alignment) + scp->dma_len_alignment_mask = dma_len_alignment - 1; + + if (((cmd->io_attr.dev_type == PS3_DEV_TYPE_VD) && + (cmd->io_attr.vd_entry->isNvme)) || + (cmd->io_attr.dev_type == PS3_DEV_TYPE_NVME_SSD)) { + scp->dma_addr_alignment_mask = PS3_SCSI_4K_ALINNMENT_MASK; + scp->dma_len_alignment_mask = PS3_SCSI_4K_ALINNMENT_MASK; + } + + LOG_DEBUG( + "tid:0x%llx hno:%u align[addr:%d,len:%d][addr_mask:%x,len_mask:%x]\n", + cmd->trace_id, PS3_HOST(cmd->instance), dma_addr_alignment, + dma_len_alignment, scp->dma_addr_alignment_mask, + scp->dma_len_alignment_mask); +} + +static void ps3_scsi_need_remap_check(struct ps3_cmd *cmd) +{ + int index = 0; + unsigned short os_sge_count = 0; + struct scsi_cmnd *scp = cmd->scmd; + struct scatterlist *os_sgl = NULL; + + scp->is_remap_databuff = PS3_FALSE; + + if (scp->scatterlist == NULL) + goto l_out; + + if (!ps3_scsih_cdb_is_rw_cmd(scp->cmnd) && + (cmd->io_attr.dev_type == PS3_DEV_TYPE_NVME_SSD)) { + goto l_out; + } + + ps3_scsi_dma_algin_calc(cmd); + + os_sge_count = (unsigned short)scp->scatterlist->NumberOfElements; + scsi_for_each_sg(scp, os_sgl, os_sge_count, index) { + if (os_sgl == NULL) + break; + + if (ps3_scsi_sge_remap_check(cmd, os_sgl, index)) { + scp->is_remap_databuff = PS3_TRUE; + break; + } + } +l_out: + if (scp->is_remap_databuff) { + LOG_DEBUG( + "tid:0x%llx hno:%u unalign[addr_mask:%x,len_mask:%x]\n" + "\tsgl addr:0x%llx len:%d or SAS sge num:%d > %d, index:%d\n", + cmd->trace_id, PS3_HOST(cmd->instance), + scp->dma_addr_alignment_mask, + scp->dma_len_alignment_mask, + os_sgl->PhysicalAddress.QuadPart, os_sgl->Length, + scp->scatterlist->NumberOfElements, + PS3_FRAME_REQ_SGE_NUM_HW, index); + } +} + +static int ps3_scsi_remap_sgl(struct ps3_cmd *cmd) +{ + int ret = -PS3_FAILED; + struct scsi_cmnd *scp = cmd->scmd; + void *srb_data_buff = SrbGetDataBuffer(cmd->srb); + + if (srb_data_buff == NULL) + goto l_out; + + scp->remap_databuff_length = scp->data_transfer_length; + if (scp->data_transfer_length & scp->dma_len_alignment_mask) { + scp->remap_databuff_length = + (scp->data_transfer_length + + scp->dma_len_alignment_mask + 1) & + (~(unsigned long long)scp->dma_len_alignment_mask); + } + + scp->remap_databuff = ps3_dma_alloc_coherent( + cmd->instance, scp->remap_databuff_length, + (unsigned long long *)&scp->remap_databuff_phy); + if (scp->remap_databuff == NULL) + goto l_out; + + cmd->os_sge_map_count = 1; + memset(&scp->remap_sgl, 0, sizeof(scp->remap_sgl)); + scp->scatterlist = (STOR_SCATTER_GATHER_LIST *)&scp->remap_sgl; + scp->scatterlist->NumberOfElements = 1; + scp->scatterlist->List[0].Length = scp->remap_databuff_length; + scp->scatterlist->List[0].PhysicalAddress.QuadPart = + (LONGLONG)scp->remap_databuff_phy; + RtlMoveMemory(scp->remap_databuff, srb_data_buff, + scp->data_transfer_length); + LOG_DEBUG("remap io,%p, data_len:%d, remap_len:%d, addr:0x%llx\n", + srb_data_buff, scp->data_transfer_length, + scp->remap_databuff_length, scp->remap_databuff_phy); + + ret = PS3_SUCCESS; +l_out: + return ret; +} + +static int ps3_scsi_unremap_sgl(struct ps3_cmd *cmd) +{ + int ret = -PS3_FAILED; + struct scsi_cmnd *scp = cmd->scmd; + void *srb_data_buff = SrbGetDataBuffer(cmd->srb); + + if (srb_data_buff == NULL) + goto l_out; + + if (scp->remap_databuff != NULL) { + RtlMoveMemory(srb_data_buff, scp->remap_databuff, + scp->data_transfer_length); + + LOG_DEBUG("unremap io,%p, len:%d\n", srb_data_buff, + scp->data_transfer_length); + + ps3_dma_free_coherent(cmd->instance, scp->remap_databuff_length, + scp->remap_databuff, + scp->remap_databuff_phy); + scp->remap_databuff = NULL; + scp->is_remap_databuff = PS3_FALSE; + cmd->os_sge_map_count = 0; + ret = PS3_SUCCESS; + } + +l_out: + return ret; +} +#endif + +int ps3_scsi_dma_map(struct ps3_cmd *cmd) +{ + int index = 0; + int os_sge_count = 0; + struct scsi_cmnd *scp = cmd->scmd; + struct scatterlist *os_sgl = NULL; + + os_sge_count = scsi_dma_map(scp); + if (unlikely(os_sge_count <= 0)) { + cmd->os_sge_map_count = 0; + goto l_out; + } + + scsi_for_each_sg(scp, os_sgl, os_sge_count, index) { + if (os_sgl == NULL) + break; + if (index == 0 && cmd->instance->page_mode_change && + (sg_dma_address_u64(os_sgl) & + cmd->instance->page_mode_addr_mask)) { + cmd->instance->page_mode_addr_mask = + PS3_PAGE_MODE_ABOVE_4_ADDR_MASK; + if (sg_dma_address_u64(os_sgl) & + PS3_PAGE_MODE_ABOVE_4_ADDR_MASK) { + cmd->instance->page_mode_change = PS3_FALSE; + } + dev_info( + &cmd->instance->pdev->dev, + "page mode change, addr:0x%llx\n", + (unsigned long long)sg_dma_address_u64(os_sgl)); + } + sg_dma_address(os_sgl) = PCIE_DMA_HOST_ADDR_BIT_POS_SET_NEW( + cmd->instance->dma_addr_bit_pos, + sg_dma_address_u64(os_sgl)); + } + + cmd->os_sge_map_count = os_sge_count; +l_out: + return os_sge_count; +} + +void ps3_scsi_dma_unmap(struct ps3_cmd *cmd) +{ + int index = 0; + unsigned short os_sge_count = 0; + struct scsi_cmnd *scp = NULL; + struct scatterlist *os_sgl = NULL; + + if (cmd == NULL) + goto l_out; + + os_sge_count = cmd->os_sge_map_count; + scp = cmd->scmd; + if (os_sge_count == 0) + goto l_out; + scsi_for_each_sg(scp, os_sgl, os_sge_count, index) { + if (os_sgl == NULL) + break; + sg_dma_address(os_sgl) = PCIE_DMA_HOST_ADDR_BIT_POS_CLEAR_NEW( + cmd->instance->dma_addr_bit_pos, + sg_dma_address_u64(os_sgl)); + } + if (scp->sdb.table.sgl) + scsi_dma_unmap(scp); + cmd->os_sge_map_count = 0; + +l_out: + return; +} + +static int ps3_vd_access_policy_check(struct ps3_instance *instance, + unsigned char channel, unsigned short id, + struct scsi_cmnd *s_cmd) +{ + int ret = PS3_SUCCESS; + unsigned char opcode = 0; + unsigned short sub_opcode = 0; + struct PS3VDEntry *entry = + ps3_dev_mgr_lookup_vd_info(instance, channel, id); + static unsigned long j; + + if (unlikely(entry == NULL)) { +#ifndef _WINDOWS + s_cmd->result = PS3_SCSI_RESULT_HOST_STATUS(DID_BAD_TARGET); +#else + scsi_cmnd_hoststatus_set(s_cmd, DID_BAD_TARGET); +#endif + LOG_ERROR_TIME_LIM(&j, PS3_LOG_LIMIT_INTERVAL_MSEC, + "hno:%u chl:%u id:%u\n", PS3_HOST(instance), + channel, id); + ret = -PS3_ENODEV; + goto l_out; + } + + if (unlikely((entry->accessPolicy == VD_ACCESS_POLICY_BLOCK) && + ps3_scsih_cdb_is_rw_cmd(s_cmd->cmnd))) { + ps3_errcode_to_scsi_status(instance, s_cmd, + PS3_STATUS_ACCESS_BLOCK, NULL, 0, + NULL); + ps3_scsih_cdb_opcode_get(s_cmd->cmnd, &opcode, &sub_opcode); + + LOG_DEBUG("hno:%u chl:%u id:%u vd_access_policy:%u op:0x%x sub_op:0x%x block io\n", + PS3_HOST(instance), channel, id, entry->accessPolicy, + opcode, sub_opcode); + ret = -PS3_IO_BLOCK; + goto l_out; + } else if (unlikely((entry->accessPolicy == + VD_ACCESS_POLICY_READ_ONLY) && + ps3_scsih_cdb_is_write_cmd(s_cmd->cmnd))) { + ps3_errcode_to_scsi_status(instance, s_cmd, + PS3_STATUS_ACCESS_BLOCK, NULL, 0, + NULL); + ps3_scsih_cdb_opcode_get(s_cmd->cmnd, &opcode, &sub_opcode); + + LOG_DEBUG("hno:%u chl:%u id:%u vd_access_policy:%u op:0x%x sub_op:0x%x write io\n", + PS3_HOST(instance), channel, id, entry->accessPolicy, + opcode, sub_opcode); + ret = -PS3_IO_BLOCK; + goto l_out; + } +l_out: + return ret; +} + +int ps3_get_requeue_or_reset(void) +{ +#if defined(PS3_DID_REQUEUE) + return PS3_SCSI_RESULT_HOST_STATUS(DID_REQUEUE); +#else + return PS3_SCSI_RESULT_HOST_STATUS(DID_RESET); +#endif +} + +unsigned char ps3_write_direct_enable(struct ps3_cmd *cmd) +{ + unsigned char result = PS3_TRUE; + + if (ps3_scsih_is_write_cmd(cmd->io_attr.rw_flag) && + !cmd->io_attr.vd_entry->isWriteDirectEnable) { + result = PS3_FALSE; + } + + return result; +} + +unsigned char ps3_ssd_vd_qmask_calculate_hba(struct ps3_cmd *cmd) +{ + unsigned char ret = PS3_FALSE; + struct ps3_scsi_priv_data *device_priv_data = NULL; + + device_priv_data = PS3_SDEV_PRI_DATA(cmd->scmd->device); + if (device_priv_data != NULL) { + cmd->cmd_word.qMask = + 1 << (device_priv_data->qmask_count & + (cmd->instance->ctrl_info.vdQueueNum - 1)); + device_priv_data->qmask_count++; + ret = PS3_TRUE; + ; + } + + return ret; +} diff --git a/drivers/scsi/linkdata/ps3stor/ps3_scsih.h b/drivers/scsi/linkdata/ps3stor/ps3_scsih.h new file mode 100644 index 0000000000000000000000000000000000000000..3c816e7925dd7e363d4a5846a250664753bb0e70 --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/ps3_scsih.h @@ -0,0 +1,143 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _PS3_SCSIH_H_ +#define _PS3_SCSIH_H_ + +#ifndef _WINDOWS +#include +#include +#else +#include "ps3_cmd_adp.h" +#endif + +#include "ps3_htp_def.h" +#include "ps3_inner_data.h" +#include "ps3_kernel_version.h" + +#define PS3_HW_VD_MAX_IO_SIZE_1M (1ULL << 20) +#define PS3_PAGE_MODE_ABOVE_3_ADDR_MASK 0xFFFFFF8000000000ULL +#define PS3_PAGE_MODE_ABOVE_4_ADDR_MASK 0xFFFF000000000000ULL + +#define PS3_IS_R0J1(raidlevel) \ + ((raidlevel) == RAID0 || (raidlevel) == RAID1 || \ + (raidlevel) == RAID10 || (raidlevel) == RAID1E || \ + (raidlevel) == RAID00) + +#if defined(PS3_SCMD_GET_REQUEST) +#define SCMD_GET_REQUEST(scmd) scsi_cmd_to_rq(scmd) +#else +#define SCMD_GET_REQUEST(scmd) scmd->request +#endif + +#if defined(PS3_SCMD_IO_DONE) +#define SCMD_IO_DONE(scmd) scsi_done(scmd) +#else +#define SCMD_IO_DONE(scmd) scmd->scsi_done(scmd) +#endif + +#define PS3_IF_QUIT_STREAM_DIRECT_DETECT() \ + (ps3_direct_check_stream_query() == PS3_FALSE) + +struct disk_type_to_proc_func_table { + unsigned char type; + int (*func)(struct ps3_cmd *cmd); +}; +#define CMND_LEN16 (16) +#define FRAME_CMD_MASK_SHIFT (0x1) +#define FRAME_CMD_MASK_BITS (0x07) + +enum PS3_FRAME_CMD_TYPE { + SCSI_FRAME_CMD = 0, + SAS_FRAME_CMD = 1, + SATA_FRAME_CMD = 2, + NVME_FRAME_CMD = 3, + UNKNOWN_FRAME_CMD, +}; +enum PS3_RW_CMD_TYPE { + SCSI_RW_UNUSED_CMD = 0, + SCSI_RW_SEQ_CMD = 1, + SCSI_RW_RANDOM_CMD = 2, +}; + +struct scsi_cmd_parse_table { + unsigned char cmd_type; + unsigned char rw_attr; +}; + +static inline void ps3_put_unaligned_be64(unsigned char *p, unsigned int val_hi, + unsigned int val_lo) +{ + p[0] = (unsigned char)(val_hi >> PS3_SHIFT_3BYTE) & 0xff; + p[1] = (unsigned char)(val_hi >> PS3_SHIFT_WORD) & 0xff; + p[2] = (unsigned char)(val_hi >> PS3_SHIFT_BYTE) & 0xff; + p[3] = (unsigned char)val_hi & 0xff; + + p[4] = (unsigned char)(val_lo >> PS3_SHIFT_3BYTE) & 0xff; + p[5] = (unsigned char)(val_lo >> PS3_SHIFT_WORD) & 0xff; + p[6] = (unsigned char)(val_lo >> PS3_SHIFT_BYTE) & 0xff; + p[7] = (unsigned char)val_lo & 0xff; +} + +static inline void ps3_put_unaligned_be32(unsigned char *p, unsigned int val) +{ + p[0] = (unsigned char)(val >> PS3_SHIFT_3BYTE) & 0xff; + p[1] = (unsigned char)(val >> PS3_SHIFT_WORD) & 0xff; + p[2] = (unsigned char)(val >> PS3_SHIFT_BYTE) & 0xff; + p[3] = (unsigned char)val & 0xff; +} + +static inline void ps3_put_unaligned_be16(unsigned char *p, unsigned short val) +{ + p[0] = (unsigned char)(val >> PS3_SHIFT_BYTE) & 0xff; + p[1] = (unsigned char)val & 0xff; +} + +static inline unsigned short ps3_get_unaligned_be16(unsigned char *p) +{ + return (unsigned short)((p[0] << PS3_SHIFT_BYTE) | p[1]); +} + +#ifndef _WINDOWS + +int ps3_scsih_queue_command(struct Scsi_Host *s_host, struct scsi_cmnd *s_cmd); +#else +unsigned char ps3_scsih_sys_state_check(struct ps3_instance *instance, + int *host_status); +#endif + +int ps3_scsih_cmd_build(struct ps3_cmd *cmd); + +void ps3_scsih_direct_to_normal_req_frame_rebuild(struct ps3_cmd *cmd); + +int ps3_scsih_io_done(struct ps3_cmd *cmd, unsigned short reply_flags); + +void ps3_scsi_dma_unmap(struct ps3_cmd *cmd); + +int ps3_scsi_dma_map(struct ps3_cmd *cmd); + +unsigned char +ps3_scsih_sata_direct_is_support(struct ps3_cmd *cmd, + const struct ps3_pd_entry *pd_entry); + +unsigned char ps3_scsih_stream_is_detect(struct ps3_cmd *cmd); +unsigned char ps3_raid_scsih_stream_is_direct(const struct ps3_cmd *cmd); +unsigned char ps3_hba_scsih_stream_is_direct(const struct ps3_cmd *cmd); + +void ps3_scsih_print_req(struct ps3_cmd *cmd, unsigned char log_level); + +int ps3_get_requeue_or_reset(void); + +unsigned char ps3_scsih_sata_direct_is_need(struct ps3_cmd *cmd); + +unsigned char ps3_scsih_is_sata_jbod_mgr_cmd(const struct ps3_cmd *cmd); + +unsigned int ps3_scsih_xfer_cnt_get(const struct ps3_cmd *cmd); + +unsigned char ps3_is_r1x_write_cmd(const struct ps3_cmd *cmd); + +int ps3_vd_direct_req_frame_build(struct ps3_cmd *cmd); + +unsigned char ps3_write_direct_enable(struct ps3_cmd *cmd); + +unsigned char ps3_ssd_vd_qmask_calculate_hba(struct ps3_cmd *cmd); + +#endif diff --git a/drivers/scsi/linkdata/ps3stor/ps3_scsih_cmd_parse.c b/drivers/scsi/linkdata/ps3stor/ps3_scsih_cmd_parse.c new file mode 100644 index 0000000000000000000000000000000000000000..cd490339c027eab63aee31aa99b6c2cc456a0249 --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/ps3_scsih_cmd_parse.c @@ -0,0 +1,863 @@ +// SPDX-License-Identifier: GPL-2.0 +#ifndef _WINDOWS +#include +#include "ps3_scsih.h" +#include "ps3_module_para.h" +#endif + +#include "ps3_scsih_cmd_parse.h" +#include "ps3_err_def.h" +#include "ps3_inner_data.h" +#include "ps3_instance_manager.h" +#include "ps3_driver_log.h" +#include "ps3_kernel_version.h" + +#define PS3_WRITE_VERIFY_16 (0x8e) +#define PS3_WRITE_VERIFY_32 (0x0C) +#define ORWRITE_16 (0x8B) +#define ORWRITE_32 (0x0E) +#define PRE_FETCH_16 (0x90) +#define READ_BUFFER_16 (0X9B) +#define REPORT_REFERRALS (0x9E) +#define SANITIZE (0x48) +#define WRITE_ATOMIC_16 (0x9C) +#define WRITE_ATOMIC_32 (0x0F) +#define WRITE_SCATTERED_16 (0x12) +#define WRITE_SCATTERED_32 (0x0011) +#define WRITE_STREAM_16 (0x9A) +#define WRITE_STREAM_32 (0x10) +#define WRITE_LONG_16 (0x11) + +unsigned char ps3_scsih_is_rw_type(unsigned char type) +{ + unsigned char is_rw_type = PS3_FALSE; + + switch (type) { + case PS3_SCSI_CMD_TYPE_READ: + case PS3_SCSI_CMD_TYPE_WRITE: + case PS3_SCSI_CMD_TYPE_UNMAP: + case PS3_SCSI_CMD_TYPE_RW: + is_rw_type = PS3_TRUE; + break; + default: + is_rw_type = PS3_FALSE; + break; + } + + return is_rw_type; +} + +unsigned char ps3_scsih_rw_cmd_is_need_split_hba(struct ps3_cmd *cmd) +{ + unsigned char ret = PS3_FALSE; + (void)cmd; + + return ret; +} + +unsigned char ps3_scsih_rw_cmd_is_need_split_raid(struct ps3_cmd *cmd) +{ + unsigned char is_need_split = PS3_FALSE; + unsigned int num_blocks = 0; + unsigned int lba_lo = 0; + unsigned int lba_hi = 0; + + + ps3_scsih_cdb_parse(cmd->scmd->cmnd, &num_blocks, &lba_lo, &lba_hi, + &is_need_split); + + return is_need_split; +} + +static unsigned char +ps3_scsih_service_action32_rw_type_get(const unsigned char *cdb) +{ + enum ps3_scsi_cmd_type rw_type = PS3_SCSI_CMD_TYPE_UNKNOWN; + unsigned short cmd_type = PS3_SERVICE_ACTION32(cdb); + + switch (cmd_type) { + case READ_32: + rw_type = PS3_SCSI_CMD_TYPE_READ; + break; + + case WRITE_32: + case PS3_WRITE_VERIFY_32: + case ORWRITE_32: + case WRITE_ATOMIC_32: + rw_type = (enum ps3_scsi_cmd_type)( + (unsigned char)PS3_SCSI_CMD_TYPE_WRITE | + PS3_SCSI_CONFLICT_CHECK); + break; + case VERIFY_32: + case WRITE_SAME_32: + case WRITE_STREAM_32: + case WRITE_SCATTERED_32: + rw_type = PS3_SCSI_CMD_TYPE_WRITE; + break; + + default: + rw_type = PS3_SCSI_CMD_TYPE_NORW; + break; + } + + return (unsigned char)rw_type; +} + +static inline unsigned char +ps3_service_action16_rw_type_get(const unsigned char *cdb) +{ + enum ps3_scsi_cmd_type rw_type = PS3_SCSI_CMD_TYPE_UNKNOWN; + unsigned char cmd_type = cdb[1] & 0x1f; + + switch (cmd_type) { + case WRITE_LONG_16: + case WRITE_SCATTERED_16: + rw_type = PS3_SCSI_CMD_TYPE_WRITE; + break; + default: + rw_type = PS3_SCSI_CMD_TYPE_NORW; + break; + } + + return (unsigned char)rw_type; +} + +static inline void +ps3_scsih_cdb_options_get(const unsigned char *cdb, + union ps3_scsi_cdb_option *cdb_opts) +{ + union ps3_scsi_cdb_option *pRead = (union ps3_scsi_cdb_option *)(cdb); + + cdb_opts->fua = pRead->fua; + cdb_opts->protect = pRead->protect; + cdb_opts->dpo = pRead->dpo; +} + +int ps3_scsih_cdb_opts_parse(struct ps3_cmd *cmd) +{ + union ps3_scsi_cdb_option *cdb_opts = &cmd->io_attr.cdb_opts; +#ifndef _WINDOWS + const unsigned char *cdb = cmd->scmd->cmnd; +#else + const unsigned char *cdb = scsi_cmnd_cdb(cmd->scmd); +#endif + + unsigned short sub_cmd_type = 0; + int ret = PS3_SUCCESS; + + switch (cdb[0]) { + case READ_10: + case READ_12: + case READ_16: + case WRITE_10: + case WRITE_12: + case WRITE_16: + ps3_scsih_cdb_options_get(&cdb[1], cdb_opts); + break; + case VARIABLE_LENGTH_CMD: + sub_cmd_type = PS3_SERVICE_ACTION32(cdb); + switch (sub_cmd_type) { + case READ_32: + case WRITE_32: + ps3_scsih_cdb_options_get(&cdb[10], cdb_opts); + break; + default: + ret = -PS3_FAILED; + break; + } + break; + case WRITE_6: + cdb_opts->option = 0; + break; + case READ_6: + cdb_opts->option = 0; + break; + default: + ret = -PS3_FAILED; + break; + } + + return ret; +} + +unsigned char ps3_scsih_is_protocal_rw(const unsigned char *cdb) +{ + unsigned char ret = PS3_DRV_FALSE; + unsigned short sub_cmd_type = 0; + + switch (cdb[0]) { + case READ_6: + case READ_10: + case READ_12: + case READ_16: + case WRITE_6: + case WRITE_10: + case WRITE_12: + case WRITE_16: + ret = PS3_DRV_TRUE; + break; + case VARIABLE_LENGTH_CMD: + sub_cmd_type = PS3_SERVICE_ACTION32(cdb); + switch (sub_cmd_type) { + case READ_32: + case WRITE_32: + ret = PS3_DRV_TRUE; + break; + default: + break; + } + break; + default: + break; + } + + return ret; +} + +unsigned char ps3_scsih_cdb_rw_type_get(const unsigned char *cdb) +{ + unsigned char rw_type = (unsigned char)PS3_SCSI_CMD_TYPE_UNKNOWN; + + switch (cdb[0]) { + case READ_6: + case READ_10: + case READ_12: + case READ_16: + case PRE_FETCH: + case PRE_FETCH_16: + rw_type = (unsigned char)PS3_SCSI_CMD_TYPE_READ; + break; + + case WRITE_6: + case WRITE_10: + case WRITE_12: + case WRITE_16: + case WRITE_VERIFY: + case WRITE_VERIFY_12: + case ORWRITE_16: + case WRITE_ATOMIC_16: + case PS3_WRITE_VERIFY_16: + rw_type = (unsigned char)PS3_SCSI_CMD_TYPE_WRITE | + PS3_SCSI_CONFLICT_CHECK; + break; + case VERIFY: + case WRITE_SAME: + case VERIFY_12: + case VERIFY_16: + case WRITE_SAME_16: + case SYNCHRONIZE_CACHE: + case SYNCHRONIZE_CACHE_16: + case WRITE_STREAM_16: + case WRITE_LONG: + rw_type = (unsigned char)PS3_SCSI_CMD_TYPE_WRITE; + break; + + case VARIABLE_LENGTH_CMD: + rw_type = ps3_scsih_service_action32_rw_type_get(cdb); + break; + case UNMAP: + rw_type = (unsigned char)PS3_SCSI_CMD_TYPE_UNMAP | + PS3_SCSI_CONFLICT_CHECK; + break; + + case COMPARE_AND_WRITE: + rw_type = (unsigned char)PS3_SCSI_CMD_TYPE_RW | + PS3_SCSI_CONFLICT_CHECK; + break; + + case SERVICE_ACTION_OUT_16: + rw_type = ps3_service_action16_rw_type_get(cdb); + break; + + default: + rw_type = (unsigned char)PS3_SCSI_CMD_TYPE_NORW; + break; + } + + return rw_type; +} + +static inline void ps3_scsih_cdb_rw6_rebuild(unsigned char *cdb, + unsigned int num_blocks, + unsigned int lba_lo) +{ + cdb[1] &= ~(0x1f); + cdb[1] |= (unsigned char)(lba_lo >> PS3_SHIFT_WORD) & 0x1f; + cdb[2] = (unsigned char)(lba_lo >> PS3_SHIFT_BYTE) & 0xff; + cdb[3] = (unsigned char)lba_lo & 0xff; + + cdb[4] = (num_blocks == 256) ? 0 : ((unsigned char)num_blocks & 0xff); +} + +static inline void ps3_scsih_cdb_rw10_rebuild(unsigned char *cdb, + unsigned int num_blocks, + unsigned int lba_lo) +{ + cdb[2] = (unsigned char)(lba_lo >> PS3_SHIFT_3BYTE) & 0xff; + cdb[3] = (unsigned char)(lba_lo >> PS3_SHIFT_WORD) & 0xff; + cdb[4] = (unsigned char)(lba_lo >> PS3_SHIFT_BYTE) & 0xff; + cdb[5] = (unsigned char)lba_lo & 0xff; + + cdb[7] = (unsigned char)(num_blocks >> PS3_SHIFT_BYTE) & 0xff; + cdb[8] = (unsigned char)num_blocks & 0xff; +} + +static inline void ps3_scsih_cdb_rw12_rebuild(unsigned char *cdb, + unsigned int num_blocks, + unsigned int lba_lo) +{ + cdb[2] = (unsigned char)(lba_lo >> PS3_SHIFT_3BYTE) & 0xff; + cdb[3] = (unsigned char)(lba_lo >> PS3_SHIFT_WORD) & 0xff; + cdb[4] = (unsigned char)(lba_lo >> PS3_SHIFT_BYTE) & 0xff; + cdb[5] = (unsigned char)lba_lo & 0xff; + + cdb[6] = (unsigned char)(num_blocks >> PS3_SHIFT_3BYTE) & 0xff; + cdb[7] = (unsigned char)(num_blocks >> PS3_SHIFT_WORD) & 0xff; + cdb[8] = (unsigned char)(num_blocks >> PS3_SHIFT_BYTE) & 0xff; + cdb[9] = (unsigned char)num_blocks & 0xff; +} + +static inline void ps3_scsih_cdb_rw16_rebuild(unsigned char *cdb, + unsigned int num_blocks, + unsigned int lba_lo, + unsigned int lba_hi) +{ + cdb[2] = (unsigned char)(lba_hi >> PS3_SHIFT_3BYTE) & 0xff; + cdb[3] = (unsigned char)(lba_hi >> PS3_SHIFT_WORD) & 0xff; + cdb[4] = (unsigned char)(lba_hi >> PS3_SHIFT_BYTE) & 0xff; + cdb[5] = (unsigned char)lba_hi & 0xff; + + cdb[6] = (unsigned char)(lba_lo >> PS3_SHIFT_3BYTE) & 0xff; + cdb[7] = (unsigned char)(lba_lo >> PS3_SHIFT_WORD) & 0xff; + cdb[8] = (unsigned char)(lba_lo >> PS3_SHIFT_BYTE) & 0xff; + cdb[9] = (unsigned char)lba_lo & 0xff; + + cdb[10] = (unsigned char)(num_blocks >> PS3_SHIFT_3BYTE) & 0xff; + cdb[11] = (unsigned char)(num_blocks >> PS3_SHIFT_WORD) & 0xff; + cdb[12] = (unsigned char)(num_blocks >> PS3_SHIFT_BYTE) & 0xff; + cdb[13] = (unsigned char)num_blocks & 0xff; +} + +static inline void ps3_scsih_cdb_rw32_rebuild(unsigned char *cdb, + unsigned int num_blocks, + unsigned int lba_lo, + unsigned int lba_hi) +{ + unsigned short cmd_type = PS3_SERVICE_ACTION32(cdb); + + LOG_DEBUG("[ps3]VARIABLE_LENGTH_CMD :0x%x!\n", cmd_type); + + switch (cmd_type) { + case READ_32: + case VERIFY_32: + case WRITE_32: + case PS3_WRITE_VERIFY_32: + case WRITE_SAME_32: + case ORWRITE_32: + case WRITE_ATOMIC_32: + case WRITE_STREAM_32: + cdb[12] = (unsigned char)(lba_hi >> PS3_SHIFT_3BYTE) & 0xff; + cdb[13] = (unsigned char)(lba_hi >> PS3_SHIFT_WORD) & 0xff; + cdb[14] = (unsigned char)(lba_hi >> PS3_SHIFT_BYTE) & 0xff; + cdb[15] = (unsigned char)lba_hi & 0xff; + + cdb[16] = (unsigned char)(lba_lo >> PS3_SHIFT_3BYTE) & 0xff; + cdb[17] = (unsigned char)(lba_lo >> PS3_SHIFT_WORD) & 0xff; + cdb[18] = (unsigned char)(lba_lo >> PS3_SHIFT_BYTE) & 0xff; + cdb[19] = (unsigned char)lba_lo & 0xff; + cdb[20] = (unsigned char)(lba_lo >> PS3_SHIFT_3BYTE) & 0xff; + cdb[21] = (unsigned char)(lba_lo >> PS3_SHIFT_WORD) & 0xff; + cdb[22] = (unsigned char)(lba_lo >> PS3_SHIFT_BYTE) & 0xff; + cdb[23] = (unsigned char)lba_lo & 0xff; + cdb[28] = (unsigned char)(num_blocks >> PS3_SHIFT_3BYTE) & 0xff; + cdb[29] = (unsigned char)(num_blocks >> PS3_SHIFT_WORD) & 0xff; + cdb[30] = (unsigned char)(num_blocks >> PS3_SHIFT_BYTE) & 0xff; + cdb[31] = (unsigned char)num_blocks & 0xff; + break; + default: + break; + } +} +static inline void ps3_scsih_cdb_write_long10_rebuild(unsigned char *cdb, + unsigned int lba_lo) +{ + cdb[2] = (unsigned char)(lba_lo >> PS3_SHIFT_3BYTE) & 0xff; + cdb[3] = (unsigned char)(lba_lo >> PS3_SHIFT_WORD) & 0xff; + cdb[4] = (unsigned char)(lba_lo >> PS3_SHIFT_BYTE) & 0xff; + cdb[5] = (unsigned char)lba_lo & 0xff; +} + +static inline void ps3_cdb_comp_and_write_rebuild(unsigned char *cdb, + unsigned int num_blocks, + unsigned int lba_lo, + unsigned int lba_hi) +{ + cdb[2] = (unsigned char)(lba_hi >> PS3_SHIFT_3BYTE) & 0xff; + cdb[3] = (unsigned char)(lba_hi >> PS3_SHIFT_WORD) & 0xff; + cdb[4] = (unsigned char)(lba_hi >> PS3_SHIFT_BYTE) & 0xff; + cdb[5] = (unsigned char)lba_hi & 0xff; + + cdb[6] = (unsigned char)(lba_lo >> PS3_SHIFT_3BYTE) & 0xff; + cdb[7] = (unsigned char)(lba_lo >> PS3_SHIFT_WORD) & 0xff; + cdb[8] = (unsigned char)(lba_lo >> PS3_SHIFT_BYTE) & 0xff; + cdb[9] = (unsigned char)lba_lo & 0xff; + + cdb[13] = (unsigned char)num_blocks & 0xff; +} + +static inline void ps3_scsih_service_16_rebuild(unsigned char *cdb, + unsigned int lba_lo, + unsigned int lba_hi) +{ + unsigned char cmd_type = cdb[1] & 0x1f; + + LOG_DEBUG("[ps3] CMD :0x%x!\n", cmd_type); + + if (cmd_type == WRITE_LONG_16) { + cdb[2] = (unsigned char)(lba_hi >> PS3_SHIFT_3BYTE) & 0xff; + cdb[3] = (unsigned char)(lba_hi >> PS3_SHIFT_WORD) & 0xff; + cdb[4] = (unsigned char)(lba_hi >> PS3_SHIFT_BYTE) & 0xff; + cdb[5] = (unsigned char)lba_hi & 0xff; + + cdb[6] = (unsigned char)(lba_lo >> PS3_SHIFT_3BYTE) & 0xff; + cdb[7] = (unsigned char)(lba_lo >> PS3_SHIFT_WORD) & 0xff; + cdb[8] = (unsigned char)(lba_lo >> PS3_SHIFT_BYTE) & 0xff; + cdb[9] = (unsigned char)lba_lo & 0xff; + } +} + +static inline void ps3_scsih_atomic_stream_16_rebuild(unsigned char *cdb, + unsigned int num_blocks, + unsigned int lba_lo, + unsigned int lba_hi) +{ + cdb[2] = (lba_hi >> PS3_SHIFT_3BYTE) & 0xff; + cdb[3] = (lba_hi >> PS3_SHIFT_WORD) & 0xff; + cdb[4] = (lba_hi >> PS3_SHIFT_BYTE) & 0xff; + cdb[5] = lba_hi & 0xff; + + cdb[6] = (lba_lo >> PS3_SHIFT_3BYTE) & 0xff; + cdb[7] = (lba_lo >> PS3_SHIFT_WORD) & 0xff; + cdb[8] = (lba_lo >> PS3_SHIFT_BYTE) & 0xff; + cdb[9] = lba_lo & 0xff; + + cdb[12] = (num_blocks >> PS3_SHIFT_BYTE) & 0xff; + cdb[13] = num_blocks & 0xff; +} + +static inline void ps3_scsih_cdb_rw6_parse(const unsigned char *cdb, + unsigned int *num_blocks, + unsigned int *lba_lo, + unsigned int *lba_hi) +{ + const unsigned int default_num_blocks = 256; + (void)lba_hi; + *lba_lo = (unsigned int)(((cdb[1] & 0x1f) << PS3_SHIFT_WORD) | + (cdb[2] << PS3_SHIFT_BYTE) | cdb[3]); + *num_blocks = ((unsigned int)cdb[4] == 0) ? default_num_blocks : + (unsigned int)cdb[4]; +} + +static inline void ps3_scsih_cdb_rw10_parse(const unsigned char *cdb, + unsigned int *num_blocks, + unsigned int *lba_lo, + unsigned int *lba_hi) +{ + (void)lba_hi; + *lba_lo = ((unsigned int)cdb[2] << PS3_SHIFT_3BYTE) | + ((unsigned int)cdb[3] << PS3_SHIFT_WORD) | + ((unsigned int)cdb[4] << PS3_SHIFT_BYTE) | + (unsigned int)cdb[5]; + *num_blocks = ((unsigned int)cdb[8] | + ((unsigned int)cdb[7] << PS3_SHIFT_BYTE)); + +} + +static inline void ps3_scsih_cdb_rw12_parse(const unsigned char *cdb, + unsigned int *num_blocks, + unsigned int *lba_lo, + unsigned int *lba_hi) +{ + (void)lba_hi; + *lba_lo = ((unsigned int)cdb[2] << PS3_SHIFT_3BYTE) | + ((unsigned int)cdb[3] << PS3_SHIFT_WORD) | + ((unsigned int)cdb[4] << PS3_SHIFT_BYTE) | + (unsigned int)cdb[5]; + *num_blocks = ((unsigned int)cdb[6] << PS3_SHIFT_3BYTE) | + ((unsigned int)cdb[7] << PS3_SHIFT_WORD) | + ((unsigned int)cdb[8] << PS3_SHIFT_BYTE) | + (unsigned int)cdb[9]; +} + +static inline void ps3_scsih_cdb_rw16_parse(const unsigned char *cdb, + unsigned int *num_blocks, + unsigned int *lba_lo, + unsigned int *lba_hi) +{ + *lba_lo = ((unsigned int)cdb[6] << PS3_SHIFT_3BYTE) | + ((unsigned int)cdb[7] << PS3_SHIFT_WORD) | + ((unsigned int)cdb[8] << PS3_SHIFT_BYTE) | + (unsigned int)cdb[9]; + *lba_hi = ((unsigned int)cdb[2] << PS3_SHIFT_3BYTE) | + ((unsigned int)cdb[3] << PS3_SHIFT_WORD) | + ((unsigned int)cdb[4] << PS3_SHIFT_BYTE) | + (unsigned int)cdb[5]; + *num_blocks = ((unsigned int)cdb[10] << PS3_SHIFT_3BYTE) | + ((unsigned int)cdb[11] << PS3_SHIFT_WORD) | + ((unsigned int)cdb[12] << PS3_SHIFT_BYTE) | + (unsigned int)cdb[13]; +} + +static inline void ps3_scsih_cdb_rw32_parse(const unsigned char *cdb, + unsigned int *num_blocks, + unsigned int *lba_lo, + unsigned int *lba_hi, + unsigned char *is_need_split) +{ + unsigned short cmd_type = PS3_SERVICE_ACTION32(cdb); + + LOG_DEBUG("[ps3]VARIABLE_LENGTH_CMD :0x%x!\n", cmd_type); + *is_need_split = PS3_FALSE; + + switch (cmd_type) { + case READ_32: + case WRITE_32: + case PS3_WRITE_VERIFY_32: + *is_need_split = PS3_TRUE; +#if defined(PS3_FALLTHROUGH) + fallthrough; +#endif + case VERIFY_32: + case WRITE_SAME_32: + case ORWRITE_32: + case WRITE_ATOMIC_32: + case WRITE_STREAM_32: + *lba_lo = ((unsigned int)cdb[16] << PS3_SHIFT_3BYTE) | + ((unsigned int)cdb[17] << PS3_SHIFT_WORD) | + ((unsigned int)cdb[18] << PS3_SHIFT_BYTE) | + (unsigned int)cdb[19]; + *lba_hi = ((unsigned int)cdb[12] << PS3_SHIFT_3BYTE) | + ((unsigned int)cdb[13] << PS3_SHIFT_WORD) | + ((unsigned int)cdb[14] << PS3_SHIFT_BYTE) | + (unsigned int)cdb[15]; + *num_blocks = ((unsigned int)cdb[28] << PS3_SHIFT_3BYTE) | + ((unsigned int)cdb[29] << PS3_SHIFT_WORD) | + ((unsigned int)cdb[30] << PS3_SHIFT_BYTE) | + (unsigned int)cdb[31]; + break; + + case WRITE_SCATTERED_32: + *lba_lo = 0; + *num_blocks = 0; + break; + + default: + break; + } + +} + +static inline void ps3_cdb_write_long10_parse(const unsigned char *cdb, + unsigned int *num_blocks, + unsigned int *lba_lo, + unsigned int *lba_hi) +{ + (void)lba_hi; + *lba_lo = ((unsigned int)cdb[2] << PS3_SHIFT_3BYTE) | + ((unsigned int)cdb[3] << PS3_SHIFT_WORD) | + ((unsigned int)cdb[4] << PS3_SHIFT_BYTE) | + (unsigned int)cdb[5]; + *num_blocks = 1; +} + +static inline void ps3_cdb_comp_and_write_parse(const unsigned char *cdb, + unsigned int *num_blocks, + unsigned int *lba_lo, + unsigned int *lba_hi) +{ + *lba_lo = ((unsigned int)cdb[6] << PS3_SHIFT_3BYTE) | + ((unsigned int)cdb[7] << PS3_SHIFT_WORD) | + ((unsigned int)cdb[8] << PS3_SHIFT_BYTE) | + (unsigned int)cdb[9]; + *lba_hi = ((unsigned int)cdb[2] << PS3_SHIFT_3BYTE) | + ((unsigned int)cdb[3] << PS3_SHIFT_WORD) | + ((unsigned int)cdb[4] << PS3_SHIFT_BYTE) | + (unsigned int)cdb[5]; + *num_blocks = (unsigned int)cdb[13]; +} + +static inline void ps3_scsih_service_16_parse(const unsigned char *cdb, + unsigned int *num_blocks, + unsigned int *lba_lo, + unsigned int *lba_hi) +{ + unsigned char cmd_type = cdb[1] & 0x1f; + + LOG_DEBUG("[ps3] CMD :0x%x!\n", cmd_type); + + switch (cmd_type) { + case WRITE_LONG_16: + *lba_lo = ((unsigned int)cdb[6] << PS3_SHIFT_3BYTE) | + ((unsigned int)cdb[7] << PS3_SHIFT_WORD) | + ((unsigned int)cdb[8] << PS3_SHIFT_BYTE) | + (unsigned int)cdb[9]; + *lba_hi = ((unsigned int)cdb[2] << PS3_SHIFT_3BYTE) | + ((unsigned int)cdb[3] << PS3_SHIFT_WORD) | + ((unsigned int)cdb[4] << PS3_SHIFT_BYTE) | + (unsigned int)cdb[5]; + *num_blocks = 1; + break; + + case WRITE_SCATTERED_16: + *lba_lo = 0; + *num_blocks = 0; + break; + + default: + break; + } + +} + +void ps3_scsih_cdb_parse(const unsigned char *cdb, unsigned int *num_blocks, + unsigned int *lba_lo, unsigned int *lba_hi, + unsigned char *is_need_split) +{ + *num_blocks = 0; + *lba_lo = 0; + *lba_hi = 0; + *is_need_split = PS3_FALSE; + + switch (cdb[0]) { + case READ_6: + case WRITE_6: + ps3_scsih_cdb_rw6_parse(cdb, num_blocks, lba_lo, lba_hi); + *is_need_split = PS3_TRUE; + break; + + case READ_10: + case WRITE_10: + case WRITE_VERIFY: + *is_need_split = PS3_TRUE; +#if defined(PS3_FALLTHROUGH) + fallthrough; +#endif + case VERIFY: + case WRITE_SAME: + case PRE_FETCH: + case SYNCHRONIZE_CACHE: + ps3_scsih_cdb_rw10_parse(cdb, num_blocks, lba_lo, lba_hi); + break; + + case READ_12: + case WRITE_12: + case WRITE_VERIFY_12: + *is_need_split = PS3_TRUE; +#if defined(PS3_FALLTHROUGH) + fallthrough; +#endif + case VERIFY_12: + ps3_scsih_cdb_rw12_parse(cdb, num_blocks, lba_lo, lba_hi); + break; + + case READ_16: + case WRITE_16: + case PS3_WRITE_VERIFY_16: + *is_need_split = PS3_TRUE; +#if defined(PS3_FALLTHROUGH) + fallthrough; +#endif + case VERIFY_16: + case WRITE_SAME_16: + case ORWRITE_16: + case PRE_FETCH_16: + case SYNCHRONIZE_CACHE_16: + ps3_scsih_cdb_rw16_parse(cdb, num_blocks, lba_lo, lba_hi); + break; + + case FORMAT_UNIT: + case UNMAP: + case SANITIZE: + break; + + case VARIABLE_LENGTH_CMD: + ps3_scsih_cdb_rw32_parse(cdb, num_blocks, lba_lo, lba_hi, + is_need_split); + break; + + case COMPARE_AND_WRITE: + ps3_cdb_comp_and_write_parse(cdb, num_blocks, lba_lo, lba_hi); + break; + + case WRITE_ATOMIC_16: + case WRITE_STREAM_16: + *lba_lo = ((unsigned int)cdb[6] << PS3_SHIFT_3BYTE) | + ((unsigned int)cdb[7] << PS3_SHIFT_WORD) | + ((unsigned int)cdb[8] << PS3_SHIFT_BYTE) | + (unsigned int)cdb[9]; + *lba_hi = ((unsigned int)cdb[2] << PS3_SHIFT_3BYTE) | + ((unsigned int)cdb[3] << PS3_SHIFT_WORD) | + ((unsigned int)cdb[4] << PS3_SHIFT_BYTE) | + (unsigned int)cdb[5]; + *num_blocks = ((unsigned int)cdb[12] << PS3_SHIFT_BYTE) | + (unsigned int)cdb[13]; + break; + + case WRITE_LONG: + ps3_cdb_write_long10_parse(cdb, num_blocks, lba_lo, lba_hi); + break; + + case SERVICE_ACTION_OUT_16: + ps3_scsih_service_16_parse(cdb, num_blocks, lba_lo, lba_hi); + break; + + default: + break; + } + +} +static inline void ps3_convert_to_cdb16(unsigned char *cdb, + unsigned short cdb_len, + unsigned int num_blocks, + unsigned int lba_lo, + unsigned int lba_hi) +{ + unsigned char opcode = 0; + unsigned char flagvals = 0; + unsigned char groupnum = 0; + unsigned char control = 0; + + switch (cdb_len) { + case 6: + opcode = cdb[0] == READ_6 ? READ_16 : WRITE_16; + control = cdb[5]; + break; + case 10: + opcode = cdb[0] == READ_10 ? READ_16 : WRITE_16; + flagvals = cdb[1]; + groupnum = cdb[6]; + control = cdb[9]; + break; + case 12: + opcode = cdb[0] == READ_12 ? READ_16 : WRITE_16; + flagvals = cdb[1]; + groupnum = cdb[10]; + control = cdb[11]; + break; + default: + break; + } + + cdb[0] = opcode; + cdb[1] = flagvals; + cdb[14] = groupnum; + cdb[15] = control; + cdb[9] = (unsigned char)(lba_lo & 0xff); + cdb[8] = (unsigned char)((lba_lo >> PS3_SHIFT_BYTE) & 0xff); + cdb[7] = (unsigned char)((lba_lo >> PS3_SHIFT_WORD) & 0xff); + cdb[6] = (unsigned char)((lba_lo >> PS3_SHIFT_3BYTE) & 0xff); + cdb[5] = (unsigned char)(lba_hi & 0xff); + cdb[4] = (unsigned char)((lba_hi >> PS3_SHIFT_BYTE) & 0xff); + cdb[3] = (unsigned char)((lba_hi >> PS3_SHIFT_WORD) & 0xff); + cdb[2] = (unsigned char)((lba_hi >> PS3_SHIFT_3BYTE) & 0xff); + cdb[13] = (unsigned char)(num_blocks & 0xff); + cdb[12] = (unsigned char)((num_blocks >> PS3_SHIFT_BYTE) & 0xff); + cdb[11] = (unsigned char)((num_blocks >> PS3_SHIFT_WORD) & 0xff); + cdb[10] = (unsigned char)((num_blocks >> PS3_SHIFT_3BYTE) & 0xff); +} + +void ps3_scsih_cdb_rebuild(unsigned char *cdb, unsigned short cdb_len, + unsigned int num_blocks, unsigned int lba_lo, + unsigned int lba_hi) +{ + if (unlikely((cdb_len < 16) && + (((unsigned long long)lba_hi << PS3_SHIFT_DWORD | lba_lo) > + 0xffffffff))) { + ps3_convert_to_cdb16(cdb, cdb_len, num_blocks, lba_lo, lba_hi); + goto l_out; + } + + switch (cdb[0]) { + case READ_6: + case WRITE_6: + ps3_scsih_cdb_rw6_rebuild(cdb, num_blocks, lba_lo); + break; + + case READ_10: + case WRITE_10: + case VERIFY: + case WRITE_VERIFY: + case WRITE_SAME: + case PRE_FETCH: + case SYNCHRONIZE_CACHE: + ps3_scsih_cdb_rw10_rebuild(cdb, num_blocks, lba_lo); + break; + + case READ_12: + case WRITE_12: + case VERIFY_12: + case WRITE_VERIFY_12: + ps3_scsih_cdb_rw12_rebuild(cdb, num_blocks, lba_lo); + break; + + case READ_16: + case WRITE_16: + case VERIFY_16: + case PS3_WRITE_VERIFY_16: + case WRITE_SAME_16: + case ORWRITE_16: + case PRE_FETCH_16: + case SYNCHRONIZE_CACHE_16: + ps3_scsih_cdb_rw16_rebuild(cdb, num_blocks, lba_lo, lba_hi); + break; + + case VARIABLE_LENGTH_CMD: + ps3_scsih_cdb_rw32_rebuild(cdb, num_blocks, lba_lo, lba_hi); + break; + + case COMPARE_AND_WRITE: + ps3_cdb_comp_and_write_rebuild(cdb, num_blocks, lba_lo, lba_hi); + break; + + case WRITE_ATOMIC_16: + case WRITE_STREAM_16: + ps3_scsih_atomic_stream_16_rebuild(cdb, num_blocks, lba_lo, + lba_hi); + break; + + case WRITE_LONG: + ps3_scsih_cdb_write_long10_rebuild(cdb, lba_lo); + break; + + case SERVICE_ACTION_OUT_16: + ps3_scsih_service_16_rebuild(cdb, lba_lo, lba_hi); + break; + + default: + break; + } +l_out: + return; +} + +void ps3_scsih_lba_parse(const unsigned char *cdb, unsigned long long *lba) +{ + unsigned int num_blocks = 0; + unsigned int lba_lo = 0; + unsigned int lba_hi = 0; + unsigned char is_need_split = PS3_FALSE; + + ps3_scsih_cdb_parse(cdb, &num_blocks, &lba_lo, &lba_hi, &is_need_split); + *lba = ((unsigned long long)lba_hi << PS3_SHIFT_DWORD) | lba_lo; +} + +void ps3_scsih_len_parse(const unsigned char *cdb, unsigned int *len) +{ + unsigned int num_blocks = 0; + unsigned int lba_lo = 0; + unsigned int lba_hi = 0; + unsigned char is_need_split = PS3_FALSE; + + ps3_scsih_cdb_parse(cdb, &num_blocks, &lba_lo, &lba_hi, &is_need_split); + *len = num_blocks; +} diff --git a/drivers/scsi/linkdata/ps3stor/ps3_scsih_cmd_parse.h b/drivers/scsi/linkdata/ps3stor/ps3_scsih_cmd_parse.h new file mode 100644 index 0000000000000000000000000000000000000000..fb0b7c68714e6a14179b1058676e3d441f5a8a5c --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/ps3_scsih_cmd_parse.h @@ -0,0 +1,195 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _PS3_SCSIH_CMD_PARSE_H_ +#define _PS3_SCSIH_CMD_PARSE_H_ + +#include "ps3_err_def.h" + +#include "ps3_inner_data.h" +#include "ps3_driver_log.h" + +#ifndef _WINDOWS +#include +#else + +#define VARIABLE_LENGTH_CMD 0x7f +#define SYNCHRONIZE_CACHE_16 0x91 +#define SYNCHRONIZE_CACHE 0x35 + +#define FORMAT_UNIT 0x04 +#define REASSIGN_BLOCKS 0x07 +#define READ_6 0x08 +#define WRITE_6 0x0a +#define READ_10 0x28 +#define WRITE_10 0x2a +#define WRITE_VERIFY 0x2e +#define VERIFY 0x2f +#define PRE_FETCH 0x34 +#define SYNCHRONIZE_CACHE 0x35 +#define WRITE_LONG 0x3f +#define WRITE_SAME 0x41 +#define UNMAP 0x42 +#define VARIABLE_LENGTH_CMD 0x7f +#define READ_12 0xa8 +#define WRITE_12 0xaa +#define WRITE_VERIFY_12 0xae +#define VERIFY_12 0xaf +#define EXTENDED_COPY 0x83 +#define READ_16 0x88 +#define COMPARE_AND_WRITE 0x89 +#define WRITE_16 0x8a +#define WRITE_ATTRIBUTE 0x8d +#define PS3_WRITE_VERIFY_16 0x8e +#define VERIFY_16 0x8f +#define SYNCHRONIZE_CACHE_16 0x91 +#define WRITE_SAME_16 0x93 +#define SERVICE_ACTION_IN_16 0x9e +#define SERVICE_ACTION_OUT_16 0x9f +#define READ_32 0x09 +#define VERIFY_32 0x0a +#define WRITE_32 0x0b +#define PS3_WRITE_VERIFY_32 0x0c +#define WRITE_SAME_32 0x0d + +#endif + +#define PS3_SCSI_CONFLICT_CHECK 0x80 +#define PS3_SCSI_CONFLICT_CHECK_TEST(type) ((type)&PS3_SCSI_CONFLICT_CHECK) +#define PS3_SCSI_CMD_TYPE(type) ((type)&0x7F) + +enum ps3_scsi_cmd_type { + PS3_SCSI_CMD_TYPE_UNKNOWN, + PS3_SCSI_CMD_TYPE_READ, + PS3_SCSI_CMD_TYPE_WRITE, + PS3_SCSI_CMD_TYPE_RW, + PS3_SCSI_CMD_TYPE_UNMAP, + PS3_SCSI_CMD_TYPE_NORW, + PS3_SCSI_CMD_TYPE_COUNT +}; + +enum { + PS3_ATA_OPC_READ_FPDMA = 0x60, + PS3_ATA_OPC_WRITE_FPDMA = 0x61, + PS3_SATA_DEV_REG_DEFAULT = 0x40, + PS3_NCQ_FUA_OFFSET_IN_DEVICE_REG = 7, +}; + +#define PS3_SERVICE_ACTION32(cdb) (*(cdb + 9) | (*(cdb + 8) << PS3_SHIFT_BYTE)) +#define PS3_SERVICE_ACTION16_TO_OPCODE(cdb) (*(cdb + 1) & 0x1f) + +static inline void ps3_scsih_cdb_opcode_get(const unsigned char *cdb, + unsigned char *opcode, + unsigned short *sub_opcode) +{ + *opcode = cdb[0]; + switch (cdb[0]) { + case VARIABLE_LENGTH_CMD: + *sub_opcode = PS3_SERVICE_ACTION32(cdb); + break; + case SERVICE_ACTION_IN_16: + case SERVICE_ACTION_OUT_16: + *sub_opcode = + (unsigned short)PS3_SERVICE_ACTION16_TO_OPCODE(cdb); + break; + default: + *sub_opcode = 0; + break; + } +} + +#ifndef U32_HIGH_LOW_TO_U64 +#define U32_HIGH_LOW_TO_U64(u64_high, u64_low) \ + (((unsigned long long)(u64_high) << 32) | (u64_low)) +#endif + +unsigned char ps3_scsih_is_rw_type(unsigned char type); + +unsigned char ps3_scsih_cdb_rw_type_get(const unsigned char *cdb); + +void ps3_scsih_cdb_parse(const unsigned char *cdb, unsigned int *num_blocks, + unsigned int *lba_lo, unsigned int *lba_hi, + unsigned char *is_need_split); + +static inline unsigned char ps3_scsih_cdb_is_rw_cmd(const unsigned char *cdb) +{ + unsigned char type = PS3_SCSI_CMD_TYPE(ps3_scsih_cdb_rw_type_get(cdb)); + + return ps3_scsih_is_rw_type(type); +} + +static inline unsigned char ps3_scsih_is_read_cmd(unsigned char type) +{ + return (type == PS3_SCSI_CMD_TYPE_READ); +} + +static inline unsigned char ps3_scsih_is_write_cmd(unsigned char type) +{ + unsigned char ret = PS3_FALSE; + + switch (type) { + case PS3_SCSI_CMD_TYPE_WRITE: + case PS3_SCSI_CMD_TYPE_UNMAP: + case PS3_SCSI_CMD_TYPE_RW: + ret = PS3_TRUE; + goto l_out; + default: + ret = PS3_FALSE; + goto l_out; + } +l_out: + return ret; +} +static inline unsigned char ps3_scsih_is_sync_cache(const unsigned char *cdb) +{ + return cdb[0] == SYNCHRONIZE_CACHE || cdb[0] == SYNCHRONIZE_CACHE_16; +} + +void ps3_scsih_lba_parse(const unsigned char *cdb, unsigned long long *lba); + +void ps3_scsih_len_parse(const unsigned char *cdb, unsigned int *len); + +int ps3_scsih_cdb_opts_parse(struct ps3_cmd *cmd); + +void ps3_scsih_cdb_rebuild(unsigned char *cdb, unsigned short cdb_len, + unsigned int num_blocks, unsigned int lba_lo, + unsigned int lba_hi); + +static inline unsigned char ps3_scsih_cdb_is_read_cmd(const unsigned char *cdb) +{ + unsigned char type = PS3_SCSI_CMD_TYPE(ps3_scsih_cdb_rw_type_get(cdb)); + + return ps3_scsih_is_read_cmd(type); +} + +static inline unsigned char ps3_scsih_cdb_is_write_cmd(const unsigned char *cdb) +{ + unsigned char type = PS3_SCSI_CMD_TYPE(ps3_scsih_cdb_rw_type_get(cdb)); + + return ps3_scsih_is_write_cmd(type); +} + +unsigned char ps3_scsih_is_protocal_rw(const unsigned char *cdb); + +unsigned char ps3_scsih_rw_cmd_is_need_split_hba(struct ps3_cmd *cmd); + +unsigned char ps3_scsih_rw_cmd_is_need_split_raid(struct ps3_cmd *cmd); + +static inline void ps3_scsih_unmap_desc_parse(const unsigned char *desc, + unsigned int *num_blocks, + unsigned int *lba_lo, + unsigned int *lba_hi) +{ + *lba_lo = ((unsigned int)desc[4] << PS3_SHIFT_3BYTE) | + ((unsigned int)desc[5] << PS3_SHIFT_WORD) | + ((unsigned int)desc[6] << PS3_SHIFT_BYTE) | + (unsigned int)desc[7]; + *lba_hi = ((unsigned int)desc[0] << PS3_SHIFT_3BYTE) | + ((unsigned int)desc[1] << PS3_SHIFT_WORD) | + ((unsigned int)desc[2] << PS3_SHIFT_BYTE) | + (unsigned int)desc[3]; + *num_blocks = ((unsigned int)desc[8] << PS3_SHIFT_3BYTE) | + ((unsigned int)desc[9] << PS3_SHIFT_WORD) | + ((unsigned int)desc[10] << PS3_SHIFT_BYTE) | + (unsigned int)desc[11]; +} + +#endif diff --git a/drivers/scsi/linkdata/ps3stor/ps3_scsih_raid_engine.c b/drivers/scsi/linkdata/ps3stor/ps3_scsih_raid_engine.c new file mode 100644 index 0000000000000000000000000000000000000000..949d153c1e0920c24006cc1fb72fed6336dd05b0 --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/ps3_scsih_raid_engine.c @@ -0,0 +1,1511 @@ +// SPDX-License-Identifier: GPL-2.0 +#ifndef _WINDOWS +#include +#include +#else +#include "ps3_def.h" +#endif + +#include "ps3_scsih_raid_engine.h" +#include "ps3_instance_manager.h" +#include "ps3_htp_meta.h" +#include "ps3_scsih_cmd_parse.h" +#include "ps3_driver_log.h" +#include "ps3_util.h" +#include "ps3_module_para.h" +#include "ps3_scsih.h" + +#define LBA_TO_STRIPE_INDEX(u64_lba, u32_stripe_data_size, u64_stripe_idx) \ + ((u64_stripe_idx) = (PS3_DIV64_32((u64_lba), (u32_stripe_data_size)))) + +#define LBA_TO_STRIPE_OFFSET(u64_lba, u32_stripe_data_size, u32_stripe_offset) \ + ((u32_stripe_offset) = (PS3_MOD64((u64_lba), (u32_stripe_data_size)))) + +#define SPANNO_DISKIDX_TO_PHYDISKID(vd_entry, span_idx, span_disk_idx) \ + ((vd_entry) \ + ->span[(span_idx)] \ + .extent[(span_disk_idx)] \ + .phyDiskID.ps3Dev.phyDiskID) + +#define VD_SPAN_PD_NUM(vd_entry, span_idx) \ + ((vd_entry)->span[(span_idx)].spanPdNum) + +#define STRIP_SIZE_MASK(strip_size) ((strip_size)-1) + +#define RAID660_GET_PQ_SPINDLENO(stripe_idx, phy_disk_count, q_disk_idx) \ + do { \ + if (ps3_is_power_of_2(phy_disk_count)) { \ + (q_disk_idx) = (stripe_idx) & ((phy_disk_count)-1); \ + } else { \ + (q_disk_idx) = \ + PS3_MOD64((stripe_idx), (phy_disk_count)); \ + } \ + (q_disk_idx) = (phy_disk_count) - (q_disk_idx)-1; \ + } while (0) + +#define PS3_R1X_HDD_MAX_SWAP_CNT_1 (64) +#define PS3_R1X_HDD_MAX_SWAP_CNT_2 (32) +#define PS3_R1X_SSD_MAX_SWAP_CNT_1 (32) +#define PS3_R1X_SSD_MAX_SWAP_CNT_2 (16) +#define PS3_R1X_SWAP_VD_MEMBER_CNT (8) +#define PS3_R1X_RB_DIFF_CMDS_DEFAULT (4) +#define ABS_DIFF(a, b) (((a) > (b)) ? ((a) - (b)) : ((b) - (a))) +#define PS3_R1X_RB_INFO_INDEX(span, span_disk_idx) \ + ((span) * PS3_MAX_PD_COUNT_IN_SPAN + (span_disk_idx) + 1) +unsigned int g_ps3_r1x_rb_diff_cmds = PS3_R1X_RB_DIFF_CMDS_DEFAULT; + +static inline unsigned char ps3_is_power_of_2(unsigned int n) +{ + return (unsigned char)(n != 0 && ((n & (n - 1)) == 0)); +} + +static inline unsigned char +ps3_vd_entry_valid_check(const struct PS3VDEntry *vd_entry) +{ + unsigned char ret = PS3_DRV_TRUE; + + if (unlikely(vd_entry == NULL)) { + ret = PS3_DRV_FALSE; + goto l_out; + } + + if (unlikely(!ps3_is_power_of_2(vd_entry->stripSize))) { + ret = PS3_DRV_FALSE; + goto l_out; + } + + if (unlikely(!vd_entry->stripeDataSize)) { + ret = PS3_DRV_FALSE; + goto l_out; + } +l_out: + return ret; +} + +unsigned char ps3_scsih_is_same_strip(const struct PS3VDEntry *vd_entry, + unsigned int vlba_lo, + unsigned int lba_length) +{ + if (unlikely(!ps3_vd_entry_valid_check(vd_entry))) + return PS3_DRV_FALSE; + + if ((vd_entry->stripSize - + (vlba_lo & STRIP_SIZE_MASK(vd_entry->stripSize))) >= lba_length) { + return PS3_DRV_TRUE; + } else { + return PS3_DRV_FALSE; + } +} + +static void ps3_r0_convert_vlba_to_pd_attr(struct ps3_cmd *cmd) +{ + unsigned int stripe_offset = 0; + unsigned long long stripe_idx = 0; + unsigned int strip_size_shift = 0; + unsigned int span_data_disk_idx = 0; + unsigned long long vlba = + U32_HIGH_LOW_TO_U64(cmd->io_attr.lba_hi, cmd->io_attr.lba_lo); + const struct PS3VDEntry *vd_entry = cmd->io_attr.vd_entry; + + LBA_TO_STRIPE_INDEX(vlba, vd_entry->stripeDataSize, stripe_idx); + LBA_TO_STRIPE_OFFSET(vlba, vd_entry->stripeDataSize, stripe_offset); + + strip_size_shift = ps3_blocksize_to_shift(vd_entry->stripSize); + span_data_disk_idx = stripe_offset >> strip_size_shift; + + cmd->io_attr.plba = + vd_entry->startLBA + (stripe_idx << strip_size_shift) + + (stripe_offset & STRIP_SIZE_MASK(vd_entry->stripSize)); + + cmd->io_attr.span_idx = 0; + cmd->io_attr.span_pd_idx = (unsigned char)span_data_disk_idx; +} + +static void ps3_r1x_rand_read_target_pd_calc(struct ps3_cmd *cmd, + unsigned int span_idx, + unsigned int span_pd_idx, + unsigned int span_pd_back_idx) +{ + struct ps3_r1x_read_balance_info *rb_info = NULL; + struct ps3_scsi_priv_data *priv_data = NULL; + unsigned long long vlba = 0; + unsigned int outstanding0 = 0; + unsigned int outstanding1 = 0; + unsigned long long diff0 = 0; + unsigned long long diff1 = 0; + unsigned int pd0 = 0; + unsigned int pd1 = 0; + unsigned int target_pd = 0; + + if (!cmd->r1x_read_pd) { + priv_data = scsi_device_private_data(cmd->scmd); + rb_info = priv_data->r1x_rb_info; + vlba = U32_HIGH_LOW_TO_U64(cmd->io_attr.lba_hi, + cmd->io_attr.lba_lo); + pd0 = PS3_R1X_RB_INFO_INDEX(span_idx, span_pd_idx); + pd1 = PS3_R1X_RB_INFO_INDEX(span_idx, span_pd_back_idx); + outstanding0 = + ps3_atomic_read(&rb_info->scsi_outstanding_cmds[pd0]); + outstanding1 = + ps3_atomic_read(&rb_info->scsi_outstanding_cmds[pd1]); + + if (outstanding0 > outstanding1 + g_ps3_r1x_rb_diff_cmds) { + target_pd = pd1; + } else if (outstanding1 > + outstanding0 + g_ps3_r1x_rb_diff_cmds) { + target_pd = pd0; + } else { + diff0 = ABS_DIFF(vlba, + rb_info->last_accessed_block[pd0]); + diff1 = ABS_DIFF(vlba, + rb_info->last_accessed_block[pd1]); + target_pd = (diff0 <= diff1 ? pd0 : pd1); + } + + if (target_pd == pd0) { + cmd->io_attr.span_pd_idx = (unsigned char)span_pd_idx; + cmd->io_attr.span_pd_idx_p = + (unsigned char)span_pd_back_idx; + } else { + cmd->io_attr.span_pd_idx = + (unsigned char)span_pd_back_idx; + cmd->io_attr.span_pd_idx_p = (unsigned char)span_pd_idx; + } + + cmd->r1x_read_pd = target_pd; + rb_info->last_accessed_block[target_pd] = + vlba + cmd->io_attr.num_blocks; + ps3_atomic_inc(&rb_info->scsi_outstanding_cmds[target_pd]); + } +} + +static void ps3_r1x_seq_read_target_pd_calc(struct ps3_cmd *cmd, + unsigned int span_idx, + unsigned int span_pd_idx, + unsigned int span_pd_back_idx) +{ + struct ps3_r1x_read_balance_info *rb_info = NULL; + struct ps3_scsi_priv_data *priv_data = NULL; + unsigned long long vlba = 0; + unsigned int outstanding0 = 0; + unsigned int outstanding1 = 0; + unsigned long long diff0 = 0; + unsigned long long diff1 = 0; + unsigned int pd0 = 0; + unsigned int pd1 = 0; + unsigned int target_pd = 0; + + if (!cmd->r1x_read_pd) { + priv_data = scsi_device_private_data(cmd->scmd); + rb_info = priv_data->r1x_rb_info; + vlba = U32_HIGH_LOW_TO_U64(cmd->io_attr.lba_hi, + cmd->io_attr.lba_lo); + pd0 = PS3_R1X_RB_INFO_INDEX(span_idx, span_pd_idx); + pd1 = PS3_R1X_RB_INFO_INDEX(span_idx, span_pd_back_idx); + outstanding0 = + ps3_atomic_read(&rb_info->scsi_outstanding_cmds[pd0]); + outstanding1 = + ps3_atomic_read(&rb_info->scsi_outstanding_cmds[pd1]); + + if (outstanding0 == 0 && outstanding1 != 0) { + target_pd = pd0; + } else if (outstanding0 != 0 && outstanding1 == 0) { + target_pd = pd1; + } else { + diff0 = ABS_DIFF(vlba, + rb_info->last_accessed_block[pd0]); + diff1 = ABS_DIFF(vlba, + rb_info->last_accessed_block[pd1]); + target_pd = (diff0 <= diff1 ? pd0 : pd1); + } + + if (target_pd == pd0) { + cmd->io_attr.span_pd_idx = (unsigned char)span_pd_idx; + cmd->io_attr.span_pd_idx_p = + (unsigned char)span_pd_back_idx; + } else { + cmd->io_attr.span_pd_idx = + (unsigned char)span_pd_back_idx; + cmd->io_attr.span_pd_idx_p = (unsigned char)span_pd_idx; + } + + cmd->r1x_read_pd = target_pd; + rb_info->last_accessed_block[target_pd] = + vlba + cmd->io_attr.num_blocks; + ps3_atomic_inc(&rb_info->scsi_outstanding_cmds[target_pd]); + } +} + +static void ps3_r1_convert_vlba_to_pd_attr(struct ps3_cmd *cmd) +{ + const struct PS3VDEntry *vd_entry = cmd->io_attr.vd_entry; + unsigned long long vlba = + U32_HIGH_LOW_TO_U64(cmd->io_attr.lba_hi, cmd->io_attr.lba_lo); + cmd->io_attr.plba = vd_entry->startLBA + vlba; + cmd->io_attr.span_idx = 0; + cmd->io_attr.plba_back = cmd->io_attr.plba; + + if (ps3_scsih_is_read_cmd(cmd->io_attr.rw_flag)) { + if (cmd->io_attr.seq_flag == SCSI_RW_RANDOM_CMD) + ps3_r1x_rand_read_target_pd_calc(cmd, 0, 0, 1); + else + ps3_r1x_seq_read_target_pd_calc(cmd, 0, 0, 1); + } else { + cmd->io_attr.span_pd_idx = 0; + cmd->io_attr.span_pd_idx_p = 1; + } + + LOG_DEBUG( + "hno:%u chl:%u id:%u rw=%u, span_pd_idx=%u, span_pd_idx_p=%u\n", + PS3_HOST(cmd->instance), cmd->scmd->device->channel, + cmd->scmd->device->id, cmd->io_attr.rw_flag, + cmd->io_attr.span_pd_idx, cmd->io_attr.span_pd_idx_p); +} + +static void ps3_r5_convert_vlba_to_pd_attr(struct ps3_cmd *cmd) +{ + unsigned int stripe_offset = 0; + unsigned long long stripe_idx = 0; + unsigned int strip_size_shift = 0; + unsigned int span_data_disk_idx = 0; + unsigned int span_parity_disk_idx = 0; + const struct PS3VDEntry *vd_entry = cmd->io_attr.vd_entry; + unsigned long long vlba = + U32_HIGH_LOW_TO_U64(cmd->io_attr.lba_hi, cmd->io_attr.lba_lo); + + LBA_TO_STRIPE_INDEX(vlba, vd_entry->stripeDataSize, stripe_idx); + LBA_TO_STRIPE_OFFSET(vlba, vd_entry->stripeDataSize, stripe_offset); + + if (ps3_is_power_of_2(vd_entry->physDrvCnt)) { + span_parity_disk_idx = stripe_idx & (vd_entry->physDrvCnt - 1); + } else { + span_parity_disk_idx = + PS3_MOD64(stripe_idx, vd_entry->physDrvCnt); + } + span_parity_disk_idx = vd_entry->physDrvCnt - span_parity_disk_idx - 1; + + strip_size_shift = ps3_blocksize_to_shift(vd_entry->stripSize); + span_data_disk_idx = stripe_offset >> strip_size_shift; + span_data_disk_idx = span_parity_disk_idx + span_data_disk_idx + 1; + if (span_data_disk_idx >= vd_entry->physDrvCnt) + span_data_disk_idx -= vd_entry->physDrvCnt; + + cmd->io_attr.plba = + vd_entry->startLBA + (stripe_idx << strip_size_shift) + + (stripe_offset & STRIP_SIZE_MASK(vd_entry->stripSize)); + + cmd->io_attr.span_idx = 0; + cmd->io_attr.span_pd_idx = (unsigned char)span_data_disk_idx; + cmd->io_attr.span_pd_idx_p = (unsigned char)span_parity_disk_idx; + LOG_DEBUG("vlba:0x%llx plba:0x%llx startLBA:0x%llx stripe_idx:%llu\n" + "\tstrip_size_shift:%u stripe_offset:%u span_pd_indx:%u\n", + vlba, cmd->io_attr.plba, vd_entry->startLBA, stripe_idx, + strip_size_shift, stripe_offset, cmd->io_attr.span_pd_idx); +} + +static void ps3_r6_convert_vlba_to_pd_attr(struct ps3_cmd *cmd) +{ + unsigned int stripe_offset = 0; + unsigned long long stripe_idx = 0; + unsigned int strip_size_shift = 0; + unsigned int span_data_disk_idx = 0; + unsigned int span_q_disk_idx = 0; + const struct PS3VDEntry *vd_entry = cmd->io_attr.vd_entry; + unsigned long long vlba = + U32_HIGH_LOW_TO_U64(cmd->io_attr.lba_hi, cmd->io_attr.lba_lo); + + LBA_TO_STRIPE_INDEX(vlba, vd_entry->stripeDataSize, stripe_idx); + LBA_TO_STRIPE_OFFSET(vlba, vd_entry->stripeDataSize, stripe_offset); + + RAID660_GET_PQ_SPINDLENO(stripe_idx, vd_entry->physDrvCnt, + span_q_disk_idx); + + strip_size_shift = ps3_blocksize_to_shift(vd_entry->stripSize); + span_data_disk_idx = stripe_offset >> strip_size_shift; + span_data_disk_idx = span_data_disk_idx + span_q_disk_idx + 1; + if (span_data_disk_idx >= vd_entry->physDrvCnt) + span_data_disk_idx -= vd_entry->physDrvCnt; + + cmd->io_attr.plba = + vd_entry->startLBA + (stripe_idx << strip_size_shift) + + (stripe_offset & STRIP_SIZE_MASK(vd_entry->stripSize)); + + cmd->io_attr.span_idx = 0; + cmd->io_attr.span_pd_idx = (unsigned char)span_data_disk_idx; + cmd->io_attr.span_pd_idx_q = (unsigned char)span_q_disk_idx; + if (span_q_disk_idx != 0) + cmd->io_attr.span_pd_idx_p = span_q_disk_idx - 1; + else + cmd->io_attr.span_pd_idx_p = vd_entry->physDrvCnt - 1; + +} + +static void ps3_r10_convert_vlba_to_pd_attr(struct ps3_cmd *cmd) +{ + unsigned int stripe_offset = 0; + unsigned long long stripe_idx = 0; + unsigned int strip_size_shift = 0; + unsigned int span_data_disk_idx = 0; + unsigned int span_data_disk_back_idx = 0; + unsigned int span_idx = 0; + unsigned char span_pd_num = 0; + const struct PS3VDEntry *vd_entry = cmd->io_attr.vd_entry; + unsigned long long vlba = + U32_HIGH_LOW_TO_U64(cmd->io_attr.lba_hi, cmd->io_attr.lba_lo); + + LBA_TO_STRIPE_INDEX(vlba, vd_entry->stripeDataSize, stripe_idx); + LBA_TO_STRIPE_OFFSET(vlba, vd_entry->stripeDataSize, stripe_offset); + + while (stripe_offset >= vd_entry->span[span_idx].spanStripeDataSize) { + stripe_offset -= vd_entry->span[span_idx].spanStripeDataSize; + span_idx++; + } + + strip_size_shift = ps3_blocksize_to_shift(vd_entry->stripSize); + span_data_disk_idx = stripe_offset >> strip_size_shift; + span_data_disk_idx = span_data_disk_idx << 1; + span_pd_num = VD_SPAN_PD_NUM(vd_entry, span_idx); + if (span_data_disk_idx > span_pd_num) { + span_data_disk_idx -= span_pd_num; + span_data_disk_back_idx = + ((span_data_disk_idx + 1) == span_pd_num) ? + 0 : + (span_data_disk_idx + 1); + stripe_idx = (stripe_idx << 1) + 1; + } else { + if (span_pd_num & 1) { + stripe_idx = stripe_idx << 1; + span_data_disk_back_idx = + ((span_data_disk_idx + 1) == span_pd_num) ? + 0 : + (span_data_disk_idx + 1); + } else { + span_data_disk_back_idx = span_data_disk_idx + 1; + } + } + + cmd->io_attr.plba = + vd_entry->startLBA + (stripe_idx << strip_size_shift) + + (stripe_offset & STRIP_SIZE_MASK(vd_entry->stripSize)); + cmd->io_attr.plba_back = cmd->io_attr.plba; + if (span_data_disk_back_idx == 0) + cmd->io_attr.plba_back += vd_entry->stripSize; + cmd->io_attr.span_idx = (unsigned char)span_idx; + + if (!(span_pd_num & 1) && ps3_scsih_is_read_cmd(cmd->io_attr.rw_flag)) { + if (cmd->io_attr.seq_flag == SCSI_RW_RANDOM_CMD) { + ps3_r1x_rand_read_target_pd_calc( + cmd, span_idx, span_data_disk_idx, + span_data_disk_back_idx); + } else { + ps3_r1x_seq_read_target_pd_calc( + cmd, span_idx, span_data_disk_idx, + span_data_disk_back_idx); + } + } else { + cmd->io_attr.span_pd_idx = (unsigned char)span_data_disk_idx; + cmd->io_attr.span_pd_idx_p = + (unsigned char)span_data_disk_back_idx; + } + + LOG_DEBUG( + "hno:%u tid:0x%llx chl:%u id:%u\n" + "\todd physDrvCnt:%d rw=%u span_pd_idx=%u-plba:0x%llx\n" + "\tspan_pd_idx_p=%u-bplba:0x%llx stripe_offset=0x%x\n", + PS3_HOST(cmd->instance), cmd->trace_id, + cmd->scmd->device->channel, cmd->scmd->device->id, + vd_entry->physDrvCnt & 1, cmd->io_attr.rw_flag, + cmd->io_attr.span_pd_idx, cmd->io_attr.plba, + cmd->io_attr.span_pd_idx_p, cmd->io_attr.plba_back, + stripe_offset); +} + +static void ps3_r1e_convert_vlba_to_pd_attr(struct ps3_cmd *cmd) +{ + unsigned int stripe_offset = 0; + unsigned long long stripe_idx = 0; + unsigned int strip_size_shift = 0; + unsigned int span_data_disk_idx = 0; + unsigned int span_data_disk_back_idx = 0; + const struct PS3VDEntry *vd_entry = cmd->io_attr.vd_entry; + unsigned long long vlba = + U32_HIGH_LOW_TO_U64(cmd->io_attr.lba_hi, cmd->io_attr.lba_lo); + + LBA_TO_STRIPE_INDEX(vlba, vd_entry->stripeDataSize, stripe_idx); + LBA_TO_STRIPE_OFFSET(vlba, vd_entry->stripeDataSize, stripe_offset); + + strip_size_shift = ps3_blocksize_to_shift(vd_entry->stripSize); + + span_data_disk_idx = stripe_offset >> strip_size_shift; + span_data_disk_idx = span_data_disk_idx << 1; + if (span_data_disk_idx > vd_entry->physDrvCnt) { + span_data_disk_idx -= vd_entry->physDrvCnt; + span_data_disk_back_idx = + ((span_data_disk_idx + 1) == vd_entry->physDrvCnt) ? + 0 : + (span_data_disk_idx + 1); + stripe_idx = (stripe_idx << 1) + 1; + } else { + if (vd_entry->physDrvCnt & 1) { + stripe_idx = stripe_idx << 1; + span_data_disk_back_idx = + ((span_data_disk_idx + 1) == + vd_entry->physDrvCnt) ? + 0 : + (span_data_disk_idx + 1); + } else { + span_data_disk_back_idx = span_data_disk_idx + 1; + } + } + + cmd->io_attr.plba = + vd_entry->startLBA + (stripe_idx << strip_size_shift) + + (stripe_offset & STRIP_SIZE_MASK(vd_entry->stripSize)); + cmd->io_attr.plba_back = cmd->io_attr.plba; + if (span_data_disk_back_idx == 0) + cmd->io_attr.plba_back += vd_entry->stripSize; + cmd->io_attr.span_idx = 0; + + if (!(vd_entry->physDrvCnt & 1) && + ps3_scsih_is_read_cmd(cmd->io_attr.rw_flag)) { + if (cmd->io_attr.seq_flag == SCSI_RW_RANDOM_CMD) { + ps3_r1x_rand_read_target_pd_calc( + cmd, 0, span_data_disk_idx, + span_data_disk_back_idx); + } else { + ps3_r1x_seq_read_target_pd_calc( + cmd, 0, span_data_disk_idx, + span_data_disk_back_idx); + } + } else { + cmd->io_attr.span_pd_idx = (unsigned char)span_data_disk_idx; + cmd->io_attr.span_pd_idx_p = + (unsigned char)span_data_disk_back_idx; + } + + LOG_DEBUG("hno:%u chl:%u id:%u odd physDrvCnt:%d rw=%u, span_pd_idx=%u, span_pd_idx_p=%u\n", + PS3_HOST(cmd->instance), cmd->scmd->device->channel, + cmd->scmd->device->id, vd_entry->physDrvCnt & 1, + cmd->io_attr.rw_flag, cmd->io_attr.span_pd_idx, + cmd->io_attr.span_pd_idx_p); +} + +static void ps3_r00_convert_vlba_to_pd_attr(struct ps3_cmd *cmd) +{ + unsigned int stripe_offset = 0; + unsigned long long stripe_idx = 0; + unsigned int strip_size_shift = 0; + unsigned int span_data_disk_idx = 0; + unsigned int span_idx = 0; + const struct PS3VDEntry *vd_entry = cmd->io_attr.vd_entry; + unsigned long long vlba = + U32_HIGH_LOW_TO_U64(cmd->io_attr.lba_hi, cmd->io_attr.lba_lo); + + LBA_TO_STRIPE_INDEX(vlba, vd_entry->stripeDataSize, stripe_idx); + LBA_TO_STRIPE_OFFSET(vlba, vd_entry->stripeDataSize, stripe_offset); + + while (stripe_offset >= vd_entry->span[span_idx].spanStripeDataSize) { + stripe_offset -= vd_entry->span[span_idx].spanStripeDataSize; + span_idx++; + } + + strip_size_shift = ps3_blocksize_to_shift(vd_entry->stripSize); + span_data_disk_idx = stripe_offset >> strip_size_shift; + + cmd->io_attr.plba = + vd_entry->startLBA + (stripe_idx << strip_size_shift) + + (stripe_offset & STRIP_SIZE_MASK(vd_entry->stripSize)); + cmd->io_attr.span_idx = (unsigned char)span_idx; + cmd->io_attr.span_pd_idx = (unsigned char)span_data_disk_idx; +} + +static void ps3_r50_convert_vlba_to_pd_attr(struct ps3_cmd *cmd) +{ + unsigned int stripe_offset = 0; + unsigned long long stripe_idx = 0; + unsigned int strip_size_shift = 0; + unsigned int span_data_disk_idx = 0; + unsigned int span_parity_disk_idx = 0; + unsigned int span_idx = 0; + unsigned char span_pd_num = 0; + const struct PS3VDEntry *vd_entry = cmd->io_attr.vd_entry; + unsigned long long vlba = + U32_HIGH_LOW_TO_U64(cmd->io_attr.lba_hi, cmd->io_attr.lba_lo); + + LBA_TO_STRIPE_INDEX(vlba, vd_entry->stripeDataSize, stripe_idx); + LBA_TO_STRIPE_OFFSET(vlba, vd_entry->stripeDataSize, stripe_offset); + + while (stripe_offset >= vd_entry->span[span_idx].spanStripeDataSize) { + stripe_offset -= vd_entry->span[span_idx].spanStripeDataSize; + span_idx++; + } + + span_pd_num = VD_SPAN_PD_NUM(vd_entry, span_idx); + if (ps3_is_power_of_2(span_pd_num)) + span_parity_disk_idx = stripe_idx & (span_pd_num - 1); + else + span_parity_disk_idx = PS3_MOD64(stripe_idx, span_pd_num); + + span_parity_disk_idx = span_pd_num - span_parity_disk_idx - 1; + + strip_size_shift = ps3_blocksize_to_shift(vd_entry->stripSize); + span_data_disk_idx = stripe_offset >> strip_size_shift; + span_data_disk_idx = span_parity_disk_idx + span_data_disk_idx + 1; + if (span_data_disk_idx >= span_pd_num) + span_data_disk_idx -= span_pd_num; + + cmd->io_attr.plba = + vd_entry->startLBA + (stripe_idx << strip_size_shift) + + (stripe_offset & STRIP_SIZE_MASK(vd_entry->stripSize)); + cmd->io_attr.span_idx = (unsigned char)span_idx; + cmd->io_attr.span_pd_idx = (unsigned char)span_data_disk_idx; + cmd->io_attr.span_pd_idx_p = (unsigned char)span_parity_disk_idx; +} + +static void ps3_r60_convert_vlba_to_pd_attr(struct ps3_cmd *cmd) +{ + unsigned int stripe_offset = 0; + unsigned long long stripe_idx = 0; + unsigned int strip_size_shift = 0; + unsigned int span_data_disk_idx = 0; + unsigned int span_q_disk_idx = 0; + unsigned int span_idx = 0; + unsigned char span_pd_num = 0; + const struct PS3VDEntry *vd_entry = cmd->io_attr.vd_entry; + unsigned long long vlba = + U32_HIGH_LOW_TO_U64(cmd->io_attr.lba_hi, cmd->io_attr.lba_lo); + + LBA_TO_STRIPE_INDEX(vlba, vd_entry->stripeDataSize, stripe_idx); + LBA_TO_STRIPE_OFFSET(vlba, vd_entry->stripeDataSize, stripe_offset); + + while (stripe_offset >= vd_entry->span[span_idx].spanStripeDataSize) { + stripe_offset -= vd_entry->span[span_idx].spanStripeDataSize; + span_idx++; + } + + span_pd_num = VD_SPAN_PD_NUM(vd_entry, span_idx); + RAID660_GET_PQ_SPINDLENO(stripe_idx, span_pd_num, span_q_disk_idx); + + strip_size_shift = ps3_blocksize_to_shift(vd_entry->stripSize); + span_data_disk_idx = stripe_offset >> strip_size_shift; + span_data_disk_idx = span_data_disk_idx + span_q_disk_idx + 1; + if (span_data_disk_idx >= span_pd_num) + span_data_disk_idx -= span_pd_num; + + cmd->io_attr.plba = + vd_entry->startLBA + (stripe_idx << strip_size_shift) + + (stripe_offset & STRIP_SIZE_MASK(vd_entry->stripSize)); + cmd->io_attr.span_idx = (unsigned char)span_idx; + cmd->io_attr.span_pd_idx = (unsigned char)span_data_disk_idx; + cmd->io_attr.span_pd_idx_q = (unsigned char)span_q_disk_idx; + if (span_q_disk_idx != 0) + cmd->io_attr.span_pd_idx_p = span_q_disk_idx - 1; + else + cmd->io_attr.span_pd_idx_p = span_pd_num - 1; +} + +static int ps3_convert_to_pd_info(struct ps3_cmd *cmd) +{ + int ret = PS3_SUCCESS; + const struct PS3VDEntry *vd_entry = cmd->io_attr.vd_entry; + const struct PS3Extent *extent = + &(vd_entry->span[cmd->io_attr.span_idx] + .extent[cmd->io_attr.span_pd_idx]); + + if (extent->state != MIC_PD_STATE_ONLINE) { + LOG_DEBUG("cmd :%u direct check pd:%u state:%s != ONLINE\n", + cmd->index, extent->phyDiskID.ps3Dev.phyDiskID, + getPdStateName((enum MicPdState)extent->state, + cmd->instance->is_raid)); + ret = -PS3_ENODEV; + goto l_out; + } + + cmd->io_attr.pd_entry = ps3_dev_mgr_lookup_pd_info_by_id( + cmd->instance, extent->phyDiskID.ps3Dev.phyDiskID); + if (unlikely(cmd->io_attr.pd_entry == NULL)) { + LOG_FILE_ERROR( + "host_no:%u trace_id:0x%llx idspan_id:%d span_pd_idx:%d pd[%u:%u:%u], pd_entry == NULL\n", + PS3_HOST(cmd->instance), cmd->trace_id, + cmd->io_attr.span_idx, cmd->io_attr.span_pd_idx, + extent->phyDiskID.ps3Dev.softChan, + extent->phyDiskID.ps3Dev.devID, + extent->phyDiskID.ps3Dev.phyDiskID); + ret = -PS3_ENODEV; + goto l_out; + } + + if (!ps3_is_r1x_write_cmd(cmd)) + goto l_out; + + extent = &(vd_entry->span[cmd->io_attr.span_idx] + .extent[cmd->io_attr.span_pd_idx_p]); + + if (extent->state != MIC_PD_STATE_ONLINE) { + LOG_DEBUG("cmd :%u direct check pd:%u state:%s != ONLINE\n", + cmd->index, extent->phyDiskID.ps3Dev.phyDiskID, + getPdStateName((enum MicPdState)extent->state, + cmd->instance->is_raid)); + ret = -PS3_ENODEV; + goto l_out; + } + + cmd->io_attr.peer_pd_entry = ps3_dev_mgr_lookup_pd_info_by_id( + cmd->instance, extent->phyDiskID.ps3Dev.phyDiskID); + if (unlikely(cmd->io_attr.peer_pd_entry == NULL)) { + LOG_ERROR_LIM( + "host_no:%u trace_id:0x%llx idspan_id:%d span_pd_idx:%d peer_pd[%u:%u:%u], pd_entry == NULL\n", + PS3_HOST(cmd->instance), cmd->trace_id, + cmd->io_attr.span_idx, cmd->io_attr.span_pd_idx, + extent->phyDiskID.ps3Dev.softChan, + extent->phyDiskID.ps3Dev.devID, + extent->phyDiskID.ps3Dev.phyDiskID); + ret = -PS3_ENODEV; + goto l_out; + } + +l_out: + return ret; +} + +int ps3_scsih_vlba_to_pd_calc(struct ps3_cmd *cmd) +{ + int ret = PS3_SUCCESS; + + if (unlikely(!ps3_vd_entry_valid_check(cmd->io_attr.vd_entry))) { + ret = -PS3_FAILED; + LOG_ERROR_LIM( + "trace_id:0x%llx host_no:%u vd entry is invalid\n", + cmd->trace_id, PS3_HOST(cmd->instance)); + goto l_out; + } + + switch (cmd->io_attr.vd_entry->raidLevel) { + case RAID0: + ps3_r0_convert_vlba_to_pd_attr(cmd); + break; + case RAID1: + ps3_r1_convert_vlba_to_pd_attr(cmd); + break; + case RAID5: + ps3_r5_convert_vlba_to_pd_attr(cmd); + break; + case RAID6: + ps3_r6_convert_vlba_to_pd_attr(cmd); + break; + case RAID10: + ps3_r10_convert_vlba_to_pd_attr(cmd); + break; + case RAID1E: + ps3_r1e_convert_vlba_to_pd_attr(cmd); + break; + case RAID00: + ps3_r00_convert_vlba_to_pd_attr(cmd); + break; + case RAID50: + ps3_r50_convert_vlba_to_pd_attr(cmd); + break; + case RAID60: + ps3_r60_convert_vlba_to_pd_attr(cmd); + break; + default: + ret = -PS3_FAILED; + LOG_ERROR_LIM( + "trace_id:0x%llx host_no:%u vd level:%d is illegal\n", + cmd->trace_id, PS3_HOST(cmd->instance), + cmd->io_attr.vd_entry->raidLevel); + goto l_out; + } + +l_out: + return ret; +} + +unsigned char ps3_scsih_vd_acc_att_build(struct ps3_cmd *cmd) +{ + int ret = PS3_SUCCESS; + struct PS3VDAccAttr *acc_attr = &cmd->req_frame->frontendReq.vdAccAttr; + static unsigned long j; + + ret = ps3_scsih_vlba_to_pd_calc(cmd); + if (ret != PS3_SUCCESS) { + LOG_WARN_TIME_LIM(&j, PS3_LOG_LIMIT_INTERVAL_MSEC, + "trace_id:0x%llx host_no:%u vlba calc NOK\n", + cmd->trace_id, PS3_HOST(cmd->instance)); + acc_attr->isAccActive = 0; + goto l_out; + } + + acc_attr->isAccActive = 1; + acc_attr->firstPdStartLba = cmd->io_attr.plba; + acc_attr->firstSpanNo = cmd->io_attr.span_idx; + acc_attr->fisrtSeqInSpan = cmd->io_attr.span_pd_idx; + acc_attr->secondSeqInSapn = cmd->io_attr.span_pd_idx_p; + acc_attr->thirdSeqInSapn = cmd->io_attr.span_pd_idx_q; +l_out: + return (acc_attr->isAccActive == 1); +} + +int ps3_scsih_vd_rw_io_to_pd_calc(struct ps3_cmd *cmd) +{ + int ret = PS3_SUCCESS; + + ret = ps3_scsih_vlba_to_pd_calc(cmd); + if (ret != PS3_SUCCESS) { + LOG_WARN_LIM("trace_id:0x%llx host_no:%u vlba calc NOK\n", + cmd->trace_id, PS3_HOST(cmd->instance)); + goto l_out; + } + + ret = ps3_convert_to_pd_info(cmd); + +l_out: + return ret; +} + +static void ps3_update_cmd_target_pd(struct ps3_cmd *cmd, + unsigned short disk_id) +{ + unsigned short i = 0; + struct ps3_qos_pd_mgr *qos_pd_mgr = NULL; + struct ps3_instance *instance = cmd->instance; + + if (unlikely(cmd->target_pd_count >= PS3_QOS_MAX_PD_IN_VD)) { + LOG_INFO( + "host_no:%u CFID:%u did:%u target pd count %u check NOK\n", + PS3_HOST(instance), cmd->index, disk_id, + cmd->target_pd_count); + goto l_out; + } + if (disk_id > instance->ctrl_info.maxPdCount) { + LOG_DEBUG("disk_id is error.host_no:%u CFID:%u did:%u\n", + PS3_HOST(instance), cmd->index, disk_id); + goto l_out; + } + + qos_pd_mgr = &instance->qos_context.pd_ctx.qos_pd_mgrs[disk_id]; + if (ps3_atomic_read(&qos_pd_mgr->valid) != 1) { + LOG_DEBUG("qos pd is invalid. host_no:%u CFID:%u did:%u\n", + PS3_HOST(instance), cmd->index, disk_id); + goto l_out; + } + + for (i = 0; i < cmd->target_pd_count; i++) { + if (cmd->target_pd[i].flat_disk_id == disk_id) { + cmd->target_pd[i].strip_count++; + goto l_out; + } + } + + cmd->target_pd[cmd->target_pd_count].flat_disk_id = disk_id; + cmd->target_pd[cmd->target_pd_count].strip_count = 1; + cmd->target_pd_count++; +l_out: + return; +} + +static void ps3_r0_vlba_to_pd(struct ps3_cmd *cmd, unsigned long long lba) +{ + unsigned int stripe_offset = 0; + unsigned int strip_size_shift = 0; + unsigned char span_idx = 0; + unsigned int span_disk_idx = 0; + unsigned short disk_id = 0; + const struct PS3VDEntry *vd_entry = cmd->io_attr.vd_entry; + + strip_size_shift = ps3_blocksize_to_shift(vd_entry->stripSize); + LBA_TO_STRIPE_OFFSET(lba, vd_entry->stripeDataSize, stripe_offset); + span_disk_idx = stripe_offset >> strip_size_shift; + if (vd_entry->span[span_idx].extent[span_disk_idx].state == + MIC_PD_STATE_ONLINE) { + disk_id = vd_entry->span[span_idx] + .extent[span_disk_idx] + .phyDiskID.ps3Dev.phyDiskID; + ps3_update_cmd_target_pd(cmd, disk_id); + } +} + +static void ps3_r0_target_pd_get(struct ps3_cmd *cmd) +{ + unsigned long long vlba = + U32_HIGH_LOW_TO_U64(cmd->io_attr.lba_hi, cmd->io_attr.lba_lo); + const struct PS3VDEntry *vd_entry = cmd->io_attr.vd_entry; + unsigned int left_len = cmd->io_attr.num_blocks; + unsigned int strip_len = 0; + unsigned char first_strip = PS3_TRUE; + + while (left_len > 0) { + ps3_r0_vlba_to_pd(cmd, vlba); + if (first_strip) { + strip_len = + vd_entry->stripSize - + (vlba & STRIP_SIZE_MASK(vd_entry->stripSize)); + strip_len = PS3_MIN(strip_len, left_len); + first_strip = PS3_FALSE; + } else { + strip_len = PS3_MIN(left_len, vd_entry->stripSize); + } + left_len -= strip_len; + vlba += strip_len; + } +} + +static void ps3_r1_target_pd_get(struct ps3_cmd *cmd) +{ + unsigned char span_idx = 0; + unsigned short span_disk_idx = 0; + unsigned short disk_id = 0; + const struct PS3Extent *extent = NULL; + const struct PS3VDEntry *vd_entry = cmd->io_attr.vd_entry; + unsigned char primary_valid = PS3_FALSE; + + ps3_scsih_vlba_to_pd_calc(cmd); + span_disk_idx = cmd->io_attr.span_pd_idx; + extent = &vd_entry->span[span_idx].extent[span_disk_idx]; + if (extent->state == MIC_PD_STATE_ONLINE) { + disk_id = extent->phyDiskID.ps3Dev.phyDiskID; + ps3_update_cmd_target_pd(cmd, disk_id); + primary_valid = PS3_TRUE; + } + + if (ps3_scsih_is_write_cmd(cmd->io_attr.rw_flag) || !primary_valid) { + span_disk_idx = cmd->io_attr.span_pd_idx_p; + extent = &vd_entry->span[span_idx].extent[span_disk_idx]; + if (extent->state == MIC_PD_STATE_ONLINE) { + disk_id = extent->phyDiskID.ps3Dev.phyDiskID; + ps3_update_cmd_target_pd(cmd, disk_id); + } + } +} + +static void ps3_r00_vlba_to_pd(struct ps3_cmd *cmd, unsigned long long lba) +{ + unsigned int stripe_offset = 0; + unsigned int strip_size_shift = 0; + unsigned char span_idx = 0; + unsigned int span_disk_idx = 0; + unsigned short disk_id = 0; + const struct PS3VDEntry *vd_entry = cmd->io_attr.vd_entry; + + strip_size_shift = ps3_blocksize_to_shift(vd_entry->stripSize); + LBA_TO_STRIPE_OFFSET(lba, vd_entry->stripeDataSize, stripe_offset); + while (stripe_offset >= vd_entry->span[span_idx].spanStripeDataSize) { + stripe_offset -= vd_entry->span[span_idx].spanStripeDataSize; + span_idx++; + } + span_disk_idx = stripe_offset >> strip_size_shift; + if (vd_entry->span[span_idx].extent[span_disk_idx].state == + MIC_PD_STATE_ONLINE) { + disk_id = vd_entry->span[span_idx] + .extent[span_disk_idx] + .phyDiskID.ps3Dev.phyDiskID; + ps3_update_cmd_target_pd(cmd, disk_id); + } +} + +static void ps3_r00_target_pd_get(struct ps3_cmd *cmd) +{ + unsigned long long vlba = + U32_HIGH_LOW_TO_U64(cmd->io_attr.lba_hi, cmd->io_attr.lba_lo); + const struct PS3VDEntry *vd_entry = cmd->io_attr.vd_entry; + unsigned int left_len = cmd->io_attr.num_blocks; + unsigned int strip_len = 0; + unsigned char first_strip = PS3_TRUE; + + while (left_len > 0) { + ps3_r00_vlba_to_pd(cmd, vlba); + if (first_strip) { + strip_len = + vd_entry->stripSize - + (vlba & STRIP_SIZE_MASK(vd_entry->stripSize)); + strip_len = PS3_MIN(strip_len, left_len); + first_strip = PS3_FALSE; + } else { + strip_len = PS3_MIN(left_len, vd_entry->stripSize); + } + left_len -= strip_len; + vlba += strip_len; + } +} + +static void ps3_r5_vlba_to_pd(struct ps3_cmd *cmd, unsigned long long vlba) +{ + unsigned int stripe_offset = 0; + unsigned long long stripe_idx = 0; + unsigned int strip_size_shift = 0; + unsigned int span_data_disk_idx = 0; + unsigned int span_parity_disk_idx = 0; + const struct PS3VDEntry *vd_entry = cmd->io_attr.vd_entry; + unsigned short disk_id = 0; + + LBA_TO_STRIPE_INDEX(vlba, vd_entry->stripeDataSize, stripe_idx); + LBA_TO_STRIPE_OFFSET(vlba, vd_entry->stripeDataSize, stripe_offset); + + if (ps3_is_power_of_2(vd_entry->physDrvCnt)) { + span_parity_disk_idx = stripe_idx & (vd_entry->physDrvCnt - 1); + } else { + span_parity_disk_idx = + PS3_MOD64(stripe_idx, vd_entry->physDrvCnt); + } + span_parity_disk_idx = vd_entry->physDrvCnt - span_parity_disk_idx - 1; + + strip_size_shift = ps3_blocksize_to_shift(vd_entry->stripSize); + span_data_disk_idx = stripe_offset >> strip_size_shift; + span_data_disk_idx = span_parity_disk_idx + span_data_disk_idx + 1; + if (span_data_disk_idx >= vd_entry->physDrvCnt) + span_data_disk_idx -= vd_entry->physDrvCnt; + + if (vd_entry->span[0].extent[span_data_disk_idx].state == + MIC_PD_STATE_ONLINE) { + disk_id = vd_entry->span[0] + .extent[span_data_disk_idx] + .phyDiskID.ps3Dev.phyDiskID; + ps3_update_cmd_target_pd(cmd, disk_id); + } +} + +static void ps3_r5_target_pd_get(struct ps3_cmd *cmd) +{ + unsigned long long vlba = + U32_HIGH_LOW_TO_U64(cmd->io_attr.lba_hi, cmd->io_attr.lba_lo); + const struct PS3VDEntry *vd_entry = cmd->io_attr.vd_entry; + unsigned int left_len = cmd->io_attr.num_blocks; + unsigned int strip_len = 0; + unsigned char first_strip = PS3_TRUE; + + while (left_len > 0) { + ps3_r5_vlba_to_pd(cmd, vlba); + if (first_strip) { + strip_len = + vd_entry->stripSize - + (vlba & STRIP_SIZE_MASK(vd_entry->stripSize)); + strip_len = PS3_MIN(strip_len, left_len); + first_strip = PS3_FALSE; + } else { + strip_len = PS3_MIN(left_len, vd_entry->stripSize); + } + left_len -= strip_len; + vlba += strip_len; + } +} + +static void ps3_r6_vlba_to_pd(struct ps3_cmd *cmd, unsigned long long vlba) +{ + unsigned int stripe_offset = 0; + unsigned long long stripe_idx = 0; + unsigned int strip_size_shift = 0; + unsigned int span_data_disk_idx = 0; + unsigned int span_q_disk_idx = 0; + const struct PS3VDEntry *vd_entry = cmd->io_attr.vd_entry; + unsigned short disk_id = 0; + + LBA_TO_STRIPE_INDEX(vlba, vd_entry->stripeDataSize, stripe_idx); + LBA_TO_STRIPE_OFFSET(vlba, vd_entry->stripeDataSize, stripe_offset); + + if (ps3_is_power_of_2(vd_entry->physDrvCnt)) + span_q_disk_idx = stripe_idx & (vd_entry->physDrvCnt - 1); + else + span_q_disk_idx = PS3_MOD64(stripe_idx, vd_entry->physDrvCnt); + span_q_disk_idx = (vd_entry->physDrvCnt) - span_q_disk_idx - 1; + + strip_size_shift = ps3_blocksize_to_shift(vd_entry->stripSize); + span_data_disk_idx = stripe_offset >> strip_size_shift; + span_data_disk_idx = span_data_disk_idx + span_q_disk_idx + 1; + if (span_data_disk_idx >= vd_entry->physDrvCnt) + span_data_disk_idx -= vd_entry->physDrvCnt; + + if (vd_entry->span[0].extent[span_data_disk_idx].state == + MIC_PD_STATE_ONLINE) { + disk_id = vd_entry->span[0] + .extent[span_data_disk_idx] + .phyDiskID.ps3Dev.phyDiskID; + ps3_update_cmd_target_pd(cmd, disk_id); + } +} + +static void ps3_r6_target_pd_get(struct ps3_cmd *cmd) +{ + unsigned long long vlba = + U32_HIGH_LOW_TO_U64(cmd->io_attr.lba_hi, cmd->io_attr.lba_lo); + const struct PS3VDEntry *vd_entry = cmd->io_attr.vd_entry; + unsigned int left_len = cmd->io_attr.num_blocks; + unsigned int strip_len = 0; + + ps3_r6_vlba_to_pd(cmd, vlba); + + strip_len = vd_entry->stripSize - + (vlba & STRIP_SIZE_MASK(vd_entry->stripSize)); + strip_len = PS3_MIN(strip_len, left_len); + left_len -= strip_len; + vlba += strip_len; + while (left_len > 0) { + strip_len = PS3_MIN(left_len, vd_entry->stripSize); + ps3_r6_vlba_to_pd(cmd, vlba); + left_len -= strip_len; + vlba += strip_len; + } +} + +static void ps3_r50_vlba_to_pd(struct ps3_cmd *cmd, unsigned long long vlba) +{ + unsigned int stripe_offset = 0; + unsigned long long stripe_idx = 0; + unsigned int strip_size_shift = 0; + unsigned int span_data_disk_idx = 0; + unsigned int span_parity_disk_idx = 0; + unsigned int span_idx = 0; + unsigned char span_pd_num = 0; + unsigned short disk_id = 0; + const struct PS3VDEntry *vd_entry = cmd->io_attr.vd_entry; + + LBA_TO_STRIPE_INDEX(vlba, vd_entry->stripeDataSize, stripe_idx); + LBA_TO_STRIPE_OFFSET(vlba, vd_entry->stripeDataSize, stripe_offset); + + while (stripe_offset >= vd_entry->span[span_idx].spanStripeDataSize) { + stripe_offset -= vd_entry->span[span_idx].spanStripeDataSize; + span_idx++; + } + + span_pd_num = VD_SPAN_PD_NUM(vd_entry, span_idx); + if (ps3_is_power_of_2(span_pd_num)) + span_parity_disk_idx = stripe_idx & (span_pd_num - 1); + else + span_parity_disk_idx = PS3_MOD64(stripe_idx, span_pd_num); + span_parity_disk_idx = span_pd_num - span_parity_disk_idx - 1; + + strip_size_shift = ps3_blocksize_to_shift(vd_entry->stripSize); + span_data_disk_idx = stripe_offset >> strip_size_shift; + span_data_disk_idx = span_parity_disk_idx + span_data_disk_idx + 1; + if (span_data_disk_idx >= span_pd_num) + span_data_disk_idx -= span_pd_num; + + if (vd_entry->span[span_idx].extent[span_data_disk_idx].state == + MIC_PD_STATE_ONLINE) { + disk_id = vd_entry->span[span_idx] + .extent[span_data_disk_idx] + .phyDiskID.ps3Dev.phyDiskID; + ps3_update_cmd_target_pd(cmd, disk_id); + } +} + +static void ps3_r50_target_pd_get(struct ps3_cmd *cmd) +{ + unsigned long long vlba = + U32_HIGH_LOW_TO_U64(cmd->io_attr.lba_hi, cmd->io_attr.lba_lo); + const struct PS3VDEntry *vd_entry = cmd->io_attr.vd_entry; + unsigned int left_len = cmd->io_attr.num_blocks; + unsigned int strip_len = 0; + + ps3_r50_vlba_to_pd(cmd, vlba); + + strip_len = vd_entry->stripSize - + (vlba & STRIP_SIZE_MASK(vd_entry->stripSize)); + strip_len = PS3_MIN(strip_len, left_len); + left_len -= strip_len; + vlba += strip_len; + while (left_len > 0) { + strip_len = PS3_MIN(left_len, vd_entry->stripSize); + ps3_r50_vlba_to_pd(cmd, vlba); + left_len -= strip_len; + vlba += strip_len; + } +} + +static void ps3_r60_vlba_to_pd(struct ps3_cmd *cmd, unsigned long long vlba) +{ + unsigned int stripe_offset = 0; + unsigned long long stripe_idx = 0; + unsigned int strip_size_shift = 0; + unsigned int span_data_disk_idx = 0; + unsigned int span_q_disk_idx = 0; + unsigned int span_idx = 0; + unsigned char span_pd_num = 0; + const struct PS3VDEntry *vd_entry = cmd->io_attr.vd_entry; + unsigned short disk_id = 0; + + LBA_TO_STRIPE_INDEX(vlba, vd_entry->stripeDataSize, stripe_idx); + LBA_TO_STRIPE_OFFSET(vlba, vd_entry->stripeDataSize, stripe_offset); + + while (stripe_offset >= vd_entry->span[span_idx].spanStripeDataSize) { + stripe_offset -= vd_entry->span[span_idx].spanStripeDataSize; + span_idx++; + } + + span_pd_num = VD_SPAN_PD_NUM(vd_entry, span_idx); + RAID660_GET_PQ_SPINDLENO(stripe_idx, span_pd_num, span_q_disk_idx); + + strip_size_shift = ps3_blocksize_to_shift(vd_entry->stripSize); + span_data_disk_idx = stripe_offset >> strip_size_shift; + span_data_disk_idx = span_data_disk_idx + span_q_disk_idx + 1; + if (span_data_disk_idx >= span_pd_num) + span_data_disk_idx -= span_pd_num; + + if (vd_entry->span[span_idx].extent[span_data_disk_idx].state == + MIC_PD_STATE_ONLINE) { + disk_id = vd_entry->span[span_idx] + .extent[span_data_disk_idx] + .phyDiskID.ps3Dev.phyDiskID; + ps3_update_cmd_target_pd(cmd, disk_id); + } +} + +static void ps3_r60_target_pd_get(struct ps3_cmd *cmd) +{ + unsigned long long vlba = + U32_HIGH_LOW_TO_U64(cmd->io_attr.lba_hi, cmd->io_attr.lba_lo); + const struct PS3VDEntry *vd_entry = cmd->io_attr.vd_entry; + unsigned int left_len = cmd->io_attr.num_blocks; + unsigned int strip_len = 0; + + ps3_r60_vlba_to_pd(cmd, vlba); + + strip_len = vd_entry->stripSize - + (vlba & STRIP_SIZE_MASK(vd_entry->stripSize)); + strip_len = PS3_MIN(strip_len, left_len); + left_len -= strip_len; + vlba += strip_len; + while (left_len > 0) { + strip_len = PS3_MIN(left_len, vd_entry->stripSize); + ps3_r60_vlba_to_pd(cmd, vlba); + left_len -= strip_len; + vlba += strip_len; + } +} + +static void ps3_r10_vlba_to_pd(struct ps3_cmd *cmd, unsigned long long lba) +{ + unsigned int stripe_offset = 0; + unsigned int strip_size_shift = 0; + unsigned char span_idx = 0; + unsigned int span_disk_idx = 0; + unsigned int span_disk_back_idx = 0; + unsigned short span_pd_num = 0; + unsigned short disk_id = 0; + const struct PS3VDEntry *vd_entry = cmd->io_attr.vd_entry; + unsigned char primary_valid = PS3_FALSE; + + strip_size_shift = ps3_blocksize_to_shift(vd_entry->stripSize); + LBA_TO_STRIPE_OFFSET(lba, vd_entry->stripeDataSize, stripe_offset); + while (stripe_offset >= vd_entry->span[span_idx].spanStripeDataSize) { + stripe_offset -= vd_entry->span[span_idx].spanStripeDataSize; + span_idx++; + } + span_disk_idx = stripe_offset >> strip_size_shift; + span_disk_idx = span_disk_idx << 1; + span_pd_num = VD_SPAN_PD_NUM(vd_entry, span_idx); + if (span_pd_num & 1) { + if (span_disk_idx > span_pd_num) + span_disk_idx -= span_pd_num; + + if (span_disk_idx + 1 == span_pd_num) + span_disk_back_idx = 0; + else + span_disk_back_idx = span_disk_idx + 1; + } else { + if (ps3_scsih_is_read_cmd(cmd->io_attr.rw_flag)) { + if (cmd->io_attr.span_pd_idx & 1) { + span_disk_back_idx = span_disk_idx; + span_disk_idx++; + } + + } else { + span_disk_back_idx = span_disk_idx + 1; + } + } + + if (vd_entry->span[span_idx].extent[span_disk_idx].state == + MIC_PD_STATE_ONLINE) { + disk_id = vd_entry->span[span_idx] + .extent[span_disk_idx] + .phyDiskID.ps3Dev.phyDiskID; + ps3_update_cmd_target_pd(cmd, disk_id); + primary_valid = PS3_TRUE; + } + + if (ps3_scsih_is_write_cmd(cmd->io_attr.rw_flag) || !primary_valid) { + if (vd_entry->span[span_idx].extent[span_disk_back_idx].state == + MIC_PD_STATE_ONLINE) { + disk_id = vd_entry->span[span_idx] + .extent[span_disk_back_idx] + .phyDiskID.ps3Dev.phyDiskID; + ps3_update_cmd_target_pd(cmd, disk_id); + } + } +} + +static void ps3_r10_target_pd_get(struct ps3_cmd *cmd) +{ + unsigned long long vlba = + U32_HIGH_LOW_TO_U64(cmd->io_attr.lba_hi, cmd->io_attr.lba_lo); + const struct PS3VDEntry *vd_entry = cmd->io_attr.vd_entry; + const struct PS3Extent *extent = NULL; + unsigned int left_len = cmd->io_attr.num_blocks; + unsigned int strip_len = 0; + unsigned short flat_pd_id = 0; + unsigned char primary_valid = PS3_FALSE; + unsigned char span_idx = 0; + unsigned char span_disk_idx = 0; + + ps3_scsih_vlba_to_pd_calc(cmd); + span_idx = cmd->io_attr.span_idx; + span_disk_idx = cmd->io_attr.span_pd_idx; + extent = &vd_entry->span[span_idx].extent[span_disk_idx]; + if (extent->state == MIC_PD_STATE_ONLINE) { + flat_pd_id = extent->phyDiskID.ps3Dev.phyDiskID; + ps3_update_cmd_target_pd(cmd, flat_pd_id); + primary_valid = PS3_TRUE; + } + if (ps3_scsih_is_write_cmd(cmd->io_attr.rw_flag) || !primary_valid) { + span_disk_idx = cmd->io_attr.span_pd_idx_p; + extent = &vd_entry->span[span_idx].extent[span_disk_idx]; + if (extent->state == MIC_PD_STATE_ONLINE) { + flat_pd_id = extent->phyDiskID.ps3Dev.phyDiskID; + ps3_update_cmd_target_pd(cmd, flat_pd_id); + } + } + + strip_len = vd_entry->stripSize - + (vlba & STRIP_SIZE_MASK(vd_entry->stripSize)); + strip_len = PS3_MIN(strip_len, left_len); + left_len -= strip_len; + vlba += strip_len; + while (left_len > 0) { + strip_len = PS3_MIN(left_len, vd_entry->stripSize); + ps3_r10_vlba_to_pd(cmd, vlba); + left_len -= strip_len; + vlba += strip_len; + } +} + +static void ps3_r1e_vlba_to_pd(struct ps3_cmd *cmd, unsigned long long lba) +{ + unsigned int stripe_offset = 0; + unsigned int strip_size_shift = 0; + unsigned char span_idx = 0; + unsigned int span_disk_idx = 0; + unsigned int span_disk_back_idx = 0; + unsigned short disk_id = 0; + const struct PS3VDEntry *vd_entry = cmd->io_attr.vd_entry; + unsigned char primary_valid = PS3_FALSE; + + strip_size_shift = ps3_blocksize_to_shift(vd_entry->stripSize); + LBA_TO_STRIPE_OFFSET(lba, vd_entry->stripeDataSize, stripe_offset); + span_disk_idx = stripe_offset >> strip_size_shift; + span_disk_idx = span_disk_idx << 1; + if (vd_entry->physDrvCnt & 1) { + if (span_disk_idx > vd_entry->physDrvCnt) + span_disk_idx -= vd_entry->physDrvCnt; + if (span_disk_idx + 1 == vd_entry->physDrvCnt) + span_disk_back_idx = 0; + else + span_disk_back_idx = span_disk_idx + 1; + } else { + if (ps3_scsih_is_read_cmd(cmd->io_attr.rw_flag)) { + if (cmd->io_attr.span_pd_idx & 1) { + span_disk_back_idx = span_disk_idx; + span_disk_idx++; + } + + } else { + span_disk_back_idx = span_disk_idx + 1; + } + } + + if (vd_entry->span[span_idx].extent[span_disk_idx].state == + MIC_PD_STATE_ONLINE) { + disk_id = vd_entry->span[span_idx] + .extent[span_disk_idx] + .phyDiskID.ps3Dev.phyDiskID; + ps3_update_cmd_target_pd(cmd, disk_id); + primary_valid = PS3_TRUE; + } + if (ps3_scsih_is_write_cmd(cmd->io_attr.rw_flag) || !primary_valid) { + if (vd_entry->span[span_idx].extent[span_disk_back_idx].state == + MIC_PD_STATE_ONLINE) { + disk_id = vd_entry->span[span_idx] + .extent[span_disk_back_idx] + .phyDiskID.ps3Dev.phyDiskID; + ps3_update_cmd_target_pd(cmd, disk_id); + } + } +} + +static void ps3_r1e_target_pd_get(struct ps3_cmd *cmd) +{ + unsigned long long vlba = + U32_HIGH_LOW_TO_U64(cmd->io_attr.lba_hi, cmd->io_attr.lba_lo); + const struct PS3VDEntry *vd_entry = cmd->io_attr.vd_entry; + const struct PS3Extent *extent = NULL; + unsigned int left_len = cmd->io_attr.num_blocks; + unsigned int strip_len = 0; + unsigned short flat_pd_id = 0; + unsigned char primary_valid = PS3_FALSE; + unsigned char span_idx = 0; + unsigned char span_disk_idx = 0; + + ps3_scsih_vlba_to_pd_calc(cmd); + span_idx = cmd->io_attr.span_idx; + span_disk_idx = cmd->io_attr.span_pd_idx; + extent = &vd_entry->span[span_idx].extent[span_disk_idx]; + if (extent->state == MIC_PD_STATE_ONLINE) { + flat_pd_id = extent->phyDiskID.ps3Dev.phyDiskID; + ps3_update_cmd_target_pd(cmd, flat_pd_id); + primary_valid = PS3_TRUE; + } + if (ps3_scsih_is_write_cmd(cmd->io_attr.rw_flag) || !primary_valid) { + span_disk_idx = cmd->io_attr.span_pd_idx_p; + extent = &vd_entry->span[span_idx].extent[span_disk_idx]; + if (extent->state == MIC_PD_STATE_ONLINE) { + flat_pd_id = extent->phyDiskID.ps3Dev.phyDiskID; + ps3_update_cmd_target_pd(cmd, flat_pd_id); + } + } + + strip_len = vd_entry->stripSize - + (vlba & STRIP_SIZE_MASK(vd_entry->stripSize)); + strip_len = PS3_MIN(strip_len, left_len); + left_len -= strip_len; + vlba += strip_len; + while (left_len > 0) { + strip_len = PS3_MIN(left_len, vd_entry->stripSize); + ps3_r1e_vlba_to_pd(cmd, vlba); + left_len -= strip_len; + vlba += strip_len; + } +} + +static inline void ps3_swap_in_array(struct ps3_qos_member_pd_info *arr, + unsigned short i, unsigned short j) +{ + struct ps3_qos_member_pd_info tmp; + + tmp = arr[i]; + arr[i] = arr[j]; + arr[j] = tmp; +} + +static void ps3_qos_target_pd_adjust(struct ps3_cmd *cmd) +{ + unsigned short i = 0; + unsigned short j = 0; + + for (i = 0; i < cmd->target_pd_count; i++) { + for (j = i + 1; j < cmd->target_pd_count; j++) { + if (cmd->target_pd[i].flat_disk_id > + cmd->target_pd[j].flat_disk_id) { + ps3_swap_in_array(cmd->target_pd, i, j); + } + } + } +} + +void ps3_qos_cmd_member_pd_calc(struct ps3_cmd *cmd) +{ + const struct PS3VDEntry *vd_entry = NULL; + unsigned short disk_id = 0; + struct ps3_qos_pd_mgr *qos_pd_mgr = NULL; + struct ps3_qos_pd_mgr *qos_peer_pd_mgr = NULL; + static unsigned long j; + + if (cmd->io_attr.dev_type == PS3_DEV_TYPE_VD) { + vd_entry = cmd->io_attr.vd_entry; + + if (cmd->cmd_word.direct == PS3_CMDWORD_DIRECT_ADVICE) { + disk_id = PS3_PDID(&cmd->io_attr.pd_entry->disk_pos); + if (ps3_is_r1x_write_cmd(cmd)) { + qos_pd_mgr = &cmd->instance->qos_context.pd_ctx + .qos_pd_mgrs[disk_id]; + disk_id = PS3_PDID( + &cmd->io_attr.peer_pd_entry->disk_pos); + qos_peer_pd_mgr = + &cmd->instance->qos_context.pd_ctx + .qos_pd_mgrs[disk_id]; + disk_id = (qos_pd_mgr->pd_quota <= + qos_peer_pd_mgr->pd_quota) ? + qos_pd_mgr->disk_id : + qos_peer_pd_mgr->disk_id; + } + ps3_update_cmd_target_pd(cmd, disk_id); + } else { + if (!vd_entry->isNvme && !vd_entry->isSsd) + goto _out; + switch (vd_entry->raidLevel) { + case RAID0: + ps3_r0_target_pd_get(cmd); + break; + case RAID1: + ps3_r1_target_pd_get(cmd); + break; + case RAID10: + ps3_r10_target_pd_get(cmd); + break; + case RAID1E: + ps3_r1e_target_pd_get(cmd); + break; + case RAID00: + ps3_r00_target_pd_get(cmd); + break; + case RAID5: + ps3_r5_target_pd_get(cmd); + break; + case RAID6: + ps3_r6_target_pd_get(cmd); + break; + case RAID50: + ps3_r50_target_pd_get(cmd); + break; + case RAID60: + ps3_r60_target_pd_get(cmd); + break; + default: + LOG_ERROR_TIME_LIM( + &j, PS3_LOG_LIMIT_INTERVAL_MSEC, + "trace_id:0x%llx host_no:%u vd level:%d is illegal\n", + cmd->trace_id, PS3_HOST(cmd->instance), + cmd->io_attr.vd_entry->raidLevel); + } + + ps3_qos_target_pd_adjust(cmd); + } + } else { + ps3_update_cmd_target_pd(cmd, cmd->io_attr.disk_id); + } +_out: + LOG_DEBUG( + "qos target pd calc. host_no:%u cmd[%u,%u] pd_cnt:%u dev_t:%u diskid:%u\n", + PS3_HOST(cmd->instance), cmd->index, cmd->cmd_word.type, + cmd->target_pd_count, cmd->io_attr.dev_type, + cmd->io_attr.disk_id); +} + +unsigned short ps3_odd_r1x_judge(struct PS3VDEntry *vd_entry) +{ + unsigned short ret = PS3_IS_SSD_EVEN_R1X_VD; + unsigned char span_idx = 0; + + if (!vd_entry->isSsd) { + ret = PS3_IS_HDD_R1X_VD; + goto l_out; + } + switch (vd_entry->raidLevel) { + case RAID10: + for (; span_idx < vd_entry->spanCount; span_idx++) { + if (VD_SPAN_PD_NUM(vd_entry, span_idx) & 1) { + ret = PS3_IS_SSD_ODD_R1X_VD; + goto l_out; + } + } + break; + case RAID1E: + if (VD_SPAN_PD_NUM(vd_entry, 0) & 1) { + ret = PS3_IS_SSD_ODD_R1X_VD; + goto l_out; + } + break; + default: + ret = PS3_IS_VALID_R1X_VD; + break; + } +l_out: + return ret; +} diff --git a/drivers/scsi/linkdata/ps3stor/ps3_scsih_raid_engine.h b/drivers/scsi/linkdata/ps3stor/ps3_scsih_raid_engine.h new file mode 100644 index 0000000000000000000000000000000000000000..99fdca3d2bb8e813332e8a76bab089582d7dc435 --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/ps3_scsih_raid_engine.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _PS3_SCSIH_RAID_ENGINE_H_ +#define _PS3_SCSIH_RAID_ENGINE_H_ + +#include "ps3_htp.h" +#include "ps3_err_def.h" +#include "ps3_cmd_channel.h" + +enum ps3_odd_r1x_vd_judge_type { + PS3_IS_HDD_R1X_VD, + PS3_IS_SSD_ODD_R1X_VD, + PS3_IS_SSD_EVEN_R1X_VD, + PS3_IS_VALID_R1X_VD +}; + +unsigned char ps3_scsih_is_same_strip(const struct PS3VDEntry *vd_entry, + unsigned int vlba_lo, + unsigned int lba_length); + +int ps3_scsih_vd_rw_io_to_pd_calc(struct ps3_cmd *cmd); + +unsigned char ps3_scsih_vd_acc_att_build(struct ps3_cmd *cmd); + +int ps3_scsih_vlba_to_pd_calc(struct ps3_cmd *cmd); + +void ps3_qos_cmd_member_pd_calc(struct ps3_cmd *cmd); + +unsigned short ps3_odd_r1x_judge(struct PS3VDEntry *vd_entry); + +#endif diff --git a/drivers/scsi/linkdata/ps3stor/ps3_trace_id_alloc.c b/drivers/scsi/linkdata/ps3stor/ps3_trace_id_alloc.c new file mode 100644 index 0000000000000000000000000000000000000000..d6f2edbfe0d990b3ac7e1ef08e87351a6dbf5470 --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/ps3_trace_id_alloc.c @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: GPL-2.0 +#ifndef _WINDOWS + +#include +#include +#include + +#include "ps3_htp_trace_id.h" +#include "ps3_err_def.h" +#include "ps3_trace_id_alloc.h" + +static DEFINE_PER_CPU(union ps3_trace_id, ps3_trace_id); + +static unsigned char g_ps3_trace_id_switch = ps3_trace_id_switch_open; + +void ps3_trace_id_alloc(unsigned long long *trace_id) +{ + union ps3_trace_id *id = NULL; + unsigned long long trace_id_count = 0; + + if (g_ps3_trace_id_switch != ps3_trace_id_switch_open) { + *trace_id = 0; + goto l_out; + } + + preempt_disable(); + id = this_cpu_ptr(&ps3_trace_id); + + trace_id_count = id->ps3_trace_id.count; + ++trace_id_count; + id->ps3_trace_id.count = + (trace_id_count & TRACE_ID_CHIP_OUT_COUNT_MASK); + + *trace_id = id->trace_id; + preempt_enable(); + +l_out: + return; +} + +void ps3_trace_id_init(void) +{ + int cpu = 0; + union ps3_trace_id *id = NULL; + + for_each_possible_cpu(cpu) { + id = &per_cpu(ps3_trace_id, cpu); + id->ps3_trace_id.flag = ps3_trace_id_flag_host_driver; + id->ps3_trace_id.cpu_id = (cpu & TRACE_ID_CHIP_OUT_CPUID_MASK); + id->ps3_trace_id.count = 0; + } + +} + +int ps3_trace_id_switch_store(unsigned char value) +{ + int ret = PS3_SUCCESS; + + if (value != ps3_trace_id_switch_close && + value != ps3_trace_id_switch_open) { + ret = PS3_FAILED; + goto l_out; + } + + g_ps3_trace_id_switch = value; + +l_out: + return ret; +} + +unsigned char ps3_trace_id_switch_show(void) +{ + return g_ps3_trace_id_switch; +} +#endif diff --git a/drivers/scsi/linkdata/ps3stor/ps3_trace_id_alloc.h b/drivers/scsi/linkdata/ps3stor/ps3_trace_id_alloc.h new file mode 100644 index 0000000000000000000000000000000000000000..da26009b405ff25c3cb1af46ef596bc34cdf7618 --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/ps3_trace_id_alloc.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _PS3_TRACE_ID_ALLOC_H_ +#define _PS3_TRACE_ID_ALLOC_H_ + +#include "ps3_htp_def.h" + +enum { + ps3_trace_id_flag_host_driver = 0, +}; + +enum { + ps3_trace_id_switch_open, + ps3_trace_id_switch_close, +}; + +union ps3_trace_id { + unsigned long long trace_id; + struct { + unsigned long long count : 52; + unsigned long long cpu_id : 11; + unsigned long long flag : 1; + } ps3_trace_id; +}; + +void ps3_trace_id_alloc(unsigned long long *trace_id); + +void ps3_trace_id_init(void); + +int ps3_trace_id_switch_store(unsigned char value); + +unsigned char ps3_trace_id_switch_show(void); + +#endif diff --git a/drivers/scsi/linkdata/ps3stor/ps3_util.h b/drivers/scsi/linkdata/ps3stor/ps3_util.h new file mode 100644 index 0000000000000000000000000000000000000000..377c5561185e6b62fa55abf34233b1b59f6c2177 --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/ps3_util.h @@ -0,0 +1,458 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _PS3_UTIL_H_ +#define _PS3_UTIL_H_ + +#ifndef _WINDOWS +#include +#include "ps3_instance_manager.h" + +#endif +#include "htp_v200/ps3_htp_def.h" +#include "ps3_driver_log.h" +#include "ps3_platform_utils.h" +#include "ps3_kernel_version.h" + +#define PS3_DRV_MAX(x, y) ((x) > (y) ? (x) : (y)) + +#define PCIE_DMA_HOST_ADDR_BIT53_MASK_CHECK(addr) \ + (((1ULL) << (PCIE_DMA_HOST_ADDR_BIT_POS_F1)) & (addr)) + +#define PCIE_DMA_HOST_ADDR_BIT54_MASK_CHECK(addr) \ + (((1ULL) << (PCIE_DMA_HOST_ADDR_BIT_POS_F0)) & (addr)) + +enum { + PS3_BLOCK_SIZE_16 = 16, + PS3_BLOCK_SIZE_32 = 32, + PS3_BLOCK_SIZE_64 = 64, + PS3_BLOCK_SIZE_128 = 128, + PS3_BLOCK_SIZE_256 = 256, + PS3_BLOCK_SIZE_512 = 512, + PS3_BLOCK_SIZE_1024 = 1024, + PS3_BLOCK_SIZE_2048 = 2048, + PS3_BLOCK_SIZE_4096 = 4096, + PS3_BLOCK_SIZE_8192 = 8192, +}; + +#define PS3_BLOCK_SIZE_16K (16uLL << 10) +#define PS3_BLOCK_SIZE_32K (32uLL << 10) +#define PS3_BLOCK_SIZE_64K (64uLL << 10) +#define PS3_BLOCK_SIZE_128K (128uLL << 10) +#define PS3_BLOCK_SIZE_256K (256uLL << 10) +#define PS3_BLOCK_SIZE_512K (512uLL << 10) +#define PS3_BLOCK_SIZE_1M (1uLL << 20) +#define PS3_BLOCK_SIZE_2M (2uLL << 20) +#define PS3_BLOCK_SIZE_4M (4uLL << 20) +#define PS3_BLOCK_SIZE_8M (8uLL << 20) +#define PS3_BLOCK_SIZE_16M (16uLL << 20) +#define PS3_BLOCK_SIZE_32M (32uLL << 20) +#define PS3_BLOCK_SIZE_64M (64uLL << 20) +#define PS3_BLOCK_SIZE_128M (128uLL << 20) +#define PS3_BLOCK_SIZE_256M (256uLL << 20) +#define PS3_BLOCK_SIZE_512M (512uLL << 20) +#define PS3_BLOCK_SIZE_1G (1uLL << 30) +#define PS3_BLOCK_SIZE_2G (2uLL << 30) +#define PS3_BLOCK_SIZE_4G (4uLL << 30) +#define PS3_BLOCK_SIZE_8G (8uLL << 30) +#define PS3_BLOCK_SIZE_16G (16uLL << 30) +#define PS3_BLOCK_SIZE_32G (32uLL << 30) +#define PS3_BLOCK_SIZE_64G (64uLL << 30) +#define PS3_BLOCK_SIZE_128G (128uLL << 30) +#define PS3_BLOCK_SIZE_256G (256uLL << 30) +#define PS3_BLOCK_SIZE_512G (512uLL << 30) +#define PS3_BLOCK_SIZE_1T (1uLL << 40) +#define PS3_BLOCK_SIZE_2T (2uLL << 40) +#define PS3_BLOCK_SIZE_4T (4uLL << 40) +#define PS3_BLOCK_SIZE_8T (8uLL << 40) +#define PS3_BLOCK_SIZE_16T (16uLL << 40) +#define PS3_BLOCK_SIZE_32T (32uLL << 40) + +enum { + PS3_BLOCK_SIZE_SHIFT_4 = 4, + PS3_BLOCK_SIZE_SHIFT_5 = 5, + PS3_BLOCK_SIZE_SHIFT_6 = 6, + PS3_BLOCK_SIZE_SHIFT_7 = 7, + PS3_BLOCK_SIZE_SHIFT_8 = 8, + PS3_BLOCK_SIZE_SHIFT_9 = 9, + PS3_BLOCK_SIZE_SHIFT_10 = 10, + PS3_BLOCK_SIZE_SHIFT_11 = 11, + PS3_BLOCK_SIZE_SHIFT_12 = 12, + PS3_BLOCK_SIZE_SHIFT_13 = 13, + PS3_BLOCK_SIZE_SHIFT_16K = 14, + PS3_BLOCK_SIZE_SHIFT_32K = 15, + PS3_BLOCK_SIZE_SHIFT_64K, + PS3_BLOCK_SIZE_SHIFT_128K, + PS3_BLOCK_SIZE_SHIFT_256K, + PS3_BLOCK_SIZE_SHIFT_512K, + PS3_BLOCK_SIZE_SHIFT_1M = 20, + PS3_BLOCK_SIZE_SHIFT_2M, + PS3_BLOCK_SIZE_SHIFT_4M, + PS3_BLOCK_SIZE_SHIFT_8M, + PS3_BLOCK_SIZE_SHIFT_16M, + PS3_BLOCK_SIZE_SHIFT_32M = 25, + PS3_BLOCK_SIZE_SHIFT_64M, + PS3_BLOCK_SIZE_SHIFT_128M, + PS3_BLOCK_SIZE_SHIFT_256M, + PS3_BLOCK_SIZE_SHIFT_512M, + PS3_BLOCK_SIZE_SHIFT_1G = 30, + PS3_BLOCK_SIZE_SHIFT_2G, + PS3_BLOCK_SIZE_SHIFT_4G, + PS3_BLOCK_SIZE_SHIFT_8G, + PS3_BLOCK_SIZE_SHIFT_16G, + PS3_BLOCK_SIZE_SHIFT_32G = 35, + PS3_BLOCK_SIZE_SHIFT_64G, + PS3_BLOCK_SIZE_SHIFT_128G, + PS3_BLOCK_SIZE_SHIFT_256G, + PS3_BLOCK_SIZE_SHIFT_512G, + PS3_BLOCK_SIZE_SHIFT_1T = 40, + PS3_BLOCK_SIZE_SHIFT_2T, + PS3_BLOCK_SIZE_SHIFT_4T, + PS3_BLOCK_SIZE_SHIFT_8T, + PS3_BLOCK_SIZE_SHIFT_16T, + PS3_BLOCK_SIZE_SHIFT_32T = 45, +}; + +static inline unsigned int ps3_blocksize_to_shift(unsigned int block_size) +{ + unsigned int shift = 0; + + switch (block_size) { + case PS3_BLOCK_SIZE_16: + shift = PS3_BLOCK_SIZE_SHIFT_4; + break; + case PS3_BLOCK_SIZE_32: + shift = PS3_BLOCK_SIZE_SHIFT_5; + break; + case PS3_BLOCK_SIZE_64: + shift = PS3_BLOCK_SIZE_SHIFT_6; + break; + case PS3_BLOCK_SIZE_128: + shift = PS3_BLOCK_SIZE_SHIFT_7; + break; + case PS3_BLOCK_SIZE_256: + shift = PS3_BLOCK_SIZE_SHIFT_8; + break; + case PS3_BLOCK_SIZE_512: + shift = PS3_BLOCK_SIZE_SHIFT_9; + break; + case PS3_BLOCK_SIZE_1024: + shift = PS3_BLOCK_SIZE_SHIFT_10; + break; + case PS3_BLOCK_SIZE_2048: + shift = PS3_BLOCK_SIZE_SHIFT_11; + break; + case PS3_BLOCK_SIZE_4096: + shift = PS3_BLOCK_SIZE_SHIFT_12; + break; + case PS3_BLOCK_SIZE_8192: + shift = PS3_BLOCK_SIZE_SHIFT_13; + break; + default: + PS3_BUG(); + break; + } + + return shift; +} + +static inline unsigned int ps3_ringsize_to_shift(unsigned long long ring_size) +{ + unsigned int shift = 0; + + switch (ring_size) { + case PS3_BLOCK_SIZE_512: + shift = PS3_BLOCK_SIZE_SHIFT_9; + break; + case PS3_BLOCK_SIZE_1024: + shift = PS3_BLOCK_SIZE_SHIFT_10; + break; + case PS3_BLOCK_SIZE_2048: + shift = PS3_BLOCK_SIZE_SHIFT_11; + break; + case PS3_BLOCK_SIZE_4096: + shift = PS3_BLOCK_SIZE_SHIFT_12; + break; + case PS3_BLOCK_SIZE_8192: + shift = PS3_BLOCK_SIZE_SHIFT_13; + break; + case PS3_BLOCK_SIZE_16K: + shift = PS3_BLOCK_SIZE_SHIFT_16K; + break; + case PS3_BLOCK_SIZE_32K: + shift = PS3_BLOCK_SIZE_SHIFT_32K; + break; + case PS3_BLOCK_SIZE_64K: + shift = PS3_BLOCK_SIZE_SHIFT_64K; + break; + case PS3_BLOCK_SIZE_128K: + shift = PS3_BLOCK_SIZE_SHIFT_128K; + break; + case PS3_BLOCK_SIZE_256K: + shift = PS3_BLOCK_SIZE_SHIFT_256K; + break; + case PS3_BLOCK_SIZE_512K: + shift = PS3_BLOCK_SIZE_SHIFT_512K; + break; + case PS3_BLOCK_SIZE_1M: + shift = PS3_BLOCK_SIZE_SHIFT_1M; + break; + case PS3_BLOCK_SIZE_2M: + shift = PS3_BLOCK_SIZE_SHIFT_2M; + break; + case PS3_BLOCK_SIZE_4M: + shift = PS3_BLOCK_SIZE_SHIFT_4M; + break; + case PS3_BLOCK_SIZE_8M: + shift = PS3_BLOCK_SIZE_SHIFT_8M; + break; + case PS3_BLOCK_SIZE_16M: + shift = PS3_BLOCK_SIZE_SHIFT_16M; + break; + case PS3_BLOCK_SIZE_32M: + shift = PS3_BLOCK_SIZE_SHIFT_32M; + break; + case PS3_BLOCK_SIZE_64M: + shift = PS3_BLOCK_SIZE_SHIFT_64M; + break; + case PS3_BLOCK_SIZE_128M: + shift = PS3_BLOCK_SIZE_SHIFT_128M; + break; + case PS3_BLOCK_SIZE_256M: + shift = PS3_BLOCK_SIZE_SHIFT_256M; + break; + case PS3_BLOCK_SIZE_512M: + shift = PS3_BLOCK_SIZE_SHIFT_512M; + break; + case PS3_BLOCK_SIZE_1G: + shift = PS3_BLOCK_SIZE_SHIFT_1G; + break; + case PS3_BLOCK_SIZE_2G: + shift = PS3_BLOCK_SIZE_SHIFT_2G; + break; + case PS3_BLOCK_SIZE_4G: + shift = PS3_BLOCK_SIZE_SHIFT_4G; + break; + case PS3_BLOCK_SIZE_8G: + shift = PS3_BLOCK_SIZE_SHIFT_8G; + break; + case PS3_BLOCK_SIZE_16G: + shift = PS3_BLOCK_SIZE_SHIFT_16G; + break; + case PS3_BLOCK_SIZE_32G: + shift = PS3_BLOCK_SIZE_SHIFT_32G; + break; + case PS3_BLOCK_SIZE_64G: + shift = PS3_BLOCK_SIZE_SHIFT_64G; + break; + case PS3_BLOCK_SIZE_128G: + shift = PS3_BLOCK_SIZE_SHIFT_128G; + break; + case PS3_BLOCK_SIZE_256G: + shift = PS3_BLOCK_SIZE_SHIFT_256G; + break; + case PS3_BLOCK_SIZE_512G: + shift = PS3_BLOCK_SIZE_SHIFT_512G; + break; + case PS3_BLOCK_SIZE_1T: + shift = PS3_BLOCK_SIZE_SHIFT_1T; + break; + case PS3_BLOCK_SIZE_2T: + shift = PS3_BLOCK_SIZE_SHIFT_2T; + break; + case PS3_BLOCK_SIZE_4T: + shift = PS3_BLOCK_SIZE_SHIFT_4T; + break; + case PS3_BLOCK_SIZE_8T: + shift = PS3_BLOCK_SIZE_SHIFT_8T; + break; + case PS3_BLOCK_SIZE_16T: + shift = PS3_BLOCK_SIZE_SHIFT_16T; + break; + case PS3_BLOCK_SIZE_32T: + shift = PS3_BLOCK_SIZE_SHIFT_32T; + break; + default: + shift = PS3_BLOCK_SIZE_SHIFT_1T; + break; + } + return shift; +} + +static unsigned char ps3_dma_addr_bit_pos_check(dma_addr_t handle) +{ + unsigned char bit_pos = 0; + + if (PCIE_DMA_HOST_ADDR_BIT54_MASK_CHECK(handle)) + bit_pos = PCIE_DMA_HOST_ADDR_BIT_POS_F0; + else if (PCIE_DMA_HOST_ADDR_BIT53_MASK_CHECK(handle)) + bit_pos = PCIE_DMA_HOST_ADDR_BIT_POS_F1; + else + bit_pos = PCIE_DMA_HOST_ADDR_BIT_POS; + return bit_pos; +} + +static inline struct dma_pool *ps3_dma_pool_create(const char *name, + struct device *dev, + size_t size, size_t align, + size_t boundary) +{ + struct dma_pool *pool = NULL; + + pool = dma_pool_create(name, dev, size, align, boundary); + LOG_INFO( + "create dma pool:name '%s', size %lu, align %lu, boundary %lu, pool %p\n", + name, (unsigned long)size, (unsigned long)align, + (unsigned long)boundary, pool); + return pool; +} + +static inline void *ps3_dma_pool_alloc(struct ps3_instance *instance, + struct dma_pool *pool, gfp_t mem_flags, + dma_addr_t *handle) +{ + void *ret = dma_pool_alloc(pool, mem_flags, handle); + + if (ret != NULL) { + *handle = PCIE_DMA_HOST_ADDR_BIT_POS_SET_NEW( + instance->dma_addr_bit_pos, *handle); + } + return ret; +} + +static inline void *ps3_dma_pool_zalloc(struct ps3_instance *instance, + struct dma_pool *pool, gfp_t mem_flags, + dma_addr_t *handle) +{ + void *ret = dma_pool_zalloc(pool, mem_flags, handle); + *handle = PCIE_DMA_HOST_ADDR_BIT_POS_SET_NEW(instance->dma_addr_bit_pos, + *handle); + return ret; +} + +static inline void ps3_dma_pool_destroy(struct dma_pool *pool) +{ + dma_pool_destroy(pool); + LOG_INFO("pool destroy %p\n", pool); +} + +static inline void ps3_dma_pool_free(struct dma_pool *pool, void *vaddr, + dma_addr_t dma) +{ + unsigned char bit_pos = 0; + + bit_pos = ps3_dma_addr_bit_pos_check(dma); + dma_pool_free(pool, vaddr, + PCIE_DMA_HOST_ADDR_BIT_POS_CLEAR_NEW(bit_pos, dma)); +} + +static inline unsigned int ps3_scsi_channel_query(struct scsi_cmnd *scmd) +{ + unsigned int ret = U32_MAX; + + if (scmd == NULL) + goto l_out; + + if (scmd->device == NULL) + goto l_out; + + ret = scmd->device->channel; +l_out: + return ret; +} + +static inline unsigned int ps3_scsi_target_query(struct scsi_cmnd *scmd) +{ + unsigned int ret = U32_MAX; + + if (scmd == NULL) + goto l_out; + + if (scmd->device == NULL) + goto l_out; + + ret = scmd->device->id; +l_out: + return ret; +} + +static inline void *ps3_dma_alloc_coherent(struct ps3_instance *instance, + size_t size, + unsigned long long *handle) +{ + void *buffer = NULL; + + buffer = dma_alloc_coherent(&instance->pdev->dev, size, + (dma_addr_t *)handle, GFP_KERNEL); + *handle = PCIE_DMA_HOST_ADDR_BIT_POS_SET_NEW(instance->dma_addr_bit_pos, + *handle); + return buffer; +} + +static inline void ps3_dma_free_coherent(struct ps3_instance *instance, + size_t size, void *vaddr, + unsigned long long dma_handle) +{ + unsigned char bit_pos = 0; + + bit_pos = ps3_dma_addr_bit_pos_check(dma_handle); + dma_free_coherent(&instance->pdev->dev, size, vaddr, + PCIE_DMA_HOST_ADDR_BIT_POS_CLEAR_NEW(bit_pos, + dma_handle)); +} + +static inline unsigned int ps3_utility_mod64(unsigned long long dividend, + unsigned int divisor) +{ + unsigned long long d = dividend; + unsigned int remainder = 0; + + if (!divisor) { + LOG_ERROR("DIVISOR is zero, in div fn\n"); + return (unsigned int)-1; + } + +#ifndef _WINDOWS + remainder = do_div(d, divisor); +#else + remainder = d % divisor; +#endif + return remainder; +} + +static inline unsigned long long +ps3_utility_div64_32(unsigned long long dividend, unsigned int divisor) +{ + unsigned long long d = dividend; + + if (!divisor) + LOG_ERROR("DIVISOR is zero, in div64_32 fn\n"); + + do_div(d, divisor); + + return d; +} + +#if defined(PS3_STRSCPY) +#define PS3_STRCPY(dest, src, n) strscpy(dest, src, n) +#else +#endif + +#if defined(PS3_KMAP_LOCAL) +#define PS3_KMAP(page) kmap_local_page(page) +#else +#endif + +#if defined(PS3_KUNMAP_LOCAL) +#define PS3_KUNMAP(page, buff) kunmap_local(buff) +#else +#endif + +#if defined(CONFIG_64BIT) +#define PS3_DIV64_32(dividend, divisor) ((dividend) / (divisor)) +#define PS3_MOD64(dividend, divisor) ((dividend) % (divisor)) +#else +#define PS3_DIV64_32(dividend, divisor) \ + (ps3_utility_div64_32((dividend), (divisor))) +#define PS3_MOD64(dividend, divisor) (ps3_utility_mod64((dividend), (divisor))) +#endif + +#endif diff --git a/drivers/scsi/linkdata/ps3stor/ps3_watchdog.c b/drivers/scsi/linkdata/ps3stor/ps3_watchdog.c new file mode 100644 index 0000000000000000000000000000000000000000..3046d6814de4f5e03fc863d1507ea5d5025c2f02 --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/ps3_watchdog.c @@ -0,0 +1,369 @@ +// SPDX-License-Identifier: GPL-2.0 +#ifndef _WINDOWS +#include +#include +#include +#include +#include + +#include +#endif + +#include "ps3_instance_manager.h" +#include "ps3_ioc_state.h" +#include "ps3_mgr_cmd_err.h" +#include "ps3_watchdog.h" +#include "ps3_driver_log.h" +#include "ps3_dump.h" +#include "ps3_module_para.h" +#include "ps3_ioc_manager.h" + +#define PS3_WATCHDOG_NAME_MAX_LENGTH (48) +static int ps3_watchdog_fault_detect_and_recovery(struct ps3_instance *instance) +{ + unsigned int ioc_recovery_count = 0; + unsigned int ioc_state = PS3_FW_STATE_UNDEFINED; + int ret = PS3_SUCCESS; + unsigned int heartbeat_triggered = 0; + + if (ps3_pci_err_recovery_get(instance)) { + LOG_WARN_LIM("hno:%u pci recovery resetting\n", + PS3_HOST(instance)); + goto l_out; + } + + if (!ps3_ioc_recovery_count_get(instance, &ioc_recovery_count)) { + ps3_atomic_inc(&instance->watchdog_reg_read_fail_count); + LOG_ERROR_LIM("hno:%u get recovery count NOK, cnt: %d\n", + PS3_HOST(instance), + ps3_atomic_read( + &instance->watchdog_reg_read_fail_count)); + goto l_out; + } + + if (!ps3_ioc_state_get_with_check(instance, &ioc_state)) { + ps3_atomic_inc(&instance->watchdog_reg_read_fail_count); + LOG_ERROR_LIM("hno:%u get ioc state NOK, cnt: %u\n", + PS3_HOST(instance), + ps3_atomic_read( + &instance->watchdog_reg_read_fail_count)); + goto l_out; + } + + ps3_atomic_set(&instance->watchdog_reg_read_fail_count, 0); + + if (instance->ioc_adpter->ioc_heartbeat_detect) { + if ((instance->recovery_context->heartbeat_recovery == + PS3_HEARTBEAT_HARDRESET_DECIDE || + instance->recovery_context->heartbeat_recovery == + PS3_HEARTBEAT_HARDRESET_RECOVERY || + instance->recovery_context->heartbeat_recovery == + PS3_HEARTBEAT_HARDRESET_RETRY) && + (instance->recovery_context->recovery_state == + PS3_HARD_RECOVERY_SHALLOW || + instance->recovery_context->recovery_state == + PS3_HARD_RECOVERY_DECIDE)) { + goto l_out; + } + + heartbeat_triggered = + instance->ioc_adpter->ioc_heartbeat_detect(instance); + if (heartbeat_triggered == PS3_TRUE && + !ps3_pci_err_recovery_get(instance)) { + LOG_INFO("hno:%u heartbeat recovery triggered\n", + PS3_HOST(instance)); + ps3_mutex_lock(&instance->recovery_context + ->ps3_watchdog_recovery_mutex); + if (instance->recovery_context->heartbeat_recovery == + PS3_HEARTBEAT_NULL) { + instance->recovery_context->heartbeat_recovery = + PS3_HEARTBEAT_HARDRESET_DECIDE; + } + ps3_mutex_unlock( + &instance->recovery_context + ->ps3_watchdog_recovery_mutex); + goto l_recovery; + } + } + + if (ioc_state == PS3_FW_STATE_FAULT || + ioc_state == PS3_FW_STATE_CRITICAL) { + goto l_recovery; + } + + if ((ioc_state == PS3_FW_STATE_RUNNING) && + (instance->recovery_context->ioc_recovery_count == + ioc_recovery_count)) { + goto l_out; + } + + if (ioc_state == PS3_FW_STATE_RESET) + goto l_out; + + if (ioc_recovery_count == PS3_IOC_RECOVERY_COUNT_MASK) + goto l_out; + + if (ioc_state == PS3_FW_STATE_UNDEFINED) + goto l_out; + + if (ioc_state == PS3_FW_STATE_MASK) + goto l_out; + +l_recovery: + LOG_INFO( + "hno:%u watchdog recovery req, ioc_state[0x%x] ioc_reco_cnt[%u], local_reco_cnt[%u]\n", + PS3_HOST(instance), ioc_state, ioc_recovery_count, + instance->recovery_context->ioc_recovery_count); + + + if (ps3_atomic_read(&instance->state_machine.state) != + PS3_INSTANCE_STATE_DEAD) { + if (ps3_need_block_hard_reset_request(instance) || + PS3_IS_INSTANCE_NOT_LOAD_NORMAL(instance) || + instance->recovery_context->recovery_state == + PS3_HARD_RECOVERY_SHALLOW || + instance->recovery_context->recovery_state == + PS3_HARD_RECOVERY_DECIDE) { + LOG_WARN_LIM( + "hno:%u can not start reset request recovery_state:%d\n", + PS3_HOST(instance), + instance->recovery_context->recovery_state); + } else { + LOG_WARN( + "hno:%u ps3 watchdog recovery start, heartbeat_triggered:%u ioc_state:0x%x\n", + PS3_HOST(instance), heartbeat_triggered, + ioc_state); + if (ps3_recovery_request(instance) == PS3_SUCCESS) { + LOG_INFO("hno[%u], recovery success!\n", + PS3_HOST(instance)); + ret = PS3_SUCCESS; + } else { + LOG_ERROR( + "hno[%u], recovery request NOK, %s!\n", + PS3_HOST(instance), + namePS3InstanceState(ps3_atomic_read( + &instance->state_machine + .state))); + return -PS3_FAILED; + } + } + } + +l_out: + if (ps3_atomic_read(&instance->watchdog_reg_read_fail_count) >= + PS3_TRANS_DEAD_IOC_FAILED_MAX_COUNT) { +#ifdef PS3_HARDWARE_SIM + LOG_WARN( + "hno:%u reg read failed 5 times consecutively, simu platform ignore\n", + PS3_HOST(instance)); +#else + ps3_instance_state_transfer_to_dead(instance); + instance->watchdog_context.is_stop = PS3_TRUE; + LOG_ERROR( + "hno:%u reg read failed 5 times consecutively, transfer to dead\n", + PS3_HOST(instance)); + ret = -PS3_FAILED; +#endif + } + + return ret; +} + +#ifndef _WINDOWS + +static void ps3_watchdog_service(struct work_struct *work) +{ + struct ps3_watchdog_context *watchdog_ctx = ps3_container_of( + work, struct ps3_watchdog_context, watchdog_work.work); + struct ps3_instance *instance = watchdog_ctx->instance; + struct delayed_work *delayed_work = &watchdog_ctx->watchdog_work; + int ret = PS3_SUCCESS; + + if (instance->recovery_context->recovery_state == + PS3_HARD_RECOVERY_SHALLOW || + instance->recovery_context->recovery_state == + PS3_HARD_RECOVERY_DECIDE) { + watchdog_ctx->is_halt = PS3_TRUE; + goto l_next; + } + + watchdog_ctx->is_halt = PS3_FALSE; + if (instance->is_probe_finish && instance->state_machine.is_load) { + ret = ps3_watchdog_fault_detect_and_recovery(instance); + if (ret != PS3_SUCCESS) { + LOG_ERROR( + "hno:%u watchdog fault detect and recovery NOK watchdog service exit\n", + PS3_HOST(instance)); + } + } +l_next: + + if (watchdog_ctx->is_stop) { + LOG_INFO("hno:%u watchdog has stop\n", PS3_HOST(instance)); + return; + } + + ps3_qos_waitq_poll(instance); + + if (watchdog_ctx->watchdog_queue != NULL) { + queue_delayed_work(watchdog_ctx->watchdog_queue, delayed_work, + msecs_to_jiffies(PS3_WATCHDOG_INTERVAL)); + } +} + +int ps3_watchdog_start(struct ps3_instance *instance) +{ + struct ps3_watchdog_context *watchdog_ctx = &instance->watchdog_context; + char watchdog_queue_name[PS3_WATCHDOG_NAME_MAX_LENGTH]; + struct delayed_work *delayed_work = &watchdog_ctx->watchdog_work; + + ps3_atomic_inc(&instance->watchdog_ref); + mb(); /* in order to force CPU ordering */ + + if (watchdog_ctx->is_stop == PS3_FALSE) { + LOG_DEBUG("hno:%u watchdog running\n", PS3_HOST(instance)); + ps3_atomic_dec(&instance->watchdog_ref); + return PS3_SUCCESS; + } + + if (watchdog_ctx->watchdog_queue != NULL) { + LOG_DEBUG("hno:%u watchdog for is already started!\n", + PS3_HOST(instance)); + ps3_atomic_dec(&instance->watchdog_ref); + goto l_out; + } + + memset(watchdog_queue_name, 0, PS3_WATCHDOG_NAME_MAX_LENGTH); + memset(watchdog_ctx, 0, sizeof(struct ps3_watchdog_context)); + INIT_DELAYED_WORK(delayed_work, ps3_watchdog_service); + + snprintf(watchdog_queue_name, PS3_WATCHDOG_NAME_MAX_LENGTH, + "ps3_watchdog_service_host%d", instance->host->host_no); + + watchdog_ctx->watchdog_queue = + create_singlethread_workqueue(watchdog_queue_name); + if (watchdog_ctx->watchdog_queue == NULL) { + LOG_ERROR("hno:%u watchdog work queue create failed\n", + PS3_HOST(instance)); + ps3_atomic_dec(&instance->watchdog_ref); + return -PS3_FAILED; + } + + watchdog_ctx->instance = instance; + watchdog_ctx->is_stop = PS3_FALSE; + queue_delayed_work(watchdog_ctx->watchdog_queue, delayed_work, + msecs_to_jiffies(PS3_WATCHDOG_INTERVAL)); + ps3_atomic_dec(&instance->watchdog_ref); + +l_out: + return PS3_SUCCESS; +} + +void ps3_watchdog_stop(struct ps3_instance *instance) +{ + struct ps3_watchdog_context *watchdog_ctx = &instance->watchdog_context; + struct workqueue_struct *watchdog_queue; + struct delayed_work *delayed_work = &watchdog_ctx->watchdog_work; + + if (watchdog_ctx->is_stop == PS3_TRUE) + return; + + watchdog_ctx->is_stop = PS3_TRUE; + mb(); /* in order to force CPU ordering */ + while (ps3_atomic_read(&instance->watchdog_ref) != 0) + ps3_msleep(PS3_LOOP_TIME_INTERVAL_100MS); + + if (watchdog_ctx->watchdog_queue != NULL) { + watchdog_queue = watchdog_ctx->watchdog_queue; + if (!cancel_delayed_work_sync(delayed_work)) + flush_workqueue(watchdog_queue); + + destroy_workqueue(watchdog_queue); + watchdog_ctx->watchdog_queue = NULL; + LOG_INFO("hno:%u watchdog destroy work and stop\n", + PS3_HOST(instance)); + } +} +#else + +static void ps3_watchdog_service(void *ins) +{ + struct ps3_instance *instance = (struct ps3_instance *)ins; + struct ps3_watchdog_context *watchdog_ctx = &instance->watchdog_context; + struct ps3_delay_worker *watchdog_work = &watchdog_ctx->watchdog_work; + int ret = PS3_SUCCESS; + + if (ps3_atomic_read(&instance->state_machine.state) != + PS3_INSTANCE_STATE_OPERATIONAL && + (instance->recovery_context->recovery_state == + PS3_HARD_RECOVERY_SHALLOW || + instance->recovery_context->recovery_state == + PS3_HARD_RECOVERY_DECIDE)) { + watchdog_ctx->is_halt = PS3_TRUE; + goto l_next; + } + + watchdog_ctx->is_halt = PS3_FALSE; + + if (instance->is_probe_finish) { + ret = ps3_watchdog_fault_detect_and_recovery(instance); + if (ret != PS3_SUCCESS) { + LOG_ERROR( + "hno:%u watchdog fault detect and recovery failed watchdog service exit\n", + PS3_HOST(instance)); + } + } +l_next: + + if (watchdog_ctx->is_stop) { + LOG_DEBUG("hno:%u watchdog has stop\n", PS3_HOST(instance)); + return; + } + + ps3_dump_detect(instance); + + ps3_delay_worker_start(watchdog_work, PS3_WATCHDOG_INTERVAL); +} + +int ps3_watchdog_start(struct ps3_instance *instance) +{ + int ret = PS3_SUCCESS; + struct ps3_watchdog_context *watchdog_ctx = &instance->watchdog_context; + struct ps3_delay_worker *watchdog_work = &watchdog_ctx->watchdog_work; + + memset(watchdog_ctx, 0, sizeof(struct ps3_watchdog_context)); + + if (ps3_delay_worker_init(instance, watchdog_work, + "ps3_watchdog_service", + ps3_watchdog_service) != PS3_SUCCESS) { + LOG_ERROR("hno:%u timer init failed\n", PS3_HOST(instance)); + ret = -PS3_FAILED; + goto l_out; + } + + watchdog_ctx->is_stop = PS3_FALSE; + if (ps3_delay_worker_start(watchdog_work, PS3_WATCHDOG_INTERVAL) != + PS3_SUCCESS) { + LOG_ERROR("hno:%u timer request failed\n", PS3_HOST(instance)); + ret = -PS3_FAILED; + watchdog_ctx->is_stop = PS3_TRUE; + goto l_out; + } + +l_out: + return PS3_SUCCESS; +} + +void ps3_watchdog_stop(struct ps3_instance *instance) +{ + struct ps3_watchdog_context *watchdog_ctx = &instance->watchdog_context; + struct ps3_delay_worker *watchdog_work = &watchdog_ctx->watchdog_work; + + watchdog_ctx->is_stop = PS3_TRUE; + ps3_delay_worker_exit(watchdog_work); + + memset(watchdog_ctx, 0, sizeof(struct ps3_watchdog_context)); + LOG_WARN("hno:%u watchdog destroy work and stop\n", + PS3_HOST(instance)); +} +#endif diff --git a/drivers/scsi/linkdata/ps3stor/ps3_watchdog.h b/drivers/scsi/linkdata/ps3stor/ps3_watchdog.h new file mode 100644 index 0000000000000000000000000000000000000000..cc1098db478009dc10a7a02198579874b3a61c5e --- /dev/null +++ b/drivers/scsi/linkdata/ps3stor/ps3_watchdog.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _PS3_WATCHDOG_H_ +#define _PS3_WATCHDOG_H_ + +#ifndef _WINDOWS +#include +#include +#include +#else +#include "ps3_worker.h" +#endif + +#include "ps3_htp_def.h" +#include "ps3_instance_manager.h" +#define PS3_WATCHDOG_INTERVAL (1000) + +struct ps3_watchdog_context { +#ifndef _WINDOWS + struct delayed_work watchdog_work; + struct workqueue_struct *watchdog_queue; + struct ps3_instance *instance; +#else + struct ps3_delay_worker watchdog_work; +#endif + unsigned char is_stop; + unsigned char is_halt; +}; + +int ps3_watchdog_start(struct ps3_instance *instance); +void ps3_watchdog_stop(struct ps3_instance *instance); + +#endif