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 * $FreeBSD$ 33 */ 34 35 /** 36 * \file vdev.cc 37 * 38 * Implementation of the Vdev class. 39 */ 40 #include <syslog.h> 41 #include <sys/cdefs.h> 42 #include <sys/fs/zfs.h> 43 44 #include <libzfs.h> 45 /* 46 * Undefine flush, defined by cpufunc.h on sparc64, because it conflicts with 47 * C++ flush methods 48 */ 49 #undef flush 50 51 #include <list> 52 #include <map> 53 #include <string> 54 #include <sstream> 55 56 #include <devdctl/guid.h> 57 #include <devdctl/event.h> 58 #include <devdctl/event_factory.h> 59 #include <devdctl/exception.h> 60 #include <devdctl/consumer.h> 61 62 #include "vdev.h" 63 #include "vdev_iterator.h" 64 #include "zfsd.h" 65 #include "zfsd_exception.h" 66 #include "zpool_list.h" 67 68 __FBSDID("$FreeBSD$"); 69 /*============================ Namespace Control =============================*/ 70 using std::string; 71 using std::stringstream; 72 73 //- Special objects ----------------------------------------------------------- 74 Vdev NonexistentVdev; 75 76 //- Vdev Inline Public Methods ------------------------------------------------ 77 /*=========================== Class Implementations ==========================*/ 78 /*----------------------------------- Vdev -----------------------------------*/ 79 80 /* Special constructor for NonexistentVdev. */ 81 Vdev::Vdev() 82 : m_poolConfig(NULL), 83 m_config(NULL) 84 {} 85 86 bool 87 Vdev::VdevLookupPoolGuid() 88 { 89 uint64_t guid; 90 if (nvlist_lookup_uint64(m_poolConfig, ZPOOL_CONFIG_POOL_GUID, &guid)) 91 return (false); 92 m_poolGUID = guid; 93 return (true); 94 } 95 96 void 97 Vdev::VdevLookupGuid() 98 { 99 uint64_t guid; 100 if (nvlist_lookup_uint64(m_config, ZPOOL_CONFIG_GUID, &guid) != 0) 101 throw ZfsdException("Unable to extract vdev GUID " 102 "from vdev config data."); 103 m_vdevGUID = guid; 104 } 105 106 Vdev::Vdev(zpool_handle_t *pool, nvlist_t *config) 107 : m_poolConfig(zpool_get_config(pool, NULL)), 108 m_config(config) 109 { 110 if (!VdevLookupPoolGuid()) 111 throw ZfsdException("Can't extract pool GUID from handle."); 112 VdevLookupGuid(); 113 } 114 115 Vdev::Vdev(nvlist_t *poolConfig, nvlist_t *config) 116 : m_poolConfig(poolConfig), 117 m_config(config) 118 { 119 if (!VdevLookupPoolGuid()) 120 throw ZfsdException("Can't extract pool GUID from config."); 121 VdevLookupGuid(); 122 } 123 124 Vdev::Vdev(nvlist_t *labelConfig) 125 : m_poolConfig(labelConfig), 126 m_config(labelConfig) 127 { 128 /* 129 * Spares do not have a Pool GUID. Tolerate its absence. 130 * Code accessing this Vdev in a context where the Pool GUID is 131 * required will find it invalid (as it is upon Vdev construction) 132 * and act accordingly. 133 */ 134 (void) VdevLookupPoolGuid(); 135 VdevLookupGuid(); 136 137 try { 138 m_config = VdevIterator(labelConfig).Find(m_vdevGUID); 139 } catch (const ZfsdException &exp) { 140 /* 141 * When reading a spare's label, it is normal not to find 142 * a list of vdevs 143 */ 144 m_config = NULL; 145 } 146 } 147 148 bool 149 Vdev::IsSpare() const 150 { 151 uint64_t is_spare(0); 152 153 if (m_config == NULL) 154 return (false); 155 156 (void)nvlist_lookup_uint64(m_config, ZPOOL_CONFIG_IS_SPARE, &is_spare); 157 return (bool(is_spare)); 158 } 159 160 vdev_state 161 Vdev::State() const 162 { 163 uint64_t *nvlist_array; 164 vdev_stat_t *vs; 165 uint_t vsc; 166 167 if (m_config == NULL) { 168 /* 169 * If we couldn't find the list of vdevs, that normally means 170 * that this is an available hotspare. In that case, we will 171 * presume it to be healthy. Even if this spare had formerly 172 * been in use, been degraded, and been replaced, the act of 173 * replacement wipes the degraded bit from the label. So we 174 * have no choice but to presume that it is healthy. 175 */ 176 return (VDEV_STATE_HEALTHY); 177 } 178 179 if (nvlist_lookup_uint64_array(m_config, ZPOOL_CONFIG_VDEV_STATS, 180 &nvlist_array, &vsc) == 0) { 181 vs = reinterpret_cast<vdev_stat_t *>(nvlist_array); 182 return (static_cast<vdev_state>(vs->vs_state)); 183 } 184 185 /* 186 * Stats are not available. This vdev was created from a label. 187 * Synthesize a state based on available data. 188 */ 189 uint64_t faulted(0); 190 uint64_t degraded(0); 191 (void)nvlist_lookup_uint64(m_config, ZPOOL_CONFIG_FAULTED, &faulted); 192 (void)nvlist_lookup_uint64(m_config, ZPOOL_CONFIG_DEGRADED, °raded); 193 if (faulted) 194 return (VDEV_STATE_FAULTED); 195 if (degraded) 196 return (VDEV_STATE_DEGRADED); 197 return (VDEV_STATE_HEALTHY); 198 } 199 200 std::list<Vdev> 201 Vdev::Children() 202 { 203 nvlist_t **vdevChildren; 204 int result; 205 u_int numChildren; 206 std::list<Vdev> children; 207 208 if (m_poolConfig == NULL || m_config == NULL) 209 return (children); 210 211 result = nvlist_lookup_nvlist_array(m_config, 212 ZPOOL_CONFIG_CHILDREN, &vdevChildren, &numChildren); 213 if (result != 0) 214 return (children); 215 216 for (u_int c = 0;c < numChildren; c++) 217 children.push_back(Vdev(m_poolConfig, vdevChildren[c])); 218 219 return (children); 220 } 221 222 Vdev 223 Vdev::RootVdev() 224 { 225 nvlist_t *rootVdev; 226 227 if (m_poolConfig == NULL) 228 return (NonexistentVdev); 229 230 if (nvlist_lookup_nvlist(m_poolConfig, ZPOOL_CONFIG_VDEV_TREE, 231 &rootVdev) != 0) 232 return (NonexistentVdev); 233 return (Vdev(m_poolConfig, rootVdev)); 234 } 235 236 /* 237 * Find our parent. This requires doing a traversal of the config; we can't 238 * cache it as leaf vdevs may change their pool config location (spare, 239 * replacing, mirror, etc). 240 */ 241 Vdev 242 Vdev::Parent() 243 { 244 std::list<Vdev> to_examine; 245 std::list<Vdev> children; 246 std::list<Vdev>::iterator children_it; 247 248 to_examine.push_back(RootVdev()); 249 for (;;) { 250 if (to_examine.empty()) 251 return (NonexistentVdev); 252 Vdev vd = to_examine.front(); 253 if (vd.DoesNotExist()) 254 return (NonexistentVdev); 255 to_examine.pop_front(); 256 children = vd.Children(); 257 children_it = children.begin(); 258 for (;children_it != children.end(); children_it++) { 259 Vdev child = *children_it; 260 261 if (child.GUID() == GUID()) 262 return (vd); 263 to_examine.push_front(child); 264 } 265 } 266 } 267 268 bool 269 Vdev::IsAvailableSpare() const 270 { 271 /* If we have a pool guid, we cannot be an available spare. */ 272 if (PoolGUID()) 273 return (false); 274 275 return (true); 276 } 277 278 bool 279 Vdev::IsSpare() 280 { 281 uint64_t spare; 282 if (nvlist_lookup_uint64(m_config, ZPOOL_CONFIG_IS_SPARE, &spare) != 0) 283 return (false); 284 return (spare != 0); 285 } 286 287 bool 288 Vdev::IsActiveSpare() const 289 { 290 vdev_stat_t *vs; 291 uint_t c; 292 293 if (m_poolConfig == NULL) 294 return (false); 295 296 (void) nvlist_lookup_uint64_array(m_config, ZPOOL_CONFIG_VDEV_STATS, 297 reinterpret_cast<uint64_t **>(&vs), &c); 298 if (vs == NULL || vs->vs_aux != VDEV_AUX_SPARED) 299 return (false); 300 return (true); 301 } 302 303 bool 304 Vdev::IsResilvering() const 305 { 306 pool_scan_stat_t *ps = NULL; 307 uint_t c; 308 309 if (State() != VDEV_STATE_HEALTHY) 310 return (false); 311 312 (void) nvlist_lookup_uint64_array(m_config, ZPOOL_CONFIG_SCAN_STATS, 313 reinterpret_cast<uint64_t **>(&ps), &c); 314 if (ps == NULL || ps->pss_func != POOL_SCAN_RESILVER) 315 return (false); 316 return (true); 317 } 318 319 string 320 Vdev::GUIDString() const 321 { 322 stringstream vdevGUIDString; 323 324 vdevGUIDString << GUID(); 325 return (vdevGUIDString.str()); 326 } 327 328 string 329 Vdev::Name(zpool_handle_t *zhp, bool verbose) const 330 { 331 return (zpool_vdev_name(g_zfsHandle, zhp, m_config, 332 verbose ? B_TRUE : B_FALSE)); 333 } 334 335 string 336 Vdev::Path() const 337 { 338 char *path(NULL); 339 340 if ((m_config != NULL) 341 && (nvlist_lookup_string(m_config, ZPOOL_CONFIG_PATH, &path) == 0)) 342 return (path); 343 344 return (""); 345 } 346 347 string 348 Vdev::PhysicalPath() const 349 { 350 char *path(NULL); 351 352 if ((m_config != NULL) && (nvlist_lookup_string(m_config, 353 ZPOOL_CONFIG_PHYS_PATH, &path) == 0)) 354 return (path); 355 356 return (""); 357 } 358