summaryrefslogtreecommitdiffstats
path: root/sys/dev/mfi/mfi.c
diff options
context:
space:
mode:
authorscottl <scottl@FreeBSD.org>2008-05-12 14:09:19 +0000
committerscottl <scottl@FreeBSD.org>2008-05-12 14:09:19 +0000
commit030bf318b071bf6c5b25521b69264018a17c1cd2 (patch)
tree11ae9de16305fd6519d4fa708c98661fec8182b1 /sys/dev/mfi/mfi.c
parent005efc38af0b61675062d775e960748e140aa588 (diff)
downloadFreeBSD-src-030bf318b071bf6c5b25521b69264018a17c1cd2.zip
FreeBSD-src-030bf318b071bf6c5b25521b69264018a17c1cd2.tar.gz
Add support for management apps. Work around an apparent firmware bug that
results in hung i/o if more than 128 commands are scheduled for an array.
Diffstat (limited to 'sys/dev/mfi/mfi.c')
-rw-r--r--sys/dev/mfi/mfi.c116
1 files changed, 108 insertions, 8 deletions
diff --git a/sys/dev/mfi/mfi.c b/sys/dev/mfi/mfi.c
index 9393e3a..e7e8fd9 100644
--- a/sys/dev/mfi/mfi.c
+++ b/sys/dev/mfi/mfi.c
@@ -106,6 +106,8 @@ static void mfi_complete(struct mfi_softc *, struct mfi_command *);
static int mfi_abort(struct mfi_softc *, struct mfi_command *);
static int mfi_linux_ioctl_int(struct cdev *, u_long, caddr_t, int, d_thread_t *);
static void mfi_timeout(void *);
+static int mfi_user_command(struct mfi_softc *,
+ struct mfi_ioc_passthru *);
static void mfi_enable_intr_xscale(struct mfi_softc *sc);
static void mfi_enable_intr_ppc(struct mfi_softc *sc);
static int32_t mfi_read_fw_status_xscale(struct mfi_softc *sc);
@@ -126,6 +128,11 @@ TUNABLE_INT("hw.mfi.event_class", &mfi_event_class);
SYSCTL_INT(_hw_mfi, OID_AUTO, event_class, CTLFLAG_RW, &mfi_event_class,
0, "event message class");
+static int mfi_max_cmds = 128;
+TUNABLE_INT("hw.mfi.max_cmds", &mfi_max_cmds);
+SYSCTL_INT(_hw_mfi, OID_AUTO, max_cmds, CTLFLAG_RD, &mfi_max_cmds,
+ 0, "Max commands");
+
/* Management interface */
static d_open_t mfi_open;
static d_close_t mfi_close;
@@ -535,7 +542,11 @@ mfi_alloc_commands(struct mfi_softc *sc)
* XXX Should we allocate all the commands up front, or allocate on
* demand later like 'aac' does?
*/
- ncmds = sc->mfi_max_fw_cmds;
+ ncmds = MIN(mfi_max_cmds, sc->mfi_max_fw_cmds);
+ if (bootverbose)
+ device_printf(sc->mfi_dev, "Max fw cmds= %d, sizing driver "
+ "pool to %d\n", sc->mfi_max_fw_cmds, ncmds);
+
sc->mfi_commands = malloc(sizeof(struct mfi_command) * ncmds, M_MFIBUF,
M_WAITOK | M_ZERO);
@@ -2018,6 +2029,80 @@ mfi_check_command_post(struct mfi_softc *sc, struct mfi_command *cm)
}
static int
+mfi_user_command(struct mfi_softc *sc, struct mfi_ioc_passthru *ioc)
+{
+ struct mfi_command *cm;
+ struct mfi_dcmd_frame *dcmd;
+ void *ioc_buf = NULL;
+ uint32_t context;
+ int error = 0, locked;
+
+
+ if (ioc->buf_size > 0) {
+ ioc_buf = malloc(ioc->buf_size, M_MFIBUF, M_WAITOK);
+ if (ioc_buf == NULL) {
+ return (ENOMEM);
+ }
+ error = copyin(ioc->buf, ioc_buf, ioc->buf_size);
+ if (error) {
+ device_printf(sc->mfi_dev, "failed to copyin\n");
+ free(ioc_buf, M_MFIBUF);
+ return (error);
+ }
+ }
+
+ locked = mfi_config_lock(sc, ioc->ioc_frame.opcode);
+
+ mtx_lock(&sc->mfi_io_lock);
+ while ((cm = mfi_dequeue_free(sc)) == NULL)
+ msleep(mfi_user_command, &sc->mfi_io_lock, 0, "mfiioc", hz);
+
+ /* Save context for later */
+ context = cm->cm_frame->header.context;
+
+ dcmd = &cm->cm_frame->dcmd;
+ bcopy(&ioc->ioc_frame, dcmd, sizeof(struct mfi_dcmd_frame));
+
+ cm->cm_sg = &dcmd->sgl;
+ cm->cm_total_frame_size = MFI_DCMD_FRAME_SIZE;
+ cm->cm_data = ioc_buf;
+ cm->cm_len = ioc->buf_size;
+
+ /* restore context */
+ cm->cm_frame->header.context = context;
+
+ /* Cheat since we don't know if we're writing or reading */
+ cm->cm_flags = MFI_CMD_DATAIN | MFI_CMD_DATAOUT;
+
+ error = mfi_check_command_pre(sc, cm);
+ if (error)
+ goto out;
+
+ error = mfi_wait_command(sc, cm);
+ if (error) {
+ device_printf(sc->mfi_dev, "ioctl failed %d\n", error);
+ goto out;
+ }
+ bcopy(dcmd, &ioc->ioc_frame, sizeof(struct mfi_dcmd_frame));
+ mfi_check_command_post(sc, cm);
+out:
+ mfi_release_command(cm);
+ mtx_unlock(&sc->mfi_io_lock);
+ mfi_config_unlock(sc, locked);
+ if (ioc->buf_size > 0)
+ error = copyout(ioc_buf, ioc->buf, ioc->buf_size);
+ if (ioc_buf)
+ free(ioc_buf, M_MFIBUF);
+ return (error);
+}
+
+#ifdef __amd64__
+#define PTRIN(p) ((void *)(uintptr_t)(p))
+#else
+#define PTRIN(p) (p)
+#endif
+
+static int
mfi_ioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, d_thread_t *td)
{
struct mfi_softc *sc;
@@ -2029,6 +2114,11 @@ mfi_ioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, d_thread_t *td)
uint8_t *sense_ptr;
uint8_t *data = NULL, *temp;
int i;
+ struct mfi_ioc_passthru *iop = (struct mfi_ioc_passthru *)arg;
+#ifdef __amd64__
+ struct mfi_ioc_passthru32 *iop32 = (struct mfi_ioc_passthru32 *)arg;
+ struct mfi_ioc_passthru iop_swab;
+#endif
int error, locked;
sc = dev->si_drv1;
@@ -2253,6 +2343,21 @@ out:
cmd, arg, flag, td));
break;
}
+#ifdef __amd64__
+ case MFIIO_PASSTHRU32:
+ iop_swab.ioc_frame = iop32->ioc_frame;
+ iop_swab.buf_size = iop32->buf_size;
+ iop_swab.buf = PTRIN(iop32->buf);
+ iop = &iop_swab;
+ /* FALLTHROUGH */
+#endif
+ case MFIIO_PASSTHRU:
+ error = mfi_user_command(sc, iop);
+#ifdef __amd64__
+ if (cmd == MFIIO_PASSTHRU32)
+ iop32->ioc_frame = iop_swab.ioc_frame;
+#endif
+ break;
default:
device_printf(sc->mfi_dev, "IOCTL 0x%lx not handled\n", cmd);
error = ENOENT;
@@ -2273,7 +2378,6 @@ mfi_linux_ioctl_int(struct cdev *dev, u_long cmd, caddr_t arg, int flag, d_threa
uint8_t *sense_ptr;
uint32_t context;
uint8_t *data = NULL, *temp;
- void *temp_convert;
int i;
int error, locked;
@@ -2332,9 +2436,7 @@ mfi_linux_ioctl_int(struct cdev *dev, u_long cmd, caddr_t arg, int flag, d_threa
temp = data;
if (cm->cm_flags & MFI_CMD_DATAOUT) {
for (i = 0; i < l_ioc.lioc_sge_count; i++) {
- temp_convert =
- (void *)(uintptr_t)l_ioc.lioc_sgl[i].iov_base;
- error = copyin(temp_convert,
+ error = copyin(PTRIN(l_ioc.lioc_sgl[i].iov_base),
temp,
l_ioc.lioc_sgl[i].iov_len);
if (error != 0) {
@@ -2369,10 +2471,8 @@ mfi_linux_ioctl_int(struct cdev *dev, u_long cmd, caddr_t arg, int flag, d_threa
temp = data;
if (cm->cm_flags & MFI_CMD_DATAIN) {
for (i = 0; i < l_ioc.lioc_sge_count; i++) {
- temp_convert =
- (void *)(uintptr_t)l_ioc.lioc_sgl[i].iov_base;
error = copyout(temp,
- temp_convert,
+ PTRIN(l_ioc.lioc_sgl[i].iov_base),
l_ioc.lioc_sgl[i].iov_len);
if (error != 0) {
device_printf(sc->mfi_dev,
OpenPOWER on IntegriCloud