diff --git a/sys/dev/mpi3mr/mpi3mr.h b/sys/dev/mpi3mr/mpi3mr.h --- a/sys/dev/mpi3mr/mpi3mr.h +++ b/sys/dev/mpi3mr/mpi3mr.h @@ -231,6 +231,8 @@ #define MPI3MR_PERIODIC_DELAY 1 /* 1 second heartbeat/watchdog check */ +#define WRITE_SAME_32 0x0d + struct completion { unsigned int done; struct mtx lock; diff --git a/sys/dev/mpi3mr/mpi3mr.c b/sys/dev/mpi3mr/mpi3mr.c --- a/sys/dev/mpi3mr/mpi3mr.c +++ b/sys/dev/mpi3mr/mpi3mr.c @@ -2176,6 +2176,8 @@ time_in_msec = (now.tv_sec * 1000 + now.tv_usec/1000); iocinit_req.TimeStamp = htole64(time_in_msec); + iocinit_req.MsgFlags |= MPI3_IOCINIT_MSGFLAGS_WRITESAMEDIVERT_SUPPORTED; + init_completion(&sc->init_cmds.completion); retval = mpi3mr_submit_admin_cmd(sc, &iocinit_req, sizeof(iocinit_req)); @@ -3339,6 +3341,19 @@ break; } + switch (flags & MPI3_DEVICE0_FLAGS_MAX_WRITE_SAME_MASK) { + case MPI3_DEVICE0_FLAGS_MAX_WRITE_SAME_256_LB: + tgtdev->ws_len = 256; + break; + case MPI3_DEVICE0_FLAGS_MAX_WRITE_SAME_2048_LB: + tgtdev->ws_len = 2048; + break; + case MPI3_DEVICE0_FLAGS_MAX_WRITE_SAME_NO_LIMIT: + default: + tgtdev->ws_len = 0; + break; + } + switch (tgtdev->dev_type) { case MPI3_DEVICE_DEVFORM_SAS_SATA: { @@ -5648,6 +5663,7 @@ target->io_throttle_enabled = 0; target->io_divert = 0; target->throttle_group = NULL; + target->ws_len = 0; } } mtx_unlock_spin(&sc->target_lock); diff --git a/sys/dev/mpi3mr/mpi3mr_cam.h b/sys/dev/mpi3mr/mpi3mr_cam.h --- a/sys/dev/mpi3mr/mpi3mr_cam.h +++ b/sys/dev/mpi3mr/mpi3mr_cam.h @@ -121,6 +121,7 @@ struct mpi3mr_throttle_group_info *throttle_group; uint64_t q_depth; enum mpi3mr_target_state state; + uint16_t ws_len; }; struct mpi3mr_cam_softc { diff --git a/sys/dev/mpi3mr/mpi3mr_cam.c b/sys/dev/mpi3mr/mpi3mr_cam.c --- a/sys/dev/mpi3mr/mpi3mr_cam.c +++ b/sys/dev/mpi3mr/mpi3mr_cam.c @@ -82,6 +82,7 @@ #include "mpi3mr.h" #include /* XXX for pcpu.h */ #include /* XXX for PCPU_GET */ +#include #define smp_processor_id() PCPU_GET(cpuid) @@ -101,6 +102,37 @@ static U32 event_count; +static +inline void mpi3mr_divert_ws(Mpi3SCSIIORequest_t *req, + struct ccb_scsiio *csio, + U16 ws_len) +{ + U8 unmap = 0, ndob = 0; + U32 num_blocks = 0; + U8 opcode = scsiio_cdb_ptr(csio)[0]; + U16 service_action = ((scsiio_cdb_ptr(csio)[8] << 8) | scsiio_cdb_ptr(csio)[9]); + + + if (opcode == WRITE_SAME_16 || + (opcode == VARIABLE_LEN_CDB && + service_action == WRITE_SAME_32)) { + + int unmap_ndob_index = (opcode == WRITE_SAME_16) ? 1 : 10; + + unmap = scsiio_cdb_ptr(csio)[unmap_ndob_index] & 0x08; + ndob = scsiio_cdb_ptr(csio)[unmap_ndob_index] & 0x01; + num_blocks = get_unaligned_be32(scsiio_cdb_ptr(csio) + + ((opcode == WRITE_SAME_16) ? 10 : 28)); + + /* Check conditions for diversion to firmware */ + if (unmap && ndob && num_blocks > ws_len) { + req->MsgFlags |= MPI3_SCSIIO_MSGFLAGS_DIVERT_TO_FIRMWARE; + req->Flags = htole32(le32toh(req->Flags) | + MPI3_SCSIIO_FLAGS_DIVERT_REASON_WRITE_SAME_TOO_LARGE); + } + } +} + static void mpi3mr_prepare_sgls(void *arg, bus_dma_segment_t *segs, int nsegs, int error) { @@ -1079,6 +1111,9 @@ break; } + if (targ->ws_len) + mpi3mr_divert_ws(req, csio, targ->ws_len); + req->Flags = htole32(mpi_control); if (csio->ccb_h.flags & CAM_CDB_POINTER) diff --git a/sys/modules/mpi3mr/Makefile b/sys/modules/mpi3mr/Makefile --- a/sys/modules/mpi3mr/Makefile +++ b/sys/modules/mpi3mr/Makefile @@ -6,6 +6,9 @@ SRCS+= opt_cam.h SRCS+= device_if.h bus_if.h pci_if.h +CFLAGS+= -I${SRCTOP}/sys/compat/linuxkpi/common/include +DEBUG_FLAGS= -g + .include CWARNFLAGS.mpi3mr_sas.c= ${NO_WUNNEEDED_INTERNAL_DECL}