Add the mfi(4) ioctl support to mrsas(4)

The hardware supported by mfi(4) and mrsas(4) use the same dcmd's.
mfiutil(8) in theory could run on controlled attached to mrsas(4).
It can't since mrsas(4) doesn't have support for the FreeBSD mfi(4)
ioctl.  Porting the ioctl from mfi(4) to mrsas(4) would be the first
step in making mrsasutil(8) which is an additional name for mfiutil(8)
but opens /dev/mrsasX instead of /dev/mfiX

PR:			https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=265794
Reviewed by:		jhb
Differential revision:	https://reviews.freebsd.org/D36342
Tested by:		Dan Mahoney <freebsd@gushi.org>
This commit is contained in:
Doug Ambrisko 2022-08-24 15:27:03 -07:00
parent 2711d4345e
commit e315351fc7
4 changed files with 278 additions and 1 deletions

View File

@ -1449,7 +1449,14 @@ mrsas_ioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag,
int ret = 0, i = 0;
MRSAS_DRV_PCI_INFORMATION *pciDrvInfo;
sc = mrsas_get_softc_instance(dev, cmd, arg);
switch (cmd) {
case MFIIO_PASSTHRU:
sc = (struct mrsas_softc *)(dev->si_drv1);
break;
default:
sc = mrsas_get_softc_instance(dev, cmd, arg);
break;
}
if (!sc)
return ENOENT;
@ -1512,6 +1519,10 @@ mrsas_ioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag,
ret = 0;
break;
case MFIIO_PASSTHRU:
ret = mrsas_user_command(sc, (struct mfi_ioc_passthru *)arg);
break;
default:
mrsas_dprint(sc, MRSAS_TRACE, "IOCTL command 0x%lx is not handled\n", cmd);
ret = ENOENT;

View File

@ -3648,4 +3648,7 @@ mrsas_test_bit(int b, volatile void *p)
return ((volatile int *)p)[b >> 5] & (1 << (b & 0x1f));
}
#include "mrsas_ioctl.h"
extern int mrsas_user_command(struct mrsas_softc *, struct mfi_ioc_passthru *);
#endif /* MRSAS_H */

View File

