scsi: smartpqi: Add support for CCISS_BIG_PASSTHRU ioctl

Add pqi_big_passthru_ioctl() to handle CCISS_BIG_PASSTHRU ioctl
requests. The existing passthru ioctl uses a 16-bit integer for the I/O
buffer size, limiting transfers to 64KB. The big passthru ioctl uses
BIG_IOCTL_Command_struct which stores the buffer size as a 32-bit integer,
allowing larger transfers required by some management utilities.

Add CCISS_BIG_PASSTHRU_SUPPORTED to uapi/linux/cciss_ioctl.h and return 0
from pqi_ioctl() to advertise driver support.  Userspace tools can send
this ioctl to probe whether the driver supports CCISS_BIG_PASSTHRU before
issuing it.

Co-developed-by: Mike McGowen <mike.mcgowen@microchip.com>
Signed-off-by: Mike McGowen <mike.mcgowen@microchip.com>
Signed-off-by: David Strahan <david.strahan@microchip.com>
Acked-by: Don Brace <don.brace@microchip.com>
Link: https://lore.kernel.org/linux-scsi/20260722220401.6357-3-david.strahan@microchip.com/
Link: https://lore.kernel.org/linux-scsi/20260722220401.6357-1-david.strahan@microchip.com/
Link: https://patch.msgid.link/20260722220401.6357-3-david.strahan@microchip.com
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
This commit is contained in:
David Strahan
2026-07-22 17:03:59 -05:00
committed by Martin K. Petersen
parent 225548863f
commit c64b2ab2ff
2 changed files with 145 additions and 0 deletions

View File

