1/*- 2 * Copyright (c) 2011, 2012, 2013, 2014 Spectra Logic Corporation 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions, and the following disclaimer, 10 * without modification. 11 * 2. Redistributions in binary form must reproduce at minimum a disclaimer 12 * substantially similar to the "NO WARRANTY" disclaimer below 13 * ("Disclaimer") and any redistribution must be conditioned upon 14 * including a substantially similar Disclaimer requirement for further 15 * binary redistribution. 16 * 17 * NO WARRANTY 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 26 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 27 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 * POSSIBILITY OF SUCH DAMAGES. 29 * 30 * Authors: Justin T. Gibbs (Spectra Logic Corporation) 31 */ 32 33/** 34 * \file vdev.cc 35 * 36 * Implementation of the Vdev class. 37 */ 38#include <syslog.h> 39#include <sys/cdefs.h> 40#include <sys/byteorder.h> 41#include <sys/fs/zfs.h> 42 43#include <libzfs.h> 44/* 45 * Undefine flush, defined by cpufunc.h on sparc64, because it conflicts with 46 * C++ flush methods 47 */ 48#undef flush 49 50#include <list> 51#include <map> 52#include <string> 53#include <sstream> 54 55#include <devdctl/guid.h> 56#include <devdctl/event.h> 57#include <devdctl/event_factory.h> 58#include <devdctl/exception.h> 59#include <devdctl/consumer.h> 60 61#include "vdev.h" 62#include "vdev_iterator.h" 63#include "zfsd.h" 64#include "zfsd_exception.h" 65#include "zpool_list.h" 66/*============================ Namespace Control =============================*/ 67using std::string; 68using std::stringstream; 69 70//- Special objects ----------------------------------------------------------- 71Vdev NonexistentVdev; 72 73//- Vdev Inline Public Methods ------------------------------------------------ 74/*=========================== Class Implementations ==========================*/ 75/*----------------------------------- Vdev -----------------------------------*/ 76 77/* Special constructor for NonexistentVdev. */ 78Vdev::Vdev() 79 : m_poolConfig(NULL), 80 m_config(NULL) 81{} 82 83bool 84Vdev::VdevLookupPoolGuid() 85{ 86 uint64_t guid; 87 if (nvlist_lookup_uint64(m_poolConfig, ZPOOL_CONFIG_POOL_GUID, &guid)) 88 return (false); 89 m_poolGUID = guid; 90 return (true); 91} 92 93void 94Vdev::VdevLookupGuid() 95{ 96 uint64_t guid; 97 if (nvlist_lookup_uint64(m_config, ZPOOL_CONFIG_GUID, &guid) != 0) 98 throw ZfsdException("Unable to extract vdev GUID " 99 "from vdev config data."); 100 m_vdevGUID = guid; 101} 102 103Vdev::Vdev(zpool_handle_t *pool, nvlist_t *config) 104 : m_poolConfig(zpool_get_config(pool, NULL)), 105 m_config(config) 106{ 107 if (!VdevLookupPoolGuid()) 108 throw ZfsdException("Can't extract pool GUID from handle."); 109 VdevLookupGuid(); 110} 111 112Vdev::Vdev(nvlist_t *poolConfig, nvlist_t *config) 113 : m_poolConfig(poolConfig), 114 m_config(config) 115{ 116 if (!VdevLookupPoolGuid()) 117 throw ZfsdException("Can't extract pool GUID from config."); 118 VdevLookupGuid(); 119} 120 121Vdev::Vdev(nvlist_t *labelConfig) 122 : m_poolConfig(labelConfig), 123 m_config(labelConfig) 124{ 125 /* 126 * Spares do not have a Pool GUID. Tolerate its absence. 127 * Code accessing this Vdev in a context where the Pool GUID is 128 * required will find it invalid (as it is upon Vdev construction) 129 * and act accordingly. 130 */ 131 (void) VdevLookupPoolGuid(); 132 VdevLookupGuid(); 133 134 try { 135 m_config = VdevIterator(labelConfig).Find(m_vdevGUID); 136 } catch (const ZfsdException &exp) { 137 /* 138 * When reading a spare's label, it is normal not to find 139 * a list of vdevs 140 */ 141 m_config = NULL; 142 } 143} 144 145bool 146Vdev::IsSpare() const 147{ 148 uint64_t is_spare(0); 149 150 if (m_config == NULL) 151 return (false); 152 153 (void)nvlist_lookup_uint64(m_config, ZPOOL_CONFIG_IS_SPARE, &is_spare); 154 return (bool(is_spare)); 155} 156 157vdev_state 158Vdev::State() const 159{ 160 uint64_t *nvlist_array; 161 vdev_stat_t *vs; 162 uint_t vsc; 163 164 if (m_config == NULL) { 165 /* 166 * If we couldn't find the list of vdevs, that normally means 167 * that this is an available hotspare. In that case, we will 168 * presume it to be healthy. Even if this spare had formerly 169 * been in use, been degraded, and been replaced, the act of 170 * replacement wipes the degraded bit from the label. So we 171 * have no choice but to presume that it is healthy. 172 */ 173 return (VDEV_STATE_HEALTHY); 174 } 175 176 if (nvlist_lookup_uint64_array(m_config, ZPOOL_CONFIG_VDEV_STATS, 177 &nvlist_array, &vsc) == 0) { 178 vs = reinterpret_cast<vdev_stat_t *>(nvlist_array); 179 return (static_cast<vdev_state>(vs->vs_state)); 180 } 181 182 /* 183 * Stats are not available. This vdev was created from a label. 184 * Synthesize a state based on available data. 185 */ 186 uint64_t faulted(0); 187 uint64_t degraded(0); 188 (void)nvlist_lookup_uint64(m_config, ZPOOL_CONFIG_FAULTED, &faulted); 189 (void)nvlist_lookup_uint64(m_config, ZPOOL_CONFIG_DEGRADED, °raded); 190 if (faulted) 191 return (VDEV_STATE_FAULTED); 192 if (degraded) 193 return (VDEV_STATE_DEGRADED); 194 return (VDEV_STATE_HEALTHY); 195} 196 197std::list<Vdev> 198Vdev::Children() 199{ 200 nvlist_t **vdevChildren; 201 int result; 202 u_int numChildren; 203 std::list<Vdev> children; 204 205 if (m_poolConfig == NULL || m_config == NULL) 206 return (children); 207 208 result = nvlist_lookup_nvlist_array(m_config, 209 ZPOOL_CONFIG_CHILDREN, &vdevChildren, &numChildren); 210 if (result != 0) 211 return (children); 212 213 for (u_int c = 0;c < numChildren; c++) 214 children.push_back(Vdev(m_poolConfig, vdevChildren[c])); 215 216 return (children); 217} 218 219Vdev 220Vdev::RootVdev() 221{ 222 nvlist_t *rootVdev; 223 224 if (m_poolConfig == NULL) 225 return (NonexistentVdev); 226 227 if (nvlist_lookup_nvlist(m_poolConfig, ZPOOL_CONFIG_VDEV_TREE, 228 &rootVdev) != 0) 229 return (NonexistentVdev); 230 return (Vdev(m_poolConfig, rootVdev)); 231} 232 233/* 234 * Find our parent. This requires doing a traversal of the config; we can't 235 * cache it as leaf vdevs may change their pool config location (spare, 236 * replacing, mirror, etc). 237 */ 238Vdev 239Vdev::Parent() 240{ 241 std::list<Vdev> to_examine; 242 std::list<Vdev> children; 243 std::list<Vdev>::iterator children_it; 244 245 to_examine.push_back(RootVdev()); 246 for (;;) { 247 if (to_examine.empty()) 248 return (NonexistentVdev); 249 Vdev vd = to_examine.front(); 250 if (vd.DoesNotExist()) 251 return (NonexistentVdev); 252 to_examine.pop_front(); 253 children = vd.Children(); 254 children_it = children.begin(); 255 for (;children_it != children.end(); children_it++) { 256 Vdev child = *children_it; 257 258 if (child.GUID() == GUID()) 259 return (vd); 260 to_examine.push_front(child); 261 } 262 } 263} 264 265bool 266Vdev::IsAvailableSpare() const 267{ 268 /* If we have a pool guid, we cannot be an available spare. */ 269 if (PoolGUID()) 270 return (false); 271 272 return (true); 273} 274 275bool 276Vdev::IsSpare() 277{ 278 uint64_t spare; 279 if (nvlist_lookup_uint64(m_config, ZPOOL_CONFIG_IS_SPARE, &spare) != 0) 280 return (false); 281 return (spare != 0); 282} 283 284bool 285Vdev::IsActiveSpare() const 286{ 287 vdev_stat_t *vs; 288 uint_t c; 289 290 if (m_poolConfig == NULL) 291 return (false); 292 293 (void) nvlist_lookup_uint64_array(m_config, ZPOOL_CONFIG_VDEV_STATS, 294 reinterpret_cast<uint64_t **>(&vs), &c); 295 if (vs == NULL || vs->vs_aux != VDEV_AUX_SPARED) 296 return (false); 297 return (true); 298} 299 300bool 301Vdev::IsResilvering() const 302{ 303 pool_scan_stat_t *ps = NULL; 304 uint_t c; 305 306 if (State() != VDEV_STATE_HEALTHY) 307 return (false); 308 309 (void) nvlist_lookup_uint64_array(m_config, ZPOOL_CONFIG_SCAN_STATS, 310 reinterpret_cast<uint64_t **>(&ps), &c); 311 if (ps == NULL || ps->pss_func != POOL_SCAN_RESILVER) 312 return (false); 313 return (true); 314} 315 316string 317Vdev::GUIDString() const 318{ 319 stringstream vdevGUIDString; 320 321 vdevGUIDString << GUID(); 322 return (vdevGUIDString.str()); 323} 324 325string 326Vdev::Name(zpool_handle_t *zhp, bool verbose) const 327{ 328 return (zpool_vdev_name(g_zfsHandle, zhp, m_config, 329 verbose ? B_TRUE : B_FALSE)); 330} 331 332string 333Vdev::Path() const 334{ 335 const char *path(NULL); 336 337 if ((m_config != NULL) 338 && (nvlist_lookup_string(m_config, ZPOOL_CONFIG_PATH, &path) == 0)) 339 return (path); 340 341 return (""); 342} 343 344string 345Vdev::PhysicalPath() const 346{ 347 const char *path(NULL); 348 349 if ((m_config != NULL) && (nvlist_lookup_string(m_config, 350 ZPOOL_CONFIG_PHYS_PATH, &path) == 0)) 351 return (path); 352 353 return (""); 354} 355