Date: Mon, 3 Nov 2003 01:33:19 +0100 From: "Walter C. Pelissero" <walter@pelissero.de> To: hackers@freebsd.org Subject: SCSI spin down on suspend (patch) Message-ID: <16293.41551.103901.897025@hyde.home.loc>
next in thread | raw e-mail | index | archive | help
I eventually got around to hack scsi_da.c to implement spin down/up on suspend/resume events of APM or ACPI. Actually the ACPI stuff is untested and the APM once didn't work properly on my system: one of the disks, after resume, was misbehaving (hardware errors). A power toggle fixed the problem, though. I suspect this code could be improved introducing a delay after the spin up of each HD (I've got 6 HD hooked to a 350W power supply, this might explain the hardware error), but not being a kernel hacker myself, I don't know how to proceed. Suggestions are welcome. Please note you need to define CAM_APM_COOP or CAM_ACPI_COOP. --- scsi_da.c.orig Mon Nov 3 00:15:55 2003 +++ scsi_da.c Mon Nov 3 00:18:04 2003 @@ -72,6 +72,15 @@ #include <cam/scsi/scsi_da.h> #endif /* !_KERNEL */ +#ifdef CAM_ACPI_COOP +# include <sys/bus.h> +# include <dev/acpica/acpica_support.h> +# include <dev/acpica/acpivar.h> +#endif /* CAM_ACPI_COOP */ +#ifdef CAM_APM_COOP +# include <machine/apm_bios.h> +#endif + #ifdef _KERNEL typedef enum { DA_STATE_PROBE, @@ -142,6 +151,40 @@ da_quirks quirks; }; +#ifdef CAM_APM_COOP +static void da_start_stop_all(int start); + +static int +da_apm_suspend (void *junk) +{ + da_start_stop_all (0); + return 0; +} + +static int +da_apm_resume (void *junk) +{ + da_start_stop_all (1); + return 0; +} + +struct apmhook da_suspend_hook = { + 0, /* next */ + da_apm_suspend, /* fun */ + 0, /* arg */ + "da_suspend", /* name */ + 0 /* order */ +}; + +struct apmhook da_resume_hook = { + 0, /* next */ + da_apm_resume, /* fun */ + 0, /* arg */ + "da_resume", /* name */ + 0 /* order */ +}; +#endif /* CAM_APM_COOP */ + static const char quantum[] = "QUANTUM"; static const char microp[] = "MICROP"; @@ -213,6 +256,12 @@ {T_DIRECT, SIP_MEDIA_FIXED, quantum, "LPS525S", "*"}, /*quirks*/ DA_Q_NO_SYNC_CACHE }, + { {T_DIRECT, SIP_MEDIA_FIXED, quantum, "LPS540S", "*"}, + DA_Q_NO_SYNC_CACHE }, + { {T_DIRECT, SIP_MEDIA_FIXED, "CONNER", "CP3500*", "*"}, + DA_Q_NO_SYNC_CACHE }, + { {T_DIRECT, SIP_MEDIA_REMOVABLE, "DataFab*", "*", "*"}, + DA_Q_NO_6_BYTE | DA_Q_NO_SYNC_CACHE }, { /* * Doesn't work correctly with 6 byte reads/writes. @@ -935,6 +984,71 @@ return (0); } +/* + * Step through all DA peripheral drivers and spin them up/down. + */ +static void +da_start_stop_all(int start) +{ + struct cam_periph *periph; + struct da_softc *softc; + + for (periph = TAILQ_FIRST(&dadriver.units); periph != NULL; + periph = TAILQ_NEXT(periph, unit_links)) { + 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, + start, + /*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("Suspend disk failed, status " + "== 0x%x, scsi status == 0x%x\n", + ccb.ccb_h.status, ccb.csio.scsi_status); + } + } + } +} + +#ifdef CAM_ACPI_COOP +static void +da_start_all(void *arg, int state) +{ + da_start_stop_all(1); +} + +static void +da_stop_all(void *arg, int state) +{ + da_start_stop_all(0); +} +#endif /* CAM_ACPI_COOP */ + static void dainit(void) { @@ -987,6 +1101,14 @@ if ((EVENTHANDLER_REGISTER(shutdown_post_sync, dashutdown, NULL, SHUTDOWN_PRI_DEFAULT)) == NULL) printf("dainit: shutdown event registration failed!\n"); +#ifdef CAM_ACPI_COOP + EVENTHANDLER_REGISTER(acpi_sleep_event, da_stop_all, NULL, ACPI_EVENT_PRI_DEFAULT); + EVENTHANDLER_REGISTER(acpi_wakeup_event, da_start_all, NULL, ACPI_EVENT_PRI_DEFAULT); +#endif +#ifdef CAM_APM_COOP + apm_hook_establish (APM_HOOK_SUSPEND, &da_suspend_hook); + apm_hook_establish (APM_HOOK_RESUME, &da_resume_hook); +#endif } } -- walter pelissero http://www.pelissero.de
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?16293.41551.103901.897025>