mirror of
https://git.FreeBSD.org/src.git
synced 2024-12-17 10:26:15 +00:00
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.
This commit is contained in:
parent
43271eacad
commit
6f15a274a8
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=203420
@ -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 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)
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user