Index: projects/zfsd/head/cddl/sbin/zfsd/zpool_list.cc =================================================================== --- projects/zfsd/head/cddl/sbin/zfsd/zpool_list.cc (revision 300334) +++ projects/zfsd/head/cddl/sbin/zfsd/zpool_list.cc (nonexistent) @@ -1,119 +0,0 @@ -/*- - * Copyright (c) 2011 Spectra Logic Corporation - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions, and the following disclaimer, - * without modification. - * 2. Redistributions in binary form must reproduce at minimum a disclaimer - * substantially similar to the "NO WARRANTY" disclaimer below - * ("Disclaimer") and any redistribution must be conditioned upon - * including a substantially similar Disclaimer requirement for further - * binary redistribution. - * - * NO WARRANTY - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING - * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGES. - * - * Authors: Justin T. Gibbs (Spectra Logic Corporation) - */ - -/** - * \file zpool_list.cc - * - * Implementation of the ZpoolList class. - */ -#include -#include - -#include - -#include - -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "vdev.h" -#include "vdev_iterator.h" -#include "zpool_list.h" -#include "zfsd.h" - -/*============================ Namespace Control =============================*/ -using DevdCtl::Guid; - -/*=========================== Class Implementations ==========================*/ -/*--------------------------------- ZpoolList --------------------------------*/ -bool -ZpoolList::ZpoolAll(zpool_handle_t *pool, nvlist_t *poolConfig, void *cbArg) -{ - return (true); -} - -bool -ZpoolList::ZpoolByGUID(zpool_handle_t *pool, nvlist_t *poolConfig, - void *cbArg) -{ - Guid *desiredPoolGUID(static_cast(cbArg)); - uint64_t poolGUID; - - /* We are only intested in the pool that matches our pool GUID. */ - return (nvlist_lookup_uint64(poolConfig, ZPOOL_CONFIG_POOL_GUID, - &poolGUID) == 0 - && poolGUID == (uint64_t)*desiredPoolGUID); -} - -bool -ZpoolList::ZpoolByName(zpool_handle_t *pool, nvlist_t *poolConfig, void *cbArg) -{ - const string &desiredPoolName(*static_cast(cbArg)); - - /* We are only intested in the pool that matches our pool GUID. */ - return (desiredPoolName == zpool_get_name(pool)); -} - -int -ZpoolList::LoadIterator(zpool_handle_t *pool, void *data) -{ - ZpoolList *zpl(reinterpret_cast(data)); - nvlist_t *poolConfig(zpool_get_config(pool, NULL)); - - if (zpl->m_filter(pool, poolConfig, zpl->m_filterArg)) - zpl->push_back(pool); - else - zpool_close(pool); - return (0); -} - -ZpoolList::ZpoolList(PoolFilter_t *filter, void * filterArg) - : m_filter(filter), - m_filterArg(filterArg) -{ - zpool_iter(g_zfsHandle, LoadIterator, this); -} - -ZpoolList::~ZpoolList() -{ - for (iterator it(begin()); it != end(); it++) - zpool_close(*it); - - clear(); -} Index: projects/zfsd/head/cddl/sbin/zfsd/tests/zfsd_test.sh =================================================================== --- projects/zfsd/head/cddl/sbin/zfsd/tests/zfsd_test.sh (revision 300334) +++ projects/zfsd/head/cddl/sbin/zfsd/tests/zfsd_test.sh (nonexistent) @@ -1,71 +0,0 @@ -# Copyright (c) 2013 Spectra Logic Corporation -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions, and the following disclaimer, -# without modification. -# 2. Redistributions in binary form must reproduce at minimum a disclaimer -# substantially similar to the "NO WARRANTY" disclaimer below -# ("Disclaimer") and any redistribution must be conditioned upon -# including a substantially similar Disclaimer requirement for further -# binary redistribution. -# -# NO WARRANTY -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, -# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING -# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGES. -# -# Authors: Alan Somers (Spectra Logic Corporation) -# -# $FreeBSD$ - -# -# Test Case: zfsd_unittest -# TODO: get coverage in cleanup -# -atf_test_case zfsd_unittest -zfsd_unittest_head() -{ - atf_set "descr" "Run zfsd unit tests" -} - - -zfsd_unittest_body() -{ - TESTPROG=$(atf_get_srcdir)/zfsd_unittest - if atf_config_has coverage_dir; then - # If coverage_dir is defined, then we want to save the .gcda - # and .gcno files for future analysis. Put them in a directory - # tree that resembles /usr/src, but is anchored at - # coverage_dir. - export GCOV_PREFIX=`atf_config_get coverage_dir` - # Examine zfsd_unittest to calculate the GCOV_PREFIX_STRIP - # The outer echo command is needed to strip off whitespace - # printed by wc - OLDGCDADIR=`strings $TESTPROG | grep 'zfsd.gcda'` - export GCOV_PREFIX_STRIP=$( echo $( echo $OLDGCDADIR | \ - sed -e 's:/cddl/sbin/zfsd.*::' -e 's:/: :g' | \ - wc -w ) ) - NEWGCDADIR=$GCOV_PREFIX/`dirname $OLDGCDADIR | \ - sed -e 's:.*\(cddl/sbin/zfsd\):\1:'` - mkdir -p $NEWGCDADIR - cp $(atf_get_srcdir)/*.gcno $NEWGCDADIR - fi - atf_check -s exit:0 -o ignore -e ignore $TESTPROG -} - -atf_init_test_cases() -{ - atf_add_test_case zfsd_unittest -} Index: projects/zfsd/head/cddl/sbin/zfsd/tests/libmocks.c =================================================================== --- projects/zfsd/head/cddl/sbin/zfsd/tests/libmocks.c (revision 300334) +++ projects/zfsd/head/cddl/sbin/zfsd/tests/libmocks.c (nonexistent) @@ -1,24 +0,0 @@ -#include -#include -#include "libmocks.h" - -/* - * This file mocks shared library functions that are used by zfsd. Every - * function present will be used for all tests in all test suites instead of the - * normal function. - */ - -int syslog_last_priority; -char syslog_last_message[4096]; -void syslog(int priority, const char* message, ...) { - va_list ap; - - syslog_last_priority = priority; - va_start(ap, message); - vsnprintf(syslog_last_message, 4096, message, ap); - va_end(ap); -} - -int zpool_iter(libzfs_handle_t* handle, zpool_iter_f iter, void* arg) { - return (0); -} Index: projects/zfsd/head/cddl/sbin/zfsd/tests/zfsd_unittest.cc =================================================================== --- projects/zfsd/head/cddl/sbin/zfsd/tests/zfsd_unittest.cc (revision 300334) +++ projects/zfsd/head/cddl/sbin/zfsd/tests/zfsd_unittest.cc (nonexistent) @@ -1,771 +0,0 @@ -/*- - * Copyright (c) 2012, 2013 Spectra Logic Corporation - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions, and the following disclaimer, - * without modification. - * 2. Redistributions in binary form must reproduce at minimum a disclaimer - * substantially similar to the "NO WARRANTY" disclaimer below - * ("Disclaimer") and any redistribution must be conditioned upon - * including a substantially similar Disclaimer requirement for further - * binary redistribution. - * - * NO WARRANTY - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING - * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGES. - * - * Authors: Alan Somers (Spectra Logic Corporation) - */ -#include - -#include -#include - -#include -#include - -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "libmocks.h" - -__FBSDID("$FreeBSD$"); - -/*================================== Macros ==================================*/ -#define NUM_ELEMENTS(x) (sizeof(x) / sizeof(*x)) - -/*============================ Namespace Control =============================*/ -using std::string; -using std::stringstream; - -using DevdCtl::Event; -using DevdCtl::EventBuffer; -using DevdCtl::EventFactory; -using DevdCtl::EventList; -using DevdCtl::Guid; -using DevdCtl::NVPairMap; - -/* redefine zpool_handle here because libzfs_impl.h is not includable */ -struct zpool_handle -{ - libzfs_handle_t *zpool_hdl; - zpool_handle_t *zpool_next; - char zpool_name[ZPOOL_MAXNAMELEN]; - int zpool_state; - size_t zpool_config_size; - nvlist_t *zpool_config; - nvlist_t *zpool_old_config; - nvlist_t *zpool_props; - diskaddr_t zpool_start_block; -}; - -class MockZfsEvent : public ZfsEvent -{ -public: - MockZfsEvent(Event::Type, NVPairMap&, const string&); - virtual ~MockZfsEvent() {} - - static BuildMethod MockZfsEventBuilder; - - MOCK_CONST_METHOD0(ProcessPoolEvent, void()); - - static EventFactory::Record s_buildRecords[]; -}; - -EventFactory::Record MockZfsEvent::s_buildRecords[] = -{ - { Event::NOTIFY, "ZFS", &MockZfsEvent::MockZfsEventBuilder } -}; - -MockZfsEvent::MockZfsEvent(Event::Type type, NVPairMap& map, - const string& str) - : ZfsEvent(type, map, str) -{ -} - -Event * -MockZfsEvent::MockZfsEventBuilder(Event::Type type, - NVPairMap &nvpairs, - const string &eventString) -{ - return (new MockZfsEvent(type, nvpairs, eventString)); -} - -/* - * A dummy Vdev class used for testing other classes - */ -class MockVdev : public Vdev -{ -public: - MockVdev(nvlist_t *vdevConfig); - virtual ~MockVdev() {} - - MOCK_CONST_METHOD0(GUID, Guid()); - MOCK_CONST_METHOD0(PoolGUID, Guid()); - MOCK_CONST_METHOD0(State, vdev_state()); - MOCK_CONST_METHOD0(PhysicalPath, string()); -}; - -MockVdev::MockVdev(nvlist_t *vdevConfig) - : Vdev(vdevConfig) -{ -} - -/* - * A CaseFile class with side effects removed, for testing - */ -class TestableCaseFile : public CaseFile -{ -public: - static TestableCaseFile &Create(Vdev &vdev); - TestableCaseFile(Vdev &vdev); - virtual ~TestableCaseFile() {} - - MOCK_METHOD0(Close, void()); - MOCK_METHOD1(RegisterCallout, void(const Event &event)); - MOCK_METHOD0(RefreshVdevState, bool()); - MOCK_METHOD1(ReEvaluate, bool(const ZfsEvent &event)); - - bool RealReEvaluate(const ZfsEvent &event) - { - return (CaseFile::ReEvaluate(event)); - } - - /* - * This splices the event lists, a procedure that would normally be done - * by OnGracePeriodEnded, but we don't necessarily call that in the - * unit tests - */ - void SpliceEvents(); - - /* - * Used by some of our expectations. CaseFile does not publicize this - */ - static int getActiveCases() - { - return (s_activeCases.size()); - } -}; - -TestableCaseFile::TestableCaseFile(Vdev &vdev) - : CaseFile(vdev) -{ -} - -TestableCaseFile & -TestableCaseFile::Create(Vdev &vdev) -{ - TestableCaseFile *newCase; - newCase = new TestableCaseFile(vdev); - return (*newCase); -} - -void -TestableCaseFile::SpliceEvents() -{ - m_events.splice(m_events.begin(), m_tentativeEvents); -} - - -/* - * Test class ZfsdException - */ -class ZfsdExceptionTest : public ::testing::Test -{ -protected: - virtual void SetUp() - { - ASSERT_EQ(0, nvlist_alloc(&poolConfig, NV_UNIQUE_NAME, 0)); - ASSERT_EQ(0, nvlist_add_string(poolConfig, - ZPOOL_CONFIG_POOL_NAME, "unit_test_pool")); - ASSERT_EQ(0, nvlist_add_uint64(poolConfig, - ZPOOL_CONFIG_POOL_GUID, 0x1234)); - - ASSERT_EQ(0, nvlist_alloc(&vdevConfig, NV_UNIQUE_NAME, 0)); - ASSERT_EQ(0, nvlist_add_uint64(vdevConfig, - ZPOOL_CONFIG_GUID, 0x5678)); - bzero(&poolHandle, sizeof(poolHandle)); - poolHandle.zpool_config = poolConfig; - } - - virtual void TearDown() - { - nvlist_free(poolConfig); - nvlist_free(vdevConfig); - } - - nvlist_t *poolConfig; - nvlist_t *vdevConfig; - zpool_handle_t poolHandle; -}; - -TEST_F(ZfsdExceptionTest, StringConstructorNull) -{ - ZfsdException ze(""); - EXPECT_STREQ("", ze.GetString().c_str()); -} - -TEST_F(ZfsdExceptionTest, StringConstructorFormatted) -{ - ZfsdException ze(" %d %s", 55, "hello world"); - EXPECT_STREQ(" 55 hello world", ze.GetString().c_str()); -} - -TEST_F(ZfsdExceptionTest, LogSimple) -{ - ZfsdException ze("unit test w/o vdev or pool"); - ze.Log(); - EXPECT_EQ(LOG_ERR, syslog_last_priority); - EXPECT_STREQ("unit test w/o vdev or pool\n", syslog_last_message); -} - -TEST_F(ZfsdExceptionTest, Pool) -{ - const char msg[] = "Exception with pool name"; - char expected[4096]; - sprintf(expected, "Pool unit_test_pool: %s\n", msg); - ZfsdException ze(poolConfig, msg); - ze.Log(); - EXPECT_STREQ(expected, syslog_last_message); -} - -TEST_F(ZfsdExceptionTest, PoolHandle) -{ - const char msg[] = "Exception with pool handle"; - char expected[4096]; - sprintf(expected, "Pool unit_test_pool: %s\n", msg); - ZfsdException ze(&poolHandle, msg); - ze.Log(); - EXPECT_STREQ(expected, syslog_last_message); -} - -/* - * Test class Vdev - */ -class VdevTest : public ::testing::Test -{ -protected: - virtual void SetUp() - { - ASSERT_EQ(0, nvlist_alloc(&m_poolConfig, NV_UNIQUE_NAME, 0)); - ASSERT_EQ(0, nvlist_add_uint64(m_poolConfig, - ZPOOL_CONFIG_POOL_GUID, - 0x1234)); - - ASSERT_EQ(0, nvlist_alloc(&m_vdevConfig, NV_UNIQUE_NAME, 0)); - ASSERT_EQ(0, nvlist_add_uint64(m_vdevConfig, ZPOOL_CONFIG_GUID, - 0x5678)); - } - - virtual void TearDown() - { - nvlist_free(m_poolConfig); - nvlist_free(m_vdevConfig); - } - - nvlist_t *m_poolConfig; - nvlist_t *m_vdevConfig; -}; - - -TEST_F(VdevTest, StateFromConfig) -{ - vdev_stat_t vs; - - vs.vs_state = VDEV_STATE_OFFLINE; - - ASSERT_EQ(0, nvlist_add_uint64_array(m_vdevConfig, - ZPOOL_CONFIG_VDEV_STATS, - (uint64_t*)&vs, - sizeof(vs) / sizeof(uint64_t))); - - Vdev vdev(m_poolConfig, m_vdevConfig); - - EXPECT_EQ(VDEV_STATE_OFFLINE, vdev.State()); -} - -TEST_F(VdevTest, StateFaulted) -{ - ASSERT_EQ(0, nvlist_add_uint64(m_vdevConfig, ZPOOL_CONFIG_FAULTED, 1)); - - Vdev vdev(m_poolConfig, m_vdevConfig); - - EXPECT_EQ(VDEV_STATE_FAULTED, vdev.State()); -} - -/* - * Test that we can construct a Vdev from the label information that is stored - * on an available spare drive - */ -TEST_F(VdevTest, ConstructAvailSpare) -{ - nvlist_t *labelConfig; - - ASSERT_EQ(0, nvlist_alloc(&labelConfig, NV_UNIQUE_NAME, 0)); - ASSERT_EQ(0, nvlist_add_uint64(labelConfig, ZPOOL_CONFIG_GUID, - 1948339428197961030)); - ASSERT_EQ(0, nvlist_add_uint64(labelConfig, ZPOOL_CONFIG_POOL_STATE, - POOL_STATE_SPARE)); - - EXPECT_NO_THROW(Vdev vdev(labelConfig)); - - nvlist_free(labelConfig); -} - -/* Available spares will always show the HEALTHY state */ -TEST_F(VdevTest, AvailSpareState) { - nvlist_t *labelConfig; - - ASSERT_EQ(0, nvlist_alloc(&labelConfig, NV_UNIQUE_NAME, 0)); - ASSERT_EQ(0, nvlist_add_uint64(labelConfig, ZPOOL_CONFIG_GUID, - 1948339428197961030)); - ASSERT_EQ(0, nvlist_add_uint64(labelConfig, ZPOOL_CONFIG_POOL_STATE, - POOL_STATE_SPARE)); - - Vdev vdev(labelConfig); - EXPECT_EQ(VDEV_STATE_HEALTHY, vdev.State()); - - nvlist_free(labelConfig); -} - -/* Test the Vdev::IsSpare method */ -TEST_F(VdevTest, IsSpare) { - Vdev notSpare(m_poolConfig, m_vdevConfig); - EXPECT_EQ(false, notSpare.IsSpare()); - - ASSERT_EQ(0, nvlist_add_uint64(m_vdevConfig, ZPOOL_CONFIG_IS_SPARE, 1)); - Vdev isSpare(m_poolConfig, m_vdevConfig); - EXPECT_EQ(true, isSpare.IsSpare()); -} - -/* - * Test class ZFSEvent - */ -class ZfsEventTest : public ::testing::Test -{ -protected: - virtual void SetUp() - { - m_eventFactory = new EventFactory(); - m_eventFactory->UpdateRegistry(MockZfsEvent::s_buildRecords, - NUM_ELEMENTS(MockZfsEvent::s_buildRecords)); - - m_event = NULL; - } - - virtual void TearDown() - { - delete m_eventFactory; - delete m_event; - } - - EventFactory *m_eventFactory; - Event *m_event; -}; - -TEST_F(ZfsEventTest, ProcessPoolEventGetsCalled) -{ - string evString("!system=ZFS " - "subsystem=ZFS " - "type=misc.fs.zfs.vdev_remove " - "pool_name=foo " - "pool_guid=9756779504028057996 " - "vdev_guid=1631193447431603339 " - "vdev_path=/dev/da1 " - "timestamp=1348871594"); - m_event = Event::CreateEvent(*m_eventFactory, evString); - MockZfsEvent *mock_event = static_cast(m_event); - - EXPECT_CALL(*mock_event, ProcessPoolEvent()).Times(1); - mock_event->Process(); -} - -/* - * Test class CaseFile - */ - -class CaseFileTest : public ::testing::Test -{ -protected: - virtual void SetUp() - { - m_eventFactory = new EventFactory(); - m_eventFactory->UpdateRegistry(MockZfsEvent::s_buildRecords, - NUM_ELEMENTS(MockZfsEvent::s_buildRecords)); - - m_event = NULL; - - nvlist_alloc(&m_vdevConfig, NV_UNIQUE_NAME, 0); - ASSERT_EQ(0, nvlist_add_uint64(m_vdevConfig, - ZPOOL_CONFIG_GUID, 0xbeef)); - m_vdev = new MockVdev(m_vdevConfig); - ON_CALL(*m_vdev, GUID()) - .WillByDefault(::testing::Return(Guid(123))); - ON_CALL(*m_vdev, PoolGUID()) - .WillByDefault(::testing::Return(Guid(456))); - ON_CALL(*m_vdev, State()) - .WillByDefault(::testing::Return(VDEV_STATE_HEALTHY)); - m_caseFile = &TestableCaseFile::Create(*m_vdev); - ON_CALL(*m_caseFile, ReEvaluate(::testing::_)) - .WillByDefault(::testing::Invoke(m_caseFile, &TestableCaseFile::RealReEvaluate)); - return; - } - - virtual void TearDown() - { - delete m_caseFile; - nvlist_free(m_vdevConfig); - delete m_vdev; - delete m_event; - delete m_eventFactory; - } - - nvlist_t *m_vdevConfig; - MockVdev *m_vdev; - TestableCaseFile *m_caseFile; - Event *m_event; - EventFactory *m_eventFactory; -}; - -/* - * A Vdev with no events should not be degraded or faulted - */ -TEST_F(CaseFileTest, HealthyVdev) -{ - EXPECT_FALSE(m_caseFile->ShouldDegrade()); - EXPECT_FALSE(m_caseFile->ShouldFault()); -} - -/* - * A Vdev with only one event should not be degraded or faulted - * For performance reasons, RefreshVdevState should not be called. - */ -TEST_F(CaseFileTest, HealthyishVdev) -{ - string evString("!system=ZFS " - "class=ereport.fs.zfs.io " - "ena=12091638756982918145 " - "parent_guid=13237004955564865395 " - "parent_type=raidz " - "pool=testpool.4415 " - "pool_context=0 " - "pool_failmode=wait " - "pool_guid=456 " - "subsystem=ZFS " - "timestamp=1348867914 " - "type=ereport.fs.zfs.io " - "vdev_guid=123 " - "vdev_path=/dev/da400 " - "vdev_type=disk " - "zio_blkid=622 " - "zio_err=1 " - "zio_level=-2 " - "zio_object=0 " - "zio_objset=37 " - "zio_offset=25598976 " - "zio_size=1024"); - m_event = Event::CreateEvent(*m_eventFactory, evString); - ZfsEvent *zfs_event = static_cast(m_event); - - EXPECT_CALL(*m_caseFile, RefreshVdevState()) - .Times(::testing::Exactly(0)); - EXPECT_TRUE(m_caseFile->ReEvaluate(*zfs_event)); - EXPECT_FALSE(m_caseFile->ShouldDegrade()); - EXPECT_FALSE(m_caseFile->ShouldFault()); -} - -/* The case file should be closed when its pool is destroyed */ -TEST_F(CaseFileTest, PoolDestroy) -{ - string evString("!system=ZFS " - "pool_name=testpool.4415 " - "pool_guid=456 " - "subsystem=ZFS " - "timestamp=1348867914 " - "type=misc.fs.zfs.pool_destroy "); - m_event = Event::CreateEvent(*m_eventFactory, evString); - ZfsEvent *zfs_event = static_cast(m_event); - EXPECT_CALL(*m_caseFile, Close()); - EXPECT_TRUE(m_caseFile->ReEvaluate(*zfs_event)); -} - -/* - * A Vdev with a very large number of IO errors should fault - * For performance reasons, RefreshVdevState should be called at most once - */ -TEST_F(CaseFileTest, VeryManyIOErrors) -{ - EXPECT_CALL(*m_caseFile, RefreshVdevState()) - .Times(::testing::AtMost(1)) - .WillRepeatedly(::testing::Return(true)); - - for(int i=0; i<100; i++) { - stringstream evStringStream; - evStringStream << - "!system=ZFS " - "class=ereport.fs.zfs.io " - "ena=12091638756982918145 " - "parent_guid=13237004955564865395 " - "parent_type=raidz " - "pool=testpool.4415 " - "pool_context=0 " - "pool_failmode=wait " - "pool_guid=456 " - "subsystem=ZFS " - "timestamp="; - evStringStream << i << " "; - evStringStream << - "type=ereport.fs.zfs.io " - "vdev_guid=123 " - "vdev_path=/dev/da400 " - "vdev_type=disk " - "zio_blkid=622 " - "zio_err=1 " - "zio_level=-2 " - "zio_object=0 " - "zio_objset=37 " - "zio_offset=25598976 " - "zio_size=1024"; - Event *event(Event::CreateEvent(*m_eventFactory, - evStringStream.str())); - ZfsEvent *zfs_event = static_cast(event); - EXPECT_TRUE(m_caseFile->ReEvaluate(*zfs_event)); - delete event; - } - - m_caseFile->SpliceEvents(); - EXPECT_FALSE(m_caseFile->ShouldDegrade()); - EXPECT_TRUE(m_caseFile->ShouldFault()); -} - -/* - * A Vdev with a very large number of checksum errors should degrade - * For performance reasons, RefreshVdevState should be called at most once - */ -TEST_F(CaseFileTest, VeryManyChecksumErrors) -{ - EXPECT_CALL(*m_caseFile, RefreshVdevState()) - .Times(::testing::AtMost(1)) - .WillRepeatedly(::testing::Return(true)); - - for(int i=0; i<100; i++) { - stringstream evStringStream; - evStringStream << - "!system=ZFS " - "bad_cleared_bits=03000000000000803f50b00000000000 " - "bad_range_clears=0000000e " - "bad_range_sets=00000000 " - "bad_ranges=0000000000000010 " - "bad_ranges_min_gap=8 " - "bad_set_bits=00000000000000000000000000000000 " - "class=ereport.fs.zfs.checksum " - "ena=12272856582652437505 " - "parent_guid=5838204195352909894 " - "parent_type=raidz pool=testpool.7640 " - "pool_context=0 " - "pool_failmode=wait " - "pool_guid=456 " - "subsystem=ZFS timestamp="; - evStringStream << i << " "; - evStringStream << - "type=ereport.fs.zfs.checksum " - "vdev_guid=123 " - "vdev_path=/mnt/tmp/file1.7702 " - "vdev_type=file " - "zio_blkid=0 " - "zio_err=0 " - "zio_level=0 " - "zio_object=3 " - "zio_objset=0 " - "zio_offset=16896 " - "zio_size=512"; - Event *event(Event::CreateEvent(*m_eventFactory, - evStringStream.str())); - ZfsEvent *zfs_event = static_cast(event); - EXPECT_TRUE(m_caseFile->ReEvaluate(*zfs_event)); - delete event; - } - - m_caseFile->SpliceEvents(); - EXPECT_TRUE(m_caseFile->ShouldDegrade()); - EXPECT_FALSE(m_caseFile->ShouldFault()); -} - -/* - * Test CaseFile::ReEvaluateByGuid - */ -class ReEvaluateByGuidTest : public ::testing::Test -{ -protected: - virtual void SetUp() - { - m_eventFactory = new EventFactory(); - m_eventFactory->UpdateRegistry(MockZfsEvent::s_buildRecords, - NUM_ELEMENTS(MockZfsEvent::s_buildRecords)); - m_event = Event::CreateEvent(*m_eventFactory, s_evString); - nvlist_alloc(&m_vdevConfig, NV_UNIQUE_NAME, 0); - ASSERT_EQ(0, nvlist_add_uint64(m_vdevConfig, - ZPOOL_CONFIG_GUID, 0xbeef)); - m_vdev456 = new ::testing::NiceMock(m_vdevConfig); - m_vdev789 = new ::testing::NiceMock(m_vdevConfig); - ON_CALL(*m_vdev456, GUID()) - .WillByDefault(::testing::Return(Guid(123))); - ON_CALL(*m_vdev456, PoolGUID()) - .WillByDefault(::testing::Return(Guid(456))); - ON_CALL(*m_vdev456, State()) - .WillByDefault(::testing::Return(VDEV_STATE_HEALTHY)); - ON_CALL(*m_vdev789, GUID()) - .WillByDefault(::testing::Return(Guid(123))); - ON_CALL(*m_vdev789, PoolGUID()) - .WillByDefault(::testing::Return(Guid(789))); - ON_CALL(*m_vdev789, State()) - .WillByDefault(::testing::Return(VDEV_STATE_HEALTHY)); - m_caseFile456 = NULL; - m_caseFile789 = NULL; - return; - } - - virtual void TearDown() - { - delete m_caseFile456; - delete m_caseFile789; - nvlist_free(m_vdevConfig); - delete m_vdev456; - delete m_vdev789; - delete m_event; - delete m_eventFactory; - } - - static string s_evString; - nvlist_t *m_vdevConfig; - ::testing::NiceMock *m_vdev456; - ::testing::NiceMock *m_vdev789; - TestableCaseFile *m_caseFile456; - TestableCaseFile *m_caseFile789; - Event *m_event; - EventFactory *m_eventFactory; -}; - -string ReEvaluateByGuidTest::s_evString( - "!system=ZFS " - "pool_guid=16271873792808333580 " - "pool_name=foo " - "subsystem=ZFS " - "timestamp=1360620391 " - "type=misc.fs.zfs.config_sync"); - - -/* - * Test the ReEvaluateByGuid method on an empty list of casefiles. - * We must create one event, even though it never gets used, because it will - * be passed by reference to ReEvaluateByGuid - */ -TEST_F(ReEvaluateByGuidTest, ReEvaluateByGuid_empty) -{ - ZfsEvent *zfs_event = static_cast(m_event); - - EXPECT_EQ(0, TestableCaseFile::getActiveCases()); - CaseFile::ReEvaluateByGuid(Guid(456), *zfs_event); - EXPECT_EQ(0, TestableCaseFile::getActiveCases()); -} - -/* - * Test the ReEvaluateByGuid method on a list of CaseFiles that contains only - * one CaseFile, which doesn't match the criteria - */ -TEST_F(ReEvaluateByGuidTest, ReEvaluateByGuid_oneFalse) -{ - m_caseFile456 = &TestableCaseFile::Create(*m_vdev456); - ZfsEvent *zfs_event = static_cast(m_event); - - EXPECT_EQ(1, TestableCaseFile::getActiveCases()); - EXPECT_CALL(*m_caseFile456, ReEvaluate(::testing::_)) - .Times(::testing::Exactly(0)); - CaseFile::ReEvaluateByGuid(Guid(789), *zfs_event); - EXPECT_EQ(1, TestableCaseFile::getActiveCases()); -} - -/* - * Test the ReEvaluateByGuid method on a list of CaseFiles that contains only - * one CaseFile, which does match the criteria - */ -TEST_F(ReEvaluateByGuidTest, ReEvaluateByGuid_oneTrue) -{ - m_caseFile456 = &TestableCaseFile::Create(*m_vdev456); - ZfsEvent *zfs_event = static_cast(m_event); - - EXPECT_EQ(1, TestableCaseFile::getActiveCases()); - EXPECT_CALL(*m_caseFile456, ReEvaluate(::testing::_)) - .Times(::testing::Exactly(1)) - .WillRepeatedly(::testing::Return(false)); - CaseFile::ReEvaluateByGuid(Guid(456), *zfs_event); - EXPECT_EQ(1, TestableCaseFile::getActiveCases()); -} - -/* - * Test the ReEvaluateByGuid method on a long list of CaseFiles that contains a - * few cases which meet the criteria - */ -TEST_F(ReEvaluateByGuidTest, ReEvaluateByGuid_five) -{ - TestableCaseFile *CaseFile1 = &TestableCaseFile::Create(*m_vdev456); - TestableCaseFile *CaseFile2 = &TestableCaseFile::Create(*m_vdev789); - TestableCaseFile *CaseFile3 = &TestableCaseFile::Create(*m_vdev456); - TestableCaseFile *CaseFile4 = &TestableCaseFile::Create(*m_vdev789); - TestableCaseFile *CaseFile5 = &TestableCaseFile::Create(*m_vdev789); - ZfsEvent *zfs_event = static_cast(m_event); - - EXPECT_EQ(5, TestableCaseFile::getActiveCases()); - EXPECT_CALL(*CaseFile1, ReEvaluate(::testing::_)) - .Times(::testing::Exactly(1)) - .WillRepeatedly(::testing::Return(false)); - EXPECT_CALL(*CaseFile3, ReEvaluate(::testing::_)) - .Times(::testing::Exactly(1)) - .WillRepeatedly(::testing::Return(false)); - EXPECT_CALL(*CaseFile2, ReEvaluate(::testing::_)) - .Times(::testing::Exactly(0)); - EXPECT_CALL(*CaseFile4, ReEvaluate(::testing::_)) - .Times(::testing::Exactly(0)); - EXPECT_CALL(*CaseFile5, ReEvaluate(::testing::_)) - .Times(::testing::Exactly(0)); - CaseFile::ReEvaluateByGuid(Guid(456), *zfs_event); - EXPECT_EQ(5, TestableCaseFile::getActiveCases()); - delete CaseFile1; - delete CaseFile2; - delete CaseFile3; - delete CaseFile4; - delete CaseFile5; -} Index: projects/zfsd/head/cddl/sbin/zfsd/tests/libmocks.h =================================================================== --- projects/zfsd/head/cddl/sbin/zfsd/tests/libmocks.h (revision 300334) +++ projects/zfsd/head/cddl/sbin/zfsd/tests/libmocks.h (nonexistent) @@ -1,24 +0,0 @@ -#ifndef _LIBMOCKS_H_ -#define _LIBMOCKS_H_ - -#ifdef __cplusplus -extern "C" { -#endif - -struct libzfs_handle; -typedef struct libzfs_handle libzfs_handle_t; -struct zpool_handle; -typedef struct zpool_handle zpool_handle_t; -typedef int (*zpool_iter_f)(zpool_handle_t *, void *); - -void syslog(int priority, const char* message, ...); -int zpool_iter(libzfs_handle_t*, zpool_iter_f, void*); - -extern int syslog_last_priority; -extern char syslog_last_message[4096]; - -#ifdef __cplusplus -} -#endif - -#endif Index: projects/zfsd/head/cddl/sbin/zfsd/tests/zfsd_unittest.supp =================================================================== --- projects/zfsd/head/cddl/sbin/zfsd/tests/zfsd_unittest.supp (revision 300334) +++ projects/zfsd/head/cddl/sbin/zfsd/tests/zfsd_unittest.supp (nonexistent) @@ -1,104 +0,0 @@ -# This is a valgrind suppression file used for running zfsd_unittest with -# valgrind. It suppress spurious errors generated by the googletest and -# googlemock libraries. -# -# To use, do: -# valgrind --suppressions=$PWD/zfsd_unittest.supp ./zfsd_unittest - -{ - - Memcheck:Free - fun:free - ... - fun:__cxa_finalize - fun:exit - fun:(below main) -} - -{ - - Memcheck:Free - fun:free - ... - fun:_ZN7testing8internal27PrettyUnitTestResultPrinter* - ... - ... - fun:main -} - -{ - - Memcheck:Free - fun:free - fun:_ZN7testing* - ... - fun:main -} - -{ - - Memcheck:Free - fun:free - ... - fun:_Z41__static_initialization_and_destruction_0ii - ... -} - -{ - - Memcheck:Free - fun:free - ... - fun:_ZN7testing8internal8MockSpec* - ... - fun:_ZN7testing4Test3RunEv - fun:_ZN7testing8internal12TestInfoImpl3RunEv - fun:_ZN7testing8TestCase3RunEv - fun:_ZN7testing8internal12UnitTestImpl11RunAllTestsEv -} - -{ - - Memcheck:Free - fun:free - ... - fun:_ZN7testing8internal14FunctionMocker* - ... -} - -{ - - Memcheck:Cond - obj:/lib/libc.so.7 - obj:/lib/libc.so.7 - fun:snprintf - fun:_ZN7testing45_GLOBAL__N_src_gmock_all.cc_00000000_917CAD5926PrintByteSegmentInObjectToEPKhmmPSo - fun:_ZN7testing9internal220PrintBytesInObjectToEPKhmPSo - fun:_ZN7testing9internal220TypeWithoutFormatterI8ZfsEventLb0EE10PrintValueERKS2_PSo - fun:_ZN7testing9internal2lsIcSt11char_traitsIcE8ZfsEventEERSt13basic_ostreamIT_T0_ES9_RKT1_ - fun:_ZN16testing_internal26DefaultPrintNonContainerToI8ZfsEventEEvRKT_PSo - fun:_ZN7testing8internal14DefaultPrintToI8ZfsEventEEvcNS0_13bool_constantILb0EEERKT_PSo - fun:_ZN7testing8internal7PrintToI8ZfsEventEEvRKT_PSo - fun:_ZN7testing8internal16UniversalPrinterIK8ZfsEventE5PrintERS3_PSo - fun:_ZN7testing8internal16UniversalPrinterIRK8ZfsEventE5PrintES4_PSo -} - -{ - - Memcheck:Cond - ... - fun:snprintf - ... - fun:_ZN7testing9internal220PrintBytesInObjectToEPKhmPSo - ... -} -{ - - Memcheck:Value8 - ... - fun:snprintf - ... - fun:_ZN7testing9internal220PrintBytesInObjectToEPKhmPSo - ... -} - Index: projects/zfsd/head/cddl/sbin/zfsd/tests/Makefile =================================================================== --- projects/zfsd/head/cddl/sbin/zfsd/tests/Makefile (revision 300334) +++ projects/zfsd/head/cddl/sbin/zfsd/tests/Makefile (nonexistent) @@ -1,48 +0,0 @@ -# $FreeBSD$ - -SRCDIR=${.CURDIR}/../../../.. -.include "${.CURDIR}/../Makefile.common" -.PATH: ${.CURDIR}/.. - -PROG_CXX= zfsd_unittest -SRCS:= ${SRCS:Nzfsd_main.cc} -SRCS+= libmocks.c zfsd_unittest.cc - -# Use #include in test programs. -INCFLAGS+= -I ${.CURDIR}/../.. - -.if defined(DESTDIR) -INCFLAGS+= -I ${DESTDIR}/usr/include -LIBRARY_PATH= ${DESTDIR}/lib:${DESTDIR}/usr/lib -LDFLAGS+= -L ${DESTDIR}/lib -L ${DESTDIR}/usr/lib -.elif defined(WORLDTMP) -INCFLAGS+= -I ${WORLDTMP}/usr/include -LIBRARY_PATH= ${WORLDTMP}/lib:${WORLDTMP}/usr/lib -LDFLAGS+= -L ${WORLDTMP}/lib -L ${WORLDTMP}/usr/lib -.else -LIBRARY_PATH= -.endif -ZFSD_UNITTEST= env LD_LIBRARY_PATH=${LIBRARY_PATH} ./zfsd_unittest - -# Googletest options -LOCALBASE?= /usr/local -INCFLAGS+= -I ${LOCALBASE}/include -D_THREAD_SAFE -pthread -LDFLAGS+= -L ${LOCALBASE}/lib -D_THREAD_SAFE -pthread -LDADD+= ${LOCALBASE}/lib/libgtest.a - -# GoogleMock options -LDADD+= ${LOCALBASE}/lib/libgmock.a ${LOCALBASE}/lib/libgmock_main.a - -# Googlemock fails if we don't have this line -# https://groups.google.com/forum/#!msg/googletestframework/h8ixEPCFm0o/amwfu4xGJb0J -CFLAGS+= -DGTEST_HAS_PTHREAD - -# Install the tests -TESTSBASE?= /usr/tests -TESTSDIR?= ${TESTSBASE}/cddl/sbin/zfsd -# TODO: Convert from an ATF SH test to a Kyua plain test -# Long term TODO: Convert to a Kyua googletest test -ATF_TESTS_SH+= zfsd_test -BINDIR= ${TESTSDIR} - -.include Index: projects/zfsd/head/cddl/sbin/zfsd/zfsd_main.cc =================================================================== --- projects/zfsd/head/cddl/sbin/zfsd/zfsd_main.cc (revision 300334) +++ projects/zfsd/head/cddl/sbin/zfsd/zfsd_main.cc (nonexistent) @@ -1,90 +0,0 @@ -/*- - * Copyright (c) 2011 Spectra Logic Corporation - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions, and the following disclaimer, - * without modification. - * 2. Redistributions in binary form must reproduce at minimum a disclaimer - * substantially similar to the "NO WARRANTY" disclaimer below - * ("Disclaimer") and any redistribution must be conditioned upon - * including a substantially similar Disclaimer requirement for further - * binary redistribution. - * - * NO WARRANTY - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING - * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGES. - * - * Authors: Alan Somers (Spectra Logic Corporation) - */ - -/** - * \file zfsd_main.cc - * - * main function for the ZFS Daemon. Separated to facilitate testing. - * - */ - -#include - -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "vdev_iterator.h" -#include "zfsd.h" - -__FBSDID("$FreeBSD$"); - -/*=============================== Program Main ===============================*/ -static void -usage() -{ - fprintf(stderr, "usage: %s [-d]\n", getprogname()); - exit(1); -} - -/** - * Program entry point. - */ -int -main(int argc, char **argv) -{ - int ch; - - while ((ch = getopt(argc, argv, "d")) != -1) { - switch (ch) { - case 'd': - g_debug++; - break; - default: - usage(); - } - } - - ZfsDaemon::Run(); - - return (0); -} Index: projects/zfsd/head/cddl/sbin/zfsd/case_file.cc =================================================================== --- projects/zfsd/head/cddl/sbin/zfsd/case_file.cc (revision 300334) +++ projects/zfsd/head/cddl/sbin/zfsd/case_file.cc (nonexistent) @@ -1,1127 +0,0 @@ -/*- - * Copyright (c) 2011, 2012, 2013 Spectra Logic Corporation - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions, and the following disclaimer, - * without modification. - * 2. Redistributions in binary form must reproduce at minimum a disclaimer - * substantially similar to the "NO WARRANTY" disclaimer below - * ("Disclaimer") and any redistribution must be conditioned upon - * including a substantially similar Disclaimer requirement for further - * binary redistribution. - * - * NO WARRANTY - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING - * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGES. - * - * Authors: Justin T. Gibbs (Spectra Logic Corporation) - */ - -/** - * \file case_file.cc - * - * We keep case files for any leaf vdev that is not in the optimal state. - * However, we only serialize to disk those events that need to be preserved - * across reboots. For now, this is just a log of soft errors which we - * accumulate in order to mark a device as degraded. - */ -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "callout.h" -#include "vdev_iterator.h" -#include "zfsd_event.h" -#include "case_file.h" -#include "vdev.h" -#include "zfsd.h" -#include "zfsd_exception.h" -#include "zpool_list.h" - -__FBSDID("$FreeBSD$"); - -/*============================ Namespace Control =============================*/ -using std::auto_ptr; -using std::hex; -using std::ifstream; -using std::stringstream; -using std::setfill; -using std::setw; - -using DevdCtl::Event; -using DevdCtl::EventBuffer; -using DevdCtl::EventFactory; -using DevdCtl::EventList; -using DevdCtl::Guid; -using DevdCtl::ParseException; - -/*-------------------------- File-scoped classes ----------------------------*/ -/** - * \brief Functor that operators on STL collections of CaseFiles - * - * Selectively calls ReEvaluate on the casefile, based on its pool GUID. - */ -class CaseFileReEvaluator : public std::unary_function -{ -public: - CaseFileReEvaluator(Guid guid, const ZfsEvent &event); - - void operator() (CaseFile *casefile); - -private: - Guid m_poolGUID; - const ZfsEvent &m_event; -}; - -CaseFileReEvaluator::CaseFileReEvaluator(Guid guid, const ZfsEvent &event) - : m_poolGUID(guid), m_event(event) -{ -} - -void -CaseFileReEvaluator::operator() (CaseFile *casefile) -{ - if (m_poolGUID == casefile->PoolGUID()) - casefile->ReEvaluate(m_event); -} - -/*--------------------------------- CaseFile ---------------------------------*/ -//- CaseFile Static Data ------------------------------------------------------- -CaseFileList CaseFile::s_activeCases; -const string CaseFile::s_caseFilePath = "/etc/zfs/cases"; -const timeval CaseFile::s_removeGracePeriod = { 60 /*sec*/, 0 /*usec*/}; - -//- CaseFile Static Public Methods --------------------------------------------- -CaseFile * -CaseFile::Find(Guid poolGUID, Guid vdevGUID) -{ - for (CaseFileList::iterator curCase = s_activeCases.begin(); - curCase != s_activeCases.end(); curCase++) { - - if ((*curCase)->PoolGUID() != poolGUID - || (*curCase)->VdevGUID() != vdevGUID) - continue; - - /* - * We only carry one active case per-vdev. - */ - return (*curCase); - } - return (NULL); -} - -CaseFile * -CaseFile::Find(const string &physPath) -{ - CaseFile *result = NULL; - - for (CaseFileList::iterator curCase = s_activeCases.begin(); - curCase != s_activeCases.end(); curCase++) { - - if ((*curCase)->PhysicalPath() != physPath) - continue; - - if (result != NULL) { - syslog(LOG_WARNING, "Multiple casefiles found for " - "physical path %s. " - "This is most likely a bug in zfsd", - physPath.c_str()); - } - result = *curCase; - } - return (result); -} - - -void -CaseFile::ReEvaluateByGuid(Guid poolGUID, const ZfsEvent &event) -{ - CaseFileReEvaluator reevaluator(poolGUID, event); - std::for_each(s_activeCases.begin(), s_activeCases.end(), reevaluator); -} - -CaseFile & -CaseFile::Create(Vdev &vdev) -{ - CaseFile *activeCase; - - activeCase = Find(vdev.PoolGUID(), vdev.GUID()); - if (activeCase == NULL) - activeCase = new CaseFile(vdev); - - return (*activeCase); -} - -void -CaseFile::DeSerialize() -{ - struct dirent **caseFiles; - - int numCaseFiles(scandir(s_caseFilePath.c_str(), &caseFiles, - DeSerializeSelector, /*compar*/NULL)); - - if (numCaseFiles == -1) - return; - if (numCaseFiles == 0) { - free(caseFiles); - return; - } - - for (int i = 0; i < numCaseFiles; i++) { - - DeSerializeFile(caseFiles[i]->d_name); - free(caseFiles[i]); - } - free(caseFiles); -} - -void -CaseFile::LogAll() -{ - for (CaseFileList::iterator curCase = s_activeCases.begin(); - curCase != s_activeCases.end(); curCase++) - (*curCase)->Log(); -} - -void -CaseFile::PurgeAll() -{ - /* - * Serialize casefiles before deleting them so that they can be reread - * and revalidated during BuildCaseFiles. - * CaseFiles remove themselves from this list on destruction. - */ - while (s_activeCases.size() != 0) { - CaseFile *casefile = s_activeCases.front(); - casefile->Serialize(); - delete casefile; - } - -} - -//- CaseFile Public Methods ---------------------------------------------------- -bool -CaseFile::RefreshVdevState() -{ - ZpoolList zpl(ZpoolList::ZpoolByGUID, &m_poolGUID); - zpool_handle_t *casePool(zpl.empty() ? NULL : zpl.front()); - if (casePool == NULL) - return (false); - - Vdev vd(casePool, CaseVdev(casePool)); - if (vd.DoesNotExist()) - return (false); - - m_vdevState = vd.State(); - m_vdevPhysPath = vd.PhysicalPath(); - return (true); -} - -bool -CaseFile::ReEvaluate(const string &devPath, const string &physPath, Vdev *vdev) -{ - ZpoolList zpl(ZpoolList::ZpoolByGUID, &m_poolGUID); - zpool_handle_t *pool(zpl.empty() ? NULL : zpl.front()); - - if (pool == NULL || !RefreshVdevState()) { - /* - * The pool or vdev for this case file is no longer - * part of the configuration. This can happen - * if we process a device arrival notification - * before seeing the ZFS configuration change - * event. - */ - syslog(LOG_INFO, - "CaseFile::ReEvaluate(%s,%s) Pool/Vdev unconfigured. " - "Closing\n", - PoolGUIDString().c_str(), - VdevGUIDString().c_str()); - Close(); - - /* - * Since this event was not used to close this - * case, do not report it as consumed. - */ - return (/*consumed*/false); - } - - if (VdevState() > VDEV_STATE_CANT_OPEN) { - /* - * For now, newly discovered devices only help for - * devices that are missing. In the future, we might - * use a newly inserted spare to replace a degraded - * or faulted device. - */ - syslog(LOG_INFO, "CaseFile::ReEvaluate(%s,%s): Pool/Vdev ignored", - PoolGUIDString().c_str(), VdevGUIDString().c_str()); - return (/*consumed*/false); - } - - if (vdev != NULL - && vdev->PoolGUID() == m_poolGUID - && vdev->GUID() == m_vdevGUID) { - - zpool_vdev_online(pool, vdev->GUIDString().c_str(), - ZFS_ONLINE_CHECKREMOVE | ZFS_ONLINE_UNSPARE, - &m_vdevState); - syslog(LOG_INFO, "Onlined vdev(%s/%s:%s). State now %s.\n", - zpool_get_name(pool), vdev->GUIDString().c_str(), - devPath.c_str(), - zpool_state_to_name(VdevState(), VDEV_AUX_NONE)); - - /* - * Check the vdev state post the online action to see - * if we can retire this case. - */ - CloseIfSolved(); - - return (/*consumed*/true); - } - - /* - * If the auto-replace policy is enabled, and we have physical - * path information, try a physical path replacement. - */ - if (zpool_get_prop_int(pool, ZPOOL_PROP_AUTOREPLACE, NULL) == 0) { - syslog(LOG_INFO, - "CaseFile(%s:%s:%s): AutoReplace not set. " - "Ignoring device insertion.\n", - PoolGUIDString().c_str(), - VdevGUIDString().c_str(), - zpool_state_to_name(VdevState(), VDEV_AUX_NONE)); - return (/*consumed*/false); - } - - if (PhysicalPath().empty()) { - syslog(LOG_INFO, - "CaseFile(%s:%s:%s): No physical path information. " - "Ignoring device insertion.\n", - PoolGUIDString().c_str(), - VdevGUIDString().c_str(), - zpool_state_to_name(VdevState(), VDEV_AUX_NONE)); - return (/*consumed*/false); - } - - if (physPath != PhysicalPath()) { - syslog(LOG_INFO, - "CaseFile(%s:%s:%s): Physical path mismatch. " - "Ignoring device insertion.\n", - PoolGUIDString().c_str(), - VdevGUIDString().c_str(), - zpool_state_to_name(VdevState(), VDEV_AUX_NONE)); - return (/*consumed*/false); - } - - /* Write a label on the newly inserted disk. */ - if (zpool_label_disk(g_zfsHandle, pool, devPath.c_str()) != 0) { - syslog(LOG_ERR, - "Replace vdev(%s/%s) by physical path (label): %s: %s\n", - zpool_get_name(pool), VdevGUIDString().c_str(), - libzfs_error_action(g_zfsHandle), - libzfs_error_description(g_zfsHandle)); - return (/*consumed*/false); - } - - syslog(LOG_INFO, "CaseFile::ReEvaluate(%s/%s): Replacing with %s", - PoolGUIDString().c_str(), VdevGUIDString().c_str(), - devPath.c_str()); - return (Replace(VDEV_TYPE_DISK, devPath.c_str(), /*isspare*/false)); -} - -bool -CaseFile::ReEvaluate(const ZfsEvent &event) -{ - bool consumed(false); - - if (event.Value("type") == "misc.fs.zfs.vdev_remove") { - /* - * The Vdev we represent has been removed from the - * configuration. This case is no longer of value. - */ - Close(); - - return (/*consumed*/true); - } else if (event.Value("type") == "misc.fs.zfs.pool_destroy") { - /* This Pool has been destroyed. Discard the case */ - Close(); - - return (/*consumed*/true); - } else if (event.Value("type") == "misc.fs.zfs.config_sync") { - RefreshVdevState(); - if (VdevState() < VDEV_STATE_HEALTHY) - consumed = ActivateSpare(); - } - - - if (event.Value("class") == "resource.fs.zfs.removed") { - bool spare_activated; - - if (!RefreshVdevState()) { - /* - * The pool or vdev for this case file is no longer - * part of the configuration. This can happen - * if we process a device arrival notification - * before seeing the ZFS configuration change - * event. - */ - syslog(LOG_INFO, - "CaseFile::ReEvaluate(%s,%s) Pool/Vdev " - "unconfigured. Closing\n", - PoolGUIDString().c_str(), - VdevGUIDString().c_str()); - /* - * Close the case now so we won't waste cycles in the - * system rescan - */ - Close(); - - /* - * Since this event was not used to close this - * case, do not report it as consumed. - */ - return (/*consumed*/false); - } - - /* - * Discard any tentative I/O error events for - * this case. They were most likely caused by the - * hot-unplug of this device. - */ - PurgeTentativeEvents(); - - /* Try to activate spares if they are available */ - spare_activated = ActivateSpare(); - - /* - * Rescan the drives in the system to see if a recent - * drive arrival can be used to solve this case. - */ - ZfsDaemon::RequestSystemRescan(); - - /* - * Consume the event if we successfully activated a spare. - * Otherwise, leave it in the unconsumed events list so that the - * future addition of a spare to this pool might be able to - * close the case - */ - consumed = spare_activated; - } else if (event.Value("class") == "resource.fs.zfs.statechange") { - RefreshVdevState(); - /* - * If this vdev is DEGRADED, FAULTED, or UNAVAIL, try to - * activate a hotspare. Otherwise, ignore the event - */ - if (VdevState() == VDEV_STATE_FAULTED || - VdevState() == VDEV_STATE_DEGRADED || - VdevState() == VDEV_STATE_CANT_OPEN) - (void) ActivateSpare(); - consumed = true; - } - else if (event.Value("class") == "ereport.fs.zfs.io" || - event.Value("class") == "ereport.fs.zfs.checksum") { - - m_tentativeEvents.push_front(event.DeepCopy()); - RegisterCallout(event); - consumed = true; - } - - bool closed(CloseIfSolved()); - - return (consumed || closed); -} - - -bool -CaseFile::ActivateSpare() { - nvlist_t *config, *nvroot; - nvlist_t **spares; - char *devPath, *vdev_type; - const char *poolname; - u_int nspares, i; - int error; - - ZpoolList zpl(ZpoolList::ZpoolByGUID, &m_poolGUID); - zpool_handle_t *zhp(zpl.empty() ? NULL : zpl.front()); - if (zhp == NULL) { - syslog(LOG_ERR, "CaseFile::ActivateSpare: Could not find pool " - "for pool_guid %"PRIu64".", (uint64_t)m_poolGUID); - return (false); - } - poolname = zpool_get_name(zhp); - config = zpool_get_config(zhp, NULL); - if (config == NULL) { - syslog(LOG_ERR, "CaseFile::ActivateSpare: Could not find pool " - "config for pool %s", poolname); - return (false); - } - error = nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, &nvroot); - if (error != 0){ - syslog(LOG_ERR, "CaseFile::ActivateSpare: Could not find vdev " - "tree for pool %s", poolname); - return (false); - } - nspares = 0; - nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_SPARES, &spares, - &nspares); - if (nspares == 0) { - /* The pool has no spares configured */ - syslog(LOG_INFO, "CaseFile::ActivateSpare: " - "No spares available for pool %s", poolname); - return (false); - } - for (i = 0; i < nspares; i++) { - uint64_t *nvlist_array; - vdev_stat_t *vs; - uint_t nstats; - - if (nvlist_lookup_uint64_array(spares[i], - ZPOOL_CONFIG_VDEV_STATS, &nvlist_array, &nstats) != 0) { - syslog(LOG_ERR, "CaseFile::ActivateSpare: Could not " - "find vdev stats for pool %s, spare %d", - poolname, i); - return (false); - } - vs = reinterpret_cast(nvlist_array); - - if ((vs->vs_aux != VDEV_AUX_SPARED) - && (vs->vs_state == VDEV_STATE_HEALTHY)) { - /* We found a usable spare */ - break; - } - } - - if (i == nspares) { - /* No available spares were found */ - return (false); - } - - error = nvlist_lookup_string(spares[i], ZPOOL_CONFIG_PATH, &devPath); - if (error != 0) { - syslog(LOG_ERR, "CaseFile::ActivateSpare: Cannot determine " - "the path of pool %s, spare %d. Error %d", - poolname, i, error); - return (false); - } - - error = nvlist_lookup_string(spares[i], ZPOOL_CONFIG_TYPE, &vdev_type); - if (error != 0) { - syslog(LOG_ERR, "CaseFile::ActivateSpare: Cannot determine " - "the vdev type of pool %s, spare %d. Error %d", - poolname, i, error); - return (false); - } - - return (Replace(vdev_type, devPath, /*isspare*/true)); -} - -void -CaseFile::RegisterCallout(const Event &event) -{ - timeval now, countdown, elapsed, timestamp, zero, remaining; - - gettimeofday(&now, 0); - timestamp = event.GetTimestamp(); - timersub(&now, ×tamp, &elapsed); - timersub(&s_removeGracePeriod, &elapsed, &countdown); - /* - * If countdown is <= zero, Reset the timer to the - * smallest positive time value instead - */ - timerclear(&zero); - if (timercmp(&countdown, &zero, <=)) { - timerclear(&countdown); - countdown.tv_usec = 1; - } - - remaining = m_tentativeTimer.TimeRemaining(); - - if (!m_tentativeTimer.IsPending() - || timercmp(&countdown, &remaining, <)) - m_tentativeTimer.Reset(countdown, OnGracePeriodEnded, this); -} - - -bool -CaseFile::CloseIfSolved() -{ - if (m_events.empty() - && m_tentativeEvents.empty()) { - - /* - * We currently do not track or take actions on - * devices in the degraded or faulted state. - * Once we have support for spare pools, we'll - * retain these cases so that any spares added in - * the future can be applied to them. - */ - switch (VdevState()) { - case VDEV_STATE_HEALTHY: - /* No need to keep cases for healthy vdevs */ - Close(); - return (true); - case VDEV_STATE_REMOVED: - case VDEV_STATE_CANT_OPEN: - /* - * Keep open. We may solve it with a newly inserted - * device. - */ - case VDEV_STATE_FAULTED: - case VDEV_STATE_DEGRADED: - /* - * Keep open. We may solve it with the future - * addition of a spare to the pool - */ - case VDEV_STATE_UNKNOWN: - case VDEV_STATE_CLOSED: - case VDEV_STATE_OFFLINE: - /* - * Keep open? This may not be the correct behavior, - * but it's what we've always done - */ - ; - } - - /* - * Re-serialize the case in order to remove any - * previous event data. - */ - Serialize(); - } - - return (false); -} - -void -CaseFile::Log() -{ - syslog(LOG_INFO, "CaseFile(%s,%s,%s)\n", PoolGUIDString().c_str(), - VdevGUIDString().c_str(), PhysicalPath().c_str()); - syslog(LOG_INFO, "\tVdev State = %s\n", - zpool_state_to_name(VdevState(), VDEV_AUX_NONE)); - if (m_tentativeEvents.size() != 0) { - syslog(LOG_INFO, "\t=== Tentative Events ===\n"); - for (EventList::iterator event(m_tentativeEvents.begin()); - event != m_tentativeEvents.end(); event++) - (*event)->Log(LOG_INFO); - } - if (m_events.size() != 0) { - syslog(LOG_INFO, "\t=== Events ===\n"); - for (EventList::iterator event(m_events.begin()); - event != m_events.end(); event++) - (*event)->Log(LOG_INFO); - } -} - -//- CaseFile Static Protected Methods ------------------------------------------ -void -CaseFile::OnGracePeriodEnded(void *arg) -{ - CaseFile &casefile(*static_cast(arg)); - - casefile.OnGracePeriodEnded(); -} - -int -CaseFile::DeSerializeSelector(const struct dirent *dirEntry) -{ - uint64_t poolGUID; - uint64_t vdevGUID; - - if (dirEntry->d_type == DT_REG - && sscanf(dirEntry->d_name, "pool_%"PRIu64"_vdev_%"PRIu64".case", - &poolGUID, &vdevGUID) == 2) - return (1); - return (0); -} - -void -CaseFile::DeSerializeFile(const char *fileName) -{ - string fullName(s_caseFilePath + '/' + fileName); - CaseFile *existingCaseFile(NULL); - CaseFile *caseFile(NULL); - - try { - uint64_t poolGUID; - uint64_t vdevGUID; - nvlist_t *vdevConf; - - sscanf(fileName, "pool_%"PRIu64"_vdev_%"PRIu64".case", - &poolGUID, &vdevGUID); - existingCaseFile = Find(Guid(poolGUID), Guid(vdevGUID)); - if (existingCaseFile != NULL) { - /* - * If the vdev is already degraded or faulted, - * there's no point in keeping the state around - * that we use to put a drive into the degraded - * state. However, if the vdev is simply missing, - * preseve the case data in the hopes that it will - * return. - */ - caseFile = existingCaseFile; - vdev_state curState(caseFile->VdevState()); - if (curState > VDEV_STATE_CANT_OPEN - && curState < VDEV_STATE_HEALTHY) { - unlink(fileName); - return; - } - } else { - ZpoolList zpl(ZpoolList::ZpoolByGUID, &poolGUID); - if (zpl.empty() - || (vdevConf = VdevIterator(zpl.front()) - .Find(vdevGUID)) == NULL) { - /* - * Either the pool no longer exists - * or this vdev is no longer a member of - * the pool. - */ - unlink(fullName.c_str()); - return; - } - - /* - * Any vdev we find that does not have a case file - * must be in the healthy state and thus worthy of - * continued SERD data tracking. - */ - caseFile = new CaseFile(Vdev(zpl.front(), vdevConf)); - } - - ifstream caseStream(fullName.c_str()); - if (!caseStream) - throw ZfsdException("CaseFile::DeSerialize: Unable to " - "read %s.\n", fileName); - - caseFile->DeSerialize(caseStream); - } catch (const ParseException &exp) { - - exp.Log(); - if (caseFile != existingCaseFile) - delete caseFile; - - /* - * Since we can't parse the file, unlink it so we don't - * trip over it again. - */ - unlink(fileName); - } catch (const ZfsdException &zfsException) { - - zfsException.Log(); - if (caseFile != existingCaseFile) - delete caseFile; - } -} - -//- CaseFile Protected Methods ------------------------------------------------- -CaseFile::CaseFile(const Vdev &vdev) - : m_poolGUID(vdev.PoolGUID()), - m_vdevGUID(vdev.GUID()), - m_vdevState(vdev.State()), - m_vdevPhysPath(vdev.PhysicalPath()) -{ - stringstream guidString; - - guidString << m_vdevGUID; - m_vdevGUIDString = guidString.str(); - guidString.str(""); - guidString << m_poolGUID; - m_poolGUIDString = guidString.str(); - - s_activeCases.push_back(this); - - syslog(LOG_INFO, "Creating new CaseFile:\n"); - Log(); -} - -CaseFile::~CaseFile() -{ - PurgeEvents(); - PurgeTentativeEvents(); - m_tentativeTimer.Stop(); - s_activeCases.remove(this); -} - -void -CaseFile::PurgeEvents() -{ - for (EventList::iterator event(m_events.begin()); - event != m_events.end(); event++) - delete *event; - - m_events.clear(); -} - -void -CaseFile::PurgeTentativeEvents() -{ - for (EventList::iterator event(m_tentativeEvents.begin()); - event != m_tentativeEvents.end(); event++) - delete *event; - - m_tentativeEvents.clear(); -} - -void -CaseFile::SerializeEvList(const EventList events, int fd, - const char* prefix) const -{ - if (events.empty()) - return; - for (EventList::const_iterator curEvent = events.begin(); - curEvent != events.end(); curEvent++) { - const string &eventString((*curEvent)->GetEventString()); - - // TODO: replace many write(2) calls with a single writev(2) - if (prefix) - write(fd, prefix, strlen(prefix)); - write(fd, eventString.c_str(), eventString.length()); - } -} - -void -CaseFile::Serialize() -{ - stringstream saveFile; - - saveFile << setfill('0') - << s_caseFilePath << "/" - << "pool_" << PoolGUIDString() - << "_vdev_" << VdevGUIDString() - << ".case"; - - if (m_events.empty() && m_tentativeEvents.empty()) { - unlink(saveFile.str().c_str()); - return; - } - - int fd(open(saveFile.str().c_str(), O_CREAT|O_TRUNC|O_WRONLY, 0644)); - if (fd == -1) { - syslog(LOG_ERR, "CaseFile::Serialize: Unable to open %s.\n", - saveFile.str().c_str()); - return; - } - SerializeEvList(m_events, fd); - SerializeEvList(m_tentativeEvents, fd, "tentative "); - close(fd); -} - -/* - * XXX: This method assumes that events may not contain embedded newlines. If - * ever events can contain embedded newlines, then CaseFile must switch - * serialization formats - */ -void -CaseFile::DeSerialize(ifstream &caseStream) -{ - string evString; - const EventFactory &factory(ZfsDaemon::Get().GetFactory()); - - caseStream >> std::noskipws >> std::ws; - while (caseStream.good()) { - /* - * Outline: - * read the beginning of a line and check it for - * "tentative". If found, discard "tentative". - * Create a new event - * continue - */ - EventList* destEvents; - const string tentFlag("tentative "); - string line; - std::stringbuf lineBuf; - - caseStream.get(lineBuf); - caseStream.ignore(); /*discard the newline character*/ - line = lineBuf.str(); - if (line.compare(0, tentFlag.size(), tentFlag) == 0) { - /* Discard "tentative" */ - line.erase(0, tentFlag.size()); - destEvents = &m_tentativeEvents; - } else { - destEvents = &m_events; - } - Event *event(Event::CreateEvent(factory, line)); - if (event != NULL) { - destEvents->push_back(event); - RegisterCallout(*event); - } - } -} - -void -CaseFile::Close() -{ - /* - * This case is no longer relevant. Clean up our - * serialization file, and delete the case. - */ - syslog(LOG_INFO, "CaseFile(%s,%s) closed - State %s\n", - PoolGUIDString().c_str(), VdevGUIDString().c_str(), - zpool_state_to_name(VdevState(), VDEV_AUX_NONE)); - - /* - * Serialization of a Case with no event data, clears the - * Serialization data for that event. - */ - PurgeEvents(); - Serialize(); - - delete this; -} - -void -CaseFile::OnGracePeriodEnded() -{ - bool should_fault, should_degrade; - ZpoolList zpl(ZpoolList::ZpoolByGUID, &m_poolGUID); - zpool_handle_t *zhp(zpl.empty() ? NULL : zpl.front()); - - m_events.splice(m_events.begin(), m_tentativeEvents); - should_fault = ShouldFault(); - should_degrade = ShouldDegrade(); - - if (should_fault || should_degrade) { - if (zhp == NULL - || (VdevIterator(zhp).Find(m_vdevGUID)) == NULL) { - /* - * Either the pool no longer exists - * or this vdev is no longer a member of - * the pool. - */ - Close(); - return; - } - - } - - /* A fault condition has priority over a degrade condition */ - if (ShouldFault()) { - /* Fault the vdev and close the case. */ - if (zpool_vdev_fault(zhp, (uint64_t)m_vdevGUID, - VDEV_AUX_ERR_EXCEEDED) == 0) { - syslog(LOG_INFO, "Faulting vdev(%s/%s)", - PoolGUIDString().c_str(), - VdevGUIDString().c_str()); - Close(); - return; - } - else { - syslog(LOG_ERR, "Fault vdev(%s/%s): %s: %s\n", - PoolGUIDString().c_str(), - VdevGUIDString().c_str(), - libzfs_error_action(g_zfsHandle), - libzfs_error_description(g_zfsHandle)); - } - } - else if (ShouldDegrade()) { - /* Degrade the vdev and close the case. */ - if (zpool_vdev_degrade(zhp, (uint64_t)m_vdevGUID, - VDEV_AUX_ERR_EXCEEDED) == 0) { - syslog(LOG_INFO, "Degrading vdev(%s/%s)", - PoolGUIDString().c_str(), - VdevGUIDString().c_str()); - Close(); - return; - } - else { - syslog(LOG_ERR, "Degrade vdev(%s/%s): %s: %s\n", - PoolGUIDString().c_str(), - VdevGUIDString().c_str(), - libzfs_error_action(g_zfsHandle), - libzfs_error_description(g_zfsHandle)); - } - } - Serialize(); -} - -Vdev -CaseFile::BeingReplacedBy(zpool_handle_t *zhp) { - Vdev vd(zhp, CaseVdev(zhp)); - std::list children; - std::list::iterator children_it; - - Vdev parent(vd.Parent()); - Vdev replacing(NonexistentVdev); - - /* - * To determine whether we are being replaced by another spare that - * is still working, then make sure that it is currently spared and - * that the spare is either resilvering or healthy. If any of these - * conditions fail, then we are not being replaced by a spare. - * - * If the spare is healthy, then the case file should be closed very - * soon after this check. - */ - if (parent.DoesNotExist() - || parent.Name(zhp, /*verbose*/false) != "spare") - return (NonexistentVdev); - - children = parent.Children(); - children_it = children.begin(); - for (;children_it != children.end(); children_it++) { - Vdev child = *children_it; - - /* Skip our vdev. */ - if (child.GUID() == VdevGUID()) - continue; - /* - * Accept the first child that doesn't match our GUID, or - * any resilvering/healthy device if one exists. - */ - if (replacing.DoesNotExist() || child.IsResilvering() - || child.State() == VDEV_STATE_HEALTHY) - replacing = child; - } - - return (replacing); -} - -bool -CaseFile::Replace(const char* vdev_type, const char* path, bool isspare) { - nvlist_t *nvroot, *newvd; - const char *poolname; - string oldstr(VdevGUIDString()); - bool retval = true; - - /* Figure out what pool we're working on */ - ZpoolList zpl(ZpoolList::ZpoolByGUID, &m_poolGUID); - zpool_handle_t *zhp(zpl.empty() ? NULL : zpl.front()); - if (zhp == NULL) { - syslog(LOG_ERR, "CaseFile::Replace: could not find pool for " - "pool_guid %"PRIu64".", (uint64_t)m_poolGUID); - return (false); - } - poolname = zpool_get_name(zhp); - Vdev vd(zhp, CaseVdev(zhp)); - Vdev replaced(BeingReplacedBy(zhp)); - - if (isspare && !vd.IsSpare() && !replaced.DoesNotExist()) { - /* If we are already being replaced by a working spare, pass. */ - if (replaced.IsResilvering() - || replaced.State() == VDEV_STATE_HEALTHY) { - syslog(LOG_INFO, "CaseFile::Replace(%s->%s): already " - "replaced", VdevGUIDString().c_str(), path); - return (/*consumed*/false); - } - /* - * If we have already been replaced by a spare, but that spare - * is broken, we must spare the spare, not the original device. - */ - oldstr = replaced.GUIDString(); - syslog(LOG_INFO, "CaseFile::Replace(%s->%s): sparing " - "broken spare %s instead", VdevGUIDString().c_str(), - path, oldstr.c_str()); - } - - /* - * Build a root vdev/leaf vdev configuration suitable for - * zpool_vdev_attach. Only enough data for the kernel to find - * the device (i.e. type and disk device node path) are needed. - */ - nvroot = NULL; - newvd = NULL; - - if (nvlist_alloc(&nvroot, NV_UNIQUE_NAME, 0) != 0 - || nvlist_alloc(&newvd, NV_UNIQUE_NAME, 0) != 0) { - syslog(LOG_ERR, "Replace vdev(%s/%s): Unable to allocate " - "configuration data.", poolname, oldstr.c_str()); - if (nvroot != NULL) - nvlist_free(nvroot); - return (false); - } - if (nvlist_add_string(newvd, ZPOOL_CONFIG_TYPE, vdev_type) != 0 - || nvlist_add_string(newvd, ZPOOL_CONFIG_PATH, path) != 0 - || nvlist_add_string(nvroot, ZPOOL_CONFIG_TYPE, VDEV_TYPE_ROOT) != 0 - || nvlist_add_nvlist_array(nvroot, ZPOOL_CONFIG_CHILDREN, - &newvd, 1) != 0) { - syslog(LOG_ERR, "Replace vdev(%s/%s): Unable to initialize " - "configuration data.", poolname, oldstr.c_str()); - nvlist_free(newvd); - nvlist_free(nvroot); - return (true); - } - - /* Data was copied when added to the root vdev. */ - nvlist_free(newvd); - - retval = (zpool_vdev_attach(zhp, oldstr.c_str(), path, nvroot, - /*replace*/B_TRUE) == 0); - if (retval) - syslog(LOG_INFO, "Replacing vdev(%s/%s) with %s\n", - poolname, oldstr.c_str(), path); - else - syslog(LOG_ERR, "Replace vdev(%s/%s): %s: %s\n", - poolname, oldstr.c_str(), libzfs_error_action(g_zfsHandle), - libzfs_error_description(g_zfsHandle)); - nvlist_free(nvroot); - - return (retval); -} - -/* Does the argument event refer to a checksum error? */ -static bool -IsChecksumEvent(const Event* const event) -{ - return ("ereport.fs.zfs.checksum" == event->Value("type")); -} - -/* Does the argument event refer to an IO error? */ -static bool -IsIOEvent(const Event* const event) -{ - return ("ereport.fs.zfs.io" == event->Value("type")); -} - -bool -CaseFile::ShouldDegrade() const -{ - return (std::count_if(m_events.begin(), m_events.end(), - IsChecksumEvent) > ZFS_DEGRADE_IO_COUNT); -} - -bool -CaseFile::ShouldFault() const -{ - return (std::count_if(m_events.begin(), m_events.end(), - IsIOEvent) > ZFS_DEGRADE_IO_COUNT); -} - -nvlist_t * -CaseFile::CaseVdev(zpool_handle_t *zhp) const -{ - return (VdevIterator(zhp).Find(VdevGUID())); -} Index: projects/zfsd/head/cddl/sbin/zfsd/zfsd.cc =================================================================== --- projects/zfsd/head/cddl/sbin/zfsd/zfsd.cc (revision 300334) +++ projects/zfsd/head/cddl/sbin/zfsd/zfsd.cc (nonexistent) @@ -1,448 +0,0 @@ -/*- - * Copyright (c) 2011, 2012, 2013 Spectra Logic Corporation - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions, and the following disclaimer, - * without modification. - * 2. Redistributions in binary form must reproduce at minimum a disclaimer - * substantially similar to the "NO WARRANTY" disclaimer below - * ("Disclaimer") and any redistribution must be conditioned upon - * including a substantially similar Disclaimer requirement for further - * binary redistribution. - * - * NO WARRANTY - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING - * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGES. - * - * Authors: Justin T. Gibbs (Spectra Logic Corporation) - */ - -/** - * \file zfsd.cc - * - * The ZFS daemon consumes kernel devdctl(4) event data via devd(8)'s - * unix domain socket in order to react to system changes that impact - * the function of ZFS storage pools. The goal of this daemon is to - * provide similar functionality to the Solaris ZFS Diagnostic Engine - * (zfs-diagnosis), the Solaris ZFS fault handler (zfs-retire), and - * the Solaris ZFS vdev insertion agent (zfs-mod sysevent handler). - */ - -#include -#include -#include - -#include -#include -#include -#include -#include - -#include - -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "callout.h" -#include "vdev_iterator.h" -#include "zfsd_event.h" -#include "case_file.h" -#include "vdev.h" -#include "vdev_iterator.h" -#include "zfsd.h" -#include "zfsd_exception.h" -#include "zpool_list.h" - -__FBSDID("$FreeBSD$"); - -/*================================== Macros ==================================*/ -#define NUM_ELEMENTS(x) (sizeof(x) / sizeof(*x)) - -/*============================ Namespace Control =============================*/ -using DevdCtl::Event; -using DevdCtl::EventFactory; -using DevdCtl::EventList; - -/*================================ Global Data ===============================*/ -int g_debug = 0; -libzfs_handle_t *g_zfsHandle; - -/*--------------------------------- ZfsDaemon --------------------------------*/ -//- ZfsDaemon Static Private Data ---------------------------------------------- -ZfsDaemon *ZfsDaemon::s_theZfsDaemon; -bool ZfsDaemon::s_logCaseFiles; -bool ZfsDaemon::s_terminateEventLoop; -char ZfsDaemon::s_pidFilePath[] = "/var/run/zfsd.pid"; -pidfh *ZfsDaemon::s_pidFH; -int ZfsDaemon::s_signalPipeFD[2]; -bool ZfsDaemon::s_systemRescanRequested(false); -EventFactory::Record ZfsDaemon::s_registryEntries[] = -{ - { Event::NOTIFY, "DEVFS", &DevfsEvent::Builder }, - { Event::NOTIFY, "GEOM", &GeomEvent::Builder }, - { Event::NOTIFY, "ZFS", &ZfsEvent::Builder } -}; - -//- ZfsDaemon Static Public Methods -------------------------------------------- -ZfsDaemon & -ZfsDaemon::Get() -{ - return (*s_theZfsDaemon); -} - -void -ZfsDaemon::WakeEventLoop() -{ - write(s_signalPipeFD[1], "+", 1); -} - -void -ZfsDaemon::RequestSystemRescan() -{ - s_systemRescanRequested = true; - ZfsDaemon::WakeEventLoop(); -} - -void -ZfsDaemon::Run() -{ - ZfsDaemon daemon; - - while (s_terminateEventLoop == false) { - - try { - daemon.DisconnectFromDevd(); - - if (daemon.ConnectToDevd() == false) { - sleep(30); - continue; - } - - daemon.DetectMissedEvents(); - - daemon.EventLoop(); - - } catch (const DevdCtl::Exception &exp) { - exp.Log(); - } - } - - daemon.DisconnectFromDevd(); -} - -//- ZfsDaemon Private Methods -------------------------------------------------- -ZfsDaemon::ZfsDaemon() - : Consumer(/*defBuilder*/NULL, s_registryEntries, - NUM_ELEMENTS(s_registryEntries)) -{ - if (s_theZfsDaemon != NULL) - errx(1, "Multiple ZfsDaemon instances created. Exiting"); - - s_theZfsDaemon = this; - - if (pipe(s_signalPipeFD) != 0) - errx(1, "Unable to allocate signal pipe. Exiting"); - - if (fcntl(s_signalPipeFD[0], F_SETFL, O_NONBLOCK) == -1) - errx(1, "Unable to set pipe as non-blocking. Exiting"); - - if (fcntl(s_signalPipeFD[1], F_SETFL, O_NONBLOCK) == -1) - errx(1, "Unable to set pipe as non-blocking. Exiting"); - - signal(SIGHUP, ZfsDaemon::RescanSignalHandler); - signal(SIGINFO, ZfsDaemon::InfoSignalHandler); - signal(SIGINT, ZfsDaemon::QuitSignalHandler); - signal(SIGTERM, ZfsDaemon::QuitSignalHandler); - signal(SIGUSR1, ZfsDaemon::RescanSignalHandler); - - g_zfsHandle = libzfs_init(); - if (g_zfsHandle == NULL) - errx(1, "Unable to initialize ZFS library. Exiting"); - - Callout::Init(); - InitializeSyslog(); - OpenPIDFile(); - - if (g_debug == 0) - daemon(0, 0); - - UpdatePIDFile(); -} - -ZfsDaemon::~ZfsDaemon() -{ - PurgeCaseFiles(); - ClosePIDFile(); -} - -void -ZfsDaemon::PurgeCaseFiles() -{ - CaseFile::PurgeAll(); -} - -bool -ZfsDaemon::VdevAddCaseFile(Vdev &vdev, void *cbArg) -{ - if (vdev.State() != VDEV_STATE_HEALTHY) - CaseFile::Create(vdev); - - return (/*break early*/false); -} - -void -ZfsDaemon::BuildCaseFiles() -{ - ZpoolList zpl; - ZpoolList::iterator pool; - - /* Add CaseFiles for vdevs with issues. */ - for (pool = zpl.begin(); pool != zpl.end(); pool++) - VdevIterator(*pool).Each(VdevAddCaseFile, NULL); - - /* De-serialize any saved cases. */ - CaseFile::DeSerialize(); - - /* Simulate config_sync events to force CaseFile reevaluation */ - for (pool = zpl.begin(); pool != zpl.end(); pool++) { - char evString[160]; - Event *event; - nvlist_t *config; - uint64_t poolGUID; - const char *poolname; - - poolname = zpool_get_name(*pool); - config = zpool_get_config(*pool, NULL); - if (config == NULL) { - syslog(LOG_ERR, "ZFSDaemon::BuildCaseFiles: Could not " - "find pool config for pool %s", poolname); - continue; - } - if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID, - &poolGUID) != 0) { - syslog(LOG_ERR, "ZFSDaemon::BuildCaseFiles: Could not " - "find pool guid for pool %s", poolname); - continue; - } - - - snprintf(evString, 160, "!system=ZFS subsystem=ZFS " - "type=misc.fs.zfs.config_sync sub_type=synthesized " - "pool_name=%s pool_guid=%lu\n", poolname, poolGUID); - event = Event::CreateEvent(GetFactory(), string(evString)); - if (event != NULL) { - event->Process(); - delete event; - } - } -} - -void -ZfsDaemon::RescanSystem() -{ - struct gmesh mesh; - struct gclass *mp; - struct ggeom *gp; - struct gprovider *pp; - int result; - - /* - * The devdctl system doesn't replay events for new consumers - * of the interface. Emit manufactured DEVFS arrival events - * for any devices that already before we started or during - * periods where we've lost our connection to devd. - */ - result = geom_gettree(&mesh); - if (result != 0) { - syslog(LOG_ERR, "ZfsDaemon::RescanSystem: " - "geom_gettree faild with error %d\n", result); - return; - } - - const string evStart("!system=DEVFS subsystem=CDEV type=CREATE " - "sub_type=synthesized cdev="); - LIST_FOREACH(mp, &mesh.lg_class, lg_class) { - LIST_FOREACH(gp, &mp->lg_geom, lg_geom) { - LIST_FOREACH(pp, &gp->lg_provider, lg_provider) { - Event *event; - - string evString(evStart + pp->lg_name + "\n"); - event = Event::CreateEvent(GetFactory(), - evString); - if (event != NULL) { - if (event->Process()) - SaveEvent(*event); - delete event; - } - } - } - } - geom_deletetree(&mesh); -} - -void -ZfsDaemon::DetectMissedEvents() -{ - do { - PurgeCaseFiles(); - - /* - * Discard any events waiting for us. We don't know - * if they still apply to the current state of the - * system. - */ - FlushEvents(); - - BuildCaseFiles(); - - /* - * If the system state has changed durring our - * interrogation, start over. - */ - } while (s_terminateEventLoop == false && EventsPending()); - - RescanSystem(); -} - -void -ZfsDaemon::EventLoop() -{ - while (s_terminateEventLoop == false) { - struct pollfd fds[2]; - int result; - - if (s_logCaseFiles == true) { - EventList::iterator event(m_unconsumedEvents.begin()); - s_logCaseFiles = false; - CaseFile::LogAll(); - while (event != m_unconsumedEvents.end()) - (*event++)->Log(LOG_INFO); - } - - Callout::ExpireCallouts(); - - /* Wait for data. */ - fds[0].fd = m_devdSockFD; - fds[0].events = POLLIN; - fds[0].revents = 0; - fds[1].fd = s_signalPipeFD[0]; - fds[1].events = POLLIN; - fds[1].revents = 0; - result = poll(fds, NUM_ELEMENTS(fds), /*timeout*/INFTIM); - if (result == -1) { - if (errno == EINTR) - continue; - else - err(1, "Polling for devd events failed"); - } else if (result == 0) { - errx(1, "Unexpected result of 0 from poll. Exiting"); - } - - if ((fds[0].revents & POLLIN) != 0) - ProcessEvents(); - - if ((fds[1].revents & POLLIN) != 0) { - static char discardBuf[128]; - - /* - * This pipe exists just to close the signal - * race. Its contents are of no interest to - * us, but we must ensure that future signals - * have space in the pipe to write. - */ - while (read(s_signalPipeFD[0], discardBuf, - sizeof(discardBuf)) > 0) - ; - } - - if (s_systemRescanRequested == true) { - s_systemRescanRequested = false; - syslog(LOG_INFO, "System Rescan request processed."); - RescanSystem(); - } - - if ((fds[0].revents & POLLERR) != 0) { - syslog(LOG_INFO, "POLLERROR detected on devd socket."); - break; - } - - if ((fds[0].revents & POLLHUP) != 0) { - syslog(LOG_INFO, "POLLHUP detected on devd socket."); - break; - } - } -} -//- ZfsDaemon staic Private Methods -------------------------------------------- -void -ZfsDaemon::InfoSignalHandler(int) -{ - s_logCaseFiles = true; - ZfsDaemon::WakeEventLoop(); -} - -void -ZfsDaemon::RescanSignalHandler(int) -{ - RequestSystemRescan(); -} - -void -ZfsDaemon::QuitSignalHandler(int) -{ - s_terminateEventLoop = true; - ZfsDaemon::WakeEventLoop(); -} - -void -ZfsDaemon::OpenPIDFile() -{ - pid_t otherPID; - - s_pidFH = pidfile_open(s_pidFilePath, 0600, &otherPID); - if (s_pidFH == NULL) { - if (errno == EEXIST) - errx(1, "already running as PID %d. Exiting", otherPID); - warn("cannot open PID file"); - } -} - -void -ZfsDaemon::UpdatePIDFile() -{ - if (s_pidFH != NULL) - pidfile_write(s_pidFH); -} - -void -ZfsDaemon::ClosePIDFile() -{ - if (s_pidFH != NULL) - pidfile_close(s_pidFH); -} - -void -ZfsDaemon::InitializeSyslog() -{ - openlog("zfsd", LOG_NDELAY, LOG_DAEMON); -} - Index: projects/zfsd/head/cddl/sbin/zfsd/callout.cc =================================================================== --- projects/zfsd/head/cddl/sbin/zfsd/callout.cc (revision 300334) +++ projects/zfsd/head/cddl/sbin/zfsd/callout.cc (nonexistent) @@ -1,217 +0,0 @@ -/*- - * Copyright (c) 2011 Spectra Logic Corporation - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions, and the following disclaimer, - * without modification. - * 2. Redistributions in binary form must reproduce at minimum a disclaimer - * substantially similar to the "NO WARRANTY" disclaimer below - * ("Disclaimer") and any redistribution must be conditioned upon - * including a substantially similar Disclaimer requirement for further - * binary redistribution. - * - * NO WARRANTY - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING - * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGES. - * - * Authors: Justin T. Gibbs (Spectra Logic Corporation) - */ - -/** - * \file callout.cc - * - * \brief Implementation of the Callout class - multi-client - * timer services built on top of the POSIX interval timer. - */ - -#include - -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "callout.h" -#include "vdev_iterator.h" -#include "zfsd.h" -#include "zfsd_exception.h" - -std::list Callout::s_activeCallouts; -bool Callout::s_alarmFired(false); - -void -Callout::Init() -{ - signal(SIGALRM, Callout::AlarmSignalHandler); -} - -bool -Callout::Stop() -{ - if (!IsPending()) - return (false); - - for (std::list::iterator it(s_activeCallouts.begin()); - it != s_activeCallouts.end(); it++) { - if (*it != this) - continue; - - it = s_activeCallouts.erase(it); - if (it != s_activeCallouts.end()) { - - /* - * Maintain correct interval for the - * callouts that follow the just removed - * entry. - */ - timeradd(&(*it)->m_interval, &m_interval, - &(*it)->m_interval); - } - break; - } - m_pending = false; - return (true); -} - -bool -Callout::Reset(const timeval &interval, CalloutFunc_t *func, void *arg) -{ - bool cancelled(false); - - if (!timerisset(&interval)) - throw ZfsdException("Callout::Reset: interval of 0"); - - cancelled = Stop(); - - m_interval = interval; - m_func = func; - m_arg = arg; - m_pending = true; - - std::list::iterator it(s_activeCallouts.begin()); - for (; it != s_activeCallouts.end(); it++) { - - if (timercmp(&(*it)->m_interval, &m_interval, <=)) { - /* - * Decrease our interval by those that come - * before us. - */ - timersub(&m_interval, &(*it)->m_interval, &m_interval); - } else { - /* - * Account for the time between the newly - * inserted event and those that follow. - */ - timersub(&(*it)->m_interval, &m_interval, - &(*it)->m_interval); - break; - } - } - s_activeCallouts.insert(it, this); - - - if (s_activeCallouts.front() == this) { - itimerval timerval = { {0, 0}, m_interval }; - - setitimer(ITIMER_REAL, &timerval, NULL); - } - - return (cancelled); -} - -void -Callout::AlarmSignalHandler(int) -{ - s_alarmFired = true; - ZfsDaemon::WakeEventLoop(); -} - -void -Callout::ExpireCallouts() -{ - if (!s_alarmFired) - return; - - s_alarmFired = false; - if (s_activeCallouts.empty()) { - /* Callout removal/SIGALRM race was lost. */ - return; - } - - /* - * Expire the first callout (the one we used to set the - * interval timer) as well as any callouts following that - * expire at the same time (have a zero interval from - * the callout before it). - */ - do { - Callout *cur(s_activeCallouts.front()); - s_activeCallouts.pop_front(); - cur->m_pending = false; - cur->m_func(cur->m_arg); - } while (!s_activeCallouts.empty() - && timerisset(&s_activeCallouts.front()->m_interval) == 0); - - if (!s_activeCallouts.empty()) { - Callout *next(s_activeCallouts.front()); - itimerval timerval = { { 0, 0 }, next->m_interval }; - - setitimer(ITIMER_REAL, &timerval, NULL); - } -} - -timeval -Callout::TimeRemaining() const -{ - /* - * Outline: Add the m_interval for each callout in s_activeCallouts - * ahead of this, except for the first callout. Add to that the result - * of getitimer (That's because the first callout stores its original - * interval setting while the timer is ticking). - */ - itimerval timervalToAlarm; - timeval timeToExpiry; - std::list::iterator it; - - if (!IsPending()) { - timeToExpiry.tv_sec = INT_MAX; - timeToExpiry.tv_usec = 999999; /*maximum normalized value*/ - return (timeToExpiry); - } - - timerclear(&timeToExpiry); - getitimer(ITIMER_REAL, &timervalToAlarm); - timeval& timeToAlarm = timervalToAlarm.it_value; - timeradd(&timeToExpiry, &timeToAlarm, &timeToExpiry); - - it =s_activeCallouts.begin(); - it++; /*skip the first callout in the list*/ - for (; it != s_activeCallouts.end(); it++) { - timeradd(&timeToExpiry, &(*it)->m_interval, &timeToExpiry); - if ((*it) == this) - break; - } - return (timeToExpiry); -} Index: projects/zfsd/head/cddl/sbin/zfsd/zfsd.h =================================================================== --- projects/zfsd/head/cddl/sbin/zfsd/zfsd.h (revision 300334) +++ projects/zfsd/head/cddl/sbin/zfsd/zfsd.h (nonexistent) @@ -1,228 +0,0 @@ -/*- - * Copyright (c) 2011 Spectra Logic Corporation - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions, and the following disclaimer, - * without modification. - * 2. Redistributions in binary form must reproduce at minimum a disclaimer - * substantially similar to the "NO WARRANTY" disclaimer below - * ("Disclaimer") and any redistribution must be conditioned upon - * including a substantially similar Disclaimer requirement for further - * binary redistribution. - * - * NO WARRANTY - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING - * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGES. - * - * Authors: Justin T. Gibbs (Spectra Logic Corporation) - * - * $FreeBSD$ - */ - -/** - * \file zfsd.h - * - * Class definitions and supporting data strutures for the ZFS fault - * management daemon. - * - * Header requirements: - * - * #include - * - * #include - * - * #include - * #include - * #include - * - * #include - * #include - * #include - * #include - * - * #include "vdev_iterator.h" - */ -#ifndef _ZFSD_H_ -#define _ZFSD_H_ - -/*=========================== Forward Declarations ===========================*/ -struct pidfh; - -struct zpool_handle; -typedef struct zpool_handle zpool_handle_t; - -struct zfs_handle; -typedef struct libzfs_handle libzfs_handle_t; - -struct nvlist; -typedef struct nvlist nvlist_t; - -typedef int LeafIterFunc(zpool_handle_t *, nvlist_t *, void *); - -/*================================ Global Data ===============================*/ -extern int g_debug; -extern libzfs_handle_t *g_zfsHandle; - -/*============================= Class Definitions ============================*/ -/*--------------------------------- ZfsDaemon --------------------------------*/ -/** - * Static singleton orchestrating the operations of the ZFS daemon program. - */ -class ZfsDaemon : public DevdCtl::Consumer -{ -public: - /** Return the ZfsDaemon singleton. */ - static ZfsDaemon &Get(); - - /** - * Used by signal handlers to ensure, in a race free way, that - * the event loop will perform at least one more full loop - * before sleeping again. - */ - static void WakeEventLoop(); - - /** - * Schedules a rescan of devices in the system for potential - * candidates to replace a missing vdev. The scan is performed - * during the next run of the event loop. - */ - static void RequestSystemRescan(); - - /** Daemonize and perform all functions of the ZFS daemon. */ - static void Run(); - -private: - ZfsDaemon(); - ~ZfsDaemon(); - - static VdevCallback_t VdevAddCaseFile; - - /** Purge our cache of outstanding ZFS issues in the system. */ - void PurgeCaseFiles(); - - /** Build a cache of outstanding ZFS issues in the system. */ - void BuildCaseFiles(); - - /** - * Iterate over all known issues and attempt to solve them - * given resources currently available in the system. - */ - void RescanSystem(); - - /** - * Interrogate the system looking for previously unknown - * faults that occurred either before ZFSD was started, - * or during a period of lost communication with Devd. - */ - void DetectMissedEvents(); - - /** - * Wait for and process event source activity. - */ - void EventLoop(); - - /** - * Signal handler for which our response is to - * log the current state of the daemon. - * - * \param sigNum The signal caught. - */ - static void InfoSignalHandler(int sigNum); - - /** - * Signal handler for which our response is to - * request a case rescan. - * - * \param sigNum The signal caught. - */ - static void RescanSignalHandler(int sigNum); - - /** - * Signal handler for which our response is to - * gracefully terminate. - * - * \param sigNum The signal caught. - */ - static void QuitSignalHandler(int sigNum); - - /** - * Open and lock our PID file. - */ - static void OpenPIDFile(); - - /** - * Update our PID file with our PID. - */ - static void UpdatePIDFile(); - - /** - * Close and release the lock on our PID file. - */ - static void ClosePIDFile(); - - /** - * Perform syslog configuraiton. - */ - static void InitializeSyslog(); - - static ZfsDaemon *s_theZfsDaemon; - - /** - * Set to true when our program is signaled to - * gracefully exit. - */ - static bool s_logCaseFiles; - - /** - * Set to true when our program is signaled to - * gracefully exit. - */ - static bool s_terminateEventLoop; - - /** - * The canonical path and file name of zfsd's PID file. - */ - static char s_pidFilePath[]; - - /** - * Control structure for PIDFILE(3) API. - */ - static pidfh *s_pidFH; - - /** - * Pipe file descriptors used to close races with our - * signal handlers. - */ - static int s_signalPipeFD[2]; - - /** - * Flag controlling a rescan from ZFSD's event loop of all - * GEOM providers in the system to find candidates for solving - * cases. - */ - static bool s_systemRescanRequested; - - /** - * Flag controlling whether events can be queued. This boolean - * is set during event replay to ensure that events for pools or - * devices no longer in the system are not retained forever. - */ - static bool s_consumingEvents; - - static DevdCtl::EventFactory::Record s_registryEntries[]; -}; - -#endif /* _ZFSD_H_ */ Index: projects/zfsd/head/cddl/sbin/zfsd/callout.h =================================================================== --- projects/zfsd/head/cddl/sbin/zfsd/callout.h (revision 300334) +++ projects/zfsd/head/cddl/sbin/zfsd/callout.h (nonexistent) @@ -1,183 +0,0 @@ -/*- - * Copyright (c) 2011 Spectra Logic Corporation - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions, and the following disclaimer, - * without modification. - * 2. Redistributions in binary form must reproduce at minimum a disclaimer - * substantially similar to the "NO WARRANTY" disclaimer below - * ("Disclaimer") and any redistribution must be conditioned upon - * including a substantially similar Disclaimer requirement for further - * binary redistribution. - * - * NO WARRANTY - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING - * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGES. - * - * Authors: Justin T. Gibbs (Spectra Logic Corporation) - */ - -/** - * \file callout.h - * - * \brief Interface for timer based callback services. - * - * Header requirements: - * - * #include - * - * #include - */ - -#ifndef _CALLOUT_H_ -#define _CALLOUT_H_ - -/** - * \brief Type of the function callback from a Callout. - */ -typedef void CalloutFunc_t(void *); - -/** - * \brief Interface to a schedulable one-shot timer with the granlarity - * of the system clock (see setitimer(2)). - * - * Determination of callback expiration is triggered by the SIGALRM - * signal. Callout callbacks are always delivered from Zfsd's event - * processing loop. - * - * Periodic actions can be triggered via the Callout mechanisms by - * resetting the Callout from within its callback. - */ -class Callout -{ -public: - - /** - * Initialize the Callout subsystem. - */ - static void Init(); - - /** - * Function called (via SIGALRM) when our interval - * timer expires. - */ - static void AlarmSignalHandler(int); - - /** - * Execute callbacks for all callouts that have the same - * expiration time as the first callout in the list. - */ - static void ExpireCallouts(); - - /** Constructor. */ - Callout(); - - /** - * Returns true if callout has not been stopped, - * or deactivated since the last time the callout was - * reset. - */ - bool IsActive() const; - - /** - * Returns true if callout is still waiting to expire. - */ - bool IsPending() const; - - /** - * Disestablish a callout. - */ - bool Stop(); - - /** - * \brief Establish or change a timeout. - * - * \param interval Timeval indicating the time which must elapse - * before this callout fires. - * \param func Pointer to the callback funtion - * \param arg Argument pointer to pass to callback function - * - * \return Cancelation status. - * true: The previous callback was pending and therfore - * was cancelled. - * false: The callout was not pending at the time of this - * reset request. - * In all cases, a new callout is established. - */ - bool Reset(const timeval &interval, CalloutFunc_t *func, void *arg); - - /** - * \brief Calculate the remaining time until this Callout's timer - * expires. - * - * The return value will be slightly greater than the actual time to - * expiry. - * - * If the callout is not pending, returns INT_MAX. - */ - timeval TimeRemaining() const; - -private: - /** - * All active callouts sorted by expiration time. The callout - * with the nearest expiration time is at the head of the list. - */ - static std::list s_activeCallouts; - - /** - * The interval timer has expired. This variable is set from - * signal handler context and tested from Zfsd::EventLoop() - * context via ExpireCallouts(). - */ - static bool s_alarmFired; - - /** - * Time, realtive to others in the active list, until - * this callout is fired. - */ - timeval m_interval; - - /** Callback function argument. */ - void *m_arg; - - /** - * The callback function associated with this timer - * entry. - */ - CalloutFunc_t *m_func; - - /** State of this callout. */ - bool m_pending; -}; - -//- Callout public const methods ---------------------------------------------- -inline bool -Callout::IsPending() const -{ - return (m_pending); -} - -//- Callout public methods ---------------------------------------------------- -inline -Callout::Callout() - : m_arg(0), - m_func(NULL), - m_pending(false) -{ - timerclear(&m_interval); -} - -#endif /* CALLOUT_H_ */ Index: projects/zfsd/head/cddl/sbin/zfsd/case_file.h =================================================================== --- projects/zfsd/head/cddl/sbin/zfsd/case_file.h (revision 300334) +++ projects/zfsd/head/cddl/sbin/zfsd/case_file.h (nonexistent) @@ -1,426 +0,0 @@ -/*- - * Copyright (c) 2011 Spectra Logic Corporation - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions, and the following disclaimer, - * without modification. - * 2. Redistributions in binary form must reproduce at minimum a disclaimer - * substantially similar to the "NO WARRANTY" disclaimer below - * ("Disclaimer") and any redistribution must be conditioned upon - * including a substantially similar Disclaimer requirement for further - * binary redistribution. - * - * NO WARRANTY - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING - * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGES. - * - * Authors: Justin T. Gibbs (Spectra Logic Corporation) - * - * $FreeBSD$ - */ - -/** - * \file case_file.h - * - * CaseFile objects aggregate vdev faults that may require ZFSD action - * in order to maintain the health of a ZFS pool. - * - * Header requirements: - * - * #include - * - * #include "callout.h" - * #include "zfsd_event.h" - */ -#ifndef _CASE_FILE_H_ -#define _CASE_FILE_H_ - -/*=========================== Forward Declarations ===========================*/ -class CaseFile; -class Vdev; - -/*============================= Class Definitions ============================*/ -/*------------------------------- CaseFileList -------------------------------*/ -/** - * CaseFileList is a specialization of the standard list STL container. - */ -typedef std::list< CaseFile *> CaseFileList; - -/*--------------------------------- CaseFile ---------------------------------*/ -/** - * A CaseFile object is instantiated anytime a vdev for an active pool - * experiences an I/O error, is faulted by ZFS, or is determined to be - * missing/removed. - * - * A vdev may have at most one CaseFile. - * - * CaseFiles are retired when a vdev leaves an active pool configuration - * or an action is taken to resolve the issues recorded in the CaseFile. - * - * Logging a case against a vdev does not imply that an immediate action - * to resolve a fault is required or even desired. For example, a CaseFile - * must accumulate a number of I/O errors in order to flag a device as - * degraded. - * - * Vdev I/O errors are not recorded in ZFS label inforamation. For this - * reasons, CaseFile%%s with accumulated I/O error events are serialized - * to the file system so that they survive across boots. Currently all - * other fault types can be reconstructed from ZFS label information, so - * CaseFile%%s for missing, faulted, or degradded members are just recreated - * at ZFSD startup instead of being deserialized from the file system. - */ -class CaseFile -{ -public: - /** - * \brief Find a CaseFile object by a vdev's pool/vdev GUID tuple. - * - * \param poolGUID Pool GUID for the vdev of the CaseFile to find. - * \param vdevGUID Vdev GUID for the vdev of the CaseFile to find. - * - * \return If found, a pointer to a valid CaseFile object. - * Otherwise NULL. - */ - static CaseFile *Find(DevdCtl::Guid poolGUID, DevdCtl::Guid vdevGUID); - - /** - * \brief Find a CaseFile object by a vdev's current/last known - * physical path. - * - * \param physPath Physical path of the vdev of the CaseFile to find. - * - * \return If found, a pointer to a valid CaseFile object. - * Otherwise NULL. - */ - static CaseFile *Find(const string &physPath); - - /** - * \brief ReEvaluate all open cases whose pool guid matches the argument - * - * \param poolGUID Only reevaluate cases for this pool - * \param event Try to consume this event with the casefile - */ - static void ReEvaluateByGuid(DevdCtl::Guid poolGUID, - const ZfsEvent &event); - - /** - * \brief Create or return an existing active CaseFile for the - * specified vdev. - * - * \param vdev The vdev object for which to find/create a CaseFile. - * - * \return A reference to a valid CaseFile object. - */ - static CaseFile &Create(Vdev &vdev); - - /** - * \brief Deserialize all serialized CaseFile objects found in - * the file system. - */ - static void DeSerialize(); - - /** - * \brief Emit syslog data on all active CaseFile%%s in the system. - */ - static void LogAll(); - - /** - * \brief Destroy the in-core cache of CaseFile data. - * - * This routine does not disturb the on disk, serialized, CaseFile - * data. - */ - static void PurgeAll(); - - DevdCtl::Guid PoolGUID() const; - DevdCtl::Guid VdevGUID() const; - vdev_state VdevState() const; - const string &PoolGUIDString() const; - const string &VdevGUIDString() const; - const string &PhysicalPath() const; - - /** - * \brief Attempt to resolve this CaseFile using the disk - * resource at the given device/physical path/vdev object - * tuple. - * - * \param devPath The devfs path for the disk resource. - * \param physPath The physical path information reported by - * the disk resource. - * \param vdev If the disk contains ZFS label information, - * a pointer to the disk label's vdev object - * data. Otherwise NULL. - * - * \return True if this event was consumed by this CaseFile. - */ - bool ReEvaluate(const string &devPath, const string &physPath, - Vdev *vdev); - - /** - * \brief Update this CaseFile in light of the provided ZfsEvent. - * - * Must be virtual so it can be overridden in the unit tests - * - * \param event The ZfsEvent to evaluate. - * - * \return True if this event was consumed by this CaseFile. - */ - virtual bool ReEvaluate(const ZfsEvent &event); - - /** - * \brief Register an itimer callout for the given event, if necessary - */ - virtual void RegisterCallout(const DevdCtl::Event &event); - - /** - * \brief Close a case if it is no longer relevant. - * - * This method deals with cases tracking soft errors. Soft errors - * will be discarded should a remove event occur within a short period - * of the soft errors being reported. We also discard the events - * if the vdev is marked degraded or failed. - * - * \return True if the case is closed. False otherwise. - */ - bool CloseIfSolved(); - - /** - * \brief Emit data about this CaseFile via syslog(3). - */ - void Log(); - - /** - * \brief Whether we should degrade this vdev - */ - bool ShouldDegrade() const; - - /** - * \brief Whether we should fault this vdev - */ - bool ShouldFault() const; - -protected: - enum { - /** - * The number of soft errors on a vdev required - * to transition a vdev from healthy to degrated - * status. - */ - ZFS_DEGRADE_IO_COUNT = 50 - }; - - static CalloutFunc_t OnGracePeriodEnded; - - /** - * \brief scandir(3) filter function used to find files containing - * serialized CaseFile data. - * - * \param dirEntry Directory entry for the file to filter. - * - * \return Non-zero for a file to include in the selection, - * otherwise 0. - */ - static int DeSerializeSelector(const struct dirent *dirEntry); - - /** - * \brief Given the name of a file containing serialized events from a - * CaseFile object, create/update an in-core CaseFile object - * representing the serialized data. - * - * \param fileName The name of a file containing serialized events - * from a CaseFile object. - */ - static void DeSerializeFile(const char *fileName); - - /** Constructor. */ - CaseFile(const Vdev &vdev); - - /** - * Destructor. - * Must be virtual so it can be subclassed in the unit tests - */ - virtual ~CaseFile(); - - /** - * \brief Reload state for the vdev associated with this CaseFile. - * - * \return True if the refresh was successful. False if the system - * has no record of the pool or vdev for this CaseFile. - */ - virtual bool RefreshVdevState(); - - /** - * \brief Free all events in the m_events list. - */ - void PurgeEvents(); - - /** - * \brief Free all events in the m_tentativeEvents list. - */ - void PurgeTentativeEvents(); - - /** - * \brief Commit to file system storage. - */ - void Serialize(); - - /** - * \brief Retrieve event data from a serialization stream. - * - * \param caseStream The serializtion stream to parse. - */ - void DeSerialize(std::ifstream &caseStream); - - /** - * \brief Serializes the supplied event list and writes it to fd - * - * \param prefix If not NULL, this prefix will be prepended to - * every event in the file. - */ - void SerializeEvList(const DevdCtl::EventList events, int fd, - const char* prefix=NULL) const; - - /** - * \brief Unconditionally close a CaseFile. - */ - virtual void Close(); - - /** - * \brief Callout callback invoked when the remove timer grace - * period expires. - * - * If no remove events are received prior to the grace period - * firing, then any tentative events are promoted and counted - * against the health of the vdev. - */ - void OnGracePeriodEnded(); - - /** - * \brief Attempt to activate a spare on this case's pool. - * - * Call this whenever a pool becomes degraded. It will look for any - * spare devices and activate one to replace the casefile's vdev. It - * will _not_ close the casefile; that should only happen when the - * missing drive is replaced or the user promotes the spare. - * - * \return True if a spare was activated - */ - bool ActivateSpare(); - - /** - * \brief replace a pool's vdev with another - * - * \param vdev_type The type of the new vdev. Usually either - * VDEV_TYPE_DISK or VDEV_TYPE_FILE - * \param path The file system path to the new vdev - * \param isspare Whether the new vdev is a spare - * - * \return true iff the replacement was successful - */ - bool Replace(const char* vdev_type, const char* path, bool isspare); - - /** - * \brief Which vdev, if any, is replacing ours. - * - * \param zhp Pool handle state from the caller context - * - * \return the vdev that is currently replacing ours, - * or NonexistentVdev if there isn't one. - */ - Vdev BeingReplacedBy(zpool_handle_t *zhp); - - /** - * \brief All CaseFiles being tracked by ZFSD. - */ - static CaseFileList s_activeCases; - - /** - * \brief The file system path to serialized CaseFile data. - */ - static const string s_caseFilePath; - - /** - * \brief The time ZFSD waits before promoting a tentative event - * into a permanent event. - */ - static const timeval s_removeGracePeriod; - - /** - * \brief A list of soft error events counted against the health of - * a vdev. - */ - DevdCtl::EventList m_events; - - /** - * \brief A list of soft error events waiting for a grace period - * expiration before being counted against the health of - * a vdev. - */ - DevdCtl::EventList m_tentativeEvents; - - DevdCtl::Guid m_poolGUID; - DevdCtl::Guid m_vdevGUID; - vdev_state m_vdevState; - string m_poolGUIDString; - string m_vdevGUIDString; - string m_vdevPhysPath; - - /** - * \brief Callout activated when a grace period - */ - Callout m_tentativeTimer; - -private: - nvlist_t *CaseVdev(zpool_handle_t *zhp) const; -}; - -inline DevdCtl::Guid -CaseFile::PoolGUID() const -{ - return (m_poolGUID); -} - -inline DevdCtl::Guid -CaseFile::VdevGUID() const -{ - return (m_vdevGUID); -} - -inline vdev_state -CaseFile::VdevState() const -{ - return (m_vdevState); -} - -inline const string & -CaseFile::PoolGUIDString() const -{ - return (m_poolGUIDString); -} - -inline const string & -CaseFile::VdevGUIDString() const -{ - return (m_vdevGUIDString); -} - -inline const string & -CaseFile::PhysicalPath() const -{ - return (m_vdevPhysPath); -} - -#endif /* _CASE_FILE_H_ */ Index: projects/zfsd/head/cddl/sbin/zfsd/Makefile.common =================================================================== --- projects/zfsd/head/cddl/sbin/zfsd/Makefile.common (revision 300334) +++ projects/zfsd/head/cddl/sbin/zfsd/Makefile.common (nonexistent) @@ -1,44 +0,0 @@ -# $FreeBSD$ - -SRCS= callout.cc \ - case_file.cc \ - zfsd_event.cc \ - vdev.cc \ - vdev_iterator.cc \ - zfsd.cc \ - zfsd_exception.cc \ - zpool_list.cc \ - zfsd_main.cc - -MAN= - -WARNS?= 3 - -# Ignore warnings about Solaris specific pragmas. -IGNORE_PRAGMA= YES - -INCFLAGS+= -I${SRCDIR}/cddl/contrib/opensolaris/lib/libzpool/common -INCFLAGS+= -I${SRCDIR}/cddl/compat/opensolaris/include -INCFLAGS+= -I${SRCDIR}/cddl/compat/opensolaris/lib/libumem -INCFLAGS+= -I${SRCDIR}/sys/cddl/compat/opensolaris -INCFLAGS+= -I${SRCDIR}/cddl/contrib/opensolaris/head -INCFLAGS+= -I${SRCDIR}/cddl/contrib/opensolaris/lib/libuutil/common -INCFLAGS+= -I${SRCDIR}/cddl/contrib/opensolaris/lib/libumem/common -INCFLAGS+= -I${SRCDIR}/cddl/contrib/opensolaris/lib/libzfs_core/common -INCFLAGS+= -I${SRCDIR}/cddl/contrib/opensolaris/lib/libzfs/common -INCFLAGS+= -I${SRCDIR}/cddl/contrib/opensolaris/lib/libnvpair -INCFLAGS+= -I${SRCDIR}/sys/cddl/contrib/opensolaris/common/zfs -INCFLAGS+= -I${SRCDIR}/sys/cddl/contrib/opensolaris/uts/common -INCFLAGS+= -I${SRCDIR}/sys/cddl/contrib/opensolaris/uts/common/fs/zfs -INCFLAGS+= -I${SRCDIR}/sys/cddl/contrib/opensolaris/uts/common/sys - -CFLAGS= -g -DNEED_SOLARIS_BOOLEAN ${INCFLAGS} - -DPADD= ${LIBDEVDCTL} ${LIBZFS} ${LIBZFS_CORE} ${LIBUTIL} ${LIBGEOM} \ - ${LIBBSDXML} ${LIBSBUF} ${LIBNVPAIR} ${LIBUUTIL} -LIBADD= devdctl zfs zfs_core util geom bsdxml sbuf nvpair uutil - -cscope: - find ${.CURDIR} -type f -a \( -name "*.[ch]" -o -name "*.cc" \) \ - > ${.CURDIR}/cscope.files - cd ${.CURDIR} && cscope -buq ${INCFLAGS} Index: projects/zfsd/head/cddl/sbin/zfsd/vdev_iterator.cc =================================================================== --- projects/zfsd/head/cddl/sbin/zfsd/vdev_iterator.cc (revision 300334) +++ projects/zfsd/head/cddl/sbin/zfsd/vdev_iterator.cc (nonexistent) @@ -1,151 +0,0 @@ -/*- - * Copyright (c) 2011 Spectra Logic Corporation - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions, and the following disclaimer, - * without modification. - * 2. Redistributions in binary form must reproduce at minimum a disclaimer - * substantially similar to the "NO WARRANTY" disclaimer below - * ("Disclaimer") and any redistribution must be conditioned upon - * including a substantially similar Disclaimer requirement for further - * binary redistribution. - * - * NO WARRANTY - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING - * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGES. - * - * Authors: Justin T. Gibbs (Spectra Logic Corporation) - */ - -/** - * \file vdev_iterator.cc - * - * Implementation of the VdevIterator class. - */ -#include -#include - -#include -#include - -#include - -#include -#include - -#include -#include - -#include "vdev.h" -#include "vdev_iterator.h" -#include "zfsd_exception.h" - -/*============================ Namespace Control =============================*/ -using DevdCtl::Guid; - -/*=========================== Class Implementations ==========================*/ -/*------------------------------- VdevIterator -------------------------------*/ -VdevIterator::VdevIterator(zpool_handle_t *pool) - : m_poolConfig(zpool_get_config(pool, NULL)) -{ - Reset(); -} - -VdevIterator::VdevIterator(nvlist_t *poolConfig) - : m_poolConfig(poolConfig) -{ - Reset(); -} - -void -VdevIterator::Reset() -{ - nvlist_t *rootVdev; - int result; - - result = nvlist_lookup_nvlist(m_poolConfig, - ZPOOL_CONFIG_VDEV_TREE, - &rootVdev); - if (result != 0) - throw ZfsdException(m_poolConfig, "Unable to extract " - "ZPOOL_CONFIG_VDEV_TREE from pool."); - m_vdevQueue.assign(1, rootVdev); -} - -nvlist_t * -VdevIterator::Next() -{ - nvlist_t *vdevConfig; - - if (m_vdevQueue.empty()) - return (NULL); - - while (1) { - nvlist_t **vdevChildren; - int result; - u_int numChildren; - - vdevConfig = m_vdevQueue.front(); - m_vdevQueue.pop_front(); - - /* Expand non-leaf vdevs. */ - result = nvlist_lookup_nvlist_array(vdevConfig, - ZPOOL_CONFIG_CHILDREN, - &vdevChildren, &numChildren); - if (result != 0) { - /* leaf vdev */ - break; - } - - /* - * Insert children at the head of the queue to effect a - * depth first traversal of the tree. - */ - m_vdevQueue.insert(m_vdevQueue.begin(), vdevChildren, - vdevChildren + numChildren); - }; - - return (vdevConfig); -} - -void -VdevIterator::Each(VdevCallback_t *callBack, void *callBackArg) -{ - nvlist_t *vdevConfig; - - Reset(); - while ((vdevConfig = Next()) != NULL) { - Vdev vdev(m_poolConfig, vdevConfig); - - if (callBack(vdev, callBackArg)) - break; - } -} - -nvlist_t * -VdevIterator::Find(Guid vdevGUID) -{ - nvlist_t *vdevConfig; - - Reset(); - while ((vdevConfig = Next()) != NULL) { - Vdev vdev(m_poolConfig, vdevConfig); - - if (vdev.GUID() == vdevGUID) - return (vdevConfig); - } - return (NULL); -} Index: projects/zfsd/head/cddl/sbin/zfsd/zfsd_event.h =================================================================== --- projects/zfsd/head/cddl/sbin/zfsd/zfsd_event.h (revision 300334) +++ projects/zfsd/head/cddl/sbin/zfsd/zfsd_event.h (nonexistent) @@ -1,168 +0,0 @@ -/*- - * Copyright (c) 2011 Spectra Logic Corporation - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions, and the following disclaimer, - * without modification. - * 2. Redistributions in binary form must reproduce at minimum a disclaimer - * substantially similar to the "NO WARRANTY" disclaimer below - * ("Disclaimer") and any redistribution must be conditioned upon - * including a substantially similar Disclaimer requirement for further - * binary redistribution. - * - * NO WARRANTY - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING - * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGES. - * - * Authors: Justin T. Gibbs (Spectra Logic Corporation) - * - * $FreeBSD$ - */ - -/** - * \file dev_ctl_event.h - * - * \brief Class hierarchy used to express events received via - * the devdctl API. - * - * Header requirements: - * #include - * #include - * #include - * - * #include - * #include - */ - -#ifndef _ZFSD_EVENT_H_ -#define _ZFSD_EVENT_H_ - -/*============================ Namespace Control =============================*/ -using std::string; - -/*=========================== Forward Declarations ===========================*/ -struct zpool_handle; -typedef struct zpool_handle zpool_handle_t; - -struct nvlist; -typedef struct nvlist nvlist_t; - -/*============================= Class Definitions ============================*/ -/*-------------------------------- DevfsEvent --------------------------------*/ -class DevfsEvent : public DevdCtl::DevfsEvent -{ -public: - /** Specialized DevdCtlEvent object factory for Devfs events. */ - static BuildMethod Builder; - - virtual DevdCtl::Event *DeepCopy() const; - - /** - * Interpret and perform any actions necessary to - * consume the event. - * \return True if this event should be queued for later reevaluation - */ - virtual bool Process() const; - -protected: - /** - * \brief Read and return label information for a device. - * - * \param devFd The device from which to read ZFS label information. - * \param inUse The device is part of an active or potentially - * active configuration. - * \param degraded The device label indicates the vdev is not healthy. - * - * \return If label information is available, an nvlist describing - * the vdev configuraiton found on the device specified by - * devFd. Otherwise NULL. - */ - static nvlist_t *ReadLabel(int devFd, bool &inUse, bool °raded); - - /** - * Attempt to match the ZFS labeled device at devPath with an active - * CaseFile for a missing vdev. If a CaseFile is found, attempt - * to re-integrate the device with its pool. - * - * \param devPath The devfs path to the potential leaf vdev. - * \param physPath The physical path string reported by the device - * at devPath. - * \param devConfig The ZFS label information found on the device - * at devPath. - * - * \return true if the event that caused the online action can - * be considered consumed. - */ - static bool OnlineByLabel(const string &devPath, - const string& physPath, - nvlist_t *devConfig); - - /** DeepCopy Constructor. */ - DevfsEvent(const DevfsEvent &src); - - /** Constructor */ - DevfsEvent(Type, DevdCtl::NVPairMap &, const string &); -}; - -/*--------------------------------- ZfsEvent ---------------------------------*/ -class ZfsEvent : public DevdCtl::ZfsEvent -{ -public: - /** Specialized DevdCtlEvent object factory for ZFS events. */ - static BuildMethod Builder; - - virtual DevdCtl::Event *DeepCopy() const; - - /** - * Interpret and perform any actions necessary to - * consume the event. - * \return True if this event should be queued for later reevaluation - */ - virtual bool Process() const; - -protected: - /** DeepCopy Constructor. */ - ZfsEvent(const ZfsEvent &src); - - /** Constructor */ - ZfsEvent(Type, DevdCtl::NVPairMap &, const string &); - - /** - * Detach any spares that are no longer needed, but were not - * automatically detached by the kernel - */ - virtual void CleanupSpares() const; - virtual void ProcessPoolEvent() const; - static VdevCallback_t TryDetach; -}; - -class GeomEvent : public DevdCtl::GeomEvent -{ -public: - static BuildMethod Builder; - - virtual DevdCtl::Event *DeepCopy() const; - - virtual bool Process() const; - -protected: - /** DeepCopy Constructor. */ - GeomEvent(const GeomEvent &src); - - /** Constructor */ - GeomEvent(Type, DevdCtl::NVPairMap &, const string &); -}; -#endif /*_ZFSD_EVENT_H_ */ Index: projects/zfsd/head/cddl/sbin/zfsd/zfsd_exception.h =================================================================== --- projects/zfsd/head/cddl/sbin/zfsd/zfsd_exception.h (revision 300334) +++ projects/zfsd/head/cddl/sbin/zfsd/zfsd_exception.h (nonexistent) @@ -1,107 +0,0 @@ -/*- - * Copyright (c) 2011 Spectra Logic Corporation - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions, and the following disclaimer, - * without modification. - * 2. Redistributions in binary form must reproduce at minimum a disclaimer - * substantially similar to the "NO WARRANTY" disclaimer below - * ("Disclaimer") and any redistribution must be conditioned upon - * including a substantially similar Disclaimer requirement for further - * binary redistribution. - * - * NO WARRANTY - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING - * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGES. - * - * Authors: Justin T. Gibbs (Spectra Logic Corporation) - */ - -/** - * \file zfsd_exception.h - * - * Definition of the ZfsdException class hierarchy. All exceptions - * explicitly thrown by Zfsd are defined here. - * - * Header requirements: - * #include - * - * #include - */ -#ifndef _ZFSD_EXCEPTION_H_ -#define _ZFSD_EXCEPTION_H_ - -/*=========================== Forward Declarations ===========================*/ -struct zpool_handle; -typedef struct zpool_handle zpool_handle_t; - -struct nvlist; -typedef struct nvlist nvlist_t; - -/*============================= Class Definitions ============================*/ -/*------------------------------- ZfsdException ------------------------------*/ -/** - * \brief Class allowing unified reporting/logging of exceptional events. - */ -class ZfsdException : public DevdCtl::Exception -{ -public: - /** - * \brief ZfsdException constructor allowing arbitrary string - * data to be reported. - * - * \param fmt Printf-like string format specifier. - */ - ZfsdException(const char *fmt, ...); - - /** - * \brief ZfsdException constructor allowing arbitrary string - * data to be reported and associated with the configuration - * data for a ZFS pool. - * - * \param pool Pool handle describing the pool to which this - * exception is associated. - * \param fmt Printf-like string format specifier. - * - * Instantiation with this method is used to report global - * pool errors. - */ - ZfsdException(zpool_handle_t *pool, const char *, ...); - - /** - * \brief ZfsdException constructor allowing arbitrary string - * data to be reported and associated with the configuration - * data for a ZFS pool. - * - * \param poolConfig Pool configuration describing the pool to - * which this exception is associated. - * \param fmt Printf-like string format specifier. - * - * Instantiation with this method is used to report global - * pool errors. - */ - ZfsdException(nvlist_t *poolConfig, const char *, ...); - - /** - * \brief Emit exception data to syslog(3). - */ - virtual void Log() const; -private: - nvlist_t *m_poolConfig; - nvlist_t *m_vdevConfig; -}; - -#endif /* _ZFSD_EXCEPTION_H_ */ Index: projects/zfsd/head/cddl/sbin/zfsd/vdev_iterator.h =================================================================== --- projects/zfsd/head/cddl/sbin/zfsd/vdev_iterator.h (revision 300334) +++ projects/zfsd/head/cddl/sbin/zfsd/vdev_iterator.h (nonexistent) @@ -1,121 +0,0 @@ -/*- - * Copyright (c) 2011 Spectra Logic Corporation - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions, and the following disclaimer, - * without modification. - * 2. Redistributions in binary form must reproduce at minimum a disclaimer - * substantially similar to the "NO WARRANTY" disclaimer below - * ("Disclaimer") and any redistribution must be conditioned upon - * including a substantially similar Disclaimer requirement for further - * binary redistribution. - * - * NO WARRANTY - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING - * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGES. - * - * Authors: Justin T. Gibbs (Spectra Logic Corporation) - */ - -/** - * \file vdev_iterator.h - * - * VdevIterator class definition. - * - * Header requirements: - * - * #include - */ -#ifndef _VDEV_ITERATOR_H_ -#define _VDEV_ITERATOR_H_ - -/*=========================== Forward Declarations ===========================*/ -struct zpool_handle; -typedef struct zpool_handle zpool_handle_t; - -struct nvlist; -typedef struct nvlist nvlist_t; - -class Vdev; - -/*============================= Class Definitions ============================*/ -/*------------------------------- VdevIterator -------------------------------*/ -typedef bool VdevCallback_t(Vdev &vdev, void *cbArg); - -/** - * \brief VdevIterator provides mechanisms for traversing and searching - * the leaf vdevs contained in a ZFS pool configuration. - */ -class VdevIterator -{ -public: - /** - * \brief Instantiate a VdevIterator for the given ZFS pool. - * - * \param pool The ZFS pool to traverse/search. - */ - VdevIterator(zpool_handle_t *pool); - - /** - * \brief Instantiate a VdevIterator for the given ZFS pool. - * - * \param poolConfig The configuration data for the ZFS pool - * to traverse/search. - */ - VdevIterator(nvlist_t *poolConfig); - - /** - * \brief Reset this iterator's cursor so that Next() will - * report the first member of the pool. - */ - void Reset(); - - /** - * \brief Report the leaf vdev at this iterator's cursor and increment - * the cursor to the next leaf pool member. - */ - nvlist_t *Next(); - - /** - * \brief Traverse the entire pool configuration starting its - * first member, returning a vdev object with the given - * vdev GUID if found. - * - * \param vdevGUID The vdev GUID of the vdev object to find. - * - * \return A Vdev object for the matching vdev if found. Otherwise - * NULL. - * - * Upon return, the VdevIterator's cursor points to the vdev just - * past the returned vdev or end() if no matching vdev is found. - */ - nvlist_t *Find(DevdCtl::Guid vdevGUID); - - /** - * \brief Perform the specified operation on each leaf member of - * a pool's vdev membership. - * - * \param cb Callback function to execute for each member. - * \param cbArg Argument to pass to cb. - */ - void Each(VdevCallback_t *cb, void *cbArg); - -private: - nvlist_t *m_poolConfig; - std::list m_vdevQueue; -}; - -#endif /* _VDEV_ITERATOR_H_ */ Index: projects/zfsd/head/cddl/sbin/zfsd/zfsd_event.cc =================================================================== --- projects/zfsd/head/cddl/sbin/zfsd/zfsd_event.cc (revision 300334) +++ projects/zfsd/head/cddl/sbin/zfsd/zfsd_event.cc (nonexistent) @@ -1,530 +0,0 @@ -/*- - * Copyright (c) 2011 Spectra Logic Corporation - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions, and the following disclaimer, - * without modification. - * 2. Redistributions in binary form must reproduce at minimum a disclaimer - * substantially similar to the "NO WARRANTY" disclaimer below - * ("Disclaimer") and any redistribution must be conditioned upon - * including a substantially similar Disclaimer requirement for further - * binary redistribution. - * - * NO WARRANTY - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING - * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGES. - * - * Authors: Justin T. Gibbs (Spectra Logic Corporation) - */ - -/** - * \file zfsd_event.cc - */ -#include -#include -#include - -#include - -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "callout.h" -#include "vdev_iterator.h" -#include "zfsd_event.h" -#include "case_file.h" -#include "vdev.h" -#include "zfsd.h" -#include "zfsd_exception.h" -#include "zpool_list.h" - -__FBSDID("$FreeBSD$"); -/*============================ Namespace Control =============================*/ -using DevdCtl::Event; -using DevdCtl::Guid; -using DevdCtl::NVPairMap; -using std::stringstream; - -/*=========================== Class Implementations ==========================*/ - -/*-------------------------------- DevfsEvent --------------------------------*/ - -//- DevfsEvent Static Public Methods ------------------------------------------- -Event * -DevfsEvent::Builder(Event::Type type, - NVPairMap &nvPairs, - const string &eventString) -{ - return (new DevfsEvent(type, nvPairs, eventString)); -} - -//- DevfsEvent Static Protected Methods ---------------------------------------- -nvlist_t * -DevfsEvent::ReadLabel(int devFd, bool &inUse, bool °raded) -{ - pool_state_t poolState; - char *poolName; - boolean_t b_inuse; - - inUse = false; - degraded = false; - poolName = NULL; - if (zpool_in_use(g_zfsHandle, devFd, &poolState, - &poolName, &b_inuse) == 0) { - nvlist_t *devLabel; - - inUse = b_inuse == B_TRUE; - if (poolName != NULL) - free(poolName); - - if (zpool_read_label(devFd, &devLabel) != 0 - || devLabel == NULL) - return (NULL); - - try { - Vdev vdev(devLabel); - degraded = vdev.State() != VDEV_STATE_HEALTHY; - return (devLabel); - } catch (ZfsdException &exp) { - string devName = fdevname(devFd); - string devPath = _PATH_DEV + devName; - string context("DevfsEvent::ReadLabel: " - + devPath + ": "); - - exp.GetString().insert(0, context); - exp.Log(); - } - } - return (NULL); -} - -bool -DevfsEvent::OnlineByLabel(const string &devPath, const string& physPath, - nvlist_t *devConfig) -{ - try { - /* - * A device with ZFS label information has been - * inserted. If it matches a device for which we - * have a case, see if we can solve that case. - */ - syslog(LOG_INFO, "Interrogating VDEV label for %s\n", - devPath.c_str()); - Vdev vdev(devConfig); - CaseFile *caseFile(CaseFile::Find(vdev.PoolGUID(), - vdev.GUID())); - if (caseFile != NULL) - return (caseFile->ReEvaluate(devPath, physPath, &vdev)); - - } catch (ZfsdException &exp) { - string context("DevfsEvent::OnlineByLabel: " + devPath + ": "); - - exp.GetString().insert(0, context); - exp.Log(); - } - return (false); -} - -//- DevfsEvent Virtual Public Methods ------------------------------------------ -Event * -DevfsEvent::DeepCopy() const -{ - return (new DevfsEvent(*this)); -} - -bool -DevfsEvent::Process() const -{ - /* - * We are only concerned with newly discovered - * devices that can be ZFS vdevs. - */ - if (Value("type") != "CREATE" || !IsDiskDev()) - return (false); - - /* Log the event since it is of interest. */ - Log(LOG_INFO); - - string devPath; - if (!DevPath(devPath)) - return (false); - - int devFd(open(devPath.c_str(), O_RDONLY)); - if (devFd == -1) - return (false); - - bool inUse; - bool degraded; - nvlist_t *devLabel(ReadLabel(devFd, inUse, degraded)); - - string physPath; - bool havePhysPath(PhysicalPath(physPath)); - - string devName; - DevName(devName); - close(devFd); - - if (inUse && devLabel != NULL) { - OnlineByLabel(devPath, physPath, devLabel); - } else if (degraded) { - syslog(LOG_INFO, "%s is marked degraded. Ignoring " - "as a replace by physical path candidate.\n", - devName.c_str()); - } else if (havePhysPath && IsWholeDev()) { - /* - * TODO: attempt to resolve events using every casefile - * that matches this physpath - */ - CaseFile *caseFile(CaseFile::Find(physPath)); - if (caseFile != NULL) { - syslog(LOG_INFO, - "Found CaseFile(%s:%s:%s) - ReEvaluating\n", - caseFile->PoolGUIDString().c_str(), - caseFile->VdevGUIDString().c_str(), - zpool_state_to_name(caseFile->VdevState(), - VDEV_AUX_NONE)); - caseFile->ReEvaluate(devPath, physPath, /*vdev*/NULL); - } - } - if (devLabel != NULL) - nvlist_free(devLabel); - return (false); -} - -//- DevfsEvent Protected Methods ----------------------------------------------- -DevfsEvent::DevfsEvent(Event::Type type, NVPairMap &nvpairs, - const string &eventString) - : DevdCtl::DevfsEvent(type, nvpairs, eventString) -{ -} - -DevfsEvent::DevfsEvent(const DevfsEvent &src) - : DevdCtl::DevfsEvent::DevfsEvent(src) -{ -} - -/*-------------------------------- GeomEvent --------------------------------*/ - -//- GeomEvent Static Public Methods ------------------------------------------- -Event * -GeomEvent::Builder(Event::Type type, - NVPairMap &nvPairs, - const string &eventString) -{ - return (new GeomEvent(type, nvPairs, eventString)); -} - -//- GeomEvent Virtual Public Methods ------------------------------------------ -Event * -GeomEvent::DeepCopy() const -{ - return (new GeomEvent(*this)); -} - -bool -GeomEvent::Process() const -{ - /* - * We are only concerned with physical path changes, because those can - * be used to satisfy autoreplace operations - */ - if (Value("type") != "GEOM::physpath" || !IsDiskDev()) - return (false); - - /* Log the event since it is of interest. */ - Log(LOG_INFO); - - string devPath; - if (!DevPath(devPath)) - return (false); - - string physPath; - bool havePhysPath(PhysicalPath(physPath)); - - string devName; - DevName(devName); - - if (havePhysPath) { - /* - * TODO: attempt to resolve events using every casefile - * that matches this physpath - */ - CaseFile *caseFile(CaseFile::Find(physPath)); - if (caseFile != NULL) { - syslog(LOG_INFO, - "Found CaseFile(%s:%s:%s) - ReEvaluating\n", - caseFile->PoolGUIDString().c_str(), - caseFile->VdevGUIDString().c_str(), - zpool_state_to_name(caseFile->VdevState(), - VDEV_AUX_NONE)); - caseFile->ReEvaluate(devPath, physPath, /*vdev*/NULL); - } - } - return (false); -} - -//- GeomEvent Protected Methods ----------------------------------------------- -GeomEvent::GeomEvent(Event::Type type, NVPairMap &nvpairs, - const string &eventString) - : DevdCtl::GeomEvent(type, nvpairs, eventString) -{ -} - -GeomEvent::GeomEvent(const GeomEvent &src) - : DevdCtl::GeomEvent::GeomEvent(src) -{ -} - - -/*--------------------------------- ZfsEvent ---------------------------------*/ -//- ZfsEvent Static Public Methods --------------------------------------------- -DevdCtl::Event * -ZfsEvent::Builder(Event::Type type, NVPairMap &nvpairs, - const string &eventString) -{ - return (new ZfsEvent(type, nvpairs, eventString)); -} - -//- ZfsEvent Virtual Public Methods -------------------------------------------- -Event * -ZfsEvent::DeepCopy() const -{ - return (new ZfsEvent(*this)); -} - -bool -ZfsEvent::Process() const -{ - string logstr(""); - - if (!Contains("class") && !Contains("type")) { - syslog(LOG_ERR, - "ZfsEvent::Process: Missing class or type data."); - return (false); - } - - /* On config syncs, replay any queued events first. */ - if (Value("type").find("misc.fs.zfs.config_sync") == 0) { - /* - * Even if saved events are unconsumed the second time - * around, drop them. Any events that still can't be - * consumed are probably referring to vdevs or pools that - * no longer exist. - */ - ZfsDaemon::Get().ReplayUnconsumedEvents(/*discard*/true); - CaseFile::ReEvaluateByGuid(PoolGUID(), *this); - } - - if (Value("type").find("misc.fs.zfs.") == 0) { - /* Configuration changes, resilver events, etc. */ - ProcessPoolEvent(); - return (false); - } - - if (!Contains("pool_guid") || !Contains("vdev_guid")) { - /* Only currently interested in Vdev related events. */ - return (false); - } - - CaseFile *caseFile(CaseFile::Find(PoolGUID(), VdevGUID())); - if (caseFile != NULL) { - Log(LOG_INFO); - syslog(LOG_INFO, "Evaluating existing case file\n"); - caseFile->ReEvaluate(*this); - return (false); - } - - /* Skip events that can't be handled. */ - Guid poolGUID(PoolGUID()); - /* If there are no replicas for a pool, then it's not manageable. */ - if (Value("class").find("fs.zfs.vdev.no_replicas") == 0) { - stringstream msg; - msg << "No replicas available for pool " << poolGUID; - msg << ", ignoring"; - Log(LOG_INFO); - syslog(LOG_INFO, "%s", msg.str().c_str()); - return (false); - } - - /* - * Create a case file for this vdev, and have it - * evaluate the event. - */ - ZpoolList zpl(ZpoolList::ZpoolByGUID, &poolGUID); - if (zpl.empty()) { - stringstream msg; - int priority = LOG_INFO; - msg << "ZfsEvent::Process: Event for unknown pool "; - msg << poolGUID << " "; - msg << "queued"; - Log(LOG_INFO); - syslog(priority, "%s", msg.str().c_str()); - return (true); - } - - nvlist_t *vdevConfig = VdevIterator(zpl.front()).Find(VdevGUID()); - if (vdevConfig == NULL) { - stringstream msg; - int priority = LOG_INFO; - msg << "ZfsEvent::Process: Event for unknown vdev "; - msg << VdevGUID() << " "; - msg << "queued"; - Log(LOG_INFO); - syslog(priority, "%s", msg.str().c_str()); - return (true); - } - - Vdev vdev(zpl.front(), vdevConfig); - caseFile = &CaseFile::Create(vdev); - if (caseFile->ReEvaluate(*this) == false) { - stringstream msg; - int priority = LOG_INFO; - msg << "ZfsEvent::Process: Unconsumed event for vdev("; - msg << zpool_get_name(zpl.front()) << ","; - msg << vdev.GUID() << ") "; - msg << "queued"; - Log(LOG_INFO); - syslog(priority, "%s", msg.str().c_str()); - return (true); - } - return (false); -} - -//- ZfsEvent Protected Methods ------------------------------------------------- -ZfsEvent::ZfsEvent(Event::Type type, NVPairMap &nvpairs, - const string &eventString) - : DevdCtl::ZfsEvent(type, nvpairs, eventString) -{ -} - -ZfsEvent::ZfsEvent(const ZfsEvent &src) - : DevdCtl::ZfsEvent(src) -{ -} - -/* - * Sometimes the kernel won't detach a spare when it is no longer needed. This - * can happen for example if a drive is removed, then either the pool is - * exported or the machine is powered off, then the drive is reinserted, then - * the machine is powered on or the pool is imported. ZFSD must detach these - * spares itself. - */ -void -ZfsEvent::CleanupSpares() const -{ - Guid poolGUID(PoolGUID()); - ZpoolList zpl(ZpoolList::ZpoolByGUID, &poolGUID); - if (!zpl.empty()) { - zpool_handle_t* hdl; - - hdl = zpl.front(); - VdevIterator(hdl).Each(TryDetach, (void*)hdl); - } -} - -void -ZfsEvent::ProcessPoolEvent() const -{ - bool degradedDevice(false); - - /* The pool is destroyed. Discard any open cases */ - if (Value("type") == "misc.fs.zfs.pool_destroy") { - Log(LOG_INFO); - CaseFile::ReEvaluateByGuid(PoolGUID(), *this); - return; - } - - CaseFile *caseFile(CaseFile::Find(PoolGUID(), VdevGUID())); - if (caseFile != NULL) { - if (caseFile->VdevState() != VDEV_STATE_UNKNOWN - && caseFile->VdevState() < VDEV_STATE_HEALTHY) - degradedDevice = true; - - Log(LOG_INFO); - caseFile->ReEvaluate(*this); - } - else if (Value("type") == "misc.fs.zfs.resilver_finish") - { - /* - * It's possible to get a resilver_finish event with no - * corresponding casefile. For example, if a damaged pool were - * exported, repaired, then reimported. - */ - Log(LOG_INFO); - CleanupSpares(); - } - - if (Value("type") == "misc.fs.zfs.vdev_remove" - && degradedDevice == false) { - - /* See if any other cases can make use of this device. */ - Log(LOG_INFO); - ZfsDaemon::RequestSystemRescan(); - } -} - -bool -ZfsEvent::TryDetach(Vdev &vdev, void *cbArg) -{ - /* - * Outline: - * if this device is a spare, and its parent includes one healthy, - * non-spare child, then detach this device. - */ - zpool_handle_t *hdl(static_cast(cbArg)); - - if (vdev.IsSpare()) { - std::list siblings; - std::list::iterator siblings_it; - boolean_t cleanup = B_FALSE; - - Vdev parent = vdev.Parent(); - siblings = parent.Children(); - - /* Determine whether the parent should be cleaned up */ - for (siblings_it = siblings.begin(); - siblings_it != siblings.end(); - siblings_it++) { - Vdev sibling = *siblings_it; - - if (!sibling.IsSpare() && - sibling.State() == VDEV_STATE_HEALTHY) { - cleanup = B_TRUE; - break; - } - } - - if (cleanup) { - syslog(LOG_INFO, "Detaching spare vdev %s from pool %s", - vdev.Path().c_str(), zpool_get_name(hdl)); - zpool_vdev_detach(hdl, vdev.Path().c_str()); - } - - } - - /* Always return false, because there may be other spares to detach */ - return (false); -} Index: projects/zfsd/head/cddl/sbin/zfsd/zfsd_exception.cc =================================================================== --- projects/zfsd/head/cddl/sbin/zfsd/zfsd_exception.cc (revision 300334) +++ projects/zfsd/head/cddl/sbin/zfsd/zfsd_exception.cc (nonexistent) @@ -1,134 +0,0 @@ -/*- - * Copyright (c) 2011, 2012, 2013 Spectra Logic Corporation - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions, and the following disclaimer, - * without modification. - * 2. Redistributions in binary form must reproduce at minimum a disclaimer - * substantially similar to the "NO WARRANTY" disclaimer below - * ("Disclaimer") and any redistribution must be conditioned upon - * including a substantially similar Disclaimer requirement for further - * binary redistribution. - * - * NO WARRANTY - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING - * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGES. - * - * Authors: Justin T. Gibbs (Spectra Logic Corporation) - */ - -/** - * \file zfsd_exception - * - * Implementation of the ZfsdException class. - */ -#include -#include - -#include - -#include -#include -#include - -#include -#include - -#include - -#include "vdev.h" -#include "zfsd_exception.h" - -__FBSDID("$FreeBSD$"); -/*============================ Namespace Control =============================*/ -using std::endl; -using std::string; -using std::stringstream; - -/*=========================== Class Implementations ==========================*/ -/*------------------------------- ZfsdException ------------------------------*/ -ZfsdException::ZfsdException(const char *fmt, ...) - : DevdCtl::Exception(), - m_poolConfig(NULL), - m_vdevConfig(NULL) -{ - va_list ap; - - va_start(ap, fmt); - FormatLog(fmt, ap); - va_end(ap); -} - -ZfsdException::ZfsdException(zpool_handle_t *pool, const char *fmt, ...) - : DevdCtl::Exception(), - m_poolConfig(zpool_get_config(pool, NULL)), - m_vdevConfig(NULL) -{ - va_list ap; - - va_start(ap, fmt); - FormatLog(fmt, ap); - va_end(ap); -} - -ZfsdException::ZfsdException(nvlist_t *poolConfig, const char *fmt, ...) - : DevdCtl::Exception(), - m_poolConfig(poolConfig), - m_vdevConfig(NULL) -{ - va_list ap; - - va_start(ap, fmt); - FormatLog(fmt, ap); - va_end(ap); -} - -void -ZfsdException::Log() const -{ - stringstream output; - - if (m_poolConfig != NULL) { - - output << "Pool "; - - char *poolName; - if (nvlist_lookup_string(m_poolConfig, ZPOOL_CONFIG_POOL_NAME, - &poolName) == 0) - output << poolName; - else - output << "Unkown"; - output << ": "; - } - - if (m_vdevConfig != NULL) { - - if (m_poolConfig != NULL) { - Vdev vdev(m_poolConfig, m_vdevConfig); - - output << "Vdev " << vdev.GUID() << ": "; - } else { - Vdev vdev(m_vdevConfig); - - output << "Pool " << vdev.PoolGUID() << ": "; - output << "Vdev " << vdev.GUID() << ": "; - } - } - - output << m_log << endl; - syslog(LOG_ERR, "%s", output.str().c_str()); -} - Index: projects/zfsd/head/cddl/sbin/zfsd/vdev.cc =================================================================== --- projects/zfsd/head/cddl/sbin/zfsd/vdev.cc (revision 300334) +++ projects/zfsd/head/cddl/sbin/zfsd/vdev.cc (nonexistent) @@ -1,352 +0,0 @@ -/*- - * Copyright (c) 2011 Spectra Logic Corporation - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions, and the following disclaimer, - * without modification. - * 2. Redistributions in binary form must reproduce at minimum a disclaimer - * substantially similar to the "NO WARRANTY" disclaimer below - * ("Disclaimer") and any redistribution must be conditioned upon - * including a substantially similar Disclaimer requirement for further - * binary redistribution. - * - * NO WARRANTY - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING - * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGES. - * - * Authors: Justin T. Gibbs (Spectra Logic Corporation) - * - * $FreeBSD$ - */ - -/** - * \file vdev.cc - * - * Implementation of the Vdev class. - */ -#include -#include -#include - -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "vdev.h" -#include "vdev_iterator.h" -#include "zfsd.h" -#include "zfsd_exception.h" -#include "zpool_list.h" - -__FBSDID("$FreeBSD$"); -/*============================ Namespace Control =============================*/ -using std::string; -using std::stringstream; - -//- Special objects ----------------------------------------------------------- -Vdev NonexistentVdev; - -//- Vdev Inline Public Methods ------------------------------------------------ -/*=========================== Class Implementations ==========================*/ -/*----------------------------------- Vdev -----------------------------------*/ - -/* Special constructor for NonexistentVdev. */ -Vdev::Vdev() - : m_poolConfig(NULL), - m_config(NULL) -{} - -bool -Vdev::VdevLookupPoolGuid() -{ - uint64_t guid; - if (nvlist_lookup_uint64(m_poolConfig, ZPOOL_CONFIG_POOL_GUID, &guid)) - return (false); - m_poolGUID = guid; - return (true); -} - -void -Vdev::VdevLookupGuid() -{ - uint64_t guid; - if (nvlist_lookup_uint64(m_config, ZPOOL_CONFIG_GUID, &guid) != 0) - throw ZfsdException("Unable to extract vdev GUID " - "from vdev config data."); - m_vdevGUID = guid; -} - -Vdev::Vdev(zpool_handle_t *pool, nvlist_t *config) - : m_poolConfig(zpool_get_config(pool, NULL)), - m_config(config) -{ - if (!VdevLookupPoolGuid()) - throw ZfsdException("Can't extract pool GUID from handle."); - VdevLookupGuid(); -} - -Vdev::Vdev(nvlist_t *poolConfig, nvlist_t *config) - : m_poolConfig(poolConfig), - m_config(config) -{ - if (!VdevLookupPoolGuid()) - throw ZfsdException("Can't extract pool GUID from config."); - VdevLookupGuid(); -} - -Vdev::Vdev(nvlist_t *labelConfig) - : m_poolConfig(labelConfig), - m_config(labelConfig) -{ - /* - * Spares do not have a Pool GUID. Tolerate its absence. - * Code accessing this Vdev in a context where the Pool GUID is - * required will find it invalid (as it is upon Vdev construction) - * and act accordingly. - */ - (void) VdevLookupPoolGuid(); - VdevLookupGuid(); - - try { - m_config = VdevIterator(labelConfig).Find(m_vdevGUID); - } catch (const ZfsdException &exp) { - /* - * When reading a spare's label, it is normal not to find - * a list of vdevs - */ - m_config = NULL; - } -} - -bool -Vdev::IsSpare() const -{ - uint64_t is_spare(0); - - if (m_config == NULL) - return (false); - - (void)nvlist_lookup_uint64(m_config, ZPOOL_CONFIG_IS_SPARE, &is_spare); - return (bool(is_spare)); -} - -vdev_state -Vdev::State() const -{ - uint64_t *nvlist_array; - vdev_stat_t *vs; - uint_t vsc; - - if (m_config == NULL) { - /* - * If we couldn't find the list of vdevs, that normally means - * that this is an available hotspare. In that case, we will - * presume it to be healthy. Even if this spare had formerly - * been in use, been degraded, and been replaced, the act of - * replacement wipes the degraded bit from the label. So we - * have no choice but to presume that it is healthy. - */ - return (VDEV_STATE_HEALTHY); - } - - if (nvlist_lookup_uint64_array(m_config, ZPOOL_CONFIG_VDEV_STATS, - &nvlist_array, &vsc) == 0) { - vs = reinterpret_cast(nvlist_array); - return (static_cast(vs->vs_state)); - } - - /* - * Stats are not available. This vdev was created from a label. - * Synthesize a state based on available data. - */ - uint64_t faulted(0); - uint64_t degraded(0); - (void)nvlist_lookup_uint64(m_config, ZPOOL_CONFIG_FAULTED, &faulted); - (void)nvlist_lookup_uint64(m_config, ZPOOL_CONFIG_DEGRADED, °raded); - if (faulted) - return (VDEV_STATE_FAULTED); - if (degraded) - return (VDEV_STATE_DEGRADED); - return (VDEV_STATE_HEALTHY); -} - -std::list -Vdev::Children() -{ - nvlist_t **vdevChildren; - int result; - u_int numChildren; - std::list children; - - if (m_poolConfig == NULL || m_config == NULL) - return (children); - - result = nvlist_lookup_nvlist_array(m_config, - ZPOOL_CONFIG_CHILDREN, &vdevChildren, &numChildren); - if (result != 0) - return (children); - - for (u_int c = 0;c < numChildren; c++) - children.push_back(Vdev(m_poolConfig, vdevChildren[c])); - - return (children); -} - -Vdev -Vdev::RootVdev() -{ - nvlist_t *rootVdev; - - if (m_poolConfig == NULL) - return (NonexistentVdev); - - if (nvlist_lookup_nvlist(m_poolConfig, ZPOOL_CONFIG_VDEV_TREE, - &rootVdev) != 0) - return (NonexistentVdev); - return (Vdev(m_poolConfig, rootVdev)); -} - -/* - * Find our parent. This requires doing a traversal of the config; we can't - * cache it as leaf vdevs may change their pool config location (spare, - * replacing, mirror, etc). - */ -Vdev -Vdev::Parent() -{ - std::list to_examine; - std::list children; - std::list::iterator children_it; - - to_examine.push_back(RootVdev()); - for (;;) { - if (to_examine.empty()) - return (NonexistentVdev); - Vdev vd = to_examine.front(); - if (vd.DoesNotExist()) - return (NonexistentVdev); - to_examine.pop_front(); - children = vd.Children(); - children_it = children.begin(); - for (;children_it != children.end(); children_it++) { - Vdev child = *children_it; - - if (child.GUID() == GUID()) - return (vd); - to_examine.push_front(child); - } - } -} - -bool -Vdev::IsAvailableSpare() const -{ - /* If we have a pool guid, we cannot be an available spare. */ - if (PoolGUID()) - return (false); - - return (true); -} - -bool -Vdev::IsSpare() -{ - uint64_t spare; - if (nvlist_lookup_uint64(m_config, ZPOOL_CONFIG_IS_SPARE, &spare) != 0) - return (false); - return (spare != 0); -} - -bool -Vdev::IsActiveSpare() const -{ - vdev_stat_t *vs; - uint_t c; - - if (m_poolConfig == NULL) - return (false); - - (void) nvlist_lookup_uint64_array(m_config, ZPOOL_CONFIG_VDEV_STATS, - reinterpret_cast(&vs), &c); - if (vs == NULL || vs->vs_aux != VDEV_AUX_SPARED) - return (false); - return (true); -} - -bool -Vdev::IsResilvering() const -{ - pool_scan_stat_t *ps = NULL; - uint_t c; - - if (State() != VDEV_STATE_HEALTHY) - return (false); - - (void) nvlist_lookup_uint64_array(m_config, ZPOOL_CONFIG_SCAN_STATS, - reinterpret_cast(&ps), &c); - if (ps == NULL || ps->pss_func != POOL_SCAN_RESILVER) - return (false); - return (true); -} - -string -Vdev::GUIDString() const -{ - stringstream vdevGUIDString; - - vdevGUIDString << GUID(); - return (vdevGUIDString.str()); -} - -string -Vdev::Name(zpool_handle_t *zhp, bool verbose) const -{ - return (zpool_vdev_name(g_zfsHandle, zhp, m_config, - verbose ? B_TRUE : B_FALSE)); -} - -string -Vdev::Path() const -{ - char *path(NULL); - - if ((m_config != NULL) - && (nvlist_lookup_string(m_config, ZPOOL_CONFIG_PATH, &path) == 0)) - return (path); - - return (""); -} - -string -Vdev::PhysicalPath() const -{ - char *path(NULL); - - if ((m_config != NULL) && (nvlist_lookup_string(m_config, - ZPOOL_CONFIG_PHYS_PATH, &path) == 0)) - return (path); - - return (""); -} Index: projects/zfsd/head/cddl/sbin/zfsd/zpool_list.h =================================================================== --- projects/zfsd/head/cddl/sbin/zfsd/zpool_list.h (revision 300334) +++ projects/zfsd/head/cddl/sbin/zfsd/zpool_list.h (nonexistent) @@ -1,131 +0,0 @@ -/*- - * Copyright (c) 2011 Spectra Logic Corporation - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions, and the following disclaimer, - * without modification. - * 2. Redistributions in binary form must reproduce at minimum a disclaimer - * substantially similar to the "NO WARRANTY" disclaimer below - * ("Disclaimer") and any redistribution must be conditioned upon - * including a substantially similar Disclaimer requirement for further - * binary redistribution. - * - * NO WARRANTY - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING - * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGES. - * - * Authors: Justin T. Gibbs (Spectra Logic Corporation) - */ - -/** - * \file zpool_list.h - * - * ZpoolList class definition. ZpoolList is a standard container - * allowing filtering and iteration of imported ZFS pool information. - * - * Header requirements: - * - * #include - * #include - */ -#ifndef _ZPOOL_LIST_H_ -#define _ZPOOL_LIST_H_ - -/*============================ Namespace Control =============================*/ -using std::string; - -/*=========================== Forward Declarations ===========================*/ -struct zpool_handle; -typedef struct zpool_handle zpool_handle_t; - -struct nvlist; -typedef struct nvlist nvlist_t; - -class Vdev; - -/*============================= Class Definitions ============================*/ -/*--------------------------------- ZpoolList --------------------------------*/ -class ZpoolList; -typedef bool PoolFilter_t(zpool_handle_t *pool, nvlist_t *poolConfig, - void *filterArg); - -/** - * \brief Container of imported ZFS pool data. - * - * ZpoolList is a convenience class that converts libzfs's ZFS - * pool methods into a standard list container. - */ -class ZpoolList : public std::list -{ -public: - /** - * \brief Utility ZpoolList construction filter that causes all - * pools known to the system to be included in the - * intantiated ZpoolList. - */ - static PoolFilter_t ZpoolAll; - - /** - * \brief Utility ZpoolList construction filter that causes only - * a pool known to the system and having the specified GUID - * to be included in the intantiated ZpoolList. - */ - static PoolFilter_t ZpoolByGUID; - - /** - * \brief Utility ZpoolList construction filter that causes only - * pools known to the system and having the specified name - * to be included in the intantiated ZpoolList. - */ - static PoolFilter_t ZpoolByName; - - /** - * \brief ZpoolList contructor - * - * \param filter The filter function to use when constructing - * the ZpoolList. This may be one of the static - * utility filters defined for ZpoolList or a - * user defined function. - * \param filterArg A single argument to pass into the filter function - * when it is invoked on each candidate pool. - */ - ZpoolList(PoolFilter_t *filter = ZpoolAll, void *filterArg = NULL); - ~ZpoolList(); - -private: - /** - * \brief Helper routine used to populate the internal - * data store of ZFS pool objects using libzfs's - * zpool_iter() function. - * - * \param pool The ZFS pool object to filter. - * \param data User argument passed through zpool_iter(). - */ - static int LoadIterator(zpool_handle_t *pool, void *data); - - /** - * \brief The filter with which this ZpoolList was constructed. - */ - PoolFilter_t *m_filter; - - /** - * \brief The filter argument with which this ZpoolList was - * constructed. - */ - void *m_filterArg; -}; - -#endif /* _ZPOOL_ITERATOR_H_ */ Index: projects/zfsd/head/cddl/sbin/zfsd/vdev.h =================================================================== --- projects/zfsd/head/cddl/sbin/zfsd/vdev.h (revision 300334) +++ projects/zfsd/head/cddl/sbin/zfsd/vdev.h (nonexistent) @@ -1,178 +0,0 @@ -/*- - * Copyright (c) 2011 Spectra Logic Corporation - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions, and the following disclaimer, - * without modification. - * 2. Redistributions in binary form must reproduce at minimum a disclaimer - * substantially similar to the "NO WARRANTY" disclaimer below - * ("Disclaimer") and any redistribution must be conditioned upon - * including a substantially similar Disclaimer requirement for further - * binary redistribution. - * - * NO WARRANTY - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING - * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGES. - * - * Authors: Justin T. Gibbs (Spectra Logic Corporation) - * - * $FreeBSD$ - */ - -/** - * \file vdev.h - * - * Definition of the Vdev class. - * - * Header requirements: - * - * #include - * #include - * - * #include - */ -#ifndef _VDEV_H_ -#define _VDEV_H_ - -/*=========================== Forward Declarations ===========================*/ -struct zpool_handle; -typedef struct zpool_handle zpool_handle_t; - -struct nvlist; -typedef struct nvlist nvlist_t; - -/*============================= Class Definitions ============================*/ -/*----------------------------------- Vdev -----------------------------------*/ -/** - * \brief Wrapper class for a vdev's name/value configuration list - * simplifying access to commonly used vdev attributes. - */ -class Vdev -{ -public: - /** - * \brief Instantiate a vdev object for a vdev that is a member - * of an imported pool. - * - * \param pool The pool object containing the vdev with - * configuration data provided in vdevConfig. - * \param vdevConfig Vdev configuration data. - * - * This method should be used whenever dealing with vdev's - * enumerated via the ZpoolList class. The in-core configuration - * data for a vdev does not contain all of the items found in - * the on-disk label. This requires the vdev class to augment - * the data in vdevConfig with data found in the pool object. - */ - Vdev(zpool_handle_t *pool, nvlist_t *vdevConfig); - - /** - * \brief Instantiate a vdev object for a vdev that is a member - * of a pool configuration. - * - * \param poolConfig The pool configuration containing the vdev - * configuration data provided in vdevConfig. - * \param vdevConfig Vdev configuration data. - * - * This method should be used whenever dealing with vdev's - * enumerated via the ZpoolList class. The in-core configuration - * data for a vdev does not contain all of the items found in - * the on-disk label. This requires the vdev class to augment - * the data in vdevConfig with data found in the pool object. - */ - Vdev(nvlist_t *poolConfig, nvlist_t *vdevConfig); - - /** - * \brief Instantiate a vdev object from a ZFS label stored on - * the device. - * - * \param vdevConfig The name/value list retrieved by reading - * the label information on a leaf vdev. - */ - Vdev(nvlist_t *vdevConfig); - - /** - * \brief No-op copy constructor for nonexistent vdevs. - */ - Vdev(); - bool DoesNotExist() const; - - /** - * \brief Return a list of the vdev's children. - */ - std::list Children(); - - virtual DevdCtl::Guid GUID() const; - bool IsSpare() const; - virtual DevdCtl::Guid PoolGUID() const; - virtual vdev_state State() const; - std::string Path() const; - virtual std::string PhysicalPath() const; - std::string GUIDString() const; - nvlist_t *PoolConfig() const; - nvlist_t *Config() const; - Vdev Parent(); - Vdev RootVdev(); - std::string Name(zpool_handle_t *, bool verbose) const; - bool IsSpare(); - bool IsAvailableSpare() const; - bool IsActiveSpare() const; - bool IsResilvering() const; - -private: - void VdevLookupGuid(); - bool VdevLookupPoolGuid(); - DevdCtl::Guid m_poolGUID; - DevdCtl::Guid m_vdevGUID; - nvlist_t *m_poolConfig; - nvlist_t *m_config; -}; - -//- Special objects ----------------------------------------------------------- -extern Vdev NonexistentVdev; - -//- Vdev Inline Public Methods ------------------------------------------------ -inline DevdCtl::Guid -Vdev::PoolGUID() const -{ - return (m_poolGUID); -} - -inline DevdCtl::Guid -Vdev::GUID() const -{ - return (m_vdevGUID); -} - -inline nvlist_t * -Vdev::PoolConfig() const -{ - return (m_poolConfig); -} - -inline nvlist_t * -Vdev::Config() const -{ - return (m_config); -} - -inline bool -Vdev::DoesNotExist() const -{ - return (m_config == NULL); -} - -#endif /* _VDEV_H_ */ Index: projects/zfsd/head/cddl/sbin/zfsd/Makefile =================================================================== --- projects/zfsd/head/cddl/sbin/zfsd/Makefile (revision 300334) +++ projects/zfsd/head/cddl/sbin/zfsd/Makefile (nonexistent) @@ -1,15 +0,0 @@ -# $FreeBSD$ - -SRCDIR=${.CURDIR}/../../.. -.include "Makefile.common" - -PROG_CXX= zfsd - -.include - -# Check for the existence of the googletest and googlemock header files, which -# come from ports. Don't compile the tests without them. -.if exists(${LOCALBASE}/include/gtest/gtest.h) && exists(${LOCALBASE}/include/gmock/gmock.h) -.else -SUBDIR= -.endif Index: projects/zfsd/head/cddl/sbin/Makefile =================================================================== --- projects/zfsd/head/cddl/sbin/Makefile (revision 300334) +++ projects/zfsd/head/cddl/sbin/Makefile (revision 300335) @@ -1,21 +1,18 @@ # $FreeBSD$ .include -SUBDIR= ${_tests} ${_zfs} ${_zfsd} ${_zpool} +SUBDIR= ${_tests} ${_zfs} ${_zpool} .if ${MK_TESTS} != "no" _tests= tests .endif .if ${MK_ZFS} != "no" _zfs= zfs _zpool= zpool -. if ${MK_CXX} != "no" -_zfsd= zfsd -. endif .endif SUBDIR_PARALLEL= .include Index: projects/zfsd/head/cddl/usr.sbin/Makefile =================================================================== --- projects/zfsd/head/cddl/usr.sbin/Makefile (revision 300334) +++ projects/zfsd/head/cddl/usr.sbin/Makefile (revision 300335) @@ -1,45 +1,49 @@ # $FreeBSD$ .include SUBDIR= ${_dtrace} \ ${_lockstat} \ ${_plockstat} \ ${_tests} \ ${_zdb} \ + ${_zfsd} \ ${_zhack} .if ${MK_TESTS} != "no" _tests= tests .endif .if ${MK_ZFS} != "no" .if ${MK_LIBTHR} != "no" _zdb= zdb _zhack= zhack .endif +. if ${MK_CXX} != "no" +_zfsd= zfsd +. endif .endif .if ${MACHINE_ARCH} == "amd64" || ${MACHINE_ARCH} == "i386" _dtrace= dtrace _lockstat= lockstat _plockstat= plockstat .endif .if ${MACHINE_CPUARCH} == "aarch64" || ${MACHINE_CPUARCH} == "arm" _dtrace= dtrace _lockstat= lockstat .endif .if ${MACHINE_CPUARCH} == "mips" _dtrace= dtrace .endif .if ${MACHINE_CPUARCH} == "powerpc" _dtrace= dtrace _lockstat= lockstat .endif SUBDIR_PARALLEL= .include Index: projects/zfsd/head/cddl/usr.sbin/zfsd/case_file.cc =================================================================== --- projects/zfsd/head/cddl/usr.sbin/zfsd/case_file.cc (nonexistent) +++ projects/zfsd/head/cddl/usr.sbin/zfsd/case_file.cc (revision 300335) @@ -0,0 +1,1127 @@ +/*- + * Copyright (c) 2011, 2012, 2013 Spectra Logic Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + * Authors: Justin T. Gibbs (Spectra Logic Corporation) + */ + +/** + * \file case_file.cc + * + * We keep case files for any leaf vdev that is not in the optimal state. + * However, we only serialize to disk those events that need to be preserved + * across reboots. For now, this is just a log of soft errors which we + * accumulate in order to mark a device as degraded. + */ +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "callout.h" +#include "vdev_iterator.h" +#include "zfsd_event.h" +#include "case_file.h" +#include "vdev.h" +#include "zfsd.h" +#include "zfsd_exception.h" +#include "zpool_list.h" + +__FBSDID("$FreeBSD$"); + +/*============================ Namespace Control =============================*/ +using std::auto_ptr; +using std::hex; +using std::ifstream; +using std::stringstream; +using std::setfill; +using std::setw; + +using DevdCtl::Event; +using DevdCtl::EventBuffer; +using DevdCtl::EventFactory; +using DevdCtl::EventList; +using DevdCtl::Guid; +using DevdCtl::ParseException; + +/*-------------------------- File-scoped classes ----------------------------*/ +/** + * \brief Functor that operators on STL collections of CaseFiles + * + * Selectively calls ReEvaluate on the casefile, based on its pool GUID. + */ +class CaseFileReEvaluator : public std::unary_function +{ +public: + CaseFileReEvaluator(Guid guid, const ZfsEvent &event); + + void operator() (CaseFile *casefile); + +private: + Guid m_poolGUID; + const ZfsEvent &m_event; +}; + +CaseFileReEvaluator::CaseFileReEvaluator(Guid guid, const ZfsEvent &event) + : m_poolGUID(guid), m_event(event) +{ +} + +void +CaseFileReEvaluator::operator() (CaseFile *casefile) +{ + if (m_poolGUID == casefile->PoolGUID()) + casefile->ReEvaluate(m_event); +} + +/*--------------------------------- CaseFile ---------------------------------*/ +//- CaseFile Static Data ------------------------------------------------------- +CaseFileList CaseFile::s_activeCases; +const string CaseFile::s_caseFilePath = "/etc/zfs/cases"; +const timeval CaseFile::s_removeGracePeriod = { 60 /*sec*/, 0 /*usec*/}; + +//- CaseFile Static Public Methods --------------------------------------------- +CaseFile * +CaseFile::Find(Guid poolGUID, Guid vdevGUID) +{ + for (CaseFileList::iterator curCase = s_activeCases.begin(); + curCase != s_activeCases.end(); curCase++) { + + if ((*curCase)->PoolGUID() != poolGUID + || (*curCase)->VdevGUID() != vdevGUID) + continue; + + /* + * We only carry one active case per-vdev. + */ + return (*curCase); + } + return (NULL); +} + +CaseFile * +CaseFile::Find(const string &physPath) +{ + CaseFile *result = NULL; + + for (CaseFileList::iterator curCase = s_activeCases.begin(); + curCase != s_activeCases.end(); curCase++) { + + if ((*curCase)->PhysicalPath() != physPath) + continue; + + if (result != NULL) { + syslog(LOG_WARNING, "Multiple casefiles found for " + "physical path %s. " + "This is most likely a bug in zfsd", + physPath.c_str()); + } + result = *curCase; + } + return (result); +} + + +void +CaseFile::ReEvaluateByGuid(Guid poolGUID, const ZfsEvent &event) +{ + CaseFileReEvaluator reevaluator(poolGUID, event); + std::for_each(s_activeCases.begin(), s_activeCases.end(), reevaluator); +} + +CaseFile & +CaseFile::Create(Vdev &vdev) +{ + CaseFile *activeCase; + + activeCase = Find(vdev.PoolGUID(), vdev.GUID()); + if (activeCase == NULL) + activeCase = new CaseFile(vdev); + + return (*activeCase); +} + +void +CaseFile::DeSerialize() +{ + struct dirent **caseFiles; + + int numCaseFiles(scandir(s_caseFilePath.c_str(), &caseFiles, + DeSerializeSelector, /*compar*/NULL)); + + if (numCaseFiles == -1) + return; + if (numCaseFiles == 0) { + free(caseFiles); + return; + } + + for (int i = 0; i < numCaseFiles; i++) { + + DeSerializeFile(caseFiles[i]->d_name); + free(caseFiles[i]); + } + free(caseFiles); +} + +void +CaseFile::LogAll() +{ + for (CaseFileList::iterator curCase = s_activeCases.begin(); + curCase != s_activeCases.end(); curCase++) + (*curCase)->Log(); +} + +void +CaseFile::PurgeAll() +{ + /* + * Serialize casefiles before deleting them so that they can be reread + * and revalidated during BuildCaseFiles. + * CaseFiles remove themselves from this list on destruction. + */ + while (s_activeCases.size() != 0) { + CaseFile *casefile = s_activeCases.front(); + casefile->Serialize(); + delete casefile; + } + +} + +//- CaseFile Public Methods ---------------------------------------------------- +bool +CaseFile::RefreshVdevState() +{ + ZpoolList zpl(ZpoolList::ZpoolByGUID, &m_poolGUID); + zpool_handle_t *casePool(zpl.empty() ? NULL : zpl.front()); + if (casePool == NULL) + return (false); + + Vdev vd(casePool, CaseVdev(casePool)); + if (vd.DoesNotExist()) + return (false); + + m_vdevState = vd.State(); + m_vdevPhysPath = vd.PhysicalPath(); + return (true); +} + +bool +CaseFile::ReEvaluate(const string &devPath, const string &physPath, Vdev *vdev) +{ + ZpoolList zpl(ZpoolList::ZpoolByGUID, &m_poolGUID); + zpool_handle_t *pool(zpl.empty() ? NULL : zpl.front()); + + if (pool == NULL || !RefreshVdevState()) { + /* + * The pool or vdev for this case file is no longer + * part of the configuration. This can happen + * if we process a device arrival notification + * before seeing the ZFS configuration change + * event. + */ + syslog(LOG_INFO, + "CaseFile::ReEvaluate(%s,%s) Pool/Vdev unconfigured. " + "Closing\n", + PoolGUIDString().c_str(), + VdevGUIDString().c_str()); + Close(); + + /* + * Since this event was not used to close this + * case, do not report it as consumed. + */ + return (/*consumed*/false); + } + + if (VdevState() > VDEV_STATE_CANT_OPEN) { + /* + * For now, newly discovered devices only help for + * devices that are missing. In the future, we might + * use a newly inserted spare to replace a degraded + * or faulted device. + */ + syslog(LOG_INFO, "CaseFile::ReEvaluate(%s,%s): Pool/Vdev ignored", + PoolGUIDString().c_str(), VdevGUIDString().c_str()); + return (/*consumed*/false); + } + + if (vdev != NULL + && vdev->PoolGUID() == m_poolGUID + && vdev->GUID() == m_vdevGUID) { + + zpool_vdev_online(pool, vdev->GUIDString().c_str(), + ZFS_ONLINE_CHECKREMOVE | ZFS_ONLINE_UNSPARE, + &m_vdevState); + syslog(LOG_INFO, "Onlined vdev(%s/%s:%s). State now %s.\n", + zpool_get_name(pool), vdev->GUIDString().c_str(), + devPath.c_str(), + zpool_state_to_name(VdevState(), VDEV_AUX_NONE)); + + /* + * Check the vdev state post the online action to see + * if we can retire this case. + */ + CloseIfSolved(); + + return (/*consumed*/true); + } + + /* + * If the auto-replace policy is enabled, and we have physical + * path information, try a physical path replacement. + */ + if (zpool_get_prop_int(pool, ZPOOL_PROP_AUTOREPLACE, NULL) == 0) { + syslog(LOG_INFO, + "CaseFile(%s:%s:%s): AutoReplace not set. " + "Ignoring device insertion.\n", + PoolGUIDString().c_str(), + VdevGUIDString().c_str(), + zpool_state_to_name(VdevState(), VDEV_AUX_NONE)); + return (/*consumed*/false); + } + + if (PhysicalPath().empty()) { + syslog(LOG_INFO, + "CaseFile(%s:%s:%s): No physical path information. " + "Ignoring device insertion.\n", + PoolGUIDString().c_str(), + VdevGUIDString().c_str(), + zpool_state_to_name(VdevState(), VDEV_AUX_NONE)); + return (/*consumed*/false); + } + + if (physPath != PhysicalPath()) { + syslog(LOG_INFO, + "CaseFile(%s:%s:%s): Physical path mismatch. " + "Ignoring device insertion.\n", + PoolGUIDString().c_str(), + VdevGUIDString().c_str(), + zpool_state_to_name(VdevState(), VDEV_AUX_NONE)); + return (/*consumed*/false); + } + + /* Write a label on the newly inserted disk. */ + if (zpool_label_disk(g_zfsHandle, pool, devPath.c_str()) != 0) { + syslog(LOG_ERR, + "Replace vdev(%s/%s) by physical path (label): %s: %s\n", + zpool_get_name(pool), VdevGUIDString().c_str(), + libzfs_error_action(g_zfsHandle), + libzfs_error_description(g_zfsHandle)); + return (/*consumed*/false); + } + + syslog(LOG_INFO, "CaseFile::ReEvaluate(%s/%s): Replacing with %s", + PoolGUIDString().c_str(), VdevGUIDString().c_str(), + devPath.c_str()); + return (Replace(VDEV_TYPE_DISK, devPath.c_str(), /*isspare*/false)); +} + +bool +CaseFile::ReEvaluate(const ZfsEvent &event) +{ + bool consumed(false); + + if (event.Value("type") == "misc.fs.zfs.vdev_remove") { + /* + * The Vdev we represent has been removed from the + * configuration. This case is no longer of value. + */ + Close(); + + return (/*consumed*/true); + } else if (event.Value("type") == "misc.fs.zfs.pool_destroy") { + /* This Pool has been destroyed. Discard the case */ + Close(); + + return (/*consumed*/true); + } else if (event.Value("type") == "misc.fs.zfs.config_sync") { + RefreshVdevState(); + if (VdevState() < VDEV_STATE_HEALTHY) + consumed = ActivateSpare(); + } + + + if (event.Value("class") == "resource.fs.zfs.removed") { + bool spare_activated; + + if (!RefreshVdevState()) { + /* + * The pool or vdev for this case file is no longer + * part of the configuration. This can happen + * if we process a device arrival notification + * before seeing the ZFS configuration change + * event. + */ + syslog(LOG_INFO, + "CaseFile::ReEvaluate(%s,%s) Pool/Vdev " + "unconfigured. Closing\n", + PoolGUIDString().c_str(), + VdevGUIDString().c_str()); + /* + * Close the case now so we won't waste cycles in the + * system rescan + */ + Close(); + + /* + * Since this event was not used to close this + * case, do not report it as consumed. + */ + return (/*consumed*/false); + } + + /* + * Discard any tentative I/O error events for + * this case. They were most likely caused by the + * hot-unplug of this device. + */ + PurgeTentativeEvents(); + + /* Try to activate spares if they are available */ + spare_activated = ActivateSpare(); + + /* + * Rescan the drives in the system to see if a recent + * drive arrival can be used to solve this case. + */ + ZfsDaemon::RequestSystemRescan(); + + /* + * Consume the event if we successfully activated a spare. + * Otherwise, leave it in the unconsumed events list so that the + * future addition of a spare to this pool might be able to + * close the case + */ + consumed = spare_activated; + } else if (event.Value("class") == "resource.fs.zfs.statechange") { + RefreshVdevState(); + /* + * If this vdev is DEGRADED, FAULTED, or UNAVAIL, try to + * activate a hotspare. Otherwise, ignore the event + */ + if (VdevState() == VDEV_STATE_FAULTED || + VdevState() == VDEV_STATE_DEGRADED || + VdevState() == VDEV_STATE_CANT_OPEN) + (void) ActivateSpare(); + consumed = true; + } + else if (event.Value("class") == "ereport.fs.zfs.io" || + event.Value("class") == "ereport.fs.zfs.checksum") { + + m_tentativeEvents.push_front(event.DeepCopy()); + RegisterCallout(event); + consumed = true; + } + + bool closed(CloseIfSolved()); + + return (consumed || closed); +} + + +bool +CaseFile::ActivateSpare() { + nvlist_t *config, *nvroot; + nvlist_t **spares; + char *devPath, *vdev_type; + const char *poolname; + u_int nspares, i; + int error; + + ZpoolList zpl(ZpoolList::ZpoolByGUID, &m_poolGUID); + zpool_handle_t *zhp(zpl.empty() ? NULL : zpl.front()); + if (zhp == NULL) { + syslog(LOG_ERR, "CaseFile::ActivateSpare: Could not find pool " + "for pool_guid %"PRIu64".", (uint64_t)m_poolGUID); + return (false); + } + poolname = zpool_get_name(zhp); + config = zpool_get_config(zhp, NULL); + if (config == NULL) { + syslog(LOG_ERR, "CaseFile::ActivateSpare: Could not find pool " + "config for pool %s", poolname); + return (false); + } + error = nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, &nvroot); + if (error != 0){ + syslog(LOG_ERR, "CaseFile::ActivateSpare: Could not find vdev " + "tree for pool %s", poolname); + return (false); + } + nspares = 0; + nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_SPARES, &spares, + &nspares); + if (nspares == 0) { + /* The pool has no spares configured */ + syslog(LOG_INFO, "CaseFile::ActivateSpare: " + "No spares available for pool %s", poolname); + return (false); + } + for (i = 0; i < nspares; i++) { + uint64_t *nvlist_array; + vdev_stat_t *vs; + uint_t nstats; + + if (nvlist_lookup_uint64_array(spares[i], + ZPOOL_CONFIG_VDEV_STATS, &nvlist_array, &nstats) != 0) { + syslog(LOG_ERR, "CaseFile::ActivateSpare: Could not " + "find vdev stats for pool %s, spare %d", + poolname, i); + return (false); + } + vs = reinterpret_cast(nvlist_array); + + if ((vs->vs_aux != VDEV_AUX_SPARED) + && (vs->vs_state == VDEV_STATE_HEALTHY)) { + /* We found a usable spare */ + break; + } + } + + if (i == nspares) { + /* No available spares were found */ + return (false); + } + + error = nvlist_lookup_string(spares[i], ZPOOL_CONFIG_PATH, &devPath); + if (error != 0) { + syslog(LOG_ERR, "CaseFile::ActivateSpare: Cannot determine " + "the path of pool %s, spare %d. Error %d", + poolname, i, error); + return (false); + } + + error = nvlist_lookup_string(spares[i], ZPOOL_CONFIG_TYPE, &vdev_type); + if (error != 0) { + syslog(LOG_ERR, "CaseFile::ActivateSpare: Cannot determine " + "the vdev type of pool %s, spare %d. Error %d", + poolname, i, error); + return (false); + } + + return (Replace(vdev_type, devPath, /*isspare*/true)); +} + +void +CaseFile::RegisterCallout(const Event &event) +{ + timeval now, countdown, elapsed, timestamp, zero, remaining; + + gettimeofday(&now, 0); + timestamp = event.GetTimestamp(); + timersub(&now, ×tamp, &elapsed); + timersub(&s_removeGracePeriod, &elapsed, &countdown); + /* + * If countdown is <= zero, Reset the timer to the + * smallest positive time value instead + */ + timerclear(&zero); + if (timercmp(&countdown, &zero, <=)) { + timerclear(&countdown); + countdown.tv_usec = 1; + } + + remaining = m_tentativeTimer.TimeRemaining(); + + if (!m_tentativeTimer.IsPending() + || timercmp(&countdown, &remaining, <)) + m_tentativeTimer.Reset(countdown, OnGracePeriodEnded, this); +} + + +bool +CaseFile::CloseIfSolved() +{ + if (m_events.empty() + && m_tentativeEvents.empty()) { + + /* + * We currently do not track or take actions on + * devices in the degraded or faulted state. + * Once we have support for spare pools, we'll + * retain these cases so that any spares added in + * the future can be applied to them. + */ + switch (VdevState()) { + case VDEV_STATE_HEALTHY: + /* No need to keep cases for healthy vdevs */ + Close(); + return (true); + case VDEV_STATE_REMOVED: + case VDEV_STATE_CANT_OPEN: + /* + * Keep open. We may solve it with a newly inserted + * device. + */ + case VDEV_STATE_FAULTED: + case VDEV_STATE_DEGRADED: + /* + * Keep open. We may solve it with the future + * addition of a spare to the pool + */ + case VDEV_STATE_UNKNOWN: + case VDEV_STATE_CLOSED: + case VDEV_STATE_OFFLINE: + /* + * Keep open? This may not be the correct behavior, + * but it's what we've always done + */ + ; + } + + /* + * Re-serialize the case in order to remove any + * previous event data. + */ + Serialize(); + } + + return (false); +} + +void +CaseFile::Log() +{ + syslog(LOG_INFO, "CaseFile(%s,%s,%s)\n", PoolGUIDString().c_str(), + VdevGUIDString().c_str(), PhysicalPath().c_str()); + syslog(LOG_INFO, "\tVdev State = %s\n", + zpool_state_to_name(VdevState(), VDEV_AUX_NONE)); + if (m_tentativeEvents.size() != 0) { + syslog(LOG_INFO, "\t=== Tentative Events ===\n"); + for (EventList::iterator event(m_tentativeEvents.begin()); + event != m_tentativeEvents.end(); event++) + (*event)->Log(LOG_INFO); + } + if (m_events.size() != 0) { + syslog(LOG_INFO, "\t=== Events ===\n"); + for (EventList::iterator event(m_events.begin()); + event != m_events.end(); event++) + (*event)->Log(LOG_INFO); + } +} + +//- CaseFile Static Protected Methods ------------------------------------------ +void +CaseFile::OnGracePeriodEnded(void *arg) +{ + CaseFile &casefile(*static_cast(arg)); + + casefile.OnGracePeriodEnded(); +} + +int +CaseFile::DeSerializeSelector(const struct dirent *dirEntry) +{ + uint64_t poolGUID; + uint64_t vdevGUID; + + if (dirEntry->d_type == DT_REG + && sscanf(dirEntry->d_name, "pool_%"PRIu64"_vdev_%"PRIu64".case", + &poolGUID, &vdevGUID) == 2) + return (1); + return (0); +} + +void +CaseFile::DeSerializeFile(const char *fileName) +{ + string fullName(s_caseFilePath + '/' + fileName); + CaseFile *existingCaseFile(NULL); + CaseFile *caseFile(NULL); + + try { + uint64_t poolGUID; + uint64_t vdevGUID; + nvlist_t *vdevConf; + + sscanf(fileName, "pool_%"PRIu64"_vdev_%"PRIu64".case", + &poolGUID, &vdevGUID); + existingCaseFile = Find(Guid(poolGUID), Guid(vdevGUID)); + if (existingCaseFile != NULL) { + /* + * If the vdev is already degraded or faulted, + * there's no point in keeping the state around + * that we use to put a drive into the degraded + * state. However, if the vdev is simply missing, + * preseve the case data in the hopes that it will + * return. + */ + caseFile = existingCaseFile; + vdev_state curState(caseFile->VdevState()); + if (curState > VDEV_STATE_CANT_OPEN + && curState < VDEV_STATE_HEALTHY) { + unlink(fileName); + return; + } + } else { + ZpoolList zpl(ZpoolList::ZpoolByGUID, &poolGUID); + if (zpl.empty() + || (vdevConf = VdevIterator(zpl.front()) + .Find(vdevGUID)) == NULL) { + /* + * Either the pool no longer exists + * or this vdev is no longer a member of + * the pool. + */ + unlink(fullName.c_str()); + return; + } + + /* + * Any vdev we find that does not have a case file + * must be in the healthy state and thus worthy of + * continued SERD data tracking. + */ + caseFile = new CaseFile(Vdev(zpl.front(), vdevConf)); + } + + ifstream caseStream(fullName.c_str()); + if (!caseStream) + throw ZfsdException("CaseFile::DeSerialize: Unable to " + "read %s.\n", fileName); + + caseFile->DeSerialize(caseStream); + } catch (const ParseException &exp) { + + exp.Log(); + if (caseFile != existingCaseFile) + delete caseFile; + + /* + * Since we can't parse the file, unlink it so we don't + * trip over it again. + */ + unlink(fileName); + } catch (const ZfsdException &zfsException) { + + zfsException.Log(); + if (caseFile != existingCaseFile) + delete caseFile; + } +} + +//- CaseFile Protected Methods ------------------------------------------------- +CaseFile::CaseFile(const Vdev &vdev) + : m_poolGUID(vdev.PoolGUID()), + m_vdevGUID(vdev.GUID()), + m_vdevState(vdev.State()), + m_vdevPhysPath(vdev.PhysicalPath()) +{ + stringstream guidString; + + guidString << m_vdevGUID; + m_vdevGUIDString = guidString.str(); + guidString.str(""); + guidString << m_poolGUID; + m_poolGUIDString = guidString.str(); + + s_activeCases.push_back(this); + + syslog(LOG_INFO, "Creating new CaseFile:\n"); + Log(); +} + +CaseFile::~CaseFile() +{ + PurgeEvents(); + PurgeTentativeEvents(); + m_tentativeTimer.Stop(); + s_activeCases.remove(this); +} + +void +CaseFile::PurgeEvents() +{ + for (EventList::iterator event(m_events.begin()); + event != m_events.end(); event++) + delete *event; + + m_events.clear(); +} + +void +CaseFile::PurgeTentativeEvents() +{ + for (EventList::iterator event(m_tentativeEvents.begin()); + event != m_tentativeEvents.end(); event++) + delete *event; + + m_tentativeEvents.clear(); +} + +void +CaseFile::SerializeEvList(const EventList events, int fd, + const char* prefix) const +{ + if (events.empty()) + return; + for (EventList::const_iterator curEvent = events.begin(); + curEvent != events.end(); curEvent++) { + const string &eventString((*curEvent)->GetEventString()); + + // TODO: replace many write(2) calls with a single writev(2) + if (prefix) + write(fd, prefix, strlen(prefix)); + write(fd, eventString.c_str(), eventString.length()); + } +} + +void +CaseFile::Serialize() +{ + stringstream saveFile; + + saveFile << setfill('0') + << s_caseFilePath << "/" + << "pool_" << PoolGUIDString() + << "_vdev_" << VdevGUIDString() + << ".case"; + + if (m_events.empty() && m_tentativeEvents.empty()) { + unlink(saveFile.str().c_str()); + return; + } + + int fd(open(saveFile.str().c_str(), O_CREAT|O_TRUNC|O_WRONLY, 0644)); + if (fd == -1) { + syslog(LOG_ERR, "CaseFile::Serialize: Unable to open %s.\n", + saveFile.str().c_str()); + return; + } + SerializeEvList(m_events, fd); + SerializeEvList(m_tentativeEvents, fd, "tentative "); + close(fd); +} + +/* + * XXX: This method assumes that events may not contain embedded newlines. If + * ever events can contain embedded newlines, then CaseFile must switch + * serialization formats + */ +void +CaseFile::DeSerialize(ifstream &caseStream) +{ + string evString; + const EventFactory &factory(ZfsDaemon::Get().GetFactory()); + + caseStream >> std::noskipws >> std::ws; + while (caseStream.good()) { + /* + * Outline: + * read the beginning of a line and check it for + * "tentative". If found, discard "tentative". + * Create a new event + * continue + */ + EventList* destEvents; + const string tentFlag("tentative "); + string line; + std::stringbuf lineBuf; + + caseStream.get(lineBuf); + caseStream.ignore(); /*discard the newline character*/ + line = lineBuf.str(); + if (line.compare(0, tentFlag.size(), tentFlag) == 0) { + /* Discard "tentative" */ + line.erase(0, tentFlag.size()); + destEvents = &m_tentativeEvents; + } else { + destEvents = &m_events; + } + Event *event(Event::CreateEvent(factory, line)); + if (event != NULL) { + destEvents->push_back(event); + RegisterCallout(*event); + } + } +} + +void +CaseFile::Close() +{ + /* + * This case is no longer relevant. Clean up our + * serialization file, and delete the case. + */ + syslog(LOG_INFO, "CaseFile(%s,%s) closed - State %s\n", + PoolGUIDString().c_str(), VdevGUIDString().c_str(), + zpool_state_to_name(VdevState(), VDEV_AUX_NONE)); + + /* + * Serialization of a Case with no event data, clears the + * Serialization data for that event. + */ + PurgeEvents(); + Serialize(); + + delete this; +} + +void +CaseFile::OnGracePeriodEnded() +{ + bool should_fault, should_degrade; + ZpoolList zpl(ZpoolList::ZpoolByGUID, &m_poolGUID); + zpool_handle_t *zhp(zpl.empty() ? NULL : zpl.front()); + + m_events.splice(m_events.begin(), m_tentativeEvents); + should_fault = ShouldFault(); + should_degrade = ShouldDegrade(); + + if (should_fault || should_degrade) { + if (zhp == NULL + || (VdevIterator(zhp).Find(m_vdevGUID)) == NULL) { + /* + * Either the pool no longer exists + * or this vdev is no longer a member of + * the pool. + */ + Close(); + return; + } + + } + + /* A fault condition has priority over a degrade condition */ + if (ShouldFault()) { + /* Fault the vdev and close the case. */ + if (zpool_vdev_fault(zhp, (uint64_t)m_vdevGUID, + VDEV_AUX_ERR_EXCEEDED) == 0) { + syslog(LOG_INFO, "Faulting vdev(%s/%s)", + PoolGUIDString().c_str(), + VdevGUIDString().c_str()); + Close(); + return; + } + else { + syslog(LOG_ERR, "Fault vdev(%s/%s): %s: %s\n", + PoolGUIDString().c_str(), + VdevGUIDString().c_str(), + libzfs_error_action(g_zfsHandle), + libzfs_error_description(g_zfsHandle)); + } + } + else if (ShouldDegrade()) { + /* Degrade the vdev and close the case. */ + if (zpool_vdev_degrade(zhp, (uint64_t)m_vdevGUID, + VDEV_AUX_ERR_EXCEEDED) == 0) { + syslog(LOG_INFO, "Degrading vdev(%s/%s)", + PoolGUIDString().c_str(), + VdevGUIDString().c_str()); + Close(); + return; + } + else { + syslog(LOG_ERR, "Degrade vdev(%s/%s): %s: %s\n", + PoolGUIDString().c_str(), + VdevGUIDString().c_str(), + libzfs_error_action(g_zfsHandle), + libzfs_error_description(g_zfsHandle)); + } + } + Serialize(); +} + +Vdev +CaseFile::BeingReplacedBy(zpool_handle_t *zhp) { + Vdev vd(zhp, CaseVdev(zhp)); + std::list children; + std::list::iterator children_it; + + Vdev parent(vd.Parent()); + Vdev replacing(NonexistentVdev); + + /* + * To determine whether we are being replaced by another spare that + * is still working, then make sure that it is currently spared and + * that the spare is either resilvering or healthy. If any of these + * conditions fail, then we are not being replaced by a spare. + * + * If the spare is healthy, then the case file should be closed very + * soon after this check. + */ + if (parent.DoesNotExist() + || parent.Name(zhp, /*verbose*/false) != "spare") + return (NonexistentVdev); + + children = parent.Children(); + children_it = children.begin(); + for (;children_it != children.end(); children_it++) { + Vdev child = *children_it; + + /* Skip our vdev. */ + if (child.GUID() == VdevGUID()) + continue; + /* + * Accept the first child that doesn't match our GUID, or + * any resilvering/healthy device if one exists. + */ + if (replacing.DoesNotExist() || child.IsResilvering() + || child.State() == VDEV_STATE_HEALTHY) + replacing = child; + } + + return (replacing); +} + +bool +CaseFile::Replace(const char* vdev_type, const char* path, bool isspare) { + nvlist_t *nvroot, *newvd; + const char *poolname; + string oldstr(VdevGUIDString()); + bool retval = true; + + /* Figure out what pool we're working on */ + ZpoolList zpl(ZpoolList::ZpoolByGUID, &m_poolGUID); + zpool_handle_t *zhp(zpl.empty() ? NULL : zpl.front()); + if (zhp == NULL) { + syslog(LOG_ERR, "CaseFile::Replace: could not find pool for " + "pool_guid %"PRIu64".", (uint64_t)m_poolGUID); + return (false); + } + poolname = zpool_get_name(zhp); + Vdev vd(zhp, CaseVdev(zhp)); + Vdev replaced(BeingReplacedBy(zhp)); + + if (isspare && !vd.IsSpare() && !replaced.DoesNotExist()) { + /* If we are already being replaced by a working spare, pass. */ + if (replaced.IsResilvering() + || replaced.State() == VDEV_STATE_HEALTHY) { + syslog(LOG_INFO, "CaseFile::Replace(%s->%s): already " + "replaced", VdevGUIDString().c_str(), path); + return (/*consumed*/false); + } + /* + * If we have already been replaced by a spare, but that spare + * is broken, we must spare the spare, not the original device. + */ + oldstr = replaced.GUIDString(); + syslog(LOG_INFO, "CaseFile::Replace(%s->%s): sparing " + "broken spare %s instead", VdevGUIDString().c_str(), + path, oldstr.c_str()); + } + + /* + * Build a root vdev/leaf vdev configuration suitable for + * zpool_vdev_attach. Only enough data for the kernel to find + * the device (i.e. type and disk device node path) are needed. + */ + nvroot = NULL; + newvd = NULL; + + if (nvlist_alloc(&nvroot, NV_UNIQUE_NAME, 0) != 0 + || nvlist_alloc(&newvd, NV_UNIQUE_NAME, 0) != 0) { + syslog(LOG_ERR, "Replace vdev(%s/%s): Unable to allocate " + "configuration data.", poolname, oldstr.c_str()); + if (nvroot != NULL) + nvlist_free(nvroot); + return (false); + } + if (nvlist_add_string(newvd, ZPOOL_CONFIG_TYPE, vdev_type) != 0 + || nvlist_add_string(newvd, ZPOOL_CONFIG_PATH, path) != 0 + || nvlist_add_string(nvroot, ZPOOL_CONFIG_TYPE, VDEV_TYPE_ROOT) != 0 + || nvlist_add_nvlist_array(nvroot, ZPOOL_CONFIG_CHILDREN, + &newvd, 1) != 0) { + syslog(LOG_ERR, "Replace vdev(%s/%s): Unable to initialize " + "configuration data.", poolname, oldstr.c_str()); + nvlist_free(newvd); + nvlist_free(nvroot); + return (true); + } + + /* Data was copied when added to the root vdev. */ + nvlist_free(newvd); + + retval = (zpool_vdev_attach(zhp, oldstr.c_str(), path, nvroot, + /*replace*/B_TRUE) == 0); + if (retval) + syslog(LOG_INFO, "Replacing vdev(%s/%s) with %s\n", + poolname, oldstr.c_str(), path); + else + syslog(LOG_ERR, "Replace vdev(%s/%s): %s: %s\n", + poolname, oldstr.c_str(), libzfs_error_action(g_zfsHandle), + libzfs_error_description(g_zfsHandle)); + nvlist_free(nvroot); + + return (retval); +} + +/* Does the argument event refer to a checksum error? */ +static bool +IsChecksumEvent(const Event* const event) +{ + return ("ereport.fs.zfs.checksum" == event->Value("type")); +} + +/* Does the argument event refer to an IO error? */ +static bool +IsIOEvent(const Event* const event) +{ + return ("ereport.fs.zfs.io" == event->Value("type")); +} + +bool +CaseFile::ShouldDegrade() const +{ + return (std::count_if(m_events.begin(), m_events.end(), + IsChecksumEvent) > ZFS_DEGRADE_IO_COUNT); +} + +bool +CaseFile::ShouldFault() const +{ + return (std::count_if(m_events.begin(), m_events.end(), + IsIOEvent) > ZFS_DEGRADE_IO_COUNT); +} + +nvlist_t * +CaseFile::CaseVdev(zpool_handle_t *zhp) const +{ + return (VdevIterator(zhp).Find(VdevGUID())); +} Index: projects/zfsd/head/cddl/usr.sbin/zfsd/Makefile.common =================================================================== --- projects/zfsd/head/cddl/usr.sbin/zfsd/Makefile.common (nonexistent) +++ projects/zfsd/head/cddl/usr.sbin/zfsd/Makefile.common (revision 300335) @@ -0,0 +1,44 @@ +# $FreeBSD$ + +SRCS= callout.cc \ + case_file.cc \ + zfsd_event.cc \ + vdev.cc \ + vdev_iterator.cc \ + zfsd.cc \ + zfsd_exception.cc \ + zpool_list.cc \ + zfsd_main.cc + +MAN= + +WARNS?= 3 + +# Ignore warnings about Solaris specific pragmas. +IGNORE_PRAGMA= YES + +INCFLAGS+= -I${SRCDIR}/cddl/contrib/opensolaris/lib/libzpool/common +INCFLAGS+= -I${SRCDIR}/cddl/compat/opensolaris/include +INCFLAGS+= -I${SRCDIR}/cddl/compat/opensolaris/lib/libumem +INCFLAGS+= -I${SRCDIR}/sys/cddl/compat/opensolaris +INCFLAGS+= -I${SRCDIR}/cddl/contrib/opensolaris/head +INCFLAGS+= -I${SRCDIR}/cddl/contrib/opensolaris/lib/libuutil/common +INCFLAGS+= -I${SRCDIR}/cddl/contrib/opensolaris/lib/libumem/common +INCFLAGS+= -I${SRCDIR}/cddl/contrib/opensolaris/lib/libzfs_core/common +INCFLAGS+= -I${SRCDIR}/cddl/contrib/opensolaris/lib/libzfs/common +INCFLAGS+= -I${SRCDIR}/cddl/contrib/opensolaris/lib/libnvpair +INCFLAGS+= -I${SRCDIR}/sys/cddl/contrib/opensolaris/common/zfs +INCFLAGS+= -I${SRCDIR}/sys/cddl/contrib/opensolaris/uts/common +INCFLAGS+= -I${SRCDIR}/sys/cddl/contrib/opensolaris/uts/common/fs/zfs +INCFLAGS+= -I${SRCDIR}/sys/cddl/contrib/opensolaris/uts/common/sys + +CFLAGS= -g -DNEED_SOLARIS_BOOLEAN ${INCFLAGS} + +DPADD= ${LIBDEVDCTL} ${LIBZFS} ${LIBZFS_CORE} ${LIBUTIL} ${LIBGEOM} \ + ${LIBBSDXML} ${LIBSBUF} ${LIBNVPAIR} ${LIBUUTIL} +LIBADD= devdctl zfs zfs_core util geom bsdxml sbuf nvpair uutil + +cscope: + find ${.CURDIR} -type f -a \( -name "*.[ch]" -o -name "*.cc" \) \ + > ${.CURDIR}/cscope.files + cd ${.CURDIR} && cscope -buq ${INCFLAGS} Index: projects/zfsd/head/cddl/usr.sbin/zfsd/zfsd.cc =================================================================== --- projects/zfsd/head/cddl/usr.sbin/zfsd/zfsd.cc (nonexistent) +++ projects/zfsd/head/cddl/usr.sbin/zfsd/zfsd.cc (revision 300335) @@ -0,0 +1,448 @@ +/*- + * Copyright (c) 2011, 2012, 2013 Spectra Logic Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + * Authors: Justin T. Gibbs (Spectra Logic Corporation) + */ + +/** + * \file zfsd.cc + * + * The ZFS daemon consumes kernel devdctl(4) event data via devd(8)'s + * unix domain socket in order to react to system changes that impact + * the function of ZFS storage pools. The goal of this daemon is to + * provide similar functionality to the Solaris ZFS Diagnostic Engine + * (zfs-diagnosis), the Solaris ZFS fault handler (zfs-retire), and + * the Solaris ZFS vdev insertion agent (zfs-mod sysevent handler). + */ + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "callout.h" +#include "vdev_iterator.h" +#include "zfsd_event.h" +#include "case_file.h" +#include "vdev.h" +#include "vdev_iterator.h" +#include "zfsd.h" +#include "zfsd_exception.h" +#include "zpool_list.h" + +__FBSDID("$FreeBSD$"); + +/*================================== Macros ==================================*/ +#define NUM_ELEMENTS(x) (sizeof(x) / sizeof(*x)) + +/*============================ Namespace Control =============================*/ +using DevdCtl::Event; +using DevdCtl::EventFactory; +using DevdCtl::EventList; + +/*================================ Global Data ===============================*/ +int g_debug = 0; +libzfs_handle_t *g_zfsHandle; + +/*--------------------------------- ZfsDaemon --------------------------------*/ +//- ZfsDaemon Static Private Data ---------------------------------------------- +ZfsDaemon *ZfsDaemon::s_theZfsDaemon; +bool ZfsDaemon::s_logCaseFiles; +bool ZfsDaemon::s_terminateEventLoop; +char ZfsDaemon::s_pidFilePath[] = "/var/run/zfsd.pid"; +pidfh *ZfsDaemon::s_pidFH; +int ZfsDaemon::s_signalPipeFD[2]; +bool ZfsDaemon::s_systemRescanRequested(false); +EventFactory::Record ZfsDaemon::s_registryEntries[] = +{ + { Event::NOTIFY, "DEVFS", &DevfsEvent::Builder }, + { Event::NOTIFY, "GEOM", &GeomEvent::Builder }, + { Event::NOTIFY, "ZFS", &ZfsEvent::Builder } +}; + +//- ZfsDaemon Static Public Methods -------------------------------------------- +ZfsDaemon & +ZfsDaemon::Get() +{ + return (*s_theZfsDaemon); +} + +void +ZfsDaemon::WakeEventLoop() +{ + write(s_signalPipeFD[1], "+", 1); +} + +void +ZfsDaemon::RequestSystemRescan() +{ + s_systemRescanRequested = true; + ZfsDaemon::WakeEventLoop(); +} + +void +ZfsDaemon::Run() +{ + ZfsDaemon daemon; + + while (s_terminateEventLoop == false) { + + try { + daemon.DisconnectFromDevd(); + + if (daemon.ConnectToDevd() == false) { + sleep(30); + continue; + } + + daemon.DetectMissedEvents(); + + daemon.EventLoop(); + + } catch (const DevdCtl::Exception &exp) { + exp.Log(); + } + } + + daemon.DisconnectFromDevd(); +} + +//- ZfsDaemon Private Methods -------------------------------------------------- +ZfsDaemon::ZfsDaemon() + : Consumer(/*defBuilder*/NULL, s_registryEntries, + NUM_ELEMENTS(s_registryEntries)) +{ + if (s_theZfsDaemon != NULL) + errx(1, "Multiple ZfsDaemon instances created. Exiting"); + + s_theZfsDaemon = this; + + if (pipe(s_signalPipeFD) != 0) + errx(1, "Unable to allocate signal pipe. Exiting"); + + if (fcntl(s_signalPipeFD[0], F_SETFL, O_NONBLOCK) == -1) + errx(1, "Unable to set pipe as non-blocking. Exiting"); + + if (fcntl(s_signalPipeFD[1], F_SETFL, O_NONBLOCK) == -1) + errx(1, "Unable to set pipe as non-blocking. Exiting"); + + signal(SIGHUP, ZfsDaemon::RescanSignalHandler); + signal(SIGINFO, ZfsDaemon::InfoSignalHandler); + signal(SIGINT, ZfsDaemon::QuitSignalHandler); + signal(SIGTERM, ZfsDaemon::QuitSignalHandler); + signal(SIGUSR1, ZfsDaemon::RescanSignalHandler); + + g_zfsHandle = libzfs_init(); + if (g_zfsHandle == NULL) + errx(1, "Unable to initialize ZFS library. Exiting"); + + Callout::Init(); + InitializeSyslog(); + OpenPIDFile(); + + if (g_debug == 0) + daemon(0, 0); + + UpdatePIDFile(); +} + +ZfsDaemon::~ZfsDaemon() +{ + PurgeCaseFiles(); + ClosePIDFile(); +} + +void +ZfsDaemon::PurgeCaseFiles() +{ + CaseFile::PurgeAll(); +} + +bool +ZfsDaemon::VdevAddCaseFile(Vdev &vdev, void *cbArg) +{ + if (vdev.State() != VDEV_STATE_HEALTHY) + CaseFile::Create(vdev); + + return (/*break early*/false); +} + +void +ZfsDaemon::BuildCaseFiles() +{ + ZpoolList zpl; + ZpoolList::iterator pool; + + /* Add CaseFiles for vdevs with issues. */ + for (pool = zpl.begin(); pool != zpl.end(); pool++) + VdevIterator(*pool).Each(VdevAddCaseFile, NULL); + + /* De-serialize any saved cases. */ + CaseFile::DeSerialize(); + + /* Simulate config_sync events to force CaseFile reevaluation */ + for (pool = zpl.begin(); pool != zpl.end(); pool++) { + char evString[160]; + Event *event; + nvlist_t *config; + uint64_t poolGUID; + const char *poolname; + + poolname = zpool_get_name(*pool); + config = zpool_get_config(*pool, NULL); + if (config == NULL) { + syslog(LOG_ERR, "ZFSDaemon::BuildCaseFiles: Could not " + "find pool config for pool %s", poolname); + continue; + } + if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID, + &poolGUID) != 0) { + syslog(LOG_ERR, "ZFSDaemon::BuildCaseFiles: Could not " + "find pool guid for pool %s", poolname); + continue; + } + + + snprintf(evString, 160, "!system=ZFS subsystem=ZFS " + "type=misc.fs.zfs.config_sync sub_type=synthesized " + "pool_name=%s pool_guid=%lu\n", poolname, poolGUID); + event = Event::CreateEvent(GetFactory(), string(evString)); + if (event != NULL) { + event->Process(); + delete event; + } + } +} + +void +ZfsDaemon::RescanSystem() +{ + struct gmesh mesh; + struct gclass *mp; + struct ggeom *gp; + struct gprovider *pp; + int result; + + /* + * The devdctl system doesn't replay events for new consumers + * of the interface. Emit manufactured DEVFS arrival events + * for any devices that already before we started or during + * periods where we've lost our connection to devd. + */ + result = geom_gettree(&mesh); + if (result != 0) { + syslog(LOG_ERR, "ZfsDaemon::RescanSystem: " + "geom_gettree faild with error %d\n", result); + return; + } + + const string evStart("!system=DEVFS subsystem=CDEV type=CREATE " + "sub_type=synthesized cdev="); + LIST_FOREACH(mp, &mesh.lg_class, lg_class) { + LIST_FOREACH(gp, &mp->lg_geom, lg_geom) { + LIST_FOREACH(pp, &gp->lg_provider, lg_provider) { + Event *event; + + string evString(evStart + pp->lg_name + "\n"); + event = Event::CreateEvent(GetFactory(), + evString); + if (event != NULL) { + if (event->Process()) + SaveEvent(*event); + delete event; + } + } + } + } + geom_deletetree(&mesh); +} + +void +ZfsDaemon::DetectMissedEvents() +{ + do { + PurgeCaseFiles(); + + /* + * Discard any events waiting for us. We don't know + * if they still apply to the current state of the + * system. + */ + FlushEvents(); + + BuildCaseFiles(); + + /* + * If the system state has changed durring our + * interrogation, start over. + */ + } while (s_terminateEventLoop == false && EventsPending()); + + RescanSystem(); +} + +void +ZfsDaemon::EventLoop() +{ + while (s_terminateEventLoop == false) { + struct pollfd fds[2]; + int result; + + if (s_logCaseFiles == true) { + EventList::iterator event(m_unconsumedEvents.begin()); + s_logCaseFiles = false; + CaseFile::LogAll(); + while (event != m_unconsumedEvents.end()) + (*event++)->Log(LOG_INFO); + } + + Callout::ExpireCallouts(); + + /* Wait for data. */ + fds[0].fd = m_devdSockFD; + fds[0].events = POLLIN; + fds[0].revents = 0; + fds[1].fd = s_signalPipeFD[0]; + fds[1].events = POLLIN; + fds[1].revents = 0; + result = poll(fds, NUM_ELEMENTS(fds), /*timeout*/INFTIM); + if (result == -1) { + if (errno == EINTR) + continue; + else + err(1, "Polling for devd events failed"); + } else if (result == 0) { + errx(1, "Unexpected result of 0 from poll. Exiting"); + } + + if ((fds[0].revents & POLLIN) != 0) + ProcessEvents(); + + if ((fds[1].revents & POLLIN) != 0) { + static char discardBuf[128]; + + /* + * This pipe exists just to close the signal + * race. Its contents are of no interest to + * us, but we must ensure that future signals + * have space in the pipe to write. + */ + while (read(s_signalPipeFD[0], discardBuf, + sizeof(discardBuf)) > 0) + ; + } + + if (s_systemRescanRequested == true) { + s_systemRescanRequested = false; + syslog(LOG_INFO, "System Rescan request processed."); + RescanSystem(); + } + + if ((fds[0].revents & POLLERR) != 0) { + syslog(LOG_INFO, "POLLERROR detected on devd socket."); + break; + } + + if ((fds[0].revents & POLLHUP) != 0) { + syslog(LOG_INFO, "POLLHUP detected on devd socket."); + break; + } + } +} +//- ZfsDaemon staic Private Methods -------------------------------------------- +void +ZfsDaemon::InfoSignalHandler(int) +{ + s_logCaseFiles = true; + ZfsDaemon::WakeEventLoop(); +} + +void +ZfsDaemon::RescanSignalHandler(int) +{ + RequestSystemRescan(); +} + +void +ZfsDaemon::QuitSignalHandler(int) +{ + s_terminateEventLoop = true; + ZfsDaemon::WakeEventLoop(); +} + +void +ZfsDaemon::OpenPIDFile() +{ + pid_t otherPID; + + s_pidFH = pidfile_open(s_pidFilePath, 0600, &otherPID); + if (s_pidFH == NULL) { + if (errno == EEXIST) + errx(1, "already running as PID %d. Exiting", otherPID); + warn("cannot open PID file"); + } +} + +void +ZfsDaemon::UpdatePIDFile() +{ + if (s_pidFH != NULL) + pidfile_write(s_pidFH); +} + +void +ZfsDaemon::ClosePIDFile() +{ + if (s_pidFH != NULL) + pidfile_close(s_pidFH); +} + +void +ZfsDaemon::InitializeSyslog() +{ + openlog("zfsd", LOG_NDELAY, LOG_DAEMON); +} + Index: projects/zfsd/head/cddl/usr.sbin/zfsd/zfsd_event.cc =================================================================== --- projects/zfsd/head/cddl/usr.sbin/zfsd/zfsd_event.cc (nonexistent) +++ projects/zfsd/head/cddl/usr.sbin/zfsd/zfsd_event.cc (revision 300335) @@ -0,0 +1,530 @@ +/*- + * Copyright (c) 2011 Spectra Logic Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + * Authors: Justin T. Gibbs (Spectra Logic Corporation) + */ + +/** + * \file zfsd_event.cc + */ +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "callout.h" +#include "vdev_iterator.h" +#include "zfsd_event.h" +#include "case_file.h" +#include "vdev.h" +#include "zfsd.h" +#include "zfsd_exception.h" +#include "zpool_list.h" + +__FBSDID("$FreeBSD$"); +/*============================ Namespace Control =============================*/ +using DevdCtl::Event; +using DevdCtl::Guid; +using DevdCtl::NVPairMap; +using std::stringstream; + +/*=========================== Class Implementations ==========================*/ + +/*-------------------------------- DevfsEvent --------------------------------*/ + +//- DevfsEvent Static Public Methods ------------------------------------------- +Event * +DevfsEvent::Builder(Event::Type type, + NVPairMap &nvPairs, + const string &eventString) +{ + return (new DevfsEvent(type, nvPairs, eventString)); +} + +//- DevfsEvent Static Protected Methods ---------------------------------------- +nvlist_t * +DevfsEvent::ReadLabel(int devFd, bool &inUse, bool °raded) +{ + pool_state_t poolState; + char *poolName; + boolean_t b_inuse; + + inUse = false; + degraded = false; + poolName = NULL; + if (zpool_in_use(g_zfsHandle, devFd, &poolState, + &poolName, &b_inuse) == 0) { + nvlist_t *devLabel; + + inUse = b_inuse == B_TRUE; + if (poolName != NULL) + free(poolName); + + if (zpool_read_label(devFd, &devLabel) != 0 + || devLabel == NULL) + return (NULL); + + try { + Vdev vdev(devLabel); + degraded = vdev.State() != VDEV_STATE_HEALTHY; + return (devLabel); + } catch (ZfsdException &exp) { + string devName = fdevname(devFd); + string devPath = _PATH_DEV + devName; + string context("DevfsEvent::ReadLabel: " + + devPath + ": "); + + exp.GetString().insert(0, context); + exp.Log(); + } + } + return (NULL); +} + +bool +DevfsEvent::OnlineByLabel(const string &devPath, const string& physPath, + nvlist_t *devConfig) +{ + try { + /* + * A device with ZFS label information has been + * inserted. If it matches a device for which we + * have a case, see if we can solve that case. + */ + syslog(LOG_INFO, "Interrogating VDEV label for %s\n", + devPath.c_str()); + Vdev vdev(devConfig); + CaseFile *caseFile(CaseFile::Find(vdev.PoolGUID(), + vdev.GUID())); + if (caseFile != NULL) + return (caseFile->ReEvaluate(devPath, physPath, &vdev)); + + } catch (ZfsdException &exp) { + string context("DevfsEvent::OnlineByLabel: " + devPath + ": "); + + exp.GetString().insert(0, context); + exp.Log(); + } + return (false); +} + +//- DevfsEvent Virtual Public Methods ------------------------------------------ +Event * +DevfsEvent::DeepCopy() const +{ + return (new DevfsEvent(*this)); +} + +bool +DevfsEvent::Process() const +{ + /* + * We are only concerned with newly discovered + * devices that can be ZFS vdevs. + */ + if (Value("type") != "CREATE" || !IsDiskDev()) + return (false); + + /* Log the event since it is of interest. */ + Log(LOG_INFO); + + string devPath; + if (!DevPath(devPath)) + return (false); + + int devFd(open(devPath.c_str(), O_RDONLY)); + if (devFd == -1) + return (false); + + bool inUse; + bool degraded; + nvlist_t *devLabel(ReadLabel(devFd, inUse, degraded)); + + string physPath; + bool havePhysPath(PhysicalPath(physPath)); + + string devName; + DevName(devName); + close(devFd); + + if (inUse && devLabel != NULL) { + OnlineByLabel(devPath, physPath, devLabel); + } else if (degraded) { + syslog(LOG_INFO, "%s is marked degraded. Ignoring " + "as a replace by physical path candidate.\n", + devName.c_str()); + } else if (havePhysPath && IsWholeDev()) { + /* + * TODO: attempt to resolve events using every casefile + * that matches this physpath + */ + CaseFile *caseFile(CaseFile::Find(physPath)); + if (caseFile != NULL) { + syslog(LOG_INFO, + "Found CaseFile(%s:%s:%s) - ReEvaluating\n", + caseFile->PoolGUIDString().c_str(), + caseFile->VdevGUIDString().c_str(), + zpool_state_to_name(caseFile->VdevState(), + VDEV_AUX_NONE)); + caseFile->ReEvaluate(devPath, physPath, /*vdev*/NULL); + } + } + if (devLabel != NULL) + nvlist_free(devLabel); + return (false); +} + +//- DevfsEvent Protected Methods ----------------------------------------------- +DevfsEvent::DevfsEvent(Event::Type type, NVPairMap &nvpairs, + const string &eventString) + : DevdCtl::DevfsEvent(type, nvpairs, eventString) +{ +} + +DevfsEvent::DevfsEvent(const DevfsEvent &src) + : DevdCtl::DevfsEvent::DevfsEvent(src) +{ +} + +/*-------------------------------- GeomEvent --------------------------------*/ + +//- GeomEvent Static Public Methods ------------------------------------------- +Event * +GeomEvent::Builder(Event::Type type, + NVPairMap &nvPairs, + const string &eventString) +{ + return (new GeomEvent(type, nvPairs, eventString)); +} + +//- GeomEvent Virtual Public Methods ------------------------------------------ +Event * +GeomEvent::DeepCopy() const +{ + return (new GeomEvent(*this)); +} + +bool +GeomEvent::Process() const +{ + /* + * We are only concerned with physical path changes, because those can + * be used to satisfy autoreplace operations + */ + if (Value("type") != "GEOM::physpath" || !IsDiskDev()) + return (false); + + /* Log the event since it is of interest. */ + Log(LOG_INFO); + + string devPath; + if (!DevPath(devPath)) + return (false); + + string physPath; + bool havePhysPath(PhysicalPath(physPath)); + + string devName; + DevName(devName); + + if (havePhysPath) { + /* + * TODO: attempt to resolve events using every casefile + * that matches this physpath + */ + CaseFile *caseFile(CaseFile::Find(physPath)); + if (caseFile != NULL) { + syslog(LOG_INFO, + "Found CaseFile(%s:%s:%s) - ReEvaluating\n", + caseFile->PoolGUIDString().c_str(), + caseFile->VdevGUIDString().c_str(), + zpool_state_to_name(caseFile->VdevState(), + VDEV_AUX_NONE)); + caseFile->ReEvaluate(devPath, physPath, /*vdev*/NULL); + } + } + return (false); +} + +//- GeomEvent Protected Methods ----------------------------------------------- +GeomEvent::GeomEvent(Event::Type type, NVPairMap &nvpairs, + const string &eventString) + : DevdCtl::GeomEvent(type, nvpairs, eventString) +{ +} + +GeomEvent::GeomEvent(const GeomEvent &src) + : DevdCtl::GeomEvent::GeomEvent(src) +{ +} + + +/*--------------------------------- ZfsEvent ---------------------------------*/ +//- ZfsEvent Static Public Methods --------------------------------------------- +DevdCtl::Event * +ZfsEvent::Builder(Event::Type type, NVPairMap &nvpairs, + const string &eventString) +{ + return (new ZfsEvent(type, nvpairs, eventString)); +} + +//- ZfsEvent Virtual Public Methods -------------------------------------------- +Event * +ZfsEvent::DeepCopy() const +{ + return (new ZfsEvent(*this)); +} + +bool +ZfsEvent::Process() const +{ + string logstr(""); + + if (!Contains("class") && !Contains("type")) { + syslog(LOG_ERR, + "ZfsEvent::Process: Missing class or type data."); + return (false); + } + + /* On config syncs, replay any queued events first. */ + if (Value("type").find("misc.fs.zfs.config_sync") == 0) { + /* + * Even if saved events are unconsumed the second time + * around, drop them. Any events that still can't be + * consumed are probably referring to vdevs or pools that + * no longer exist. + */ + ZfsDaemon::Get().ReplayUnconsumedEvents(/*discard*/true); + CaseFile::ReEvaluateByGuid(PoolGUID(), *this); + } + + if (Value("type").find("misc.fs.zfs.") == 0) { + /* Configuration changes, resilver events, etc. */ + ProcessPoolEvent(); + return (false); + } + + if (!Contains("pool_guid") || !Contains("vdev_guid")) { + /* Only currently interested in Vdev related events. */ + return (false); + } + + CaseFile *caseFile(CaseFile::Find(PoolGUID(), VdevGUID())); + if (caseFile != NULL) { + Log(LOG_INFO); + syslog(LOG_INFO, "Evaluating existing case file\n"); + caseFile->ReEvaluate(*this); + return (false); + } + + /* Skip events that can't be handled. */ + Guid poolGUID(PoolGUID()); + /* If there are no replicas for a pool, then it's not manageable. */ + if (Value("class").find("fs.zfs.vdev.no_replicas") == 0) { + stringstream msg; + msg << "No replicas available for pool " << poolGUID; + msg << ", ignoring"; + Log(LOG_INFO); + syslog(LOG_INFO, "%s", msg.str().c_str()); + return (false); + } + + /* + * Create a case file for this vdev, and have it + * evaluate the event. + */ + ZpoolList zpl(ZpoolList::ZpoolByGUID, &poolGUID); + if (zpl.empty()) { + stringstream msg; + int priority = LOG_INFO; + msg << "ZfsEvent::Process: Event for unknown pool "; + msg << poolGUID << " "; + msg << "queued"; + Log(LOG_INFO); + syslog(priority, "%s", msg.str().c_str()); + return (true); + } + + nvlist_t *vdevConfig = VdevIterator(zpl.front()).Find(VdevGUID()); + if (vdevConfig == NULL) { + stringstream msg; + int priority = LOG_INFO; + msg << "ZfsEvent::Process: Event for unknown vdev "; + msg << VdevGUID() << " "; + msg << "queued"; + Log(LOG_INFO); + syslog(priority, "%s", msg.str().c_str()); + return (true); + } + + Vdev vdev(zpl.front(), vdevConfig); + caseFile = &CaseFile::Create(vdev); + if (caseFile->ReEvaluate(*this) == false) { + stringstream msg; + int priority = LOG_INFO; + msg << "ZfsEvent::Process: Unconsumed event for vdev("; + msg << zpool_get_name(zpl.front()) << ","; + msg << vdev.GUID() << ") "; + msg << "queued"; + Log(LOG_INFO); + syslog(priority, "%s", msg.str().c_str()); + return (true); + } + return (false); +} + +//- ZfsEvent Protected Methods ------------------------------------------------- +ZfsEvent::ZfsEvent(Event::Type type, NVPairMap &nvpairs, + const string &eventString) + : DevdCtl::ZfsEvent(type, nvpairs, eventString) +{ +} + +ZfsEvent::ZfsEvent(const ZfsEvent &src) + : DevdCtl::ZfsEvent(src) +{ +} + +/* + * Sometimes the kernel won't detach a spare when it is no longer needed. This + * can happen for example if a drive is removed, then either the pool is + * exported or the machine is powered off, then the drive is reinserted, then + * the machine is powered on or the pool is imported. ZFSD must detach these + * spares itself. + */ +void +ZfsEvent::CleanupSpares() const +{ + Guid poolGUID(PoolGUID()); + ZpoolList zpl(ZpoolList::ZpoolByGUID, &poolGUID); + if (!zpl.empty()) { + zpool_handle_t* hdl; + + hdl = zpl.front(); + VdevIterator(hdl).Each(TryDetach, (void*)hdl); + } +} + +void +ZfsEvent::ProcessPoolEvent() const +{ + bool degradedDevice(false); + + /* The pool is destroyed. Discard any open cases */ + if (Value("type") == "misc.fs.zfs.pool_destroy") { + Log(LOG_INFO); + CaseFile::ReEvaluateByGuid(PoolGUID(), *this); + return; + } + + CaseFile *caseFile(CaseFile::Find(PoolGUID(), VdevGUID())); + if (caseFile != NULL) { + if (caseFile->VdevState() != VDEV_STATE_UNKNOWN + && caseFile->VdevState() < VDEV_STATE_HEALTHY) + degradedDevice = true; + + Log(LOG_INFO); + caseFile->ReEvaluate(*this); + } + else if (Value("type") == "misc.fs.zfs.resilver_finish") + { + /* + * It's possible to get a resilver_finish event with no + * corresponding casefile. For example, if a damaged pool were + * exported, repaired, then reimported. + */ + Log(LOG_INFO); + CleanupSpares(); + } + + if (Value("type") == "misc.fs.zfs.vdev_remove" + && degradedDevice == false) { + + /* See if any other cases can make use of this device. */ + Log(LOG_INFO); + ZfsDaemon::RequestSystemRescan(); + } +} + +bool +ZfsEvent::TryDetach(Vdev &vdev, void *cbArg) +{ + /* + * Outline: + * if this device is a spare, and its parent includes one healthy, + * non-spare child, then detach this device. + */ + zpool_handle_t *hdl(static_cast(cbArg)); + + if (vdev.IsSpare()) { + std::list siblings; + std::list::iterator siblings_it; + boolean_t cleanup = B_FALSE; + + Vdev parent = vdev.Parent(); + siblings = parent.Children(); + + /* Determine whether the parent should be cleaned up */ + for (siblings_it = siblings.begin(); + siblings_it != siblings.end(); + siblings_it++) { + Vdev sibling = *siblings_it; + + if (!sibling.IsSpare() && + sibling.State() == VDEV_STATE_HEALTHY) { + cleanup = B_TRUE; + break; + } + } + + if (cleanup) { + syslog(LOG_INFO, "Detaching spare vdev %s from pool %s", + vdev.Path().c_str(), zpool_get_name(hdl)); + zpool_vdev_detach(hdl, vdev.Path().c_str()); + } + + } + + /* Always return false, because there may be other spares to detach */ + return (false); +} Index: projects/zfsd/head/cddl/usr.sbin/zfsd/zfsd_event.h =================================================================== --- projects/zfsd/head/cddl/usr.sbin/zfsd/zfsd_event.h (nonexistent) +++ projects/zfsd/head/cddl/usr.sbin/zfsd/zfsd_event.h (revision 300335) @@ -0,0 +1,168 @@ +/*- + * Copyright (c) 2011 Spectra Logic Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + * Authors: Justin T. Gibbs (Spectra Logic Corporation) + * + * $FreeBSD$ + */ + +/** + * \file dev_ctl_event.h + * + * \brief Class hierarchy used to express events received via + * the devdctl API. + * + * Header requirements: + * #include + * #include + * #include + * + * #include + * #include + */ + +#ifndef _ZFSD_EVENT_H_ +#define _ZFSD_EVENT_H_ + +/*============================ Namespace Control =============================*/ +using std::string; + +/*=========================== Forward Declarations ===========================*/ +struct zpool_handle; +typedef struct zpool_handle zpool_handle_t; + +struct nvlist; +typedef struct nvlist nvlist_t; + +/*============================= Class Definitions ============================*/ +/*-------------------------------- DevfsEvent --------------------------------*/ +class DevfsEvent : public DevdCtl::DevfsEvent +{ +public: + /** Specialized DevdCtlEvent object factory for Devfs events. */ + static BuildMethod Builder; + + virtual DevdCtl::Event *DeepCopy() const; + + /** + * Interpret and perform any actions necessary to + * consume the event. + * \return True if this event should be queued for later reevaluation + */ + virtual bool Process() const; + +protected: + /** + * \brief Read and return label information for a device. + * + * \param devFd The device from which to read ZFS label information. + * \param inUse The device is part of an active or potentially + * active configuration. + * \param degraded The device label indicates the vdev is not healthy. + * + * \return If label information is available, an nvlist describing + * the vdev configuraiton found on the device specified by + * devFd. Otherwise NULL. + */ + static nvlist_t *ReadLabel(int devFd, bool &inUse, bool °raded); + + /** + * Attempt to match the ZFS labeled device at devPath with an active + * CaseFile for a missing vdev. If a CaseFile is found, attempt + * to re-integrate the device with its pool. + * + * \param devPath The devfs path to the potential leaf vdev. + * \param physPath The physical path string reported by the device + * at devPath. + * \param devConfig The ZFS label information found on the device + * at devPath. + * + * \return true if the event that caused the online action can + * be considered consumed. + */ + static bool OnlineByLabel(const string &devPath, + const string& physPath, + nvlist_t *devConfig); + + /** DeepCopy Constructor. */ + DevfsEvent(const DevfsEvent &src); + + /** Constructor */ + DevfsEvent(Type, DevdCtl::NVPairMap &, const string &); +}; + +/*--------------------------------- ZfsEvent ---------------------------------*/ +class ZfsEvent : public DevdCtl::ZfsEvent +{ +public: + /** Specialized DevdCtlEvent object factory for ZFS events. */ + static BuildMethod Builder; + + virtual DevdCtl::Event *DeepCopy() const; + + /** + * Interpret and perform any actions necessary to + * consume the event. + * \return True if this event should be queued for later reevaluation + */ + virtual bool Process() const; + +protected: + /** DeepCopy Constructor. */ + ZfsEvent(const ZfsEvent &src); + + /** Constructor */ + ZfsEvent(Type, DevdCtl::NVPairMap &, const string &); + + /** + * Detach any spares that are no longer needed, but were not + * automatically detached by the kernel + */ + virtual void CleanupSpares() const; + virtual void ProcessPoolEvent() const; + static VdevCallback_t TryDetach; +}; + +class GeomEvent : public DevdCtl::GeomEvent +{ +public: + static BuildMethod Builder; + + virtual DevdCtl::Event *DeepCopy() const; + + virtual bool Process() const; + +protected: + /** DeepCopy Constructor. */ + GeomEvent(const GeomEvent &src); + + /** Constructor */ + GeomEvent(Type, DevdCtl::NVPairMap &, const string &); +}; +#endif /*_ZFSD_EVENT_H_ */ Index: projects/zfsd/head/cddl/usr.sbin/zfsd/callout.cc =================================================================== --- projects/zfsd/head/cddl/usr.sbin/zfsd/callout.cc (nonexistent) +++ projects/zfsd/head/cddl/usr.sbin/zfsd/callout.cc (revision 300335) @@ -0,0 +1,217 @@ +/*- + * Copyright (c) 2011 Spectra Logic Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + * Authors: Justin T. Gibbs (Spectra Logic Corporation) + */ + +/** + * \file callout.cc + * + * \brief Implementation of the Callout class - multi-client + * timer services built on top of the POSIX interval timer. + */ + +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "callout.h" +#include "vdev_iterator.h" +#include "zfsd.h" +#include "zfsd_exception.h" + +std::list Callout::s_activeCallouts; +bool Callout::s_alarmFired(false); + +void +Callout::Init() +{ + signal(SIGALRM, Callout::AlarmSignalHandler); +} + +bool +Callout::Stop() +{ + if (!IsPending()) + return (false); + + for (std::list::iterator it(s_activeCallouts.begin()); + it != s_activeCallouts.end(); it++) { + if (*it != this) + continue; + + it = s_activeCallouts.erase(it); + if (it != s_activeCallouts.end()) { + + /* + * Maintain correct interval for the + * callouts that follow the just removed + * entry. + */ + timeradd(&(*it)->m_interval, &m_interval, + &(*it)->m_interval); + } + break; + } + m_pending = false; + return (true); +} + +bool +Callout::Reset(const timeval &interval, CalloutFunc_t *func, void *arg) +{ + bool cancelled(false); + + if (!timerisset(&interval)) + throw ZfsdException("Callout::Reset: interval of 0"); + + cancelled = Stop(); + + m_interval = interval; + m_func = func; + m_arg = arg; + m_pending = true; + + std::list::iterator it(s_activeCallouts.begin()); + for (; it != s_activeCallouts.end(); it++) { + + if (timercmp(&(*it)->m_interval, &m_interval, <=)) { + /* + * Decrease our interval by those that come + * before us. + */ + timersub(&m_interval, &(*it)->m_interval, &m_interval); + } else { + /* + * Account for the time between the newly + * inserted event and those that follow. + */ + timersub(&(*it)->m_interval, &m_interval, + &(*it)->m_interval); + break; + } + } + s_activeCallouts.insert(it, this); + + + if (s_activeCallouts.front() == this) { + itimerval timerval = { {0, 0}, m_interval }; + + setitimer(ITIMER_REAL, &timerval, NULL); + } + + return (cancelled); +} + +void +Callout::AlarmSignalHandler(int) +{ + s_alarmFired = true; + ZfsDaemon::WakeEventLoop(); +} + +void +Callout::ExpireCallouts() +{ + if (!s_alarmFired) + return; + + s_alarmFired = false; + if (s_activeCallouts.empty()) { + /* Callout removal/SIGALRM race was lost. */ + return; + } + + /* + * Expire the first callout (the one we used to set the + * interval timer) as well as any callouts following that + * expire at the same time (have a zero interval from + * the callout before it). + */ + do { + Callout *cur(s_activeCallouts.front()); + s_activeCallouts.pop_front(); + cur->m_pending = false; + cur->m_func(cur->m_arg); + } while (!s_activeCallouts.empty() + && timerisset(&s_activeCallouts.front()->m_interval) == 0); + + if (!s_activeCallouts.empty()) { + Callout *next(s_activeCallouts.front()); + itimerval timerval = { { 0, 0 }, next->m_interval }; + + setitimer(ITIMER_REAL, &timerval, NULL); + } +} + +timeval +Callout::TimeRemaining() const +{ + /* + * Outline: Add the m_interval for each callout in s_activeCallouts + * ahead of this, except for the first callout. Add to that the result + * of getitimer (That's because the first callout stores its original + * interval setting while the timer is ticking). + */ + itimerval timervalToAlarm; + timeval timeToExpiry; + std::list::iterator it; + + if (!IsPending()) { + timeToExpiry.tv_sec = INT_MAX; + timeToExpiry.tv_usec = 999999; /*maximum normalized value*/ + return (timeToExpiry); + } + + timerclear(&timeToExpiry); + getitimer(ITIMER_REAL, &timervalToAlarm); + timeval& timeToAlarm = timervalToAlarm.it_value; + timeradd(&timeToExpiry, &timeToAlarm, &timeToExpiry); + + it =s_activeCallouts.begin(); + it++; /*skip the first callout in the list*/ + for (; it != s_activeCallouts.end(); it++) { + timeradd(&timeToExpiry, &(*it)->m_interval, &timeToExpiry); + if ((*it) == this) + break; + } + return (timeToExpiry); +} Index: projects/zfsd/head/cddl/usr.sbin/zfsd/case_file.h =================================================================== --- projects/zfsd/head/cddl/usr.sbin/zfsd/case_file.h (nonexistent) +++ projects/zfsd/head/cddl/usr.sbin/zfsd/case_file.h (revision 300335) @@ -0,0 +1,426 @@ +/*- + * Copyright (c) 2011 Spectra Logic Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + * Authors: Justin T. Gibbs (Spectra Logic Corporation) + * + * $FreeBSD$ + */ + +/** + * \file case_file.h + * + * CaseFile objects aggregate vdev faults that may require ZFSD action + * in order to maintain the health of a ZFS pool. + * + * Header requirements: + * + * #include + * + * #include "callout.h" + * #include "zfsd_event.h" + */ +#ifndef _CASE_FILE_H_ +#define _CASE_FILE_H_ + +/*=========================== Forward Declarations ===========================*/ +class CaseFile; +class Vdev; + +/*============================= Class Definitions ============================*/ +/*------------------------------- CaseFileList -------------------------------*/ +/** + * CaseFileList is a specialization of the standard list STL container. + */ +typedef std::list< CaseFile *> CaseFileList; + +/*--------------------------------- CaseFile ---------------------------------*/ +/** + * A CaseFile object is instantiated anytime a vdev for an active pool + * experiences an I/O error, is faulted by ZFS, or is determined to be + * missing/removed. + * + * A vdev may have at most one CaseFile. + * + * CaseFiles are retired when a vdev leaves an active pool configuration + * or an action is taken to resolve the issues recorded in the CaseFile. + * + * Logging a case against a vdev does not imply that an immediate action + * to resolve a fault is required or even desired. For example, a CaseFile + * must accumulate a number of I/O errors in order to flag a device as + * degraded. + * + * Vdev I/O errors are not recorded in ZFS label inforamation. For this + * reasons, CaseFile%%s with accumulated I/O error events are serialized + * to the file system so that they survive across boots. Currently all + * other fault types can be reconstructed from ZFS label information, so + * CaseFile%%s for missing, faulted, or degradded members are just recreated + * at ZFSD startup instead of being deserialized from the file system. + */ +class CaseFile +{ +public: + /** + * \brief Find a CaseFile object by a vdev's pool/vdev GUID tuple. + * + * \param poolGUID Pool GUID for the vdev of the CaseFile to find. + * \param vdevGUID Vdev GUID for the vdev of the CaseFile to find. + * + * \return If found, a pointer to a valid CaseFile object. + * Otherwise NULL. + */ + static CaseFile *Find(DevdCtl::Guid poolGUID, DevdCtl::Guid vdevGUID); + + /** + * \brief Find a CaseFile object by a vdev's current/last known + * physical path. + * + * \param physPath Physical path of the vdev of the CaseFile to find. + * + * \return If found, a pointer to a valid CaseFile object. + * Otherwise NULL. + */ + static CaseFile *Find(const string &physPath); + + /** + * \brief ReEvaluate all open cases whose pool guid matches the argument + * + * \param poolGUID Only reevaluate cases for this pool + * \param event Try to consume this event with the casefile + */ + static void ReEvaluateByGuid(DevdCtl::Guid poolGUID, + const ZfsEvent &event); + + /** + * \brief Create or return an existing active CaseFile for the + * specified vdev. + * + * \param vdev The vdev object for which to find/create a CaseFile. + * + * \return A reference to a valid CaseFile object. + */ + static CaseFile &Create(Vdev &vdev); + + /** + * \brief Deserialize all serialized CaseFile objects found in + * the file system. + */ + static void DeSerialize(); + + /** + * \brief Emit syslog data on all active CaseFile%%s in the system. + */ + static void LogAll(); + + /** + * \brief Destroy the in-core cache of CaseFile data. + * + * This routine does not disturb the on disk, serialized, CaseFile + * data. + */ + static void PurgeAll(); + + DevdCtl::Guid PoolGUID() const; + DevdCtl::Guid VdevGUID() const; + vdev_state VdevState() const; + const string &PoolGUIDString() const; + const string &VdevGUIDString() const; + const string &PhysicalPath() const; + + /** + * \brief Attempt to resolve this CaseFile using the disk + * resource at the given device/physical path/vdev object + * tuple. + * + * \param devPath The devfs path for the disk resource. + * \param physPath The physical path information reported by + * the disk resource. + * \param vdev If the disk contains ZFS label information, + * a pointer to the disk label's vdev object + * data. Otherwise NULL. + * + * \return True if this event was consumed by this CaseFile. + */ + bool ReEvaluate(const string &devPath, const string &physPath, + Vdev *vdev); + + /** + * \brief Update this CaseFile in light of the provided ZfsEvent. + * + * Must be virtual so it can be overridden in the unit tests + * + * \param event The ZfsEvent to evaluate. + * + * \return True if this event was consumed by this CaseFile. + */ + virtual bool ReEvaluate(const ZfsEvent &event); + + /** + * \brief Register an itimer callout for the given event, if necessary + */ + virtual void RegisterCallout(const DevdCtl::Event &event); + + /** + * \brief Close a case if it is no longer relevant. + * + * This method deals with cases tracking soft errors. Soft errors + * will be discarded should a remove event occur within a short period + * of the soft errors being reported. We also discard the events + * if the vdev is marked degraded or failed. + * + * \return True if the case is closed. False otherwise. + */ + bool CloseIfSolved(); + + /** + * \brief Emit data about this CaseFile via syslog(3). + */ + void Log(); + + /** + * \brief Whether we should degrade this vdev + */ + bool ShouldDegrade() const; + + /** + * \brief Whether we should fault this vdev + */ + bool ShouldFault() const; + +protected: + enum { + /** + * The number of soft errors on a vdev required + * to transition a vdev from healthy to degrated + * status. + */ + ZFS_DEGRADE_IO_COUNT = 50 + }; + + static CalloutFunc_t OnGracePeriodEnded; + + /** + * \brief scandir(3) filter function used to find files containing + * serialized CaseFile data. + * + * \param dirEntry Directory entry for the file to filter. + * + * \return Non-zero for a file to include in the selection, + * otherwise 0. + */ + static int DeSerializeSelector(const struct dirent *dirEntry); + + /** + * \brief Given the name of a file containing serialized events from a + * CaseFile object, create/update an in-core CaseFile object + * representing the serialized data. + * + * \param fileName The name of a file containing serialized events + * from a CaseFile object. + */ + static void DeSerializeFile(const char *fileName); + + /** Constructor. */ + CaseFile(const Vdev &vdev); + + /** + * Destructor. + * Must be virtual so it can be subclassed in the unit tests + */ + virtual ~CaseFile(); + + /** + * \brief Reload state for the vdev associated with this CaseFile. + * + * \return True if the refresh was successful. False if the system + * has no record of the pool or vdev for this CaseFile. + */ + virtual bool RefreshVdevState(); + + /** + * \brief Free all events in the m_events list. + */ + void PurgeEvents(); + + /** + * \brief Free all events in the m_tentativeEvents list. + */ + void PurgeTentativeEvents(); + + /** + * \brief Commit to file system storage. + */ + void Serialize(); + + /** + * \brief Retrieve event data from a serialization stream. + * + * \param caseStream The serializtion stream to parse. + */ + void DeSerialize(std::ifstream &caseStream); + + /** + * \brief Serializes the supplied event list and writes it to fd + * + * \param prefix If not NULL, this prefix will be prepended to + * every event in the file. + */ + void SerializeEvList(const DevdCtl::EventList events, int fd, + const char* prefix=NULL) const; + + /** + * \brief Unconditionally close a CaseFile. + */ + virtual void Close(); + + /** + * \brief Callout callback invoked when the remove timer grace + * period expires. + * + * If no remove events are received prior to the grace period + * firing, then any tentative events are promoted and counted + * against the health of the vdev. + */ + void OnGracePeriodEnded(); + + /** + * \brief Attempt to activate a spare on this case's pool. + * + * Call this whenever a pool becomes degraded. It will look for any + * spare devices and activate one to replace the casefile's vdev. It + * will _not_ close the casefile; that should only happen when the + * missing drive is replaced or the user promotes the spare. + * + * \return True if a spare was activated + */ + bool ActivateSpare(); + + /** + * \brief replace a pool's vdev with another + * + * \param vdev_type The type of the new vdev. Usually either + * VDEV_TYPE_DISK or VDEV_TYPE_FILE + * \param path The file system path to the new vdev + * \param isspare Whether the new vdev is a spare + * + * \return true iff the replacement was successful + */ + bool Replace(const char* vdev_type, const char* path, bool isspare); + + /** + * \brief Which vdev, if any, is replacing ours. + * + * \param zhp Pool handle state from the caller context + * + * \return the vdev that is currently replacing ours, + * or NonexistentVdev if there isn't one. + */ + Vdev BeingReplacedBy(zpool_handle_t *zhp); + + /** + * \brief All CaseFiles being tracked by ZFSD. + */ + static CaseFileList s_activeCases; + + /** + * \brief The file system path to serialized CaseFile data. + */ + static const string s_caseFilePath; + + /** + * \brief The time ZFSD waits before promoting a tentative event + * into a permanent event. + */ + static const timeval s_removeGracePeriod; + + /** + * \brief A list of soft error events counted against the health of + * a vdev. + */ + DevdCtl::EventList m_events; + + /** + * \brief A list of soft error events waiting for a grace period + * expiration before being counted against the health of + * a vdev. + */ + DevdCtl::EventList m_tentativeEvents; + + DevdCtl::Guid m_poolGUID; + DevdCtl::Guid m_vdevGUID; + vdev_state m_vdevState; + string m_poolGUIDString; + string m_vdevGUIDString; + string m_vdevPhysPath; + + /** + * \brief Callout activated when a grace period + */ + Callout m_tentativeTimer; + +private: + nvlist_t *CaseVdev(zpool_handle_t *zhp) const; +}; + +inline DevdCtl::Guid +CaseFile::PoolGUID() const +{ + return (m_poolGUID); +} + +inline DevdCtl::Guid +CaseFile::VdevGUID() const +{ + return (m_vdevGUID); +} + +inline vdev_state +CaseFile::VdevState() const +{ + return (m_vdevState); +} + +inline const string & +CaseFile::PoolGUIDString() const +{ + return (m_poolGUIDString); +} + +inline const string & +CaseFile::VdevGUIDString() const +{ + return (m_vdevGUIDString); +} + +inline const string & +CaseFile::PhysicalPath() const +{ + return (m_vdevPhysPath); +} + +#endif /* _CASE_FILE_H_ */ Index: projects/zfsd/head/cddl/usr.sbin/zfsd/tests/zfsd_unittest.cc =================================================================== --- projects/zfsd/head/cddl/usr.sbin/zfsd/tests/zfsd_unittest.cc (nonexistent) +++ projects/zfsd/head/cddl/usr.sbin/zfsd/tests/zfsd_unittest.cc (revision 300335) @@ -0,0 +1,771 @@ +/*- + * Copyright (c) 2012, 2013 Spectra Logic Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + * Authors: Alan Somers (Spectra Logic Corporation) + */ +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libmocks.h" + +__FBSDID("$FreeBSD$"); + +/*================================== Macros ==================================*/ +#define NUM_ELEMENTS(x) (sizeof(x) / sizeof(*x)) + +/*============================ Namespace Control =============================*/ +using std::string; +using std::stringstream; + +using DevdCtl::Event; +using DevdCtl::EventBuffer; +using DevdCtl::EventFactory; +using DevdCtl::EventList; +using DevdCtl::Guid; +using DevdCtl::NVPairMap; + +/* redefine zpool_handle here because libzfs_impl.h is not includable */ +struct zpool_handle +{ + libzfs_handle_t *zpool_hdl; + zpool_handle_t *zpool_next; + char zpool_name[ZPOOL_MAXNAMELEN]; + int zpool_state; + size_t zpool_config_size; + nvlist_t *zpool_config; + nvlist_t *zpool_old_config; + nvlist_t *zpool_props; + diskaddr_t zpool_start_block; +}; + +class MockZfsEvent : public ZfsEvent +{ +public: + MockZfsEvent(Event::Type, NVPairMap&, const string&); + virtual ~MockZfsEvent() {} + + static BuildMethod MockZfsEventBuilder; + + MOCK_CONST_METHOD0(ProcessPoolEvent, void()); + + static EventFactory::Record s_buildRecords[]; +}; + +EventFactory::Record MockZfsEvent::s_buildRecords[] = +{ + { Event::NOTIFY, "ZFS", &MockZfsEvent::MockZfsEventBuilder } +}; + +MockZfsEvent::MockZfsEvent(Event::Type type, NVPairMap& map, + const string& str) + : ZfsEvent(type, map, str) +{ +} + +Event * +MockZfsEvent::MockZfsEventBuilder(Event::Type type, + NVPairMap &nvpairs, + const string &eventString) +{ + return (new MockZfsEvent(type, nvpairs, eventString)); +} + +/* + * A dummy Vdev class used for testing other classes + */ +class MockVdev : public Vdev +{ +public: + MockVdev(nvlist_t *vdevConfig); + virtual ~MockVdev() {} + + MOCK_CONST_METHOD0(GUID, Guid()); + MOCK_CONST_METHOD0(PoolGUID, Guid()); + MOCK_CONST_METHOD0(State, vdev_state()); + MOCK_CONST_METHOD0(PhysicalPath, string()); +}; + +MockVdev::MockVdev(nvlist_t *vdevConfig) + : Vdev(vdevConfig) +{ +} + +/* + * A CaseFile class with side effects removed, for testing + */ +class TestableCaseFile : public CaseFile +{ +public: + static TestableCaseFile &Create(Vdev &vdev); + TestableCaseFile(Vdev &vdev); + virtual ~TestableCaseFile() {} + + MOCK_METHOD0(Close, void()); + MOCK_METHOD1(RegisterCallout, void(const Event &event)); + MOCK_METHOD0(RefreshVdevState, bool()); + MOCK_METHOD1(ReEvaluate, bool(const ZfsEvent &event)); + + bool RealReEvaluate(const ZfsEvent &event) + { + return (CaseFile::ReEvaluate(event)); + } + + /* + * This splices the event lists, a procedure that would normally be done + * by OnGracePeriodEnded, but we don't necessarily call that in the + * unit tests + */ + void SpliceEvents(); + + /* + * Used by some of our expectations. CaseFile does not publicize this + */ + static int getActiveCases() + { + return (s_activeCases.size()); + } +}; + +TestableCaseFile::TestableCaseFile(Vdev &vdev) + : CaseFile(vdev) +{ +} + +TestableCaseFile & +TestableCaseFile::Create(Vdev &vdev) +{ + TestableCaseFile *newCase; + newCase = new TestableCaseFile(vdev); + return (*newCase); +} + +void +TestableCaseFile::SpliceEvents() +{ + m_events.splice(m_events.begin(), m_tentativeEvents); +} + + +/* + * Test class ZfsdException + */ +class ZfsdExceptionTest : public ::testing::Test +{ +protected: + virtual void SetUp() + { + ASSERT_EQ(0, nvlist_alloc(&poolConfig, NV_UNIQUE_NAME, 0)); + ASSERT_EQ(0, nvlist_add_string(poolConfig, + ZPOOL_CONFIG_POOL_NAME, "unit_test_pool")); + ASSERT_EQ(0, nvlist_add_uint64(poolConfig, + ZPOOL_CONFIG_POOL_GUID, 0x1234)); + + ASSERT_EQ(0, nvlist_alloc(&vdevConfig, NV_UNIQUE_NAME, 0)); + ASSERT_EQ(0, nvlist_add_uint64(vdevConfig, + ZPOOL_CONFIG_GUID, 0x5678)); + bzero(&poolHandle, sizeof(poolHandle)); + poolHandle.zpool_config = poolConfig; + } + + virtual void TearDown() + { + nvlist_free(poolConfig); + nvlist_free(vdevConfig); + } + + nvlist_t *poolConfig; + nvlist_t *vdevConfig; + zpool_handle_t poolHandle; +}; + +TEST_F(ZfsdExceptionTest, StringConstructorNull) +{ + ZfsdException ze(""); + EXPECT_STREQ("", ze.GetString().c_str()); +} + +TEST_F(ZfsdExceptionTest, StringConstructorFormatted) +{ + ZfsdException ze(" %d %s", 55, "hello world"); + EXPECT_STREQ(" 55 hello world", ze.GetString().c_str()); +} + +TEST_F(ZfsdExceptionTest, LogSimple) +{ + ZfsdException ze("unit test w/o vdev or pool"); + ze.Log(); + EXPECT_EQ(LOG_ERR, syslog_last_priority); + EXPECT_STREQ("unit test w/o vdev or pool\n", syslog_last_message); +} + +TEST_F(ZfsdExceptionTest, Pool) +{ + const char msg[] = "Exception with pool name"; + char expected[4096]; + sprintf(expected, "Pool unit_test_pool: %s\n", msg); + ZfsdException ze(poolConfig, msg); + ze.Log(); + EXPECT_STREQ(expected, syslog_last_message); +} + +TEST_F(ZfsdExceptionTest, PoolHandle) +{ + const char msg[] = "Exception with pool handle"; + char expected[4096]; + sprintf(expected, "Pool unit_test_pool: %s\n", msg); + ZfsdException ze(&poolHandle, msg); + ze.Log(); + EXPECT_STREQ(expected, syslog_last_message); +} + +/* + * Test class Vdev + */ +class VdevTest : public ::testing::Test +{ +protected: + virtual void SetUp() + { + ASSERT_EQ(0, nvlist_alloc(&m_poolConfig, NV_UNIQUE_NAME, 0)); + ASSERT_EQ(0, nvlist_add_uint64(m_poolConfig, + ZPOOL_CONFIG_POOL_GUID, + 0x1234)); + + ASSERT_EQ(0, nvlist_alloc(&m_vdevConfig, NV_UNIQUE_NAME, 0)); + ASSERT_EQ(0, nvlist_add_uint64(m_vdevConfig, ZPOOL_CONFIG_GUID, + 0x5678)); + } + + virtual void TearDown() + { + nvlist_free(m_poolConfig); + nvlist_free(m_vdevConfig); + } + + nvlist_t *m_poolConfig; + nvlist_t *m_vdevConfig; +}; + + +TEST_F(VdevTest, StateFromConfig) +{ + vdev_stat_t vs; + + vs.vs_state = VDEV_STATE_OFFLINE; + + ASSERT_EQ(0, nvlist_add_uint64_array(m_vdevConfig, + ZPOOL_CONFIG_VDEV_STATS, + (uint64_t*)&vs, + sizeof(vs) / sizeof(uint64_t))); + + Vdev vdev(m_poolConfig, m_vdevConfig); + + EXPECT_EQ(VDEV_STATE_OFFLINE, vdev.State()); +} + +TEST_F(VdevTest, StateFaulted) +{ + ASSERT_EQ(0, nvlist_add_uint64(m_vdevConfig, ZPOOL_CONFIG_FAULTED, 1)); + + Vdev vdev(m_poolConfig, m_vdevConfig); + + EXPECT_EQ(VDEV_STATE_FAULTED, vdev.State()); +} + +/* + * Test that we can construct a Vdev from the label information that is stored + * on an available spare drive + */ +TEST_F(VdevTest, ConstructAvailSpare) +{ + nvlist_t *labelConfig; + + ASSERT_EQ(0, nvlist_alloc(&labelConfig, NV_UNIQUE_NAME, 0)); + ASSERT_EQ(0, nvlist_add_uint64(labelConfig, ZPOOL_CONFIG_GUID, + 1948339428197961030)); + ASSERT_EQ(0, nvlist_add_uint64(labelConfig, ZPOOL_CONFIG_POOL_STATE, + POOL_STATE_SPARE)); + + EXPECT_NO_THROW(Vdev vdev(labelConfig)); + + nvlist_free(labelConfig); +} + +/* Available spares will always show the HEALTHY state */ +TEST_F(VdevTest, AvailSpareState) { + nvlist_t *labelConfig; + + ASSERT_EQ(0, nvlist_alloc(&labelConfig, NV_UNIQUE_NAME, 0)); + ASSERT_EQ(0, nvlist_add_uint64(labelConfig, ZPOOL_CONFIG_GUID, + 1948339428197961030)); + ASSERT_EQ(0, nvlist_add_uint64(labelConfig, ZPOOL_CONFIG_POOL_STATE, + POOL_STATE_SPARE)); + + Vdev vdev(labelConfig); + EXPECT_EQ(VDEV_STATE_HEALTHY, vdev.State()); + + nvlist_free(labelConfig); +} + +/* Test the Vdev::IsSpare method */ +TEST_F(VdevTest, IsSpare) { + Vdev notSpare(m_poolConfig, m_vdevConfig); + EXPECT_EQ(false, notSpare.IsSpare()); + + ASSERT_EQ(0, nvlist_add_uint64(m_vdevConfig, ZPOOL_CONFIG_IS_SPARE, 1)); + Vdev isSpare(m_poolConfig, m_vdevConfig); + EXPECT_EQ(true, isSpare.IsSpare()); +} + +/* + * Test class ZFSEvent + */ +class ZfsEventTest : public ::testing::Test +{ +protected: + virtual void SetUp() + { + m_eventFactory = new EventFactory(); + m_eventFactory->UpdateRegistry(MockZfsEvent::s_buildRecords, + NUM_ELEMENTS(MockZfsEvent::s_buildRecords)); + + m_event = NULL; + } + + virtual void TearDown() + { + delete m_eventFactory; + delete m_event; + } + + EventFactory *m_eventFactory; + Event *m_event; +}; + +TEST_F(ZfsEventTest, ProcessPoolEventGetsCalled) +{ + string evString("!system=ZFS " + "subsystem=ZFS " + "type=misc.fs.zfs.vdev_remove " + "pool_name=foo " + "pool_guid=9756779504028057996 " + "vdev_guid=1631193447431603339 " + "vdev_path=/dev/da1 " + "timestamp=1348871594"); + m_event = Event::CreateEvent(*m_eventFactory, evString); + MockZfsEvent *mock_event = static_cast(m_event); + + EXPECT_CALL(*mock_event, ProcessPoolEvent()).Times(1); + mock_event->Process(); +} + +/* + * Test class CaseFile + */ + +class CaseFileTest : public ::testing::Test +{ +protected: + virtual void SetUp() + { + m_eventFactory = new EventFactory(); + m_eventFactory->UpdateRegistry(MockZfsEvent::s_buildRecords, + NUM_ELEMENTS(MockZfsEvent::s_buildRecords)); + + m_event = NULL; + + nvlist_alloc(&m_vdevConfig, NV_UNIQUE_NAME, 0); + ASSERT_EQ(0, nvlist_add_uint64(m_vdevConfig, + ZPOOL_CONFIG_GUID, 0xbeef)); + m_vdev = new MockVdev(m_vdevConfig); + ON_CALL(*m_vdev, GUID()) + .WillByDefault(::testing::Return(Guid(123))); + ON_CALL(*m_vdev, PoolGUID()) + .WillByDefault(::testing::Return(Guid(456))); + ON_CALL(*m_vdev, State()) + .WillByDefault(::testing::Return(VDEV_STATE_HEALTHY)); + m_caseFile = &TestableCaseFile::Create(*m_vdev); + ON_CALL(*m_caseFile, ReEvaluate(::testing::_)) + .WillByDefault(::testing::Invoke(m_caseFile, &TestableCaseFile::RealReEvaluate)); + return; + } + + virtual void TearDown() + { + delete m_caseFile; + nvlist_free(m_vdevConfig); + delete m_vdev; + delete m_event; + delete m_eventFactory; + } + + nvlist_t *m_vdevConfig; + MockVdev *m_vdev; + TestableCaseFile *m_caseFile; + Event *m_event; + EventFactory *m_eventFactory; +}; + +/* + * A Vdev with no events should not be degraded or faulted + */ +TEST_F(CaseFileTest, HealthyVdev) +{ + EXPECT_FALSE(m_caseFile->ShouldDegrade()); + EXPECT_FALSE(m_caseFile->ShouldFault()); +} + +/* + * A Vdev with only one event should not be degraded or faulted + * For performance reasons, RefreshVdevState should not be called. + */ +TEST_F(CaseFileTest, HealthyishVdev) +{ + string evString("!system=ZFS " + "class=ereport.fs.zfs.io " + "ena=12091638756982918145 " + "parent_guid=13237004955564865395 " + "parent_type=raidz " + "pool=testpool.4415 " + "pool_context=0 " + "pool_failmode=wait " + "pool_guid=456 " + "subsystem=ZFS " + "timestamp=1348867914 " + "type=ereport.fs.zfs.io " + "vdev_guid=123 " + "vdev_path=/dev/da400 " + "vdev_type=disk " + "zio_blkid=622 " + "zio_err=1 " + "zio_level=-2 " + "zio_object=0 " + "zio_objset=37 " + "zio_offset=25598976 " + "zio_size=1024"); + m_event = Event::CreateEvent(*m_eventFactory, evString); + ZfsEvent *zfs_event = static_cast(m_event); + + EXPECT_CALL(*m_caseFile, RefreshVdevState()) + .Times(::testing::Exactly(0)); + EXPECT_TRUE(m_caseFile->ReEvaluate(*zfs_event)); + EXPECT_FALSE(m_caseFile->ShouldDegrade()); + EXPECT_FALSE(m_caseFile->ShouldFault()); +} + +/* The case file should be closed when its pool is destroyed */ +TEST_F(CaseFileTest, PoolDestroy) +{ + string evString("!system=ZFS " + "pool_name=testpool.4415 " + "pool_guid=456 " + "subsystem=ZFS " + "timestamp=1348867914 " + "type=misc.fs.zfs.pool_destroy "); + m_event = Event::CreateEvent(*m_eventFactory, evString); + ZfsEvent *zfs_event = static_cast(m_event); + EXPECT_CALL(*m_caseFile, Close()); + EXPECT_TRUE(m_caseFile->ReEvaluate(*zfs_event)); +} + +/* + * A Vdev with a very large number of IO errors should fault + * For performance reasons, RefreshVdevState should be called at most once + */ +TEST_F(CaseFileTest, VeryManyIOErrors) +{ + EXPECT_CALL(*m_caseFile, RefreshVdevState()) + .Times(::testing::AtMost(1)) + .WillRepeatedly(::testing::Return(true)); + + for(int i=0; i<100; i++) { + stringstream evStringStream; + evStringStream << + "!system=ZFS " + "class=ereport.fs.zfs.io " + "ena=12091638756982918145 " + "parent_guid=13237004955564865395 " + "parent_type=raidz " + "pool=testpool.4415 " + "pool_context=0 " + "pool_failmode=wait " + "pool_guid=456 " + "subsystem=ZFS " + "timestamp="; + evStringStream << i << " "; + evStringStream << + "type=ereport.fs.zfs.io " + "vdev_guid=123 " + "vdev_path=/dev/da400 " + "vdev_type=disk " + "zio_blkid=622 " + "zio_err=1 " + "zio_level=-2 " + "zio_object=0 " + "zio_objset=37 " + "zio_offset=25598976 " + "zio_size=1024"; + Event *event(Event::CreateEvent(*m_eventFactory, + evStringStream.str())); + ZfsEvent *zfs_event = static_cast(event); + EXPECT_TRUE(m_caseFile->ReEvaluate(*zfs_event)); + delete event; + } + + m_caseFile->SpliceEvents(); + EXPECT_FALSE(m_caseFile->ShouldDegrade()); + EXPECT_TRUE(m_caseFile->ShouldFault()); +} + +/* + * A Vdev with a very large number of checksum errors should degrade + * For performance reasons, RefreshVdevState should be called at most once + */ +TEST_F(CaseFileTest, VeryManyChecksumErrors) +{ + EXPECT_CALL(*m_caseFile, RefreshVdevState()) + .Times(::testing::AtMost(1)) + .WillRepeatedly(::testing::Return(true)); + + for(int i=0; i<100; i++) { + stringstream evStringStream; + evStringStream << + "!system=ZFS " + "bad_cleared_bits=03000000000000803f50b00000000000 " + "bad_range_clears=0000000e " + "bad_range_sets=00000000 " + "bad_ranges=0000000000000010 " + "bad_ranges_min_gap=8 " + "bad_set_bits=00000000000000000000000000000000 " + "class=ereport.fs.zfs.checksum " + "ena=12272856582652437505 " + "parent_guid=5838204195352909894 " + "parent_type=raidz pool=testpool.7640 " + "pool_context=0 " + "pool_failmode=wait " + "pool_guid=456 " + "subsystem=ZFS timestamp="; + evStringStream << i << " "; + evStringStream << + "type=ereport.fs.zfs.checksum " + "vdev_guid=123 " + "vdev_path=/mnt/tmp/file1.7702 " + "vdev_type=file " + "zio_blkid=0 " + "zio_err=0 " + "zio_level=0 " + "zio_object=3 " + "zio_objset=0 " + "zio_offset=16896 " + "zio_size=512"; + Event *event(Event::CreateEvent(*m_eventFactory, + evStringStream.str())); + ZfsEvent *zfs_event = static_cast(event); + EXPECT_TRUE(m_caseFile->ReEvaluate(*zfs_event)); + delete event; + } + + m_caseFile->SpliceEvents(); + EXPECT_TRUE(m_caseFile->ShouldDegrade()); + EXPECT_FALSE(m_caseFile->ShouldFault()); +} + +/* + * Test CaseFile::ReEvaluateByGuid + */ +class ReEvaluateByGuidTest : public ::testing::Test +{ +protected: + virtual void SetUp() + { + m_eventFactory = new EventFactory(); + m_eventFactory->UpdateRegistry(MockZfsEvent::s_buildRecords, + NUM_ELEMENTS(MockZfsEvent::s_buildRecords)); + m_event = Event::CreateEvent(*m_eventFactory, s_evString); + nvlist_alloc(&m_vdevConfig, NV_UNIQUE_NAME, 0); + ASSERT_EQ(0, nvlist_add_uint64(m_vdevConfig, + ZPOOL_CONFIG_GUID, 0xbeef)); + m_vdev456 = new ::testing::NiceMock(m_vdevConfig); + m_vdev789 = new ::testing::NiceMock(m_vdevConfig); + ON_CALL(*m_vdev456, GUID()) + .WillByDefault(::testing::Return(Guid(123))); + ON_CALL(*m_vdev456, PoolGUID()) + .WillByDefault(::testing::Return(Guid(456))); + ON_CALL(*m_vdev456, State()) + .WillByDefault(::testing::Return(VDEV_STATE_HEALTHY)); + ON_CALL(*m_vdev789, GUID()) + .WillByDefault(::testing::Return(Guid(123))); + ON_CALL(*m_vdev789, PoolGUID()) + .WillByDefault(::testing::Return(Guid(789))); + ON_CALL(*m_vdev789, State()) + .WillByDefault(::testing::Return(VDEV_STATE_HEALTHY)); + m_caseFile456 = NULL; + m_caseFile789 = NULL; + return; + } + + virtual void TearDown() + { + delete m_caseFile456; + delete m_caseFile789; + nvlist_free(m_vdevConfig); + delete m_vdev456; + delete m_vdev789; + delete m_event; + delete m_eventFactory; + } + + static string s_evString; + nvlist_t *m_vdevConfig; + ::testing::NiceMock *m_vdev456; + ::testing::NiceMock *m_vdev789; + TestableCaseFile *m_caseFile456; + TestableCaseFile *m_caseFile789; + Event *m_event; + EventFactory *m_eventFactory; +}; + +string ReEvaluateByGuidTest::s_evString( + "!system=ZFS " + "pool_guid=16271873792808333580 " + "pool_name=foo " + "subsystem=ZFS " + "timestamp=1360620391 " + "type=misc.fs.zfs.config_sync"); + + +/* + * Test the ReEvaluateByGuid method on an empty list of casefiles. + * We must create one event, even though it never gets used, because it will + * be passed by reference to ReEvaluateByGuid + */ +TEST_F(ReEvaluateByGuidTest, ReEvaluateByGuid_empty) +{ + ZfsEvent *zfs_event = static_cast(m_event); + + EXPECT_EQ(0, TestableCaseFile::getActiveCases()); + CaseFile::ReEvaluateByGuid(Guid(456), *zfs_event); + EXPECT_EQ(0, TestableCaseFile::getActiveCases()); +} + +/* + * Test the ReEvaluateByGuid method on a list of CaseFiles that contains only + * one CaseFile, which doesn't match the criteria + */ +TEST_F(ReEvaluateByGuidTest, ReEvaluateByGuid_oneFalse) +{ + m_caseFile456 = &TestableCaseFile::Create(*m_vdev456); + ZfsEvent *zfs_event = static_cast(m_event); + + EXPECT_EQ(1, TestableCaseFile::getActiveCases()); + EXPECT_CALL(*m_caseFile456, ReEvaluate(::testing::_)) + .Times(::testing::Exactly(0)); + CaseFile::ReEvaluateByGuid(Guid(789), *zfs_event); + EXPECT_EQ(1, TestableCaseFile::getActiveCases()); +} + +/* + * Test the ReEvaluateByGuid method on a list of CaseFiles that contains only + * one CaseFile, which does match the criteria + */ +TEST_F(ReEvaluateByGuidTest, ReEvaluateByGuid_oneTrue) +{ + m_caseFile456 = &TestableCaseFile::Create(*m_vdev456); + ZfsEvent *zfs_event = static_cast(m_event); + + EXPECT_EQ(1, TestableCaseFile::getActiveCases()); + EXPECT_CALL(*m_caseFile456, ReEvaluate(::testing::_)) + .Times(::testing::Exactly(1)) + .WillRepeatedly(::testing::Return(false)); + CaseFile::ReEvaluateByGuid(Guid(456), *zfs_event); + EXPECT_EQ(1, TestableCaseFile::getActiveCases()); +} + +/* + * Test the ReEvaluateByGuid method on a long list of CaseFiles that contains a + * few cases which meet the criteria + */ +TEST_F(ReEvaluateByGuidTest, ReEvaluateByGuid_five) +{ + TestableCaseFile *CaseFile1 = &TestableCaseFile::Create(*m_vdev456); + TestableCaseFile *CaseFile2 = &TestableCaseFile::Create(*m_vdev789); + TestableCaseFile *CaseFile3 = &TestableCaseFile::Create(*m_vdev456); + TestableCaseFile *CaseFile4 = &TestableCaseFile::Create(*m_vdev789); + TestableCaseFile *CaseFile5 = &TestableCaseFile::Create(*m_vdev789); + ZfsEvent *zfs_event = static_cast(m_event); + + EXPECT_EQ(5, TestableCaseFile::getActiveCases()); + EXPECT_CALL(*CaseFile1, ReEvaluate(::testing::_)) + .Times(::testing::Exactly(1)) + .WillRepeatedly(::testing::Return(false)); + EXPECT_CALL(*CaseFile3, ReEvaluate(::testing::_)) + .Times(::testing::Exactly(1)) + .WillRepeatedly(::testing::Return(false)); + EXPECT_CALL(*CaseFile2, ReEvaluate(::testing::_)) + .Times(::testing::Exactly(0)); + EXPECT_CALL(*CaseFile4, ReEvaluate(::testing::_)) + .Times(::testing::Exactly(0)); + EXPECT_CALL(*CaseFile5, ReEvaluate(::testing::_)) + .Times(::testing::Exactly(0)); + CaseFile::ReEvaluateByGuid(Guid(456), *zfs_event); + EXPECT_EQ(5, TestableCaseFile::getActiveCases()); + delete CaseFile1; + delete CaseFile2; + delete CaseFile3; + delete CaseFile4; + delete CaseFile5; +} Index: projects/zfsd/head/cddl/usr.sbin/zfsd/tests/Makefile =================================================================== --- projects/zfsd/head/cddl/usr.sbin/zfsd/tests/Makefile (nonexistent) +++ projects/zfsd/head/cddl/usr.sbin/zfsd/tests/Makefile (revision 300335) @@ -0,0 +1,48 @@ +# $FreeBSD$ + +SRCDIR=${.CURDIR}/../../../.. +.include "${.CURDIR}/../Makefile.common" +.PATH: ${.CURDIR}/.. + +PROG_CXX= zfsd_unittest +SRCS:= ${SRCS:Nzfsd_main.cc} +SRCS+= libmocks.c zfsd_unittest.cc + +# Use #include in test programs. +INCFLAGS+= -I ${.CURDIR}/../.. + +.if defined(DESTDIR) +INCFLAGS+= -I ${DESTDIR}/usr/include +LIBRARY_PATH= ${DESTDIR}/lib:${DESTDIR}/usr/lib +LDFLAGS+= -L ${DESTDIR}/lib -L ${DESTDIR}/usr/lib +.elif defined(WORLDTMP) +INCFLAGS+= -I ${WORLDTMP}/usr/include +LIBRARY_PATH= ${WORLDTMP}/lib:${WORLDTMP}/usr/lib +LDFLAGS+= -L ${WORLDTMP}/lib -L ${WORLDTMP}/usr/lib +.else +LIBRARY_PATH= +.endif +ZFSD_UNITTEST= env LD_LIBRARY_PATH=${LIBRARY_PATH} ./zfsd_unittest + +# Googletest options +LOCALBASE?= /usr/local +INCFLAGS+= -I ${LOCALBASE}/include -D_THREAD_SAFE -pthread +LDFLAGS+= -L ${LOCALBASE}/lib -D_THREAD_SAFE -pthread +LDADD+= ${LOCALBASE}/lib/libgtest.a + +# GoogleMock options +LDADD+= ${LOCALBASE}/lib/libgmock.a ${LOCALBASE}/lib/libgmock_main.a + +# Googlemock fails if we don't have this line +# https://groups.google.com/forum/#!msg/googletestframework/h8ixEPCFm0o/amwfu4xGJb0J +CFLAGS+= -DGTEST_HAS_PTHREAD + +# Install the tests +TESTSBASE?= /usr/tests +TESTSDIR?= ${TESTSBASE}/cddl/sbin/zfsd +# TODO: Convert from an ATF SH test to a Kyua plain test +# Long term TODO: Convert to a Kyua googletest test +ATF_TESTS_SH+= zfsd_test +BINDIR= ${TESTSDIR} + +.include Index: projects/zfsd/head/cddl/usr.sbin/zfsd/tests/zfsd_test.sh =================================================================== --- projects/zfsd/head/cddl/usr.sbin/zfsd/tests/zfsd_test.sh (nonexistent) +++ projects/zfsd/head/cddl/usr.sbin/zfsd/tests/zfsd_test.sh (revision 300335) @@ -0,0 +1,71 @@ +# Copyright (c) 2013 Spectra Logic Corporation +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions, and the following disclaimer, +# without modification. +# 2. Redistributions in binary form must reproduce at minimum a disclaimer +# substantially similar to the "NO WARRANTY" disclaimer below +# ("Disclaimer") and any redistribution must be conditioned upon +# including a substantially similar Disclaimer requirement for further +# binary redistribution. +# +# NO WARRANTY +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGES. +# +# Authors: Alan Somers (Spectra Logic Corporation) +# +# $FreeBSD$ + +# +# Test Case: zfsd_unittest +# TODO: get coverage in cleanup +# +atf_test_case zfsd_unittest +zfsd_unittest_head() +{ + atf_set "descr" "Run zfsd unit tests" +} + + +zfsd_unittest_body() +{ + TESTPROG=$(atf_get_srcdir)/zfsd_unittest + if atf_config_has coverage_dir; then + # If coverage_dir is defined, then we want to save the .gcda + # and .gcno files for future analysis. Put them in a directory + # tree that resembles /usr/src, but is anchored at + # coverage_dir. + export GCOV_PREFIX=`atf_config_get coverage_dir` + # Examine zfsd_unittest to calculate the GCOV_PREFIX_STRIP + # The outer echo command is needed to strip off whitespace + # printed by wc + OLDGCDADIR=`strings $TESTPROG | grep 'zfsd.gcda'` + export GCOV_PREFIX_STRIP=$( echo $( echo $OLDGCDADIR | \ + sed -e 's:/cddl/sbin/zfsd.*::' -e 's:/: :g' | \ + wc -w ) ) + NEWGCDADIR=$GCOV_PREFIX/`dirname $OLDGCDADIR | \ + sed -e 's:.*\(cddl/sbin/zfsd\):\1:'` + mkdir -p $NEWGCDADIR + cp $(atf_get_srcdir)/*.gcno $NEWGCDADIR + fi + atf_check -s exit:0 -o ignore -e ignore $TESTPROG +} + +atf_init_test_cases() +{ + atf_add_test_case zfsd_unittest +} Index: projects/zfsd/head/cddl/usr.sbin/zfsd/tests/libmocks.c =================================================================== --- projects/zfsd/head/cddl/usr.sbin/zfsd/tests/libmocks.c (nonexistent) +++ projects/zfsd/head/cddl/usr.sbin/zfsd/tests/libmocks.c (revision 300335) @@ -0,0 +1,24 @@ +#include +#include +#include "libmocks.h" + +/* + * This file mocks shared library functions that are used by zfsd. Every + * function present will be used for all tests in all test suites instead of the + * normal function. + */ + +int syslog_last_priority; +char syslog_last_message[4096]; +void syslog(int priority, const char* message, ...) { + va_list ap; + + syslog_last_priority = priority; + va_start(ap, message); + vsnprintf(syslog_last_message, 4096, message, ap); + va_end(ap); +} + +int zpool_iter(libzfs_handle_t* handle, zpool_iter_f iter, void* arg) { + return (0); +} Index: projects/zfsd/head/cddl/usr.sbin/zfsd/tests/libmocks.h =================================================================== --- projects/zfsd/head/cddl/usr.sbin/zfsd/tests/libmocks.h (nonexistent) +++ projects/zfsd/head/cddl/usr.sbin/zfsd/tests/libmocks.h (revision 300335) @@ -0,0 +1,24 @@ +#ifndef _LIBMOCKS_H_ +#define _LIBMOCKS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +struct libzfs_handle; +typedef struct libzfs_handle libzfs_handle_t; +struct zpool_handle; +typedef struct zpool_handle zpool_handle_t; +typedef int (*zpool_iter_f)(zpool_handle_t *, void *); + +void syslog(int priority, const char* message, ...); +int zpool_iter(libzfs_handle_t*, zpool_iter_f, void*); + +extern int syslog_last_priority; +extern char syslog_last_message[4096]; + +#ifdef __cplusplus +} +#endif + +#endif Index: projects/zfsd/head/cddl/usr.sbin/zfsd/tests/zfsd_unittest.supp =================================================================== --- projects/zfsd/head/cddl/usr.sbin/zfsd/tests/zfsd_unittest.supp (nonexistent) +++ projects/zfsd/head/cddl/usr.sbin/zfsd/tests/zfsd_unittest.supp (revision 300335) @@ -0,0 +1,104 @@ +# This is a valgrind suppression file used for running zfsd_unittest with +# valgrind. It suppress spurious errors generated by the googletest and +# googlemock libraries. +# +# To use, do: +# valgrind --suppressions=$PWD/zfsd_unittest.supp ./zfsd_unittest + +{ + + Memcheck:Free + fun:free + ... + fun:__cxa_finalize + fun:exit + fun:(below main) +} + +{ + + Memcheck:Free + fun:free + ... + fun:_ZN7testing8internal27PrettyUnitTestResultPrinter* + ... + ... + fun:main +} + +{ + + Memcheck:Free + fun:free + fun:_ZN7testing* + ... + fun:main +} + +{ + + Memcheck:Free + fun:free + ... + fun:_Z41__static_initialization_and_destruction_0ii + ... +} + +{ + + Memcheck:Free + fun:free + ... + fun:_ZN7testing8internal8MockSpec* + ... + fun:_ZN7testing4Test3RunEv + fun:_ZN7testing8internal12TestInfoImpl3RunEv + fun:_ZN7testing8TestCase3RunEv + fun:_ZN7testing8internal12UnitTestImpl11RunAllTestsEv +} + +{ + + Memcheck:Free + fun:free + ... + fun:_ZN7testing8internal14FunctionMocker* + ... +} + +{ + + Memcheck:Cond + obj:/lib/libc.so.7 + obj:/lib/libc.so.7 + fun:snprintf + fun:_ZN7testing45_GLOBAL__N_src_gmock_all.cc_00000000_917CAD5926PrintByteSegmentInObjectToEPKhmmPSo + fun:_ZN7testing9internal220PrintBytesInObjectToEPKhmPSo + fun:_ZN7testing9internal220TypeWithoutFormatterI8ZfsEventLb0EE10PrintValueERKS2_PSo + fun:_ZN7testing9internal2lsIcSt11char_traitsIcE8ZfsEventEERSt13basic_ostreamIT_T0_ES9_RKT1_ + fun:_ZN16testing_internal26DefaultPrintNonContainerToI8ZfsEventEEvRKT_PSo + fun:_ZN7testing8internal14DefaultPrintToI8ZfsEventEEvcNS0_13bool_constantILb0EEERKT_PSo + fun:_ZN7testing8internal7PrintToI8ZfsEventEEvRKT_PSo + fun:_ZN7testing8internal16UniversalPrinterIK8ZfsEventE5PrintERS3_PSo + fun:_ZN7testing8internal16UniversalPrinterIRK8ZfsEventE5PrintES4_PSo +} + +{ + + Memcheck:Cond + ... + fun:snprintf + ... + fun:_ZN7testing9internal220PrintBytesInObjectToEPKhmPSo + ... +} +{ + + Memcheck:Value8 + ... + fun:snprintf + ... + fun:_ZN7testing9internal220PrintBytesInObjectToEPKhmPSo + ... +} + Index: projects/zfsd/head/cddl/usr.sbin/zfsd/vdev.cc =================================================================== --- projects/zfsd/head/cddl/usr.sbin/zfsd/vdev.cc (nonexistent) +++ projects/zfsd/head/cddl/usr.sbin/zfsd/vdev.cc (revision 300335) @@ -0,0 +1,352 @@ +/*- + * Copyright (c) 2011 Spectra Logic Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + * Authors: Justin T. Gibbs (Spectra Logic Corporation) + * + * $FreeBSD$ + */ + +/** + * \file vdev.cc + * + * Implementation of the Vdev class. + */ +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "vdev.h" +#include "vdev_iterator.h" +#include "zfsd.h" +#include "zfsd_exception.h" +#include "zpool_list.h" + +__FBSDID("$FreeBSD$"); +/*============================ Namespace Control =============================*/ +using std::string; +using std::stringstream; + +//- Special objects ----------------------------------------------------------- +Vdev NonexistentVdev; + +//- Vdev Inline Public Methods ------------------------------------------------ +/*=========================== Class Implementations ==========================*/ +/*----------------------------------- Vdev -----------------------------------*/ + +/* Special constructor for NonexistentVdev. */ +Vdev::Vdev() + : m_poolConfig(NULL), + m_config(NULL) +{} + +bool +Vdev::VdevLookupPoolGuid() +{ + uint64_t guid; + if (nvlist_lookup_uint64(m_poolConfig, ZPOOL_CONFIG_POOL_GUID, &guid)) + return (false); + m_poolGUID = guid; + return (true); +} + +void +Vdev::VdevLookupGuid() +{ + uint64_t guid; + if (nvlist_lookup_uint64(m_config, ZPOOL_CONFIG_GUID, &guid) != 0) + throw ZfsdException("Unable to extract vdev GUID " + "from vdev config data."); + m_vdevGUID = guid; +} + +Vdev::Vdev(zpool_handle_t *pool, nvlist_t *config) + : m_poolConfig(zpool_get_config(pool, NULL)), + m_config(config) +{ + if (!VdevLookupPoolGuid()) + throw ZfsdException("Can't extract pool GUID from handle."); + VdevLookupGuid(); +} + +Vdev::Vdev(nvlist_t *poolConfig, nvlist_t *config) + : m_poolConfig(poolConfig), + m_config(config) +{ + if (!VdevLookupPoolGuid()) + throw ZfsdException("Can't extract pool GUID from config."); + VdevLookupGuid(); +} + +Vdev::Vdev(nvlist_t *labelConfig) + : m_poolConfig(labelConfig), + m_config(labelConfig) +{ + /* + * Spares do not have a Pool GUID. Tolerate its absence. + * Code accessing this Vdev in a context where the Pool GUID is + * required will find it invalid (as it is upon Vdev construction) + * and act accordingly. + */ + (void) VdevLookupPoolGuid(); + VdevLookupGuid(); + + try { + m_config = VdevIterator(labelConfig).Find(m_vdevGUID); + } catch (const ZfsdException &exp) { + /* + * When reading a spare's label, it is normal not to find + * a list of vdevs + */ + m_config = NULL; + } +} + +bool +Vdev::IsSpare() const +{ + uint64_t is_spare(0); + + if (m_config == NULL) + return (false); + + (void)nvlist_lookup_uint64(m_config, ZPOOL_CONFIG_IS_SPARE, &is_spare); + return (bool(is_spare)); +} + +vdev_state +Vdev::State() const +{ + uint64_t *nvlist_array; + vdev_stat_t *vs; + uint_t vsc; + + if (m_config == NULL) { + /* + * If we couldn't find the list of vdevs, that normally means + * that this is an available hotspare. In that case, we will + * presume it to be healthy. Even if this spare had formerly + * been in use, been degraded, and been replaced, the act of + * replacement wipes the degraded bit from the label. So we + * have no choice but to presume that it is healthy. + */ + return (VDEV_STATE_HEALTHY); + } + + if (nvlist_lookup_uint64_array(m_config, ZPOOL_CONFIG_VDEV_STATS, + &nvlist_array, &vsc) == 0) { + vs = reinterpret_cast(nvlist_array); + return (static_cast(vs->vs_state)); + } + + /* + * Stats are not available. This vdev was created from a label. + * Synthesize a state based on available data. + */ + uint64_t faulted(0); + uint64_t degraded(0); + (void)nvlist_lookup_uint64(m_config, ZPOOL_CONFIG_FAULTED, &faulted); + (void)nvlist_lookup_uint64(m_config, ZPOOL_CONFIG_DEGRADED, °raded); + if (faulted) + return (VDEV_STATE_FAULTED); + if (degraded) + return (VDEV_STATE_DEGRADED); + return (VDEV_STATE_HEALTHY); +} + +std::list +Vdev::Children() +{ + nvlist_t **vdevChildren; + int result; + u_int numChildren; + std::list children; + + if (m_poolConfig == NULL || m_config == NULL) + return (children); + + result = nvlist_lookup_nvlist_array(m_config, + ZPOOL_CONFIG_CHILDREN, &vdevChildren, &numChildren); + if (result != 0) + return (children); + + for (u_int c = 0;c < numChildren; c++) + children.push_back(Vdev(m_poolConfig, vdevChildren[c])); + + return (children); +} + +Vdev +Vdev::RootVdev() +{ + nvlist_t *rootVdev; + + if (m_poolConfig == NULL) + return (NonexistentVdev); + + if (nvlist_lookup_nvlist(m_poolConfig, ZPOOL_CONFIG_VDEV_TREE, + &rootVdev) != 0) + return (NonexistentVdev); + return (Vdev(m_poolConfig, rootVdev)); +} + +/* + * Find our parent. This requires doing a traversal of the config; we can't + * cache it as leaf vdevs may change their pool config location (spare, + * replacing, mirror, etc). + */ +Vdev +Vdev::Parent() +{ + std::list to_examine; + std::list children; + std::list::iterator children_it; + + to_examine.push_back(RootVdev()); + for (;;) { + if (to_examine.empty()) + return (NonexistentVdev); + Vdev vd = to_examine.front(); + if (vd.DoesNotExist()) + return (NonexistentVdev); + to_examine.pop_front(); + children = vd.Children(); + children_it = children.begin(); + for (;children_it != children.end(); children_it++) { + Vdev child = *children_it; + + if (child.GUID() == GUID()) + return (vd); + to_examine.push_front(child); + } + } +} + +bool +Vdev::IsAvailableSpare() const +{ + /* If we have a pool guid, we cannot be an available spare. */ + if (PoolGUID()) + return (false); + + return (true); +} + +bool +Vdev::IsSpare() +{ + uint64_t spare; + if (nvlist_lookup_uint64(m_config, ZPOOL_CONFIG_IS_SPARE, &spare) != 0) + return (false); + return (spare != 0); +} + +bool +Vdev::IsActiveSpare() const +{ + vdev_stat_t *vs; + uint_t c; + + if (m_poolConfig == NULL) + return (false); + + (void) nvlist_lookup_uint64_array(m_config, ZPOOL_CONFIG_VDEV_STATS, + reinterpret_cast(&vs), &c); + if (vs == NULL || vs->vs_aux != VDEV_AUX_SPARED) + return (false); + return (true); +} + +bool +Vdev::IsResilvering() const +{ + pool_scan_stat_t *ps = NULL; + uint_t c; + + if (State() != VDEV_STATE_HEALTHY) + return (false); + + (void) nvlist_lookup_uint64_array(m_config, ZPOOL_CONFIG_SCAN_STATS, + reinterpret_cast(&ps), &c); + if (ps == NULL || ps->pss_func != POOL_SCAN_RESILVER) + return (false); + return (true); +} + +string +Vdev::GUIDString() const +{ + stringstream vdevGUIDString; + + vdevGUIDString << GUID(); + return (vdevGUIDString.str()); +} + +string +Vdev::Name(zpool_handle_t *zhp, bool verbose) const +{ + return (zpool_vdev_name(g_zfsHandle, zhp, m_config, + verbose ? B_TRUE : B_FALSE)); +} + +string +Vdev::Path() const +{ + char *path(NULL); + + if ((m_config != NULL) + && (nvlist_lookup_string(m_config, ZPOOL_CONFIG_PATH, &path) == 0)) + return (path); + + return (""); +} + +string +Vdev::PhysicalPath() const +{ + char *path(NULL); + + if ((m_config != NULL) && (nvlist_lookup_string(m_config, + ZPOOL_CONFIG_PHYS_PATH, &path) == 0)) + return (path); + + return (""); +} Index: projects/zfsd/head/cddl/usr.sbin/zfsd/vdev.h =================================================================== --- projects/zfsd/head/cddl/usr.sbin/zfsd/vdev.h (nonexistent) +++ projects/zfsd/head/cddl/usr.sbin/zfsd/vdev.h (revision 300335) @@ -0,0 +1,178 @@ +/*- + * Copyright (c) 2011 Spectra Logic Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + * Authors: Justin T. Gibbs (Spectra Logic Corporation) + * + * $FreeBSD$ + */ + +/** + * \file vdev.h + * + * Definition of the Vdev class. + * + * Header requirements: + * + * #include + * #include + * + * #include + */ +#ifndef _VDEV_H_ +#define _VDEV_H_ + +/*=========================== Forward Declarations ===========================*/ +struct zpool_handle; +typedef struct zpool_handle zpool_handle_t; + +struct nvlist; +typedef struct nvlist nvlist_t; + +/*============================= Class Definitions ============================*/ +/*----------------------------------- Vdev -----------------------------------*/ +/** + * \brief Wrapper class for a vdev's name/value configuration list + * simplifying access to commonly used vdev attributes. + */ +class Vdev +{ +public: + /** + * \brief Instantiate a vdev object for a vdev that is a member + * of an imported pool. + * + * \param pool The pool object containing the vdev with + * configuration data provided in vdevConfig. + * \param vdevConfig Vdev configuration data. + * + * This method should be used whenever dealing with vdev's + * enumerated via the ZpoolList class. The in-core configuration + * data for a vdev does not contain all of the items found in + * the on-disk label. This requires the vdev class to augment + * the data in vdevConfig with data found in the pool object. + */ + Vdev(zpool_handle_t *pool, nvlist_t *vdevConfig); + + /** + * \brief Instantiate a vdev object for a vdev that is a member + * of a pool configuration. + * + * \param poolConfig The pool configuration containing the vdev + * configuration data provided in vdevConfig. + * \param vdevConfig Vdev configuration data. + * + * This method should be used whenever dealing with vdev's + * enumerated via the ZpoolList class. The in-core configuration + * data for a vdev does not contain all of the items found in + * the on-disk label. This requires the vdev class to augment + * the data in vdevConfig with data found in the pool object. + */ + Vdev(nvlist_t *poolConfig, nvlist_t *vdevConfig); + + /** + * \brief Instantiate a vdev object from a ZFS label stored on + * the device. + * + * \param vdevConfig The name/value list retrieved by reading + * the label information on a leaf vdev. + */ + Vdev(nvlist_t *vdevConfig); + + /** + * \brief No-op copy constructor for nonexistent vdevs. + */ + Vdev(); + bool DoesNotExist() const; + + /** + * \brief Return a list of the vdev's children. + */ + std::list Children(); + + virtual DevdCtl::Guid GUID() const; + bool IsSpare() const; + virtual DevdCtl::Guid PoolGUID() const; + virtual vdev_state State() const; + std::string Path() const; + virtual std::string PhysicalPath() const; + std::string GUIDString() const; + nvlist_t *PoolConfig() const; + nvlist_t *Config() const; + Vdev Parent(); + Vdev RootVdev(); + std::string Name(zpool_handle_t *, bool verbose) const; + bool IsSpare(); + bool IsAvailableSpare() const; + bool IsActiveSpare() const; + bool IsResilvering() const; + +private: + void VdevLookupGuid(); + bool VdevLookupPoolGuid(); + DevdCtl::Guid m_poolGUID; + DevdCtl::Guid m_vdevGUID; + nvlist_t *m_poolConfig; + nvlist_t *m_config; +}; + +//- Special objects ----------------------------------------------------------- +extern Vdev NonexistentVdev; + +//- Vdev Inline Public Methods ------------------------------------------------ +inline DevdCtl::Guid +Vdev::PoolGUID() const +{ + return (m_poolGUID); +} + +inline DevdCtl::Guid +Vdev::GUID() const +{ + return (m_vdevGUID); +} + +inline nvlist_t * +Vdev::PoolConfig() const +{ + return (m_poolConfig); +} + +inline nvlist_t * +Vdev::Config() const +{ + return (m_config); +} + +inline bool +Vdev::DoesNotExist() const +{ + return (m_config == NULL); +} + +#endif /* _VDEV_H_ */ Index: projects/zfsd/head/cddl/usr.sbin/zfsd/vdev_iterator.cc =================================================================== --- projects/zfsd/head/cddl/usr.sbin/zfsd/vdev_iterator.cc (nonexistent) +++ projects/zfsd/head/cddl/usr.sbin/zfsd/vdev_iterator.cc (revision 300335) @@ -0,0 +1,151 @@ +/*- + * Copyright (c) 2011 Spectra Logic Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + * Authors: Justin T. Gibbs (Spectra Logic Corporation) + */ + +/** + * \file vdev_iterator.cc + * + * Implementation of the VdevIterator class. + */ +#include +#include + +#include +#include + +#include + +#include +#include + +#include +#include + +#include "vdev.h" +#include "vdev_iterator.h" +#include "zfsd_exception.h" + +/*============================ Namespace Control =============================*/ +using DevdCtl::Guid; + +/*=========================== Class Implementations ==========================*/ +/*------------------------------- VdevIterator -------------------------------*/ +VdevIterator::VdevIterator(zpool_handle_t *pool) + : m_poolConfig(zpool_get_config(pool, NULL)) +{ + Reset(); +} + +VdevIterator::VdevIterator(nvlist_t *poolConfig) + : m_poolConfig(poolConfig) +{ + Reset(); +} + +void +VdevIterator::Reset() +{ + nvlist_t *rootVdev; + int result; + + result = nvlist_lookup_nvlist(m_poolConfig, + ZPOOL_CONFIG_VDEV_TREE, + &rootVdev); + if (result != 0) + throw ZfsdException(m_poolConfig, "Unable to extract " + "ZPOOL_CONFIG_VDEV_TREE from pool."); + m_vdevQueue.assign(1, rootVdev); +} + +nvlist_t * +VdevIterator::Next() +{ + nvlist_t *vdevConfig; + + if (m_vdevQueue.empty()) + return (NULL); + + while (1) { + nvlist_t **vdevChildren; + int result; + u_int numChildren; + + vdevConfig = m_vdevQueue.front(); + m_vdevQueue.pop_front(); + + /* Expand non-leaf vdevs. */ + result = nvlist_lookup_nvlist_array(vdevConfig, + ZPOOL_CONFIG_CHILDREN, + &vdevChildren, &numChildren); + if (result != 0) { + /* leaf vdev */ + break; + } + + /* + * Insert children at the head of the queue to effect a + * depth first traversal of the tree. + */ + m_vdevQueue.insert(m_vdevQueue.begin(), vdevChildren, + vdevChildren + numChildren); + }; + + return (vdevConfig); +} + +void +VdevIterator::Each(VdevCallback_t *callBack, void *callBackArg) +{ + nvlist_t *vdevConfig; + + Reset(); + while ((vdevConfig = Next()) != NULL) { + Vdev vdev(m_poolConfig, vdevConfig); + + if (callBack(vdev, callBackArg)) + break; + } +} + +nvlist_t * +VdevIterator::Find(Guid vdevGUID) +{ + nvlist_t *vdevConfig; + + Reset(); + while ((vdevConfig = Next()) != NULL) { + Vdev vdev(m_poolConfig, vdevConfig); + + if (vdev.GUID() == vdevGUID) + return (vdevConfig); + } + return (NULL); +} Index: projects/zfsd/head/cddl/usr.sbin/zfsd/vdev_iterator.h =================================================================== --- projects/zfsd/head/cddl/usr.sbin/zfsd/vdev_iterator.h (nonexistent) +++ projects/zfsd/head/cddl/usr.sbin/zfsd/vdev_iterator.h (revision 300335) @@ -0,0 +1,121 @@ +/*- + * Copyright (c) 2011 Spectra Logic Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + * Authors: Justin T. Gibbs (Spectra Logic Corporation) + */ + +/** + * \file vdev_iterator.h + * + * VdevIterator class definition. + * + * Header requirements: + * + * #include + */ +#ifndef _VDEV_ITERATOR_H_ +#define _VDEV_ITERATOR_H_ + +/*=========================== Forward Declarations ===========================*/ +struct zpool_handle; +typedef struct zpool_handle zpool_handle_t; + +struct nvlist; +typedef struct nvlist nvlist_t; + +class Vdev; + +/*============================= Class Definitions ============================*/ +/*------------------------------- VdevIterator -------------------------------*/ +typedef bool VdevCallback_t(Vdev &vdev, void *cbArg); + +/** + * \brief VdevIterator provides mechanisms for traversing and searching + * the leaf vdevs contained in a ZFS pool configuration. + */ +class VdevIterator +{ +public: + /** + * \brief Instantiate a VdevIterator for the given ZFS pool. + * + * \param pool The ZFS pool to traverse/search. + */ + VdevIterator(zpool_handle_t *pool); + + /** + * \brief Instantiate a VdevIterator for the given ZFS pool. + * + * \param poolConfig The configuration data for the ZFS pool + * to traverse/search. + */ + VdevIterator(nvlist_t *poolConfig); + + /** + * \brief Reset this iterator's cursor so that Next() will + * report the first member of the pool. + */ + void Reset(); + + /** + * \brief Report the leaf vdev at this iterator's cursor and increment + * the cursor to the next leaf pool member. + */ + nvlist_t *Next(); + + /** + * \brief Traverse the entire pool configuration starting its + * first member, returning a vdev object with the given + * vdev GUID if found. + * + * \param vdevGUID The vdev GUID of the vdev object to find. + * + * \return A Vdev object for the matching vdev if found. Otherwise + * NULL. + * + * Upon return, the VdevIterator's cursor points to the vdev just + * past the returned vdev or end() if no matching vdev is found. + */ + nvlist_t *Find(DevdCtl::Guid vdevGUID); + + /** + * \brief Perform the specified operation on each leaf member of + * a pool's vdev membership. + * + * \param cb Callback function to execute for each member. + * \param cbArg Argument to pass to cb. + */ + void Each(VdevCallback_t *cb, void *cbArg); + +private: + nvlist_t *m_poolConfig; + std::list m_vdevQueue; +}; + +#endif /* _VDEV_ITERATOR_H_ */ Index: projects/zfsd/head/cddl/usr.sbin/zfsd/zfsd.h =================================================================== --- projects/zfsd/head/cddl/usr.sbin/zfsd/zfsd.h (nonexistent) +++ projects/zfsd/head/cddl/usr.sbin/zfsd/zfsd.h (revision 300335) @@ -0,0 +1,228 @@ +/*- + * Copyright (c) 2011 Spectra Logic Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + * Authors: Justin T. Gibbs (Spectra Logic Corporation) + * + * $FreeBSD$ + */ + +/** + * \file zfsd.h + * + * Class definitions and supporting data strutures for the ZFS fault + * management daemon. + * + * Header requirements: + * + * #include + * + * #include + * + * #include + * #include + * #include + * + * #include + * #include + * #include + * #include + * + * #include "vdev_iterator.h" + */ +#ifndef _ZFSD_H_ +#define _ZFSD_H_ + +/*=========================== Forward Declarations ===========================*/ +struct pidfh; + +struct zpool_handle; +typedef struct zpool_handle zpool_handle_t; + +struct zfs_handle; +typedef struct libzfs_handle libzfs_handle_t; + +struct nvlist; +typedef struct nvlist nvlist_t; + +typedef int LeafIterFunc(zpool_handle_t *, nvlist_t *, void *); + +/*================================ Global Data ===============================*/ +extern int g_debug; +extern libzfs_handle_t *g_zfsHandle; + +/*============================= Class Definitions ============================*/ +/*--------------------------------- ZfsDaemon --------------------------------*/ +/** + * Static singleton orchestrating the operations of the ZFS daemon program. + */ +class ZfsDaemon : public DevdCtl::Consumer +{ +public: + /** Return the ZfsDaemon singleton. */ + static ZfsDaemon &Get(); + + /** + * Used by signal handlers to ensure, in a race free way, that + * the event loop will perform at least one more full loop + * before sleeping again. + */ + static void WakeEventLoop(); + + /** + * Schedules a rescan of devices in the system for potential + * candidates to replace a missing vdev. The scan is performed + * during the next run of the event loop. + */ + static void RequestSystemRescan(); + + /** Daemonize and perform all functions of the ZFS daemon. */ + static void Run(); + +private: + ZfsDaemon(); + ~ZfsDaemon(); + + static VdevCallback_t VdevAddCaseFile; + + /** Purge our cache of outstanding ZFS issues in the system. */ + void PurgeCaseFiles(); + + /** Build a cache of outstanding ZFS issues in the system. */ + void BuildCaseFiles(); + + /** + * Iterate over all known issues and attempt to solve them + * given resources currently available in the system. + */ + void RescanSystem(); + + /** + * Interrogate the system looking for previously unknown + * faults that occurred either before ZFSD was started, + * or during a period of lost communication with Devd. + */ + void DetectMissedEvents(); + + /** + * Wait for and process event source activity. + */ + void EventLoop(); + + /** + * Signal handler for which our response is to + * log the current state of the daemon. + * + * \param sigNum The signal caught. + */ + static void InfoSignalHandler(int sigNum); + + /** + * Signal handler for which our response is to + * request a case rescan. + * + * \param sigNum The signal caught. + */ + static void RescanSignalHandler(int sigNum); + + /** + * Signal handler for which our response is to + * gracefully terminate. + * + * \param sigNum The signal caught. + */ + static void QuitSignalHandler(int sigNum); + + /** + * Open and lock our PID file. + */ + static void OpenPIDFile(); + + /** + * Update our PID file with our PID. + */ + static void UpdatePIDFile(); + + /** + * Close and release the lock on our PID file. + */ + static void ClosePIDFile(); + + /** + * Perform syslog configuraiton. + */ + static void InitializeSyslog(); + + static ZfsDaemon *s_theZfsDaemon; + + /** + * Set to true when our program is signaled to + * gracefully exit. + */ + static bool s_logCaseFiles; + + /** + * Set to true when our program is signaled to + * gracefully exit. + */ + static bool s_terminateEventLoop; + + /** + * The canonical path and file name of zfsd's PID file. + */ + static char s_pidFilePath[]; + + /** + * Control structure for PIDFILE(3) API. + */ + static pidfh *s_pidFH; + + /** + * Pipe file descriptors used to close races with our + * signal handlers. + */ + static int s_signalPipeFD[2]; + + /** + * Flag controlling a rescan from ZFSD's event loop of all + * GEOM providers in the system to find candidates for solving + * cases. + */ + static bool s_systemRescanRequested; + + /** + * Flag controlling whether events can be queued. This boolean + * is set during event replay to ensure that events for pools or + * devices no longer in the system are not retained forever. + */ + static bool s_consumingEvents; + + static DevdCtl::EventFactory::Record s_registryEntries[]; +}; + +#endif /* _ZFSD_H_ */ Index: projects/zfsd/head/cddl/usr.sbin/zfsd/zfsd_exception.cc =================================================================== --- projects/zfsd/head/cddl/usr.sbin/zfsd/zfsd_exception.cc (nonexistent) +++ projects/zfsd/head/cddl/usr.sbin/zfsd/zfsd_exception.cc (revision 300335) @@ -0,0 +1,134 @@ +/*- + * Copyright (c) 2011, 2012, 2013 Spectra Logic Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + * Authors: Justin T. Gibbs (Spectra Logic Corporation) + */ + +/** + * \file zfsd_exception + * + * Implementation of the ZfsdException class. + */ +#include +#include + +#include + +#include +#include +#include + +#include +#include + +#include + +#include "vdev.h" +#include "zfsd_exception.h" + +__FBSDID("$FreeBSD$"); +/*============================ Namespace Control =============================*/ +using std::endl; +using std::string; +using std::stringstream; + +/*=========================== Class Implementations ==========================*/ +/*------------------------------- ZfsdException ------------------------------*/ +ZfsdException::ZfsdException(const char *fmt, ...) + : DevdCtl::Exception(), + m_poolConfig(NULL), + m_vdevConfig(NULL) +{ + va_list ap; + + va_start(ap, fmt); + FormatLog(fmt, ap); + va_end(ap); +} + +ZfsdException::ZfsdException(zpool_handle_t *pool, const char *fmt, ...) + : DevdCtl::Exception(), + m_poolConfig(zpool_get_config(pool, NULL)), + m_vdevConfig(NULL) +{ + va_list ap; + + va_start(ap, fmt); + FormatLog(fmt, ap); + va_end(ap); +} + +ZfsdException::ZfsdException(nvlist_t *poolConfig, const char *fmt, ...) + : DevdCtl::Exception(), + m_poolConfig(poolConfig), + m_vdevConfig(NULL) +{ + va_list ap; + + va_start(ap, fmt); + FormatLog(fmt, ap); + va_end(ap); +} + +void +ZfsdException::Log() const +{ + stringstream output; + + if (m_poolConfig != NULL) { + + output << "Pool "; + + char *poolName; + if (nvlist_lookup_string(m_poolConfig, ZPOOL_CONFIG_POOL_NAME, + &poolName) == 0) + output << poolName; + else + output << "Unkown"; + output << ": "; + } + + if (m_vdevConfig != NULL) { + + if (m_poolConfig != NULL) { + Vdev vdev(m_poolConfig, m_vdevConfig); + + output << "Vdev " << vdev.GUID() << ": "; + } else { + Vdev vdev(m_vdevConfig); + + output << "Pool " << vdev.PoolGUID() << ": "; + output << "Vdev " << vdev.GUID() << ": "; + } + } + + output << m_log << endl; + syslog(LOG_ERR, "%s", output.str().c_str()); +} + Index: projects/zfsd/head/cddl/usr.sbin/zfsd/zfsd_exception.h =================================================================== --- projects/zfsd/head/cddl/usr.sbin/zfsd/zfsd_exception.h (nonexistent) +++ projects/zfsd/head/cddl/usr.sbin/zfsd/zfsd_exception.h (revision 300335) @@ -0,0 +1,107 @@ +/*- + * Copyright (c) 2011 Spectra Logic Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + * Authors: Justin T. Gibbs (Spectra Logic Corporation) + */ + +/** + * \file zfsd_exception.h + * + * Definition of the ZfsdException class hierarchy. All exceptions + * explicitly thrown by Zfsd are defined here. + * + * Header requirements: + * #include + * + * #include + */ +#ifndef _ZFSD_EXCEPTION_H_ +#define _ZFSD_EXCEPTION_H_ + +/*=========================== Forward Declarations ===========================*/ +struct zpool_handle; +typedef struct zpool_handle zpool_handle_t; + +struct nvlist; +typedef struct nvlist nvlist_t; + +/*============================= Class Definitions ============================*/ +/*------------------------------- ZfsdException ------------------------------*/ +/** + * \brief Class allowing unified reporting/logging of exceptional events. + */ +class ZfsdException : public DevdCtl::Exception +{ +public: + /** + * \brief ZfsdException constructor allowing arbitrary string + * data to be reported. + * + * \param fmt Printf-like string format specifier. + */ + ZfsdException(const char *fmt, ...); + + /** + * \brief ZfsdException constructor allowing arbitrary string + * data to be reported and associated with the configuration + * data for a ZFS pool. + * + * \param pool Pool handle describing the pool to which this + * exception is associated. + * \param fmt Printf-like string format specifier. + * + * Instantiation with this method is used to report global + * pool errors. + */ + ZfsdException(zpool_handle_t *pool, const char *, ...); + + /** + * \brief ZfsdException constructor allowing arbitrary string + * data to be reported and associated with the configuration + * data for a ZFS pool. + * + * \param poolConfig Pool configuration describing the pool to + * which this exception is associated. + * \param fmt Printf-like string format specifier. + * + * Instantiation with this method is used to report global + * pool errors. + */ + ZfsdException(nvlist_t *poolConfig, const char *, ...); + + /** + * \brief Emit exception data to syslog(3). + */ + virtual void Log() const; +private: + nvlist_t *m_poolConfig; + nvlist_t *m_vdevConfig; +}; + +#endif /* _ZFSD_EXCEPTION_H_ */ Index: projects/zfsd/head/cddl/usr.sbin/zfsd/zfsd_main.cc =================================================================== --- projects/zfsd/head/cddl/usr.sbin/zfsd/zfsd_main.cc (nonexistent) +++ projects/zfsd/head/cddl/usr.sbin/zfsd/zfsd_main.cc (revision 300335) @@ -0,0 +1,90 @@ +/*- + * Copyright (c) 2011 Spectra Logic Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + * Authors: Alan Somers (Spectra Logic Corporation) + */ + +/** + * \file zfsd_main.cc + * + * main function for the ZFS Daemon. Separated to facilitate testing. + * + */ + +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "vdev_iterator.h" +#include "zfsd.h" + +__FBSDID("$FreeBSD$"); + +/*=============================== Program Main ===============================*/ +static void +usage() +{ + fprintf(stderr, "usage: %s [-d]\n", getprogname()); + exit(1); +} + +/** + * Program entry point. + */ +int +main(int argc, char **argv) +{ + int ch; + + while ((ch = getopt(argc, argv, "d")) != -1) { + switch (ch) { + case 'd': + g_debug++; + break; + default: + usage(); + } + } + + ZfsDaemon::Run(); + + return (0); +} Index: projects/zfsd/head/cddl/usr.sbin/zfsd/zpool_list.cc =================================================================== --- projects/zfsd/head/cddl/usr.sbin/zfsd/zpool_list.cc (nonexistent) +++ projects/zfsd/head/cddl/usr.sbin/zfsd/zpool_list.cc (revision 300335) @@ -0,0 +1,119 @@ +/*- + * Copyright (c) 2011 Spectra Logic Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + * Authors: Justin T. Gibbs (Spectra Logic Corporation) + */ + +/** + * \file zpool_list.cc + * + * Implementation of the ZpoolList class. + */ +#include +#include + +#include + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "vdev.h" +#include "vdev_iterator.h" +#include "zpool_list.h" +#include "zfsd.h" + +/*============================ Namespace Control =============================*/ +using DevdCtl::Guid; + +/*=========================== Class Implementations ==========================*/ +/*--------------------------------- ZpoolList --------------------------------*/ +bool +ZpoolList::ZpoolAll(zpool_handle_t *pool, nvlist_t *poolConfig, void *cbArg) +{ + return (true); +} + +bool +ZpoolList::ZpoolByGUID(zpool_handle_t *pool, nvlist_t *poolConfig, + void *cbArg) +{ + Guid *desiredPoolGUID(static_cast(cbArg)); + uint64_t poolGUID; + + /* We are only intested in the pool that matches our pool GUID. */ + return (nvlist_lookup_uint64(poolConfig, ZPOOL_CONFIG_POOL_GUID, + &poolGUID) == 0 + && poolGUID == (uint64_t)*desiredPoolGUID); +} + +bool +ZpoolList::ZpoolByName(zpool_handle_t *pool, nvlist_t *poolConfig, void *cbArg) +{ + const string &desiredPoolName(*static_cast(cbArg)); + + /* We are only intested in the pool that matches our pool GUID. */ + return (desiredPoolName == zpool_get_name(pool)); +} + +int +ZpoolList::LoadIterator(zpool_handle_t *pool, void *data) +{ + ZpoolList *zpl(reinterpret_cast(data)); + nvlist_t *poolConfig(zpool_get_config(pool, NULL)); + + if (zpl->m_filter(pool, poolConfig, zpl->m_filterArg)) + zpl->push_back(pool); + else + zpool_close(pool); + return (0); +} + +ZpoolList::ZpoolList(PoolFilter_t *filter, void * filterArg) + : m_filter(filter), + m_filterArg(filterArg) +{ + zpool_iter(g_zfsHandle, LoadIterator, this); +} + +ZpoolList::~ZpoolList() +{ + for (iterator it(begin()); it != end(); it++) + zpool_close(*it); + + clear(); +} Index: projects/zfsd/head/cddl/usr.sbin/zfsd/Makefile =================================================================== --- projects/zfsd/head/cddl/usr.sbin/zfsd/Makefile (nonexistent) +++ projects/zfsd/head/cddl/usr.sbin/zfsd/Makefile (revision 300335) @@ -0,0 +1,15 @@ +# $FreeBSD$ + +SRCDIR=${.CURDIR}/../../.. +.include "Makefile.common" + +PROG_CXX= zfsd + +.include + +# Check for the existence of the googletest and googlemock header files, which +# come from ports. Don't compile the tests without them. +.if exists(${LOCALBASE}/include/gtest/gtest.h) && exists(${LOCALBASE}/include/gmock/gmock.h) +.else +SUBDIR= +.endif Index: projects/zfsd/head/cddl/usr.sbin/zfsd/callout.h =================================================================== --- projects/zfsd/head/cddl/usr.sbin/zfsd/callout.h (nonexistent) +++ projects/zfsd/head/cddl/usr.sbin/zfsd/callout.h (revision 300335) @@ -0,0 +1,183 @@ +/*- + * Copyright (c) 2011 Spectra Logic Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + * Authors: Justin T. Gibbs (Spectra Logic Corporation) + */ + +/** + * \file callout.h + * + * \brief Interface for timer based callback services. + * + * Header requirements: + * + * #include + * + * #include + */ + +#ifndef _CALLOUT_H_ +#define _CALLOUT_H_ + +/** + * \brief Type of the function callback from a Callout. + */ +typedef void CalloutFunc_t(void *); + +/** + * \brief Interface to a schedulable one-shot timer with the granlarity + * of the system clock (see setitimer(2)). + * + * Determination of callback expiration is triggered by the SIGALRM + * signal. Callout callbacks are always delivered from Zfsd's event + * processing loop. + * + * Periodic actions can be triggered via the Callout mechanisms by + * resetting the Callout from within its callback. + */ +class Callout +{ +public: + + /** + * Initialize the Callout subsystem. + */ + static void Init(); + + /** + * Function called (via SIGALRM) when our interval + * timer expires. + */ + static void AlarmSignalHandler(int); + + /** + * Execute callbacks for all callouts that have the same + * expiration time as the first callout in the list. + */ + static void ExpireCallouts(); + + /** Constructor. */ + Callout(); + + /** + * Returns true if callout has not been stopped, + * or deactivated since the last time the callout was + * reset. + */ + bool IsActive() const; + + /** + * Returns true if callout is still waiting to expire. + */ + bool IsPending() const; + + /** + * Disestablish a callout. + */ + bool Stop(); + + /** + * \brief Establish or change a timeout. + * + * \param interval Timeval indicating the time which must elapse + * before this callout fires. + * \param func Pointer to the callback funtion + * \param arg Argument pointer to pass to callback function + * + * \return Cancelation status. + * true: The previous callback was pending and therfore + * was cancelled. + * false: The callout was not pending at the time of this + * reset request. + * In all cases, a new callout is established. + */ + bool Reset(const timeval &interval, CalloutFunc_t *func, void *arg); + + /** + * \brief Calculate the remaining time until this Callout's timer + * expires. + * + * The return value will be slightly greater than the actual time to + * expiry. + * + * If the callout is not pending, returns INT_MAX. + */ + timeval TimeRemaining() const; + +private: + /** + * All active callouts sorted by expiration time. The callout + * with the nearest expiration time is at the head of the list. + */ + static std::list s_activeCallouts; + + /** + * The interval timer has expired. This variable is set from + * signal handler context and tested from Zfsd::EventLoop() + * context via ExpireCallouts(). + */ + static bool s_alarmFired; + + /** + * Time, realtive to others in the active list, until + * this callout is fired. + */ + timeval m_interval; + + /** Callback function argument. */ + void *m_arg; + + /** + * The callback function associated with this timer + * entry. + */ + CalloutFunc_t *m_func; + + /** State of this callout. */ + bool m_pending; +}; + +//- Callout public const methods ---------------------------------------------- +inline bool +Callout::IsPending() const +{ + return (m_pending); +} + +//- Callout public methods ---------------------------------------------------- +inline +Callout::Callout() + : m_arg(0), + m_func(NULL), + m_pending(false) +{ + timerclear(&m_interval); +} + +#endif /* CALLOUT_H_ */ Index: projects/zfsd/head/cddl/usr.sbin/zfsd/zpool_list.h =================================================================== --- projects/zfsd/head/cddl/usr.sbin/zfsd/zpool_list.h (nonexistent) +++ projects/zfsd/head/cddl/usr.sbin/zfsd/zpool_list.h (revision 300335) @@ -0,0 +1,131 @@ +/*- + * Copyright (c) 2011 Spectra Logic Corporation + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + * Authors: Justin T. Gibbs (Spectra Logic Corporation) + */ + +/** + * \file zpool_list.h + * + * ZpoolList class definition. ZpoolList is a standard container + * allowing filtering and iteration of imported ZFS pool information. + * + * Header requirements: + * + * #include + * #include + */ +#ifndef _ZPOOL_LIST_H_ +#define _ZPOOL_LIST_H_ + +/*============================ Namespace Control =============================*/ +using std::string; + +/*=========================== Forward Declarations ===========================*/ +struct zpool_handle; +typedef struct zpool_handle zpool_handle_t; + +struct nvlist; +typedef struct nvlist nvlist_t; + +class Vdev; + +/*============================= Class Definitions ============================*/ +/*--------------------------------- ZpoolList --------------------------------*/ +class ZpoolList; +typedef bool PoolFilter_t(zpool_handle_t *pool, nvlist_t *poolConfig, + void *filterArg); + +/** + * \brief Container of imported ZFS pool data. + * + * ZpoolList is a convenience class that converts libzfs's ZFS + * pool methods into a standard list container. + */ +class ZpoolList : public std::list +{ +public: + /** + * \brief Utility ZpoolList construction filter that causes all + * pools known to the system to be included in the + * intantiated ZpoolList. + */ + static PoolFilter_t ZpoolAll; + + /** + * \brief Utility ZpoolList construction filter that causes only + * a pool known to the system and having the specified GUID + * to be included in the intantiated ZpoolList. + */ + static PoolFilter_t ZpoolByGUID; + + /** + * \brief Utility ZpoolList construction filter that causes only + * pools known to the system and having the specified name + * to be included in the intantiated ZpoolList. + */ + static PoolFilter_t ZpoolByName; + + /** + * \brief ZpoolList contructor + * + * \param filter The filter function to use when constructing + * the ZpoolList. This may be one of the static + * utility filters defined for ZpoolList or a + * user defined function. + * \param filterArg A single argument to pass into the filter function + * when it is invoked on each candidate pool. + */ + ZpoolList(PoolFilter_t *filter = ZpoolAll, void *filterArg = NULL); + ~ZpoolList(); + +private: + /** + * \brief Helper routine used to populate the internal + * data store of ZFS pool objects using libzfs's + * zpool_iter() function. + * + * \param pool The ZFS pool object to filter. + * \param data User argument passed through zpool_iter(). + */ + static int LoadIterator(zpool_handle_t *pool, void *data); + + /** + * \brief The filter with which this ZpoolList was constructed. + */ + PoolFilter_t *m_filter; + + /** + * \brief The filter argument with which this ZpoolList was + * constructed. + */ + void *m_filterArg; +}; + +#endif /* _ZPOOL_ITERATOR_H_ */ Index: projects/zfsd/head/etc/mtree/BSD.tests.dist =================================================================== --- projects/zfsd/head/etc/mtree/BSD.tests.dist (revision 300334) +++ projects/zfsd/head/etc/mtree/BSD.tests.dist (revision 300335) @@ -1,952 +1,950 @@ # $FreeBSD$ # # Please see the file src/etc/mtree/README before making changes to this file. # /set type=dir uname=root gname=wheel mode=0755 . bin cat .. chown .. date .. dd .. expr .. ls .. mv .. pax .. pkill .. sh builtins .. errors .. execution .. expansion .. parameters .. parser .. set-e .. .. sleep .. test .. .. cddl lib .. - sbin - zfsd - .. - .. usr.bin .. usr.sbin dtrace common aggs .. arithmetic .. arrays .. assocs .. begin .. bitfields .. buffering .. builtinvar .. cg .. clauses .. cpc .. decls .. drops .. dtraceUtil .. end .. enum .. error .. exit .. fbtprovider .. funcs .. grammar .. include .. inline .. io .. ip .. java_api .. json .. lexer .. llquantize .. mdb .. mib .. misc .. multiaggs .. offsetof .. operators .. pid .. plockstat .. pointers .. pragma .. predicates .. preprocessor .. print .. printa .. printf .. privs .. probes .. proc .. profile-n .. providers .. raise .. rates .. safety .. scalars .. sched .. scripting .. sdt .. sizeof .. speculation .. stability .. stack .. stackdepth .. stop .. strlen .. strtoll .. struct .. syscall .. sysevent .. tick-n .. trace .. tracemem .. translators .. typedef .. types .. uctf .. union .. usdt .. ustack .. vars .. version .. .. + .. + zfsd .. .. .. etc rc.d .. .. games .. gnu lib .. usr.bin diff .. .. .. lib atf libatf-c detail .. .. libatf-c++ detail .. .. test-programs .. .. libarchive .. libc c063 .. db .. gen execve .. posix_spawn .. .. hash data .. .. iconv .. inet .. locale .. net getaddrinfo data .. .. .. nss .. regex data .. .. resolv .. rpc .. ssp .. setjmp .. stdio .. stdlib .. string .. sys .. time .. tls dso .. .. termios .. ttyio .. .. libcrypt .. libdevdctl .. libmp .. libnv .. libpam .. libproc .. librt .. libthr dlopen .. .. libutil .. libxo .. msun .. .. libexec atf atf-check .. atf-sh .. .. rtld-elf .. .. sbin dhclient .. devd .. growfs .. ifconfig .. mdconfig .. .. secure lib .. libexec .. usr.bin .. usr.sbin .. .. share examples tests atf .. plain .. .. .. .. sys acl .. aio .. cddl zfs bin .. include .. tests acl cifs .. nontrivial .. trivial .. .. atime .. bootfs .. cache .. cachefile .. clean_mirror assertion_001 .. assertion_002 .. assertion_003 .. assertion_004 .. .. cli_root zfs_upgrade .. zfs_promote .. zfs_clone .. zfs_property .. zfs_destroy .. zpool_create .. zpool_history .. zpool_expand .. zpool_remove .. zfs_mount .. zfs_unshare .. zdb .. zpool_online .. zpool_get .. zpool_export .. zfs_copies .. zfs_get .. zfs .. zpool_clear .. zpool_import .. zpool .. zpool_offline .. zpool_replace .. zfs_rollback .. zpool_set .. zfs_send .. zfs_set .. zpool_detach .. zpool_scrub .. zfs_inherit .. zfs_snapshot .. zfs_share .. zpool_destroy .. zpool_status .. zfs_unmount .. zfs_receive .. zfs_create .. zpool_upgrade blockfiles .. .. zpool_add .. zfs_rename .. zpool_attach .. zfs_reservation .. .. cli_user misc .. zfs_list .. zpool_iostat .. zpool_list .. .. compression .. ctime .. delegate zfs_allow .. zfs_unallow .. .. devices .. exec .. grow_pool .. grow_replicas .. history .. hotplug .. hotspare .. inheritance .. interop .. inuse .. iscsi .. large_files .. largest_pool .. link_count .. mdb .. migration .. mmap mmap_read .. mmap_write .. .. mount .. mv_files .. nestedfs .. no_space .. online_offline .. pool_names .. poolversion .. privilege .. quota .. redundancy .. refquota .. refreserv .. remote cross_endian .. rebooting .. sharing .. .. rename_dirs .. replacement .. reservation .. rootpool .. rsend .. sas_phy_thrash .. scrub_mirror .. slog .. snapshot .. snapused .. sparse .. threadsappend .. truncate .. txg_integrity .. userquota .. utils_test .. write_dirs .. xattr .. zfsd .. zil .. zinject .. zones .. zvol zvol_ENOSPC .. zvol_cli .. zvol_misc .. zvol_swap .. .. zvol_thrash .. .. .. .. dtrace .. fifo .. file .. geom class concat .. eli .. gate .. gpt .. mirror .. nop .. part .. raid3 .. shsec .. stripe .. uzip etalon .. .. .. .. kern acct .. execve .. pipe .. .. kqueue .. mac bsdextended .. portacl .. .. mqueue .. net .. netinet .. opencrypto .. pjdfstest chflags .. chmod .. chown .. ftruncate .. granular .. link .. mkdir .. mkfifo .. mknod .. open .. rename .. rmdir .. symlink .. truncate .. unlink .. .. posixshm .. sys .. vfs .. vm .. .. usr.bin apply .. basename .. bmake archives fmt_44bsd .. fmt_44bsd_mod .. fmt_oldbsd .. .. basic t0 .. t1 .. t2 .. t3 .. .. execution ellipsis .. empty .. joberr .. plus .. .. shell builtin .. meta .. path .. path_select .. replace .. select .. .. suffixes basic .. src_wild1 .. src_wild2 .. .. syntax directive-t0 .. enl .. funny-targets .. semi .. .. sysmk t0 2 1 .. .. mk .. .. t1 2 1 .. .. mk .. .. t2 2 1 .. .. mk .. .. .. variables modifier_M .. modifier_t .. opt_V .. t0 .. .. .. bsdcat .. calendar .. cmp .. cpio .. col .. comm .. cut .. dirname .. file2c .. grep .. gzip .. ident .. join .. jot .. lastcomm .. limits .. m4 .. mkimg .. ncal .. opensm .. printf .. sdiff .. sed regress.multitest.out .. .. soelim .. tar .. timeout .. tr .. truncate .. units .. uudecode .. uuencode .. xargs .. xinstall .. xo .. yacc yacc .. .. .. usr.sbin etcupdate .. extattr .. fstyp .. makefs .. newsyslog .. nmtree .. pw .. rpcbind .. sa .. .. .. # vim: set expandtab ts=4 sw=4: Index: projects/zfsd/head/etc/rc.d/zfsd =================================================================== --- projects/zfsd/head/etc/rc.d/zfsd (revision 300334) +++ projects/zfsd/head/etc/rc.d/zfsd (revision 300335) @@ -1,17 +1,17 @@ #!/bin/sh # # $FreeBSD$ # # PROVIDE: zfsd # REQUIRE: devd zfs # KEYWORD: nojail shutdown . /etc/rc.subr name="zfsd" rcvar="zfsd_enable" -command="/sbin/${name}" +command="/usr/sbin/${name}" load_rc_config $name run_rc_command "$1" Index: projects/zfsd/head/tests/sys/cddl/zfs/include/commands.txt =================================================================== --- projects/zfsd/head/tests/sys/cddl/zfs/include/commands.txt (revision 300334) +++ projects/zfsd/head/tests/sys/cddl/zfs/include/commands.txt (revision 300335) @@ -1,189 +1,189 @@ # # 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 # or http://www.opensolaris.org/os/licensing. # 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 # #!/usr/local/bin/ksh93 -p # # Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # #ident "@(#)commands.txt 1.8 09/01/13 SMI" # # All the commands in this file are converted into environment variables # with the same name as the command e.g. # # /bin/df becomes export DF="/bin/df" # finally an export CMDS="aggregation of all the environment variables" # is created for checking in the execution environment. # # comments are ignored in this file, as are whitespace lines # General Solaris Commands /usr/bin/awk /usr/sbin/arp /usr/bin/basename /bin/cat /usr/bin/cd /usr/bin/chgrp /bin/chmod /usr/sbin/chown /usr/bin/cksum /sbin/clri /usr/bin/cmp /usr/bin/compress /usr/bin/uncompress #/usr/bin/coreadm /bin/cp /usr/bin/cpio /usr/bin/cut /bin/date /bin/dd #/usr/sbin/devfsadm %%STFSUITEDIR%%/bin/df /usr/bin/diff %%STFSUITEDIR%%/bin/dircmp /usr/bin/dirname /usr/bin/du #%%STFSUITEDIR%%/bin/dumpadm /bin/echo /usr/bin/egrep /usr/bin/env #%%STFSUITEDIR%%/bin/ff /usr/bin/fgrep /usr/bin/file /usr/bin/find #%%STFSUITEDIR%%/bin/fmadm #%%STFSUITEDIR%%/bin/fmdump #%%STFSUITEDIR%%/bin/format /sbin/fsck /sbin/fsdb /sbin/fsirand /usr/bin/fsync /usr/sbin/fstyp /usr/bin/id #/usr/bin/isainfo #/usr/sbin/iscsiadm #/usr/sbin/iscsitadm /usr/bin/getent /bin/getfacl /usr/bin/getconf /sbin/sha1 /sbin/gpart /usr/bin/grep /usr/bin/groups %%STFSUITEDIR%%/bin/groupadd %%STFSUITEDIR%%/bin/groupdel %%STFSUITEDIR%%/bin/groupmod %%STFSUITEDIR%%/bin/groupshow /usr/bin/head /bin/hostname /bin/kill /usr/local/bin/ksh93 #/usr/sbin/labelit #/usr/sbin/lockfs #/usr/sbin/lofiadm /bin/ls /usr/bin/logname #/usr/bin/mdb /sbin/mdconfig #/usr/sbin/metaclear #/usr/sbin/metadb #/usr/sbin/metainit #/usr/sbin/metastat /bin/mkdir /usr/local/sbin/mkfile /sbin/mknod #/usr/sbin/modinfo #/usr/sbin/modunload /sbin/mount /bin/mv #/usr/sbin/ncheck /sbin/newfs /usr/bin/nawk #/usr/bin/pack /usr/bin/pagesize /bin/pax /sbin/ping /usr/bin/printf #/usr/sbin/prtvtoc #/usr/bin/pfexec /bin/pgrep #/usr/bin/pkginfo /bin/pkill /bin/ps #/usr/sbin/psrinfo /bin/pwd /usr/sbin/quotaon /bin/rcp /sbin/reboot /bin/rm /bin/rmdir /usr/bin/rsh #/usr/bin/runat /usr/bin/sed #/usr/sbin/share /bin/sleep /usr/bin/su /usr/bin/sum #%%STFSUITEDIR%%/bin/svcs #/usr/sbin/svcadm #%%STFSUITEDIR%%/bin/swap #/sbin/swapadd /usr/bin/sort /usr/bin/strings /bin/sync /usr/bin/tar /usr/bin/tail /usr/bin/touch /usr/bin/tr /usr/bin/true /sbin/tunefs #/usr/sbin/ufsdump #/usr/sbin/ufsrestore /usr/bin/umask /sbin/umount /usr/bin/uname /usr/bin/uniq #/usr/sbin/unshare #/usr/bin/unpack %%STFSUITEDIR%%/bin/useradd %%STFSUITEDIR%%/bin/userdel %%STFSUITEDIR%%/bin/usermod /usr/bin/wait /usr/bin/wc #%%STFSUITEDIR%%/bin/zoneadm #%%STFSUITEDIR%%/bin/zonecfg #%%STFSUITEDIR%%/bin/zlogin #/usr/bin/zonename /sbin/swapon /sbin/swapoff /sbin/swapctl /usr/bin/xargs -/sbin/zfsd +/usr/sbin/zfsd # ZFS Commands /usr/sbin/zdb /sbin/zfs /sbin/zpool /usr/bin/zinject # Test framework commands #/opt/SUNWstc-runwattr/bin/runwattr %%STFSUITEDIR%%/bin/bsddisks