/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
*/
#include <strings.h>
#include <sys/libdevid.h>
#include <fcntl.h>
#include <libdevinfo.h>
#include <unistd.h>
#include <wait.h>
#include <spawn.h>
#include "fmd_zfs.h"
/*
* This returns true if the vdev in question has a "replacing" vdev as an
* ancestor. As there is no way to work "upwards" through an nvlist,
* we must recurse downwards to determine if the child of any "replacing" vdev
* contains the specified vdev.
*/
static int
{
char *devname =
/*
* If the vdev names match, we found it. If we return
* ZR_VDEV_FOUND, we'll unwind the stack.
*/
}
/*
* This is an inner vdev, not a leaf vdev. We need to search
* all of its children for the leaf we're interested in.
*/
for (c = 0; c < children; c++) {
char *type;
int result;
/* ignore config errors */
&type) != 0)
continue;
/* see if the disk vdev is a child of this vdev */
/*
* If we found the vdev, then determine what to return. If
* we're a replacing, then the vdev is a child of a replacing.
* Otherwise, return the result we got from searching below us.
*/
if (result != ZR_NOT_FOUND)
}
/* The vdev couldn't be found at this level of the tree */
return (ZR_NOT_FOUND);
}
/*
* Given a vdev, attempt to replace it with every known spare until one
* succeeds.
*/
/*ARGSUSED*/
int
{
char *dev_name;
char *type;
/*
* Don't try and spare something which is already spared or replacing or
* which is a cache or available spare node.
*/
switch (vdev_type) {
case VDEV_IS_AVAIL_SPARE:
case VDEV_IS_CACHE:
case VDEV_IS_SPARED_DATA:
case VDEV_IS_SPARED_LOG:
case VDEV_IS_REPLACING:
return (FMD_ZFS_ITER_CONTINUE);
}
return (FMD_ZFS_ITER_CONTINUE);
&nvroot) != 0)
return (FMD_ZFS_ITER_CONTINUE);
/*
* Find out if there are any hot spares available in the pool.
*/
return (FMD_ZFS_ITER_CONTINUE);
/*
* If the failing device is in the process of replacing a previously
* failed disk, then we will already have allocated a spare for that
* previous disk, and we don't want to allocate another one here. So
* check whether we have an ancestor which is of type "replacing"
* and if so then return.
*/
return (FMD_ZFS_ITER_CONTINUE);
}
return (FMD_ZFS_ITER_CONTINUE);
}
/*
* Try to replace each spare, ending when we successfully
* replace it.
*/
for (s = 0; s < nspares; s++) {
char *spare_name;
&spare_name) != 0)
continue;
(void) nvlist_add_nvlist_array(replacement,
replacement, B_TRUE) == 0)
break;
}
return (FMD_ZFS_ITER_CONTINUE);
}
/*ARGSUSED*/
int
{
char *type;
uint_t c;
return (FMD_ZFS_ITER_CONTINUE);
return (FMD_ZFS_ITER_CONTINUE);
}
/*ARGSUSED*/
int
{
char *type;
uint_t c;
return (FMD_ZFS_ITER_CONTINUE);
return (FMD_ZFS_ITER_CONTINUE);
}
/*ARGSUSED*/
int
{
/*
* If no vdev specified, we've been asked to clear the pool.
*/
ZPOOL_NEVER_REWIND) == 0)
}
return (FMD_ZFS_ITER_CONTINUE);
}
return (FMD_ZFS_ITER_CONTINUE);
}
/*ARGSUSED*/
int
{
uint_t c;
/*
* If no vdev specified, we've been asked for the state of
* the pool - find state of root node.
*/
}
/*
* update state if less than current value.
*/
return (FMD_ZFS_ITER_CONTINUE);
}
/*ARGSUSED*/
int
{
*(int *)arg = 1;
return (FMD_ZFS_ITER_TERMINATE);
}
typedef struct zfs_iter_cbdata {
int cb_type;
char *cb_devid;
char *cb_devpath;
char *cb_chassissn;
char *cb_location;
void *cb_arg;
static int
{
case TYPE_GUID:
return (FMD_ZFS_ITER_TERMINATE);
break;
case TYPE_LOCATION:
&chassissn) == 0 &&
&location) == 0 &&
return (FMD_ZFS_ITER_TERMINATE);
break;
case TYPE_DEVPATH:
return (FMD_ZFS_ITER_TERMINATE);
break;
case TYPE_DEVID:
return (FMD_ZFS_ITER_TERMINATE);
break;
case TYPE_ALL:
return (FMD_ZFS_ITER_TERMINATE);
break;
}
for (c = 0; c < children; c++) {
/*
* If type is VDEV_NULL, this is a top-level
* vdev. See if it is of type log or data.
*/
(void) nvlist_lookup_uint64(vdev,
} else {
/*
* If the parent node is SPARED or REPLACING
* work out what type of node the child is
* and keep track of the child's sibling.
*/
(void) nvlist_lookup_string(vdev,
VDEV_TYPE_REPLACING) == 0) {
if (c == 0) {
} else {
}
VDEV_TYPE_SPARE) == 0) {
if (c == 0) {
} else {
}
} else
}
return (FMD_ZFS_ITER_TERMINATE);
}
for (c = 0; c < children; c++)
return (FMD_ZFS_ITER_TERMINATE);
for (c = 0; c < children; c++)
VDEV_IS_AVAIL_SPARE, NULL) ==
return (FMD_ZFS_ITER_TERMINATE);
return (FMD_ZFS_ITER_CONTINUE);
}
static int
{
goto done;
NULL);
goto done;
}
}
&nvroot) == 0)
done:
}
void
void *arg)
{
}
void
{
}
void
{
}
void
{
}
void
{
}
static int
{
int ret;
return (-1);
return (0);
}
int
{
const char *oldsuffix;
int fd;
/*
* Ignore vdevs that are already being replaced.
*/
if (type == VDEV_IS_REPLACING)
return (FMD_ZFS_ITER_CONTINUE);
goto forcefault;
goto forcefault;
goto forcefault;
goto forcefault;
goto forcefault;
goto do_lofi;
}
/*
* sure they have the same slice (if any) as before.
*/
oldsuffix = "";
goto forcefault;
} else
oldsuffix = "";
goto forcefault;
} else
goto forcefault;
} else
/*
* If the 'checkdisk' program exists, then verify that the type of the
* disk inserted matches the type of the disk it is attempting to
* replace.
*/
argv[0] = ZFS_CHECK_EXT;
"log" : "data";
if (fmd_zfs_spawn(argv) != 0)
goto forcefault;
}
/*
* Before we attempt a replacement, we zero out the labels. While
* unlikely, it's possible that we are re-inserting a stale disk that
* was offlined, and its old label is going to conflict with what's
* currently in the config. But don't do this for cache or active
* spare devices as there's no zpool_vdev_attach to recreate the labels.
*/
(void) zpool_clear_label(fd);
}
}
/*
* If this is a request to label a whole disk, then attempt to
* write out the label. If it is not a whole disk, and the
* external 'labeldisk' program exists, then invoke this
* program to label the disk.
*
* If any part of this process fails, then do a force
* a hot spare replacement).
*/
if (wholedisk) {
argv[0] = ZFS_LABELFAIL_EXT;
(void) fmd_zfs_spawn(argv);
}
goto forcefault;
}
} else {
argv[0] = ZFS_LABEL_EXT;
if (fmd_zfs_spawn(argv) != 0) {
if (stat64(ZFS_LABELFAIL_EXT,
&statbuf) == 0) {
argv[0] = ZFS_LABELFAIL_EXT;
(void) fmd_zfs_spawn(argv);
}
goto forcefault;
}
}
}
/*
* Construct the root vdev to pass to zpool_vdev_attach(). While adding
* the entire vdev structure is harmless, we construct a reduced set of
*/
goto forcefault;
if (type == VDEV_IS_CACHE) {
/*
*/
&newvd, 1) != 0)
goto forcefault;
NULL, 0) != 0)
goto forcefault;
} else if (type == VDEV_IS_AVAIL_SPARE) {
/*
*/
&newvd, 1) != 0)
goto forcefault;
NULL, 0) != 0)
goto forcefault;
} else if (type == VDEV_IS_ACTIVE_SPARE) {
/*
* Do a detach of the active spare first.
*/
goto forcefault;
/*
* Now remove the old hot spare and add the new hot spare.
*/
&newvd, 1) != 0)
goto forcefault;
NULL, 0) != 0)
goto forcefault;
goto forcefault;
goto forcefault;
/*
* Now do the attach of the new active spare.
*/
NULL, 0) != 0)
goto forcefault;
&newvd, 1) != 0)
goto forcefault;
} else {
&newvd, 1) != 0)
goto forcefault;
B_TRUE);
}
/*
* If this action might have made the pool usable again then enable
* datasets.
*/
out:
if (nvroot)
if (newvd)
if (physpath)
if (path)
if (devdskpath)
if (vdevname)
if (savedevidstr)
return (FMD_ZFS_ITER_CONTINUE);
/*
* If we are being called after an fmd restart, we may have missed
* a check event. So force a fault here by doing a re-online of the
* original device - which we know will fail.
*/
if (fzp->fz_isrestart)
goto out;
}
/*ARGSUSED*/
int
{
char *vdevname;
char *sibvdevname;
if (type == VDEV_IS_AVAIL_SPARE)
return (FMD_ZFS_ITER_CONTINUE);
return (FMD_ZFS_ITER_CONTINUE);
(newstate == VDEV_STATE_HEALTHY ||
newstate == VDEV_STATE_DEGRADED)) {
/*
* If this action might have made the pool usable again then
* enable datasets.
*/
if (newstate == VDEV_STATE_HEALTHY &&
(type == VDEV_IS_SPARED_DATA ||
type == VDEV_IS_SPARED_LOG)) {
/*
* If there was a hot spare activated, then detach the
* spare now rather than waiting for resilvering to
* complete. If we don't do this, then we are forced to
* wait for the spare itself to resilver, which can be
* orders of magnitude longer than the original disk
* (which is only missing a small amount of data). Note
* that if we are in the rare situation where the spare
* has the only valid copy of some data then the
* zpool_vdev_detach() will simply fail.
*/
if (sibvdevname != NULL) {
}
}
/*
* Only have non-null arg for LOFI devices.
*/
}
return (FMD_ZFS_ITER_CONTINUE);
}
/*ARGSUSED*/
int
{
return (FMD_ZFS_ITER_CONTINUE);
}
/*ARGSUSED*/
int
{
if (type == VDEV_IS_AVAIL_SPARE)
return (FMD_ZFS_ITER_CONTINUE);
if (wholedisk)
return (FMD_ZFS_ITER_CONTINUE);
}