@@ -6935,6 +6935,144 @@ static int pqi_passthru_ioctl(struct pqi_ctrl_info *ctrl_info, void __user *arg)
return rc;
}
static int pqi_big_passthru_ioctl(struct pqi_ctrl_info *ctrl_info, void __user *arg)
{
int rc;
char *kernel_buffer = NULL;
u16 iu_length;
size_t sense_data_length;
BIG_IOCTL_Command_struct iocommand;
struct pqi_raid_path_request request;
struct pqi_raid_error_info pqi_error_info;
struct ciss_error_info ciss_error_info;
if (pqi_ctrl_offline(ctrl_info))
return -ENXIO;
if (pqi_ofa_in_progress(ctrl_info) && pqi_ctrl_blocked(ctrl_info))
return -EBUSY;
if (!arg)
return -EINVAL;
if (!capable(CAP_SYS_RAWIO))
return -EPERM;
if (copy_from_user(&iocommand, arg, sizeof(iocommand)))
return -EFAULT;
if (iocommand.buf_size < 1 &&
iocommand.Request.Type.Direction != XFER_NONE)
return -EINVAL;
if (iocommand.Request.CDBLen > sizeof(request.cdb))
return -EINVAL;
if (iocommand.Request.Type.Type != TYPE_CMD)
return -EINVAL;
switch (iocommand.Request.Type.Direction) {
case XFER_NONE:
case XFER_WRITE:
case XFER_READ:
case XFER_READ | XFER_WRITE:
break;
default:
return -EINVAL;
}
if (iocommand.buf_size > 0) {
kernel_buffer = kmalloc(iocommand.buf_size, GFP_KERNEL);
if (!kernel_buffer)
return -ENOMEM;
if (iocommand.Request.Type.Direction & XFER_WRITE) {
if (copy_from_user(kernel_buffer, iocommand.buf,
iocommand.buf_size)) {
rc = -EFAULT;
goto out;
}
} else {
memset(kernel_buffer, 0, iocommand.buf_size);
}
}
memset(&request, 0, sizeof(request));
request.header.iu_type = PQI_REQUEST_IU_RAID_PATH_IO;
iu_length = offsetof(struct pqi_raid_path_request, sg_descriptors) -
PQI_REQUEST_HEADER_LENGTH;
memcpy(request.lun_number, iocommand.LUN_info.LunAddrBytes, sizeof(request.lun_number));
memcpy(request.cdb, iocommand.Request.CDB, iocommand.Request.CDBLen);
request.additional_cdb_bytes_usage = SOP_ADDITIONAL_CDB_BYTES_0;
switch (iocommand.Request.Type.Direction) {
case XFER_NONE:
request.data_direction = SOP_NO_DIRECTION_FLAG;
break;
case XFER_WRITE:
request.data_direction = SOP_WRITE_FLAG;
break;
case XFER_READ:
request.data_direction = SOP_READ_FLAG;
break;
case XFER_READ | XFER_WRITE:
request.data_direction = SOP_BIDIRECTIONAL;
break;
}
request.task_attribute = SOP_TASK_ATTRIBUTE_SIMPLE;
if (iocommand.buf_size > 0) {
put_unaligned_le32(iocommand.buf_size, &request.buffer_length);
rc = pqi_map_single(ctrl_info->pci_dev,
&request.sg_descriptors[0], kernel_buffer,
iocommand.buf_size, DMA_BIDIRECTIONAL);
if (rc)
goto out;
iu_length += sizeof(request.sg_descriptors[0]);
}
put_unaligned_le16(iu_length, &request.header.iu_length);
if (ctrl_info->raid_iu_timeout_supported)
put_unaligned_le32(iocommand.Request.Timeout, &request.timeout);
rc = pqi_submit_raid_request_synchronous(ctrl_info, &request.header,
PQI_SYNC_FLAGS_INTERRUPTABLE, &pqi_error_info);
if (iocommand.buf_size > 0)
pqi_pci_unmap(ctrl_info->pci_dev, request.sg_descriptors, 1, DMA_BIDIRECTIONAL);
memset(&iocommand.error_info, 0, sizeof(iocommand.error_info));
if (rc == 0) {
pqi_error_info_to_ciss(&pqi_error_info, &ciss_error_info);
iocommand.error_info.ScsiStatus = ciss_error_info.scsi_status;
iocommand.error_info.CommandStatus = ciss_error_info.command_status;
sense_data_length = ciss_error_info.sense_data_length;
if (sense_data_length) {
if (sense_data_length > sizeof(iocommand.error_info.SenseInfo))
sense_data_length = sizeof(iocommand.error_info.SenseInfo);
memcpy(iocommand.error_info.SenseInfo,
pqi_error_info.data, sense_data_length);
iocommand.error_info.SenseLen = sense_data_length;
}
}
if (copy_to_user(arg, &iocommand, sizeof(iocommand))) {
rc = -EFAULT;
goto out;
}
if (rc == 0 && iocommand.buf_size > 0 &&
(iocommand.Request.Type.Direction & XFER_READ)) {
if (copy_to_user(iocommand.buf, kernel_buffer,
iocommand.buf_size)) {
rc = -EFAULT;
}
}
out:
kfree(kernel_buffer);
return rc;
}
static int pqi_ioctl(struct scsi_device *sdev, unsigned int cmd,
void __user *arg)
{
@@ -6958,6 +7096,12 @@ static int pqi_ioctl(struct scsi_device *sdev, unsigned int cmd,
case CCISS_PASSTHRU:
rc = pqi_passthru_ioctl(ctrl_info, arg);
break;
case CCISS_BIG_PASSTHRU:
rc = pqi_big_passthru_ioctl(ctrl_info, arg);
break;
case CCISS_BIG_PASSTHRU_SUPPORTED:
rc = 0;
break;
default:
rc = -EINVAL;
break;

View File

@@ -85,5 +85,6 @@ typedef struct _LogvolInfo_struct{
#define CCISS_RESCANDISK _IO(CCISS_IOC_MAGIC, 16)
#define CCISS_GETLUNINFO _IOR(CCISS_IOC_MAGIC, 17, LogvolInfo_struct)
#define CCISS_BIG_PASSTHRU _IOWR(CCISS_IOC_MAGIC, 18, BIG_IOCTL_Command_struct)
#define CCISS_BIG_PASSTHRU_SUPPORTED _IO(CCISS_IOC_MAGIC, 19)
#endif /* _UAPICCISS_IOCTLH */