git: 3f3a15543a67 - main - mpi3mr: Divert large WriteSame IOs to firmware if unmap and ndob bits are set

From: Sumit Saxena <ssaxena_at_FreeBSD.org>
Date: Thu, 06 Jun 2024 10:39:32 UTC
The branch main has been updated by ssaxena:

URL: https://cgit.FreeBSD.org/src/commit/?id=3f3a15543a6721100dda0e4219eb48ecbe35731a

commit 3f3a15543a6721100dda0e4219eb48ecbe35731a
Author:     Chandrakanth patil <chandrakanth.patil@broadcom.com>
AuthorDate: 2024-06-06 08:38:52 +0000
Commit:     Sumit Saxena <ssaxena@FreeBSD.org>
CommitDate: 2024-06-06 10:39:15 +0000

    mpi3mr: Divert large WriteSame IOs to firmware if unmap and ndob bits are set
    
    Firmware advertises the transfer lenght for writesame commands to driver during init.
    So for any writesame IOs with ndob and unmap bit set and transfer lengh is greater
    than the max write same length specified by the firmware, then direct those commands
    to firmware instead of hardware otherwise hardware will break.
    
    Reviewed by:            imp
    Approved by:            imp
    Differential revision:  https://reviews.freebsd.org/D44452
---
 sys/dev/mpi3mr/mpi3mr.c     | 16 ++++++++++++++++
 sys/dev/mpi3mr/mpi3mr.h     |  2 ++
 sys/dev/mpi3mr/mpi3mr_cam.c | 35 +++++++++++++++++++++++++++++++++++
 sys/dev/mpi3mr/mpi3mr_cam.h |  1 +
 sys/modules/mpi3mr/Makefile |  3 +++
 5 files changed, 57 insertions(+)

diff --git a/sys/dev/mpi3mr/mpi3mr.c b/sys/dev/mpi3mr/mpi3mr.c
index 5227e8b60435..36e540520e42 100644
--- a/sys/dev/mpi3mr/mpi3mr.c
+++ b/sys/dev/mpi3mr/mpi3mr.c
@@ -2176,6 +2176,8 @@ static int mpi3mr_issue_iocinit(struct mpi3mr_softc *sc)
 	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 @@ void mpi3mr_update_device(struct mpi3mr_softc *sc,
 		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 @@ static void mpi3mr_invalidate_devhandles(struct mpi3mr_softc *sc)
 			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.h b/sys/dev/mpi3mr/mpi3mr.h
index 45f00068021e..31217f823569 100644
--- a/sys/dev/mpi3mr/mpi3mr.h
+++ b/sys/dev/mpi3mr/mpi3mr.h
@@ -231,6 +231,8 @@ extern char fmt_os_ver[16];
 
 #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_cam.c b/sys/dev/mpi3mr/mpi3mr_cam.c
index f8603eec9ae2..e3958ed8daf2 100644
--- a/sys/dev/mpi3mr/mpi3mr_cam.c
+++ b/sys/dev/mpi3mr/mpi3mr_cam.c
@@ -82,6 +82,7 @@
 #include "mpi3mr.h"
 #include <sys/time.h>			/* XXX for pcpu.h */
 #include <sys/pcpu.h>			/* XXX for PCPU_GET */
+#include <asm/unaligned.h>
 
 #define	smp_processor_id()  PCPU_GET(cpuid)
 
@@ -101,6 +102,37 @@ extern void mpi3mr_add_sg_single(void *paddr, U8 flags, U32 length,
 
 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 @@ mpi3mr_action_scsiio(struct mpi3mr_cam_softc *cam_sc, union ccb *ccb)
 		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/dev/mpi3mr/mpi3mr_cam.h b/sys/dev/mpi3mr/mpi3mr_cam.h
index 3a0526217f86..115ce0c4b8d7 100644
--- a/sys/dev/mpi3mr/mpi3mr_cam.h
+++ b/sys/dev/mpi3mr/mpi3mr_cam.h
@@ -121,6 +121,7 @@ struct mpi3mr_target {
 	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/modules/mpi3mr/Makefile b/sys/modules/mpi3mr/Makefile
index 3f1f63a94ac3..39aa2e3f0ddd 100644
--- a/sys/modules/mpi3mr/Makefile
+++ b/sys/modules/mpi3mr/Makefile
@@ -6,6 +6,9 @@ SRCS=	mpi3mr_pci.c mpi3mr.c mpi3mr_cam.c mpi3mr_app.c
 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 <bsd.kmod.mk>
 
 CWARNFLAGS.mpi3mr_sas.c=		${NO_WUNNEEDED_INTERNAL_DECL}