From 93d9a654b31355fc2cd1be0886cce561b93e5f8b Mon Sep 17 00:00:00 2001 From: Doug Ambrisko Date: Mon, 4 Feb 2008 14:54:21 +0000 Subject: MFC: Fix some bugs in dealing with DCMD's without data. MegaCli was sending down some DCMD's without any data. Thanks to Dell and LSI for helping to provide clues to figure out this problem. Now MegaCli can upgrade the firmware and should work identical when run on Linux. Approved by: re (kensmith) --- sys/dev/mfi/mfi.c | 150 +++++++++++++++++++++++++++++++-------------------- sys/dev/mfi/mfireg.h | 2 + 2 files changed, 94 insertions(+), 58 deletions(-) diff --git a/sys/dev/mfi/mfi.c b/sys/dev/mfi/mfi.c index 592aa00e6d1d..2e9f4e7b47eb 100644 --- a/sys/dev/mfi/mfi.c +++ b/sys/dev/mfi/mfi.c @@ -573,7 +573,7 @@ mfi_release_command(struct mfi_command *cm) * as 32 bit words. Clear out the first S/G entry too for safety. */ hdr = &cm->cm_frame->header; - if (hdr->sg_count) { + if (cm->cm_data != NULL && hdr->sg_count) { cm->cm_sg->sg32[0].len = 0; cm->cm_sg->sg32[0].addr = 0; } @@ -2096,33 +2096,48 @@ mfi_ioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, d_thread_t *td) context = cm->cm_frame->header.context; bcopy(ioc->mfi_frame.raw, cm->cm_frame, - ioc->mfi_sgl_off); /* Linux can do 2 frames ? */ - cm->cm_total_frame_size = ioc->mfi_sgl_off; - cm->cm_sg = - (union mfi_sgl *)&cm->cm_frame->bytes[ioc->mfi_sgl_off]; - cm->cm_flags = MFI_CMD_DATAIN | MFI_CMD_DATAOUT; + 2 * MFI_DCMD_FRAME_SIZE); /* this isn't quite right */ + cm->cm_total_frame_size = (sizeof(union mfi_sgl) * ioc->mfi_sge_count) + ioc->mfi_sgl_off; + if (ioc->mfi_sge_count) { + cm->cm_sg = + (union mfi_sgl *)&cm->cm_frame->bytes[ioc->mfi_sgl_off]; + } + cm->cm_flags = 0; + if (cm->cm_frame->header.flags & MFI_FRAME_DATAIN) + cm->cm_flags |= MFI_CMD_DATAIN; + if (cm->cm_frame->header.flags & MFI_FRAME_DATAOUT) + cm->cm_flags |= MFI_CMD_DATAOUT; + /* Legacy app shim */ + if (cm->cm_flags == 0) + cm->cm_flags |= MFI_CMD_DATAIN | MFI_CMD_DATAOUT; cm->cm_len = cm->cm_frame->header.data_len; - cm->cm_data = data = malloc(cm->cm_len, M_MFIBUF, - M_WAITOK | M_ZERO); - if (cm->cm_data == NULL) { - device_printf(sc->mfi_dev, "Malloc failed\n"); - goto out; + if (cm->cm_flags & (MFI_CMD_DATAIN | MFI_CMD_DATAOUT)) { + cm->cm_data = data = malloc(cm->cm_len, M_MFIBUF, + M_WAITOK | M_ZERO); + if (cm->cm_data == NULL) { + device_printf(sc->mfi_dev, "Malloc failed\n"); + goto out; + } + } else { + cm->cm_data = 0; } /* restore header context */ cm->cm_frame->header.context = context; temp = data; - for (i = 0; i < ioc->mfi_sge_count; i++) { - error = copyin(ioc->mfi_sgl[i].iov_base, - temp, - ioc->mfi_sgl[i].iov_len); - if (error != 0) { - device_printf(sc->mfi_dev, - "Copy in failed\n"); - goto out; + if (cm->cm_flags & MFI_CMD_DATAOUT) { + for (i = 0; i < ioc->mfi_sge_count; i++) { + error = copyin(ioc->mfi_sgl[i].iov_base, + temp, + ioc->mfi_sgl[i].iov_len); + if (error != 0) { + device_printf(sc->mfi_dev, + "Copy in failed\n"); + goto out; + } + temp = &temp[ioc->mfi_sgl[i].iov_len]; } - temp = &temp[ioc->mfi_sgl[i].iov_len]; } if (cm->cm_frame->header.cmd == MFI_CMD_DCMD) @@ -2146,16 +2161,18 @@ mfi_ioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, d_thread_t *td) mtx_unlock(&sc->mfi_io_lock); temp = data; - for (i = 0; i < ioc->mfi_sge_count; i++) { - error = copyout(temp, - ioc->mfi_sgl[i].iov_base, - ioc->mfi_sgl[i].iov_len); - if (error != 0) { - device_printf(sc->mfi_dev, - "Copy out failed\n"); - goto out; + if (cm->cm_flags & MFI_CMD_DATAIN) { + for (i = 0; i < ioc->mfi_sge_count; i++) { + error = copyout(temp, + ioc->mfi_sgl[i].iov_base, + ioc->mfi_sgl[i].iov_len); + if (error != 0) { + device_printf(sc->mfi_dev, + "Copy out failed\n"); + goto out; + } + temp = &temp[ioc->mfi_sgl[i].iov_len]; } - temp = &temp[ioc->mfi_sgl[i].iov_len]; } if (ioc->mfi_sense_len) { @@ -2282,31 +2299,46 @@ mfi_linux_ioctl_int(struct cdev *dev, u_long cmd, caddr_t arg, int flag, d_threa context = cm->cm_frame->header.context; bcopy(l_ioc.lioc_frame.raw, cm->cm_frame, - l_ioc.lioc_sgl_off); /* Linux can do 2 frames ? */ - cm->cm_total_frame_size = l_ioc.lioc_sgl_off; - cm->cm_sg = - (union mfi_sgl *)&cm->cm_frame->bytes[l_ioc.lioc_sgl_off]; - cm->cm_flags = MFI_CMD_DATAIN | MFI_CMD_DATAOUT; + 2 * MFI_DCMD_FRAME_SIZE); /* this isn't quite right */ + cm->cm_total_frame_size = (sizeof(union mfi_sgl) * l_ioc.lioc_sge_count) + l_ioc.lioc_sgl_off; + if (l_ioc.lioc_sge_count) + cm->cm_sg = + (union mfi_sgl *)&cm->cm_frame->bytes[l_ioc.lioc_sgl_off]; + cm->cm_flags = 0; + if (cm->cm_frame->header.flags & MFI_FRAME_DATAIN) + cm->cm_flags |= MFI_CMD_DATAIN; + if (cm->cm_frame->header.flags & MFI_FRAME_DATAOUT) + cm->cm_flags |= MFI_CMD_DATAOUT; cm->cm_len = cm->cm_frame->header.data_len; - cm->cm_data = data = malloc(cm->cm_len, M_MFIBUF, - M_WAITOK | M_ZERO); + if (cm->cm_flags & (MFI_CMD_DATAIN | MFI_CMD_DATAOUT)) { + cm->cm_data = data = malloc(cm->cm_len, M_MFIBUF, + M_WAITOK | M_ZERO); + if (cm->cm_data == NULL) { + device_printf(sc->mfi_dev, "Malloc failed\n"); + goto out; + } + } else { + cm->cm_data = 0; + } /* restore header context */ cm->cm_frame->header.context = context; temp = data; - 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, - temp, - l_ioc.lioc_sgl[i].iov_len); - if (error != 0) { - device_printf(sc->mfi_dev, - "Copy in failed\n"); - goto out; + 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, + temp, + l_ioc.lioc_sgl[i].iov_len); + if (error != 0) { + device_printf(sc->mfi_dev, + "Copy in failed\n"); + goto out; + } + temp = &temp[l_ioc.lioc_sgl[i].iov_len]; } - temp = &temp[l_ioc.lioc_sgl[i].iov_len]; } if (cm->cm_frame->header.cmd == MFI_CMD_DCMD) @@ -2330,18 +2362,20 @@ mfi_linux_ioctl_int(struct cdev *dev, u_long cmd, caddr_t arg, int flag, d_threa mtx_unlock(&sc->mfi_io_lock); temp = data; - 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, - l_ioc.lioc_sgl[i].iov_len); - if (error != 0) { - device_printf(sc->mfi_dev, - "Copy out failed\n"); - goto out; + 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, + l_ioc.lioc_sgl[i].iov_len); + if (error != 0) { + device_printf(sc->mfi_dev, + "Copy out failed\n"); + goto out; + } + temp = &temp[l_ioc.lioc_sgl[i].iov_len]; } - temp = &temp[l_ioc.lioc_sgl[i].iov_len]; } if (l_ioc.lioc_sense_len) { diff --git a/sys/dev/mfi/mfireg.h b/sys/dev/mfi/mfireg.h index 2bd8a51d3a9a..c54602664003 100644 --- a/sys/dev/mfi/mfireg.h +++ b/sys/dev/mfi/mfireg.h @@ -352,6 +352,8 @@ struct mfi_frame_header { uint32_t context; uint32_t pad0; uint16_t flags; +#define MFI_FRAME_DATAOUT 0x08 +#define MFI_FRAME_DATAIN 0x10 uint16_t timeout; uint32_t data_len; } __packed; -- cgit v1.3