Mostly revert r203420, and add similar functionality into ada(4) since the

existing code caused problems with some SCSI controllers.

A new sysctl kern.cam.ada.spindown_shutdown has been added that controls
whether or not to spin-down disks when shutting down.
Spinning down the disks unloads/parks the heads - this is
much better than removing power when the disk is still
spinning because otherwise an Emergency Unload occurs which may cause damage
to the actuator.

PR:	kern/140752
Submitted by:   olli
Reviewed by:	arundel
Discussed with: mav
MFC after:	2 weeks
This commit is contained in:
Rebecca Cran 2010-10-24 16:31:57 +00:00
parent 95adbc959b
commit fd104c151b
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=214279
4 changed files with 67 additions and 95 deletions

View File

@ -118,6 +118,9 @@ This variable determines how long the
driver will wait before timing out an outstanding command.
The units for this value are seconds, and the default is currently 30
seconds.
.It kern.cam.ada.spindown_shutdown
.Pp
This variable determines whether to spin-down disks when shutting down.
.El
.Sh FILES
.Bl -tag -width ".Pa /dev/ada*" -compact

View File

@ -42,6 +42,7 @@ __FBSDID("$FreeBSD$");
#include <sys/eventhandler.h>
#include <sys/malloc.h>
#include <sys/cons.h>
#include <sys/reboot.h>
#include <geom/geom_disk.h>
#endif /* _KERNEL */
@ -79,7 +80,8 @@ typedef enum {
ADA_FLAG_CAN_TRIM = 0x080,
ADA_FLAG_OPEN = 0x100,
ADA_FLAG_SCTX_INIT = 0x200,
ADA_FLAG_CAN_CFA = 0x400
ADA_FLAG_CAN_CFA = 0x400,
ADA_FLAG_CAN_POWERMGT = 0x800
} ada_flags;
typedef enum {
@ -180,6 +182,10 @@ static void adashutdown(void *arg, int howto);
#define ADA_DEFAULT_SEND_ORDERED 1
#endif
#ifndef ADA_DEFAULT_SPINDOWN_SHUTDOWN
#define ADA_DEFAULT_SPINDOWN_SHUTDOWN 1
#endif
/*
* Most platforms map firmware geometry to actual, but some don't. If
* not overridden, default to nothing.
@ -191,6 +197,7 @@ static void adashutdown(void *arg, int howto);
static int ada_retry_count = ADA_DEFAULT_RETRY;
static int ada_default_timeout = ADA_DEFAULT_TIMEOUT;
static int ada_send_ordered = ADA_DEFAULT_SEND_ORDERED;
static int ada_spindown_shutdown = ADA_DEFAULT_SPINDOWN_SHUTDOWN;
SYSCTL_NODE(_kern_cam, OID_AUTO, ada, CTLFLAG_RD, 0,
"CAM Direct Access Disk driver");
@ -203,6 +210,9 @@ TUNABLE_INT("kern.cam.ada.default_timeout", &ada_default_timeout);
SYSCTL_INT(_kern_cam_ada, OID_AUTO, ada_send_ordered, CTLFLAG_RW,
&ada_send_ordered, 0, "Send Ordered Tags");
TUNABLE_INT("kern.cam.ada.ada_send_ordered", &ada_send_ordered);
SYSCTL_INT(_kern_cam_ada, OID_AUTO, spindown_shutdown, CTLFLAG_RW,
&ada_spindown_shutdown, 0, "Spin down upon shutdown");
TUNABLE_INT("kern.cam.ada.spindown_shutdown", &ada_spindown_shutdown);
/*
* ADA_ORDEREDTAG_INTERVAL determines how often, relative
@ -665,6 +675,8 @@ adaregister(struct cam_periph *periph, void *arg)
softc->flags |= ADA_FLAG_CAN_48BIT;
if (cgd->ident_data.support.command2 & ATA_SUPPORT_FLUSHCACHE)
softc->flags |= ADA_FLAG_CAN_FLUSHCACHE;
if (cgd->ident_data.support.command1 & ATA_SUPPORT_POWERMGT)
softc->flags |= ADA_FLAG_CAN_POWERMGT;
if (cgd->ident_data.satacapabilities & ATA_SUPPORT_NCQ &&
cgd->inq_flags & SID_CmdQue)
softc->flags |= ADA_FLAG_CAN_NCQ;
@ -1227,6 +1239,56 @@ adashutdown(void * arg, int howto)
/*getcount_only*/0);
cam_periph_unlock(periph);
}
if (ada_spindown_shutdown == 0 ||
(howto & (RB_HALT | RB_POWEROFF)) == 0)
return;
TAILQ_FOREACH(periph, &adadriver.units, unit_links) {
union ccb ccb;
/* If we paniced with lock held - not recurse here. */
if (cam_periph_owned(periph))
continue;
cam_periph_lock(periph);
softc = (struct ada_softc *)periph->softc;
/*
* We only spin-down the drive if it is capable of it..
*/
if ((softc->flags & ADA_FLAG_CAN_POWERMGT) == 0) {
cam_periph_unlock(periph);
continue;
}
if (bootverbose)
xpt_print(periph->path, "spin-down\n");
xpt_setup_ccb(&ccb.ccb_h, periph->path, CAM_PRIORITY_NORMAL);
ccb.ccb_h.ccb_state = ADA_CCB_DUMP;
cam_fill_ataio(&ccb.ataio,
1,
adadone,
CAM_DIR_NONE,
0,
NULL,
0,
ada_default_timeout*1000);
ata_28bit_cmd(&ccb.ataio, ATA_STANDBY_IMMEDIATE, 0, 0, 0);
xpt_polled_action(&ccb);
if ((ccb.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP)
xpt_print(periph->path, "Spin-down disk 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);
cam_periph_unlock(periph);
}
}
#endif /* _KERNEL */

View File

@ -39,7 +39,6 @@ __FBSDID("$FreeBSD$");
#include <sys/time.h>
#include <sys/conf.h>
#include <sys/fcntl.h>
#include <sys/reboot.h>
#include <sys/interrupt.h>
#include <sys/sbuf.h>
#include <sys/taskqueue.h>
@ -153,10 +152,6 @@ 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 = 0;
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;
@ -250,7 +245,6 @@ 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 *);
@ -4538,12 +4532,6 @@ 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);
@ -4625,87 +4613,6 @@ xpt_finishconfig_task(void *context, int pending)
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)

View File

@ -144,7 +144,7 @@ shutdown_conf(void *unused)
{
EVENTHANDLER_REGISTER(shutdown_final, poweroff_wait, NULL,
SHUTDOWN_PRI_FIRST + 100);
SHUTDOWN_PRI_FIRST);
EVENTHANDLER_REGISTER(shutdown_final, shutdown_halt, NULL,
SHUTDOWN_PRI_LAST + 100);
EVENTHANDLER_REGISTER(shutdown_final, shutdown_panic, NULL,