@ -43,6 +43,18 @@ __FBSDID("$FreeBSD$");
#include <dev/mrsas/mrsas.h>
#include <dev/mrsas/mrsas_ioctl.h>
struct mrsas_passthru_cmd {
struct iovec *kern_sge;
struct mrsas_softc *sc;
struct mrsas_mfi_cmd *cmd;
bus_dma_tag_t ioctl_data_tag;
bus_dmamap_t ioctl_data_dmamap;
u_int32_t error_code;
u_int32_t sge_count;
int complete;
};
/*
* Function prototypes
*/
@ -62,6 +74,54 @@ extern int
mrsas_issue_blocked_cmd(struct mrsas_softc *sc,
struct mrsas_mfi_cmd *cmd);
/*
* mrsas_data_load_cb: Callback entry point
* input: Pointer to command packet as argument
* Pointer to segment
* Number of segments Error
*
* This is the callback function of the bus dma map load. It builds the SG
* list.
*/
static void
mrsas_passthru_load_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error)
{
struct mrsas_passthru_cmd *cb = (struct mrsas_passthru_cmd *)arg;
struct mrsas_softc *sc = cb->sc;
int i = 0;
if (error) {
cb->error_code = error;
if (error == EFBIG) {
device_printf(sc->mrsas_dev, "mrsas_passthru_load_cb: "
"error=%d EFBIG\n", error);
cb->complete = 1;
return;
} else {
device_printf(sc->mrsas_dev, "mrsas_passthru_load_cb: "
"error=%d UNKNOWN\n", error);
}
}
if (nseg > MAX_IOCTL_SGE) {
cb->error_code = EFBIG;
device_printf(sc->mrsas_dev, "mrsas_passthru_load_cb: "
"too many segments: %d\n", nseg);
cb->complete = 1;
return;
}
for (i = 0; i < nseg; i++) {
cb->kern_sge[i].iov_base = PTRIN(segs[i].ds_addr);
cb->kern_sge[i].iov_len = segs[i].ds_len;
}
cb->sge_count = nseg;
bus_dmamap_sync(cb->ioctl_data_tag, cb->ioctl_data_dmamap,
BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
cb->complete = 1;
}
/*
* mrsas_passthru: Handle pass-through commands
* input: Adapter instance soft state argument pointer
@ -344,6 +404,200 @@ mrsas_passthru(struct mrsas_softc *sc, void *arg, u_long ioctlCmd)
return (ret);
}
/**
* mrsas_user_command: Handle user mode DCMD and buffer
* input: Adapter instance soft state
* argument pointer
*
* This function is called from mrsas_ioctl() DCMDs to firmware for mfiutil
*/
int
mrsas_user_command(struct mrsas_softc *sc, struct mfi_ioc_passthru *ioc)
{
struct mrsas_mfi_cmd *cmd;
struct mrsas_dcmd_frame *dcmd;
struct mrsas_passthru_cmd *passcmd;
bus_dma_tag_t ioctl_data_tag;
bus_dmamap_t ioctl_data_dmamap;
bus_addr_t ioctl_data_phys_addr;
struct iovec *kern_sge;
int ret, ioctl_data_size;
char *ioctl_temp_data_mem;
ret = 0;
ioctl_temp_data_mem = NULL;
passcmd = NULL;
ioctl_data_phys_addr = 0;
dcmd = NULL;
cmd = NULL;
ioctl_data_tag = NULL;
ioctl_data_dmamap = NULL;
ioctl_data_dmamap = NULL;
/* Get a command */
cmd = mrsas_get_mfi_cmd(sc);
if (!cmd) {
device_printf(sc->mrsas_dev,
"Failed to get a free cmd for IOCTL\n");
return(ENOMEM);
}
/*
* Frame is DCMD
*/
dcmd = (struct mrsas_dcmd_frame *)cmd->frame;
memcpy(dcmd, &ioc->ioc_frame, sizeof(struct mrsas_dcmd_frame));
ioctl_data_size = ioc->buf_size;
cmd->frame->hdr.context = cmd->index;
cmd->frame->hdr.pad_0 = 0;
cmd->frame->hdr.flags = MFI_FRAME_DIR_BOTH;
if (sizeof(bus_addr_t) == 8)
cmd->frame->hdr.flags |= MFI_FRAME_SGL64 | MFI_FRAME_SENSE64;
kern_sge = (struct iovec *)(&dcmd->sgl);
if (ioctl_data_size == 0) {
kern_sge[0].iov_base = 0;
kern_sge[0].iov_len = 0;
} else {
ioctl_temp_data_mem = malloc(ioc->buf_size, M_MRSAS, M_WAITOK);
if (ioctl_temp_data_mem == NULL) {
device_printf(sc->mrsas_dev, "Could not allocate "
"%d memory for temporary passthrough ioctl\n",
ioc->buf_size);
ret = ENOMEM;
goto out;
}
/* Copy in data from user space */
ret = copyin(ioc->buf, ioctl_temp_data_mem, ioc->buf_size);
if (ret) {
device_printf(sc->mrsas_dev, "IOCTL copyin failed!\n");
goto out;
}
/*
* Allocate a temporary struct to hold parameters for the
* callback
*/
passcmd = malloc(sizeof(struct mrsas_passthru_cmd), M_MRSAS,
M_WAITOK);
if (passcmd == NULL) {
device_printf(sc->mrsas_dev, "Could not allocate "
"memory for temporary passthrough cb struct\n");
ret = ENOMEM;
goto out;
}
passcmd->complete = 0;
passcmd->sc = sc;
passcmd->cmd = cmd;
passcmd->kern_sge = kern_sge;
/*
* Create a dma tag for passthru buffers
*/
if (bus_dma_tag_create(sc->mrsas_parent_tag, /* parent */
1, 0, /* algnmnt, boundary */
BUS_SPACE_MAXADDR, /* lowaddr */
BUS_SPACE_MAXADDR, /* highaddr */
NULL, NULL, /* filter, filterarg */
ioctl_data_size, /* maxsize */
MAX_IOCTL_SGE, /* msegments */
ioctl_data_size, /* maxsegsize */
BUS_DMA_ALLOCNOW, /* flags */
busdma_lock_mutex, /* lockfunc */
&sc->ioctl_lock, /* lockarg */
&ioctl_data_tag)) {
device_printf(sc->mrsas_dev,
"Cannot allocate ioctl data tag %d\n",
ioc->buf_size);
ret = ENOMEM;
goto out;
}
/* Create memmap */
if (bus_dmamap_create(ioctl_data_tag, 0, &ioctl_data_dmamap)) {
device_printf(sc->mrsas_dev, "Cannot create ioctl "
"passthru dmamap\n");
ret = ENOMEM;
goto out;
}
passcmd->ioctl_data_tag = ioctl_data_tag;
passcmd->ioctl_data_dmamap = ioctl_data_dmamap;
/* Map data buffer into bus space */
if (bus_dmamap_load(ioctl_data_tag, ioctl_data_dmamap,
ioctl_temp_data_mem, ioc->buf_size, mrsas_passthru_load_cb,
passcmd, BUS_DMA_NOWAIT)) {
device_printf(sc->mrsas_dev, "Cannot load ioctl "
"passthru data mem%s %d\n", curproc->p_comm, ioctl_data_size);
ret = ENOMEM;
goto out;
}
while (passcmd->complete == 0) {
pause("mrsas_passthru", hz);
}
cmd->frame->dcmd.sge_count = passcmd->sge_count;
}
/*
* Set the sync_cmd flag so that the ISR knows not to complete this
* cmd to the SCSI mid-layer
*/
cmd->sync_cmd = 1;
mrsas_issue_blocked_cmd(sc, cmd);
cmd->sync_cmd = 0;
if (ioctl_data_size != 0) {
bus_dmamap_sync(ioctl_data_tag, ioctl_data_dmamap,
BUS_DMASYNC_POSTREAD);
/*
* copy out the kernel buffers to user buffers
*/
ret = copyout(ioctl_temp_data_mem, ioc->buf, ioc->buf_size);
if (ret) {
device_printf(sc->mrsas_dev,
"IOCTL copyout failed!\n");
goto out;
}
}
/*
* Return command status to user space
*/
memcpy(&ioc->ioc_frame.cmd_status, &cmd->frame->hdr.cmd_status,
sizeof(u_int8_t));
out:
/*
* Release temporary passthrough ioctl
*/
if (ioctl_temp_data_mem)
free(ioctl_temp_data_mem, M_MRSAS);
if (passcmd)
free(passcmd, M_MRSAS);
/*
* Release data buffers
*/
if (ioctl_data_phys_addr) {
bus_dmamap_unload(ioctl_data_tag, ioctl_data_dmamap);
bus_dmamap_destroy(ioctl_data_tag, ioctl_data_dmamap);
}
if (ioctl_data_tag != NULL)
bus_dma_tag_destroy(ioctl_data_tag);
/* Free command */
mrsas_release_mfi_cmd(cmd);
return(ret);
}
/*
* mrsas_alloc_mfi_cmds: Allocates the command packets
* input: Adapter instance soft state

View File

@ -121,4 +121,13 @@ struct mrsas_iocpacket32 {
#pragma pack()
#endif /* COMPAT_FREEBSD32 */
struct mfi_ioc_passthru {
struct mrsas_dcmd_frame ioc_frame;
uint32_t pad_skinny_flag;
uint32_t buf_size;
uint8_t *buf;
} __packed;
#define MFIIO_PASSTHRU _IOWR('C', 102, struct mfi_ioc_passthru)
#endif /* MRSAS_IOCTL_H */