mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-09-01 12:38:03 -04:00
scsi: leapraid: Add new SCSI driver
The LeapRAID driver provides support for LeapRAID PCIe RAID controllers, enabling communication between the host operating system, firmware, and hardware for efficient storage management. The driver is organized into several logical modules, each with a clear responsibility: leapraid_os.c: Integrates with the Linux SCSI subsystem, handling host template callbacks, PCIe device probing, and initialization. leapraid_func.c: Contains low-level routines for firmware/hardware interaction, interrupt handling, and reset logic. leapraid_app.c: Provides the ioctl interface for user-space tools. leapraid_transport.c: Manages interactions with the SCSI transport layer for SAS PHYs and ports. leapraid_func.h: Contains internal definitions shared among driver modules. leapraid.h: Contains low-level hardware definitions for driver/firmware interaction. The leapraid_probe() function orchestrates the setup: it allocates the adapter structure and SCSI host, configures hardware interfaces, and registers it with the SCSI mid-layer. Following registration, scsi_scan_host() is invoked to initiate device discovery, with firmware reporting devices via interrupt-driven events. This initial commit provides the necessary infrastructure for subsequent development of full I/O path handling, error recovery, and advanced management features. Reviewed-by: Damien Le Moal <dlemoal@kernel.org> Reviewed-by: Hannes Reinecke <hare@kernel.org> Signed-off-by: Dongdong Hao <doubled@leap-io-kernel.com> Link: https://patch.msgid.link/0cbc6245aabdc6e8c90587675e76ba316c5b549e.1785823793.git.doubled@leap-io-kernel.com Signed-off-by: Martin K. Petersen (Oracle) <mkp@kernel.org>
This commit is contained in:
committed by
Martin K. Petersen (Oracle)
parent
dc59e4fea9
commit
5597088c9e
@@ -14650,6 +14650,13 @@ S: Maintained
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git for-next/hardening
|
||||
F: scripts/leaking_addresses.pl
|
||||
|
||||
LEAPIO SCSI RAID DRIVER
|
||||
M: Dongdong Hao <doubled@leap-io-kernel.com>
|
||||
L: linux-scsi@vger.kernel.org
|
||||
S: Supported
|
||||
F: Documentation/scsi/leapraid.rst
|
||||
F: drivers/scsi/leapraid/
|
||||
|
||||
LED SUBSYSTEM
|
||||
M: Lee Jones <lee@kernel.org>
|
||||
M: Pavel Machek <pavel@kernel.org>
|
||||
|
||||
@@ -499,6 +499,7 @@ source "drivers/scsi/esas2r/Kconfig"
|
||||
source "drivers/scsi/megaraid/Kconfig.megaraid"
|
||||
source "drivers/scsi/mpt3sas/Kconfig"
|
||||
source "drivers/scsi/mpi3mr/Kconfig"
|
||||
source "drivers/scsi/leapraid/Kconfig"
|
||||
source "drivers/scsi/smartpqi/Kconfig"
|
||||
|
||||
config SCSI_HPTIOP
|
||||
|
||||
@@ -91,6 +91,7 @@ obj-$(CONFIG_SCSI_BFA_FC) += bfa/
|
||||
obj-$(CONFIG_SCSI_CHELSIO_FCOE) += csiostor/
|
||||
obj-$(CONFIG_SCSI_DMX3191D) += dmx3191d.o
|
||||
obj-$(CONFIG_SCSI_HPSA) += hpsa.o
|
||||
obj-$(CONFIG_SCSI_LEAPRAID) += leapraid/
|
||||
obj-$(CONFIG_SCSI_SMARTPQI) += smartpqi/
|
||||
obj-$(CONFIG_SCSI_SYM53C8XX_2) += sym53c8xx_2/
|
||||
obj-$(CONFIG_SCSI_ZALON) += zalon7xx.o
|
||||
|
||||
14
drivers/scsi/leapraid/Kconfig
Normal file
14
drivers/scsi/leapraid/Kconfig
Normal file
@@ -0,0 +1,14 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
config SCSI_LEAPRAID
|
||||
tristate "LeapIO RAID Adapter"
|
||||
depends on PCI && SCSI
|
||||
select SCSI_SAS_ATTRS
|
||||
help
|
||||
This driver supports LeapIO PCIe-based storage
|
||||
and RAID controllers.
|
||||
|
||||
<http://www.leap-io.com>
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
resulting kernel module will be named leapraid.
|
||||
10
drivers/scsi/leapraid/Makefile
Normal file
10
drivers/scsi/leapraid/Makefile
Normal file
@@ -0,0 +1,10 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# Makefile for the LEAPRAID drivers.
|
||||
#
|
||||
|
||||
obj-$(CONFIG_SCSI_LEAPRAID) += leapraid.o
|
||||
leapraid-objs += leapraid_func.o \
|
||||
leapraid_os.o \
|
||||
leapraid_transport.o \
|
||||
leapraid_app.o
|
||||
2048
drivers/scsi/leapraid/leapraid.h
Normal file
2048
drivers/scsi/leapraid/leapraid.h
Normal file
File diff suppressed because it is too large
Load Diff
803
drivers/scsi/leapraid/leapraid_app.c
Normal file
803
drivers/scsi/leapraid/leapraid_app.c
Normal file
@@ -0,0 +1,803 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2026 LeapIO Tech Inc.
|
||||
*
|
||||
* LeapRAID storage and RAID controller driver.
|
||||
*/
|
||||
#include <linux/compat.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/miscdevice.h>
|
||||
|
||||
#include "leapraid_func.h"
|
||||
|
||||
/* IOCTL device file name. */
|
||||
#define LEAPRAID_DEV_NAME "leapraid_ctl"
|
||||
|
||||
/* IOCTL version. */
|
||||
#define LEAPRAID_IOCTL_VERSION 0x07
|
||||
|
||||
/* IOCTL commands. */
|
||||
#define LEAPRAID_ADAPTER_INFO 17
|
||||
#define LEAPRAID_COMMAND 20
|
||||
#define LEAPRAID_EVENTQUERY 21
|
||||
#define LEAPRAID_EVENTREPORT 23
|
||||
|
||||
/**
|
||||
* struct leapraid_ioctl_header - IOCTL command header
|
||||
*
|
||||
* @adapter_id : Adapter identifier.
|
||||
* @port_number: Port identifier.
|
||||
* @max_data_size: Maximum data size for transfer.
|
||||
*/
|
||||
struct leapraid_ioctl_header {
|
||||
u32 adapter_id;
|
||||
u32 port_number;
|
||||
u32 max_data_size;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct leapraid_ioctl_diag_reset - Diagnostic reset request
|
||||
*
|
||||
* @hdr: Common IOCTL header.
|
||||
*/
|
||||
struct leapraid_ioctl_diag_reset {
|
||||
struct leapraid_ioctl_header hdr;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct leapraid_ioctl_pci_info - PCI device information
|
||||
*
|
||||
* @u: Union holding PCI bus/device/function information.
|
||||
* @u.bits.dev: PCI device number.
|
||||
* @u.bits.func: PCI function number.
|
||||
* @u.bits.bus: PCI bus number.
|
||||
* @u.word: Combined representation of PCI BDF.
|
||||
* @seg_id: PCI segment identifier.
|
||||
*/
|
||||
struct leapraid_ioctl_pci_info {
|
||||
union {
|
||||
struct {
|
||||
u32 dev:5;
|
||||
u32 func:3;
|
||||
u32 bus:24;
|
||||
} bits;
|
||||
u32 word;
|
||||
} u;
|
||||
u32 seg_id;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct leapraid_ioctl_adapter_info - Adapter information for IOCTL
|
||||
*
|
||||
* @hdr: IOCTL header.
|
||||
* @adapter_type: Adapter type identifier.
|
||||
* @port_number: Port number.
|
||||
* @pci_id: PCI device ID.
|
||||
* @revision: Revision number.
|
||||
* @sub_dev: Subsystem device ID.
|
||||
* @sub_vendor: Subsystem vendor ID.
|
||||
* @r0: Reserved.
|
||||
* @fw_ver: Firmware version.
|
||||
* @bios_ver: BIOS version.
|
||||
* @driver_ver: Driver version.
|
||||
* @r1: Reserved.
|
||||
* @scsi_id: SCSI ID.
|
||||
* @r2: Reserved.
|
||||
* @pci_info: PCI information structure.
|
||||
*/
|
||||
struct leapraid_ioctl_adapter_info {
|
||||
struct leapraid_ioctl_header hdr;
|
||||
u32 adapter_type;
|
||||
u32 port_number;
|
||||
u32 pci_id;
|
||||
u32 revision;
|
||||
u32 sub_dev;
|
||||
u32 sub_vendor;
|
||||
u32 r0;
|
||||
u32 fw_ver;
|
||||
u32 bios_ver;
|
||||
u8 driver_ver[32];
|
||||
u8 r1;
|
||||
u8 scsi_id;
|
||||
u16 r2;
|
||||
struct leapraid_ioctl_pci_info pci_info;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct leapraid_ioctl_command - IOCTL command structure
|
||||
*
|
||||
* @hdr: IOCTL header.
|
||||
* @timeout: Command timeout.
|
||||
* @rep_msg_buf_ptr: User pointer to reply message buffer.
|
||||
* @c2h_buf_ptr: User pointer to card-to-host data buffer.
|
||||
* @h2c_buf_ptr: User pointer to host-to-card data buffer.
|
||||
* @sense_data_ptr: User pointer to sense data buffer.
|
||||
* @max_rep_bytes: Maximum reply bytes.
|
||||
* @c2h_size: Card-to-host data size.
|
||||
* @h2c_size: Host-to-card data size.
|
||||
* @max_sense_bytes: Maximum sense data bytes.
|
||||
* @data_sge_offset: Data SGE offset.
|
||||
* @mf: Message frame data (flexible array).
|
||||
*/
|
||||
struct leapraid_ioctl_command {
|
||||
struct leapraid_ioctl_header hdr;
|
||||
u32 timeout;
|
||||
void __user *rep_msg_buf_ptr;
|
||||
void __user *c2h_buf_ptr;
|
||||
void __user *h2c_buf_ptr;
|
||||
void __user *sense_data_ptr;
|
||||
u32 max_rep_bytes;
|
||||
u32 c2h_size;
|
||||
u32 h2c_size;
|
||||
u32 max_sense_bytes;
|
||||
u32 data_sge_offset;
|
||||
u8 mf[];
|
||||
};
|
||||
|
||||
static int leapraid_ctl_validate_sge_offset(struct leapraid_adapter *adapter,
|
||||
const struct leapraid_req *req,
|
||||
u32 offset_bytes,
|
||||
size_t h2c_size,
|
||||
size_t c2h_size)
|
||||
{
|
||||
size_t sge_bytes;
|
||||
|
||||
switch (req->func) {
|
||||
case LEAPRAID_FUNC_SCSIIO:
|
||||
case LEAPRAID_FUNC_SCSIIO_RAID_PASSTHROUGH:
|
||||
case LEAPRAID_FUNC_SMP_PASSTHROUGH:
|
||||
case LEAPRAID_FUNC_SCSIIO_SATA_PASSTHROUGH:
|
||||
case LEAPRAID_FUNC_FW_DOWNLOAD:
|
||||
case LEAPRAID_FUNC_FW_UPLOAD:
|
||||
sge_bytes = LEAPRAID_IEEE_SGE64_ENTRY_SIZE;
|
||||
break;
|
||||
default:
|
||||
sge_bytes = adapter->adapter_attr.use_32_dma_mask ?
|
||||
sizeof(struct leapraid_sge_simple32) :
|
||||
sizeof(struct leapraid_sge_simple64);
|
||||
break;
|
||||
}
|
||||
|
||||
if (h2c_size && c2h_size)
|
||||
sge_bytes *= 2;
|
||||
|
||||
if (offset_bytes > LEAPRAID_REQUEST_SIZE - sge_bytes) {
|
||||
dev_err(&adapter->pdev->dev,
|
||||
"%s: Invalid offset_bytes=%u for func=0x%x\n",
|
||||
__func__, offset_bytes, req->func);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct leapraid_adapter *leapraid_ctl_lookup_adapter(int adapter_id)
|
||||
{
|
||||
struct leapraid_adapter *adapter;
|
||||
struct Scsi_Host *shost;
|
||||
|
||||
spin_lock(&leapraid_adapter_lock);
|
||||
list_for_each_entry(adapter, &leapraid_adapter_list, list) {
|
||||
if (adapter->adapter_attr.id == adapter_id) {
|
||||
if (READ_ONCE(adapter->access_ctrl.host_removing))
|
||||
break;
|
||||
shost = adapter->shost;
|
||||
if (!shost || !scsi_host_get(shost))
|
||||
break;
|
||||
spin_unlock(&leapraid_adapter_lock);
|
||||
return adapter;
|
||||
}
|
||||
}
|
||||
spin_unlock(&leapraid_adapter_lock);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void leapraid_ctl_put_adapter(struct leapraid_adapter *adapter)
|
||||
{
|
||||
if (adapter && adapter->shost)
|
||||
scsi_host_put(adapter->shost);
|
||||
}
|
||||
|
||||
static void leapraid_ctl_scsiio_cmd(struct leapraid_adapter *adapter,
|
||||
void *ctl_sp_mpi_req,
|
||||
u16 taskid,
|
||||
dma_addr_t h2c_dma_addr, size_t h2c_size,
|
||||
dma_addr_t c2h_dma_addr, size_t c2h_size,
|
||||
u16 dev_hdl, void *psge)
|
||||
{
|
||||
struct leapraid_mpi_scsiio_req *scsiio_request = ctl_sp_mpi_req;
|
||||
|
||||
scsiio_request->sense_buffer_len = SCSI_SENSE_BUFFERSIZE;
|
||||
scsiio_request->sense_buffer_low_add =
|
||||
leapraid_get_sense_buffer_dma(adapter, taskid);
|
||||
memset(&adapter->driver_cmds.ctl_cmd.sense,
|
||||
0, SCSI_SENSE_BUFFERSIZE);
|
||||
leapraid_build_ieee_sg(adapter, psge, h2c_dma_addr,
|
||||
h2c_size, c2h_dma_addr, c2h_size);
|
||||
if (scsiio_request->func == LEAPRAID_FUNC_SCSIIO)
|
||||
leapraid_fire_scsi_io(adapter, taskid, dev_hdl);
|
||||
else
|
||||
leapraid_fire_task(adapter, taskid);
|
||||
}
|
||||
|
||||
static int leapraid_ctl_smp_passthrough_cmd(struct leapraid_adapter *adapter,
|
||||
void *ctl_sp_req,
|
||||
u16 taskid,
|
||||
dma_addr_t h2c_dma_addr,
|
||||
size_t h2c_size,
|
||||
dma_addr_t c2h_dma_addr,
|
||||
size_t c2h_size,
|
||||
void *psge, void *h2c)
|
||||
{
|
||||
struct leapraid_smp_passthrough_req *smp_pt_req = ctl_sp_req;
|
||||
u8 *data;
|
||||
|
||||
if (!adapter->adapter_attr.enable_mp)
|
||||
smp_pt_req->physical_port = LEAPRAID_DISABLE_MP_PORT_ID;
|
||||
if (smp_pt_req->passthrough_flg & LEAPRAID_SMP_PT_FLAG_SGL_PTR)
|
||||
data = (u8 *)&smp_pt_req->sgl;
|
||||
else
|
||||
data = h2c;
|
||||
|
||||
if (!(smp_pt_req->passthrough_flg & LEAPRAID_SMP_PT_FLAG_SGL_PTR) &&
|
||||
h2c_size <= 10) {
|
||||
dev_err(&adapter->pdev->dev,
|
||||
"%s: Invalid request size=%zu\n",
|
||||
__func__, h2c_size);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (data[1] == SMP_PHY_CONTROL &&
|
||||
(data[10] == SMP_PHY_CONTROL_LINK_RESET ||
|
||||
data[10] == SMP_PHY_CONTROL_HARD_RESET))
|
||||
adapter->reset_desc.adapter_link_resetting = 1;
|
||||
|
||||
leapraid_build_ieee_sg(adapter, psge, h2c_dma_addr,
|
||||
h2c_size, c2h_dma_addr, c2h_size);
|
||||
leapraid_fire_task(adapter, taskid);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void leapraid_ctl_sas_io_unit_ctrl_cmd(struct leapraid_adapter *adapter,
|
||||
void *ctl_sp_req,
|
||||
dma_addr_t h2c_dma_addr,
|
||||
size_t h2c_size,
|
||||
dma_addr_t c2h_dma_addr,
|
||||
size_t c2h_size,
|
||||
void *psge, u16 taskid)
|
||||
{
|
||||
struct leapraid_sas_io_unit_ctrl_req *sas_io_unit_ctl_req = ctl_sp_req;
|
||||
|
||||
if (sas_io_unit_ctl_req->op == LEAPRAID_SAS_OP_PHY_HARD_RESET ||
|
||||
sas_io_unit_ctl_req->op == LEAPRAID_SAS_OP_PHY_LINK_RESET)
|
||||
adapter->reset_desc.adapter_link_resetting = 1;
|
||||
leapraid_build_mpi_sg(adapter, psge, h2c_dma_addr,
|
||||
h2c_size, c2h_dma_addr, c2h_size);
|
||||
leapraid_fire_task(adapter, taskid);
|
||||
}
|
||||
|
||||
static int leapraid_ctl_do_command(struct leapraid_adapter *adapter,
|
||||
struct leapraid_ioctl_command *karg,
|
||||
void __user *mf)
|
||||
{
|
||||
struct leapraid_req *leap_mpi_req;
|
||||
struct leapraid_req *ctl_sp_mpi_req;
|
||||
u16 taskid;
|
||||
void *h2c = NULL;
|
||||
size_t h2c_size = 0;
|
||||
dma_addr_t h2c_dma_addr = 0;
|
||||
void *c2h = NULL;
|
||||
size_t c2h_size = 0;
|
||||
dma_addr_t c2h_dma_addr = 0;
|
||||
void *psge;
|
||||
unsigned long timeout;
|
||||
u16 dev_hdl = LEAPRAID_INVALID_DEV_HANDLE;
|
||||
bool issue_reset = false;
|
||||
u32 data_sge_offset_bytes;
|
||||
u32 sz;
|
||||
int rc;
|
||||
|
||||
rc = leapraid_check_adapter_is_op(adapter, LEAPRAID_DB_WAIT_OP_SHORT,
|
||||
__func__);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
leap_mpi_req = kzalloc(LEAPRAID_REQUEST_SIZE, GFP_KERNEL);
|
||||
if (!leap_mpi_req)
|
||||
return -ENOMEM;
|
||||
|
||||
if (karg->data_sge_offset > (UINT_MAX / LEAPRAID_SGE_OFFSET_SIZE)) {
|
||||
dev_err(&adapter->pdev->dev,
|
||||
"%s: Invalid data_sge_offset=%u\n",
|
||||
__func__, karg->data_sge_offset);
|
||||
rc = -EINVAL;
|
||||
goto out_cleanup;
|
||||
}
|
||||
|
||||
data_sge_offset_bytes = karg->data_sge_offset *
|
||||
LEAPRAID_SGE_OFFSET_SIZE;
|
||||
if (data_sge_offset_bytes > LEAPRAID_REQUEST_SIZE) {
|
||||
dev_err(&adapter->pdev->dev,
|
||||
"%s: Invalid data_sge_offset=%u\n",
|
||||
__func__, karg->data_sge_offset);
|
||||
rc = -EINVAL;
|
||||
goto out_cleanup;
|
||||
}
|
||||
|
||||
if (copy_from_user(leap_mpi_req, mf, data_sge_offset_bytes)) {
|
||||
dev_err(&adapter->pdev->dev,
|
||||
"%s: Failed to copy request message from user\n",
|
||||
__func__);
|
||||
rc = -EFAULT;
|
||||
goto out_cleanup;
|
||||
}
|
||||
|
||||
h2c_size = karg->h2c_size;
|
||||
c2h_size = karg->c2h_size;
|
||||
rc = leapraid_ctl_validate_sge_offset(adapter, leap_mpi_req,
|
||||
data_sge_offset_bytes,
|
||||
h2c_size, c2h_size);
|
||||
if (rc)
|
||||
goto out_cleanup;
|
||||
|
||||
taskid = adapter->driver_cmds.ctl_cmd.taskid;
|
||||
|
||||
adapter->driver_cmds.ctl_cmd.status = LEAPRAID_CMD_PENDING;
|
||||
memset(&adapter->driver_cmds.ctl_cmd.reply, 0, LEAPRAID_REPLY_SIZE);
|
||||
ctl_sp_mpi_req = leapraid_get_task_desc(adapter, taskid);
|
||||
memset(ctl_sp_mpi_req, 0, LEAPRAID_REQUEST_SIZE);
|
||||
memcpy(ctl_sp_mpi_req, leap_mpi_req, data_sge_offset_bytes);
|
||||
|
||||
if (ctl_sp_mpi_req->func == LEAPRAID_FUNC_SCSIIO ||
|
||||
ctl_sp_mpi_req->func == LEAPRAID_FUNC_SCSIIO_RAID_PASSTHROUGH ||
|
||||
ctl_sp_mpi_req->func == LEAPRAID_FUNC_SCSIIO_SATA_PASSTHROUGH) {
|
||||
dev_hdl = le16_to_cpu(ctl_sp_mpi_req->func_dep1);
|
||||
if (!dev_hdl ||
|
||||
dev_hdl > adapter->adapter_attr.features.max_dev_handle) {
|
||||
dev_err(&adapter->pdev->dev,
|
||||
"%s: Invalid device handle\n", __func__);
|
||||
rc = -EINVAL;
|
||||
goto out_cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
if (WARN_ON(ctl_sp_mpi_req->func == LEAPRAID_FUNC_SCSI_TMF)) {
|
||||
rc = -EINVAL;
|
||||
goto out_cleanup;
|
||||
}
|
||||
|
||||
if (h2c_size) {
|
||||
h2c = dma_alloc_coherent(&adapter->pdev->dev, h2c_size,
|
||||
&h2c_dma_addr, GFP_KERNEL);
|
||||
if (!h2c) {
|
||||
rc = -ENOMEM;
|
||||
goto out_cleanup;
|
||||
}
|
||||
if (copy_from_user(h2c, karg->h2c_buf_ptr, h2c_size)) {
|
||||
dev_err(&adapter->pdev->dev,
|
||||
"%s: Failed to copy h2c from user\n",
|
||||
__func__);
|
||||
rc = -EFAULT;
|
||||
goto out_cleanup;
|
||||
}
|
||||
}
|
||||
if (c2h_size) {
|
||||
c2h = dma_alloc_coherent(&adapter->pdev->dev, c2h_size,
|
||||
&c2h_dma_addr, GFP_KERNEL);
|
||||
if (!c2h) {
|
||||
rc = -ENOMEM;
|
||||
goto out_cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
psge = (void *)ctl_sp_mpi_req + data_sge_offset_bytes;
|
||||
init_completion(&adapter->driver_cmds.ctl_cmd.done);
|
||||
|
||||
switch (ctl_sp_mpi_req->func) {
|
||||
case LEAPRAID_FUNC_SCSIIO:
|
||||
case LEAPRAID_FUNC_SCSIIO_RAID_PASSTHROUGH:
|
||||
leapraid_ctl_scsiio_cmd(adapter, ctl_sp_mpi_req, taskid,
|
||||
h2c_dma_addr, h2c_size,
|
||||
c2h_dma_addr, c2h_size,
|
||||
dev_hdl, psge);
|
||||
break;
|
||||
case LEAPRAID_FUNC_SMP_PASSTHROUGH:
|
||||
if (!h2c) {
|
||||
rc = -EINVAL;
|
||||
goto out_cleanup;
|
||||
}
|
||||
rc = leapraid_ctl_smp_passthrough_cmd(adapter,
|
||||
ctl_sp_mpi_req, taskid,
|
||||
h2c_dma_addr, h2c_size,
|
||||
c2h_dma_addr, c2h_size,
|
||||
psge, h2c);
|
||||
if (rc)
|
||||
goto out_cleanup;
|
||||
break;
|
||||
case LEAPRAID_FUNC_SCSIIO_SATA_PASSTHROUGH:
|
||||
leapraid_build_ieee_sg(adapter, psge, h2c_dma_addr, h2c_size,
|
||||
c2h_dma_addr, c2h_size);
|
||||
leapraid_fire_task(adapter, taskid);
|
||||
break;
|
||||
case LEAPRAID_FUNC_FW_DOWNLOAD:
|
||||
case LEAPRAID_FUNC_FW_UPLOAD:
|
||||
leapraid_build_ieee_sg(adapter, psge, h2c_dma_addr, h2c_size,
|
||||
c2h_dma_addr, c2h_size);
|
||||
leapraid_fire_task(adapter, taskid);
|
||||
break;
|
||||
case LEAPRAID_FUNC_SAS_IO_UNIT_CTRL:
|
||||
leapraid_ctl_sas_io_unit_ctrl_cmd(adapter, ctl_sp_mpi_req,
|
||||
h2c_dma_addr, h2c_size,
|
||||
c2h_dma_addr, c2h_size,
|
||||
psge, taskid);
|
||||
break;
|
||||
default:
|
||||
leapraid_build_mpi_sg(adapter, psge, h2c_dma_addr,
|
||||
h2c_size, c2h_dma_addr, c2h_size);
|
||||
leapraid_fire_task(adapter, taskid);
|
||||
break;
|
||||
}
|
||||
|
||||
timeout = karg->timeout;
|
||||
if (timeout < LEAPRAID_CTL_CMD_TIMEOUT)
|
||||
timeout = LEAPRAID_CTL_CMD_TIMEOUT;
|
||||
if (!wait_for_completion_timeout(&adapter->driver_cmds.ctl_cmd.done,
|
||||
timeout * HZ)) {
|
||||
dev_err(&adapter->pdev->dev,
|
||||
"%s: ctl_cmd timeout, status=0x%x\n",
|
||||
__func__, adapter->driver_cmds.ctl_cmd.status);
|
||||
leapraid_log_req_context(adapter, taskid, ctl_sp_mpi_req);
|
||||
}
|
||||
|
||||
if ((leap_mpi_req->func == LEAPRAID_FUNC_SMP_PASSTHROUGH ||
|
||||
leap_mpi_req->func == LEAPRAID_FUNC_SAS_IO_UNIT_CTRL) &&
|
||||
adapter->reset_desc.adapter_link_resetting)
|
||||
adapter->reset_desc.adapter_link_resetting = 0;
|
||||
|
||||
if (!(adapter->driver_cmds.ctl_cmd.status & LEAPRAID_CMD_DONE)) {
|
||||
issue_reset =
|
||||
leapraid_check_reset(
|
||||
adapter->driver_cmds.ctl_cmd.status);
|
||||
goto reset;
|
||||
}
|
||||
|
||||
if (c2h_size && copy_to_user(karg->c2h_buf_ptr, c2h, c2h_size)) {
|
||||
dev_err(&adapter->pdev->dev,
|
||||
"%s: Failed to copy c2h to user\n", __func__);
|
||||
rc = -ENODATA;
|
||||
goto out_cleanup;
|
||||
}
|
||||
if (karg->max_rep_bytes) {
|
||||
sz = min_t(u32, karg->max_rep_bytes, LEAPRAID_REPLY_SIZE);
|
||||
if (copy_to_user(karg->rep_msg_buf_ptr,
|
||||
&adapter->driver_cmds.ctl_cmd.reply,
|
||||
sz)) {
|
||||
dev_err(&adapter->pdev->dev,
|
||||
"%s: Failed to copy reply to user\n",
|
||||
__func__);
|
||||
rc = -ENODATA;
|
||||
goto out_cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
if (karg->max_sense_bytes &&
|
||||
(leap_mpi_req->func == LEAPRAID_FUNC_SCSIIO ||
|
||||
leap_mpi_req->func == LEAPRAID_FUNC_SCSIIO_RAID_PASSTHROUGH)) {
|
||||
if (!karg->sense_data_ptr)
|
||||
goto out_cleanup;
|
||||
|
||||
sz = min_t(u32, karg->max_sense_bytes, SCSI_SENSE_BUFFERSIZE);
|
||||
if (copy_to_user(karg->sense_data_ptr,
|
||||
&adapter->driver_cmds.ctl_cmd.sense,
|
||||
sz)) {
|
||||
dev_err(&adapter->pdev->dev,
|
||||
"%s: Failed to copy sense data to user\n",
|
||||
__func__);
|
||||
rc = -ENODATA;
|
||||
goto out_cleanup;
|
||||
}
|
||||
}
|
||||
reset:
|
||||
if (issue_reset) {
|
||||
rc = -ENODATA;
|
||||
if (leap_mpi_req->func == LEAPRAID_FUNC_SCSIIO ||
|
||||
leap_mpi_req->func ==
|
||||
LEAPRAID_FUNC_SCSIIO_RAID_PASSTHROUGH ||
|
||||
leap_mpi_req->func ==
|
||||
LEAPRAID_FUNC_SCSIIO_SATA_PASSTHROUGH) {
|
||||
dev_err(&adapter->pdev->dev,
|
||||
"Fire tgt reset, hdl=0x%04x\n",
|
||||
le16_to_cpu(leap_mpi_req->func_dep1));
|
||||
leapraid_issue_locked_tm(
|
||||
adapter,
|
||||
le16_to_cpu(leap_mpi_req->func_dep1), 0, 0, 0,
|
||||
LEAPRAID_TM_TASKTYPE_TARGET_RESET, taskid,
|
||||
LEAPRAID_TM_MSGFLAGS_LINK_RESET);
|
||||
} else {
|
||||
dev_info(&adapter->pdev->dev,
|
||||
"%s:%d: call hard_reset\n",
|
||||
__func__, __LINE__);
|
||||
leapraid_hard_reset_handler(adapter, FULL_RESET);
|
||||
}
|
||||
}
|
||||
out_cleanup:
|
||||
if (c2h)
|
||||
dma_free_coherent(&adapter->pdev->dev, c2h_size,
|
||||
c2h, c2h_dma_addr);
|
||||
if (h2c)
|
||||
dma_free_coherent(&adapter->pdev->dev, h2c_size,
|
||||
h2c, h2c_dma_addr);
|
||||
kfree(leap_mpi_req);
|
||||
adapter->driver_cmds.ctl_cmd.status = LEAPRAID_CMD_NOT_USED;
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int leapraid_ctl_get_adapter_info(struct leapraid_adapter *adapter,
|
||||
void __user *arg)
|
||||
{
|
||||
struct leapraid_ioctl_adapter_info *karg;
|
||||
u8 revision;
|
||||
int rc = 0;
|
||||
|
||||
karg = kzalloc_obj(*karg);
|
||||
if (!karg)
|
||||
return -ENOMEM;
|
||||
|
||||
pci_read_config_byte(adapter->pdev, PCI_CLASS_REVISION, &revision);
|
||||
karg->revision = revision;
|
||||
karg->pci_id = adapter->pdev->device;
|
||||
karg->sub_dev = adapter->pdev->subsystem_device;
|
||||
karg->sub_vendor = adapter->pdev->subsystem_vendor;
|
||||
karg->pci_info.u.bits.bus = adapter->pdev->bus->number;
|
||||
karg->pci_info.u.bits.dev = PCI_SLOT(adapter->pdev->devfn);
|
||||
karg->pci_info.u.bits.func = PCI_FUNC(adapter->pdev->devfn);
|
||||
karg->pci_info.seg_id = pci_domain_nr(adapter->pdev->bus);
|
||||
karg->fw_ver = adapter->adapter_attr.features.fw_version;
|
||||
|
||||
snprintf(karg->driver_ver, sizeof(karg->driver_ver), "%s-%s",
|
||||
LEAPRAID_DRIVER_NAME, LEAPRAID_DRIVER_VERSION);
|
||||
|
||||
karg->adapter_type = LEAPRAID_IOCTL_VERSION;
|
||||
karg->bios_ver = adapter->adapter_attr.bios_version;
|
||||
if (copy_to_user(arg, karg,
|
||||
sizeof(struct leapraid_ioctl_adapter_info))) {
|
||||
dev_err(&adapter->pdev->dev,
|
||||
"%s: Failed to copy info to user\n", __func__);
|
||||
rc = -EFAULT;
|
||||
goto free_buf;
|
||||
}
|
||||
|
||||
free_buf:
|
||||
kfree(karg);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int leapraid_ctl_ioctl_main(struct file *file, unsigned int cmd,
|
||||
void __user *arg)
|
||||
{
|
||||
struct leapraid_ioctl_header ioctl_header;
|
||||
struct leapraid_adapter *adapter = NULL;
|
||||
struct leapraid_ioctl_command __user *uarg;
|
||||
struct leapraid_ioctl_command karg;
|
||||
int rc = -ENOIOCTLCMD;
|
||||
|
||||
if (copy_from_user(&ioctl_header, arg,
|
||||
sizeof(struct leapraid_ioctl_header))) {
|
||||
pr_err("%s:%s: Failed to copy header from user\n",
|
||||
LEAPRAID_DRIVER_NAME, __func__);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
adapter = leapraid_ctl_lookup_adapter(ioctl_header.adapter_id);
|
||||
if (!adapter)
|
||||
return -EFAULT;
|
||||
|
||||
if (atomic_read(&adapter->overheat_desc.thermal_alert)) {
|
||||
dev_warn(&adapter->pdev->dev,
|
||||
"%s: Failed, thermal_alert=%d\n",
|
||||
__func__,
|
||||
atomic_read(&adapter->overheat_desc.thermal_alert));
|
||||
rc = -EFAULT;
|
||||
goto out_put;
|
||||
}
|
||||
|
||||
mutex_lock(&adapter->access_ctrl.pci_access_lock);
|
||||
|
||||
rc = leapraid_check_adapter_is_op(adapter, LEAPRAID_DB_WAIT_OP_LONG,
|
||||
__func__);
|
||||
if (rc)
|
||||
goto unlock;
|
||||
|
||||
if (!wait_event_timeout(adapter->access_ctrl.shost_recover_wq,
|
||||
!adapter->access_ctrl.shost_recover_async,
|
||||
LEAPRAID_WAIT_SHOST_RECOVERY * HZ)) {
|
||||
dev_warn(&adapter->pdev->dev,
|
||||
"Timeout waiting for shost recovery async\n");
|
||||
rc = -EAGAIN;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
if (adapter->access_ctrl.pcie_recovering ||
|
||||
adapter->scan_dev_desc.driver_loading ||
|
||||
adapter->access_ctrl.host_removing) {
|
||||
rc = -EAGAIN;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
if (file->f_flags & O_NONBLOCK) {
|
||||
if (!mutex_trylock(&adapter->driver_cmds.ctl_cmd.mutex)) {
|
||||
rc = -EAGAIN;
|
||||
goto unlock;
|
||||
}
|
||||
} else if (mutex_lock_interruptible(&adapter->driver_cmds
|
||||
.ctl_cmd.mutex)) {
|
||||
rc = -ERESTARTSYS;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
switch (_IOC_NR(cmd)) {
|
||||
case LEAPRAID_ADAPTER_INFO:
|
||||
if (_IOC_SIZE(cmd) ==
|
||||
sizeof(struct leapraid_ioctl_adapter_info))
|
||||
rc = leapraid_ctl_get_adapter_info(adapter, arg);
|
||||
break;
|
||||
case LEAPRAID_COMMAND:
|
||||
if (copy_from_user(&karg, arg, sizeof(karg))) {
|
||||
dev_err(&adapter->pdev->dev,
|
||||
"%s: Failed to copy data from user\n",
|
||||
__func__);
|
||||
rc = -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
if (karg.hdr.adapter_id != ioctl_header.adapter_id) {
|
||||
rc = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (_IOC_SIZE(cmd) == sizeof(struct leapraid_ioctl_command)) {
|
||||
uarg = arg;
|
||||
rc = leapraid_ctl_do_command(adapter, &karg,
|
||||
&uarg->mf);
|
||||
if (rc)
|
||||
dev_warn(&adapter->pdev->dev,
|
||||
"%s: IOCTL cmd failed rc=%d\n",
|
||||
__func__, rc);
|
||||
}
|
||||
break;
|
||||
case LEAPRAID_EVENTQUERY:
|
||||
case LEAPRAID_EVENTREPORT:
|
||||
rc = 0;
|
||||
break;
|
||||
default:
|
||||
dev_err(&adapter->pdev->dev,
|
||||
"Unknown IOCTL opcode=0x%08x\n", cmd);
|
||||
break;
|
||||
}
|
||||
mutex_unlock(&adapter->driver_cmds.ctl_cmd.mutex);
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&adapter->access_ctrl.pci_access_lock);
|
||||
out_put:
|
||||
leapraid_ctl_put_adapter(adapter);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static long leapraid_ctl_ioctl(struct file *file, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
return leapraid_ctl_ioctl_main(file, cmd, (void __user *)arg);
|
||||
}
|
||||
|
||||
static void leapraid_fw_mmap_open(struct vm_area_struct *vma)
|
||||
{
|
||||
struct leapraid_adapter *adapter = vma->vm_private_data;
|
||||
|
||||
if (!adapter)
|
||||
return;
|
||||
|
||||
atomic_inc(&adapter->fw_log_desc.mmap_refcnt);
|
||||
}
|
||||
|
||||
static void leapraid_fw_mmap_close(struct vm_area_struct *vma)
|
||||
{
|
||||
struct leapraid_adapter *adapter = vma->vm_private_data;
|
||||
|
||||
if (!adapter)
|
||||
return;
|
||||
|
||||
if (atomic_dec_and_test(&adapter->fw_log_desc.mmap_refcnt))
|
||||
wake_up(&adapter->fw_log_desc.mmap_waitq);
|
||||
leapraid_ctl_put_adapter(adapter);
|
||||
}
|
||||
|
||||
static const struct vm_operations_struct leapraid_fw_mmap_vm_ops = {
|
||||
.open = leapraid_fw_mmap_open,
|
||||
.close = leapraid_fw_mmap_close,
|
||||
};
|
||||
|
||||
static int leapraid_fw_mmap(struct file *filp, struct vm_area_struct *vma)
|
||||
{
|
||||
struct leapraid_adapter *adapter = NULL;
|
||||
/* Userspace passes the adapter ID via vma->vm_pgoff. */
|
||||
u32 adapter_id = vma->vm_pgoff;
|
||||
unsigned long length;
|
||||
int rc = -EINVAL;
|
||||
|
||||
length = vma->vm_end - vma->vm_start;
|
||||
|
||||
adapter = leapraid_ctl_lookup_adapter(adapter_id);
|
||||
if (!adapter) {
|
||||
pr_err("%s: No adapter found!\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (READ_ONCE(adapter->access_ctrl.host_removing)) {
|
||||
rc = -EAGAIN;
|
||||
goto out_put;
|
||||
}
|
||||
|
||||
if (length > (LEAPRAID_SYS_LOG_BUF_SIZE +
|
||||
LEAPRAID_SYS_LOG_BUF_RESERVE)) {
|
||||
dev_err(&adapter->pdev->dev,
|
||||
"Requested mapping size is too large!\n");
|
||||
rc = -EINVAL;
|
||||
goto out_put;
|
||||
}
|
||||
|
||||
if (!adapter->fw_log_desc.fw_log_buffer) {
|
||||
dev_err(&adapter->pdev->dev, "No log buffer!\n");
|
||||
rc = -EINVAL;
|
||||
goto out_put;
|
||||
}
|
||||
|
||||
vma->vm_pgoff = 0;
|
||||
|
||||
rc = dma_mmap_coherent(&adapter->pdev->dev, vma,
|
||||
adapter->fw_log_desc.fw_log_buffer,
|
||||
adapter->fw_log_desc.fw_log_buffer_dma,
|
||||
length);
|
||||
if (rc) {
|
||||
dev_err(&adapter->pdev->dev,
|
||||
"Failed to map memory to user space!\n");
|
||||
goto out_put;
|
||||
}
|
||||
|
||||
vma->vm_private_data = adapter;
|
||||
vma->vm_ops = &leapraid_fw_mmap_vm_ops;
|
||||
leapraid_fw_mmap_open(vma);
|
||||
adapter = NULL;
|
||||
|
||||
rc = 0;
|
||||
out_put:
|
||||
leapraid_ctl_put_adapter(adapter);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static const struct file_operations leapraid_ctl_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.unlocked_ioctl = leapraid_ctl_ioctl,
|
||||
.mmap = leapraid_fw_mmap,
|
||||
};
|
||||
|
||||
static struct miscdevice leapraid_ctl_dev = {
|
||||
.minor = MISC_DYNAMIC_MINOR,
|
||||
.name = LEAPRAID_DEV_NAME,
|
||||
.fops = &leapraid_ctl_fops,
|
||||
};
|
||||
|
||||
int leapraid_ctl_init(void)
|
||||
{
|
||||
if (misc_register(&leapraid_ctl_dev) < 0) {
|
||||
pr_err("%s Can't register misc device\n",
|
||||
LEAPRAID_DRIVER_NAME);
|
||||
return -ENODEV;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void leapraid_ctl_exit(void)
|
||||
{
|
||||
misc_deregister(&leapraid_ctl_dev);
|
||||
}
|
||||
8626
drivers/scsi/leapraid/leapraid_func.c
Normal file
8626
drivers/scsi/leapraid/leapraid_func.c
Normal file
File diff suppressed because it is too large
Load Diff
1486
drivers/scsi/leapraid/leapraid_func.h
Normal file
1486
drivers/scsi/leapraid/leapraid_func.h
Normal file
File diff suppressed because it is too large
Load Diff
2468
drivers/scsi/leapraid/leapraid_os.c
Normal file
2468
drivers/scsi/leapraid/leapraid_os.c
Normal file
File diff suppressed because it is too large
Load Diff
1403
drivers/scsi/leapraid/leapraid_transport.c
Normal file
1403
drivers/scsi/leapraid/leapraid_transport.c
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user