git: e315351fc7af - main - Add the mfi(4) ioctl support to mrsas(4)
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Fri, 28 Apr 2023 17:15:29 UTC
The branch main has been updated by ambrisko: URL: https://cgit.FreeBSD.org/src/commit/?id=e315351fc7af11638c85fa22222536f4a2538f11 commit e315351fc7af11638c85fa22222536f4a2538f11 Author: Doug Ambrisko <ambrisko@FreeBSD.org> AuthorDate: 2022-08-24 22:27:03 +0000 Commit: Doug Ambrisko <ambrisko@FreeBSD.org> CommitDate: 2023-04-28 17:14:30 +0000 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> --- sys/dev/mrsas/mrsas.c | 13 ++- sys/dev/mrsas/mrsas.h | 3 + sys/dev/mrsas/mrsas_ioctl.c | 254 ++++++++++++++++++++++++++++++++++++++++++++ sys/dev/mrsas/mrsas_ioctl.h | 9 ++ 4 files changed, 278 insertions(+), 1 deletion(-) diff --git a/sys/dev/mrsas/mrsas.c b/sys/dev/mrsas/mrsas.c index 2f531bb44674..a865f5ab04d3 100644 --- a/sys/dev/mrsas/mrsas.c +++ b/sys/dev/mrsas/mrsas.c @@ -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 @@ do_ioctl: 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; diff --git a/sys/dev/mrsas/mrsas.h b/sys/dev/mrsas/mrsas.h index 76894bbdbc4e..fa9a5cf118e6 100644 --- a/sys/dev/mrsas/mrsas.h +++ b/sys/dev/mrsas/mrsas.h @@ -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 */ diff --git a/sys/dev/mrsas/mrsas_ioctl.c b/sys/dev/mrsas/mrsas_ioctl.c index 1044fcbbcf92..b8d88c164e81 100644 --- a/sys/dev/mrsas/mrsas_ioctl.c +++ b/sys/dev/mrsas/mrsas_ioctl.c @@ -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 @@ out: 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 diff --git a/sys/dev/mrsas/mrsas_ioctl.h b/sys/dev/mrsas/mrsas_ioctl.h index 07ee211714cc..3851de710b5a 100644 --- a/sys/dev/mrsas/mrsas_ioctl.h +++ b/sys/dev/mrsas/mrsas_ioctl.h @@ -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 */