I amended the patch.  Now:
  - the behaviour is controlled by a sysctl (off by default)
. - a 1s delay is done after each drive spin up, making things
    easier for the system power supply
  - no need to define anything; APM or ACPI methods are chosen
    according to what has been made available in the kernel
    configuration file; if neither APM or ACPI is selcted the code
    is (mostly) removed

I tried to introduce a cache flush before the spin down, but it didn't
work (system panic), so the code is there but ifdef-ed out.  Maybe
somebody with a deeper knowledge of the CAM layer could shed a light.

Once I get some confidence about the stability of the patch, and if
anybody is interested, I could submit a pr.

One thing I forgot to mention in my former message: the patch has
been tested on 4.9-STABLE only.

Enjoy.

-- 
walter pelissero
http://www.pelissero.de



--- scsi_da.c.orig      Mon Nov  3 00:15:55 2003
+++ scsi_da.c   Tue Nov  4 20:20:54 2003
@@ -72,6 +72,21 @@
 #include <cam/scsi/scsi_da.h>
 #endif /* !_KERNEL */
 
+#include <apm.h>
+#include <acpica.h>
+/* to get DELAY declaration */
+#include <machine/clock.h>
+
+#if NAPM > 0
+# include <machine/apm_bios.h>
+#endif
+
+#if NACPICA > 0
+# include <sys/bus.h>
+# include <dev/acpica/acpica_support.h>
+# include <dev/acpica/acpivar.h>
+#endif
+
 #ifdef _KERNEL
 typedef enum {
        DA_STATE_PROBE,
@@ -414,6 +435,8 @@
                                  struct scsi_read_capacity_data * rdcap);
 static timeout_t       dasendorderedtag;
 static void            dashutdown(void *arg, int howto);
+static void            dasync(struct cam_periph *periph);
+static void            daspin(struct cam_periph *periph, int up);
 
 #ifndef DA_DEFAULT_TIMEOUT
 #define DA_DEFAULT_TIMEOUT 60  /* Timeout in seconds */
@@ -435,6 +458,11 @@
 SYSCTL_INT(_kern_cam_da, OID_AUTO, default_timeout, CTLFLAG_RW,
            &da_default_timeout, 0, "Normal I/O timeout (in seconds)");
 TUNABLE_INT("kern.cam.da.default_timeout", &da_default_timeout);
+#if NAPM > 0 || NACPICA > 0
+static int da_spindown_on_suspend = 0;
+SYSCTL_INT(_kern_cam_da, OID_AUTO, spindown_on_suspend, CTLFLAG_RW,
+           &da_spindown_on_suspend, 0, "Spindown hard disks on system suspend");
+#endif
 
 /*
  * DA_ORDEREDTAG_INTERVAL determines how often, relative
@@ -490,6 +518,95 @@
 static SLIST_HEAD(,da_softc) softc_list;
 static struct extend_array *daperiphs;
 
+#if NAPM > 0 || NACPICA > 0
+/* Step through all DA peripheral drivers and spin them up/down. */
+static void
+da_spin_all(int up)
+{
+       struct cam_periph *periph;
+
+       for (periph = TAILQ_FIRST(&dadriver.units); periph != NULL;
+            periph = TAILQ_NEXT(periph, unit_links)) {
+#if 0                  /* this panics the system -wcp4/11/03. */
+               /* if spinning down, it might be safer to synchronise
+                * the cache before */
+               if (!up)
+                       dasync(periph);
+#endif
+               daspin(periph, up);
+               /* If spinning up, wait a moment to avoid overloading
+                * the power supply. A better solution would be to
+                * check until the device is ready.  I don't know how
+                * to do it, though. */
+               if (up)
+                       DELAY(1000 * 1000);
+       }
+}
+
+static void
+da_suspend(void)
+{
+       if (da_spindown_on_suspend)
+               da_spin_all(0);
+}
+
+static void
+da_resume(void)
+{
+       if (da_spindown_on_suspend)
+               da_spin_all(1);
+}
+#endif /* NAPM > 0 || NACPICA > 0 */
+
+/* The mechanism to hook functions to certain events in the APM and
+ * ACPI code are different for no apparent reason. */
+
+#if NAPM > 0
+static int
+da_apm_suspend (void *junk)
+{
+       da_suspend();
+       return 0;
+}
+
+static int
+da_apm_resume (void *junk)
+{
+       da_resume();
+       return 0;
+}
+
+struct apmhook da_apm_suspend_hook = {
+       0,                      /* next */
+       da_apm_suspend,         /* fun */
+       0,                      /* arg */
+       "da_suspend",           /* name */
+       0                       /* order */
+};
+
+struct apmhook da_apm_resume_hook = {
+       0,                      /* next */
+       da_apm_resume,          /* fun */
+       0,                      /* arg */
+       "da_resume",            /* name */
+       0                       /* order */
+};
+#endif /* NAPM > 0 */
+
+#if NACPICA > 0
+static void
+da_acpi_resume(void *arg, int state)
+{
+       da_resume();
+}
+
+static void
+da_acpi_suspend(void *arg, int state)
+{
+       da_suspend();
+}
+#endif /* NACPICA > 0 */
+
 static int
 daopen(dev_t dev, int flags, int fmt, struct proc *p)
 {
@@ -987,6 +1104,14 @@
                if ((EVENTHANDLER_REGISTER(shutdown_post_sync, dashutdown, 
                                           NULL, SHUTDOWN_PRI_DEFAULT)) == NULL)
                    printf("dainit: shutdown event registration failed!\n");
+#if NACPICA > 0
+               EVENTHANDLER_REGISTER(acpi_sleep_event, da_acpi_suspend, NULL, 
ACPI_EVENT_PRI_DEFAULT);
+               EVENTHANDLER_REGISTER(acpi_wakeup_event, da_acpi_resume, NULL, 
ACPI_EVENT_PRI_DEFAULT);
+#endif
+#if NAPM > 0
+               apm_hook_establish (APM_HOOK_SUSPEND, &da_apm_suspend_hook);
+               apm_hook_establish (APM_HOOK_RESUME, &da_apm_resume_hook);
+#endif
        }
 }
 
