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 =============================*/ 67 using std::string; 68 using std::stringstream; 69 70 //- Special objects ----------------------------------------------------------- 71 Vdev NonexistentVdev; 72 73 //- Vdev Inline Public Methods ------------------------------------------------ 74 /*=========================== Class Implementations ==========================*/ 75 /*----------------------------------- Vdev -----------------------------------*/ 76 77 /* Special constructor for NonexistentVdev. */ 78 Vdev::Vdev() 79 : m_poolConfig(NULL), 80 m_config(NULL) 81 {} 82 83 bool 84 Vdev::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 93 void 94 Vdev::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 103 Vdev::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 112 Vdev::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 121 Vdev::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 145 bool 146 Vdev::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 157 vdev_state 158 Vdev::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 197 std::list<Vdev> 198 Vdev::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 219 Vdev 220 Vdev::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 */ 238 Vdev 239 Vdev::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 265 bool 266 Vdev::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 275 bool 276 Vdev::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 284 bool 285 Vdev::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 300 bool 301 Vdev::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 316 string 317 Vdev::GUIDString() const 318 { 319 stringstream vdevGUIDString; 320 321 vdevGUIDString << GUID(); 322 return (vdevGUIDString.str()); 323 } 324 325 string 326 Vdev::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 332 string 333 Vdev::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 344 string 345 Vdev::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