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