@@ -1888,6 +2013,109 @@
        timeout(dasendorderedtag, NULL,
                (da_default_timeout * hz) / DA_ORDEREDTAG_INTERVAL);
 }
+
+static void
+daspin(struct cam_periph *periph, int up)
+{
+       struct da_softc *softc;
+       union ccb ccb;
+
+       softc = (struct da_softc *)periph->softc;
+
+       xpt_setup_ccb(&ccb.ccb_h, periph->path, /*priority*/1);
+
+       ccb.ccb_h.ccb_state = DA_CCB_DUMP;
+       scsi_start_stop(&ccb.csio,
+                       /*retries*/ 1,
+                       /*cbfcnp*/ dadone,
+                       MSG_SIMPLE_Q_TAG,
+                       /*up/down*/ up,
+                       /*load_eject*/ 0,
+                       /*immediate*/ FALSE,
+                       /*sense_len*/ SSD_FULL_SIZE,
+                       /*timeout*/ 50000);
+       xpt_polled_action(&ccb);
+
+       if ((ccb.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
+               if (((ccb.ccb_h.status & CAM_STATUS_MASK) ==
+                    CAM_SCSI_STATUS_ERROR)
+                   && (ccb.csio.scsi_status == SCSI_STATUS_CHECK_COND)){
+                       int error_code, sense_key, asc, ascq;
+
+                       scsi_extract_sense(&ccb.csio.sense_data,
+                                          &error_code, &sense_key,
+                                          &asc, &ascq);
+
+                       if (sense_key != SSD_KEY_ILLEGAL_REQUEST)
+                               scsi_sense_print(&ccb.csio);
+               } else {
+                       xpt_print_path(periph->path);
+                       printf("Spin %s disk failed, status "
+                              "== 0x%x, scsi status == 0x%x\n",
+                              (up ? "up" : "down"),
+                              ccb.ccb_h.status, ccb.csio.scsi_status);
+               }
+       }
+       CAM_DEBUG(periph->path, CAM_DEBUG_TRACE,
+                 ("daspin: spinned ~A\n", (up ? "up" : "down")));
+}
+
+/* This would be used only in da_spin_all and it panics the system
+   -wcp4/11/03.*/
+#if 0
+static void
+dasync(struct cam_periph *periph)
+{
+       struct da_softc *softc;
+       union ccb ccb;
+
+       softc = (struct da_softc *)periph->softc;
+
+       /* We only sync the cache if the drive is open, and if the
+        * drive is capable of it. */
+       if (((softc->flags & DA_FLAG_OPEN) == 0)
+           || (softc->quirks & DA_Q_NO_SYNC_CACHE))
+               return;
+
+       xpt_setup_ccb(&ccb.ccb_h, periph->path, /*priority*/1);
+       ccb.ccb_h.ccb_state = DA_CCB_DUMP;
+       scsi_synchronize_cache(&ccb.csio,
+                              /*retries*/1,
+                              /*cbfcnp*/dadone,
+                              MSG_SIMPLE_Q_TAG,
+                              /*begin_lba*/0, /* whole disk */
+                              /*lb_count*/0,
+                              SSD_FULL_SIZE,
+                              5 * 60 * 1000);
+       xpt_polled_action(&ccb);
+
+       if ((ccb.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
+               if (((ccb.ccb_h.status & CAM_STATUS_MASK) ==
+                    CAM_SCSI_STATUS_ERROR)
+                   && (ccb.csio.scsi_status == SCSI_STATUS_CHECK_COND)){
+                       int error_code, sense_key, asc, ascq;
+
+                       scsi_extract_sense(&ccb.csio.sense_data,
+                                          &error_code, &sense_key,
+                                          &asc, &ascq);
+
+                       if (sense_key != SSD_KEY_ILLEGAL_REQUEST)
+                               scsi_sense_print(&ccb.csio);
+               } else {
+                       xpt_print_path(periph->path);
+                       printf("Synchronize cache failed, status "
+                              "== 0x%x, scsi status == 0x%x\n",
+                              ccb.ccb_h.status, ccb.csio.scsi_status);
+               }
+       }
+       if ((ccb.ccb_h.status & CAM_DEV_QFRZN) != 0)
+               cam_release_devq(ccb.ccb_h.path,
+                                /*relsim_flags*/0,
+                                /*reduction*/0,
+                                /*timeout*/0,
+                                /*getcount_only*/0);
+}
+#endif /* 0 */
 
 /*
  * Step through all DA peripheral drivers, and if the device is still open,

_______________________________________________
[EMAIL PROTECTED] mailing list
http://lists.freebsd.org/mailman/listinfo/freebsd-hackers
To unsubscribe, send any mail to "[EMAIL PROTECTED]"

Reply via email to