Date: Wed, 5 Nov 2003 00:08:49 +0100 From: "Walter C. Pelissero" <walter@pelissero.de> To: hackers@freebsd.org Subject: SCSI spin down on suspend (patch) Message-ID: <16296.12673.686825.358250@hyde.home.loc> In-Reply-To: <16293.41551.103901.897025@hyde.home.loc> References: <16293.41551.103901.897025@hyde.home.loc>
next in thread | previous in thread | raw e-mail | index | archive | help
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,
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?16296.12673.686825.358250>