mirror of
https://git.FreeBSD.org/src.git
synced 2024-12-15 10:17:20 +00:00
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:
parent
95adbc959b
commit
fd104c151b
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=214279
@ -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
|
||||
|
@ -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 */
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user