scsi: hisi_sas: Enable force phy when SATA disk directly connected

[ Upstream commit 8aa580cd92843b60d4d6331f3b0a9e8409bb70eb ]

when a SATA disk is directly connected the SAS controller determines the
disk to which I/Os are delivered based on the port ID in the DQ entry.

When many phys are disconnected and reconnect, the port ID of phys were
changed and used by other link, resulting in I/O being sent to incorrect
disk. Data inconsistency on the SATA disk may occur during I/O retries
using the old port ID. So enable force phy, then force the command to be
executed in a certain phy, and if the actual phy ID of the port does not
match the phy configured in the command, the chip will stop delivering the
I/O to disk.

Fixes: ce60689e12 ("scsi: hisi_sas: add v3 code to send ATA frame")
Signed-off-by: Xingui Yang <yangxingui@huawei.com>
Link: https://lore.kernel.org/r/20250312095135.3048379-2-yangxingui@huawei.com
Reviewed-by: Yihang Li <liyihang9@huawei.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
Xingui Yang
2025-03-12 17:51:34 +08:00
committed by Greg Kroah-Hartman
parent 58eb29dba7
commit a70ea92964
2 changed files with 19 additions and 4 deletions

View File

@@ -2501,6 +2501,7 @@ static void prep_ata_v2_hw(struct hisi_hba *hisi_hba,
struct hisi_sas_port *port = to_hisi_sas_port(sas_port); struct hisi_sas_port *port = to_hisi_sas_port(sas_port);
struct sas_ata_task *ata_task = &task->ata_task; struct sas_ata_task *ata_task = &task->ata_task;
struct sas_tmf_task *tmf = slot->tmf; struct sas_tmf_task *tmf = slot->tmf;
int phy_id;
u8 *buf_cmd; u8 *buf_cmd;
int has_data = 0, hdr_tag = 0; int has_data = 0, hdr_tag = 0;
u32 dw0, dw1 = 0, dw2 = 0; u32 dw0, dw1 = 0, dw2 = 0;
@@ -2508,10 +2509,14 @@ static void prep_ata_v2_hw(struct hisi_hba *hisi_hba,
/* create header */ /* create header */
/* dw0 */ /* dw0 */
dw0 = port->id << CMD_HDR_PORT_OFF; dw0 = port->id << CMD_HDR_PORT_OFF;
if (parent_dev && dev_is_expander(parent_dev->dev_type)) if (parent_dev && dev_is_expander(parent_dev->dev_type)) {
dw0 |= 3 << CMD_HDR_CMD_OFF; dw0 |= 3 << CMD_HDR_CMD_OFF;
else } else {
phy_id = device->phy->identify.phy_identifier;
dw0 |= (1U << phy_id) << CMD_HDR_PHY_ID_OFF;
dw0 |= CMD_HDR_FORCE_PHY_MSK;
dw0 |= 4 << CMD_HDR_CMD_OFF; dw0 |= 4 << CMD_HDR_CMD_OFF;
}
if (tmf && ata_task->force_phy) { if (tmf && ata_task->force_phy) {
dw0 |= CMD_HDR_FORCE_PHY_MSK; dw0 |= CMD_HDR_FORCE_PHY_MSK;

View File

@@ -358,6 +358,10 @@
#define CMD_HDR_RESP_REPORT_MSK (0x1 << CMD_HDR_RESP_REPORT_OFF) #define CMD_HDR_RESP_REPORT_MSK (0x1 << CMD_HDR_RESP_REPORT_OFF)
#define CMD_HDR_TLR_CTRL_OFF 6 #define CMD_HDR_TLR_CTRL_OFF 6
#define CMD_HDR_TLR_CTRL_MSK (0x3 << CMD_HDR_TLR_CTRL_OFF) #define CMD_HDR_TLR_CTRL_MSK (0x3 << CMD_HDR_TLR_CTRL_OFF)
#define CMD_HDR_PHY_ID_OFF 8
#define CMD_HDR_PHY_ID_MSK (0x1ff << CMD_HDR_PHY_ID_OFF)
#define CMD_HDR_FORCE_PHY_OFF 17
#define CMD_HDR_FORCE_PHY_MSK (0x1U << CMD_HDR_FORCE_PHY_OFF)
#define CMD_HDR_PORT_OFF 18 #define CMD_HDR_PORT_OFF 18
#define CMD_HDR_PORT_MSK (0xf << CMD_HDR_PORT_OFF) #define CMD_HDR_PORT_MSK (0xf << CMD_HDR_PORT_OFF)
#define CMD_HDR_PRIORITY_OFF 27 #define CMD_HDR_PRIORITY_OFF 27
@@ -1425,15 +1429,21 @@ static void prep_ata_v3_hw(struct hisi_hba *hisi_hba,
struct hisi_sas_cmd_hdr *hdr = slot->cmd_hdr; struct hisi_sas_cmd_hdr *hdr = slot->cmd_hdr;
struct asd_sas_port *sas_port = device->port; struct asd_sas_port *sas_port = device->port;
struct hisi_sas_port *port = to_hisi_sas_port(sas_port); struct hisi_sas_port *port = to_hisi_sas_port(sas_port);
int phy_id;
u8 *buf_cmd; u8 *buf_cmd;
int has_data = 0, hdr_tag = 0; int has_data = 0, hdr_tag = 0;
u32 dw1 = 0, dw2 = 0; u32 dw1 = 0, dw2 = 0;
hdr->dw0 = cpu_to_le32(port->id << CMD_HDR_PORT_OFF); hdr->dw0 = cpu_to_le32(port->id << CMD_HDR_PORT_OFF);
if (parent_dev && dev_is_expander(parent_dev->dev_type)) if (parent_dev && dev_is_expander(parent_dev->dev_type)) {
hdr->dw0 |= cpu_to_le32(3 << CMD_HDR_CMD_OFF); hdr->dw0 |= cpu_to_le32(3 << CMD_HDR_CMD_OFF);
else } else {
phy_id = device->phy->identify.phy_identifier;
hdr->dw0 |= cpu_to_le32((1U << phy_id)
<< CMD_HDR_PHY_ID_OFF);
hdr->dw0 |= CMD_HDR_FORCE_PHY_MSK;
hdr->dw0 |= cpu_to_le32(4U << CMD_HDR_CMD_OFF); hdr->dw0 |= cpu_to_le32(4U << CMD_HDR_CMD_OFF);
}
switch (task->data_dir) { switch (task->data_dir) {
case DMA_TO_DEVICE: case DMA_TO_DEVICE: