Date: Wed, 3 Feb 2010 08:42:09 +0000 (UTC) From: Alexander Motin <mav@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r203420 - in head/sys: cam kern Message-ID: <201002030842.o138g9lt034282@svn.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: mav Date: Wed Feb 3 08:42:08 2010 New Revision: 203420 URL: http://svn.freebsd.org/changeset/base/203420 Log: MFp4: Make CAM to stop all attached devices on system shutdown. It allows devices to park heads, reducing stress on power loss. Add `kern.cam.power_down` tunable and sysctl to controll it. Modified: head/sys/cam/cam_xpt.c head/sys/kern/kern_shutdown.c Modified: head/sys/cam/cam_xpt.c ============================================================================== --- head/sys/cam/cam_xpt.c Wed Feb 3 07:21:20 2010 (r203419) +++ head/sys/cam/cam_xpt.c Wed Feb 3 08:42:08 2010 (r203420) @@ -39,7 +39,7 @@ __FBSDID("$FreeBSD$"); #include <sys/time.h> #include <sys/conf.h> #include <sys/fcntl.h> -#include <sys/md5.h> +#include <sys/reboot.h> #include <sys/interrupt.h> #include <sys/sbuf.h> #include <sys/taskqueue.h> @@ -153,6 +153,10 @@ static struct xpt_softc xsoftc; TUNABLE_INT("kern.cam.boot_delay", &xsoftc.boot_delay); SYSCTL_INT(_kern_cam, OID_AUTO, boot_delay, CTLFLAG_RDTUN, &xsoftc.boot_delay, 0, "Bus registration wait time"); +static int xpt_power_down = 1; +TUNABLE_INT("kern.cam.power_down", &xpt_power_down); +SYSCTL_INT(_kern_cam, OID_AUTO, power_down, CTLFLAG_RW, + &xpt_power_down, 0, "Power down devices on shutdown"); /* Queues for our software interrupt handler */ typedef TAILQ_HEAD(cam_isrq, ccb_hdr) cam_isrq_t; @@ -236,6 +240,7 @@ static struct cam_ed* xpt_find_device(struct cam_et *target, lun_id_t lun_id); static void xpt_config(void *arg); static xpt_devicefunc_t xptpassannouncefunc; +static void xpt_shutdown(void *arg, int howto); static void xptaction(struct cam_sim *sim, union ccb *work_ccb); static void xptpoll(struct cam_sim *sim); static void camisr(void *); @@ -4660,6 +4665,12 @@ xpt_config(void *arg) #endif /* CAM_DEBUG_BUS */ #endif /* CAMDEBUG */ + /* Register our shutdown event handler */ + if ((EVENTHANDLER_REGISTER(shutdown_final, xpt_shutdown, + NULL, SHUTDOWN_PRI_FIRST)) == NULL) { + printf("xpt_config: failed to register shutdown event.\n"); + } + periphdriver_init(1); xpt_hold_boot(); callout_init(&xsoftc.boot_callout, 1); @@ -4667,7 +4678,7 @@ xpt_config(void *arg) xpt_boot_delay, NULL); /* Fire up rescan thread. */ if (kproc_create(xpt_scanner_thread, NULL, NULL, 0, 0, "xpt_thrd")) { - printf("xpt_init: failed to create rescan thread\n"); + printf("xpt_config: failed to create rescan thread.\n"); } } @@ -4741,6 +4752,87 @@ xpt_finishconfig_task(void *context, int free(context, M_CAMXPT); } +/* + * Power down all devices when we are going to power down the system. + */ +static void +xpt_shutdown_dev_done(struct cam_periph *periph, union ccb *done_ccb) +{ + + /* No-op. We're polling. */ + return; +} + +static int +xpt_shutdown_dev(struct cam_ed *device, void *arg) +{ + union ccb ccb; + struct cam_path path; + + if (device->flags & CAM_DEV_UNCONFIGURED) + return (1); + + if (device->protocol == PROTO_ATA) { + /* Only power down device if it supports power management. */ + if ((device->ident_data.support.command1 & + ATA_SUPPORT_POWERMGT) == 0) + return (1); + } else if (device->protocol != PROTO_SCSI) + return (1); + + xpt_compile_path(&path, + NULL, + device->target->bus->path_id, + device->target->target_id, + device->lun_id); + xpt_setup_ccb(&ccb.ccb_h, &path, CAM_PRIORITY_NORMAL); + if (device->protocol == PROTO_ATA) { + cam_fill_ataio(&ccb.ataio, + 1, + xpt_shutdown_dev_done, + CAM_DIR_NONE, + 0, + NULL, + 0, + 30*1000); + ata_28bit_cmd(&ccb.ataio, ATA_SLEEP, 0, 0, 0); + } else { + scsi_start_stop(&ccb.csio, + /*retries*/1, + xpt_shutdown_dev_done, + MSG_SIMPLE_Q_TAG, + /*start*/FALSE, + /*load/eject*/FALSE, + /*immediate*/TRUE, + SSD_FULL_SIZE, + /*timeout*/50*1000); + } + xpt_polled_action(&ccb); + + if ((ccb.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) + xpt_print(&path, "Device power down failed\n"); + 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); + xpt_release_path(&path); + return (1); +} + +static void +xpt_shutdown(void * arg, int howto) +{ + + if (!xpt_power_down) + return; + if ((howto & RB_POWEROFF) == 0) + return; + + xpt_for_all_devices(xpt_shutdown_dev, NULL); +} + cam_status xpt_register_async(int event, ac_callback_t *cbfunc, void *cbarg, struct cam_path *path) Modified: head/sys/kern/kern_shutdown.c ============================================================================== --- head/sys/kern/kern_shutdown.c Wed Feb 3 07:21:20 2010 (r203419) +++ head/sys/kern/kern_shutdown.c Wed Feb 3 08:42:08 2010 (r203420) @@ -142,7 +142,7 @@ shutdown_conf(void *unused) { EVENTHANDLER_REGISTER(shutdown_final, poweroff_wait, NULL, - SHUTDOWN_PRI_FIRST); + SHUTDOWN_PRI_FIRST + 100); EVENTHANDLER_REGISTER(shutdown_final, shutdown_halt, NULL, SHUTDOWN_PRI_LAST + 100); EVENTHANDLER_REGISTER(shutdown_final, shutdown_panic, NULL,
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201002030842.o138g9lt034282>