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:
Tony Hutter
2023-12-21 10:53:16 -08:00
committed by GitHub
parent 6930ecbb75
commit a9520e6e59
16 changed files with 875 additions and 65 deletions
+98
View File
@@ -1898,6 +1898,104 @@ zpool_find_config(libpc_handle_t *hdl, const char *target, nvlist_t **configp,
return (0);
}
/* Return if a vdev is a leaf vdev. Note: draid spares are leaf vdevs. */
static boolean_t
vdev_is_leaf(nvlist_t *nv)
{
uint_t children = 0;
nvlist_t **child;
(void) nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN,
&child, &children);
return (children == 0);
}
/* Return if a vdev is a leaf vdev and a real device (disk or file) */
static boolean_t
vdev_is_real_leaf(nvlist_t *nv)
{
const char *type = NULL;
if (!vdev_is_leaf(nv))
return (B_FALSE);
(void) nvlist_lookup_string(nv, ZPOOL_CONFIG_TYPE, &type);
if ((strcmp(type, VDEV_TYPE_DISK) == 0) ||
(strcmp(type, VDEV_TYPE_FILE) == 0)) {
return (B_TRUE);
}
return (B_FALSE);
}
/*
* This function is called by our FOR_EACH_VDEV() macros.
*
* state: State machine status (stored inside of a (nvlist_t *))
* nv: The current vdev nvlist_t we are iterating over.
* last_nv: The previous vdev nvlist_t we returned to the user in
* the last iteration of FOR_EACH_VDEV(). We use it
* to find the next vdev nvlist_t we should return.
* real_leaves_only: Only return leaf vdevs.
*
* Returns 1 if we found the next vdev nvlist_t for this iteration. 0 if
* we're still searching for it.
*/
static int
__for_each_vdev_macro_helper_func(void *state, nvlist_t *nv, void *last_nv,
boolean_t real_leaves_only)
{
enum {FIRST_NV = 0, NEXT_IS_MATCH = 1, STOP_LOOKING = 2};
/* The very first entry in the NV list is a special case */
if (*((nvlist_t **)state) == (nvlist_t *)FIRST_NV) {
if (real_leaves_only && !vdev_is_real_leaf(nv))
return (0);
*((nvlist_t **)last_nv) = nv;
*((nvlist_t **)state) = (nvlist_t *)STOP_LOOKING;
return (1);
}
/*
* We came across our last_nv, meaning the next one is the one we
* want
*/
if (nv == *((nvlist_t **)last_nv)) {
/* Next iteration of this function will return the nvlist_t */
*((nvlist_t **)state) = (nvlist_t *)NEXT_IS_MATCH;
return (0);
}
/*
* We marked NEXT_IS_MATCH on the previous iteration, so this is the one
* we want.
*/
if (*(nvlist_t **)state == (nvlist_t *)NEXT_IS_MATCH) {
if (real_leaves_only && !vdev_is_real_leaf(nv))
return (0);
*((nvlist_t **)last_nv) = nv;
*((nvlist_t **)state) = (nvlist_t *)STOP_LOOKING;
return (1);
}
return (0);
}
int
for_each_vdev_macro_helper_func(void *state, nvlist_t *nv, void *last_nv)
{
return (__for_each_vdev_macro_helper_func(state, nv, last_nv, B_FALSE));
}
int
for_each_real_leaf_vdev_macro_helper_func(void *state, nvlist_t *nv,
void *last_nv)
{
return (__for_each_vdev_macro_helper_func(state, nv, last_nv, B_TRUE));
}
/*
* Internal function for iterating over the vdevs.
*