diff options
| author | Ranjan Kumar <ranjan.kumar@broadcom.com> | 2026-09-16 13:56:59 +0530 |
|---|---|---|
| committer | Martin K. Petersen (Oracle) <mkp@kernel.org> | 2026-09-16 22:14:22 -0400 |
| commit | fb8b6139770a5dee8ace9e605b5c3d11861506c8 (patch) | |
| tree | 10f3315703e5a962c984d736d4f653b52a700610 /drivers/scsi | |
| parent | 48c666139acfef3bce5cc337b7d2cc2e089f87b5 (diff) | |
| download | linux-next-fb8b6139770a5dee8ace9e605b5c3d11861506c8.tar.gz linux-next-fb8b6139770a5dee8ace9e605b5c3d11861506c8.zip | |
scsi: mpi3mr: Fix NVMe page size caching for non-operational devices
For NVMe devices in an error state, the cached PCIe page size remains
unset. This causes management IOCTL validation to fail, preventing requests
from reaching firmware and returning incorrect errors to
userspace. Populate the page size attribute irrespective of device access
status so firmware can process IOCTLs and report appropriate errors.
Additionally, harden the device initialization path against invalid
firmware data for non-operational devices:
1. Add bounds checking for page_size, falling back to 4096 bytes (shift
exponent 12) to prevent undefined shift behavior and kernel panics.
The minimum valid NVMe page size shift is 12 and maximum is 27.
2. Initialize reset_to and abort_to timeouts with default values to
prevent IOCTLs from failing instantly. To avoid race conditions where
concurrent readers might observe these default timeouts before they are
updated with firmware values, use local variables to compute the final
values before writing them to the device structure.
3. Read the firmware-provided page size once into a local variable before
validating and using it, since it lives in memory the device can also
write to. Using it directly in both the check and the assignment
allowed the two to observe different values.
Reported-by: Sashiko <sashiko-bot@kernel.org>
Closes: https://sashiko.dev/#/patchset/20260626114109.43685-1-ranjan.kumar@broadcom.com?part=4
Closes: https://sashiko.dev/#/patchset/20260708183305.244485-1-ranjan.kumar@broadcom.com?part=4
Closes: https://sashiko.dev/#/patchset/20260724102505.115136-1-ranjan.kumar@broadcom.com?part=4
Closes: https://sashiko.dev/#/patchset/20260805110634.346670-1-ranjan.kumar@broadcom.com?part=4
Co-developed-by: Chandrakanth Patil <chandrakanth.patil@broadcom.com>
Signed-off-by: Chandrakanth Patil <chandrakanth.patil@broadcom.com>
Signed-off-by: Ranjan Kumar <ranjan.kumar@broadcom.com>
Link: https://patch.msgid.link/20260916082705.44712-5-ranjan.kumar@broadcom.com
Signed-off-by: Martin K. Petersen (Oracle) <mkp@kernel.org>
Diffstat (limited to 'drivers/scsi')
| -rw-r--r-- | drivers/scsi/mpi3mr/mpi3mr.h | 1 | ||||
| -rw-r--r-- | drivers/scsi/mpi3mr/mpi3mr_os.c | 24 |
2 files changed, 17 insertions, 8 deletions
diff --git a/drivers/scsi/mpi3mr/mpi3mr.h b/drivers/scsi/mpi3mr/mpi3mr.h index 1f2f0951b560..6128b30112e2 100644 --- a/drivers/scsi/mpi3mr/mpi3mr.h +++ b/drivers/scsi/mpi3mr/mpi3mr.h @@ -169,6 +169,7 @@ extern atomic64_t event_counter; #define MPI3MR_DEFAULT_MDTS (128 * 1024) #define MPI3MR_DEFAULT_PGSZEXP (12) +#define MPI3MR_MAX_PGSZEXP (27) /* Command retry count definitions */ #define MPI3MR_DEV_RMHS_RETRY_COUNT 3 diff --git a/drivers/scsi/mpi3mr/mpi3mr_os.c b/drivers/scsi/mpi3mr/mpi3mr_os.c index 07a2a45b4957..5506fc87f1ca 100644 --- a/drivers/scsi/mpi3mr/mpi3mr_os.c +++ b/drivers/scsi/mpi3mr/mpi3mr_os.c @@ -1350,24 +1350,32 @@ static void mpi3mr_update_tgtdev(struct mpi3mr_ioc *mrioc, struct mpi3_device0_pcie_format *pcieinf = &dev_pg0->device_specific.pcie_format; u16 dev_info = le16_to_cpu(pcieinf->device_info); + u8 pgsz = MPI3MR_DEFAULT_PGSZEXP; + u8 reset_to = MPI3MR_INTADMCMD_TIMEOUT; + u8 abort_to = MPI3MR_INTADMCMD_TIMEOUT; + u8 fw_pgsz = READ_ONCE(pcieinf->page_size); tgtdev->dev_spec.pcie_inf.dev_info = dev_info; tgtdev->dev_spec.pcie_inf.capb = le32_to_cpu(pcieinf->capabilities); tgtdev->dev_spec.pcie_inf.mdts = MPI3MR_DEFAULT_MDTS; - /* 2^12 = 4096 */ - tgtdev->dev_spec.pcie_inf.pgsz = 12; + /* Validate firmware page size to prevent undefined shift behavior */ + if (fw_pgsz >= MPI3MR_DEFAULT_PGSZEXP && fw_pgsz <= MPI3MR_MAX_PGSZEXP) + pgsz = fw_pgsz; + if (dev_pg0->access_status == MPI3_DEVICE0_ASTATUS_NO_ERRORS) { tgtdev->dev_spec.pcie_inf.mdts = le32_to_cpu(pcieinf->maximum_data_transfer_size); - tgtdev->dev_spec.pcie_inf.pgsz = pcieinf->page_size; - tgtdev->dev_spec.pcie_inf.reset_to = - max_t(u8, pcieinf->controller_reset_to, - MPI3MR_INTADMCMD_TIMEOUT); - tgtdev->dev_spec.pcie_inf.abort_to = - max_t(u8, pcieinf->nvme_abort_to, + reset_to = max_t(u8, pcieinf->controller_reset_to, + MPI3MR_INTADMCMD_TIMEOUT); + abort_to = max_t(u8, pcieinf->nvme_abort_to, MPI3MR_INTADMCMD_TIMEOUT); } + + tgtdev->dev_spec.pcie_inf.pgsz = pgsz; + tgtdev->dev_spec.pcie_inf.reset_to = reset_to; + tgtdev->dev_spec.pcie_inf.abort_to = abort_to; + if (tgtdev->dev_spec.pcie_inf.mdts > (1024 * 1024)) tgtdev->dev_spec.pcie_inf.mdts = (1024 * 1024); if (((dev_info & MPI3_DEVICE0_PCIE_DEVICE_INFO_TYPE_MASK) != |
