mirror of
https://git.proxmox.com/git/mirror_zfs.git
synced 2026-05-22 02:27:36 +03:00
zpool: Add slot power control, print power status
Add `zpool` flags to control the slot power to drives. This assumes
your SAS or NVMe enclosure supports slot power control via sysfs.
The new `--power` flag is added to `zpool offline|online|clear`:
zpool offline --power <pool> <device> Turn off device slot power
zpool online --power <pool> <device> Turn on device slot power
zpool clear --power <pool> [device] Turn on device slot power
If the ZPOOL_AUTO_POWER_ON_SLOT env var is set, then the '--power'
option is automatically implied for `zpool online` and `zpool clear`
and does not need to be passed.
zpool status also gets a --power option to print the slot power status.
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Mart Frauenlob <AllKind@fastest.cc>
Signed-off-by: Tony Hutter <hutter2@llnl.gov>
Closes #15662
This commit is contained in:
@@ -124,3 +124,17 @@ check_file(const char *file, boolean_t force, boolean_t isspare)
|
||||
{
|
||||
return (check_file_generic(file, force, isspare));
|
||||
}
|
||||
|
||||
int
|
||||
zpool_power_current_state(zpool_handle_t *zhp, char *vdev)
|
||||
{
|
||||
/* Enclosure slot power not supported on FreeBSD yet */
|
||||
return (-1);
|
||||
}
|
||||
|
||||
int
|
||||
zpool_power(zpool_handle_t *zhp, char *vdev, boolean_t turn_on)
|
||||
{
|
||||
/* Enclosure slot power not supported on FreeBSD yet */
|
||||
return (ENOTSUP);
|
||||
}
|
||||
|
||||
@@ -416,3 +416,258 @@ check_file(const char *file, boolean_t force, boolean_t isspare)
|
||||
{
|
||||
return (check_file_generic(file, force, isspare));
|
||||
}
|
||||
|
||||
/*
|
||||
* Read from a sysfs file and return an allocated string. Removes
|
||||
* the newline from the end of the string if there is one.
|
||||
*
|
||||
* Returns a string on success (which must be freed), or NULL on error.
|
||||
*/
|
||||
static char *zpool_sysfs_gets(char *path)
|
||||
{
|
||||
int fd;
|
||||
struct stat statbuf;
|
||||
char *buf = NULL;
|
||||
ssize_t count = 0;
|
||||
fd = open(path, O_RDONLY);
|
||||
if (fd < 0)
|
||||
return (NULL);
|
||||
|
||||
if (fstat(fd, &statbuf) != 0) {
|
||||
close(fd);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
buf = calloc(sizeof (*buf), statbuf.st_size + 1);
|
||||
if (buf == NULL) {
|
||||
close(fd);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Note, we can read less bytes than st_size, and that's ok. Sysfs
|
||||
* files will report their size is 4k even if they only return a small
|
||||
* string.
|
||||
*/
|
||||
count = read(fd, buf, statbuf.st_size);
|
||||
if (count < 0) {
|
||||
/* Error doing read() or we overran the buffer */
|
||||
close(fd);
|
||||
free(buf);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
/* Remove trailing newline */
|
||||
if (buf[count - 1] == '\n')
|
||||
buf[count - 1] = 0;
|
||||
|
||||
close(fd);
|
||||
|
||||
return (buf);
|
||||
}
|
||||
|
||||
/*
|
||||
* Write a string to a sysfs file.
|
||||
*
|
||||
* Returns 0 on success, non-zero otherwise.
|
||||
*/
|
||||
static int zpool_sysfs_puts(char *path, char *str)
|
||||
{
|
||||
FILE *file;
|
||||
|
||||
file = fopen(path, "w");
|
||||
if (!file) {
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (fputs(str, file) < 0) {
|
||||
fclose(file);
|
||||
return (-2);
|
||||
}
|
||||
fclose(file);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Given a vdev nvlist_t, rescan its enclosure sysfs path */
|
||||
static void
|
||||
rescan_vdev_config_dev_sysfs_path(nvlist_t *vdev_nv)
|
||||
{
|
||||
update_vdev_config_dev_sysfs_path(vdev_nv,
|
||||
fnvlist_lookup_string(vdev_nv, ZPOOL_CONFIG_PATH),
|
||||
ZPOOL_CONFIG_VDEV_ENC_SYSFS_PATH);
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a power string: "on", "off", "1", or "0", return 0 if it's an
|
||||
* off value, 1 if it's an on value, and -1 if the value is unrecognized.
|
||||
*/
|
||||
static int zpool_power_parse_value(char *str)
|
||||
{
|
||||
if ((strcmp(str, "off") == 0) || (strcmp(str, "0") == 0))
|
||||
return (0);
|
||||
|
||||
if ((strcmp(str, "on") == 0) || (strcmp(str, "1") == 0))
|
||||
return (1);
|
||||
|
||||
return (-1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a vdev string return an allocated string containing the sysfs path to
|
||||
* its power control file. Also do a check if the power control file really
|
||||
* exists and has correct permissions.
|
||||
*
|
||||
* Example returned strings:
|
||||
*
|
||||
* /sys/class/enclosure/0:0:122:0/10/power_status
|
||||
* /sys/bus/pci/slots/10/power
|
||||
*
|
||||
* Returns allocated string on success (which must be freed), NULL on failure.
|
||||
*/
|
||||
static char *
|
||||
zpool_power_sysfs_path(zpool_handle_t *zhp, char *vdev)
|
||||
{
|
||||
const char *enc_sysfs_dir = NULL;
|
||||
char *path = NULL;
|
||||
nvlist_t *vdev_nv = zpool_find_vdev(zhp, vdev, NULL, NULL, NULL);
|
||||
|
||||
if (vdev_nv == NULL) {
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
/* Make sure we're getting the updated enclosure sysfs path */
|
||||
rescan_vdev_config_dev_sysfs_path(vdev_nv);
|
||||
|
||||
if (nvlist_lookup_string(vdev_nv, ZPOOL_CONFIG_VDEV_ENC_SYSFS_PATH,
|
||||
&enc_sysfs_dir) != 0) {
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
if (asprintf(&path, "%s/power_status", enc_sysfs_dir) == -1)
|
||||
return (NULL);
|
||||
|
||||
if (access(path, W_OK) != 0) {
|
||||
free(path);
|
||||
path = NULL;
|
||||
/* No HDD 'power_control' file, maybe it's NVMe? */
|
||||
if (asprintf(&path, "%s/power", enc_sysfs_dir) == -1) {
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
if (access(path, R_OK | W_OK) != 0) {
|
||||
/* Not NVMe either */
|
||||
free(path);
|
||||
return (NULL);
|
||||
}
|
||||
}
|
||||
|
||||
return (path);
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a path to a sysfs power control file, return B_TRUE if you should use
|
||||
* "on/off" words to control it, or B_FALSE otherwise ("0/1" to control).
|
||||
*/
|
||||
static boolean_t
|
||||
zpool_power_use_word(char *sysfs_path)
|
||||
{
|
||||
if (strcmp(&sysfs_path[strlen(sysfs_path) - strlen("power_status")],
|
||||
"power_status") == 0) {
|
||||
return (B_TRUE);
|
||||
}
|
||||
return (B_FALSE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check the sysfs power control value for a vdev.
|
||||
*
|
||||
* Returns:
|
||||
* 0 - Power is off
|
||||
* 1 - Power is on
|
||||
* -1 - Error or unsupported
|
||||
*/
|
||||
int
|
||||
zpool_power_current_state(zpool_handle_t *zhp, char *vdev)
|
||||
{
|
||||
char *val;
|
||||
int rc;
|
||||
|
||||
char *path = zpool_power_sysfs_path(zhp, vdev);
|
||||
if (path == NULL)
|
||||
return (-1);
|
||||
|
||||
val = zpool_sysfs_gets(path);
|
||||
if (val == NULL) {
|
||||
free(path);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
rc = zpool_power_parse_value(val);
|
||||
free(val);
|
||||
free(path);
|
||||
return (rc);
|
||||
}
|
||||
|
||||
/*
|
||||
* Turn on or off the slot to a device
|
||||
*
|
||||
* Device path is the full path to the device (like /dev/sda or /dev/sda1).
|
||||
*
|
||||
* Return code:
|
||||
* 0: Success
|
||||
* ENOTSUP: Power control not supported for OS
|
||||
* EBADSLT: Couldn't read current power state
|
||||
* ENOENT: No sysfs path to power control
|
||||
* EIO: Couldn't write sysfs power value
|
||||
* EBADE: Sysfs power value didn't change
|
||||
*/
|
||||
int
|
||||
zpool_power(zpool_handle_t *zhp, char *vdev, boolean_t turn_on)
|
||||
{
|
||||
char *sysfs_path;
|
||||
const char *val;
|
||||
int rc;
|
||||
int timeout_ms;
|
||||
|
||||
rc = zpool_power_current_state(zhp, vdev);
|
||||
if (rc == -1) {
|
||||
return (EBADSLT);
|
||||
}
|
||||
|
||||
/* Already correct value? */
|
||||
if (rc == (int)turn_on)
|
||||
return (0);
|
||||
|
||||
sysfs_path = zpool_power_sysfs_path(zhp, vdev);
|
||||
if (sysfs_path == NULL)
|
||||
return (ENOENT);
|
||||
|
||||
if (zpool_power_use_word(sysfs_path)) {
|
||||
val = turn_on ? "on" : "off";
|
||||
} else {
|
||||
val = turn_on ? "1" : "0";
|
||||
}
|
||||
|
||||
rc = zpool_sysfs_puts(sysfs_path, (char *)val);
|
||||
|
||||
free(sysfs_path);
|
||||
if (rc != 0) {
|
||||
return (EIO);
|
||||
}
|
||||
|
||||
/*
|
||||
* Wait up to 30 seconds for sysfs power value to change after
|
||||
* writing it.
|
||||
*/
|
||||
timeout_ms = zpool_getenv_int("ZPOOL_POWER_ON_SLOT_TIMEOUT_MS", 30000);
|
||||
for (int i = 0; i < MAX(1, timeout_ms / 200); i++) {
|
||||
rc = zpool_power_current_state(zhp, vdev);
|
||||
if (rc == (int)turn_on)
|
||||
return (0); /* success */
|
||||
|
||||
fsleep(0.200); /* 200ms */
|
||||
}
|
||||
|
||||
/* sysfs value never changed */
|
||||
return (EBADE);
|
||||
}
|
||||
|
||||
@@ -554,6 +554,10 @@ for_each_vdev_run_cb(void *zhp_data, nvlist_t *nv, void *cb_vcdl)
|
||||
if (nvlist_lookup_string(nv, ZPOOL_CONFIG_PATH, &path) != 0)
|
||||
return (1);
|
||||
|
||||
/* Make sure we're getting the updated enclosure sysfs path */
|
||||
update_vdev_config_dev_sysfs_path(nv, path,
|
||||
ZPOOL_CONFIG_VDEV_ENC_SYSFS_PATH);
|
||||
|
||||
nvlist_lookup_string(nv, ZPOOL_CONFIG_VDEV_ENC_SYSFS_PATH,
|
||||
&vdev_enc_sysfs_path);
|
||||
|
||||
|
||||
+210
-29
@@ -353,7 +353,7 @@ get_usage(zpool_help_t idx)
|
||||
return (gettext("\tattach [-fsw] [-o property=value] "
|
||||
"<pool> <device> <new-device>\n"));
|
||||
case HELP_CLEAR:
|
||||
return (gettext("\tclear [-nF] <pool> [device]\n"));
|
||||
return (gettext("\tclear [[--power]|[-nF]] <pool> [device]\n"));
|
||||
case HELP_CREATE:
|
||||
return (gettext("\tcreate [-fnd] [-o property=value] ... \n"
|
||||
"\t [-O file-system-property=value] ... \n"
|
||||
@@ -389,9 +389,11 @@ get_usage(zpool_help_t idx)
|
||||
"[-T d|u] [pool] ... \n"
|
||||
"\t [interval [count]]\n"));
|
||||
case HELP_OFFLINE:
|
||||
return (gettext("\toffline [-f] [-t] <pool> <device> ...\n"));
|
||||
return (gettext("\toffline [--power]|[[-f][-t]] <pool> "
|
||||
"<device> ...\n"));
|
||||
case HELP_ONLINE:
|
||||
return (gettext("\tonline [-e] <pool> <device> ...\n"));
|
||||
return (gettext("\tonline [--power][-e] <pool> <device> "
|
||||
"...\n"));
|
||||
case HELP_REPLACE:
|
||||
return (gettext("\treplace [-fsw] [-o property=value] "
|
||||
"<pool> <device> [new-device]\n"));
|
||||
@@ -410,7 +412,7 @@ get_usage(zpool_help_t idx)
|
||||
return (gettext("\ttrim [-dw] [-r <rate>] [-c | -s] <pool> "
|
||||
"[<device> ...]\n"));
|
||||
case HELP_STATUS:
|
||||
return (gettext("\tstatus [-c [script1,script2,...]] "
|
||||
return (gettext("\tstatus [--power] [-c [script1,script2,...]] "
|
||||
"[-igLpPstvxD] [-T d|u] [pool] ... \n"
|
||||
"\t [interval [count]]\n"));
|
||||
case HELP_UPGRADE:
|
||||
@@ -516,6 +518,77 @@ print_vdev_prop_cb(int prop, void *cb)
|
||||
return (ZPROP_CONT);
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a leaf vdev name like 'L5' return its VDEV_CONFIG_PATH like
|
||||
* '/dev/disk/by-vdev/L5'.
|
||||
*/
|
||||
static const char *
|
||||
vdev_name_to_path(zpool_handle_t *zhp, char *vdev)
|
||||
{
|
||||
nvlist_t *vdev_nv = zpool_find_vdev(zhp, vdev, NULL, NULL, NULL);
|
||||
if (vdev_nv == NULL) {
|
||||
return (NULL);
|
||||
}
|
||||
return (fnvlist_lookup_string(vdev_nv, ZPOOL_CONFIG_PATH));
|
||||
}
|
||||
|
||||
static int
|
||||
zpool_power_on(zpool_handle_t *zhp, char *vdev)
|
||||
{
|
||||
return (zpool_power(zhp, vdev, B_TRUE));
|
||||
}
|
||||
|
||||
static int
|
||||
zpool_power_on_and_disk_wait(zpool_handle_t *zhp, char *vdev)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = zpool_power_on(zhp, vdev);
|
||||
if (rc != 0)
|
||||
return (rc);
|
||||
|
||||
zpool_disk_wait(vdev_name_to_path(zhp, vdev));
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
zpool_power_on_pool_and_wait_for_devices(zpool_handle_t *zhp)
|
||||
{
|
||||
nvlist_t *nv;
|
||||
const char *path = NULL;
|
||||
int rc;
|
||||
|
||||
/* Power up all the devices first */
|
||||
FOR_EACH_REAL_LEAF_VDEV(zhp, nv) {
|
||||
path = fnvlist_lookup_string(nv, ZPOOL_CONFIG_PATH);
|
||||
if (path != NULL) {
|
||||
rc = zpool_power_on(zhp, (char *)path);
|
||||
if (rc != 0) {
|
||||
return (rc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Wait for their devices to show up. Since we powered them on
|
||||
* at roughly the same time, they should all come online around
|
||||
* the same time.
|
||||
*/
|
||||
FOR_EACH_REAL_LEAF_VDEV(zhp, nv) {
|
||||
path = fnvlist_lookup_string(nv, ZPOOL_CONFIG_PATH);
|
||||
zpool_disk_wait(path);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
zpool_power_off(zpool_handle_t *zhp, char *vdev)
|
||||
{
|
||||
return (zpool_power(zhp, vdev, B_FALSE));
|
||||
}
|
||||
|
||||
/*
|
||||
* Display usage message. If we're inside a command, display only the usage for
|
||||
* that command. Otherwise, iterate over the entire command table and display
|
||||
@@ -2093,6 +2166,7 @@ typedef struct status_cbdata {
|
||||
boolean_t cb_print_vdev_init;
|
||||
boolean_t cb_print_vdev_trim;
|
||||
vdev_cmd_data_list_t *vcdl;
|
||||
boolean_t cb_print_power;
|
||||
} status_cbdata_t;
|
||||
|
||||
/* Return 1 if string is NULL, empty, or whitespace; return 0 otherwise. */
|
||||
@@ -2378,6 +2452,26 @@ print_status_config(zpool_handle_t *zhp, status_cbdata_t *cb, const char *name,
|
||||
else
|
||||
printf(" %5s", rbuf);
|
||||
}
|
||||
if (cb->cb_print_power) {
|
||||
if (children == 0) {
|
||||
/* Only leaf vdevs have physical slots */
|
||||
switch (zpool_power_current_state(zhp, (char *)
|
||||
fnvlist_lookup_string(nv,
|
||||
ZPOOL_CONFIG_PATH))) {
|
||||
case 0:
|
||||
printf_color(ANSI_RED, " %5s",
|
||||
gettext("off"));
|
||||
break;
|
||||
case 1:
|
||||
printf(" %5s", gettext("on"));
|
||||
break;
|
||||
default:
|
||||
printf(" %5s", "-");
|
||||
}
|
||||
} else {
|
||||
printf(" %5s", "-");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_NOT_PRESENT,
|
||||
@@ -5428,19 +5522,6 @@ get_interval_count_filter_guids(int *argc, char **argv, float *interval,
|
||||
interval, count);
|
||||
}
|
||||
|
||||
/*
|
||||
* Floating point sleep(). Allows you to pass in a floating point value for
|
||||
* seconds.
|
||||
*/
|
||||
static void
|
||||
fsleep(float sec)
|
||||
{
|
||||
struct timespec req;
|
||||
req.tv_sec = floor(sec);
|
||||
req.tv_nsec = (sec - (float)req.tv_sec) * NANOSEC;
|
||||
nanosleep(&req, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Terminal height, in rows. Returns -1 if stdout is not connected to a TTY or
|
||||
* if we were unable to determine its size.
|
||||
@@ -6939,10 +7020,12 @@ zpool_do_split(int argc, char **argv)
|
||||
return (ret);
|
||||
}
|
||||
|
||||
|
||||
#define POWER_OPT 1024
|
||||
|
||||
/*
|
||||
* zpool online <pool> <device> ...
|
||||
* zpool online [--power] <pool> <device> ...
|
||||
*
|
||||
* --power: Power on the enclosure slot to the drive (if possible)
|
||||
*/
|
||||
int
|
||||
zpool_do_online(int argc, char **argv)
|
||||
@@ -6953,13 +7036,21 @@ zpool_do_online(int argc, char **argv)
|
||||
int ret = 0;
|
||||
vdev_state_t newstate;
|
||||
int flags = 0;
|
||||
boolean_t is_power_on = B_FALSE;
|
||||
struct option long_options[] = {
|
||||
{"power", no_argument, NULL, POWER_OPT},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
/* check options */
|
||||
while ((c = getopt(argc, argv, "e")) != -1) {
|
||||
while ((c = getopt_long(argc, argv, "e", long_options, NULL)) != -1) {
|
||||
switch (c) {
|
||||
case 'e':
|
||||
flags |= ZFS_ONLINE_EXPAND;
|
||||
break;
|
||||
case POWER_OPT:
|
||||
is_power_on = B_TRUE;
|
||||
break;
|
||||
case '?':
|
||||
(void) fprintf(stderr, gettext("invalid option '%c'\n"),
|
||||
optopt);
|
||||
@@ -6967,6 +7058,9 @@ zpool_do_online(int argc, char **argv)
|
||||
}
|
||||
}
|
||||
|
||||
if (libzfs_envvar_is_set("ZPOOL_AUTO_POWER_ON_SLOT"))
|
||||
is_power_on = B_TRUE;
|
||||
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
|
||||
@@ -6988,6 +7082,18 @@ zpool_do_online(int argc, char **argv)
|
||||
for (i = 1; i < argc; i++) {
|
||||
vdev_state_t oldstate;
|
||||
boolean_t avail_spare, l2cache;
|
||||
int rc;
|
||||
|
||||
if (is_power_on) {
|
||||
rc = zpool_power_on_and_disk_wait(zhp, argv[i]);
|
||||
if (rc == ENOTSUP) {
|
||||
(void) fprintf(stderr,
|
||||
gettext("Power control not supported\n"));
|
||||
}
|
||||
if (rc != 0)
|
||||
return (rc);
|
||||
}
|
||||
|
||||
nvlist_t *tgt = zpool_find_vdev(zhp, argv[i], &avail_spare,
|
||||
&l2cache, NULL);
|
||||
if (tgt == NULL) {
|
||||
@@ -7033,12 +7139,15 @@ zpool_do_online(int argc, char **argv)
|
||||
}
|
||||
|
||||
/*
|
||||
* zpool offline [-ft] <pool> <device> ...
|
||||
* zpool offline [-ft]|[--power] <pool> <device> ...
|
||||
*
|
||||
*
|
||||
* -f Force the device into a faulted state.
|
||||
*
|
||||
* -t Only take the device off-line temporarily. The offline/faulted
|
||||
* state will not be persistent across reboots.
|
||||
*
|
||||
* --power Power off the enclosure slot to the drive (if possible)
|
||||
*/
|
||||
int
|
||||
zpool_do_offline(int argc, char **argv)
|
||||
@@ -7049,9 +7158,15 @@ zpool_do_offline(int argc, char **argv)
|
||||
int ret = 0;
|
||||
boolean_t istmp = B_FALSE;
|
||||
boolean_t fault = B_FALSE;
|
||||
boolean_t is_power_off = B_FALSE;
|
||||
|
||||
struct option long_options[] = {
|
||||
{"power", no_argument, NULL, POWER_OPT},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
/* check options */
|
||||
while ((c = getopt(argc, argv, "ft")) != -1) {
|
||||
while ((c = getopt_long(argc, argv, "ft", long_options, NULL)) != -1) {
|
||||
switch (c) {
|
||||
case 'f':
|
||||
fault = B_TRUE;
|
||||
@@ -7059,6 +7174,9 @@ zpool_do_offline(int argc, char **argv)
|
||||
case 't':
|
||||
istmp = B_TRUE;
|
||||
break;
|
||||
case POWER_OPT:
|
||||
is_power_off = B_TRUE;
|
||||
break;
|
||||
case '?':
|
||||
(void) fprintf(stderr, gettext("invalid option '%c'\n"),
|
||||
optopt);
|
||||
@@ -7066,6 +7184,20 @@ zpool_do_offline(int argc, char **argv)
|
||||
}
|
||||
}
|
||||
|
||||
if (is_power_off && fault) {
|
||||
(void) fprintf(stderr,
|
||||
gettext("-0 and -f cannot be used together\n"));
|
||||
usage(B_FALSE);
|
||||
return (1);
|
||||
}
|
||||
|
||||
if (is_power_off && istmp) {
|
||||
(void) fprintf(stderr,
|
||||
gettext("-0 and -t cannot be used together\n"));
|
||||
usage(B_FALSE);
|
||||
return (1);
|
||||
}
|
||||
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
|
||||
@@ -7085,8 +7217,22 @@ zpool_do_offline(int argc, char **argv)
|
||||
return (1);
|
||||
|
||||
for (i = 1; i < argc; i++) {
|
||||
if (fault) {
|
||||
uint64_t guid = zpool_vdev_path_to_guid(zhp, argv[i]);
|
||||
uint64_t guid = zpool_vdev_path_to_guid(zhp, argv[i]);
|
||||
if (is_power_off) {
|
||||
/*
|
||||
* Note: we have to power off first, then set REMOVED,
|
||||
* or else zpool_vdev_set_removed_state() returns
|
||||
* EAGAIN.
|
||||
*/
|
||||
ret = zpool_power_off(zhp, argv[i]);
|
||||
if (ret != 0) {
|
||||
(void) fprintf(stderr, "%s %s %d\n",
|
||||
gettext("unable to power off slot for"),
|
||||
argv[i], ret);
|
||||
}
|
||||
zpool_vdev_set_removed_state(zhp, guid, VDEV_AUX_NONE);
|
||||
|
||||
} else if (fault) {
|
||||
vdev_aux_t aux;
|
||||
if (istmp == B_FALSE) {
|
||||
/* Force the fault to persist across imports */
|
||||
@@ -7109,7 +7255,7 @@ zpool_do_offline(int argc, char **argv)
|
||||
}
|
||||
|
||||
/*
|
||||
* zpool clear <pool> [device]
|
||||
* zpool clear [-nF]|[--power] <pool> [device]
|
||||
*
|
||||
* Clear all errors associated with a pool or a particular device.
|
||||
*/
|
||||
@@ -7121,13 +7267,20 @@ zpool_do_clear(int argc, char **argv)
|
||||
boolean_t dryrun = B_FALSE;
|
||||
boolean_t do_rewind = B_FALSE;
|
||||
boolean_t xtreme_rewind = B_FALSE;
|
||||
boolean_t is_power_on = B_FALSE;
|
||||
uint32_t rewind_policy = ZPOOL_NO_REWIND;
|
||||
nvlist_t *policy = NULL;
|
||||
zpool_handle_t *zhp;
|
||||
char *pool, *device;
|
||||
|
||||
struct option long_options[] = {
|
||||
{"power", no_argument, NULL, POWER_OPT},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
/* check options */
|
||||
while ((c = getopt(argc, argv, "FnX")) != -1) {
|
||||
while ((c = getopt_long(argc, argv, "FnX", long_options,
|
||||
NULL)) != -1) {
|
||||
switch (c) {
|
||||
case 'F':
|
||||
do_rewind = B_TRUE;
|
||||
@@ -7138,6 +7291,9 @@ zpool_do_clear(int argc, char **argv)
|
||||
case 'X':
|
||||
xtreme_rewind = B_TRUE;
|
||||
break;
|
||||
case POWER_OPT:
|
||||
is_power_on = B_TRUE;
|
||||
break;
|
||||
case '?':
|
||||
(void) fprintf(stderr, gettext("invalid option '%c'\n"),
|
||||
optopt);
|
||||
@@ -7145,6 +7301,9 @@ zpool_do_clear(int argc, char **argv)
|
||||
}
|
||||
}
|
||||
|
||||
if (libzfs_envvar_is_set("ZPOOL_AUTO_POWER_ON_SLOT"))
|
||||
is_power_on = B_TRUE;
|
||||
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
|
||||
@@ -7185,6 +7344,14 @@ zpool_do_clear(int argc, char **argv)
|
||||
return (1);
|
||||
}
|
||||
|
||||
if (is_power_on) {
|
||||
if (device == NULL) {
|
||||
zpool_power_on_pool_and_wait_for_devices(zhp);
|
||||
} else {
|
||||
zpool_power_on_and_disk_wait(zhp, device);
|
||||
}
|
||||
}
|
||||
|
||||
if (zpool_clear(zhp, device, policy) != 0)
|
||||
ret = 1;
|
||||
|
||||
@@ -8801,6 +8968,10 @@ status_callback(zpool_handle_t *zhp, void *data)
|
||||
printf_color(ANSI_BOLD, " %5s", gettext("SLOW"));
|
||||
}
|
||||
|
||||
if (cbp->cb_print_power) {
|
||||
printf_color(ANSI_BOLD, " %5s", gettext("POWER"));
|
||||
}
|
||||
|
||||
if (cbp->vcdl != NULL)
|
||||
print_cmd_columns(cbp->vcdl, 0);
|
||||
|
||||
@@ -8847,8 +9018,8 @@ status_callback(zpool_handle_t *zhp, void *data)
|
||||
}
|
||||
|
||||
/*
|
||||
* zpool status [-c [script1,script2,...]] [-igLpPstvx] [-T d|u] [pool] ...
|
||||
* [interval [count]]
|
||||
* zpool status [-c [script1,script2,...]] [-igLpPstvx] [--power] [-T d|u] ...
|
||||
* [pool] [interval [count]]
|
||||
*
|
||||
* -c CMD For each vdev, run command CMD
|
||||
* -i Display vdev initialization status.
|
||||
@@ -8862,6 +9033,7 @@ status_callback(zpool_handle_t *zhp, void *data)
|
||||
* -D Display dedup status (undocumented)
|
||||
* -t Display vdev TRIM status.
|
||||
* -T Display a timestamp in date(1) or Unix format
|
||||
* --power Display vdev enclosure slot power status
|
||||
*
|
||||
* Describes the health status of all pools or some subset.
|
||||
*/
|
||||
@@ -8875,8 +9047,14 @@ zpool_do_status(int argc, char **argv)
|
||||
status_cbdata_t cb = { 0 };
|
||||
char *cmd = NULL;
|
||||
|
||||
struct option long_options[] = {
|
||||
{"power", no_argument, NULL, POWER_OPT},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
/* check options */
|
||||
while ((c = getopt(argc, argv, "c:igLpPsvxDtT:")) != -1) {
|
||||
while ((c = getopt_long(argc, argv, "c:igLpPsvxDtT:", long_options,
|
||||
NULL)) != -1) {
|
||||
switch (c) {
|
||||
case 'c':
|
||||
if (cmd != NULL) {
|
||||
@@ -8935,6 +9113,9 @@ zpool_do_status(int argc, char **argv)
|
||||
case 'T':
|
||||
get_timestamp_arg(*optarg);
|
||||
break;
|
||||
case POWER_OPT:
|
||||
cb.cb_print_power = B_TRUE;
|
||||
break;
|
||||
case '?':
|
||||
if (optopt == 'c') {
|
||||
print_zpool_script_list("status");
|
||||
|
||||
@@ -138,6 +138,9 @@ int check_file(const char *file, boolean_t force, boolean_t isspare);
|
||||
void after_zpool_upgrade(zpool_handle_t *zhp);
|
||||
int check_file_generic(const char *file, boolean_t force, boolean_t isspare);
|
||||
|
||||
int zpool_power(zpool_handle_t *zhp, char *vdev, boolean_t turn_on);
|
||||
int zpool_power_current_state(zpool_handle_t *zhp, char *vdev);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user