spa_errlog.c revision ea8dc4b6d2251b437950c0056bc626b311c73c27
/*
* 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
*/
/*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Routines to manage the on-disk persistent error log.
*
* Each pool stores a log of all logical data errors seen during normal
* operation. This is actually the union of two distinct logs: the last log,
* and the current log. All errors seen are logged to the current log. When a
* scrub completes, the current log becomes the last log, the last log is thrown
* out, and the current log is reinitialized. This way, if an error is somehow
* corrected, a new scrub will show that that it no longer exists, and will be
* deleted from the log when the scrub completes.
*
* The log is stored using a ZAP object whose key is a string form of the
* zbookmark tuple (objset, object, level, blkid), and whose contents is an
* optional 'objset:object' human-readable string describing the data. When an
* error is first logged, this string will be empty, indicating that no name is
* known. This prevents us from having to issue a potentially large amount of
* I/O to discover the object name during an error path. Instead, we do the
* calculation when the data is requested, storing the result so future queries
* will be faster.
*
* This log is then shipped into an nvlist where the key is the dataset name and
* the value is the object name. Userland is then responsible for uniquifying
* this list and displaying it to the user.
*/
#include <sys/spa_impl.h>
/*
* This is a stripped-down version of strtoull, suitable only for converting
* lowercase hexidecimal numbers that don't overflow.
*/
static uint64_t
{
char c;
int digit;
while ((c = *str) != '\0') {
if (c >= '0' && c <= '9')
digit = c - '0';
else if (c >= 'a' && c <= 'f')
else
break;
val *= 16;
str++;
}
return (val);
}
/*
* Convert a bookmark to a string.
*/
static void
{
}
/*
* Convert a string to a bookmark
*/
static void
{
}
/*
* Log an uncorrectable error to the persistent error log. We add it to the
* spa's list of pending errors. The changes are actually synced out to disk
* during spa_errlog_sync().
*/
void
{
/*
* If we are trying to import a pool, ignore any errors, as we won't be
* writing to the pool any time soon.
*/
return;
/*
* If we have had a request to rotate the log, log it to the next list
* instead of the current one.
*/
else
return;
}
}
/*
* Return the number of errors currently in the error log. This is actually the
* sum of both the last log and the current log, since we don't know the union
* of these logs until we reach userland.
*/
{
if (spa->spa_errlog_scrub != 0 &&
&count) == 0)
&count) == 0)
return (total);
}
#ifdef _KERNEL
static int
{
if (obj == 0)
return (0);
zap_cursor_advance(&zc)) {
if (*count == 0) {
return (ENOMEM);
}
sizeof (zbookmark_t)) != 0)
return (EFAULT);
*count -= 1;
}
return (0);
}
static int
{
if (*count == 0)
return (ENOMEM);
sizeof (zbookmark_t)) != 0)
return (EFAULT);
*count -= 1;
}
return (0);
}
#endif
/*
* Copy all known errors to userland as an array of bookmarks. This is
* actually a union of the on-disk last log and current log, as well as any
* pending error requests.
*
* Because the act of reading the on-disk log could cause errors to be
* generated, we have two separate locks: one for the error log and one for the
* in-core error lists. We only need the error list lock to log and error, so
* we grab the error log lock while we read the on-disk logs, and only pick up
* the error list lock when we are finished.
*/
int
{
int ret = 0;
#ifdef _KERNEL
count);
if (!ret)
count);
if (!ret)
count);
#endif
return (ret);
}
/*
* Called when a scrub completes. This simply set a bit which tells which AVL
* tree to add new errors. spa_errlog_sync() is responsible for actually
* syncing the changes to the underlying objects.
*/
void
{
}
/*
* Discard any pending errors from the spa_t. Called when unloading a faulted
* pool, as the errors encountered during the open cannot be synced to disk.
*/
void
{
void *cookie;
}
/*
* Process a list of errors into the current on-disk log.
*/
static void
{
char buf[64];
void *cookie;
if (avl_numnodes(t) != 0) {
/* create log if necessary */
if (*obj == 0)
0, tx);
/* add errors to the current log */
}
/* purge the error list */
}
}
/*
* Sync the error log out to disk. This is a little tricky because the act of
* writing the error log requires the spa_errlist_lock. So, we need to lock the
* error lists, take a copy of the lists, and then reinitialize them. Then, we
* drop the error list lock and take the error log lock, at which point we
* do the errlog processing. Then, if we encounter an I/O error during this
* process, we can successfully add the error to the list. Note that this will
* result in the perpetual recycling of errors, but it is an unlikely situation
* and not a performance critical operation.
*/
void
{
int scrub_finished;
/*
* Bail out early under normal circumstances.
*/
!spa->spa_scrub_finished) {
return;
}
/*
* Sync out the current list of errors.
*/
/*
* Rotate the log if necessary.
*/
if (scrub_finished) {
if (spa->spa_errlog_last != 0)
spa->spa_errlog_scrub = 0;
}
/*
* Sync out any pending scrub errors.
*/
/*
* Update the MOS to reflect the new values.
*/
}