Subject: kern/31034: Patch included to allow mpt(4) to send bus resets
To: None <kern-bug-people@netbsd.org, gnats-admin@netbsd.org,>
From: None <mtravis@NOSPAM.embedu.com>
List: netbsd-bugs
Date: 08/22/2005 04:06:00
>Number:         31034
>Category:       kern
>Synopsis:       Patch included to allow mpt(4) to send bus resets
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    kern-bug-people
>State:          open
>Class:          change-request
>Submitter-Id:   net
>Arrival-Date:   Mon Aug 22 04:06:00 +0000 2005
>Originator:     Mark Travis
>Release:        2.0.2
>Organization:
>Environment:
NetBSD node1 2.0.2 NetBSD 2.0.2 (GENERIC_reset2) #0: Sun Aug 21 20:41:50 PDT 2005  root@node1:/usr/src/sys/arch/amd64/compile/GENERIC_reset2 amd64

>Description:
mpt(4) doesn't yet support bus resets.  I've adapted FreeBSD 5.4's mpt_bus_reset function so that it works on a 2.0.2 kernel.  I haven't tested on -CURRENT, but there have been no functional changes to that driver (at least in sys/dev/ic/mpt_netbsd.c) between 2.0.2 and now.

I've tested the esiop(4) and ahc(4) drivers and they both have bus reset functions.  So I'm not asking for something new and strange to be implemented.

My kernel config is completely GENERIC.  I created the new config GENERIC_reset2 to remind myself that I'm running a patched kernel.
>How-To-Repeat:
Install a card which uses the mpt(4) driver.  Run "scsictl scsibusX reset" as root.  It will fail because there's nothing in the driver to perform the bus reset.

>Fix:
Apply to sys/dev/ic/mpt_netbsd.c:

--- mpt_netbsd.c        2005-08-21 19:58:07.000000000 -0700
+++ mpt_netbsd.c.bus_reset      2005-08-21 20:35:14.000000000 -0700
@@ -81,6 +81,8 @@

 #include <machine/stdarg.h>            /* for mpt_prt() */

+#include <sys/scsiio.h>                        /* for SCBUSIORESET definition */
+
 static int     mpt_poll(mpt_softc_t *, struct scsipi_xfer *, int);
 static void    mpt_timeout(void *);
 static void    mpt_done(mpt_softc_t *, uint32_t);
@@ -89,10 +91,13 @@
 static void    mpt_get_xfer_mode(mpt_softc_t *, struct scsipi_periph *);
 static void    mpt_ctlop(mpt_softc_t *, void *vmsg, uint32_t);
 static void    mpt_event_notify_reply(mpt_softc_t *, MSG_EVENT_NOTIFY_REPLY *);+static int     mpt_bus_reset(struct scsipi_channel *);

 static void    mpt_scsipi_request(struct scsipi_channel *,
                    scsipi_adapter_req_t, void *);
 static void    mpt_minphys(struct buf *);
+static int     mpt_ioctl(struct scsipi_channel *, u_long, caddr_t, int,
+                   struct proc *);

 void
 mpt_scsipi_attach(mpt_softc_t *mpt)
@@ -114,6 +119,7 @@
        adapt->adapt_max_periph = maxq;
        adapt->adapt_request = mpt_scsipi_request;
        adapt->adapt_minphys = mpt_minphys;
+       adapt->adapt_ioctl = mpt_ioctl;

        /* Fill in the scsipi_channel. */
        memset(chan, 0, sizeof(*chan));
@@ -1280,7 +1286,58 @@
        }
 }

-/* XXXJRT mpt_bus_reset() */
+/* bus reset functionality:
+ * adapted from FreeBSD 5.4 sys/dev/mpt/mpt_freebsd.c by Mark Travis
+ * in 08/2005.  Tested on amd64 platform with a pair of LSIU320 cards,
+ * which are single-channel U320 cards with the 53c1020 controller.
+ */
+
+static int
+mpt_bus_reset(struct scsipi_channel *chan)
+{
+       int error;
+       request_t *req;
+       /* this is the physical adapter */
+       mpt_softc_t *mpt = (void *) chan->chan_adapter->adapt_dev;
+       MSG_SCSI_TASK_MGMT *reset_req;
+
+       /* Get a request structure off the free list */
+       if ((req = mpt_get_request(mpt)) == NULL) {
+           return (XS_REQUEUE);
+       }
+
+       reset_req = req->req_vbuf;
+       bzero(reset_req, sizeof *reset_req);
+
+       reset_req->Function = MPI_FUNCTION_SCSI_TASK_MGMT;
+       reset_req->MsgContext = req->index;
+       reset_req->TaskType = MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS;
+       if (mpt->is_fc) {
+               /*
+                * Should really be TARGET_RESET_OPTION
+                */
+               /* I don't have an FC adapter, so this is untested
+                * mtravis 08/20/05
+                */
+               reset_req->MsgFlags =
+                   MPI_SCSITASKMGMT_MSGFLAGS_LIP_RESET_OPTION;
+       }
+       /* This is a bus reset function.  All targets/LUNs on the
+        * bus.
+        */
+       reset_req->TargetID = 255;
+       reset_req->LUN[1] = 255;
+
+       error = mpt_send_handshake_cmd(mpt,
+           sizeof (MSG_SCSI_TASK_MGMT), reset_req);
+       if (error) {
+               mpt_prt(mpt,
+                   "mpt_bus_reset: mpt_send_handshake return %d", error);
+               return (XS_DRIVER_STUFFUP);
+       } else {
+               return (XS_NOERROR);
+       }
+}

 /*****************************************************************************
  * SCSI interface routines
@@ -1322,3 +1379,26 @@
                bp->b_bcount = MPT_MAX_XFER;
        minphys(bp);
 }
+
+int
+mpt_ioctl(chan, cmd, arg, flag, p)
+       struct scsipi_channel *chan;
+       u_long cmd;
+       caddr_t arg;
+       int flag;
+       struct proc *p;
+{
+
+       switch (cmd) {
+       case SCBUSIORESET:
+               return(mpt_bus_reset(chan));
+       default:
+       /* adapt_ioctl is documented in scsipi(9)
+        * as being only for bus resets--but it seems to be called
+        * by other things from time to time.  Don't want to be
+        * triggering unwanted bus resets.  And don't want to do bus
+        * reset if they don't say the right ioctl.
+        */
+               return (ENOTTY);
+       }
+}