1 /*- 2 * Copyright (c) 2011, 2012, 2013, 2014, 2016 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 zfsd_event.cc 35 */ 36 #include <sys/cdefs.h> 37 #include <sys/time.h> 38 #include <sys/fs/zfs.h> 39 #include <sys/vdev_impl.h> 40 41 #include <syslog.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 <sstream> 53 #include <string> 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 "callout.h" 62 #include "vdev_iterator.h" 63 #include "zfsd_event.h" 64 #include "case_file.h" 65 #include "vdev.h" 66 #include "zfsd.h" 67 #include "zfsd_exception.h" 68 #include "zpool_list.h" 69 70 __FBSDID("$FreeBSD$"); 71 /*============================ Namespace Control =============================*/ 72 using DevdCtl::Event; 73 using DevdCtl::Guid; 74 using DevdCtl::NVPairMap; 75 using std::stringstream; 76 77 /*=========================== Class Implementations ==========================*/ 78 79 /*-------------------------------- DevfsEvent --------------------------------*/ 80 81 //- DevfsEvent Static Public Methods ------------------------------------------- 82 Event * 83 DevfsEvent::Builder(Event::Type type, 84 NVPairMap &nvPairs, 85 const string &eventString) 86 { 87 return (new DevfsEvent(type, nvPairs, eventString)); 88 } 89 90 //- DevfsEvent Static Protected Methods ---------------------------------------- 91 nvlist_t * 92 DevfsEvent::ReadLabel(int devFd, bool &inUse, bool °raded) 93 { 94 pool_state_t poolState; 95 char *poolName; 96 boolean_t b_inuse; 97 int nlabels; 98 99 inUse = false; 100 degraded = false; 101 poolName = NULL; 102 if (zpool_in_use(g_zfsHandle, devFd, &poolState, 103 &poolName, &b_inuse) == 0) { 104 nvlist_t *devLabel; 105 106 inUse = b_inuse == B_TRUE; 107 if (poolName != NULL) 108 free(poolName); 109 110 nlabels = zpool_read_all_labels(devFd, &devLabel); 111 /* 112 * If we find a disk with fewer than the maximum number of 113 * labels, it might be the whole disk of a partitioned disk 114 * where ZFS resides on a partition. In that case, we should do 115 * nothing and wait for the partition to appear. Or, the disk 116 * might be damaged. In that case, zfsd should do nothing and 117 * wait for the sysadmin to decide. 118 */ 119 if (nlabels != VDEV_LABELS || devLabel == NULL) 120 return (NULL); 121 122 try { 123 Vdev vdev(devLabel); 124 degraded = vdev.State() != VDEV_STATE_HEALTHY; 125 return (devLabel); 126 } catch (ZfsdException &exp) { 127 string devName = fdevname(devFd); 128 string devPath = _PATH_DEV + devName; 129 string context("DevfsEvent::ReadLabel: " 130 + devPath + ": "); 131 132 exp.GetString().insert(0, context); 133 exp.Log(); 134 } 135 } 136 return (NULL); 137 } 138 139 bool 140 DevfsEvent::OnlineByLabel(const string &devPath, const string& physPath, 141 nvlist_t *devConfig) 142 { 143 try { 144 /* 145 * A device with ZFS label information has been 146 * inserted. If it matches a device for which we 147 * have a case, see if we can solve that case. 148 */ 149 syslog(LOG_INFO, "Interrogating VDEV label for %s\n", 150 devPath.c_str()); 151 Vdev vdev(devConfig); 152 CaseFile *caseFile(CaseFile::Find(vdev.PoolGUID(), 153 vdev.GUID())); 154 if (caseFile != NULL) 155 return (caseFile->ReEvaluate(devPath, physPath, &vdev)); 156 157 } catch (ZfsdException &exp) { 158 string context("DevfsEvent::OnlineByLabel: " + devPath + ": "); 159 160 exp.GetString().insert(0, context); 161 exp.Log(); 162 } 163 return (false); 164 } 165 166 //- DevfsEvent Virtual Public Methods ------------------------------------------ 167 Event * 168 DevfsEvent::DeepCopy() const 169 { 170 return (new DevfsEvent(*this)); 171 } 172 173 bool 174 DevfsEvent::Process() const 175 { 176 /* 177 * We are only concerned with newly discovered 178 * devices that can be ZFS vdevs. 179 */ 180 if (Value("type") != "CREATE" || !IsDiskDev()) 181 return (false); 182 183 /* Log the event since it is of interest. */ 184 Log(LOG_INFO); 185 186 string devPath; 187 if (!DevPath(devPath)) 188 return (false); 189 190 int devFd(open(devPath.c_str(), O_RDONLY)); 191 if (devFd == -1) 192 return (false); 193 194 bool inUse; 195 bool degraded; 196 nvlist_t *devLabel(ReadLabel(devFd, inUse, degraded)); 197 198 string physPath; 199 bool havePhysPath(PhysicalPath(physPath)); 200 201 string devName; 202 DevName(devName); 203 close(devFd); 204 205 if (inUse && devLabel != NULL) { 206 OnlineByLabel(devPath, physPath, devLabel); 207 } else if (degraded) { 208 syslog(LOG_INFO, "%s is marked degraded. Ignoring " 209 "as a replace by physical path candidate.\n", 210 devName.c_str()); 211 } else if (havePhysPath && IsWholeDev()) { 212 /* 213 * TODO: attempt to resolve events using every casefile 214 * that matches this physpath 215 */ 216 CaseFile *caseFile(CaseFile::Find(physPath)); 217 if (caseFile != NULL) { 218 syslog(LOG_INFO, 219 "Found CaseFile(%s:%s:%s) - ReEvaluating\n", 220 caseFile->PoolGUIDString().c_str(), 221 caseFile->VdevGUIDString().c_str(), 222 zpool_state_to_name(caseFile->VdevState(), 223 VDEV_AUX_NONE)); 224 caseFile->ReEvaluate(devPath, physPath, /*vdev*/NULL); 225 } 226 } 227 if (devLabel != NULL) 228 nvlist_free(devLabel); 229 return (false); 230 } 231 232 //- DevfsEvent Protected Methods ----------------------------------------------- 233 DevfsEvent::DevfsEvent(Event::Type type, NVPairMap &nvpairs, 234 const string &eventString) 235 : DevdCtl::DevfsEvent(type, nvpairs, eventString) 236 { 237 } 238 239 DevfsEvent::DevfsEvent(const DevfsEvent &src) 240 : DevdCtl::DevfsEvent::DevfsEvent(src) 241 { 242 } 243 244 /*-------------------------------- GeomEvent --------------------------------*/ 245 246 //- GeomEvent Static Public Methods ------------------------------------------- 247 Event * 248 GeomEvent::Builder(Event::Type type, 249 NVPairMap &nvPairs, 250 const string &eventString) 251 { 252 return (new GeomEvent(type, nvPairs, eventString)); 253 } 254 255 //- GeomEvent Virtual Public Methods ------------------------------------------ 256 Event * 257 GeomEvent::DeepCopy() const 258 { 259 return (new GeomEvent(*this)); 260 } 261 262 bool 263 GeomEvent::Process() const 264 { 265 /* 266 * We are only concerned with physical path changes, because those can 267 * be used to satisfy autoreplace operations 268 */ 269 if (Value("type") != "GEOM::physpath" || !IsDiskDev()) 270 return (false); 271 272 /* Log the event since it is of interest. */ 273 Log(LOG_INFO); 274 275 string devPath; 276 if (!DevPath(devPath)) 277 return (false); 278 279 string physPath; 280 bool havePhysPath(PhysicalPath(physPath)); 281 282 string devName; 283 DevName(devName); 284 285 if (havePhysPath) { 286 /* 287 * TODO: attempt to resolve events using every casefile 288 * that matches this physpath 289 */ 290 CaseFile *caseFile(CaseFile::Find(physPath)); 291 if (caseFile != NULL) { 292 syslog(LOG_INFO, 293 "Found CaseFile(%s:%s:%s) - ReEvaluating\n", 294 caseFile->PoolGUIDString().c_str(), 295 caseFile->VdevGUIDString().c_str(), 296 zpool_state_to_name(caseFile->VdevState(), 297 VDEV_AUX_NONE)); 298 caseFile->ReEvaluate(devPath, physPath, /*vdev*/NULL); 299 } 300 } 301 return (false); 302 } 303 304 //- GeomEvent Protected Methods ----------------------------------------------- 305 GeomEvent::GeomEvent(Event::Type type, NVPairMap &nvpairs, 306 const string &eventString) 307 : DevdCtl::GeomEvent(type, nvpairs, eventString) 308 { 309 } 310 311 GeomEvent::GeomEvent(const GeomEvent &src) 312 : DevdCtl::GeomEvent::GeomEvent(src) 313 { 314 } 315 316 317 /*--------------------------------- ZfsEvent ---------------------------------*/ 318 //- ZfsEvent Static Public Methods --------------------------------------------- 319 DevdCtl::Event * 320 ZfsEvent::Builder(Event::Type type, NVPairMap &nvpairs, 321 const string &eventString) 322 { 323 return (new ZfsEvent(type, nvpairs, eventString)); 324 } 325 326 //- ZfsEvent Virtual Public Methods -------------------------------------------- 327 Event * 328 ZfsEvent::DeepCopy() const 329 { 330 return (new ZfsEvent(*this)); 331 } 332 333 bool 334 ZfsEvent::Process() const 335 { 336 string logstr(""); 337 338 if (!Contains("class") && !Contains("type")) { 339 syslog(LOG_ERR, 340 "ZfsEvent::Process: Missing class or type data."); 341 return (false); 342 } 343 344 /* On config syncs, replay any queued events first. */ 345 if (Value("type").find("misc.fs.zfs.config_sync") == 0) { 346 /* 347 * Even if saved events are unconsumed the second time 348 * around, drop them. Any events that still can't be 349 * consumed are probably referring to vdevs or pools that 350 * no longer exist. 351 */ 352 ZfsDaemon::Get().ReplayUnconsumedEvents(/*discard*/true); 353 CaseFile::ReEvaluateByGuid(PoolGUID(), *this); 354 } 355 356 if (Value("type").find("misc.fs.zfs.") == 0) { 357 /* Configuration changes, resilver events, etc. */ 358 ProcessPoolEvent(); 359 return (false); 360 } 361 362 if (!Contains("pool_guid") || !Contains("vdev_guid")) { 363 /* Only currently interested in Vdev related events. */ 364 return (false); 365 } 366 367 CaseFile *caseFile(CaseFile::Find(PoolGUID(), VdevGUID())); 368 if (caseFile != NULL) { 369 Log(LOG_INFO); 370 syslog(LOG_INFO, "Evaluating existing case file\n"); 371 caseFile->ReEvaluate(*this); 372 return (false); 373 } 374 375 /* Skip events that can't be handled. */ 376 Guid poolGUID(PoolGUID()); 377 /* If there are no replicas for a pool, then it's not manageable. */ 378 if (Value("class").find("fs.zfs.vdev.no_replicas") == 0) { 379 stringstream msg; 380 msg << "No replicas available for pool " << poolGUID; 381 msg << ", ignoring"; 382 Log(LOG_INFO); 383 syslog(LOG_INFO, "%s", msg.str().c_str()); 384 return (false); 385 } 386 387 /* 388 * Create a case file for this vdev, and have it 389 * evaluate the event. 390 */ 391 ZpoolList zpl(ZpoolList::ZpoolByGUID, &poolGUID); 392 if (zpl.empty()) { 393 stringstream msg; 394 int priority = LOG_INFO; 395 msg << "ZfsEvent::Process: Event for unknown pool "; 396 msg << poolGUID << " "; 397 msg << "queued"; 398 Log(LOG_INFO); 399 syslog(priority, "%s", msg.str().c_str()); 400 return (true); 401 } 402 403 nvlist_t *vdevConfig = VdevIterator(zpl.front()).Find(VdevGUID()); 404 if (vdevConfig == NULL) { 405 stringstream msg; 406 int priority = LOG_INFO; 407 msg << "ZfsEvent::Process: Event for unknown vdev "; 408 msg << VdevGUID() << " "; 409 msg << "queued"; 410 Log(LOG_INFO); 411 syslog(priority, "%s", msg.str().c_str()); 412 return (true); 413 } 414 415 Vdev vdev(zpl.front(), vdevConfig); 416 caseFile = &CaseFile::Create(vdev); 417 if (caseFile->ReEvaluate(*this) == false) { 418 stringstream msg; 419 int priority = LOG_INFO; 420 msg << "ZfsEvent::Process: Unconsumed event for vdev("; 421 msg << zpool_get_name(zpl.front()) << ","; 422 msg << vdev.GUID() << ") "; 423 msg << "queued"; 424 Log(LOG_INFO); 425 syslog(priority, "%s", msg.str().c_str()); 426 return (true); 427 } 428 return (false); 429 } 430 431 //- ZfsEvent Protected Methods ------------------------------------------------- 432 ZfsEvent::ZfsEvent(Event::Type type, NVPairMap &nvpairs, 433 const string &eventString) 434 : DevdCtl::ZfsEvent(type, nvpairs, eventString) 435 { 436 } 437 438 ZfsEvent::ZfsEvent(const ZfsEvent &src) 439 : DevdCtl::ZfsEvent(src) 440 { 441 } 442 443 /* 444 * Sometimes the kernel won't detach a spare when it is no longer needed. This 445 * can happen for example if a drive is removed, then either the pool is 446 * exported or the machine is powered off, then the drive is reinserted, then 447 * the machine is powered on or the pool is imported. ZFSD must detach these 448 * spares itself. 449 */ 450 void 451 ZfsEvent::CleanupSpares() const 452 { 453 Guid poolGUID(PoolGUID()); 454 ZpoolList zpl(ZpoolList::ZpoolByGUID, &poolGUID); 455 if (!zpl.empty()) { 456 zpool_handle_t* hdl; 457 458 hdl = zpl.front(); 459 VdevIterator(hdl).Each(TryDetach, (void*)hdl); 460 } 461 } 462 463 void 464 ZfsEvent::ProcessPoolEvent() const 465 { 466 bool degradedDevice(false); 467 468 /* The pool is destroyed. Discard any open cases */ 469 if (Value("type") == "misc.fs.zfs.pool_destroy") { 470 Log(LOG_INFO); 471 CaseFile::ReEvaluateByGuid(PoolGUID(), *this); 472 return; 473 } 474 475 CaseFile *caseFile(CaseFile::Find(PoolGUID(), VdevGUID())); 476 if (caseFile != NULL) { 477 if (caseFile->VdevState() != VDEV_STATE_UNKNOWN 478 && caseFile->VdevState() < VDEV_STATE_HEALTHY) 479 degradedDevice = true; 480 481 Log(LOG_INFO); 482 caseFile->ReEvaluate(*this); 483 } 484 else if (Value("type") == "misc.fs.zfs.resilver_finish") 485 { 486 /* 487 * It's possible to get a resilver_finish event with no 488 * corresponding casefile. For example, if a damaged pool were 489 * exported, repaired, then reimported. 490 */ 491 Log(LOG_INFO); 492 CleanupSpares(); 493 } 494 495 if (Value("type") == "misc.fs.zfs.vdev_remove" 496 && degradedDevice == false) { 497 498 /* See if any other cases can make use of this device. */ 499 Log(LOG_INFO); 500 ZfsDaemon::RequestSystemRescan(); 501 } 502 } 503 504 bool 505 ZfsEvent::TryDetach(Vdev &vdev, void *cbArg) 506 { 507 /* 508 * Outline: 509 * if this device is a spare, and its parent includes one healthy, 510 * non-spare child, then detach this device. 511 */ 512 zpool_handle_t *hdl(static_cast<zpool_handle_t*>(cbArg)); 513 514 if (vdev.IsSpare()) { 515 std::list<Vdev> siblings; 516 std::list<Vdev>::iterator siblings_it; 517 boolean_t cleanup = B_FALSE; 518 519 Vdev parent = vdev.Parent(); 520 siblings = parent.Children(); 521 522 /* Determine whether the parent should be cleaned up */ 523 for (siblings_it = siblings.begin(); 524 siblings_it != siblings.end(); 525 siblings_it++) { 526 Vdev sibling = *siblings_it; 527 528 if (!sibling.IsSpare() && 529 sibling.State() == VDEV_STATE_HEALTHY) { 530 cleanup = B_TRUE; 531 break; 532 } 533 } 534 535 if (cleanup) { 536 syslog(LOG_INFO, "Detaching spare vdev %s from pool %s", 537 vdev.Path().c_str(), zpool_get_name(hdl)); 538 zpool_vdev_detach(hdl, vdev.Path().c_str()); 539 } 540 541 } 542 543 /* Always return false, because there may be other spares to detach */ 544 return (false); 545 } 546